From 423a5d3e858630e549640763d9022c18bdd68f7a Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Thu, 28 Nov 2019 10:49:13 -0800 Subject: [PATCH] Allow any identifier as format arg name Previously: error: invalid format string: invalid argument name `_x` --> src/main.rs:2:16 | 2 | println!("{_x}", a=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. --- src/libfmt_macros/lib.rs | 33 ++++++++-------- src/test/ui/fmt/format-string-error.rs | 6 ++- src/test/ui/fmt/format-string-error.stderr | 44 +++++++++++++--------- src/test/ui/ifmt.rs | 2 + 4 files changed, 49 insertions(+), 36 deletions(-) diff --git a/src/libfmt_macros/lib.rs b/src/libfmt_macros/lib.rs index 24b19028ac117..900c6ed5ff322 100644 --- a/src/libfmt_macros/lib.rs +++ b/src/libfmt_macros/lib.rs @@ -442,20 +442,9 @@ impl<'a> Parser<'a> { Some(ArgumentIs(i)) } else { match self.cur.peek() { - Some(&(_, c)) if c.is_alphabetic() => { + Some(&(_, c)) if rustc_lexer::is_id_start(c) => { Some(ArgumentNamed(Symbol::intern(self.word()))) } - Some(&(pos, c)) if c == '_' => { - let invalid_name = self.string(pos); - self.err_with_note(format!("invalid argument name `{}`", invalid_name), - "invalid argument name", - "argument names cannot start with an underscore", - self.to_span_index(pos).to( - self.to_span_index(pos + invalid_name.len()) - ), - ); - Some(ArgumentNamed(Symbol::intern(invalid_name))) - }, // This is an `ArgumentNext`. // Record the fact and do the resolution after parsing the @@ -611,22 +600,34 @@ impl<'a> Parser<'a> { /// Rust identifier, except that it can't start with `_` character. fn word(&mut self) -> &'a str { let start = match self.cur.peek() { - Some(&(pos, c)) if c != '_' && rustc_lexer::is_id_start(c) => { + Some(&(pos, c)) if rustc_lexer::is_id_start(c) => { self.cur.next(); pos } _ => { - return &self.input[..0]; + return ""; } }; + let mut end = None; while let Some(&(pos, c)) = self.cur.peek() { if rustc_lexer::is_id_continue(c) { self.cur.next(); } else { - return &self.input[start..pos]; + end = Some(pos); + break; } } - &self.input[start..self.input.len()] + let end = end.unwrap_or(self.input.len()); + let word = &self.input[start..end]; + if word == "_" { + self.err_with_note( + "invalid argument name `_`", + "invalid argument name", + "argument name cannot be a single underscore", + self.to_span_index(start).to(self.to_span_index(end)), + ); + } + word } /// Optionally parses an integer at the current position. This doesn't deal diff --git a/src/test/ui/fmt/format-string-error.rs b/src/test/ui/fmt/format-string-error.rs index 691c06a2402c5..eae4f3cb5479f 100644 --- a/src/test/ui/fmt/format-string-error.rs +++ b/src/test/ui/fmt/format-string-error.rs @@ -6,10 +6,12 @@ fn main() { println!("{{}}"); println!("}"); //~^ ERROR invalid format string: unmatched `}` found - let _ = format!("{_foo}", _foo = 6usize); - //~^ ERROR invalid format string: invalid argument name `_foo` let _ = format!("{_}", _ = 6usize); //~^ ERROR invalid format string: invalid argument name `_` + let _ = format!("{a:_}", a = "", _ = 0); + //~^ ERROR invalid format string: invalid argument name `_` + let _ = format!("{a:._$}", a = "", _ = 0); + //~^ ERROR invalid format string: invalid argument name `_` let _ = format!("{"); //~^ ERROR invalid format string: expected `'}'` but string was terminated let _ = format!("}"); diff --git a/src/test/ui/fmt/format-string-error.stderr b/src/test/ui/fmt/format-string-error.stderr index 32119b18774b2..8b018480fb082 100644 --- a/src/test/ui/fmt/format-string-error.stderr +++ b/src/test/ui/fmt/format-string-error.stderr @@ -16,24 +16,32 @@ LL | println!("}"); | = note: if you intended to print `}`, you can escape it using `}}` -error: invalid format string: invalid argument name `_foo` +error: invalid format string: invalid argument name `_` --> $DIR/format-string-error.rs:9:23 | -LL | let _ = format!("{_foo}", _foo = 6usize); - | ^^^^ invalid argument name in format string +LL | let _ = format!("{_}", _ = 6usize); + | ^ invalid argument name in format string | - = note: argument names cannot start with an underscore + = note: argument name cannot be a single underscore error: invalid format string: invalid argument name `_` - --> $DIR/format-string-error.rs:11:23 + --> $DIR/format-string-error.rs:11:25 | -LL | let _ = format!("{_}", _ = 6usize); - | ^ invalid argument name in format string +LL | let _ = format!("{a:_}", a = "", _ = 0); + | ^ invalid argument name in format string + | + = note: argument name cannot be a single underscore + +error: invalid format string: invalid argument name `_` + --> $DIR/format-string-error.rs:13:26 + | +LL | let _ = format!("{a:._$}", a = "", _ = 0); + | ^ invalid argument name in format string | - = note: argument names cannot start with an underscore + = note: argument name cannot be a single underscore error: invalid format string: expected `'}'` but string was terminated - --> $DIR/format-string-error.rs:13:23 + --> $DIR/format-string-error.rs:15:23 | LL | let _ = format!("{"); | -^ expected `'}'` in format string @@ -43,7 +51,7 @@ LL | let _ = format!("{"); = note: if you intended to print `{`, you can escape it using `{{` error: invalid format string: unmatched `}` found - --> $DIR/format-string-error.rs:15:22 + --> $DIR/format-string-error.rs:17:22 | LL | let _ = format!("}"); | ^ unmatched `}` in format string @@ -51,7 +59,7 @@ LL | let _ = format!("}"); = note: if you intended to print `}`, you can escape it using `}}` error: invalid format string: expected `'}'`, found `'\'` - --> $DIR/format-string-error.rs:17:23 + --> $DIR/format-string-error.rs:19:23 | LL | let _ = format!("{\}"); | -^ expected `}` in format string @@ -61,7 +69,7 @@ LL | let _ = format!("{\}"); = note: if you intended to print `{`, you can escape it using `{{` error: invalid format string: expected `'}'` but string was terminated - --> $DIR/format-string-error.rs:19:35 + --> $DIR/format-string-error.rs:21:35 | LL | let _ = format!("\n\n\n{\n\n\n"); | - ^ expected `'}'` in format string @@ -71,7 +79,7 @@ LL | let _ = format!("\n\n\n{\n\n\n"); = note: if you intended to print `{`, you can escape it using `{{` error: invalid format string: expected `'}'` but string was terminated - --> $DIR/format-string-error.rs:25:3 + --> $DIR/format-string-error.rs:27:3 | LL | {"###); | -^ expected `'}'` in format string @@ -81,7 +89,7 @@ LL | {"###); = note: if you intended to print `{`, you can escape it using `{{` error: invalid format string: expected `'}'` but string was terminated - --> $DIR/format-string-error.rs:33:1 + --> $DIR/format-string-error.rs:35:1 | LL | { | - because of this opening brace @@ -92,7 +100,7 @@ LL | "###); = note: if you intended to print `{`, you can escape it using `{{` error: invalid format string: unmatched `}` found - --> $DIR/format-string-error.rs:39:2 + --> $DIR/format-string-error.rs:41:2 | LL | } | ^ unmatched `}` in format string @@ -100,7 +108,7 @@ LL | } = note: if you intended to print `}`, you can escape it using `}}` error: invalid format string: unmatched `}` found - --> $DIR/format-string-error.rs:47:9 + --> $DIR/format-string-error.rs:49:9 | LL | } | ^ unmatched `}` in format string @@ -108,10 +116,10 @@ LL | } = note: if you intended to print `}`, you can escape it using `}}` error: 3 positional arguments in format string, but there are 2 arguments - --> $DIR/format-string-error.rs:51:15 + --> $DIR/format-string-error.rs:53:15 | LL | println!("{} {} {}", 1, 2); | ^^ ^^ ^^ - - -error: aborting due to 13 previous errors +error: aborting due to 14 previous errors diff --git a/src/test/ui/ifmt.rs b/src/test/ui/ifmt.rs index 841be20ef8645..1a070843cc446 100644 --- a/src/test/ui/ifmt.rs +++ b/src/test/ui/ifmt.rs @@ -90,6 +90,7 @@ pub fn main() { t!(format!("{foo} {bar}", foo=0, bar=1), "0 1"); t!(format!("{foo} {1} {bar} {0}", 0, 1, foo=2, bar=3), "2 1 3 0"); t!(format!("{} {0}", "a"), "a a"); + t!(format!("{_foo}", _foo = 6usize), "6"); t!(format!("{foo_bar}", foo_bar=1), "1"); t!(format!("{}", 5 + 5), "10"); t!(format!("{:#4}", C), "☃123"); @@ -125,6 +126,7 @@ pub fn main() { t!(format!("{:.*}", 4, "aaaaaaaaaaaaaaaaaa"), "aaaa"); t!(format!("{:.1$}", "aaaaaaaaaaaaaaaaaa", 4), "aaaa"); t!(format!("{:.a$}", "aaaaaaaaaaaaaaaaaa", a=4), "aaaa"); + t!(format!("{:._a$}", "aaaaaaaaaaaaaaaaaa", _a=4), "aaaa"); t!(format!("{:1$}", "a", 4), "a "); t!(format!("{1:0$}", 4, "a"), "a "); t!(format!("{:a$}", "a", a=4), "a ");