function_name_proc_macro/
lib.rs

1#[allow(unused_imports)]
2use {
3    ::core::{
4        ops::Not as _,
5    },
6    ::proc_macro::{*,
7        TokenStream,
8    },
9};
10
11#[proc_macro_attribute] pub
12fn named (
13    params: TokenStream,
14    input: TokenStream,
15) -> TokenStream
16{
17    named_impl(params.into(), input.into())
18        .unwrap_or_else(|err| {
19            let err = Some(TokenTree::from(Literal::string(err)));
20            quote!(
21                ::core::compile_error! { #err }
22            )
23        })
24        .into()
25}
26
27fn named_impl (
28    params: TokenStream,
29    input: TokenStream,
30) -> Result<TokenStream, &'static str>
31{
32    // parse::Nothing for `params`.
33    if let Some(_) = params.into_iter().next() {
34        return Err("unexpected attribute arguments".into());
35    }
36
37    let ref mut tts = input.into_iter().peekable();
38
39    let mut input = Vec::<TokenTree>::new();
40
41    // `#` `[…]` attributes:
42    while matches!(tts.peek(), Some(TokenTree::Punct(p)) if p.as_char() == '#') {
43        input.extend(tts.take(2));
44    }
45
46    // rest but scan the tt right after `fn`.
47    let fname = loop {
48        let tt = tts.next().unwrap();
49        if matches!(tt, TokenTree::Ident(ref ident) if ident.to_string() == "fn") {
50            input.push(tt);
51            let fname = tts.peek().unwrap().to_string();
52            input.extend(tts);
53            break Some(TokenTree::from(Literal::string(&fname)));
54        }
55        input.push(tt);
56    };
57
58    let g = match input.last_mut() {
59        | Some(TokenTree::Group(g)) if g.delimiter() == Delimiter::Brace => g,
60        | _ => return Err("expected a `fn`"),
61    };
62    let g_span = g.span();
63    *g = Group::new(g.delimiter(), {
64        let mut body = quote!(
65            macro_rules! function_name {() => (
66                #fname
67            )}
68        );
69        body.extend(g.stream());
70        body
71    });
72    g.set_span(g_span);
73    Ok(input.into_iter().collect())
74}
75
76/// Mini `quote!` implementation,
77/// can only interpolate `impl IntoIterator<Item = TokenTree>`.
78macro_rules! quote_ {
79    (
80        @$q:tt
81        { $($code:tt)* } $($rest:tt)*
82    ) => (
83        $q.push(
84            TokenTree::Group(Group::new(
85                Delimiter::Brace,
86                quote!($($code)*)
87            ))
88        );
89        quote!(@$q $($rest)*);
90    );
91
92    (
93        @$q:tt
94        [ $($code:tt)* ]
95        $($rest:tt)*
96    ) => (
97        $q.push(
98            TokenTree::Group(Group::new(
99                Delimiter::Bracket,
100                quote!($($code)*)
101            ))
102        );
103        quote!(@$q $($rest)*);
104    );
105
106    (
107        @$q:tt
108        ( $($code:tt)* )
109        $($rest:tt)*
110    ) => (
111        $q.push(
112            TokenTree::Group(Group::new(
113                Delimiter::Parenthesis,
114                quote!($($code)*)
115            ))
116        );
117        quote!(@$q $($rest)*);
118    );
119
120    (
121        @$q:tt
122        #$var:ident
123        $($rest:tt)*
124    ) => (
125        $q.extend($var);
126        quote!(@$q $($rest)*);
127    );
128
129    (
130        @$q:tt
131        $tt:tt $($rest:tt)*
132    ) => (
133        $q.extend(
134            stringify!($tt)
135                .parse::<TokenStream>()
136                .unwrap()
137        );
138        quote!(@$q $($rest)*);
139    );
140
141    (
142        @$q:tt
143        /* nothign left */
144    ) => ();
145
146    (
147        $($code:tt)*
148    ) => ({
149        let mut _q = Vec::<TokenTree>::new();
150        quote!(@_q $($code)*);
151        _q.into_iter().collect::<TokenStream>()
152    });
153} use quote_ as quote;