diff options
| author | Esteban Küber <esteban@kuber.com.ar> | 2019-07-15 20:51:32 -0700 |
|---|---|---|
| committer | Esteban Küber <esteban@kuber.com.ar> | 2019-07-15 20:51:32 -0700 |
| commit | 33ec1823d758c08fd7f2eaddabe083dfb3c4b26f (patch) | |
| tree | eeb359479f56e4c5320c71cb10cb3f7a7bddf846 /src/libsyntax_ext/format.rs | |
| parent | 4b65a86ebace8600c8e269e8bfe3365cdc460e68 (diff) | |
| download | rust-33ec1823d758c08fd7f2eaddabe083dfb3c4b26f.tar.gz rust-33ec1823d758c08fd7f2eaddabe083dfb3c4b26f.zip | |
Specific error for positional args after named args in `format!()`
When writing positional arguments after named arguments in the `format!()` and `println!()` macros, provide a targeted diagnostic.
Diffstat (limited to 'src/libsyntax_ext/format.rs')
| -rw-r--r-- | src/libsyntax_ext/format.rs | 39 |
1 files changed, 24 insertions, 15 deletions
diff --git a/src/libsyntax_ext/format.rs b/src/libsyntax_ext/format.rs index c3dbd48cc6e..3f3f647c826 100644 --- a/src/libsyntax_ext/format.rs +++ b/src/libsyntax_ext/format.rs @@ -146,16 +146,13 @@ fn parse_args<'a>( if p.token == token::Eof { break; } // accept trailing commas - if named || (p.token.is_ident() && p.look_ahead(1, |t| *t == token::Eq)) { + if p.token.is_ident() && p.look_ahead(1, |t| *t == token::Eq) { named = true; let name = if let token::Ident(name, _) = p.token.kind { p.bump(); name } else { - return Err(ecx.struct_span_err( - p.token.span, - "expected ident, positional arguments cannot follow named arguments", - )); + unreachable!(); }; p.expect(&token::Eq)?; @@ -176,6 +173,17 @@ fn parse_args<'a>( args.push(e); } else { let e = p.parse_expr()?; + if named { + let mut err = ecx.struct_span_err( + e.span, + "positional arguments cannot follow named arguments", + ); + err.span_label(e.span, "positional arguments must be before named arguments"); + for (_, pos) in &names { + err.span_label(args[*pos].span, "named argument"); + } + err.emit(); + } args.push(e); } } @@ -721,13 +729,14 @@ pub fn expand_format_args_nl<'cx>( /// Take the various parts of `format_args!(efmt, args..., name=names...)` /// and construct the appropriate formatting expression. -pub fn expand_preparsed_format_args(ecx: &mut ExtCtxt<'_>, - sp: Span, - efmt: P<ast::Expr>, - args: Vec<P<ast::Expr>>, - names: FxHashMap<Symbol, usize>, - append_newline: bool) - -> P<ast::Expr> { +pub fn expand_preparsed_format_args( + ecx: &mut ExtCtxt<'_>, + sp: Span, + efmt: P<ast::Expr>, + args: Vec<P<ast::Expr>>, + names: FxHashMap<Symbol, usize>, + append_newline: bool, +) -> P<ast::Expr> { // NOTE: this verbose way of initializing `Vec<Vec<ArgumentType>>` is because // `ArgumentType` does not derive `Clone`. let arg_types: Vec<_> = (0..args.len()).map(|_| Vec::new()).collect(); @@ -906,6 +915,8 @@ pub fn expand_preparsed_format_args(ecx: &mut ExtCtxt<'_>, .map(|span| fmt.span.from_inner(*span)) .collect(); + let named_pos: FxHashSet<usize> = names.values().cloned().collect(); + let mut cx = Context { ecx, args, @@ -971,14 +982,12 @@ pub fn expand_preparsed_format_args(ecx: &mut ExtCtxt<'_>, } // Make sure that all arguments were used and all arguments have types. - let num_pos_args = cx.args.len() - cx.names.len(); - let errs = cx.arg_types .iter() .enumerate() .filter(|(i, ty)| ty.is_empty() && !cx.count_positions.contains_key(&i)) .map(|(i, _)| { - let msg = if i >= num_pos_args { + let msg = if named_pos.contains(&i) { // named argument "named argument never used" } else { |
