diff options
| author | bors <bors@rust-lang.org> | 2024-11-06 11:49:52 +0000 |
|---|---|---|
| committer | bors <bors@rust-lang.org> | 2024-11-06 11:49:52 +0000 |
| commit | 4d215e2426d52ca8d1af166d5f6b5e172afbff67 (patch) | |
| tree | e76c86e70dbedc46a72683d295b6d921f3f2ec91 | |
| parent | 279604832887abeaea0cb31c20590c43dea497ae (diff) | |
| parent | 5791a66a886eef8ab010b368754f8954079ed896 (diff) | |
| download | rust-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.rs | 22 | ||||
| -rw-r--r-- | compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs | 34 | ||||
| -rw-r--r-- | compiler/rustc_hir_typeck/src/op.rs | 9 | ||||
| -rw-r--r-- | tests/ui/suggestions/partialeq_suggest_swap.rs | 11 | ||||
| -rw-r--r-- | tests/ui/suggestions/partialeq_suggest_swap.stderr | 17 |
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`. |
