]> git.lizzy.rs Git - rust.git/commitdiff
Specific error for positional args after named args in `format!()`
authorEsteban Küber <esteban@kuber.com.ar>
Tue, 16 Jul 2019 03:51:32 +0000 (20:51 -0700)
committerEsteban Küber <esteban@kuber.com.ar>
Tue, 16 Jul 2019 03:51:32 +0000 (20:51 -0700)
When writing positional arguments after named arguments in the
`format!()` and `println!()` macros, provide a targeted diagnostic.

src/libsyntax_ext/format.rs
src/test/ui/if/ifmt-bad-arg.rs
src/test/ui/if/ifmt-bad-arg.stderr
src/test/ui/macros/format-parse-errors.rs
src/test/ui/macros/format-parse-errors.stderr

index c3dbd48cc6e4e8bbebcfd83138c2e8b7fcc2e9c7..3f3f647c82692970204cfcebce456fa1ba07ca26 100644 (file)
@@ -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 @@ fn find_skips(snippet: &str, is_raw: bool) -> Vec<usize> {
         .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 @@ fn find_skips(snippet: &str, is_raw: bool) -> Vec<usize> {
     }
 
     // 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 {
index a57221af916be47e4e68acf0f3ed217fcccb0261..0ebe1fa2dff929f6735ba577bf7738efabbf5a0a 100644 (file)
@@ -38,7 +38,7 @@ fn main() {
     format!("{} {}", 1, 2, foo=1, bar=2);  //~ ERROR: multiple unused formatting arguments
 
     format!("{foo}", foo=1, foo=2);  //~ ERROR: duplicate argument
-    format!("", foo=1, 2);           //~ ERROR: positional arguments cannot follow
+    format!("{foo} {} {}", foo=1, 2);   //~ ERROR: positional arguments cannot follow
 
     // bad named arguments, #35082
 
index 65be86eaf257500160b8aa3b1965993b8e8c87e4..835b5b6592b786485b42e529193aea677bbe3424 100644 (file)
@@ -146,11 +146,13 @@ note: previously here
 LL |     format!("{foo}", foo=1, foo=2);
    |                          ^
 
-error: expected ident, positional arguments cannot follow named arguments
-  --> $DIR/ifmt-bad-arg.rs:41:24
+error: positional arguments cannot follow named arguments
+  --> $DIR/ifmt-bad-arg.rs:41:35
    |
-LL |     format!("", foo=1, 2);
-   |                        ^
+LL |     format!("{foo} {} {}", foo=1, 2);
+   |                                -  ^ positional arguments must be before named arguments
+   |                                |
+   |                                named argument
 
 error: there is no argument named `valueb`
   --> $DIR/ifmt-bad-arg.rs:45:23
index 96aee5e6aeed5cc9c9561f90ccd1f61d2661c6f4..ffa7a2817ff36fcbc78174329aac737ecab532d6 100644 (file)
@@ -1,9 +1,15 @@
 fn main() {
+    let foo = "";
+    let bar = "";
     format!(); //~ ERROR requires at least a format string argument
     format!(struct); //~ ERROR expected expression
     format!("s", name =); //~ ERROR expected expression
-    format!("s", foo = foo, bar); //~ ERROR expected `=`
-    format!("s", foo = struct); //~ ERROR expected expression
+    format!(
+        "s {foo} {} {}",
+        foo = foo,
+        bar, //~ ERROR positional arguments cannot follow named arguments
+    );
+    format!("s {foo}", foo = struct); //~ ERROR expected expression
     format!("s", struct); //~ ERROR expected expression
 
     // This error should come after parsing errors to ensure they are non-fatal.
index fd4f93091944cd5af20d48bfad1f05a82f47584d..906738d738232254df52966407baeee33a9d2f29 100644 (file)
@@ -1,5 +1,5 @@
 error: requires at least a format string argument
-  --> $DIR/format-parse-errors.rs:2:5
+  --> $DIR/format-parse-errors.rs:4:5
    |
 LL |     format!();
    |     ^^^^^^^^^^
@@ -7,37 +7,39 @@ LL |     format!();
    = note: this error originates in a macro outside of the current crate (in Nightly builds, run with -Z external-macro-backtrace for more info)
 
 error: expected expression, found keyword `struct`
-  --> $DIR/format-parse-errors.rs:3:13
+  --> $DIR/format-parse-errors.rs:5:13
    |
 LL |     format!(struct);
    |             ^^^^^^ expected expression
 
 error: expected expression, found end of macro arguments
-  --> $DIR/format-parse-errors.rs:4:24
+  --> $DIR/format-parse-errors.rs:6:24
    |
 LL |     format!("s", name =);
    |                        ^ expected expression
 
-error: expected `=`, found end of macro arguments
-  --> $DIR/format-parse-errors.rs:5:32
+error: positional arguments cannot follow named arguments
+  --> $DIR/format-parse-errors.rs:10:9
    |
-LL |     format!("s", foo = foo, bar);
-   |                                ^ expected `=`
+LL |         foo = foo,
+   |               --- named argument
+LL |         bar,
+   |         ^^^ positional arguments must be before named arguments
 
 error: expected expression, found keyword `struct`
-  --> $DIR/format-parse-errors.rs:6:24
+  --> $DIR/format-parse-errors.rs:12:30
    |
-LL |     format!("s", foo = struct);
-   |                        ^^^^^^ expected expression
+LL |     format!("s {foo}", foo = struct);
+   |                              ^^^^^^ expected expression
 
 error: expected expression, found keyword `struct`
-  --> $DIR/format-parse-errors.rs:7:18
+  --> $DIR/format-parse-errors.rs:13:18
    |
 LL |     format!("s", struct);
    |                  ^^^^^^ expected expression
 
 error: format argument must be a string literal
-  --> $DIR/format-parse-errors.rs:10:13
+  --> $DIR/format-parse-errors.rs:16:13
    |
 LL |     format!(123);
    |             ^^^