]> git.lizzy.rs Git - rust.git/commitdiff
Allow any identifier as format arg name
authorDavid Tolnay <dtolnay@gmail.com>
Thu, 28 Nov 2019 18:49:13 +0000 (10:49 -0800)
committerDavid Tolnay <dtolnay@gmail.com>
Thu, 28 Nov 2019 19:24:51 +0000 (11:24 -0800)
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
src/test/ui/fmt/format-string-error.rs
src/test/ui/fmt/format-string-error.stderr
src/test/ui/ifmt.rs

index 24b19028ac1179e65cff77613606a4eb8666fd03..900c6ed5ff32235d487ec6a187a0f8970ad8ef9f 100644 (file)
@@ -442,20 +442,9 @@ fn position(&mut self) -> Option<Position> {
             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 @@ fn count(&mut self, start: usize) -> (Count, Option<InnerSpan>) {
     /// 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
index 691c06a2402c5352ec39c4650ff3d82bb0d76505..eae4f3cb5479fc3ab5e602dc82fd15904e405696 100644 (file)
@@ -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!("}");
index 32119b18774b25585c1f00395a2a80b5fd04dbf0..8b018480fb082cec80ee4f39d733a0a6da03da53 100644 (file)
@@ -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
 
index 841be20ef8645c17e6dbc1e342ee71b376b15c6b..1a070843cc4468af85d2b13ce64a1e3c715089b3 100644 (file)
@@ -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   ");