Point at internal span in format string

This commit is contained in:
Esteban Küber 2018-07-22 22:40:24 -07:00
parent d3b3bc5767
commit 38abca8c2d
5 changed files with 224 additions and 18 deletions

View 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 @@ 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
}
}

View file

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

View file

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

View 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