1
Fork 0

Auto merge of #62710 - estebank:bad-named-args, r=petrochenkov

Specific error for positional args after named args in `format!()`

When writing positional arguments after named arguments in the
`format!()` and `println!()` macros, provide a targeted diagnostic.

Follow up to https://github.com/rust-lang/rust/pull/57522/files#r247278885
This commit is contained in:
bors 2019-07-20 06:18:52 +00:00
commit f69b07144a
5 changed files with 53 additions and 34 deletions

View file

@ -146,16 +146,13 @@ fn parse_args<'a>(
if p.token == token::Eof { if p.token == token::Eof {
break; break;
} // accept trailing commas } // accept trailing commas
if named || (p.token.is_ident() && p.look_ahead(1, |t| *t == token::Eq)) { if p.token.is_ident() && p.look_ahead(1, |t| *t == token::Eq) {
named = true; named = true;
let name = if let token::Ident(name, _) = p.token.kind { let name = if let token::Ident(name, _) = p.token.kind {
p.bump(); p.bump();
name name
} else { } else {
return Err(ecx.struct_span_err( unreachable!();
p.token.span,
"expected ident, positional arguments cannot follow named arguments",
));
}; };
p.expect(&token::Eq)?; p.expect(&token::Eq)?;
@ -176,6 +173,17 @@ fn parse_args<'a>(
args.push(e); args.push(e);
} else { } else {
let e = p.parse_expr()?; let e = p.parse_expr()?;
if named {
let mut err = ecx.struct_span_err(
e.span,
"positional arguments cannot follow named arguments",
);
err.span_label(e.span, "positional arguments must be before named arguments");
for (_, pos) in &names {
err.span_label(args[*pos].span, "named argument");
}
err.emit();
}
args.push(e); args.push(e);
} }
} }
@ -721,13 +729,14 @@ pub fn expand_format_args_nl<'cx>(
/// Take the various parts of `format_args!(efmt, args..., name=names...)` /// Take the various parts of `format_args!(efmt, args..., name=names...)`
/// and construct the appropriate formatting expression. /// and construct the appropriate formatting expression.
pub fn expand_preparsed_format_args(ecx: &mut ExtCtxt<'_>, pub fn expand_preparsed_format_args(
sp: Span, ecx: &mut ExtCtxt<'_>,
efmt: P<ast::Expr>, sp: Span,
args: Vec<P<ast::Expr>>, efmt: P<ast::Expr>,
names: FxHashMap<Symbol, usize>, args: Vec<P<ast::Expr>>,
append_newline: bool) names: FxHashMap<Symbol, usize>,
-> P<ast::Expr> { append_newline: bool,
) -> P<ast::Expr> {
// NOTE: this verbose way of initializing `Vec<Vec<ArgumentType>>` is because // NOTE: this verbose way of initializing `Vec<Vec<ArgumentType>>` is because
// `ArgumentType` does not derive `Clone`. // `ArgumentType` does not derive `Clone`.
let arg_types: Vec<_> = (0..args.len()).map(|_| Vec::new()).collect(); let arg_types: Vec<_> = (0..args.len()).map(|_| Vec::new()).collect();
@ -906,6 +915,8 @@ pub fn expand_preparsed_format_args(ecx: &mut ExtCtxt<'_>,
.map(|span| fmt.span.from_inner(*span)) .map(|span| fmt.span.from_inner(*span))
.collect(); .collect();
let named_pos: FxHashSet<usize> = names.values().cloned().collect();
let mut cx = Context { let mut cx = Context {
ecx, ecx,
args, args,
@ -971,14 +982,12 @@ pub fn expand_preparsed_format_args(ecx: &mut ExtCtxt<'_>,
} }
// Make sure that all arguments were used and all arguments have types. // Make sure that all arguments were used and all arguments have types.
let num_pos_args = cx.args.len() - cx.names.len();
let errs = cx.arg_types let errs = cx.arg_types
.iter() .iter()
.enumerate() .enumerate()
.filter(|(i, ty)| ty.is_empty() && !cx.count_positions.contains_key(&i)) .filter(|(i, ty)| ty.is_empty() && !cx.count_positions.contains_key(&i))
.map(|(i, _)| { .map(|(i, _)| {
let msg = if i >= num_pos_args { let msg = if named_pos.contains(&i) {
// named argument // named argument
"named argument never used" "named argument never used"
} else { } else {

View file

@ -38,7 +38,7 @@ fn main() {
format!("{} {}", 1, 2, foo=1, bar=2); //~ ERROR: multiple unused formatting arguments format!("{} {}", 1, 2, foo=1, bar=2); //~ ERROR: multiple unused formatting arguments
format!("{foo}", foo=1, foo=2); //~ ERROR: duplicate argument format!("{foo}", foo=1, foo=2); //~ ERROR: duplicate argument
format!("", foo=1, 2); //~ ERROR: positional arguments cannot follow format!("{foo} {} {}", foo=1, 2); //~ ERROR: positional arguments cannot follow
// bad named arguments, #35082 // bad named arguments, #35082

View file

@ -146,11 +146,13 @@ note: previously here
LL | format!("{foo}", foo=1, foo=2); LL | format!("{foo}", foo=1, foo=2);
| ^ | ^
error: expected ident, positional arguments cannot follow named arguments error: positional arguments cannot follow named arguments
--> $DIR/ifmt-bad-arg.rs:41:24 --> $DIR/ifmt-bad-arg.rs:41:35
| |
LL | format!("", foo=1, 2); LL | format!("{foo} {} {}", foo=1, 2);
| ^ | - ^ positional arguments must be before named arguments
| |
| named argument
error: there is no argument named `valueb` error: there is no argument named `valueb`
--> $DIR/ifmt-bad-arg.rs:45:23 --> $DIR/ifmt-bad-arg.rs:45:23

View file

@ -1,9 +1,15 @@
fn main() { fn main() {
let foo = "";
let bar = "";
format!(); //~ ERROR requires at least a format string argument format!(); //~ ERROR requires at least a format string argument
format!(struct); //~ ERROR expected expression format!(struct); //~ ERROR expected expression
format!("s", name =); //~ ERROR expected expression format!("s", name =); //~ ERROR expected expression
format!("s", foo = foo, bar); //~ ERROR expected `=` format!(
format!("s", foo = struct); //~ ERROR expected expression "s {foo} {} {}",
foo = foo,
bar, //~ ERROR positional arguments cannot follow named arguments
);
format!("s {foo}", foo = struct); //~ ERROR expected expression
format!("s", struct); //~ ERROR expected expression format!("s", struct); //~ ERROR expected expression
// This error should come after parsing errors to ensure they are non-fatal. // This error should come after parsing errors to ensure they are non-fatal.

View file

@ -1,5 +1,5 @@
error: requires at least a format string argument error: requires at least a format string argument
--> $DIR/format-parse-errors.rs:2:5 --> $DIR/format-parse-errors.rs:4:5
| |
LL | format!(); LL | format!();
| ^^^^^^^^^^ | ^^^^^^^^^^
@ -7,37 +7,39 @@ LL | format!();
= note: this error originates in a macro outside of the current crate (in Nightly builds, run with -Z external-macro-backtrace for more info) = note: this error originates in a macro outside of the current crate (in Nightly builds, run with -Z external-macro-backtrace for more info)
error: expected expression, found keyword `struct` error: expected expression, found keyword `struct`
--> $DIR/format-parse-errors.rs:3:13 --> $DIR/format-parse-errors.rs:5:13
| |
LL | format!(struct); LL | format!(struct);
| ^^^^^^ expected expression | ^^^^^^ expected expression
error: expected expression, found end of macro arguments error: expected expression, found end of macro arguments
--> $DIR/format-parse-errors.rs:4:24 --> $DIR/format-parse-errors.rs:6:24
| |
LL | format!("s", name =); LL | format!("s", name =);
| ^ expected expression | ^ expected expression
error: expected `=`, found end of macro arguments error: positional arguments cannot follow named arguments
--> $DIR/format-parse-errors.rs:5:32 --> $DIR/format-parse-errors.rs:10:9
| |
LL | format!("s", foo = foo, bar); LL | foo = foo,
| ^ expected `=` | --- named argument
LL | bar,
| ^^^ positional arguments must be before named arguments
error: expected expression, found keyword `struct` error: expected expression, found keyword `struct`
--> $DIR/format-parse-errors.rs:6:24 --> $DIR/format-parse-errors.rs:12:30
| |
LL | format!("s", foo = struct); LL | format!("s {foo}", foo = struct);
| ^^^^^^ expected expression | ^^^^^^ expected expression
error: expected expression, found keyword `struct` error: expected expression, found keyword `struct`
--> $DIR/format-parse-errors.rs:7:18 --> $DIR/format-parse-errors.rs:13:18
| |
LL | format!("s", struct); LL | format!("s", struct);
| ^^^^^^ expected expression | ^^^^^^ expected expression
error: format argument must be a string literal error: format argument must be a string literal
--> $DIR/format-parse-errors.rs:10:13 --> $DIR/format-parse-errors.rs:16:13
| |
LL | format!(123); LL | format!(123);
| ^^^ | ^^^