Skip to content

Commit 96dba30

Browse files
committed
add check for typo in let chains
1 parent 869fb46 commit 96dba30

File tree

4 files changed

+133
-0
lines changed

4 files changed

+133
-0
lines changed

compiler/rustc_hir_typeck/src/expr.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1249,6 +1249,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
12491249
return;
12501250
}
12511251

1252+
// ignore suggesiton to not make it broad in case of unallowed operations in let chains
1253+
// cc: https://github.com/rust-lang/rust/issues/147664
1254+
if FnCtxt::contains_let_in_chain(lhs) {
1255+
return;
1256+
}
1257+
12521258
let mut err = self.dcx().struct_span_err(op_span, "invalid left-hand side of assignment");
12531259
err.code(code);
12541260
err.span_label(lhs.span, "cannot assign to this expression");

compiler/rustc_hir_typeck/src/op.rs

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -197,6 +197,16 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
197197
}
198198
}
199199

200+
pub(crate) fn contains_let_in_chain(expr: &hir::Expr<'_>) -> bool {
201+
match &expr.kind {
202+
hir::ExprKind::Let(..) => true,
203+
hir::ExprKind::Binary(Spanned { node: hir::BinOpKind::And, .. }, left, right) => {
204+
Self::contains_let_in_chain(left) || Self::contains_let_in_chain(right)
205+
}
206+
_ => false,
207+
}
208+
}
209+
200210
fn check_overloaded_binop(
201211
&self,
202212
expr: &'tcx hir::Expr<'tcx>,
@@ -309,6 +319,37 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
309319
let lhs_ty_str = self.tcx.short_string(lhs_ty, &mut path);
310320
let rhs_ty_str = self.tcx.short_string(rhs_ty, &mut path);
311321
let (mut err, output_def_id) = match op {
322+
// this branch here is need to predict typo in let chains where
323+
// user may write `+=` instead of `==`, which is the same button
324+
// on most of keyboards
325+
// it recursivly get through let chain via `contains_let_in_chain`
326+
Op::AssignOp(assign_op)
327+
if assign_op.node == hir::AssignOpKind::AddAssign
328+
&& let hir::ExprKind::Binary(bin_op, left, right) = &lhs_expr.kind
329+
&& bin_op.node == hir::BinOpKind::And
330+
&& Self::contains_let_in_chain(left)
331+
&& let hir::ExprKind::Path(hir::QPath::Resolved(_, path)) =
332+
&right.kind
333+
&& matches!(path.res, hir::def::Res::Local(_)) =>
334+
{
335+
let mut err = struct_span_code_err!(
336+
self.dcx(),
337+
assign_op.span,
338+
E0368,
339+
"binary assignment operation `+=` cannot be applied in a let chain",
340+
);
341+
342+
err.span_label(assign_op.span, "cannot use `+=` in a let chain");
343+
344+
err.span_suggestion(
345+
assign_op.span,
346+
"you might have meant to compare with `==` instead of assigning with `+=`",
347+
"==",
348+
Applicability::MaybeIncorrect,
349+
);
350+
351+
(err, None)
352+
}
312353
Op::AssignOp(assign_op) => {
313354
let s = assign_op.node.as_str();
314355
let mut err = struct_span_code_err!(
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
//@ edition:2024
2+
3+
fn test_where_left_is_not_let() {
4+
let mut y = 2;
5+
if let x = 1 && true && y += 2 {};
6+
//~^ ERROR expected expression, found `let` statement
7+
//~| NOTE only supported directly in conditions of `if` and `while` expressions
8+
//~| ERROR mismatched types
9+
//~| NOTE expected `bool`, found integer
10+
//~| NOTE expected because this is `bool`
11+
//~| ERROR binary assignment operation `+=` cannot be applied in a let chain
12+
//~| NOTE cannot use `+=` in a let chain
13+
//~| HELP you might have meant to compare with `==` instead of assigning with `+=`
14+
}
15+
16+
fn test_where_left_is_let() {
17+
let mut y = 2;
18+
if let x = 1 && y += 2 {};
19+
//~^ ERROR expected expression, found `let` statement
20+
//~| NOTE only supported directly in conditions of `if` and `while` expressions
21+
//~| ERROR mismatched types
22+
//~| NOTE expected `bool`, found integer
23+
//~| ERROR binary assignment operation `+=` cannot be applied in a let chain
24+
//~| NOTE cannot use `+=` in a let chain
25+
//~| HELP you might have meant to compare with `==` instead of assigning with `+=`
26+
}
27+
28+
fn main() {}
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
error: expected expression, found `let` statement
2+
--> $DIR/let-chains-assign-add-incorrect.rs:5:8
3+
|
4+
LL | if let x = 1 && true && y += 2 {};
5+
| ^^^^^^^^^
6+
|
7+
= note: only supported directly in conditions of `if` and `while` expressions
8+
9+
error: expected expression, found `let` statement
10+
--> $DIR/let-chains-assign-add-incorrect.rs:18:8
11+
|
12+
LL | if let x = 1 && y += 2 {};
13+
| ^^^^^^^^^
14+
|
15+
= note: only supported directly in conditions of `if` and `while` expressions
16+
17+
error[E0308]: mismatched types
18+
--> $DIR/let-chains-assign-add-incorrect.rs:5:29
19+
|
20+
LL | if let x = 1 && true && y += 2 {};
21+
| ----------------- ^ expected `bool`, found integer
22+
| |
23+
| expected because this is `bool`
24+
25+
error[E0368]: binary assignment operation `+=` cannot be applied in a let chain
26+
--> $DIR/let-chains-assign-add-incorrect.rs:5:31
27+
|
28+
LL | if let x = 1 && true && y += 2 {};
29+
| ^^ cannot use `+=` in a let chain
30+
|
31+
help: you might have meant to compare with `==` instead of assigning with `+=`
32+
|
33+
LL - if let x = 1 && true && y += 2 {};
34+
LL + if let x = 1 && true && y == 2 {};
35+
|
36+
37+
error[E0308]: mismatched types
38+
--> $DIR/let-chains-assign-add-incorrect.rs:18:21
39+
|
40+
LL | if let x = 1 && y += 2 {};
41+
| ^ expected `bool`, found integer
42+
43+
error[E0368]: binary assignment operation `+=` cannot be applied in a let chain
44+
--> $DIR/let-chains-assign-add-incorrect.rs:18:23
45+
|
46+
LL | if let x = 1 && y += 2 {};
47+
| ^^ cannot use `+=` in a let chain
48+
|
49+
help: you might have meant to compare with `==` instead of assigning with `+=`
50+
|
51+
LL - if let x = 1 && y += 2 {};
52+
LL + if let x = 1 && y == 2 {};
53+
|
54+
55+
error: aborting due to 6 previous errors
56+
57+
Some errors have detailed explanations: E0308, E0368.
58+
For more information about an error, try `rustc --explain E0308`.

0 commit comments

Comments
 (0)