1 | use super::{ |
2 | ExpandFormatted, ExpandInto, ExpandWithFormatter, FormatArg, FormatArgs, FormatIfArgs, |
3 | LocalVariable, UncheckedFormatArg, UncheckedFormatArgs, WriteArgs, |
4 | }; |
5 | |
6 | use crate::{ |
7 | format_str::{FmtArg, FmtStrComponent, FormatStr, WhichArg}, |
8 | parse_utils::{LitStr, MyParse, ParseBuffer, ParseStream, TokenTreeExt}, |
9 | shared_arg_parsing::ExprArg, |
10 | spanned::Spans, |
11 | utils::{dummy_ident, LinearResult}, |
12 | }; |
13 | |
14 | use proc_macro2::{Ident, Span, TokenTree}; |
15 | |
16 | //////////////////////////////////////////////// |
17 | |
18 | impl MyParse for UncheckedFormatArg { |
19 | fn parse(input: ParseStream<'_>) -> Result<Self, crate::Error> { |
20 | // the compile wraps `:expr` in macro_rules macros in a TokenStream::Group |
21 | // with no delimiters. |
22 | input.parse_unwrap_group(|content| { |
23 | let mut ident = None; |
24 | if matches!(content.peek2(), Some(x) if x.is_punct('=' )) { |
25 | ident = Some(content.parse_ident()?); |
26 | content.next(); |
27 | } |
28 | |
29 | // For some reason, |
30 | // the compile wraps closures in parentheses when passing them as |
31 | // expressions to proc macros. |
32 | content.parse_unwrap_paren(|content| { |
33 | let mut fmt_ident = None; |
34 | |
35 | if matches!(content.peek(), Some(x) if x.is_punct('|' )) |
36 | && matches!(content.peek2(), Some(TokenTree::Ident(_))) |
37 | { |
38 | content.next(); |
39 | fmt_ident = Some(content.parse_ident()?); |
40 | content.parse_punct('|' )?; |
41 | } |
42 | |
43 | let (expr, spans) = if content.peek2().is_some() { |
44 | content.parse_token_stream_and_span() |
45 | } else { |
46 | content.parse_unwrap_tt(|content| Ok(content.parse_token_stream_and_span()))? |
47 | }; |
48 | |
49 | Ok(Self { |
50 | spans, |
51 | ident, |
52 | fmt_ident, |
53 | expr, |
54 | }) |
55 | }) |
56 | }) |
57 | } |
58 | } |
59 | |
60 | //////////////////////////////////////////////// |
61 | |
62 | fn lit_str_to_fmt_lit(lit: &LitStr) -> Result<FormatStr, crate::Error> { |
63 | let lit_str: &str = lit.value(); |
64 | let format_str_span: Span = lit.span; |
65 | FormatStr::parse(lit.value(), lit.rawness) |
66 | .map_err(|e: ParseError| e.into_crate_err(format_str_span, original_str:lit_str)) |
67 | } |
68 | |
69 | fn parse_fmt_lit(this: &mut FormatStr, input: ParseStream<'_>) -> Result<(), crate::Error> { |
70 | input.parse_unwrap_tt(|input| { |
71 | let tt = input.next(); |
72 | |
73 | match tt { |
74 | Some(TokenTree::Literal(lit)) => { |
75 | let mut lit = lit_str_to_fmt_lit(&LitStr::parse_from_literal(&lit)?)?; |
76 | |
77 | this.list.append(&mut lit.list); |
78 | |
79 | Ok(()) |
80 | } |
81 | Some(TokenTree::Ident(ident)) if ident == "concat" => { |
82 | input.next(); // skipping the `!` |
83 | let paren = input.parse_paren()?; |
84 | let mut input = ParseBuffer::new(paren.contents); |
85 | |
86 | while !input.is_empty() { |
87 | parse_fmt_lit(this, &mut input)?; |
88 | input.parse_opt_punct(',' )?; |
89 | } |
90 | Ok(()) |
91 | } |
92 | _ => Ok(()), |
93 | } |
94 | }) |
95 | } |
96 | |
97 | impl MyParse for UncheckedFormatArgs { |
98 | fn parse(input: ParseStream<'_>) -> Result<Self, crate::Error> { |
99 | let mut literal = FormatStr { list: Vec::new() }; |
100 | |
101 | // Have to parse `concat!()` because it's not expanded before the proc macro is called. |
102 | { |
103 | let paren = input.parse_paren()?; |
104 | let mut input = ParseBuffer::new(paren.contents); |
105 | |
106 | parse_fmt_lit(&mut literal, &mut input)?; |
107 | } |
108 | |
109 | input.parse_opt_punct(',' )?; |
110 | |
111 | let mut args = Vec::new(); |
112 | |
113 | while !input.is_empty() { |
114 | args.push(UncheckedFormatArg::parse(input)?); |
115 | |
116 | input.parse_opt_punct(',' )?; |
117 | } |
118 | |
119 | Ok(Self { literal, args }) |
120 | } |
121 | } |
122 | |
123 | //////////////////////////////////////////////// |
124 | |
125 | impl MyParse for FormatArgs { |
126 | fn parse(input: ParseStream<'_>) -> Result<Self, crate::Error> { |
127 | let prefix: Ident = Ident::new(string:"__const_fmt_local_" , Span::call_site()); |
128 | FormatArgs::parse_with(input, prefix) |
129 | } |
130 | } |
131 | |
132 | impl FormatArgs { |
133 | pub fn parse_with(input: ParseStream<'_>, prefix: Ident) -> Result<FormatArgs, crate::Error> { |
134 | let mut res = LinearResult::ok(); |
135 | |
136 | let unchecked_fargs = UncheckedFormatArgs::parse(input)?; |
137 | |
138 | let mut first_named_arg = unchecked_fargs.args.len(); |
139 | |
140 | let mut named_arg_names = Vec::<Ident>::new(); |
141 | let mut args = Vec::<FormatArg>::with_capacity(unchecked_fargs.args.len()); |
142 | let mut local_variables = Vec::<LocalVariable>::with_capacity(unchecked_fargs.args.len()); |
143 | |
144 | let arg_span_idents: Vec<(Spans, Option<Ident>)> = unchecked_fargs |
145 | .args |
146 | .iter() |
147 | .map(|x| (x.spans, x.ident.clone())) |
148 | .collect(); |
149 | |
150 | { |
151 | let mut prev_is_named_arg = false; |
152 | for (i, arg) in unchecked_fargs.args.into_iter().enumerate() { |
153 | let expr_span = arg.spans; |
154 | |
155 | let make_ident = |s: String| Ident::new(&s, expr_span.start); |
156 | |
157 | let is_named_arg = arg.ident.is_some(); |
158 | |
159 | let var_name = if let Some(ident) = arg.ident { |
160 | if !prev_is_named_arg { |
161 | first_named_arg = i; |
162 | } |
163 | |
164 | let name = make_ident(format!(" {}{}" , prefix, ident)); |
165 | named_arg_names.push(ident); |
166 | name |
167 | } else { |
168 | if prev_is_named_arg { |
169 | return Err(crate::Error::spanned( |
170 | arg.spans, |
171 | "expected a named argument, \ |
172 | named arguments cannot be followed by positional arguments." , |
173 | )); |
174 | } |
175 | |
176 | make_ident(format!(" {}{}" , prefix, i)) |
177 | }; |
178 | |
179 | let format_arg = if let Some(fmt_ident) = &arg.fmt_ident { |
180 | FormatArg::WithFormatter { |
181 | fmt_ident: fmt_ident.clone(), |
182 | expr: arg.expr.clone(), |
183 | } |
184 | } else { |
185 | local_variables.push(LocalVariable { |
186 | ident: var_name.clone(), |
187 | expr: arg.expr.clone(), |
188 | }); |
189 | |
190 | FormatArg::WithLocal(var_name) |
191 | }; |
192 | |
193 | args.push(format_arg); |
194 | |
195 | prev_is_named_arg = is_named_arg; |
196 | } |
197 | } |
198 | |
199 | let mut unused_args = vec![true; args.len()]; |
200 | |
201 | let first_named_arg = first_named_arg; |
202 | let named_arg_names = named_arg_names; |
203 | let args = args; |
204 | |
205 | let positional_args = &args[..first_named_arg]; |
206 | let named_args = &args[first_named_arg..]; |
207 | |
208 | let fmt_str_components = unchecked_fargs.literal.list; |
209 | |
210 | let expanded_into: Vec<ExpandInto> = { |
211 | let mut current_pos_arg = 0; |
212 | let mut get_variable_name = |param: FmtArg| -> ExpandInto { |
213 | let FmtArg { |
214 | which_arg, |
215 | formatting, |
216 | rawness, |
217 | } = param; |
218 | |
219 | let arg = match which_arg { |
220 | WhichArg::Ident(ident) => { |
221 | if let Some(pos) = named_arg_names.iter().position(|x| *x == ident) { |
222 | unused_args[pos + first_named_arg] = false; |
223 | &named_args[pos] |
224 | } else { |
225 | // `formatcp!("{FOO}")` assumes that FOO is a constant in scope |
226 | return ExpandInto::Formatted(ExpandFormatted { |
227 | local_variable: Ident::new(&ident, rawness.span()), |
228 | format: formatting, |
229 | }); |
230 | } |
231 | } |
232 | WhichArg::Positional(opt_pos) => { |
233 | let pos = opt_pos.unwrap_or_else(|| { |
234 | let pos = current_pos_arg; |
235 | current_pos_arg += 1; |
236 | pos |
237 | }); |
238 | |
239 | match positional_args.get(pos) { |
240 | Some(arg) => { |
241 | unused_args[pos] = false; |
242 | arg |
243 | } |
244 | None => { |
245 | res.push_err(crate::Error::new( |
246 | rawness.span(), |
247 | format!( |
248 | "attempting to use nonexistent positional argument ` {}`" , |
249 | pos, |
250 | ), |
251 | )); |
252 | return ExpandInto::Formatted(ExpandFormatted { |
253 | local_variable: dummy_ident(), |
254 | format: formatting, |
255 | }); |
256 | } |
257 | } |
258 | } |
259 | }; |
260 | |
261 | match arg { |
262 | FormatArg::WithFormatter { fmt_ident, expr } => { |
263 | ExpandInto::WithFormatter(ExpandWithFormatter { |
264 | format: formatting, |
265 | fmt_ident: fmt_ident.clone(), |
266 | expr: expr.clone(), |
267 | }) |
268 | } |
269 | FormatArg::WithLocal(local_variable) => { |
270 | ExpandInto::Formatted(ExpandFormatted { |
271 | format: formatting, |
272 | local_variable: local_variable.clone(), |
273 | }) |
274 | } |
275 | } |
276 | }; |
277 | |
278 | fmt_str_components |
279 | .into_iter() |
280 | .map(|fmt_str_comp| match fmt_str_comp { |
281 | FmtStrComponent::Str(str, str_rawness) => ExpandInto::Str(str, str_rawness), |
282 | FmtStrComponent::Arg(arg) => get_variable_name(arg), |
283 | }) |
284 | .collect() |
285 | }; |
286 | |
287 | for (i, (is_it_unused, (spans, ident))) in |
288 | unused_args.iter().zip(&arg_span_idents).enumerate() |
289 | { |
290 | if *is_it_unused { |
291 | let msg = if let Some(ident) = ident { |
292 | format!("the ' {}' argument is unused" , ident) |
293 | } else { |
294 | format!("argument number {} is unused" , i) |
295 | }; |
296 | res.push_err(crate::Error::spanned(*spans, msg)); |
297 | } |
298 | } |
299 | res.take()?; |
300 | |
301 | Ok(FormatArgs { |
302 | condition: None, |
303 | local_variables, |
304 | expanded_into, |
305 | }) |
306 | } |
307 | } |
308 | |
309 | //////////////////////////////////////////////// |
310 | |
311 | impl MyParse for FormatIfArgs { |
312 | fn parse(input: ParseStream) -> Result<Self, crate::Error> { |
313 | let condition: ExprArg = ExprArg::parse(input)?; |
314 | |
315 | let mut inner: FormatArgs = FormatArgs::parse(input)?; |
316 | inner.condition = Some(condition); |
317 | |
318 | Ok(Self { inner }) |
319 | } |
320 | } |
321 | |
322 | //////////////////////////////////////////////// |
323 | |
324 | impl MyParse for WriteArgs { |
325 | fn parse(input: ParseStream) -> Result<Self, crate::Error> { |
326 | let prefix: Ident = Ident::new(string:"__const_fmt_local_" , Span::call_site()); |
327 | |
328 | let paren: Parentheses = input.parse_paren()?; |
329 | |
330 | let mut content: ParseBuffer = ParseBuffer::new(ts:paren.contents); |
331 | |
332 | let (writer_expr: TokenStream, spans: Spans) = |
333 | content.parse_unwrap_tt(|content: &mut ParseBuffer| Ok(content.parse_token_stream_and_span()))?; |
334 | |
335 | let format_args: FormatArgs = FormatArgs::parse_with(input, prefix)?; |
336 | |
337 | Ok(Self { |
338 | writer_expr, |
339 | writer_span: spans.joined(), |
340 | format_args, |
341 | }) |
342 | } |
343 | } |
344 | |