Skip to content

Commit b14d9c2

Browse files
authored
Rollup merge of #66847 - dtolnay:_fmt, r=joshtriplett
Allow any identifier as format arg name Previously: ```console error: invalid format string: invalid argument name `_x` --> src/main.rs:2:16 | 2 | println!("{_x}", _x=0); | ^^ invalid argument name in format string | = note: argument names cannot start with an underscore ``` Not supporting identifiers starting with underscore appears to have been an arbitrary limitation from 2013 in code that was most likely never reviewed: https://github.com/rust-lang/rust/pull/8245/files#diff-0347868ef389c805e97636623e4a4ea6R277 The error message was dutifully improved in #50610 but is there any reason that leading underscore would be a special case? This commit updates the format_args parser to accept identifiers with leading underscores.
2 parents 27710d2 + 423a5d3 commit b14d9c2

File tree

4 files changed

+49
-36
lines changed

4 files changed

+49
-36
lines changed

src/libfmt_macros/lib.rs

+17-16
Original file line numberDiff line numberDiff line change
@@ -442,20 +442,9 @@ impl<'a> Parser<'a> {
442442
Some(ArgumentIs(i))
443443
} else {
444444
match self.cur.peek() {
445-
Some(&(_, c)) if c.is_alphabetic() => {
445+
Some(&(_, c)) if rustc_lexer::is_id_start(c) => {
446446
Some(ArgumentNamed(Symbol::intern(self.word())))
447447
}
448-
Some(&(pos, c)) if c == '_' => {
449-
let invalid_name = self.string(pos);
450-
self.err_with_note(format!("invalid argument name `{}`", invalid_name),
451-
"invalid argument name",
452-
"argument names cannot start with an underscore",
453-
self.to_span_index(pos).to(
454-
self.to_span_index(pos + invalid_name.len())
455-
),
456-
);
457-
Some(ArgumentNamed(Symbol::intern(invalid_name)))
458-
},
459448

460449
// This is an `ArgumentNext`.
461450
// Record the fact and do the resolution after parsing the
@@ -611,22 +600,34 @@ impl<'a> Parser<'a> {
611600
/// Rust identifier, except that it can't start with `_` character.
612601
fn word(&mut self) -> &'a str {
613602
let start = match self.cur.peek() {
614-
Some(&(pos, c)) if c != '_' && rustc_lexer::is_id_start(c) => {
603+
Some(&(pos, c)) if rustc_lexer::is_id_start(c) => {
615604
self.cur.next();
616605
pos
617606
}
618607
_ => {
619-
return &self.input[..0];
608+
return "";
620609
}
621610
};
611+
let mut end = None;
622612
while let Some(&(pos, c)) = self.cur.peek() {
623613
if rustc_lexer::is_id_continue(c) {
624614
self.cur.next();
625615
} else {
626-
return &self.input[start..pos];
616+
end = Some(pos);
617+
break;
627618
}
628619
}
629-
&self.input[start..self.input.len()]
620+
let end = end.unwrap_or(self.input.len());
621+
let word = &self.input[start..end];
622+
if word == "_" {
623+
self.err_with_note(
624+
"invalid argument name `_`",
625+
"invalid argument name",
626+
"argument name cannot be a single underscore",
627+
self.to_span_index(start).to(self.to_span_index(end)),
628+
);
629+
}
630+
word
630631
}
631632

632633
/// Optionally parses an integer at the current position. This doesn't deal

src/test/ui/fmt/format-string-error.rs

+4-2
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,12 @@ fn main() {
66
println!("{{}}");
77
println!("}");
88
//~^ ERROR invalid format string: unmatched `}` found
9-
let _ = format!("{_foo}", _foo = 6usize);
10-
//~^ ERROR invalid format string: invalid argument name `_foo`
119
let _ = format!("{_}", _ = 6usize);
1210
//~^ ERROR invalid format string: invalid argument name `_`
11+
let _ = format!("{a:_}", a = "", _ = 0);
12+
//~^ ERROR invalid format string: invalid argument name `_`
13+
let _ = format!("{a:._$}", a = "", _ = 0);
14+
//~^ ERROR invalid format string: invalid argument name `_`
1315
let _ = format!("{");
1416
//~^ ERROR invalid format string: expected `'}'` but string was terminated
1517
let _ = format!("}");

src/test/ui/fmt/format-string-error.stderr

+26-18
Original file line numberDiff line numberDiff line change
@@ -16,24 +16,32 @@ LL | println!("}");
1616
|
1717
= note: if you intended to print `}`, you can escape it using `}}`
1818

19-
error: invalid format string: invalid argument name `_foo`
19+
error: invalid format string: invalid argument name `_`
2020
--> $DIR/format-string-error.rs:9:23
2121
|
22-
LL | let _ = format!("{_foo}", _foo = 6usize);
23-
| ^^^^ invalid argument name in format string
22+
LL | let _ = format!("{_}", _ = 6usize);
23+
| ^ invalid argument name in format string
2424
|
25-
= note: argument names cannot start with an underscore
25+
= note: argument name cannot be a single underscore
2626

2727
error: invalid format string: invalid argument name `_`
28-
--> $DIR/format-string-error.rs:11:23
28+
--> $DIR/format-string-error.rs:11:25
2929
|
30-
LL | let _ = format!("{_}", _ = 6usize);
31-
| ^ invalid argument name in format string
30+
LL | let _ = format!("{a:_}", a = "", _ = 0);
31+
| ^ invalid argument name in format string
32+
|
33+
= note: argument name cannot be a single underscore
34+
35+
error: invalid format string: invalid argument name `_`
36+
--> $DIR/format-string-error.rs:13:26
37+
|
38+
LL | let _ = format!("{a:._$}", a = "", _ = 0);
39+
| ^ invalid argument name in format string
3240
|
33-
= note: argument names cannot start with an underscore
41+
= note: argument name cannot be a single underscore
3442

3543
error: invalid format string: expected `'}'` but string was terminated
36-
--> $DIR/format-string-error.rs:13:23
44+
--> $DIR/format-string-error.rs:15:23
3745
|
3846
LL | let _ = format!("{");
3947
| -^ expected `'}'` in format string
@@ -43,15 +51,15 @@ LL | let _ = format!("{");
4351
= note: if you intended to print `{`, you can escape it using `{{`
4452

4553
error: invalid format string: unmatched `}` found
46-
--> $DIR/format-string-error.rs:15:22
54+
--> $DIR/format-string-error.rs:17:22
4755
|
4856
LL | let _ = format!("}");
4957
| ^ unmatched `}` in format string
5058
|
5159
= note: if you intended to print `}`, you can escape it using `}}`
5260

5361
error: invalid format string: expected `'}'`, found `'\'`
54-
--> $DIR/format-string-error.rs:17:23
62+
--> $DIR/format-string-error.rs:19:23
5563
|
5664
LL | let _ = format!("{\}");
5765
| -^ expected `}` in format string
@@ -61,7 +69,7 @@ LL | let _ = format!("{\}");
6169
= note: if you intended to print `{`, you can escape it using `{{`
6270

6371
error: invalid format string: expected `'}'` but string was terminated
64-
--> $DIR/format-string-error.rs:19:35
72+
--> $DIR/format-string-error.rs:21:35
6573
|
6674
LL | let _ = format!("\n\n\n{\n\n\n");
6775
| - ^ expected `'}'` in format string
@@ -71,7 +79,7 @@ LL | let _ = format!("\n\n\n{\n\n\n");
7179
= note: if you intended to print `{`, you can escape it using `{{`
7280

7381
error: invalid format string: expected `'}'` but string was terminated
74-
--> $DIR/format-string-error.rs:25:3
82+
--> $DIR/format-string-error.rs:27:3
7583
|
7684
LL | {"###);
7785
| -^ expected `'}'` in format string
@@ -81,7 +89,7 @@ LL | {"###);
8189
= note: if you intended to print `{`, you can escape it using `{{`
8290

8391
error: invalid format string: expected `'}'` but string was terminated
84-
--> $DIR/format-string-error.rs:33:1
92+
--> $DIR/format-string-error.rs:35:1
8593
|
8694
LL | {
8795
| - because of this opening brace
@@ -92,26 +100,26 @@ LL | "###);
92100
= note: if you intended to print `{`, you can escape it using `{{`
93101

94102
error: invalid format string: unmatched `}` found
95-
--> $DIR/format-string-error.rs:39:2
103+
--> $DIR/format-string-error.rs:41:2
96104
|
97105
LL | }
98106
| ^ unmatched `}` in format string
99107
|
100108
= note: if you intended to print `}`, you can escape it using `}}`
101109

102110
error: invalid format string: unmatched `}` found
103-
--> $DIR/format-string-error.rs:47:9
111+
--> $DIR/format-string-error.rs:49:9
104112
|
105113
LL | }
106114
| ^ unmatched `}` in format string
107115
|
108116
= note: if you intended to print `}`, you can escape it using `}}`
109117

110118
error: 3 positional arguments in format string, but there are 2 arguments
111-
--> $DIR/format-string-error.rs:51:15
119+
--> $DIR/format-string-error.rs:53:15
112120
|
113121
LL | println!("{} {} {}", 1, 2);
114122
| ^^ ^^ ^^ - -
115123

116-
error: aborting due to 13 previous errors
124+
error: aborting due to 14 previous errors
117125

src/test/ui/ifmt.rs

+2
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,7 @@ pub fn main() {
9090
t!(format!("{foo} {bar}", foo=0, bar=1), "0 1");
9191
t!(format!("{foo} {1} {bar} {0}", 0, 1, foo=2, bar=3), "2 1 3 0");
9292
t!(format!("{} {0}", "a"), "a a");
93+
t!(format!("{_foo}", _foo = 6usize), "6");
9394
t!(format!("{foo_bar}", foo_bar=1), "1");
9495
t!(format!("{}", 5 + 5), "10");
9596
t!(format!("{:#4}", C), "☃123");
@@ -125,6 +126,7 @@ pub fn main() {
125126
t!(format!("{:.*}", 4, "aaaaaaaaaaaaaaaaaa"), "aaaa");
126127
t!(format!("{:.1$}", "aaaaaaaaaaaaaaaaaa", 4), "aaaa");
127128
t!(format!("{:.a$}", "aaaaaaaaaaaaaaaaaa", a=4), "aaaa");
129+
t!(format!("{:._a$}", "aaaaaaaaaaaaaaaaaa", _a=4), "aaaa");
128130
t!(format!("{:1$}", "a", 4), "a ");
129131
t!(format!("{1:0$}", 4, "a"), "a ");
130132
t!(format!("{:a$}", "a", a=4), "a ");

0 commit comments

Comments
 (0)