fstrings_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    let mut iterator = s.char_indices().peekable();
90    while let Some((i, c)) = iterator.next() {
91        out_format_literal.push(c);
92        if c != '{' {
93            continue;
94        }
95        // encountered `{`, let's see if it was `{{`
96        if let Some(&(_, '{')) = iterator.peek() {
97            let _ = iterator.next();
98            out_format_literal.push('{');
99            continue;
100        }
101        let (end, colon_or_closing_brace) =
102            iterator
103                .find(|&(_, c)| c == '}' || c == ':')
104                .expect(concat!(
105                    "Invalid format string literal\n",
106                    "note: if you intended to print `{`, ",
107                    "you can escape it using `{{`",
108                ))
109        ;
110        // We use defer to ensure all the `continue`s append the closing char.
111        let mut out_format_literal = defer(
112            &mut *out_format_literal,
113            |it| it.push(colon_or_closing_brace),
114        );
115        let out_format_literal: &mut String = &mut *out_format_literal;
116        let mut arg = s[i + 1 .. end].trim();
117        if let Some("=") = arg.get(arg.len().saturating_sub(1) ..) {
118            assert_eq!(
119                out_format_literal.pop(),  // Remove the opening brace
120                Some('{'),
121            );
122            arg = &arg[.. arg.len() - 1];
123            out_format_literal.push_str(arg);
124            out_format_literal.push_str(" = {");
125        }
126        if arg.is_empty() {
127            continue;
128        }
129
130        enum Segment { Ident(Ident), LitInt(LitInt) }
131        let segments: Vec<Segment> = {
132            impl Parse for Segment {
133                fn parse (input: ParseStream<'_>)
134                  -> Result<Self>
135                {
136                    let lookahead = input.lookahead1();
137                    if lookahead.peek(Ident) {
138                        input.parse().map(Segment::Ident)
139                    } else if lookahead.peek(LitInt) {
140                        input.parse().map(Segment::LitInt)
141                    } else {
142                        Err(lookahead.error())
143                    }
144                }
145            }
146            match ::syn::parse::Parser::parse_str(
147                Punctuated::<Segment, Token![.]>::parse_separated_nonempty,
148                arg,
149            )
150            {
151                | Ok(segments) => segments.into_iter().collect(),
152                | Err(err) => return err.to_compile_error().into(),
153            }
154        };
155        match segments.len() {
156            | 0 => unreachable!("`parse_separated_nonempty` returned empty"),
157            | 1 => {
158                out_format_literal.push_str(arg);
159                match {segments}.pop().unwrap() {
160                    | Segment::LitInt(_) => {
161                        // found something like `{0}`, let `format_args!`
162                        // handle it.
163                        continue;
164                    },
165                    | Segment::Ident(ident) => {
166                        // if `ident = ...` is not yet among the extra args
167                        if  named_args
168                                .iter()
169                                .all(|(it, _)| *it != ident)
170                        {
171                            named_args.push((
172                                ident.clone(),
173                                parse_quote!(#ident), // Expr
174                            ));
175                        }
176                    },
177                }
178            },
179            | _ => {
180                ::std::fmt::Write::write_fmt(
181                    out_format_literal,
182                    format_args!("{}", positional_args.len()),
183                ).expect("`usize` or `char` Display impl cannot panic");
184                let segments: Punctuated<TokenStream2, Token![.]> =
185                    segments
186                        .into_iter()
187                        .map(|it| match it {
188                            | Segment::Ident(ident) => {
189                                ident.into_token_stream()
190                            },
191                            | Segment::LitInt(literal) => {
192                                literal.into_token_stream()
193                            },
194                        })
195                        .collect()
196                ;
197                positional_args.push(parse_quote! {
198                    #segments
199                })
200            }
201        }
202    }
203
204    let named_args =
205        named_args
206            .into_iter()
207            .map(|(ident, expr)| quote! {
208                #ident = #expr
209            })
210    ;
211    format_literal = LitStr::new(
212        out_format_literal,
213        format_literal.span(),
214    );
215    TokenStream::from(debug_output!(quote! {
216        format_args!(
217            #format_literal
218            #(, #positional_args)*
219            #(, #named_args)*
220        )
221    }))
222}
223
224fn defer<'a, T : 'a, Drop : 'a> (x: T, drop: Drop)
225  -> impl ::core::ops::DerefMut<Target = T> + 'a
226where
227    Drop : FnOnce(T),
228{
229    use ::core::mem::ManuallyDrop;
230    struct Ret<T, Drop> (
231        ManuallyDrop<T>,
232        ManuallyDrop<Drop>,
233    )
234    where
235        Drop : FnOnce(T),
236    ;
237    impl<T, Drop> ::core::ops::Drop for Ret<T, Drop>
238    where
239        Drop : FnOnce(T),
240    {
241        fn drop (self: &'_ mut Self)
242        {
243            use ::core::ptr;
244            unsafe {
245                // # Safety
246                //
247                //   - This is the canonical example of using `ManuallyDrop`.
248                let value = ManuallyDrop::into_inner(ptr::read(&mut self.0));
249                let drop = ManuallyDrop::into_inner(ptr::read(&mut self.1));
250                drop(value);
251            }
252        }
253    }
254    impl<T, Drop> ::core::ops::Deref for Ret<T, Drop>
255    where
256        Drop : FnOnce(T),
257    {
258        type Target = T;
259        #[inline]
260        fn deref (self: &'_ Self)
261          -> &'_ Self::Target
262        {
263            &self.0
264        }
265    }
266    impl<T, Drop> ::core::ops::DerefMut for Ret<T, Drop>
267    where
268        Drop : FnOnce(T),
269    {
270        #[inline]
271        fn deref_mut (self: &'_ mut Self)
272          -> &'_ mut Self::Target
273        {
274            &mut self.0
275        }
276    }
277    Ret(ManuallyDrop::new(x), ManuallyDrop::new(drop))
278}