about summary refs log tree commit diff
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2020-09-02 19:29:27 +0000
committerbors <bors@rust-lang.org>2020-09-02 19:29:27 +0000
commit80fc9b0ecb29050d45b17c64af004200afd3cfc2 (patch)
tree3d0269d8f9ecb027bd59a942462003ed9e01e4ea
parenta167485e279be8a11c9c09d0dddaf1d7c5724f1c (diff)
parent3524c3ef4371d0bf4dd03568a004039f18c154f7 (diff)
downloadrust-80fc9b0ecb29050d45b17c64af004200afd3cfc2.tar.gz
rust-80fc9b0ecb29050d45b17c64af004200afd3cfc2.zip
Auto merge of #76160 - scileo:format-recovery, r=petrochenkov
Improve recovery on malformed format call

The token following a format expression should be a comma. However, when it is replaced with a similar token (such as a dot), then the corresponding error is emitted, but the token is treated as a comma, and the parsing step continues.

r? @petrochenkov
-rw-r--r--compiler/rustc_builtin_macros/src/format.rs28
-rw-r--r--compiler/rustc_parse/src/parser/mod.rs4
-rw-r--r--src/test/ui/codemap_tests/bad-format-args.rs2
-rw-r--r--src/test/ui/codemap_tests/bad-format-args.stderr2
-rw-r--r--src/test/ui/fmt/incorrect-first-separator.stderr32
-rw-r--r--src/test/ui/fmt/incorrect-separator.rs (renamed from src/test/ui/fmt/incorrect-first-separator.rs)15
-rw-r--r--src/test/ui/fmt/incorrect-separator.stderr44
-rw-r--r--src/test/ui/macros/missing-comma.rs2
-rw-r--r--src/test/ui/macros/missing-comma.stderr2
-rw-r--r--src/test/ui/parser/unicode-quote-chars.rs2
-rw-r--r--src/test/ui/parser/unicode-quote-chars.stderr2
-rw-r--r--src/tools/clippy/tests/ui/issue-3145.rs2
-rw-r--r--src/tools/clippy/tests/ui/issue-3145.stderr4
13 files changed, 88 insertions, 53 deletions
diff --git a/compiler/rustc_builtin_macros/src/format.rs b/compiler/rustc_builtin_macros/src/format.rs
index 48506148ed9..5d6f791f137 100644
--- a/compiler/rustc_builtin_macros/src/format.rs
+++ b/compiler/rustc_builtin_macros/src/format.rs
@@ -161,14 +161,26 @@ fn parse_args<'a>(
     while p.token != token::Eof {
         if !p.eat(&token::Comma) {
             if first {
-                // After `format!(""` we always expect *only* a comma...
-                let mut err = ecx.struct_span_err(p.token.span, "expected token: `,`");
-                err.span_label(p.token.span, "expected `,`");
-                p.maybe_annotate_with_ascription(&mut err, false);
-                return Err(err);
-            } else {
-                // ...after that delegate to `expect` to also include the other expected tokens.
-                let _ = p.expect(&token::Comma)?;
+                p.clear_expected_tokens();
+            }
+
+            // `Parser::expect` tries to recover using the
+            // `Parser::unexpected_try_recover` function. This function is able
+            // to recover if the expected token is a closing delimiter.
+            //
+            // As `,` is not a closing delimiter, it will always return an `Err`
+            // variant.
+            let mut err = p.expect(&token::Comma).unwrap_err();
+
+            match token::TokenKind::Comma.similar_tokens() {
+                Some(tks) if tks.contains(&p.token.kind) => {
+                    // If a similar token is found, then it may be a typo. We
+                    // consider it as a comma, and continue parsing.
+                    err.emit();
+                    p.bump();
+                }
+                // Otherwise stop the parsing and return the error.
+                _ => return Err(err),
             }
         }
         first = false;
diff --git a/compiler/rustc_parse/src/parser/mod.rs b/compiler/rustc_parse/src/parser/mod.rs
index d90a61cd34a..84edfecad19 100644
--- a/compiler/rustc_parse/src/parser/mod.rs
+++ b/compiler/rustc_parse/src/parser/mod.rs
@@ -1234,6 +1234,10 @@ impl<'a> Parser<'a> {
                 *t == token::OpenDelim(token::Brace) || *t == token::BinOp(token::Star)
             })
     }
+
+    pub fn clear_expected_tokens(&mut self) {
+        self.expected_tokens.clear();
+    }
 }
 
 crate fn make_unclosed_delims_error(
diff --git a/src/test/ui/codemap_tests/bad-format-args.rs b/src/test/ui/codemap_tests/bad-format-args.rs
index dff248344a5..e89a45a84f5 100644
--- a/src/test/ui/codemap_tests/bad-format-args.rs
+++ b/src/test/ui/codemap_tests/bad-format-args.rs
@@ -1,5 +1,5 @@
 fn main() {
     format!(); //~ ERROR requires at least a format string argument
-    format!("" 1); //~ ERROR expected token: `,`
+    format!("" 1); //~ ERROR expected `,`, found `1`
     format!("", 1 1); //~ ERROR expected one of
 }
diff --git a/src/test/ui/codemap_tests/bad-format-args.stderr b/src/test/ui/codemap_tests/bad-format-args.stderr
index 96d7b07b0e2..5ed023e1f21 100644
--- a/src/test/ui/codemap_tests/bad-format-args.stderr
+++ b/src/test/ui/codemap_tests/bad-format-args.stderr
@@ -6,7 +6,7 @@ LL |     format!();
    |
    = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info)
 
-error: expected token: `,`
+error: expected `,`, found `1`
   --> $DIR/bad-format-args.rs:3:16
    |
 LL |     format!("" 1);
diff --git a/src/test/ui/fmt/incorrect-first-separator.stderr b/src/test/ui/fmt/incorrect-first-separator.stderr
deleted file mode 100644
index 60d2a82855e..00000000000
--- a/src/test/ui/fmt/incorrect-first-separator.stderr
+++ /dev/null
@@ -1,32 +0,0 @@
-error: unknown start of token: \u{326}
-  --> $DIR/incorrect-first-separator.rs:19:28
-   |
-LL |     format!("A number: {}" ̦ iter::once(42).next().unwrap());
-   |                            ^
-
-error: expected token: `,`
-  --> $DIR/incorrect-first-separator.rs:7:27
-   |
-LL |     format!("A number: {}". iter::once(42).next().unwrap());
-   |                           ^ expected `,`
-
-error: expected token: `,`
-  --> $DIR/incorrect-first-separator.rs:12:28
-   |
-LL |     format!("A number: {}" / iter::once(42).next().unwrap());
-   |                            ^ expected `,`
-
-error: expected token: `,`
-  --> $DIR/incorrect-first-separator.rs:15:27
-   |
-LL |     format!("A number: {}"; iter::once(42).next().unwrap());
-   |                           ^ expected `,`
-
-error: expected token: `,`
-  --> $DIR/incorrect-first-separator.rs:19:30
-   |
-LL |     format!("A number: {}" ̦ iter::once(42).next().unwrap());
-   |                             ^^^^ expected `,`
-
-error: aborting due to 5 previous errors
-
diff --git a/src/test/ui/fmt/incorrect-first-separator.rs b/src/test/ui/fmt/incorrect-separator.rs
index 0b097fdfab8..b8d2e4a3473 100644
--- a/src/test/ui/fmt/incorrect-first-separator.rs
+++ b/src/test/ui/fmt/incorrect-separator.rs
@@ -5,18 +5,25 @@ use std::iter;
 
 fn main() {
     format!("A number: {}". iter::once(42).next().unwrap());
-    //~^ ERROR expected token: `,`
+    //~^ ERROR expected `,`, found `.`
 
     // Other kind of types are also checked:
 
     format!("A number: {}" / iter::once(42).next().unwrap());
-    //~^ ERROR expected token: `,`
+    //~^ ERROR expected `,`, found `/`
 
     format!("A number: {}"; iter::once(42).next().unwrap());
-    //~^ ERROR expected token: `,`
+    //~^ ERROR expected `,`, found `;`
 
     // Note: this character is an COMBINING COMMA BELOW unicode char
     format!("A number: {}" ̦ iter::once(42).next().unwrap());
-    //~^ ERROR expected token: `,`
+    //~^ ERROR expected `,`, found `iter`
     //~^^ ERROR unknown start of token: \u{326}
+
+    // Here recovery is tested.
+    // If the `compile_error!` is emitted, then the parser is able to recover
+    // from the incorrect first separator.
+    format!("{}". compile_error!("fail"));
+    //~^ ERROR expected `,`, found `.`
+    //~^^ ERROR fail
 }
diff --git a/src/test/ui/fmt/incorrect-separator.stderr b/src/test/ui/fmt/incorrect-separator.stderr
new file mode 100644
index 00000000000..5a3e5515bb9
--- /dev/null
+++ b/src/test/ui/fmt/incorrect-separator.stderr
@@ -0,0 +1,44 @@
+error: unknown start of token: \u{326}
+  --> $DIR/incorrect-separator.rs:19:28
+   |
+LL |     format!("A number: {}" ̦ iter::once(42).next().unwrap());
+   |                            ^
+
+error: expected `,`, found `.`
+  --> $DIR/incorrect-separator.rs:7:27
+   |
+LL |     format!("A number: {}". iter::once(42).next().unwrap());
+   |                           ^ expected `,`
+
+error: expected `,`, found `/`
+  --> $DIR/incorrect-separator.rs:12:28
+   |
+LL |     format!("A number: {}" / iter::once(42).next().unwrap());
+   |                            ^ expected `,`
+
+error: expected `,`, found `;`
+  --> $DIR/incorrect-separator.rs:15:27
+   |
+LL |     format!("A number: {}"; iter::once(42).next().unwrap());
+   |                           ^ expected `,`
+
+error: expected `,`, found `iter`
+  --> $DIR/incorrect-separator.rs:19:30
+   |
+LL |     format!("A number: {}" ̦ iter::once(42).next().unwrap());
+   |                             ^^^^ expected `,`
+
+error: expected `,`, found `.`
+  --> $DIR/incorrect-separator.rs:26:17
+   |
+LL |     format!("{}". compile_error!("fail"));
+   |                 ^ expected `,`
+
+error: fail
+  --> $DIR/incorrect-separator.rs:26:19
+   |
+LL |     format!("{}". compile_error!("fail"));
+   |                   ^^^^^^^^^^^^^^^^^^^^^^
+
+error: aborting due to 7 previous errors
+
diff --git a/src/test/ui/macros/missing-comma.rs b/src/test/ui/macros/missing-comma.rs
index 2002fed6c93..92f8a779505 100644
--- a/src/test/ui/macros/missing-comma.rs
+++ b/src/test/ui/macros/missing-comma.rs
@@ -17,7 +17,7 @@ macro_rules! check {
 
 fn main() {
     println!("{}" a);
-    //~^ ERROR expected token: `,`
+    //~^ ERROR expected `,`, found `a`
     foo!(a b);
     //~^ ERROR no rules expected the token `b`
     foo!(a, b, c, d e);
diff --git a/src/test/ui/macros/missing-comma.stderr b/src/test/ui/macros/missing-comma.stderr
index f96848f8239..6da92bdea19 100644
--- a/src/test/ui/macros/missing-comma.stderr
+++ b/src/test/ui/macros/missing-comma.stderr
@@ -1,4 +1,4 @@
-error: expected token: `,`
+error: expected `,`, found `a`
   --> $DIR/missing-comma.rs:19:19
    |
 LL |     println!("{}" a);
diff --git a/src/test/ui/parser/unicode-quote-chars.rs b/src/test/ui/parser/unicode-quote-chars.rs
index 1812dad81af..eeaea3628bb 100644
--- a/src/test/ui/parser/unicode-quote-chars.rs
+++ b/src/test/ui/parser/unicode-quote-chars.rs
@@ -6,5 +6,5 @@ fn main() {
     //~^^ HELP Unicode characters '“' (Left Double Quotation Mark) and '”' (Right Double Quotation Mark) look like '"' (Quotation Mark), but are not
     //~^^^ ERROR unknown start of token: \u{201d}
     //~^^^^ HELP Unicode character '”' (Right Double Quotation Mark) looks like '"' (Quotation Mark), but it is not
-    //~^^^^^ ERROR expected token: `,`
+    //~^^^^^ ERROR expected `,`, found `world`
 }
diff --git a/src/test/ui/parser/unicode-quote-chars.stderr b/src/test/ui/parser/unicode-quote-chars.stderr
index 4b0cb96ed21..d9ec92b3f8a 100644
--- a/src/test/ui/parser/unicode-quote-chars.stderr
+++ b/src/test/ui/parser/unicode-quote-chars.stderr
@@ -20,7 +20,7 @@ help: Unicode character '”' (Right Double Quotation Mark) looks like '"' (Quot
 LL |     println!(“hello world");
    |                          ^
 
-error: expected token: `,`
+error: expected `,`, found `world`
   --> $DIR/unicode-quote-chars.rs:4:21
    |
 LL |     println!(“hello world”);
diff --git a/src/tools/clippy/tests/ui/issue-3145.rs b/src/tools/clippy/tests/ui/issue-3145.rs
index f497d5550af..586d13647d1 100644
--- a/src/tools/clippy/tests/ui/issue-3145.rs
+++ b/src/tools/clippy/tests/ui/issue-3145.rs
@@ -1,3 +1,3 @@
 fn main() {
-    println!("{}" a); //~ERROR expected token: `,`
+    println!("{}" a); //~ERROR expected `,`, found `a`
 }
diff --git a/src/tools/clippy/tests/ui/issue-3145.stderr b/src/tools/clippy/tests/ui/issue-3145.stderr
index cb0d95f5e26..a35032aa150 100644
--- a/src/tools/clippy/tests/ui/issue-3145.stderr
+++ b/src/tools/clippy/tests/ui/issue-3145.stderr
@@ -1,7 +1,7 @@
-error: expected token: `,`
+error: expected `,`, found `a`
   --> $DIR/issue-3145.rs:2:19
    |
-LL |     println!("{}" a); //~ERROR expected token: `,`
+LL |     println!("{}" a); //~ERROR expected `,`, found `a`
    |                   ^ expected `,`
 
 error: aborting due to previous error