Skip to content

Commit f69b071

Browse files
committed
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
2 parents e9d2227 + 33ec182 commit f69b071

File tree

5 files changed

+53
-34
lines changed

5 files changed

+53
-34
lines changed

src/libsyntax_ext/format.rs

+24-15
Original file line numberDiff line numberDiff line change
@@ -146,16 +146,13 @@ fn parse_args<'a>(
146146
if p.token == token::Eof {
147147
break;
148148
} // accept trailing commas
149-
if named || (p.token.is_ident() && p.look_ahead(1, |t| *t == token::Eq)) {
149+
if p.token.is_ident() && p.look_ahead(1, |t| *t == token::Eq) {
150150
named = true;
151151
let name = if let token::Ident(name, _) = p.token.kind {
152152
p.bump();
153153
name
154154
} else {
155-
return Err(ecx.struct_span_err(
156-
p.token.span,
157-
"expected ident, positional arguments cannot follow named arguments",
158-
));
155+
unreachable!();
159156
};
160157

161158
p.expect(&token::Eq)?;
@@ -176,6 +173,17 @@ fn parse_args<'a>(
176173
args.push(e);
177174
} else {
178175
let e = p.parse_expr()?;
176+
if named {
177+
let mut err = ecx.struct_span_err(
178+
e.span,
179+
"positional arguments cannot follow named arguments",
180+
);
181+
err.span_label(e.span, "positional arguments must be before named arguments");
182+
for (_, pos) in &names {
183+
err.span_label(args[*pos].span, "named argument");
184+
}
185+
err.emit();
186+
}
179187
args.push(e);
180188
}
181189
}
@@ -721,13 +729,14 @@ pub fn expand_format_args_nl<'cx>(
721729

722730
/// Take the various parts of `format_args!(efmt, args..., name=names...)`
723731
/// and construct the appropriate formatting expression.
724-
pub fn expand_preparsed_format_args(ecx: &mut ExtCtxt<'_>,
725-
sp: Span,
726-
efmt: P<ast::Expr>,
727-
args: Vec<P<ast::Expr>>,
728-
names: FxHashMap<Symbol, usize>,
729-
append_newline: bool)
730-
-> P<ast::Expr> {
732+
pub fn expand_preparsed_format_args(
733+
ecx: &mut ExtCtxt<'_>,
734+
sp: Span,
735+
efmt: P<ast::Expr>,
736+
args: Vec<P<ast::Expr>>,
737+
names: FxHashMap<Symbol, usize>,
738+
append_newline: bool,
739+
) -> P<ast::Expr> {
731740
// NOTE: this verbose way of initializing `Vec<Vec<ArgumentType>>` is because
732741
// `ArgumentType` does not derive `Clone`.
733742
let arg_types: Vec<_> = (0..args.len()).map(|_| Vec::new()).collect();
@@ -906,6 +915,8 @@ pub fn expand_preparsed_format_args(ecx: &mut ExtCtxt<'_>,
906915
.map(|span| fmt.span.from_inner(*span))
907916
.collect();
908917

918+
let named_pos: FxHashSet<usize> = names.values().cloned().collect();
919+
909920
let mut cx = Context {
910921
ecx,
911922
args,
@@ -971,14 +982,12 @@ pub fn expand_preparsed_format_args(ecx: &mut ExtCtxt<'_>,
971982
}
972983

973984
// Make sure that all arguments were used and all arguments have types.
974-
let num_pos_args = cx.args.len() - cx.names.len();
975-
976985
let errs = cx.arg_types
977986
.iter()
978987
.enumerate()
979988
.filter(|(i, ty)| ty.is_empty() && !cx.count_positions.contains_key(&i))
980989
.map(|(i, _)| {
981-
let msg = if i >= num_pos_args {
990+
let msg = if named_pos.contains(&i) {
982991
// named argument
983992
"named argument never used"
984993
} else {

src/test/ui/if/ifmt-bad-arg.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ fn main() {
3838
format!("{} {}", 1, 2, foo=1, bar=2); //~ ERROR: multiple unused formatting arguments
3939

4040
format!("{foo}", foo=1, foo=2); //~ ERROR: duplicate argument
41-
format!("", foo=1, 2); //~ ERROR: positional arguments cannot follow
41+
format!("{foo} {} {}", foo=1, 2); //~ ERROR: positional arguments cannot follow
4242

4343
// bad named arguments, #35082
4444

src/test/ui/if/ifmt-bad-arg.stderr

+6-4
Original file line numberDiff line numberDiff line change
@@ -146,11 +146,13 @@ note: previously here
146146
LL | format!("{foo}", foo=1, foo=2);
147147
| ^
148148

149-
error: expected ident, positional arguments cannot follow named arguments
150-
--> $DIR/ifmt-bad-arg.rs:41:24
149+
error: positional arguments cannot follow named arguments
150+
--> $DIR/ifmt-bad-arg.rs:41:35
151151
|
152-
LL | format!("", foo=1, 2);
153-
| ^
152+
LL | format!("{foo} {} {}", foo=1, 2);
153+
| - ^ positional arguments must be before named arguments
154+
| |
155+
| named argument
154156

155157
error: there is no argument named `valueb`
156158
--> $DIR/ifmt-bad-arg.rs:45:23

src/test/ui/macros/format-parse-errors.rs

+8-2
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,15 @@
11
fn main() {
2+
let foo = "";
3+
let bar = "";
24
format!(); //~ ERROR requires at least a format string argument
35
format!(struct); //~ ERROR expected expression
46
format!("s", name =); //~ ERROR expected expression
5-
format!("s", foo = foo, bar); //~ ERROR expected `=`
6-
format!("s", foo = struct); //~ ERROR expected expression
7+
format!(
8+
"s {foo} {} {}",
9+
foo = foo,
10+
bar, //~ ERROR positional arguments cannot follow named arguments
11+
);
12+
format!("s {foo}", foo = struct); //~ ERROR expected expression
713
format!("s", struct); //~ ERROR expected expression
814

915
// This error should come after parsing errors to ensure they are non-fatal.

src/test/ui/macros/format-parse-errors.stderr

+14-12
Original file line numberDiff line numberDiff line change
@@ -1,43 +1,45 @@
11
error: requires at least a format string argument
2-
--> $DIR/format-parse-errors.rs:2:5
2+
--> $DIR/format-parse-errors.rs:4:5
33
|
44
LL | format!();
55
| ^^^^^^^^^^
66
|
77
= note: this error originates in a macro outside of the current crate (in Nightly builds, run with -Z external-macro-backtrace for more info)
88

99
error: expected expression, found keyword `struct`
10-
--> $DIR/format-parse-errors.rs:3:13
10+
--> $DIR/format-parse-errors.rs:5:13
1111
|
1212
LL | format!(struct);
1313
| ^^^^^^ expected expression
1414

1515
error: expected expression, found end of macro arguments
16-
--> $DIR/format-parse-errors.rs:4:24
16+
--> $DIR/format-parse-errors.rs:6:24
1717
|
1818
LL | format!("s", name =);
1919
| ^ expected expression
2020

21-
error: expected `=`, found end of macro arguments
22-
--> $DIR/format-parse-errors.rs:5:32
21+
error: positional arguments cannot follow named arguments
22+
--> $DIR/format-parse-errors.rs:10:9
2323
|
24-
LL | format!("s", foo = foo, bar);
25-
| ^ expected `=`
24+
LL | foo = foo,
25+
| --- named argument
26+
LL | bar,
27+
| ^^^ positional arguments must be before named arguments
2628

2729
error: expected expression, found keyword `struct`
28-
--> $DIR/format-parse-errors.rs:6:24
30+
--> $DIR/format-parse-errors.rs:12:30
2931
|
30-
LL | format!("s", foo = struct);
31-
| ^^^^^^ expected expression
32+
LL | format!("s {foo}", foo = struct);
33+
| ^^^^^^ expected expression
3234

3335
error: expected expression, found keyword `struct`
34-
--> $DIR/format-parse-errors.rs:7:18
36+
--> $DIR/format-parse-errors.rs:13:18
3537
|
3638
LL | format!("s", struct);
3739
| ^^^^^^ expected expression
3840

3941
error: format argument must be a string literal
40-
--> $DIR/format-parse-errors.rs:10:13
42+
--> $DIR/format-parse-errors.rs:16:13
4143
|
4244
LL | format!(123);
4345
| ^^^

0 commit comments

Comments
 (0)