1
Fork 0

Correctly mark the span of captured arguments in format_args!()

It should only include the identifier, or misspelling suggestions will be wrong.
This commit is contained in:
Chayim Refael Friedman 2022-02-16 00:38:04 +00:00 committed by GitHub
parent 1e12aef3fa
commit 91adb6ccd6
15 changed files with 105 additions and 74 deletions

View file

@ -700,11 +700,11 @@ fn expand_preparsed_asm(ecx: &mut ExtCtxt<'_>, args: AsmArgs) -> Option<ast::Inl
Some(idx) Some(idx)
} }
} }
parse::ArgumentNamed(name) => match args.named_args.get(&name) { parse::ArgumentNamed(name, span) => match args.named_args.get(&name) {
Some(&idx) => Some(idx), Some(&idx) => Some(idx),
None => { None => {
let msg = format!("there is no argument named `{}`", name); let msg = format!("there is no argument named `{}`", name);
ecx.struct_span_err(span, &msg).emit(); ecx.struct_span_err(template_span.from_inner(span), &msg).emit();
None None
} }
}, },

View file

@ -11,7 +11,7 @@ use rustc_errors::{pluralize, Applicability, DiagnosticBuilder};
use rustc_expand::base::{self, *}; use rustc_expand::base::{self, *};
use rustc_parse_format as parse; use rustc_parse_format as parse;
use rustc_span::symbol::{sym, Ident, Symbol}; use rustc_span::symbol::{sym, Ident, Symbol};
use rustc_span::{MultiSpan, Span}; use rustc_span::{InnerSpan, MultiSpan, Span};
use smallvec::SmallVec; use smallvec::SmallVec;
use std::borrow::Cow; use std::borrow::Cow;
@ -26,7 +26,7 @@ enum ArgumentType {
enum Position { enum Position {
Exact(usize), Exact(usize),
Capture(usize), Capture(usize),
Named(Symbol), Named(Symbol, InnerSpan),
} }
struct Context<'a, 'b> { struct Context<'a, 'b> {
@ -247,13 +247,13 @@ impl<'a, 'b> Context<'a, 'b> {
match *p { match *p {
parse::String(_) => {} parse::String(_) => {}
parse::NextArgument(ref mut arg) => { parse::NextArgument(ref mut arg) => {
if let parse::ArgumentNamed(s) = arg.position { if let parse::ArgumentNamed(s, _) = arg.position {
arg.position = parse::ArgumentIs(lookup(s)); arg.position = parse::ArgumentIs(lookup(s));
} }
if let parse::CountIsName(s) = arg.format.width { if let parse::CountIsName(s, _) = arg.format.width {
arg.format.width = parse::CountIsParam(lookup(s)); arg.format.width = parse::CountIsParam(lookup(s));
} }
if let parse::CountIsName(s) = arg.format.precision { if let parse::CountIsName(s, _) = arg.format.precision {
arg.format.precision = parse::CountIsParam(lookup(s)); arg.format.precision = parse::CountIsParam(lookup(s));
} }
} }
@ -276,7 +276,7 @@ impl<'a, 'b> Context<'a, 'b> {
// it's written second, so it should come after width/precision. // it's written second, so it should come after width/precision.
let pos = match arg.position { let pos = match arg.position {
parse::ArgumentIs(i) | parse::ArgumentImplicitlyIs(i) => Exact(i), parse::ArgumentIs(i) | parse::ArgumentImplicitlyIs(i) => Exact(i),
parse::ArgumentNamed(s) => Named(s), parse::ArgumentNamed(s, span) => Named(s, span),
}; };
let ty = Placeholder(match arg.format.ty { let ty = Placeholder(match arg.format.ty {
@ -346,8 +346,8 @@ impl<'a, 'b> Context<'a, 'b> {
parse::CountIsParam(i) => { parse::CountIsParam(i) => {
self.verify_arg_type(Exact(i), Count); self.verify_arg_type(Exact(i), Count);
} }
parse::CountIsName(s) => { parse::CountIsName(s, span) => {
self.verify_arg_type(Named(s), Count); self.verify_arg_type(Named(s, span), Count);
} }
} }
} }
@ -533,7 +533,7 @@ impl<'a, 'b> Context<'a, 'b> {
} }
} }
Named(name) => { Named(name, span) => {
match self.names.get(&name) { match self.names.get(&name) {
Some(&idx) => { Some(&idx) => {
// Treat as positional arg. // Treat as positional arg.
@ -548,7 +548,7 @@ impl<'a, 'b> Context<'a, 'b> {
self.arg_types.push(Vec::new()); self.arg_types.push(Vec::new());
self.arg_unique_types.push(Vec::new()); self.arg_unique_types.push(Vec::new());
let span = if self.is_literal { let span = if self.is_literal {
*self.arg_spans.get(self.curpiece).unwrap_or(&self.fmtsp) self.fmtsp.from_inner(span)
} else { } else {
self.fmtsp self.fmtsp
}; };
@ -559,7 +559,7 @@ impl<'a, 'b> Context<'a, 'b> {
} else { } else {
let msg = format!("there is no argument named `{}`", name); let msg = format!("there is no argument named `{}`", name);
let sp = if self.is_literal { let sp = if self.is_literal {
*self.arg_spans.get(self.curpiece).unwrap_or(&self.fmtsp) self.fmtsp.from_inner(span)
} else { } else {
self.fmtsp self.fmtsp
}; };
@ -629,7 +629,7 @@ impl<'a, 'b> Context<'a, 'b> {
} }
parse::CountImplied => count(sym::Implied, None), parse::CountImplied => count(sym::Implied, None),
// should never be the case, names are already resolved // should never be the case, names are already resolved
parse::CountIsName(_) => panic!("should never happen"), parse::CountIsName(..) => panic!("should never happen"),
} }
} }
@ -676,7 +676,7 @@ impl<'a, 'b> Context<'a, 'b> {
// should never be the case, because names are already // should never be the case, because names are already
// resolved. // resolved.
parse::ArgumentNamed(_) => panic!("should never happen"), parse::ArgumentNamed(..) => panic!("should never happen"),
} }
}; };

View file

@ -95,7 +95,7 @@ pub enum Position {
/// The argument is located at a specific index given in the format /// The argument is located at a specific index given in the format
ArgumentIs(usize), ArgumentIs(usize),
/// The argument has a name. /// The argument has a name.
ArgumentNamed(Symbol), ArgumentNamed(Symbol, InnerSpan),
} }
impl Position { impl Position {
@ -147,7 +147,7 @@ pub enum Count {
/// The count is specified explicitly. /// The count is specified explicitly.
CountIs(usize), CountIs(usize),
/// The count is specified by the argument with the given name. /// The count is specified by the argument with the given name.
CountIsName(Symbol), CountIsName(Symbol, InnerSpan),
/// The count is specified by the argument at the given index. /// The count is specified by the argument at the given index.
CountIsParam(usize), CountIsParam(usize),
/// The count is implied and cannot be explicitly specified. /// The count is implied and cannot be explicitly specified.
@ -494,8 +494,11 @@ impl<'a> Parser<'a> {
Some(ArgumentIs(i)) Some(ArgumentIs(i))
} else { } else {
match self.cur.peek() { match self.cur.peek() {
Some(&(_, c)) if rustc_lexer::is_id_start(c) => { Some(&(start, c)) if rustc_lexer::is_id_start(c) => {
Some(ArgumentNamed(Symbol::intern(self.word()))) let word = self.word();
let end = start + word.len();
let span = self.to_span_index(start).to(self.to_span_index(end));
Some(ArgumentNamed(Symbol::intern(word), span))
} }
// This is an `ArgumentNext`. // This is an `ArgumentNext`.
@ -662,8 +665,9 @@ impl<'a> Parser<'a> {
if word.is_empty() { if word.is_empty() {
self.cur = tmp; self.cur = tmp;
(CountImplied, None) (CountImplied, None)
} else if self.consume('$') { } else if let Some(end) = self.consume_pos('$') {
(CountIsName(Symbol::intern(word)), None) let span = self.to_span_index(start + 1).to(self.to_span_index(end));
(CountIsName(Symbol::intern(word), span), None)
} else { } else {
self.cur = tmp; self.cur = tmp;
(CountImplied, None) (CountImplied, None)

View file

@ -221,8 +221,8 @@ fn format_counts() {
fill: None, fill: None,
align: AlignUnknown, align: AlignUnknown,
flags: 0, flags: 0,
precision: CountIsName(Symbol::intern("b")), precision: CountIsName(Symbol::intern("b"), InnerSpan::new(6, 7)),
width: CountIsName(Symbol::intern("a")), width: CountIsName(Symbol::intern("a"), InnerSpan::new(4, 4)),
precision_span: None, precision_span: None,
width_span: None, width_span: None,
ty: "?", ty: "?",

View file

@ -309,23 +309,23 @@ impl<'tcx> OnUnimplementedFormatString {
Piece::String(_) => (), // Normal string, no need to check it Piece::String(_) => (), // Normal string, no need to check it
Piece::NextArgument(a) => match a.position { Piece::NextArgument(a) => match a.position {
// `{Self}` is allowed // `{Self}` is allowed
Position::ArgumentNamed(s) if s == kw::SelfUpper => (), Position::ArgumentNamed(s, _) if s == kw::SelfUpper => (),
// `{ThisTraitsName}` is allowed // `{ThisTraitsName}` is allowed
Position::ArgumentNamed(s) if s == name => (), Position::ArgumentNamed(s, _) if s == name => (),
// `{from_method}` is allowed // `{from_method}` is allowed
Position::ArgumentNamed(s) if s == sym::from_method => (), Position::ArgumentNamed(s, _) if s == sym::from_method => (),
// `{from_desugaring}` is allowed // `{from_desugaring}` is allowed
Position::ArgumentNamed(s) if s == sym::from_desugaring => (), Position::ArgumentNamed(s, _) if s == sym::from_desugaring => (),
// `{ItemContext}` is allowed // `{ItemContext}` is allowed
Position::ArgumentNamed(s) if s == sym::ItemContext => (), Position::ArgumentNamed(s, _) if s == sym::ItemContext => (),
// `{integral}` and `{integer}` and `{float}` are allowed // `{integral}` and `{integer}` and `{float}` are allowed
Position::ArgumentNamed(s) Position::ArgumentNamed(s, _)
if s == sym::integral || s == sym::integer_ || s == sym::float => if s == sym::integral || s == sym::integer_ || s == sym::float =>
{ {
() ()
} }
// So is `{A}` if A is a type parameter // So is `{A}` if A is a type parameter
Position::ArgumentNamed(s) => { Position::ArgumentNamed(s, _) => {
match generics.params.iter().find(|param| param.name == s) { match generics.params.iter().find(|param| param.name == s) {
Some(_) => (), Some(_) => (),
None => { None => {
@ -392,7 +392,7 @@ impl<'tcx> OnUnimplementedFormatString {
.map(|p| match p { .map(|p| match p {
Piece::String(s) => s, Piece::String(s) => s,
Piece::NextArgument(a) => match a.position { Piece::NextArgument(a) => match a.position {
Position::ArgumentNamed(s) => match generic_map.get(&s) { Position::ArgumentNamed(s, _) => match generic_map.get(&s) {
Some(val) => val, Some(val) => val,
None if s == name => &trait_str, None if s == name => &trait_str,
None => { None => {

View file

@ -23,10 +23,10 @@ LL | asm!("{1}", in(reg) foo);
= help: if this argument is intentionally unused, consider using it in an asm comment: `"/* {0} */"` = help: if this argument is intentionally unused, consider using it in an asm comment: `"/* {0} */"`
error: there is no argument named `a` error: there is no argument named `a`
--> $DIR/bad-template.rs:36:15 --> $DIR/bad-template.rs:36:16
| |
LL | asm!("{a}"); LL | asm!("{a}");
| ^^^ | ^
error: invalid reference to argument at index 0 error: invalid reference to argument at index 0
--> $DIR/bad-template.rs:38:15 --> $DIR/bad-template.rs:38:15
@ -123,10 +123,10 @@ LL | global_asm!("{1}", const FOO);
= help: if this argument is intentionally unused, consider using it in an asm comment: `"/* {0} */"` = help: if this argument is intentionally unused, consider using it in an asm comment: `"/* {0} */"`
error: there is no argument named `a` error: there is no argument named `a`
--> $DIR/bad-template.rs:63:14 --> $DIR/bad-template.rs:63:15
| |
LL | global_asm!("{a}"); LL | global_asm!("{a}");
| ^^^ | ^
error: invalid reference to argument at index 0 error: invalid reference to argument at index 0
--> $DIR/bad-template.rs:65:14 --> $DIR/bad-template.rs:65:14

View file

@ -23,10 +23,10 @@ LL | asm!("{1}", in(reg) foo);
= help: if this argument is intentionally unused, consider using it in an asm comment: `"/* {0} */"` = help: if this argument is intentionally unused, consider using it in an asm comment: `"/* {0} */"`
error: there is no argument named `a` error: there is no argument named `a`
--> $DIR/bad-template.rs:36:15 --> $DIR/bad-template.rs:36:16
| |
LL | asm!("{a}"); LL | asm!("{a}");
| ^^^ | ^
error: invalid reference to argument at index 0 error: invalid reference to argument at index 0
--> $DIR/bad-template.rs:38:15 --> $DIR/bad-template.rs:38:15
@ -123,10 +123,10 @@ LL | global_asm!("{1}", const FOO);
= help: if this argument is intentionally unused, consider using it in an asm comment: `"/* {0} */"` = help: if this argument is intentionally unused, consider using it in an asm comment: `"/* {0} */"`
error: there is no argument named `a` error: there is no argument named `a`
--> $DIR/bad-template.rs:63:14 --> $DIR/bad-template.rs:63:15
| |
LL | global_asm!("{a}"); LL | global_asm!("{a}");
| ^^^ | ^
error: invalid reference to argument at index 0 error: invalid reference to argument at index 0
--> $DIR/bad-template.rs:65:14 --> $DIR/bad-template.rs:65:14

View file

@ -23,10 +23,10 @@ LL | asm!("{1}", in(reg) foo);
= help: if this argument is intentionally unused, consider using it in an asm comment: `"/* {0} */"` = help: if this argument is intentionally unused, consider using it in an asm comment: `"/* {0} */"`
error: there is no argument named `a` error: there is no argument named `a`
--> $DIR/bad-template.rs:36:15 --> $DIR/bad-template.rs:36:16
| |
LL | asm!("{a}"); LL | asm!("{a}");
| ^^^ | ^
error: invalid reference to argument at index 0 error: invalid reference to argument at index 0
--> $DIR/bad-template.rs:38:15 --> $DIR/bad-template.rs:38:15
@ -123,10 +123,10 @@ LL | global_asm!("{1}", const FOO);
= help: if this argument is intentionally unused, consider using it in an asm comment: `"/* {0} */"` = help: if this argument is intentionally unused, consider using it in an asm comment: `"/* {0} */"`
error: there is no argument named `a` error: there is no argument named `a`
--> $DIR/bad-template.rs:63:14 --> $DIR/bad-template.rs:63:15
| |
LL | global_asm!("{a}"); LL | global_asm!("{a}");
| ^^^ | ^
error: invalid reference to argument at index 0 error: invalid reference to argument at index 0
--> $DIR/bad-template.rs:65:14 --> $DIR/bad-template.rs:65:14

View file

@ -23,10 +23,10 @@ LL | asm!("{1}", in(reg) foo);
= help: if this argument is intentionally unused, consider using it in an asm comment: `"/* {0} */"` = help: if this argument is intentionally unused, consider using it in an asm comment: `"/* {0} */"`
error: there is no argument named `a` error: there is no argument named `a`
--> $DIR/bad-template.rs:36:15 --> $DIR/bad-template.rs:36:16
| |
LL | asm!("{a}"); LL | asm!("{a}");
| ^^^ | ^
error: invalid reference to argument at index 0 error: invalid reference to argument at index 0
--> $DIR/bad-template.rs:38:15 --> $DIR/bad-template.rs:38:15
@ -123,10 +123,10 @@ LL | global_asm!("{1}", const FOO);
= help: if this argument is intentionally unused, consider using it in an asm comment: `"/* {0} */"` = help: if this argument is intentionally unused, consider using it in an asm comment: `"/* {0} */"`
error: there is no argument named `a` error: there is no argument named `a`
--> $DIR/bad-template.rs:63:14 --> $DIR/bad-template.rs:63:15
| |
LL | global_asm!("{a}"); LL | global_asm!("{a}");
| ^^^ | ^
error: invalid reference to argument at index 0 error: invalid reference to argument at index 0
--> $DIR/bad-template.rs:65:14 --> $DIR/bad-template.rs:65:14

View file

@ -10,10 +10,10 @@ error: invalid reference to positional argument 0 (no arguments were given)
--> $DIR/format-args-capture-issue-93378.rs:9:23 --> $DIR/format-args-capture-issue-93378.rs:9:23
| |
LL | println!("{a:.n$} {b:.*}"); LL | println!("{a:.n$} {b:.*}");
| ------- ^^^--^ | - ^^^--^
| | | | | |
| | this precision flag adds an extra required argument at position 0, which is why there are 3 arguments expected | | this precision flag adds an extra required argument at position 0, which is why there are 3 arguments expected
| this parameter corresponds to the precision flag | this parameter corresponds to the precision flag
| |
= note: positional arguments are zero-based = note: positional arguments are zero-based
= note: for information about formatting flags, visit https://doc.rust-lang.org/std/fmt/index.html = note: for information about formatting flags, visit https://doc.rust-lang.org/std/fmt/index.html

View file

@ -0,0 +1,7 @@
fn main() {
const FOO: i32 = 123;
println!("{foo:X}");
//~^ ERROR: cannot find value `foo` in this scope
println!("{:.foo$}", 0);
//~^ ERROR: cannot find value `foo` in this scope
}

View file

@ -0,0 +1,20 @@
error[E0425]: cannot find value `foo` in this scope
--> $DIR/format-args-capture-issue-94010.rs:3:16
|
LL | const FOO: i32 = 123;
| --------------------- similarly named constant `FOO` defined here
LL | println!("{foo:X}");
| ^^^ help: a constant with a similar name exists (notice the capitalization): `FOO`
error[E0425]: cannot find value `foo` in this scope
--> $DIR/format-args-capture-issue-94010.rs:5:18
|
LL | const FOO: i32 = 123;
| --------------------- similarly named constant `FOO` defined here
...
LL | println!("{:.foo$}", 0);
| ^^^ help: a constant with a similar name exists (notice the capitalization): `FOO`
error: aborting due to 2 previous errors
For more information about this error, try `rustc --explain E0425`.

View file

@ -7,40 +7,40 @@ LL | format!("{valuea} {valueb}", valuea=5, valuec=7);
| formatting specifier missing | formatting specifier missing
error[E0425]: cannot find value `foo` in this scope error[E0425]: cannot find value `foo` in this scope
--> $DIR/format-args-capture-missing-variables.rs:2:17 --> $DIR/format-args-capture-missing-variables.rs:2:18
| |
LL | format!("{} {foo} {} {bar} {}", 1, 2, 3); LL | format!("{} {foo} {} {bar} {}", 1, 2, 3);
| ^^^^^ not found in this scope | ^^^ not found in this scope
error[E0425]: cannot find value `bar` in this scope error[E0425]: cannot find value `bar` in this scope
--> $DIR/format-args-capture-missing-variables.rs:2:26 --> $DIR/format-args-capture-missing-variables.rs:2:27
| |
LL | format!("{} {foo} {} {bar} {}", 1, 2, 3); LL | format!("{} {foo} {} {bar} {}", 1, 2, 3);
| ^^^^^ not found in this scope | ^^^ not found in this scope
error[E0425]: cannot find value `foo` in this scope error[E0425]: cannot find value `foo` in this scope
--> $DIR/format-args-capture-missing-variables.rs:6:14 --> $DIR/format-args-capture-missing-variables.rs:6:15
| |
LL | format!("{foo}"); LL | format!("{foo}");
| ^^^^^ not found in this scope | ^^^ not found in this scope
error[E0425]: cannot find value `valueb` in this scope error[E0425]: cannot find value `valueb` in this scope
--> $DIR/format-args-capture-missing-variables.rs:8:23 --> $DIR/format-args-capture-missing-variables.rs:8:24
| |
LL | format!("{valuea} {valueb}", valuea=5, valuec=7); LL | format!("{valuea} {valueb}", valuea=5, valuec=7);
| ^^^^^^^^ not found in this scope | ^^^^^^ not found in this scope
error[E0425]: cannot find value `foo` in this scope error[E0425]: cannot find value `foo` in this scope
--> $DIR/format-args-capture-missing-variables.rs:14:9 --> $DIR/format-args-capture-missing-variables.rs:14:10
| |
LL | {foo} LL | {foo}
| ^^^^^ not found in this scope | ^^^ not found in this scope
error[E0425]: cannot find value `foo` in this scope error[E0425]: cannot find value `foo` in this scope
--> $DIR/format-args-capture-missing-variables.rs:19:13 --> $DIR/format-args-capture-missing-variables.rs:19:14
| |
LL | panic!("{foo} {bar}", bar=1); LL | panic!("{foo} {bar}", bar=1);
| ^^^^^ not found in this scope | ^^^ not found in this scope
error: aborting due to 7 previous errors error: aborting due to 7 previous errors

View file

@ -263,34 +263,34 @@ LL | println!("{:.*}");
= note: for information about formatting flags, visit https://doc.rust-lang.org/std/fmt/index.html = note: for information about formatting flags, visit https://doc.rust-lang.org/std/fmt/index.html
error[E0425]: cannot find value `foo` in this scope error[E0425]: cannot find value `foo` in this scope
--> $DIR/ifmt-bad-arg.rs:27:17 --> $DIR/ifmt-bad-arg.rs:27:18
| |
LL | format!("{} {foo} {} {bar} {}", 1, 2, 3); LL | format!("{} {foo} {} {bar} {}", 1, 2, 3);
| ^^^^^ not found in this scope | ^^^ not found in this scope
error[E0425]: cannot find value `bar` in this scope error[E0425]: cannot find value `bar` in this scope
--> $DIR/ifmt-bad-arg.rs:27:26 --> $DIR/ifmt-bad-arg.rs:27:27
| |
LL | format!("{} {foo} {} {bar} {}", 1, 2, 3); LL | format!("{} {foo} {} {bar} {}", 1, 2, 3);
| ^^^^^ not found in this scope | ^^^ not found in this scope
error[E0425]: cannot find value `foo` in this scope error[E0425]: cannot find value `foo` in this scope
--> $DIR/ifmt-bad-arg.rs:31:14 --> $DIR/ifmt-bad-arg.rs:31:15
| |
LL | format!("{foo}"); LL | format!("{foo}");
| ^^^^^ not found in this scope | ^^^ not found in this scope
error[E0425]: cannot find value `valueb` in this scope error[E0425]: cannot find value `valueb` in this scope
--> $DIR/ifmt-bad-arg.rs:45:23 --> $DIR/ifmt-bad-arg.rs:45:24
| |
LL | format!("{valuea} {valueb}", valuea=5, valuec=7); LL | format!("{valuea} {valueb}", valuea=5, valuec=7);
| ^^^^^^^^ not found in this scope | ^^^^^^ not found in this scope
error[E0425]: cannot find value `foo` in this scope error[E0425]: cannot find value `foo` in this scope
--> $DIR/ifmt-bad-arg.rs:60:9 --> $DIR/ifmt-bad-arg.rs:60:10
| |
LL | {foo} LL | {foo}
| ^^^^^ not found in this scope | ^^^ not found in this scope
error[E0308]: mismatched types error[E0308]: mismatched types
--> $DIR/ifmt-bad-arg.rs:78:32 --> $DIR/ifmt-bad-arg.rs:78:32

View file

@ -453,7 +453,7 @@ impl SimpleFormatArgs {
} }
} }
}, },
ArgumentNamed(n) => { ArgumentNamed(n, _) => {
if let Some(x) = self.named.iter_mut().find(|x| x.0 == n) { if let Some(x) = self.named.iter_mut().find(|x| x.0 == n) {
match x.1.as_slice() { match x.1.as_slice() {
// A non-empty format string has been seen already. // A non-empty format string has been seen already.