Skip to content
Merged
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
18 changes: 18 additions & 0 deletions compiler/rustc_parse/src/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -573,6 +573,24 @@ pub(crate) struct ExpectedExpressionFoundLet {
pub comparison: Option<MaybeComparison>,
}

#[derive(Diagnostic)]
#[diag("let-chain with missing `let`")]
pub(crate) struct LetChainMissingLet {
#[primary_span]
pub span: Span,
#[label("expected `let` expression, found assignment")]
pub label_span: Span,
#[label("let expression later in the condition")]
pub rhs_span: Span,
#[suggestion(
"add `let` before the expression",
applicability = "maybe-incorrect",
code = "let ",
style = "verbose"
)]
pub sug_span: Span,
}

#[derive(Diagnostic)]
#[diag("`||` operators are not supported in let chain conditions")]
pub(crate) struct OrInLetChain {
Expand Down
47 changes: 46 additions & 1 deletion compiler/rustc_parse/src/parser/expr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4282,7 +4282,52 @@ impl MutVisitor for CondChecker<'_> {
mut_visit::walk_expr(self, e);
self.forbid_let_reason = forbid_let_reason;
}
ExprKind::Assign(ref lhs, _, span) => {
ExprKind::Assign(ref lhs, ref rhs, span) => {
if let ExprKind::Call(_, _) = &lhs.kind {
fn get_path_from_rhs(e: &Expr) -> Option<(u32, &Path)> {
fn inner(e: &Expr, depth: u32) -> Option<(u32, &Path)> {
match &e.kind {
ExprKind::Binary(_, lhs, _) => inner(lhs, depth + 1),
ExprKind::Path(_, path) => Some((depth, path)),
_ => None,
}
}

inner(e, 0)
}

if let Some((depth, path)) = get_path_from_rhs(rhs) {
// For cases like if Some(_) = x && let Some(_) = y && let Some(_) = z
// This return let Some(_) = y expression
fn find_let_some(expr: &Expr) -> Option<&Expr> {
match &expr.kind {
ExprKind::Let(..) => Some(expr),

ExprKind::Binary(op, lhs, rhs) if op.node == BinOpKind::And => {
find_let_some(lhs).or_else(|| find_let_some(rhs))
}

_ => None,
}
}

let expr_span = lhs.span.to(path.span);

if let Some(later_rhs) = find_let_some(rhs)
&& depth > 0
{
let guar = self.parser.dcx().emit_err(errors::LetChainMissingLet {
span: lhs.span,
label_span: expr_span,
rhs_span: later_rhs.span,
sug_span: lhs.span.shrink_to_lo(),
});

self.found_incorrect_let_chain = Some(guar);
}
}
}

let forbid_let_reason = self.forbid_let_reason;
self.forbid_let_reason = Some(errors::ForbiddenLetReason::OtherForbidden);
let missing_let = self.missing_let;
Expand Down
12 changes: 12 additions & 0 deletions tests/ui/missing/missing-let.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,18 @@
fn main() {
let x = Some(42);
let y = Some(42);
let z = Some(42);
if let Some(_) = x
&& Some(x) = x //~^ ERROR expected expression, found `let` statement
//~| NOTE: only supported directly in conditions of `if` and `while` expressions
{}

if Some(_) = y &&
//~^ NOTE expected `let` expression, found assignment
//~| ERROR let-chain with missing `let`
let Some(_) = z
//~^ ERROR: expected expression, found `let` statement
//~| NOTE: let expression later in the condition
//~| NOTE: only supported directly in conditions of `if` and `while` expressions
{}
}
28 changes: 26 additions & 2 deletions tests/ui/missing/missing-let.stderr
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
error: expected expression, found `let` statement
--> $DIR/missing-let.rs:3:8
--> $DIR/missing-let.rs:5:8
|
LL | if let Some(_) = x
| ^^^^^^^^^^^^^^^
Expand All @@ -14,5 +14,29 @@ help: you might have meant to compare for equality
LL | && Some(x) == x
| +

error: aborting due to 1 previous error
error: expected expression, found `let` statement
--> $DIR/missing-let.rs:13:9
|
LL | let Some(_) = z
| ^^^
|
= note: only supported directly in conditions of `if` and `while` expressions

error: let-chain with missing `let`
--> $DIR/missing-let.rs:10:8
|
LL | if Some(_) = y &&
| ^^^^^^^----
| |
| expected `let` expression, found assignment
...
LL | let Some(_) = z
| --------------- let expression later in the condition
|
help: add `let` before the expression
|
LL | if let Some(_) = y &&
| +++

error: aborting due to 3 previous errors

Loading