Skip to content

Commit 85ca098

Browse files
committed
rustc_parse: improve the error diagnostic for "missing let"
Signed-off-by: Usman Akinyemi <usmanakinyemi202@gmail.com>
1 parent 8c5605e commit 85ca098

File tree

4 files changed

+102
-3
lines changed

4 files changed

+102
-3
lines changed

compiler/rustc_parse/src/errors.rs

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -573,6 +573,24 @@ pub(crate) struct ExpectedExpressionFoundLet {
573573
pub comparison: Option<MaybeComparison>,
574574
}
575575

576+
#[derive(Diagnostic)]
577+
#[diag("let-chain with missing `let`")]
578+
pub(crate) struct LetChainMissingLet {
579+
#[primary_span]
580+
pub span: Span,
581+
#[label("expected `let` expression, found assignment")]
582+
pub label_span: Span,
583+
#[label("let expression later in the condition")]
584+
pub rhs_span: Span,
585+
#[suggestion(
586+
"add `let` before the expression",
587+
applicability = "maybe-incorrect",
588+
code = "let ",
589+
style = "verbose"
590+
)]
591+
pub sug_span: Span,
592+
}
593+
576594
#[derive(Diagnostic)]
577595
#[diag("`||` operators are not supported in let chain conditions")]
578596
pub(crate) struct OrInLetChain {

compiler/rustc_parse/src/parser/expr.rs

Lines changed: 46 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4282,7 +4282,52 @@ impl MutVisitor for CondChecker<'_> {
42824282
mut_visit::walk_expr(self, e);
42834283
self.forbid_let_reason = forbid_let_reason;
42844284
}
4285-
ExprKind::Assign(ref lhs, _, span) => {
4285+
ExprKind::Assign(ref lhs, ref rhs, span) => {
4286+
if let ExprKind::Call(_, _) = &lhs.kind {
4287+
fn get_path_from_rhs(e: &Expr) -> Option<(u32, &Path)> {
4288+
fn inner(e: &Expr, depth: u32) -> Option<(u32, &Path)> {
4289+
match &e.kind {
4290+
ExprKind::Binary(_, lhs, _) => inner(lhs, depth + 1),
4291+
ExprKind::Path(_, path) => Some((depth, path)),
4292+
_ => None,
4293+
}
4294+
}
4295+
4296+
inner(e, 0)
4297+
}
4298+
4299+
if let Some((depth, path)) = get_path_from_rhs(rhs) {
4300+
// For cases like if Some(_) = x && let Some(_) = y && let Some(_) = z
4301+
// This return let Some(_) = y expression
4302+
fn find_let_some(expr: &Expr) -> Option<&Expr> {
4303+
match &expr.kind {
4304+
ExprKind::Let(..) => Some(expr),
4305+
4306+
ExprKind::Binary(op, lhs, rhs) if op.node == BinOpKind::And => {
4307+
find_let_some(lhs).or_else(|| find_let_some(rhs))
4308+
}
4309+
4310+
_ => None,
4311+
}
4312+
}
4313+
4314+
let expr_span = lhs.span.to(path.span);
4315+
4316+
if let Some(later_rhs) = find_let_some(rhs)
4317+
&& depth > 0
4318+
{
4319+
let guar = self.parser.dcx().emit_err(errors::LetChainMissingLet {
4320+
span: lhs.span,
4321+
label_span: expr_span,
4322+
rhs_span: later_rhs.span,
4323+
sug_span: lhs.span.shrink_to_lo(),
4324+
});
4325+
4326+
self.found_incorrect_let_chain = Some(guar);
4327+
}
4328+
}
4329+
}
4330+
42864331
let forbid_let_reason = self.forbid_let_reason;
42874332
self.forbid_let_reason = Some(errors::ForbiddenLetReason::OtherForbidden);
42884333
let missing_let = self.missing_let;

tests/ui/missing/missing-let.rs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,18 @@
11
fn main() {
22
let x = Some(42);
3+
let y = Some(42);
4+
let z = Some(42);
35
if let Some(_) = x
46
&& Some(x) = x //~^ ERROR expected expression, found `let` statement
7+
//~| NOTE: only supported directly in conditions of `if` and `while` expressions
8+
{}
9+
10+
if Some(_) = y &&
11+
//~^ NOTE expected `let` expression, found assignment
12+
//~| ERROR let-chain with missing `let`
13+
let Some(_) = z
14+
//~^ ERROR: expected expression, found `let` statement
15+
//~| NOTE: let expression later in the condition
16+
//~| NOTE: only supported directly in conditions of `if` and `while` expressions
517
{}
618
}

tests/ui/missing/missing-let.stderr

Lines changed: 26 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
error: expected expression, found `let` statement
2-
--> $DIR/missing-let.rs:3:8
2+
--> $DIR/missing-let.rs:5:8
33
|
44
LL | if let Some(_) = x
55
| ^^^^^^^^^^^^^^^
@@ -14,5 +14,29 @@ help: you might have meant to compare for equality
1414
LL | && Some(x) == x
1515
| +
1616

17-
error: aborting due to 1 previous error
17+
error: expected expression, found `let` statement
18+
--> $DIR/missing-let.rs:13:9
19+
|
20+
LL | let Some(_) = z
21+
| ^^^
22+
|
23+
= note: only supported directly in conditions of `if` and `while` expressions
24+
25+
error: let-chain with missing `let`
26+
--> $DIR/missing-let.rs:10:8
27+
|
28+
LL | if Some(_) = y &&
29+
| ^^^^^^^----
30+
| |
31+
| expected `let` expression, found assignment
32+
...
33+
LL | let Some(_) = z
34+
| --------------- let expression later in the condition
35+
|
36+
help: add `let` before the expression
37+
|
38+
LL | if let Some(_) = y &&
39+
| +++
40+
41+
error: aborting due to 3 previous errors
1842

0 commit comments

Comments
 (0)