Skip to content

Commit ca81f0b

Browse files
committed
Suggest swapping LHS and RHS of equality
1 parent c8b8378 commit ca81f0b

File tree

5 files changed

+93
-2
lines changed

5 files changed

+93
-2
lines changed

compiler/rustc_hir_typeck/src/expr.rs

+21-1
Original file line numberDiff line numberDiff line change
@@ -102,10 +102,30 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
102102
expr: &'tcx hir::Expr<'tcx>,
103103
expected: Ty<'tcx>,
104104
expected_ty_expr: Option<&'tcx hir::Expr<'tcx>>,
105+
) -> Ty<'tcx> {
106+
self.check_expr_coercible_to_type_or_error(expr, expected, expected_ty_expr, |_, _| {})
107+
}
108+
109+
pub(crate) fn check_expr_coercible_to_type_or_error(
110+
&self,
111+
expr: &'tcx hir::Expr<'tcx>,
112+
expected: Ty<'tcx>,
113+
expected_ty_expr: Option<&'tcx hir::Expr<'tcx>>,
114+
extend_err: impl FnOnce(&mut Diag<'_>, Ty<'tcx>),
105115
) -> Ty<'tcx> {
106116
let ty = self.check_expr_with_hint(expr, expected);
107117
// checks don't need two phase
108-
self.demand_coerce(expr, ty, expected, expected_ty_expr, AllowTwoPhase::No)
118+
match self.demand_coerce_diag(expr, ty, expected, expected_ty_expr, AllowTwoPhase::No) {
119+
Ok(ty) => ty,
120+
Err(mut err) => {
121+
extend_err(&mut err, ty);
122+
err.emit();
123+
// Return the original type instead of an error type here, otherwise the type of `x` in
124+
// `let x: u32 = ();` will be a type error, causing all subsequent usages of `x` to not
125+
// report errors, even though `x` is definitely `u32`.
126+
expected
127+
}
128+
}
109129
}
110130

111131
pub(super) fn check_expr_with_hint(

compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs

+35
Original file line numberDiff line numberDiff line change
@@ -3390,4 +3390,39 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
33903390
err.span_label(block.span, "this block is missing a tail expression");
33913391
}
33923392
}
3393+
3394+
pub(crate) fn suggest_swapping_lhs_and_rhs(
3395+
&self,
3396+
err: &mut Diag<'_>,
3397+
span: Span,
3398+
rhs_ty: Ty<'tcx>,
3399+
lhs_ty: Ty<'tcx>,
3400+
rhs_expr: &'tcx hir::Expr<'tcx>,
3401+
lhs_expr: &'tcx hir::Expr<'tcx>,
3402+
op: hir::BinOp,
3403+
) {
3404+
match op.node {
3405+
hir::BinOpKind::Eq => {
3406+
if let Some(partial_eq_def_id) = self.infcx.tcx.lang_items().eq_trait()
3407+
&& self
3408+
.infcx
3409+
.type_implements_trait(partial_eq_def_id, [rhs_ty, lhs_ty], self.param_env)
3410+
.must_apply_modulo_regions()
3411+
{
3412+
let sm = self.tcx.sess.source_map();
3413+
if let Ok(rhs_snippet) = sm.span_to_snippet(rhs_expr.span)
3414+
&& let Ok(lhs_snippet) = sm.span_to_snippet(lhs_expr.span)
3415+
{
3416+
err.span_suggestion(
3417+
span,
3418+
format!("`{rhs_ty}` implements `PartialEq<{lhs_ty}>`; consider swapping the LHS and RHS of the equality"),
3419+
format!("{} == {}", rhs_snippet, lhs_snippet),
3420+
Applicability::MachineApplicable,
3421+
);
3422+
}
3423+
}
3424+
}
3425+
_ => {}
3426+
}
3427+
}
33933428
}

compiler/rustc_hir_typeck/src/op.rs

+10-1
Original file line numberDiff line numberDiff line change
@@ -249,7 +249,16 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
249249
);
250250

251251
// see `NB` above
252-
let rhs_ty = self.check_expr_coercible_to_type(rhs_expr, rhs_ty_var, Some(lhs_expr));
252+
let rhs_ty = self.check_expr_coercible_to_type_or_error(
253+
rhs_expr,
254+
rhs_ty_var,
255+
Some(lhs_expr),
256+
|err, ty| {
257+
self.suggest_swapping_lhs_and_rhs(
258+
err, expr.span, ty, lhs_ty, rhs_expr, lhs_expr, op,
259+
);
260+
},
261+
);
253262
let rhs_ty = self.resolve_vars_with_obligations(rhs_ty);
254263

255264
let return_ty = match result {

tests/ui/partialeq_suggest_swap.rs

+11
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
struct T(i32);
2+
3+
impl PartialEq<i32> for T {
4+
fn eq(&self, other: &i32) -> bool {
5+
&self.0 == other
6+
}
7+
}
8+
9+
fn main() {
10+
4i32 == T(4); //~ mismatched types [E0308]
11+
}
+16
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
error[E0308]: mismatched types
2+
--> $DIR/partialeq_suggest_swap.rs:10:13
3+
|
4+
LL | 4i32 == T(4);
5+
| ---- ^^^^ expected `i32`, found `T`
6+
| |
7+
| expected because this is `i32`
8+
|
9+
help: `T` implements `PartialEq<i32>`; consider swapping the LHS and RHS of the equality
10+
|
11+
LL | T(4) == 4i32;
12+
| ~~~~~~~~~~~~
13+
14+
error: aborting due to 1 previous error
15+
16+
For more information about this error, try `rustc --explain E0308`.

0 commit comments

Comments
 (0)