From 42306591b9c0a280da363c83df16b47ad8b04024 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Mon, 23 Jul 2018 00:01:17 -0700 Subject: [PATCH] Point at incorrect named arg in format string --- src/libsyntax_ext/format.rs | 21 +++++++++++++++++++-- src/test/ui/ifmt-bad-arg.stderr | 16 ++++++++-------- 2 files changed, 27 insertions(+), 10 deletions(-) diff --git a/src/libsyntax_ext/format.rs b/src/libsyntax_ext/format.rs index 4700f814e58..215e4f5a835 100644 --- a/src/libsyntax_ext/format.rs +++ b/src/libsyntax_ext/format.rs @@ -111,8 +111,10 @@ struct Context<'a, 'b: 'a> { /// still existed in this phase of processing. /// Used only for `all_pieces_simple` tracking in `build_piece`. curarg: usize, + curpiece: usize, /// Keep track of invalid references to positional arguments invalid_refs: Vec, + arg_spans: Vec, } /// Parses the arguments from the given list of tokens, returning None @@ -235,6 +237,7 @@ impl<'a, 'b> Context<'a, 'b> { let ty = Placeholder(arg.format.ty.to_string()); self.verify_arg_type(pos, ty); + self.curpiece += 1; } } } @@ -347,7 +350,9 @@ impl<'a, 'b> Context<'a, 'b> { Some(e) => *e, None => { let msg = format!("there is no argument named `{}`", name); - self.ecx.span_err(self.fmtsp, &msg[..]); + let sp = *self.arg_spans.get(self.curpiece).unwrap_or(&self.fmtsp); + let mut err = self.ecx.struct_span_err(sp, &msg[..]); + err.emit(); return; } }; @@ -773,6 +778,7 @@ pub fn expand_preparsed_format_args(ecx: &mut ExtCtxt, arg_unique_types, names, curarg: 0, + curpiece: 0, arg_index_map: Vec::new(), count_args: Vec::new(), count_positions: HashMap::new(), @@ -785,6 +791,7 @@ pub fn expand_preparsed_format_args(ecx: &mut ExtCtxt, macsp, fmtsp: fmt.span, invalid_refs: Vec::new(), + arg_spans: Vec::new(), }; let fmt_str = &*fmt.node.0.as_str(); @@ -793,12 +800,22 @@ pub fn expand_preparsed_format_args(ecx: &mut ExtCtxt, ast::StrStyle::Raw(raw) => Some(raw as usize), }; let mut parser = parse::Parser::new(fmt_str, str_style); + let mut unverified_pieces = vec![]; let mut pieces = vec![]; - while let Some(mut piece) = parser.next() { + while let Some(piece) = parser.next() { if !parser.errors.is_empty() { break; } + unverified_pieces.push(piece); + } + + cx.arg_spans = parser.arg_places.iter() + .map(|&(start, end)| fmt.span.from_inner_byte_pos(start, end)) + .collect(); + + // This needs to happen *after* the Parser has consumed all pieces to create all the spans + for mut piece in unverified_pieces { cx.verify_piece(&piece); cx.resolve_name_inplace(&mut piece); pieces.push(piece); diff --git a/src/test/ui/ifmt-bad-arg.stderr b/src/test/ui/ifmt-bad-arg.stderr index 4ad3c2b6550..2d49c36c06d 100644 --- a/src/test/ui/ifmt-bad-arg.stderr +++ b/src/test/ui/ifmt-bad-arg.stderr @@ -57,22 +57,22 @@ 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 + --> $DIR/ifmt-bad-arg.rs:37:17 | LL | format!("{} {foo} {} {bar} {}", 1, 2, 3); - | ^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^ error: there is no argument named `bar` - --> $DIR/ifmt-bad-arg.rs:37:13 + --> $DIR/ifmt-bad-arg.rs:37:26 | LL | format!("{} {foo} {} {bar} {}", 1, 2, 3); - | ^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^ error: there is no argument named `foo` - --> $DIR/ifmt-bad-arg.rs:41:13 + --> $DIR/ifmt-bad-arg.rs:41:14 | LL | format!("{foo}"); //~ ERROR: no argument named `foo` - | ^^^^^^^ + | ^^^^^ error: multiple unused formatting arguments --> $DIR/ifmt-bad-arg.rs:42:17 @@ -139,10 +139,10 @@ LL | format!("", foo=1, 2); //~ ERROR: positional arguments cannot | ^ error: there is no argument named `valueb` - --> $DIR/ifmt-bad-arg.rs:55:13 + --> $DIR/ifmt-bad-arg.rs:55:23 | LL | format!("{valuea} {valueb}", valuea=5, valuec=7); - | ^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^ error: named argument never used --> $DIR/ifmt-bad-arg.rs:55:51