about summary refs log tree commit diff
diff options
context:
space:
mode:
authorMichael Goulet <michael@errs.io>2023-01-19 21:13:01 +0000
committerMichael Goulet <michael@errs.io>2023-02-10 18:18:08 +0000
commit0eba2f3c16b4e2792b75762c7643f3a09c9f1a3d (patch)
tree9e0cdabc94034431c8ff72b0d994bed72e50adef
parentd1ac43a9b9a8250d858705b0796dfed6186e18db (diff)
downloadrust-0eba2f3c16b4e2792b75762c7643f3a09c9f1a3d.tar.gz
rust-0eba2f3c16b4e2792b75762c7643f3a09c9f1a3d.zip
Suggest fn call on pattern type mismatch
-rw-r--r--compiler/rustc_hir_typeck/src/_match.rs2
-rw-r--r--compiler/rustc_hir_typeck/src/check.rs2
-rw-r--r--compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs6
-rw-r--r--compiler/rustc_hir_typeck/src/pat.rs19
-rw-r--r--tests/ui/suggestions/suggest-call-on-pat-mismatch.rs16
-rw-r--r--tests/ui/suggestions/suggest-call-on-pat-mismatch.stderr33
6 files changed, 68 insertions, 10 deletions
diff --git a/compiler/rustc_hir_typeck/src/_match.rs b/compiler/rustc_hir_typeck/src/_match.rs
index 88fb2653586..e19ef2ff3bf 100644
--- a/compiler/rustc_hir_typeck/src/_match.rs
+++ b/compiler/rustc_hir_typeck/src/_match.rs
@@ -41,7 +41,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         // #55810: Type check patterns first so we get types for all bindings.
         let scrut_span = scrut.span.find_ancestor_inside(expr.span).unwrap_or(scrut.span);
         for arm in arms {
-            self.check_pat_top(&arm.pat, scrutinee_ty, Some(scrut_span), true);
+            self.check_pat_top(&arm.pat, scrutinee_ty, Some(scrut_span), Some(scrut));
         }
 
         // Now typecheck the blocks.
diff --git a/compiler/rustc_hir_typeck/src/check.rs b/compiler/rustc_hir_typeck/src/check.rs
index 1c70c1b71e7..cc515e6c853 100644
--- a/compiler/rustc_hir_typeck/src/check.rs
+++ b/compiler/rustc_hir_typeck/src/check.rs
@@ -90,7 +90,7 @@ pub(super) fn check_fn<'a, 'tcx>(
     for (idx, (param_ty, param)) in inputs_fn.chain(maybe_va_list).zip(body.params).enumerate() {
         // Check the pattern.
         let ty_span = try { inputs_hir?.get(idx)?.span };
-        fcx.check_pat_top(&param.pat, param_ty, ty_span, false);
+        fcx.check_pat_top(&param.pat, param_ty, ty_span, None);
 
         // Check that argument is Sized.
         // The check for a non-trivial pattern is a hack to avoid duplicate warnings
diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs
index 2a1265600de..9c7a84ce198 100644
--- a/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs
+++ b/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs
@@ -1330,11 +1330,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
 
         // Does the expected pattern type originate from an expression and what is the span?
         let (origin_expr, ty_span) = match (decl.ty, decl.init) {
-            (Some(ty), _) => (false, Some(ty.span)), // Bias towards the explicit user type.
+            (Some(ty), _) => (None, Some(ty.span)), // Bias towards the explicit user type.
             (_, Some(init)) => {
-                (true, Some(init.span.find_ancestor_inside(decl.span).unwrap_or(init.span)))
+                (Some(init), Some(init.span.find_ancestor_inside(decl.span).unwrap_or(init.span)))
             } // No explicit type; so use the scrutinee.
-            _ => (false, None), // We have `let $pat;`, so the expected type is unconstrained.
+            _ => (None, None), // We have `let $pat;`, so the expected type is unconstrained.
         };
 
         // Type check the pattern. Override if necessary to avoid knock-on errors.
diff --git a/compiler/rustc_hir_typeck/src/pat.rs b/compiler/rustc_hir_typeck/src/pat.rs
index 46799245222..52236ae56ee 100644
--- a/compiler/rustc_hir_typeck/src/pat.rs
+++ b/compiler/rustc_hir_typeck/src/pat.rs
@@ -46,7 +46,7 @@ struct TopInfo<'tcx> {
     /// Was the origin of the `span` from a scrutinee expression?
     ///
     /// Otherwise there is no scrutinee and it could be e.g. from the type of a formal parameter.
-    origin_expr: bool,
+    origin_expr: Option<&'tcx hir::Expr<'tcx>>,
     /// The span giving rise to the `expected` type, if one could be provided.
     ///
     /// If `origin_expr` is `true`, then this is the span of the scrutinee as in:
@@ -74,7 +74,8 @@ struct TopInfo<'tcx> {
 
 impl<'tcx> FnCtxt<'_, 'tcx> {
     fn pattern_cause(&self, ti: TopInfo<'tcx>, cause_span: Span) -> ObligationCause<'tcx> {
-        let code = Pattern { span: ti.span, root_ty: ti.expected, origin_expr: ti.origin_expr };
+        let code =
+            Pattern { span: ti.span, root_ty: ti.expected, origin_expr: ti.origin_expr.is_some() };
         self.cause(cause_span, code)
     }
 
@@ -85,7 +86,14 @@ impl<'tcx> FnCtxt<'_, 'tcx> {
         actual: Ty<'tcx>,
         ti: TopInfo<'tcx>,
     ) -> Option<DiagnosticBuilder<'tcx, ErrorGuaranteed>> {
-        self.demand_eqtype_with_origin(&self.pattern_cause(ti, cause_span), expected, actual)
+        let mut diag =
+            self.demand_eqtype_with_origin(&self.pattern_cause(ti, cause_span), expected, actual)?;
+        if let Some(expr) = ti.origin_expr {
+            self.suggest_fn_call(&mut diag, expr, expected, |output| {
+                self.can_eq(self.param_env, output, actual).is_ok()
+            });
+        }
+        Some(diag)
     }
 
     fn demand_eqtype_pat(
@@ -127,7 +135,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         pat: &'tcx Pat<'tcx>,
         expected: Ty<'tcx>,
         span: Option<Span>,
-        origin_expr: bool,
+        origin_expr: Option<&'tcx hir::Expr<'tcx>>,
     ) {
         let info = TopInfo { expected, origin_expr, span };
         self.check_pat(pat, expected, INITIAL_BM, info);
@@ -2146,7 +2154,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
             err.help("the semantics of slice patterns changed recently; see issue #62254");
         } else if self.autoderef(span, expected_ty)
             .any(|(ty, _)| matches!(ty.kind(), ty::Slice(..) | ty::Array(..)))
-            && let (Some(span), true) = (ti.span, ti.origin_expr)
+            && let Some(span) = ti.span
+            && let Some(_) = ti.origin_expr
             && let Ok(snippet) = self.tcx.sess.source_map().span_to_snippet(span)
         {
             let ty = self.resolve_vars_if_possible(ti.expected);
diff --git a/tests/ui/suggestions/suggest-call-on-pat-mismatch.rs b/tests/ui/suggestions/suggest-call-on-pat-mismatch.rs
new file mode 100644
index 00000000000..657dd9c22c2
--- /dev/null
+++ b/tests/ui/suggestions/suggest-call-on-pat-mismatch.rs
@@ -0,0 +1,16 @@
+enum E {
+    One(i32, i32),
+}
+
+fn main() {
+    let var = E::One;
+    if let E::One(var1, var2) = var {
+        //~^ ERROR mismatched types
+        //~| HELP use parentheses to construct this tuple variant
+        println!("{var1} {var2}");
+    }
+
+    let Some(x) = Some;
+    //~^ ERROR mismatched types
+    //~| HELP use parentheses to construct this tuple variant
+}
diff --git a/tests/ui/suggestions/suggest-call-on-pat-mismatch.stderr b/tests/ui/suggestions/suggest-call-on-pat-mismatch.stderr
new file mode 100644
index 00000000000..7338312bab6
--- /dev/null
+++ b/tests/ui/suggestions/suggest-call-on-pat-mismatch.stderr
@@ -0,0 +1,33 @@
+error[E0308]: mismatched types
+  --> $DIR/suggest-call-on-pat-mismatch.rs:7:12
+   |
+LL |     if let E::One(var1, var2) = var {
+   |            ^^^^^^^^^^^^^^^^^^   --- this expression has type `fn(i32, i32) -> E {E::One}`
+   |            |
+   |            expected enum constructor, found `E`
+   |
+   = note: expected enum constructor `fn(i32, i32) -> E {E::One}`
+                          found enum `E`
+help: use parentheses to construct this tuple variant
+   |
+LL |     if let E::One(var1, var2) = var(/* i32 */, /* i32 */) {
+   |                                    ++++++++++++++++++++++
+
+error[E0308]: mismatched types
+  --> $DIR/suggest-call-on-pat-mismatch.rs:13:9
+   |
+LL |     let Some(x) = Some;
+   |         ^^^^^^^   ---- this expression has type `fn(_) -> Option<_> {Option::<_>::Some}`
+   |         |
+   |         expected enum constructor, found `Option<_>`
+   |
+   = note: expected enum constructor `fn(_) -> Option<_> {Option::<_>::Some}`
+                          found enum `Option<_>`
+help: use parentheses to construct this tuple variant
+   |
+LL |     let Some(x) = Some(/* value */);
+   |                       +++++++++++++
+
+error: aborting due to 2 previous errors
+
+For more information about this error, try `rustc --explain E0308`.