From 5791a66a886eef8ab010b368754f8954079ed896 Mon Sep 17 00:00:00 2001 From: makai410 Date: Tue, 5 Nov 2024 10:09:34 +0800 Subject: [PATCH] suggest swapping the equality --- compiler/rustc_hir_typeck/src/expr.rs | 22 +++++++++++- .../src/fn_ctxt/suggestions.rs | 34 +++++++++++++++++++ compiler/rustc_hir_typeck/src/op.rs | 9 ++++- .../ui/suggestions/partialeq_suggest_swap.rs | 11 ++++++ .../suggestions/partialeq_suggest_swap.stderr | 17 ++++++++++ 5 files changed, 91 insertions(+), 2 deletions(-) create mode 100644 tests/ui/suggestions/partialeq_suggest_swap.rs create mode 100644 tests/ui/suggestions/partialeq_suggest_swap.stderr diff --git a/compiler/rustc_hir_typeck/src/expr.rs b/compiler/rustc_hir_typeck/src/expr.rs index d6e5fab610e..17168ff174c 100644 --- a/compiler/rustc_hir_typeck/src/expr.rs +++ b/compiler/rustc_hir_typeck/src/expr.rs @@ -102,10 +102,30 @@ pub(super) fn check_expr_coercible_to_type( expr: &'tcx hir::Expr<'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 3f703c2fcf6..8f90c0a4f1f 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 @@ pub(crate) fn suggest_return_binding_for_missing_tail_expr( 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 57ac7f7d2cd..609d6871f0b 100644 --- a/compiler/rustc_hir_typeck/src/op.rs +++ b/compiler/rustc_hir_typeck/src/op.rs @@ -249,7 +249,14 @@ fn check_overloaded_binop( ); // 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 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` +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`.