Skip to content

Commit

Permalink
Auto merge of #132404 - makai410:suggest-swap-lhs-rhs, r=fee1-dead
Browse files Browse the repository at this point in the history
Suggest swapping LHS and RHS when RHS impls `PartialEq<lhs_ty>`

Closes: #130495
r? `@fee1-dead`
  • Loading branch information
bors committed Nov 6, 2024
2 parents 2796048 + 5791a66 commit 4d215e2
Show file tree
Hide file tree
Showing 5 changed files with 91 additions and 2 deletions.
22 changes: 21 additions & 1 deletion compiler/rustc_hir_typeck/src/expr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -102,10 +102,30 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
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(
Expand Down
34 changes: 34 additions & 0 deletions compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
);
}
}
}
_ => {}
}
}
}
9 changes: 8 additions & 1 deletion compiler/rustc_hir_typeck/src/op.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
11 changes: 11 additions & 0 deletions tests/ui/suggestions/partialeq_suggest_swap.rs
Original file line number Diff line number Diff line change
@@ -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]
}
17 changes: 17 additions & 0 deletions tests/ui/suggestions/partialeq_suggest_swap.stderr
Original file line number Diff line number Diff line change
@@ -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`.

0 comments on commit 4d215e2

Please sign in to comment.