| 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 | |