dyn_safe_proc_macros/
mod.rs1#![forbid(unsafe_code)]
2#![allow(bad_style)]
3
4extern crate proc_macro;
5
6use ::proc_macro::{*,
7 Span,
8 TokenStream as TS,
9 TokenTree as TT,
10};
11
12#[macro_use]
13mod utils;
14
15#[proc_macro_attribute] pub
16fn dyn_safe (attrs: TS, input: TS) -> TS
17{
18 match dyn_safe_impl(attrs, input) {
19 | Ok(it) => {
20 it
22 },
23 | Err((
24 (start_span, mb_end_span),
25 err_msg,
26 )) => {
27 macro_rules! spanned {($expr:expr) => (
28 match $expr { mut expr => {
29 expr.set_span(start_span);
30 expr
31 }}
32 )}
33 return ts![
34 Ident::new("compile_error", start_span),
35 spanned!(Punct::new('!', Spacing::Alone)),
36 spanned!(ts![ (
37 Literal::string(&*err_msg),
38 )]),
39 {
40 let mut it = spanned!(Punct::new(';', Spacing::Alone));
41 if let Some(end_span) = mb_end_span {
42 it.set_span(end_span);
43 }
44 it
45 },
46 ];
47 },
48 }
49}
50
51fn dyn_safe_impl (attrs: TS, input: TS)
52 -> Result<
53 TS,
54 (
55 (Span, Option<Span>),
56 &'static str, ),
58 >
59{Ok({
60 let mut attrs = attrs.into_iter();
61 let mut input = input.into_iter().peekable();
62 let s;
63 let (dyn_safe, dyn_safe_span) = unwrap_next_token! {
64 if let TT::Ident(ident) = attrs.next(),
65 if ({
66 s = ident.to_string();
67 s == "true" || s == "false"
68 })
69 {
70 (s == "true", ident.span())
71 } else {
72 failwith!("expected `true`");
73 }
74 };
75 match attrs.next() {
77 | None => {}, | Some(extraneous_tt) => error! {
98 "extraneous token(s)" => extraneous_tt.span(),
99 },
100 }
101
102 let mut prefix = vec![];
103 while matches!(input.peek(),
104 Some(TT::Punct(p)) if p.as_char() == '#'
105 )
106 {
107 prefix.extend(input.by_ref().take(2));
108 }
109 if matches!(input.peek(),
110 Some(TT::Ident(pub_)) if pub_.to_string() == "pub"
111 )
112 {
113 prefix.push(input.next().unwrap());
114 if matches!(input.peek(),
115 Some(TT::Group(g)) if g.delimiter() == Delimiter::Parenthesis
116 )
117 {
118 prefix.push(input.next().unwrap());
119 }
120 }
121 if matches!(input.peek(),
122 Some(TT::Ident(unsafe_)) if unsafe_.to_string() == "unsafe"
123 )
124 {
125 prefix.push(input.next().unwrap());
126 }
127 if matches!(input.peek(),
128 Some(TT::Ident(auto_)) if auto_.to_string() == "auto"
129 )
130 {
131 prefix.push(input.next().unwrap());
132 }
133 unwrap_next_token! {
134 if let TT::Ident(trait_) = input.next(),
135 if (trait_.to_string() == "trait")
136 {
137 prefix.push(trait_.into());
138 } else {
139 failwith!("expected `trait`");
140 }
141 }
142 let trait_name = unwrap_next_token! {
143 if let TT::Ident(ident) = input.next(), { ident } else {
144 failwith!("expected an identifier");
145 }
146 };
147 if dyn_safe {
148 return Ok(
149 prefix
150 .into_iter()
151 .chain(Some(trait_name.clone().into()))
152 .chain(input)
153 .chain(ts![
154 Ident::new("impl", dyn_safe_span),
155 Ident::new("dyn", dyn_safe_span),
156 trait_name,
157 ts![{}],
158 ])
159 .collect()
160 );
161 }
162 prefix.push(trait_name.into());
163 let mut depth = 0;
165 loop {
166 match input.peek() {
167 | Some(TT::Punct(p)) if p.as_char() == '<' => {
168 prefix.push(input.next().unwrap());
169 depth += 1;
170 },
171 | Some(TT::Punct(p)) if p.as_char() == '>' => {
172 prefix.push(input.next().unwrap());
173 depth -= 1;
174 },
175 | Some(_) if depth != 0 => {
176 prefix.push(input.next().unwrap());
177 },
178 | Some(TT::Punct(p)) if p.as_char() == ':' => {
179 prefix.push(input.next().unwrap());
180 break;
181 },
182 | _ => {
183 prefix.push(Punct::new(':', Spacing::Alone).into());
184 break;
185 },
186 }
187 }
188 prefix
189 .into_iter()
190 .chain(ts![
191 Punct::new(':', Spacing::Joint),
192 Punct::new(':', Spacing::Alone),
193 Ident::new("dyn_safe", dyn_safe_span),
194 Punct::new(':', Spacing::Joint),
195 Punct::new(':', Spacing::Alone),
196 Ident::new("NotObjectSafe", dyn_safe_span),
197 Punct::new('+', Spacing::Alone),
198 ])
199 .chain(input)
200 .collect()
201})}