about summary refs log tree commit diff
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2024-11-06 11:49:52 +0000
committerbors <bors@rust-lang.org>2024-11-06 11:49:52 +0000
commit4d215e2426d52ca8d1af166d5f6b5e172afbff67 (patch)
treee76c86e70dbedc46a72683d295b6d921f3f2ec91
parent279604832887abeaea0cb31c20590c43dea497ae (diff)
parent5791a66a886eef8ab010b368754f8954079ed896 (diff)
downloadrust-4d215e2426d52ca8d1af166d5f6b5e172afbff67.tar.gz
rust-4d215e2426d52ca8d1af166d5f6b5e172afbff67.zip
Auto merge of #132404 - makai410:suggest-swap-lhs-rhs, r=fee1-dead
Suggest swapping LHS and RHS when RHS impls `PartialEq<lhs_ty>`

Closes: #130495
r? `@fee1-dead`
-rw-r--r--compiler/rustc_hir_typeck/src/expr.rs22
-rw-r--r--compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs34
-rw-r--r--compiler/rustc_hir_typeck/src/op.rs9
-rw-r--r--tests/ui/suggestions/partialeq_suggest_swap.rs11
-rw-r--r--tests/ui/suggestions/partialeq_suggest_swap.stderr17
5 files changed, 91 insertions, 2 deletions
diff --git a/compiler/rustc_hir_typeck/src/expr.rs b/compiler/rustc_hir_typeck/src/expr.rs
index 915dca614d6..b05731c6d52 100644
--- a/compiler/rustc_hir_typeck/src/expr.rs
+++ b/compiler/rustc_hir_typeck/src/expr.rs
@@ -103,9 +103,29 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         expected: Ty<'tcx>,
         expected_ty_expr: Option<&'tcx hir::Expr<'tcx>>,
     ) -> Ty<'tcx> {
+        self.check_expr_coercible_to_type_or_error(expr, expected, expected_ty_expr, |_, _| {})
+    }
+
+    pub(crate) fn check_expr_coercible_to_type_or_error(
+        &self,
+        expr: &'tcx hir::Expr<'tcx>,
+        expected: Ty<'tcx>,
+        expected_ty_expr: Option<&'tcx hir::Expr<'tcx>>,
+        extend_err: impl FnOnce(&mut Diag<'_>, Ty<'tcx>),
+    ) -> Ty<'tcx> {
         let ty = self.check_expr_with_hint(expr, expected);
         // checks don't need two phase
-        self.demand_coerce(expr, ty, expected, expected_ty_expr, AllowTwoPhase::No)
+        match self.demand_coerce_diag(expr, ty, expected, expected_ty_expr, AllowTwoPhase::No) {
+            Ok(ty) => ty,
+            Err(mut err) => {
+                extend_err(&mut err, ty);
+                err.emit();
+                // Return the original type instead of an error type here, otherwise the type of `x` in
+                // `let x: u32 = ();` will be a type error, causing all subsequent usages of `x` to not
+                // report errors, even though `x` is definitely `u32`.
+                expected
+            }
+        }
     }
 
     pub(super) fn check_expr_with_hint(
diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs
index 23842b3014b..919e83724d7 100644
--- a/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs
+++ b/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs
@@ -3390,4 +3390,38 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
             err.span_label(block.span, "this block is missing a tail expression");
         }
     }
+
+    pub(crate) fn suggest_swapping_lhs_and_rhs(
+        &self,
+        err: &mut Diag<'_>,
+        rhs_ty: Ty<'tcx>,
+        lhs_ty: Ty<'tcx>,
+        rhs_expr: &'tcx hir::Expr<'tcx>,
+        lhs_expr: &'tcx hir::Expr<'tcx>,
+        op: hir::BinOp,
+    ) {
+        match op.node {
+            hir::BinOpKind::Eq => {
+                if let Some(partial_eq_def_id) = self.infcx.tcx.lang_items().eq_trait()
+                    && self
+                        .infcx
+                        .type_implements_trait(partial_eq_def_id, [rhs_ty, lhs_ty], self.param_env)
+                        .must_apply_modulo_regions()
+                {
+                    let sm = self.tcx.sess.source_map();
+                    if let Ok(rhs_snippet) = sm.span_to_snippet(rhs_expr.span)
+                        && let Ok(lhs_snippet) = sm.span_to_snippet(lhs_expr.span)
+                    {
+                        err.note(format!("`{rhs_ty}` implements `PartialEq<{lhs_ty}>`"));
+                        err.multipart_suggestion(
+                            "consider swapping the equality",
+                            vec![(lhs_expr.span, rhs_snippet), (rhs_expr.span, lhs_snippet)],
+                            Applicability::MaybeIncorrect,
+                        );
+                    }
+                }
+            }
+            _ => {}
+        }
+    }
 }
diff --git a/compiler/rustc_hir_typeck/src/op.rs b/compiler/rustc_hir_typeck/src/op.rs
index 9c1459ee188..9a3492abc9f 100644
--- a/compiler/rustc_hir_typeck/src/op.rs
+++ b/compiler/rustc_hir_typeck/src/op.rs
@@ -249,7 +249,14 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         );
 
         // see `NB` above
-        let rhs_ty = self.check_expr_coercible_to_type(rhs_expr, rhs_ty_var, Some(lhs_expr));
+        let rhs_ty = self.check_expr_coercible_to_type_or_error(
+            rhs_expr,
+            rhs_ty_var,
+            Some(lhs_expr),
+            |err, ty| {
+                self.suggest_swapping_lhs_and_rhs(err, ty, lhs_ty, rhs_expr, lhs_expr, op);
+            },
+        );
         let rhs_ty = self.resolve_vars_with_obligations(rhs_ty);
 
         let return_ty = match result {
diff --git a/tests/ui/suggestions/partialeq_suggest_swap.rs b/tests/ui/suggestions/partialeq_suggest_swap.rs
new file mode 100644
index 00000000000..ee5583a5488
--- /dev/null
+++ b/tests/ui/suggestions/partialeq_suggest_swap.rs
@@ -0,0 +1,11 @@
+struct T(i32);
+
+impl PartialEq<i32> for T {
+    fn eq(&self, other: &i32) -> bool {
+        &self.0 == other
+    }
+}
+
+fn main() {
+    4i32 == T(4); //~ mismatched types [E0308]
+}
diff --git a/tests/ui/suggestions/partialeq_suggest_swap.stderr b/tests/ui/suggestions/partialeq_suggest_swap.stderr
new file mode 100644
index 00000000000..2cadc5a16d5
--- /dev/null
+++ b/tests/ui/suggestions/partialeq_suggest_swap.stderr
@@ -0,0 +1,17 @@
+error[E0308]: mismatched types
+  --> $DIR/partialeq_suggest_swap.rs:10:13
+   |
+LL |     4i32 == T(4);
+   |     ----    ^^^^ expected `i32`, found `T`
+   |     |
+   |     expected because this is `i32`
+   |
+   = note: `T` implements `PartialEq<i32>`
+help: consider swapping the equality
+   |
+LL |     T(4) == 4i32;
+   |     ~~~~    ~~~~
+
+error: aborting due to 1 previous error
+
+For more information about this error, try `rustc --explain E0308`.