about summary refs log tree commit diff
diff options
context:
space:
mode:
authoryukang <moorekang@gmail.com>2022-10-27 14:43:15 +0800
committeryukang <moorekang@gmail.com>2022-11-08 14:10:04 +0800
commit667b15bb0ebb36683a2de31cff68ad2b5b0d3f57 (patch)
tree82d85b103a002b71b3d436135ce3df209f2ad482
parent57d3c58ed6e0faf89a62411f96c000ffc9fd3937 (diff)
downloadrust-667b15bb0ebb36683a2de31cff68ad2b5b0d3f57.tar.gz
rust-667b15bb0ebb36683a2de31cff68ad2b5b0d3f57.zip
fix #103587, Recover from common if let syntax mistakes/typos
-rw-r--r--compiler/rustc_error_messages/locales/en-US/parser.ftl3
-rw-r--r--compiler/rustc_infer/src/infer/error_reporting/mod.rs74
-rw-r--r--compiler/rustc_parse/src/errors.rs9
-rw-r--r--compiler/rustc_parse/src/parser/expr.rs16
-rw-r--r--src/test/ui/inference/issue-103587.rs12
-rw-r--r--src/test/ui/inference/issue-103587.stderr40
-rw-r--r--src/test/ui/suggestions/if-let-typo.stderr15
7 files changed, 165 insertions, 4 deletions
diff --git a/compiler/rustc_error_messages/locales/en-US/parser.ftl b/compiler/rustc_error_messages/locales/en-US/parser.ftl
index 455ff34f724..6d42b23fb3a 100644
--- a/compiler/rustc_error_messages/locales/en-US/parser.ftl
+++ b/compiler/rustc_error_messages/locales/en-US/parser.ftl
@@ -125,6 +125,9 @@ parser_if_expression_missing_condition = missing condition for `if` expression
 
 parser_expected_expression_found_let = expected expression, found `let` statement
 
+parser_expect_eq_instead_of_eqeq = expected `=`, found `==`
+    .suggestion = consider using `=` here
+
 parser_expected_else_block = expected `{"{"}`, found {$first_tok}
     .label = expected an `if` or a block after this `else`
     .suggestion = add an `if` if this is the condition of a chained `else if` statement
diff --git a/compiler/rustc_infer/src/infer/error_reporting/mod.rs b/compiler/rustc_infer/src/infer/error_reporting/mod.rs
index 3dbc9b9f3f9..ee9e59968be 100644
--- a/compiler/rustc_infer/src/infer/error_reporting/mod.rs
+++ b/compiler/rustc_infer/src/infer/error_reporting/mod.rs
@@ -1,3 +1,4 @@
+// ignore-tidy-filelength
 //! Error Reporting Code for the inference engine
 //!
 //! Because of the way inference, and in particular region inference,
@@ -58,12 +59,14 @@ use crate::traits::{
     StatementAsExpression,
 };
 
+use hir::intravisit::{walk_expr, walk_stmt};
 use rustc_data_structures::fx::{FxIndexMap, FxIndexSet};
 use rustc_errors::{pluralize, struct_span_err, Diagnostic, ErrorGuaranteed, IntoDiagnosticArg};
 use rustc_errors::{Applicability, DiagnosticBuilder, DiagnosticStyledString, MultiSpan};
 use rustc_hir as hir;
 use rustc_hir::def::DefKind;
 use rustc_hir::def_id::{DefId, LocalDefId};
+use rustc_hir::intravisit::Visitor;
 use rustc_hir::lang_items::LangItem;
 use rustc_hir::Node;
 use rustc_middle::dep_graph::DepContext;
@@ -2333,6 +2336,11 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
                                 }
                             }
                         }
+                        // For code `if Some(..) = expr `, the type mismatch may be expected `bool` but found `()`,
+                        // we try to suggest to add the missing `let` for `if let Some(..) = expr`
+                        (ty::Bool, ty::Tuple(list)) => if list.len() == 0 {
+                            self.suggest_let_for_letchains(&mut err, &trace.cause, span);
+                        }
                         _ => {}
                     }
                 }
@@ -2357,6 +2365,72 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
         diag
     }
 
+    /// Try to find code with pattern `if Some(..) = expr`
+    /// use a `visitor` to mark the `if` which its span contains given error span,
+    /// and then try to find a assignment in the `cond` part, which span is equal with error span
+    fn suggest_let_for_letchains(
+        &self,
+        err: &mut Diagnostic,
+        cause: &ObligationCause<'_>,
+        span: Span,
+    ) {
+        let hir = self.tcx.hir();
+        let fn_hir_id = hir.get_parent_node(cause.body_id);
+        if let Some(node) = self.tcx.hir().find(fn_hir_id) &&
+            let hir::Node::Item(hir::Item {
+                    kind: hir::ItemKind::Fn(_sig, _, body_id), ..
+                }) = node {
+        let body = hir.body(*body_id);
+
+        /// Find the if expression with given span
+        struct IfVisitor {
+            pub result: bool,
+            pub found_if: bool,
+            pub err_span: Span,
+        }
+
+        impl<'v> Visitor<'v> for IfVisitor {
+            fn visit_expr(&mut self, ex: &'v hir::Expr<'v>) {
+                if self.result { return; }
+                match ex.kind {
+                    hir::ExprKind::If(cond, _, _) => {
+                        self.found_if = true;
+                        walk_expr(self, cond);
+                        self.found_if = false;
+                    }
+                    _ => walk_expr(self, ex),
+                }
+            }
+
+            fn visit_stmt(&mut self, ex: &'v hir::Stmt<'v>) {
+                if let hir::StmtKind::Local(hir::Local {
+                        span, pat: hir::Pat{..}, ty: None, init: Some(_), ..
+                    }) = &ex.kind
+                    && self.found_if
+                    && span.eq(&self.err_span) {
+                        self.result = true;
+                }
+                walk_stmt(self, ex);
+            }
+
+            fn visit_body(&mut self, body: &'v hir::Body<'v>) {
+                hir::intravisit::walk_body(self, body);
+            }
+        }
+
+        let mut visitor = IfVisitor { err_span: span, found_if: false, result: false };
+        visitor.visit_body(&body);
+        if visitor.result {
+                err.span_suggestion_verbose(
+                    span.shrink_to_lo(),
+                    "consider adding `let`",
+                    "let ".to_string(),
+                    Applicability::MachineApplicable,
+                );
+            }
+        }
+    }
+
     fn emit_tuple_wrap_err(
         &self,
         err: &mut Diagnostic,
diff --git a/compiler/rustc_parse/src/errors.rs b/compiler/rustc_parse/src/errors.rs
index 0924c853715..89d5fe3d9da 100644
--- a/compiler/rustc_parse/src/errors.rs
+++ b/compiler/rustc_parse/src/errors.rs
@@ -421,6 +421,15 @@ pub(crate) struct ExpectedExpressionFoundLet {
 }
 
 #[derive(Diagnostic)]
+#[diag(parser_expect_eq_instead_of_eqeq)]
+pub(crate) struct ExpectedEqForLetExpr {
+    #[primary_span]
+    pub span: Span,
+    #[suggestion_verbose(applicability = "maybe-incorrect", code = "=")]
+    pub sugg_span: Span,
+}
+
+#[derive(Diagnostic)]
 #[diag(parser_expected_else_block)]
 pub(crate) struct ExpectedElseBlock {
     #[primary_span]
diff --git a/compiler/rustc_parse/src/parser/expr.rs b/compiler/rustc_parse/src/parser/expr.rs
index 4a1162b9599..565371668a8 100644
--- a/compiler/rustc_parse/src/parser/expr.rs
+++ b/compiler/rustc_parse/src/parser/expr.rs
@@ -9,9 +9,9 @@ use crate::errors::{
     ArrayBracketsInsteadOfSpaces, ArrayBracketsInsteadOfSpacesSugg, AsyncMoveOrderIncorrect,
     BinaryFloatLiteralNotSupported, BracesForStructLiteral, CatchAfterTry, CommaAfterBaseStruct,
     ComparisonInterpretedAsGeneric, ComparisonOrShiftInterpretedAsGenericSugg,
-    DoCatchSyntaxRemoved, DotDotDot, EqFieldInit, ExpectedElseBlock, ExpectedExpressionFoundLet,
-    FieldExpressionWithGeneric, FloatLiteralRequiresIntegerPart, FoundExprWouldBeStmt,
-    HexadecimalFloatLiteralNotSupported, IfExpressionMissingCondition,
+    DoCatchSyntaxRemoved, DotDotDot, EqFieldInit, ExpectedElseBlock, ExpectedEqForLetExpr,
+    ExpectedExpressionFoundLet, FieldExpressionWithGeneric, FloatLiteralRequiresIntegerPart,
+    FoundExprWouldBeStmt, HexadecimalFloatLiteralNotSupported, IfExpressionMissingCondition,
     IfExpressionMissingThenBlock, IfExpressionMissingThenBlockSub, IntLiteralTooLarge,
     InvalidBlockMacroSegment, InvalidComparisonOperator, InvalidComparisonOperatorSub,
     InvalidFloatLiteralSuffix, InvalidFloatLiteralWidth, InvalidIntLiteralWidth,
@@ -2334,7 +2334,15 @@ impl<'a> Parser<'a> {
             RecoverColon::Yes,
             CommaRecoveryMode::LikelyTuple,
         )?;
-        self.expect(&token::Eq)?;
+        if self.token == token::EqEq {
+            self.sess.emit_err(ExpectedEqForLetExpr {
+                span: self.token.span,
+                sugg_span: self.token.span,
+            });
+            self.bump();
+        } else {
+            self.expect(&token::Eq)?;
+        }
         let expr = self.with_res(self.restrictions | Restrictions::NO_STRUCT_LITERAL, |this| {
             this.parse_assoc_expr_with(1 + prec_let_scrutinee_needs_par(), None.into())
         })?;
diff --git a/src/test/ui/inference/issue-103587.rs b/src/test/ui/inference/issue-103587.rs
new file mode 100644
index 00000000000..11536f9f4cc
--- /dev/null
+++ b/src/test/ui/inference/issue-103587.rs
@@ -0,0 +1,12 @@
+fn main() {
+    let x = Some(123);
+
+    if let Some(_) == x {}
+    //~^ ERROR expected `=`, found `==`
+
+    if Some(_) = x {}
+    //~^ ERROR mismatched types
+
+    if None = x { }
+    //~^ ERROR mismatched types
+}
diff --git a/src/test/ui/inference/issue-103587.stderr b/src/test/ui/inference/issue-103587.stderr
new file mode 100644
index 00000000000..b373fbfbb94
--- /dev/null
+++ b/src/test/ui/inference/issue-103587.stderr
@@ -0,0 +1,40 @@
+error: expected `=`, found `==`
+  --> $DIR/issue-103587.rs:4:20
+   |
+LL |     if let Some(_) == x {}
+   |                    ^^
+   |
+help: consider using `=` here
+   |
+LL |     if let Some(_) = x {}
+   |                    ~
+
+error[E0308]: mismatched types
+  --> $DIR/issue-103587.rs:7:8
+   |
+LL |     if Some(_) = x {}
+   |        ^^^^^^^^^^^ expected `bool`, found `()`
+   |
+help: consider adding `let`
+   |
+LL |     if let Some(_) = x {}
+   |        +++
+
+error[E0308]: mismatched types
+  --> $DIR/issue-103587.rs:10:8
+   |
+LL |     if None = x { }
+   |        ^^^^^^^^ expected `bool`, found `()`
+   |
+help: you might have meant to use pattern matching
+   |
+LL |     if let None = x { }
+   |        +++
+help: you might have meant to compare for equality
+   |
+LL |     if None == x { }
+   |              +
+
+error: aborting due to 3 previous errors
+
+For more information about this error, try `rustc --explain E0308`.
diff --git a/src/test/ui/suggestions/if-let-typo.stderr b/src/test/ui/suggestions/if-let-typo.stderr
index 3d9ac40ec36..02148b7f7ad 100644
--- a/src/test/ui/suggestions/if-let-typo.stderr
+++ b/src/test/ui/suggestions/if-let-typo.stderr
@@ -25,12 +25,22 @@ error[E0308]: mismatched types
    |
 LL |     if Some(x) = foo {}
    |        ^^^^^^^^^^^^^ expected `bool`, found `()`
+   |
+help: consider adding `let`
+   |
+LL |     if let Some(x) = foo {}
+   |        +++
 
 error[E0308]: mismatched types
   --> $DIR/if-let-typo.rs:6:8
    |
 LL |     if Some(foo) = bar {}
    |        ^^^^^^^^^^^^^^^ expected `bool`, found `()`
+   |
+help: consider adding `let`
+   |
+LL |     if let Some(foo) = bar {}
+   |        +++
 
 error[E0308]: mismatched types
   --> $DIR/if-let-typo.rs:7:8
@@ -51,6 +61,11 @@ error[E0308]: mismatched types
    |
 LL |     if Some(3) = foo {}
    |        ^^^^^^^^^^^^^ expected `bool`, found `()`
+   |
+help: consider adding `let`
+   |
+LL |     if let Some(3) = foo {}
+   |        +++
 
 error: aborting due to 7 previous errors