about summary refs log tree commit diff
diff options
context:
space:
mode:
authorDylan DPC <99973273+Dylan-DPC@users.noreply.github.com>2023-05-09 12:33:46 +0530
committerGitHub <noreply@github.com>2023-05-09 12:33:46 +0530
commit8c51701b8a5ac3a2fbfbad9f6d386ec443344293 (patch)
tree279439a8af041610464a269c0f0c2c7558db4879
parentdbd090c655b4a4389fe51cbd3ad6f401e2953dbe (diff)
parent4d219d066626c49fdec3d8fa143a94c81150b633 (diff)
downloadrust-8c51701b8a5ac3a2fbfbad9f6d386ec443344293.tar.gz
rust-8c51701b8a5ac3a2fbfbad9f6d386ec443344293.zip
Rollup merge of #111120 - chenyukang:yukang-suggest-let, r=Nilstrieb
Suggest let for possible binding with ty

Origin from https://github.com/rust-lang/rust/pull/109128#discussion_r1179866137

r? `@Nilstrieb`
-rw-r--r--compiler/rustc_macros/src/diagnostics/mod.rs2
-rw-r--r--compiler/rustc_parse/messages.ftl1
-rw-r--r--compiler/rustc_parse/src/errors.rs12
-rw-r--r--compiler/rustc_parse/src/parser/diagnostics.rs31
-rw-r--r--compiler/rustc_parse/src/parser/stmt.rs17
-rw-r--r--tests/ui/suggestions/type-ascription-instead-of-let.fixed11
-rw-r--r--tests/ui/suggestions/type-ascription-instead-of-let.rs4
-rw-r--r--tests/ui/suggestions/type-ascription-instead-of-let.stderr7
-rw-r--r--tests/ui/type/missing-let-in-binding-2.fixed5
-rw-r--r--tests/ui/type/missing-let-in-binding-2.rs5
-rw-r--r--tests/ui/type/missing-let-in-binding-2.stderr13
-rw-r--r--tests/ui/type/missing-let-in-binding-3.rs5
-rw-r--r--tests/ui/type/missing-let-in-binding-3.stderr10
-rw-r--r--tests/ui/type/missing-let-in-binding-4.rs5
-rw-r--r--tests/ui/type/missing-let-in-binding-4.stderr10
15 files changed, 122 insertions, 16 deletions
diff --git a/compiler/rustc_macros/src/diagnostics/mod.rs b/compiler/rustc_macros/src/diagnostics/mod.rs
index bd84681cbb4..a536eb3b04e 100644
--- a/compiler/rustc_macros/src/diagnostics/mod.rs
+++ b/compiler/rustc_macros/src/diagnostics/mod.rs
@@ -140,7 +140,7 @@ pub fn lint_diagnostic_derive(s: Structure<'_>) -> TokenStream {
 /// ```fluent
 /// parser_expected_identifier = expected identifier
 ///
-/// parser_expected_identifier-found = expected identifier, found {$found}
+/// parser_expected_identifier_found = expected identifier, found {$found}
 ///
 /// parser_raw_identifier = escape `{$ident}` to use it as an identifier
 /// ```
diff --git a/compiler/rustc_parse/messages.ftl b/compiler/rustc_parse/messages.ftl
index 711447a0af0..1bbf833e3cd 100644
--- a/compiler/rustc_parse/messages.ftl
+++ b/compiler/rustc_parse/messages.ftl
@@ -343,6 +343,7 @@ parse_expected_identifier = expected identifier
 parse_sugg_escape_identifier = escape `{$ident_name}` to use it as an identifier
 
 parse_sugg_remove_comma = remove this comma
+parse_sugg_add_let_for_stmt = you might have meant to introduce a new binding
 
 parse_expected_semi_found_reserved_identifier_str = expected `;`, found reserved identifier `{$token}`
 parse_expected_semi_found_keyword_str = expected `;`, found keyword `{$token}`
diff --git a/compiler/rustc_parse/src/errors.rs b/compiler/rustc_parse/src/errors.rs
index 0810f0efef9..b6aeaf3d59f 100644
--- a/compiler/rustc_parse/src/errors.rs
+++ b/compiler/rustc_parse/src/errors.rs
@@ -907,6 +907,18 @@ pub(crate) struct SuggRemoveComma {
 }
 
 #[derive(Subdiagnostic)]
+#[suggestion(
+    parse_sugg_add_let_for_stmt,
+    style = "verbose",
+    applicability = "maybe-incorrect",
+    code = "let "
+)]
+pub(crate) struct SuggAddMissingLetStmt {
+    #[primary_span]
+    pub span: Span,
+}
+
+#[derive(Subdiagnostic)]
 pub(crate) enum ExpectedIdentifierFound {
     #[label(parse_expected_identifier_found_reserved_identifier)]
     ReservedIdentifier(#[primary_span] Span),
diff --git a/compiler/rustc_parse/src/parser/diagnostics.rs b/compiler/rustc_parse/src/parser/diagnostics.rs
index 36883bd2172..3002f23da75 100644
--- a/compiler/rustc_parse/src/parser/diagnostics.rs
+++ b/compiler/rustc_parse/src/parser/diagnostics.rs
@@ -13,7 +13,7 @@ use crate::errors::{
     IncorrectUseOfAwait, ParenthesesInForHead, ParenthesesInForHeadSugg,
     PatternMethodParamWithoutBody, QuestionMarkInType, QuestionMarkInTypeSugg, SelfParamNotFirst,
     StructLiteralBodyWithoutPath, StructLiteralBodyWithoutPathSugg, StructLiteralNeedingParens,
-    StructLiteralNeedingParensSugg, SuggEscapeIdentifier, SuggRemoveComma,
+    StructLiteralNeedingParensSugg, SuggAddMissingLetStmt, SuggEscapeIdentifier, SuggRemoveComma,
     UnexpectedConstInGenericParam, UnexpectedConstParamDeclaration,
     UnexpectedConstParamDeclarationSugg, UnmatchedAngleBrackets, UseEqInstead,
 };
@@ -32,8 +32,8 @@ use rustc_ast::{
 use rustc_ast_pretty::pprust;
 use rustc_data_structures::fx::FxHashSet;
 use rustc_errors::{
-    pluralize, Applicability, Diagnostic, DiagnosticBuilder, DiagnosticMessage, ErrorGuaranteed,
-    FatalError, Handler, IntoDiagnostic, MultiSpan, PResult,
+    pluralize, AddToDiagnostic, Applicability, Diagnostic, DiagnosticBuilder, DiagnosticMessage,
+    ErrorGuaranteed, FatalError, Handler, IntoDiagnostic, MultiSpan, PResult,
 };
 use rustc_session::errors::ExprParenthesesNeeded;
 use rustc_span::source_map::Spanned;
@@ -1006,6 +1006,31 @@ impl<'a> Parser<'a> {
         Err(e)
     }
 
+    /// Suggest add the missing `let` before the identifier in stmt
+    /// `a: Ty = 1` -> `let a: Ty = 1`
+    pub(super) fn suggest_add_missing_let_for_stmt(
+        &mut self,
+        err: &mut DiagnosticBuilder<'a, ErrorGuaranteed>,
+    ) {
+        if self.token == token::Colon {
+            let prev_span = self.prev_token.span.shrink_to_lo();
+            let snapshot = self.create_snapshot_for_diagnostic();
+            self.bump();
+            match self.parse_ty() {
+                Ok(_) => {
+                    if self.token == token::Eq {
+                        let sugg = SuggAddMissingLetStmt { span: prev_span };
+                        sugg.add_to_diagnostic(err);
+                    }
+                }
+                Err(e) => {
+                    e.cancel();
+                }
+            }
+            self.restore_snapshot(snapshot);
+        }
+    }
+
     /// Check to see if a pair of chained operators looks like an attempt at chained comparison,
     /// e.g. `1 < x <= 3`. If so, suggest either splitting the comparison into two, or
     /// parenthesising the leftmost comparison.
diff --git a/compiler/rustc_parse/src/parser/stmt.rs b/compiler/rustc_parse/src/parser/stmt.rs
index a0bd086053e..03279124177 100644
--- a/compiler/rustc_parse/src/parser/stmt.rs
+++ b/compiler/rustc_parse/src/parser/stmt.rs
@@ -103,7 +103,13 @@ impl<'a> Parser<'a> {
                 ForceCollect::Yes => {
                     self.collect_tokens_no_attrs(|this| this.parse_stmt_path_start(lo, attrs))?
                 }
-                ForceCollect::No => self.parse_stmt_path_start(lo, attrs)?,
+                ForceCollect::No => match self.parse_stmt_path_start(lo, attrs) {
+                    Ok(stmt) => stmt,
+                    Err(mut err) => {
+                        self.suggest_add_missing_let_for_stmt(&mut err);
+                        return Err(err);
+                    }
+                },
             }
         } else if let Some(item) = self.parse_item_common(
             attrs.clone(),
@@ -559,7 +565,6 @@ impl<'a> Parser<'a> {
                     if self.token == token::Colon {
                         // if next token is following a colon, it's likely a path
                         // and we can suggest a path separator
-                        let ident_span = self.prev_token.span;
                         self.bump();
                         if self.token.span.lo() == self.prev_token.span.hi() {
                             err.span_suggestion_verbose(
@@ -569,14 +574,6 @@ impl<'a> Parser<'a> {
                                 Applicability::MaybeIncorrect,
                             );
                         }
-                        if self.look_ahead(1, |token| token == &token::Eq) {
-                            err.span_suggestion_verbose(
-                                ident_span.shrink_to_lo(),
-                                "you might have meant to introduce a new binding",
-                                "let ",
-                                Applicability::MaybeIncorrect,
-                            );
-                        }
                         if self.sess.unstable_features.is_nightly_build() {
                             // FIXME(Nilstrieb): Remove this again after a few months.
                             err.note("type ascription syntax has been removed, see issue #101728 <https://github.com/rust-lang/rust/issues/101728>");
diff --git a/tests/ui/suggestions/type-ascription-instead-of-let.fixed b/tests/ui/suggestions/type-ascription-instead-of-let.fixed
new file mode 100644
index 00000000000..e3d03b6f22a
--- /dev/null
+++ b/tests/ui/suggestions/type-ascription-instead-of-let.fixed
@@ -0,0 +1,11 @@
+// run-rustfix
+
+fn fun(x: i32) -> i32 { x }
+
+fn main() {
+    let _closure_annotated = |value: i32| -> i32 {
+        let temp: i32 = fun(5i32);
+        //~^ ERROR expected identifier, found `:`
+        temp + value + 1
+    };
+}
diff --git a/tests/ui/suggestions/type-ascription-instead-of-let.rs b/tests/ui/suggestions/type-ascription-instead-of-let.rs
index 5ad60243298..6e1c86f9671 100644
--- a/tests/ui/suggestions/type-ascription-instead-of-let.rs
+++ b/tests/ui/suggestions/type-ascription-instead-of-let.rs
@@ -1,7 +1,9 @@
+// run-rustfix
+
 fn fun(x: i32) -> i32 { x }
 
 fn main() {
-    let closure_annotated = |value: i32| -> i32 {
+    let _closure_annotated = |value: i32| -> i32 {
         temp: i32 = fun(5i32);
         //~^ ERROR expected identifier, found `:`
         temp + value + 1
diff --git a/tests/ui/suggestions/type-ascription-instead-of-let.stderr b/tests/ui/suggestions/type-ascription-instead-of-let.stderr
index fb697b0ccfd..065b1f4d353 100644
--- a/tests/ui/suggestions/type-ascription-instead-of-let.stderr
+++ b/tests/ui/suggestions/type-ascription-instead-of-let.stderr
@@ -1,8 +1,13 @@
 error: expected identifier, found `:`
-  --> $DIR/type-ascription-instead-of-let.rs:5:13
+  --> $DIR/type-ascription-instead-of-let.rs:7:13
    |
 LL |         temp: i32 = fun(5i32);
    |             ^ expected identifier
+   |
+help: you might have meant to introduce a new binding
+   |
+LL |         let temp: i32 = fun(5i32);
+   |         +++
 
 error: aborting due to previous error
 
diff --git a/tests/ui/type/missing-let-in-binding-2.fixed b/tests/ui/type/missing-let-in-binding-2.fixed
new file mode 100644
index 00000000000..d64013c8c83
--- /dev/null
+++ b/tests/ui/type/missing-let-in-binding-2.fixed
@@ -0,0 +1,5 @@
+// run-rustfix
+
+fn main() {
+    let _v: Vec<i32> = vec![1, 2, 3]; //~ ERROR expected identifier, found `:`
+}
diff --git a/tests/ui/type/missing-let-in-binding-2.rs b/tests/ui/type/missing-let-in-binding-2.rs
new file mode 100644
index 00000000000..f95f7bef215
--- /dev/null
+++ b/tests/ui/type/missing-let-in-binding-2.rs
@@ -0,0 +1,5 @@
+// run-rustfix
+
+fn main() {
+    _v: Vec<i32> = vec![1, 2, 3]; //~ ERROR expected identifier, found `:`
+}
diff --git a/tests/ui/type/missing-let-in-binding-2.stderr b/tests/ui/type/missing-let-in-binding-2.stderr
new file mode 100644
index 00000000000..2e10125943e
--- /dev/null
+++ b/tests/ui/type/missing-let-in-binding-2.stderr
@@ -0,0 +1,13 @@
+error: expected identifier, found `:`
+  --> $DIR/missing-let-in-binding-2.rs:4:7
+   |
+LL |     _v: Vec<i32> = vec![1, 2, 3];
+   |       ^ expected identifier
+   |
+help: you might have meant to introduce a new binding
+   |
+LL |     let _v: Vec<i32> = vec![1, 2, 3];
+   |     +++
+
+error: aborting due to previous error
+
diff --git a/tests/ui/type/missing-let-in-binding-3.rs b/tests/ui/type/missing-let-in-binding-3.rs
new file mode 100644
index 00000000000..d56b1393336
--- /dev/null
+++ b/tests/ui/type/missing-let-in-binding-3.rs
@@ -0,0 +1,5 @@
+struct A {
+    : :u8, //~ ERROR expected identifier, found `:`
+}
+
+fn main() {}
diff --git a/tests/ui/type/missing-let-in-binding-3.stderr b/tests/ui/type/missing-let-in-binding-3.stderr
new file mode 100644
index 00000000000..ca828ce37eb
--- /dev/null
+++ b/tests/ui/type/missing-let-in-binding-3.stderr
@@ -0,0 +1,10 @@
+error: expected identifier, found `:`
+  --> $DIR/missing-let-in-binding-3.rs:2:5
+   |
+LL | struct A {
+   |        - while parsing this struct
+LL |     : :u8,
+   |     ^ expected identifier
+
+error: aborting due to previous error
+
diff --git a/tests/ui/type/missing-let-in-binding-4.rs b/tests/ui/type/missing-let-in-binding-4.rs
new file mode 100644
index 00000000000..879a6fedcd6
--- /dev/null
+++ b/tests/ui/type/missing-let-in-binding-4.rs
@@ -0,0 +1,5 @@
+struct A {
+    : u8 =, //~ ERROR expected identifier, found `:`
+}
+
+fn main() {}
diff --git a/tests/ui/type/missing-let-in-binding-4.stderr b/tests/ui/type/missing-let-in-binding-4.stderr
new file mode 100644
index 00000000000..e6f173a6658
--- /dev/null
+++ b/tests/ui/type/missing-let-in-binding-4.stderr
@@ -0,0 +1,10 @@
+error: expected identifier, found `:`
+  --> $DIR/missing-let-in-binding-4.rs:2:5
+   |
+LL | struct A {
+   |        - while parsing this struct
+LL |     : u8 =,
+   |     ^ expected identifier
+
+error: aborting due to previous error
+