Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions compiler/rustc_hir_typeck/src/expr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1249,6 +1249,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
return;
}

// ignore suggestion to not make it broad in case of unallowed operations in let chains
// cc: https://github.com/rust-lang/rust/issues/147664
if FnCtxt::contains_let_in_chain(lhs) {
return;
}

let mut err = self.dcx().struct_span_err(op_span, "invalid left-hand side of assignment");
err.code(code);
err.span_label(lhs.span, "cannot assign to this expression");
Expand Down
41 changes: 41 additions & 0 deletions compiler/rustc_hir_typeck/src/op.rs
Original file line number Diff line number Diff line change
Expand Up @@ -197,6 +197,16 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
}
}

pub(crate) fn contains_let_in_chain(expr: &hir::Expr<'_>) -> bool {
match &expr.kind {
hir::ExprKind::Let(..) => true,
hir::ExprKind::Binary(Spanned { node: hir::BinOpKind::And, .. }, left, right) => {
Self::contains_let_in_chain(left) || Self::contains_let_in_chain(right)
}
_ => false,
}
}

fn check_overloaded_binop(
&self,
expr: &'tcx hir::Expr<'tcx>,
Expand Down Expand Up @@ -309,6 +319,37 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
let lhs_ty_str = self.tcx.short_string(lhs_ty, &mut path);
let rhs_ty_str = self.tcx.short_string(rhs_ty, &mut path);
let (mut err, output_def_id) = match op {
// this branch here is need to predict typo in let chains where
// user may write `+=` instead of `==`, which is the same button
// on most of keyboards
// it recursively get through let chain via `contains_let_in_chain`
Op::AssignOp(assign_op)
if assign_op.node == hir::AssignOpKind::AddAssign
&& let hir::ExprKind::Binary(bin_op, left, right) = &lhs_expr.kind
&& bin_op.node == hir::BinOpKind::And
&& Self::contains_let_in_chain(left)
&& let hir::ExprKind::Path(hir::QPath::Resolved(_, path)) =
&right.kind
&& matches!(path.res, hir::def::Res::Local(_)) =>
{
let mut err = struct_span_code_err!(
self.dcx(),
assign_op.span,
E0368,
"binary assignment operation `+=` cannot be applied in a let chain",
);

err.span_label(assign_op.span, "cannot use `+=` in a let chain");

err.span_suggestion(
assign_op.span,
"you might have meant to compare with `==` instead of assigning with `+=`",
"==",
Applicability::MaybeIncorrect,
);

(err, None)
}
Op::AssignOp(assign_op) => {
let s = assign_op.node.as_str();
let mut err = struct_span_code_err!(
Expand Down
28 changes: 28 additions & 0 deletions tests/ui/parser/let-chains-assign-add-incorrect.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
//@ edition:2024

fn test_where_left_is_not_let() {
let mut y = 2;
if let x = 1 && true && y += 2 {};
//~^ ERROR expected expression, found `let` statement
//~| NOTE only supported directly in conditions of `if` and `while` expressions
//~| ERROR mismatched types
//~| NOTE expected `bool`, found integer
//~| NOTE expected because this is `bool`
//~| ERROR binary assignment operation `+=` cannot be applied in a let chain
//~| NOTE cannot use `+=` in a let chain
//~| HELP you might have meant to compare with `==` instead of assigning with `+=`
}

fn test_where_left_is_let() {
let mut y = 2;
if let x = 1 && y += 2 {};
//~^ ERROR expected expression, found `let` statement
//~| NOTE only supported directly in conditions of `if` and `while` expressions
//~| ERROR mismatched types
//~| NOTE expected `bool`, found integer
//~| ERROR binary assignment operation `+=` cannot be applied in a let chain
//~| NOTE cannot use `+=` in a let chain
//~| HELP you might have meant to compare with `==` instead of assigning with `+=`
}

fn main() {}
58 changes: 58 additions & 0 deletions tests/ui/parser/let-chains-assign-add-incorrect.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
error: expected expression, found `let` statement
--> $DIR/let-chains-assign-add-incorrect.rs:5:8
|
LL | if let x = 1 && true && y += 2 {};
| ^^^^^^^^^
|
= note: only supported directly in conditions of `if` and `while` expressions

error: expected expression, found `let` statement
--> $DIR/let-chains-assign-add-incorrect.rs:18:8
|
LL | if let x = 1 && y += 2 {};
| ^^^^^^^^^
|
= note: only supported directly in conditions of `if` and `while` expressions

error[E0308]: mismatched types
--> $DIR/let-chains-assign-add-incorrect.rs:5:29
|
LL | if let x = 1 && true && y += 2 {};
| ----------------- ^ expected `bool`, found integer
| |
| expected because this is `bool`

error[E0368]: binary assignment operation `+=` cannot be applied in a let chain
--> $DIR/let-chains-assign-add-incorrect.rs:5:31
|
LL | if let x = 1 && true && y += 2 {};
| ^^ cannot use `+=` in a let chain
|
help: you might have meant to compare with `==` instead of assigning with `+=`
|
LL - if let x = 1 && true && y += 2 {};
LL + if let x = 1 && true && y == 2 {};
|

error[E0308]: mismatched types
--> $DIR/let-chains-assign-add-incorrect.rs:18:21
|
LL | if let x = 1 && y += 2 {};
| ^ expected `bool`, found integer

error[E0368]: binary assignment operation `+=` cannot be applied in a let chain
--> $DIR/let-chains-assign-add-incorrect.rs:18:23
|
LL | if let x = 1 && y += 2 {};
| ^^ cannot use `+=` in a let chain
|
help: you might have meant to compare with `==` instead of assigning with `+=`
|
LL - if let x = 1 && y += 2 {};
LL + if let x = 1 && y == 2 {};
|

error: aborting due to 6 previous errors

Some errors have detailed explanations: E0308, E0368.
For more information about an error, try `rustc --explain E0308`.
Loading