fstrings_rust_proc_macro/
mod.rs1extern 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)] struct 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 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();
91 while let Some((i, c)) = iterator.next() {
92 out_format_literal.push(c);
93 if c != '{' {
94 continue;
95 }
96 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 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(), 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 continue;
167 },
168 | Segment::Ident(ident) => {
169 if named_args
171 .iter()
172 .all(|(it, _)| *it != ident)
173 {
174 named_args.push((
175 ident.clone(),
176 parse_quote!(#ident), ));
178 }
179 },
180 | Segment::Self_(ident) => {
181 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 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}