about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--src/libfmt_macros/lib.rs17
-rw-r--r--src/libsyntax_ext/format.rs34
-rw-r--r--src/test/ui/ifmt-bad-arg.rs (renamed from src/test/compile-fail/ifmt-bad-arg.rs)0
-rw-r--r--src/test/ui/ifmt-bad-arg.stderr187
-rw-r--r--src/test/ui/macros/macro-backtrace-println.stderr4
5 files changed, 224 insertions, 18 deletions
diff --git a/src/libfmt_macros/lib.rs b/src/libfmt_macros/lib.rs
index 9952e5f64d6..f6dcebf8c50 100644
--- a/src/libfmt_macros/lib.rs
+++ b/src/libfmt_macros/lib.rs
@@ -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 @@ impl<'a> Iterator for Parser<'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 @@ impl<'a> Parser<'a> {
             curarg: 0,
             style,
             seen_newlines: 0,
+            arg_places: vec![],
         }
     }
 
@@ -271,7 +277,7 @@ impl<'a> Parser<'a> {
 
     /// 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 @@ impl<'a> Parser<'a> {
         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 @@ impl<'a> Parser<'a> {
             } else {
                 self.err(msg, format!("expected `{:?}`", c), pos, pos);
             }
+            None
         }
     }
 
diff --git a/src/libsyntax_ext/format.rs b/src/libsyntax_ext/format.rs
index 755d2b476b7..4700f814e58 100644
--- a/src/libsyntax_ext/format.rs
+++ b/src/libsyntax_ext/format.rs
@@ -21,7 +21,7 @@ use syntax::feature_gate;
 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 @@ impl<'a, 'b> Context<'a, 'b> {
     /// 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/ui/ifmt-bad-arg.rs
index afe9bc152a3..afe9bc152a3 100644
--- a/src/test/compile-fail/ifmt-bad-arg.rs
+++ b/src/test/ui/ifmt-bad-arg.rs
diff --git a/src/test/ui/ifmt-bad-arg.stderr b/src/test/ui/ifmt-bad-arg.stderr
new file mode 100644
index 00000000000..4ad3c2b6550
--- /dev/null
+++ b/src/test/ui/ifmt-bad-arg.stderr
@@ -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
+
diff --git a/src/test/ui/macros/macro-backtrace-println.stderr b/src/test/ui/macros/macro-backtrace-println.stderr
index 8f2eb0173a4..f0ca576f652 100644
--- a/src/test/ui/macros/macro-backtrace-println.stderr
+++ b/src/test/ui/macros/macro-backtrace-println.stderr
@@ -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