]> git.lizzy.rs Git - rust.git/commitdiff
Point at internal span in format string
authorEsteban Küber <esteban@commure.com>
Mon, 23 Jul 2018 05:40:24 +0000 (22:40 -0700)
committerEsteban Küber <esteban@commure.com>
Mon, 23 Jul 2018 06:09:00 +0000 (23:09 -0700)
src/libfmt_macros/lib.rs
src/libsyntax_ext/format.rs
src/test/compile-fail/ifmt-bad-arg.rs [deleted file]
src/test/ui/ifmt-bad-arg.rs [new file with mode: 0644]
src/test/ui/ifmt-bad-arg.stderr [new file with mode: 0644]
src/test/ui/macros/macro-backtrace-println.stderr

index 9952e5f64d6ab2f59e9ca303e99b69577aaa5689..f6dcebf8c50f56337718ecb6b62a80b6773e19b5 100644 (file)
@@ -154,6 +154,7 @@ pub struct Parser<'a> {
     style: Option<usize>,
     /// How many newlines have been seen in the string so far, to adjust the error spans
     seen_newlines: usize,
+    pub arg_places: Vec<(usize, usize)>,
 }
 
 impl<'a> Iterator for Parser<'a> {
@@ -168,9 +169,13 @@ fn next(&mut self) -> Option<Piece<'a>> {
                     if self.consume('{') {
                         Some(String(self.string(pos + 1)))
                     } else {
-                        let ret = Some(NextArgument(self.argument()));
-                        self.must_consume('}');
-                        ret
+                        let mut arg = self.argument();
+                        if let Some(arg_pos) = self.must_consume('}').map(|end| {
+                            (pos + raw + 1, end + raw + 2)
+                        }) {
+                            self.arg_places.push(arg_pos);
+                        }
+                        Some(NextArgument(arg))
                     }
                 }
                 '}' => {
@@ -211,6 +216,7 @@ pub fn new(s: &'a str, style: Option<usize>) -> Parser<'a> {
             curarg: 0,
             style,
             seen_newlines: 0,
+            arg_places: vec![],
         }
     }
 
@@ -271,7 +277,7 @@ fn consume(&mut self, c: char) -> bool {
 
     /// Forces consumption of the specified character. If the character is not
     /// found, an error is emitted.
-    fn must_consume(&mut self, c: char) {
+    fn must_consume(&mut self, c: char) -> Option<usize> {
         self.ws();
         let raw = self.style.unwrap_or(0);
 
@@ -279,12 +285,14 @@ fn must_consume(&mut self, c: char) {
         if let Some(&(pos, maybe)) = self.cur.peek() {
             if c == maybe {
                 self.cur.next();
+                Some(pos)
             } else {
                 let pos = pos + padding + 1;
                 self.err(format!("expected `{:?}`, found `{:?}`", c, maybe),
                          format!("expected `{}`", c),
                          pos,
                          pos);
+                None
             }
         } else {
             let msg = format!("expected `{:?}` but string was terminated", c);
@@ -302,6 +310,7 @@ fn must_consume(&mut self, c: char) {
             } else {
                 self.err(msg, format!("expected `{:?}`", c), pos, pos);
             }
+            None
         }
     }
 
index 755d2b476b7167ae33b94f195aabc075d8b52ebb..4700f814e58538f8c5181b1f03a0adbc289119ab 100644 (file)
@@ -21,7 +21,7 @@
 use syntax::parse::token;
 use syntax::ptr::P;
 use syntax::symbol::Symbol;
-use syntax_pos::{Span, DUMMY_SP};
+use syntax_pos::{Span, MultiSpan, DUMMY_SP};
 use syntax::tokenstream;
 
 use std::collections::{HashMap, HashSet};
@@ -264,28 +264,38 @@ fn describe_num_args(&self) -> String {
     /// errors for the case where all arguments are positional and for when
     /// there are named arguments or numbered positional arguments in the
     /// format string.
-    fn report_invalid_references(&self, numbered_position_args: bool) {
+    fn report_invalid_references(&self, numbered_position_args: bool, arg_places: &[(usize, usize)]) {
         let mut e;
-        let mut refs: Vec<String> = self.invalid_refs
-                                        .iter()
-                                        .map(|r| r.to_string())
-                                        .collect();
+        let sps = arg_places.iter()
+            .map(|&(start, end)| self.fmtsp.from_inner_byte_pos(start, end))
+            .collect::<Vec<_>>();
+        let sp = MultiSpan::from_spans(sps);
+        let mut refs: Vec<_> = self.invalid_refs
+            .iter()
+            .map(|r| r.to_string())
+            .collect();
 
         if self.names.is_empty() && !numbered_position_args {
-            e = self.ecx.mut_span_err(self.fmtsp,
+            e = self.ecx.mut_span_err(sp,
                 &format!("{} positional argument{} in format string, but {}",
                          self.pieces.len(),
                          if self.pieces.len() > 1 { "s" } else { "" },
                          self.describe_num_args()));
         } else {
             let arg_list = match refs.len() {
-                1 => format!("argument {}", refs.pop().unwrap()),
-                _ => format!("arguments {head} and {tail}",
-                             tail=refs.pop().unwrap(),
+                1 => {
+                    let reg = refs.pop().unwrap();
+                    format!("argument {}", reg)
+                },
+                _ => {
+                    let reg = refs.pop().unwrap();
+                    format!("arguments {head} and {tail}",
+                             tail=reg,
                              head=refs.join(", "))
+                }
             };
 
-            e = self.ecx.mut_span_err(self.fmtsp,
+            e = self.ecx.mut_span_err(sp,
                 &format!("invalid reference to positional {} ({})",
                         arg_list,
                         self.describe_num_args()));
@@ -835,7 +845,7 @@ pub fn expand_preparsed_format_args(ecx: &mut ExtCtxt,
     }
 
     if cx.invalid_refs.len() >= 1 {
-        cx.report_invalid_references(numbered_position_args);
+        cx.report_invalid_references(numbered_position_args, &parser.arg_places);
     }
 
     // Make sure that all arguments were used and all arguments have types.
diff --git a/src/test/compile-fail/ifmt-bad-arg.rs b/src/test/compile-fail/ifmt-bad-arg.rs
deleted file mode 100644 (file)
index afe9bc1..0000000
+++ /dev/null
@@ -1,67 +0,0 @@
-// Copyright 2013 The Rust Project Developers. See the COPYRIGHT
-// file at the top-level directory of this distribution and at
-// http://rust-lang.org/COPYRIGHT.
-//
-// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
-// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
-// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
-// option. This file may not be copied, modified, or distributed
-// except according to those terms.
-
-fn main() {
-    // bad arguments to the format! call
-
-    // bad number of arguments, see #44954 (originally #15780)
-
-    format!("{}");
-    //~^ ERROR: 1 positional argument in format string, but no arguments were given
-
-    format!("{1}", 1);
-    //~^ ERROR: invalid reference to positional argument 1 (there is 1 argument)
-    //~^^ ERROR: argument never used
-
-    format!("{} {}");
-    //~^ ERROR: 2 positional arguments in format string, but no arguments were given
-
-    format!("{0} {1}", 1);
-    //~^ ERROR: invalid reference to positional argument 1 (there is 1 argument)
-
-    format!("{0} {1} {2}", 1, 2);
-    //~^ ERROR: invalid reference to positional argument 2 (there are 2 arguments)
-
-    format!("{} {value} {} {}", 1, value=2);
-    //~^ ERROR: invalid reference to positional argument 2 (there are 2 arguments)
-    format!("{name} {value} {} {} {} {} {} {}", 0, name=1, value=2);
-    //~^ ERROR: invalid reference to positional arguments 3, 4 and 5 (there are 3 arguments)
-
-    format!("{} {foo} {} {bar} {}", 1, 2, 3);
-    //~^ ERROR: there is no argument named `foo`
-    //~^^ ERROR: there is no argument named `bar`
-
-    format!("{foo}");                //~ ERROR: no argument named `foo`
-    format!("", 1, 2);               //~ ERROR: multiple unused formatting arguments
-    format!("{}", 1, 2);             //~ ERROR: argument never used
-    format!("{1}", 1, 2);            //~ ERROR: argument never used
-    format!("{}", 1, foo=2);         //~ ERROR: named argument never used
-    format!("{foo}", 1, foo=2);      //~ ERROR: argument never used
-    format!("", foo=2);              //~ ERROR: named argument never used
-    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
-
-    // bad named arguments, #35082
-
-    format!("{valuea} {valueb}", valuea=5, valuec=7);
-    //~^ ERROR there is no argument named `valueb`
-    //~^^ ERROR named argument never used
-
-    // bad syntax of the format string
-
-    format!("{"); //~ ERROR: expected `'}'` but string was terminated
-
-    format!("foo } bar"); //~ ERROR: unmatched `}` found
-    format!("foo }"); //~ ERROR: unmatched `}` found
-
-    format!("foo %s baz", "bar"); //~ ERROR: argument never used
-}
diff --git a/src/test/ui/ifmt-bad-arg.rs b/src/test/ui/ifmt-bad-arg.rs
new file mode 100644 (file)
index 0000000..afe9bc1
--- /dev/null
@@ -0,0 +1,67 @@
+// Copyright 2013 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+fn main() {
+    // bad arguments to the format! call
+
+    // bad number of arguments, see #44954 (originally #15780)
+
+    format!("{}");
+    //~^ ERROR: 1 positional argument in format string, but no arguments were given
+
+    format!("{1}", 1);
+    //~^ ERROR: invalid reference to positional argument 1 (there is 1 argument)
+    //~^^ ERROR: argument never used
+
+    format!("{} {}");
+    //~^ ERROR: 2 positional arguments in format string, but no arguments were given
+
+    format!("{0} {1}", 1);
+    //~^ ERROR: invalid reference to positional argument 1 (there is 1 argument)
+
+    format!("{0} {1} {2}", 1, 2);
+    //~^ ERROR: invalid reference to positional argument 2 (there are 2 arguments)
+
+    format!("{} {value} {} {}", 1, value=2);
+    //~^ ERROR: invalid reference to positional argument 2 (there are 2 arguments)
+    format!("{name} {value} {} {} {} {} {} {}", 0, name=1, value=2);
+    //~^ ERROR: invalid reference to positional arguments 3, 4 and 5 (there are 3 arguments)
+
+    format!("{} {foo} {} {bar} {}", 1, 2, 3);
+    //~^ ERROR: there is no argument named `foo`
+    //~^^ ERROR: there is no argument named `bar`
+
+    format!("{foo}");                //~ ERROR: no argument named `foo`
+    format!("", 1, 2);               //~ ERROR: multiple unused formatting arguments
+    format!("{}", 1, 2);             //~ ERROR: argument never used
+    format!("{1}", 1, 2);            //~ ERROR: argument never used
+    format!("{}", 1, foo=2);         //~ ERROR: named argument never used
+    format!("{foo}", 1, foo=2);      //~ ERROR: argument never used
+    format!("", foo=2);              //~ ERROR: named argument never used
+    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
+
+    // bad named arguments, #35082
+
+    format!("{valuea} {valueb}", valuea=5, valuec=7);
+    //~^ ERROR there is no argument named `valueb`
+    //~^^ ERROR named argument never used
+
+    // bad syntax of the format string
+
+    format!("{"); //~ ERROR: expected `'}'` but string was terminated
+
+    format!("foo } bar"); //~ ERROR: unmatched `}` found
+    format!("foo }"); //~ ERROR: unmatched `}` found
+
+    format!("foo %s baz", "bar"); //~ ERROR: argument never used
+}
diff --git a/src/test/ui/ifmt-bad-arg.stderr b/src/test/ui/ifmt-bad-arg.stderr
new file mode 100644 (file)
index 0000000..4ad3c2b
--- /dev/null
@@ -0,0 +1,187 @@
+error: 1 positional argument in format string, but no arguments were given
+  --> $DIR/ifmt-bad-arg.rs:16:14
+   |
+LL |     format!("{}");
+   |              ^^
+
+error: invalid reference to positional argument 1 (there is 1 argument)
+  --> $DIR/ifmt-bad-arg.rs:19:14
+   |
+LL |     format!("{1}", 1);
+   |              ^^^
+   |
+   = note: positional arguments are zero-based
+
+error: argument never used
+  --> $DIR/ifmt-bad-arg.rs:19:20
+   |
+LL |     format!("{1}", 1);
+   |                    ^
+
+error: 2 positional arguments in format string, but no arguments were given
+  --> $DIR/ifmt-bad-arg.rs:23:14
+   |
+LL |     format!("{} {}");
+   |              ^^ ^^
+
+error: invalid reference to positional argument 1 (there is 1 argument)
+  --> $DIR/ifmt-bad-arg.rs:26:14
+   |
+LL |     format!("{0} {1}", 1);
+   |              ^^^ ^^^
+   |
+   = note: positional arguments are zero-based
+
+error: invalid reference to positional argument 2 (there are 2 arguments)
+  --> $DIR/ifmt-bad-arg.rs:29:14
+   |
+LL |     format!("{0} {1} {2}", 1, 2);
+   |              ^^^ ^^^ ^^^
+   |
+   = note: positional arguments are zero-based
+
+error: invalid reference to positional argument 2 (there are 2 arguments)
+  --> $DIR/ifmt-bad-arg.rs:32:14
+   |
+LL |     format!("{} {value} {} {}", 1, value=2);
+   |              ^^ ^^^^^^^ ^^ ^^
+   |
+   = note: positional arguments are zero-based
+
+error: invalid reference to positional arguments 3, 4 and 5 (there are 3 arguments)
+  --> $DIR/ifmt-bad-arg.rs:34:14
+   |
+LL |     format!("{name} {value} {} {} {} {} {} {}", 0, name=1, value=2);
+   |              ^^^^^^ ^^^^^^^ ^^ ^^ ^^ ^^ ^^ ^^
+   |
+   = note: positional arguments are zero-based
+
+error: there is no argument named `foo`
+  --> $DIR/ifmt-bad-arg.rs:37:13
+   |
+LL |     format!("{} {foo} {} {bar} {}", 1, 2, 3);
+   |             ^^^^^^^^^^^^^^^^^^^^^^
+
+error: there is no argument named `bar`
+  --> $DIR/ifmt-bad-arg.rs:37:13
+   |
+LL |     format!("{} {foo} {} {bar} {}", 1, 2, 3);
+   |             ^^^^^^^^^^^^^^^^^^^^^^
+
+error: there is no argument named `foo`
+  --> $DIR/ifmt-bad-arg.rs:41:13
+   |
+LL |     format!("{foo}");                //~ ERROR: no argument named `foo`
+   |             ^^^^^^^
+
+error: multiple unused formatting arguments
+  --> $DIR/ifmt-bad-arg.rs:42:17
+   |
+LL |     format!("", 1, 2);               //~ ERROR: multiple unused formatting arguments
+   |             --  ^  ^
+   |             |
+   |             multiple missing formatting arguments
+
+error: argument never used
+  --> $DIR/ifmt-bad-arg.rs:43:22
+   |
+LL |     format!("{}", 1, 2);             //~ ERROR: argument never used
+   |                      ^
+
+error: argument never used
+  --> $DIR/ifmt-bad-arg.rs:44:20
+   |
+LL |     format!("{1}", 1, 2);            //~ ERROR: argument never used
+   |                    ^
+
+error: named argument never used
+  --> $DIR/ifmt-bad-arg.rs:45:26
+   |
+LL |     format!("{}", 1, foo=2);         //~ ERROR: named argument never used
+   |                          ^
+
+error: argument never used
+  --> $DIR/ifmt-bad-arg.rs:46:22
+   |
+LL |     format!("{foo}", 1, foo=2);      //~ ERROR: argument never used
+   |                      ^
+
+error: named argument never used
+  --> $DIR/ifmt-bad-arg.rs:47:21
+   |
+LL |     format!("", foo=2);              //~ ERROR: named argument never used
+   |                     ^
+
+error: multiple unused formatting arguments
+  --> $DIR/ifmt-bad-arg.rs:48:32
+   |
+LL |     format!("{} {}", 1, 2, foo=1, bar=2);  //~ ERROR: multiple unused formatting arguments
+   |             -------            ^      ^
+   |             |
+   |             multiple missing formatting arguments
+
+error: duplicate argument named `foo`
+  --> $DIR/ifmt-bad-arg.rs:50:33
+   |
+LL |     format!("{foo}", foo=1, foo=2);  //~ ERROR: duplicate argument
+   |                                 ^
+   |
+note: previously here
+  --> $DIR/ifmt-bad-arg.rs:50:26
+   |
+LL |     format!("{foo}", foo=1, foo=2);  //~ ERROR: duplicate argument
+   |                          ^
+
+error: expected ident, positional arguments cannot follow named arguments
+  --> $DIR/ifmt-bad-arg.rs:51:24
+   |
+LL |     format!("", foo=1, 2);           //~ ERROR: positional arguments cannot follow
+   |                        ^
+
+error: there is no argument named `valueb`
+  --> $DIR/ifmt-bad-arg.rs:55:13
+   |
+LL |     format!("{valuea} {valueb}", valuea=5, valuec=7);
+   |             ^^^^^^^^^^^^^^^^^^^
+
+error: named argument never used
+  --> $DIR/ifmt-bad-arg.rs:55:51
+   |
+LL |     format!("{valuea} {valueb}", valuea=5, valuec=7);
+   |                                                   ^
+
+error: invalid format string: expected `'}'` but string was terminated
+  --> $DIR/ifmt-bad-arg.rs:61:15
+   |
+LL |     format!("{"); //~ ERROR: expected `'}'` but string was terminated
+   |               ^ expected `'}'` in format string
+   |
+   = note: if you intended to print `{`, you can escape it using `{{`
+
+error: invalid format string: unmatched `}` found
+  --> $DIR/ifmt-bad-arg.rs:63:18
+   |
+LL |     format!("foo } bar"); //~ ERROR: unmatched `}` found
+   |                  ^ unmatched `}` in format string
+   |
+   = note: if you intended to print `}`, you can escape it using `}}`
+
+error: invalid format string: unmatched `}` found
+  --> $DIR/ifmt-bad-arg.rs:64:18
+   |
+LL |     format!("foo }"); //~ ERROR: unmatched `}` found
+   |                  ^ unmatched `}` in format string
+   |
+   = note: if you intended to print `}`, you can escape it using `}}`
+
+error: argument never used
+  --> $DIR/ifmt-bad-arg.rs:66:27
+   |
+LL |     format!("foo %s baz", "bar"); //~ ERROR: argument never used
+   |                           ^^^^^
+   |
+   = help: `%s` should be written as `{}`
+   = note: printf formatting not supported; see the documentation for `std::fmt`
+
+error: aborting due to 26 previous errors
+
index 8f2eb0173a499459abdbbfc44787b41b123eff07..f0ca576f652eba28bafb7b0acfad2668f432856d 100644 (file)
@@ -1,8 +1,8 @@
 error: 1 positional argument in format string, but no arguments were given
-  --> $DIR/macro-backtrace-println.rs:24:30
+  --> $DIR/macro-backtrace-println.rs:24:31
    |
 LL |     ($fmt:expr) => (myprint!(concat!($fmt, "/n"))); //~ ERROR no arguments were given
-   |                              ^^^^^^^^^^^^^^^^^^^
+   |                               ^^
 ...
 LL |     myprintln!("{}");
    |     ----------------- in this macro invocation