fstrings_rust_proc_macro/
mod.rs

1extern crate proc_macro;
2use ::proc_macro::TokenStream;
3use ::quote::{
4    quote,
5    ToTokens,
6};
7use ::proc_macro2::{
8    TokenStream as TokenStream2,
9};
10use ::syn::{*,
11    parse::{
12        Parse,
13        ParseStream,
14    },
15    punctuated::Punctuated,
16};
17use ::std::ops::Not;
18
19#[macro_use]
20mod macros;
21
22#[allow(dead_code)] // dumb compiler does not see the struct being used...
23struct Input {
24    format_literal: LitStr,
25    positional_args: Vec<Expr>,
26    named_args: Vec<(Ident, Expr)>,
27}
28
29impl Parse for Input {
30    fn parse (input: ParseStream) -> Result<Self>
31    {
32        let format_literal = input.parse()?;
33        let mut positional_args = vec![];
34        loop {
35            if input.parse::<Option<Token![,]>>()?.is_none() {
36                return Ok(Self {
37                    format_literal,
38                    positional_args,
39                    named_args: vec![],
40                });
41            }
42            if  input.peek(Ident) &&
43                input.peek2(Token![=]) &&
44                input.peek3(Token![=]).not()
45            {
46                // Found a positional parameter
47                break;
48            }
49            positional_args.push(input.parse()?);
50        }
51        let named_args =
52            Punctuated::<_, Token![,]>::parse_terminated_with(
53                input,
54                |input| Ok({
55                    let name: Ident = input.parse()?;
56                    let _: Token![=] = input.parse()?;
57                    let expr: Expr = input.parse()?;
58                    (name, expr)
59                }),
60            )?
61            .into_iter()
62            .collect()
63        ;
64        Ok(Self {
65            format_literal,
66            positional_args,
67            named_args,
68        })
69    }
70}
71
72#[::proc_macro_hack::proc_macro_hack] pub
73fn format_args_f (input: TokenStream) -> TokenStream
74{
75    #[allow(unused)]
76    const FUNCTION_NAME: &str = "format_args_f";
77
78    debug_input!(&input);
79
80    let Input {
81        mut format_literal,
82        mut positional_args,
83        mut named_args,
84    } = parse_macro_input!(input);
85
86    let s = format_literal.value();
87    let ref mut out_format_literal = String::with_capacity(s.len());
88
89    // char_indices returns index and char.
90    let mut iterator = s.char_indices().peekable();
91    while let Some((i, c)) = iterator.next() {
92        out_format_literal.push(c);
93        if c != '{' {
94            continue;
95        }
96        // encountered `{`, let's see if it was `{{`
97        if let Some(&(_, '{')) = iterator.peek() {
98            let _ = iterator.next();
99            out_format_literal.push('{');
100            continue;
101        }
102        let (end, colon_or_closing_brace) =
103            iterator
104                .find(|&(_, c)| c == '}' || c == ':')
105                .expect(concat!(
106                    "Invalid format string literal\n",
107                    "note: if you intended to print `{`, ",
108                    "you can escape it using `{{`",
109                ))
110        ;
111        // We use defer to ensure all the `continue`s append the closing char.
112        let mut out_format_literal = defer(
113            &mut *out_format_literal,
114            |it| it.push(colon_or_closing_brace),
115        );
116        let out_format_literal: &mut String = &mut *out_format_literal;
117        let mut arg = s[i + 1 .. end].trim();
118        if let Some("=") = arg.get(arg.len().saturating_sub(1) ..) {
119            assert_eq!(
120                out_format_literal.pop(),  // Remove the opening brace
121                Some('{'),
122            );
123            arg = &arg[.. arg.len() - 1];
124            out_format_literal.push_str(arg);
125            out_format_literal.push_str(" = {");
126        }
127        if arg.is_empty() {
128            continue;
129        }
130
131        enum Segment { Ident(Ident), LitInt(LitInt), Self_(Token![self]) }
132        let segments: Vec<Segment> = {
133            impl Parse for Segment {
134                fn parse (input: ParseStream<'_>)
135                  -> Result<Self>
136                {
137                    let lookahead = input.lookahead1();
138                    if lookahead.peek(Ident) {
139                        input.parse().map(Segment::Ident)
140                    } else if lookahead.peek(LitInt) {
141                        input.parse().map(Segment::LitInt)
142                    } else if input.peek(Token![self]){
143                        input.parse().map(Segment::Self_)
144                    } else {
145                        Err(lookahead.error())
146                    }
147                }
148            }
149            match ::syn::parse::Parser::parse_str(
150                Punctuated::<Segment, Token![.]>::parse_separated_nonempty,
151                arg,
152            )
153            {
154                | Ok(segments) => segments.into_iter().collect(),
155                | Err(err) => return err.to_compile_error().into(),
156            }
157        };
158        match segments.len() {
159            | 0 => unreachable!("`parse_separated_nonempty` returned empty"),
160            | 1 => {
161                out_format_literal.push_str(arg);
162                match {segments}.pop().unwrap() {
163                    | Segment::LitInt(_) => {
164                        // found something like `{0}`, let `format_args!`
165                        // handle it.
166                        continue;
167                    },
168                    | Segment::Ident(ident) => {
169                        // if `ident = ...` is not yet among the extra args
170                        if  named_args
171                                .iter()
172                                .all(|(it, _)| *it != ident)
173                        {
174                            named_args.push((
175                                ident.clone(),
176                                parse_quote!(#ident), // Expr
177                            ));
178                        }
179                    },
180                    | Segment::Self_(ident) => {
181                        // if `ident = ...` is not yet among the extra args
182                        continue;
183                    },
184                }
185            },
186            | _ => {
187                ::std::fmt::Write::write_fmt(
188                    out_format_literal,
189                    format_args!("{}", positional_args.len()),
190                ).expect("`usize` or `char` Display impl cannot panic");
191                let segments: Punctuated<TokenStream2, Token![.]> =
192                    segments
193                        .into_iter()
194                        .map(|it| match it {
195                            | Segment::Ident(ident) => {
196                                ident.into_token_stream()
197                            },
198                            | Segment::LitInt(literal) => {
199                                literal.into_token_stream()
200                            },
201                            | Segment::Self_(self_) => {
202                                self_.into_token_stream()
203                            },
204                        })
205                        .collect()
206                ;
207                positional_args.push(parse_quote! {
208                    #segments
209                })
210            }
211        }
212    }
213
214    let named_args =
215        named_args
216            .into_iter()
217            .map(|(ident, expr)| quote! {
218                #ident = #expr
219            })
220    ;
221    format_literal = LitStr::new(
222        out_format_literal,
223        format_literal.span(),
224    );
225    TokenStream::from(debug_output!(quote! {
226        format_args!(
227            #format_literal
228            #(, #positional_args)*
229            #(, #named_args)*
230        )
231    }))
232}
233
234fn defer<'a, T : 'a, Drop : 'a> (x: T, drop: Drop)
235  -> impl ::core::ops::DerefMut<Target = T> + 'a
236where
237    Drop : FnOnce(T),
238{
239    use ::core::mem::ManuallyDrop;
240    struct Ret<T, Drop> (
241        ManuallyDrop<T>,
242        ManuallyDrop<Drop>,
243    )
244    where
245        Drop : FnOnce(T),
246    ;
247    impl<T, Drop> ::core::ops::Drop for Ret<T, Drop>
248    where
249        Drop : FnOnce(T),
250    {
251        fn drop (self: &'_ mut Self)
252        {
253            use ::core::ptr;
254            unsafe {
255                // # Safety
256                //
257                //   - This is the canonical example of using `ManuallyDrop`.
258                let value = ManuallyDrop::into_inner(ptr::read(&mut self.0));
259                let drop = ManuallyDrop::into_inner(ptr::read(&mut self.1));
260                drop(value);
261            }
262        }
263    }
264    impl<T, Drop> ::core::ops::Deref for Ret<T, Drop>
265    where
266        Drop : FnOnce(T),
267    {
268        type Target = T;
269        #[inline]
270        fn deref (self: &'_ Self)
271          -> &'_ Self::Target
272        {
273            &self.0
274        }
275    }
276    impl<T, Drop> ::core::ops::DerefMut for Ret<T, Drop>
277    where
278        Drop : FnOnce(T),
279    {
280        #[inline]
281        fn deref_mut (self: &'_ mut Self)
282          -> &'_ mut Self::Target
283        {
284            &mut self.0
285        }
286    }
287    Ret(ManuallyDrop::new(x), ManuallyDrop::new(drop))
288}