const_format_proc_macros/format_args/
parsing.rs1use super::{
2 ExpandFormatted, ExpandInto, ExpandWithFormatter, FormatArg, FormatArgs, FormatIfArgs,
3 LocalVariable, UncheckedFormatArg, UncheckedFormatArgs, WriteArgs,
4};
5
6use 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
14use proc_macro2::{Ident, Span, TokenTree};
15
16impl MyParse for UncheckedFormatArg {
19 fn parse(input: ParseStream<'_>) -> Result<Self, crate::Error> {
20 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 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
60fn lit_str_to_fmt_lit(lit: &LitStr) -> Result<FormatStr, crate::Error> {
63 let lit_str = lit.value();
64 let format_str_span = lit.span;
65 FormatStr::parse(lit.value(), lit.rawness)
66 .map_err(|e| e.into_crate_err(format_str_span, lit_str))
67}
68
69fn 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(); 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
97impl MyParse for UncheckedFormatArgs {
98 fn parse(input: ParseStream<'_>) -> Result<Self, crate::Error> {
99 let mut literal = FormatStr { list: Vec::new() };
100
101 {
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
123impl MyParse for FormatArgs {
126 fn parse(input: ParseStream<'_>) -> Result<Self, crate::Error> {
127 let prefix = Ident::new("__const_fmt_local_", Span::call_site());
128 FormatArgs::parse_with(input, prefix)
129 }
130}
131
132impl 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 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
309impl MyParse for FormatIfArgs {
312 fn parse(input: ParseStream) -> Result<Self, crate::Error> {
313 let condition = ExprArg::parse(input)?;
314
315 let mut inner = FormatArgs::parse(input)?;
316 inner.condition = Some(condition);
317
318 Ok(Self { inner })
319 }
320}
321
322impl MyParse for WriteArgs {
325 fn parse(input: ParseStream) -> Result<Self, crate::Error> {
326 let prefix = Ident::new("__const_fmt_local_", Span::call_site());
327
328 let paren = input.parse_paren()?;
329
330 let mut content = ParseBuffer::new(paren.contents);
331
332 let (writer_expr, spans) =
333 content.parse_unwrap_tt(|content| Ok(content.parse_token_stream_and_span()))?;
334
335 let format_args = FormatArgs::parse_with(input, prefix)?;
336
337 Ok(Self {
338 writer_expr,
339 writer_span: spans.joined(),
340 format_args,
341 })
342 }
343}