dyn_safe_proc_macros/
mod.rs

1#![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            // println!("{}", it.to_string());
21            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, // impl 'static + ::core::ops::Deref<Target = str>,
57        ),
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    // End of attrs.
76    match attrs.next() {
77        | None => {}, // OK
78        // | Some(TT::Ident(where_)) if where_.to_string() == "where" => {
79        //     // Easter egg path
80        //     match (attrs.next(), attrs.next(), attrs.next()) {
81        //         | (
82        //             Some(TT::Ident(Self_)),
83        //             Some(TT::Punct(colon)),
84        //             Some(TT::Ident(Sized_)),
85        //         )   if Self_.to_string() == "Self"
86        //             && colon.as_char() == ':'
87        //             && Sized_.to_string() == "Sized"
88        //         => error! {
89        //             "👏👏👏👏👏👏👏👏👏👏👏👏👏👏👏👏👏👏👏"
90        //                 => where_.span(), Sized_.span()
91        //         },
92        //         | _ => error! {
93        //             "extraneous token(s)" => where_.span(),
94        //         },
95        //     }
96        // },
97        | 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    // Generics
164    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})}