rustc_parse: improve the error diagnostic for "missing let in let chain"

Signed-off-by: Usman Akinyemi <usmanakinyemi202@gmail.com>
This commit is contained in:
Usman Akinyemi 2026-01-28 02:54:32 +05:30
parent b3cda168c8
commit 9ca8ed38eb
5 changed files with 73 additions and 40 deletions

View file

@ -2760,9 +2760,13 @@ impl<'a> Parser<'a> {
let (mut cond, _) =
self.parse_expr_res(Restrictions::NO_STRUCT_LITERAL | Restrictions::ALLOW_LET, attrs)?;
CondChecker::new(self, let_chains_policy).visit_expr(&mut cond);
Ok(cond)
let mut checker = CondChecker::new(self, let_chains_policy);
checker.visit_expr(&mut cond);
Ok(if let Some(guar) = checker.found_incorrect_let_chain {
self.mk_expr_err(cond.span, guar)
} else {
cond
})
}
/// Parses a `let $pat = $expr` pseudo-expression.
@ -3484,13 +3488,19 @@ impl<'a> Parser<'a> {
let if_span = self.prev_token.span;
let mut cond = self.parse_match_guard_condition()?;
CondChecker::new(self, LetChainsPolicy::AlwaysAllowed).visit_expr(&mut cond);
let mut checker = CondChecker::new(self, LetChainsPolicy::AlwaysAllowed);
checker.visit_expr(&mut cond);
if has_let_expr(&cond) {
let span = if_span.to(cond.span);
self.psess.gated_spans.gate(sym::if_let_guard, span);
}
Ok(Some(cond))
Ok(Some(if let Some(guar) = checker.found_incorrect_let_chain {
self.mk_expr_err(cond.span, guar)
} else {
cond
}))
}
fn parse_match_arm_pat_and_guard(&mut self) -> PResult<'a, (Pat, Option<Box<Expr>>)> {
@ -3511,13 +3521,23 @@ impl<'a> Parser<'a> {
let ast::PatKind::Paren(subpat) = pat.kind else { unreachable!() };
let ast::PatKind::Guard(_, mut cond) = subpat.kind else { unreachable!() };
self.psess.gated_spans.ungate_last(sym::guard_patterns, cond.span);
CondChecker::new(self, LetChainsPolicy::AlwaysAllowed).visit_expr(&mut cond);
let mut checker = CondChecker::new(self, LetChainsPolicy::AlwaysAllowed);
checker.visit_expr(&mut cond);
let right = self.prev_token.span;
self.dcx().emit_err(errors::ParenthesesInMatchPat {
span: vec![left, right],
sugg: errors::ParenthesesInMatchPatSugg { left, right },
});
Ok((self.mk_pat(span, ast::PatKind::Wild), Some(cond)))
Ok((
self.mk_pat(span, ast::PatKind::Wild),
(if let Some(guar) = checker.found_incorrect_let_chain {
Some(self.mk_expr_err(cond.span, guar))
} else {
Some(cond)
}),
))
} else {
Ok((pat, self.parse_match_arm_guard()?))
}
@ -4208,6 +4228,7 @@ struct CondChecker<'a> {
forbid_let_reason: Option<ForbiddenLetReason>,
missing_let: Option<errors::MaybeMissingLet>,
comparison: Option<errors::MaybeComparison>,
found_incorrect_let_chain: Option<ErrorGuaranteed>,
}
impl<'a> CondChecker<'a> {
@ -4218,6 +4239,7 @@ impl<'a> CondChecker<'a> {
missing_let: None,
comparison: None,
let_chains_policy,
found_incorrect_let_chain: None,
depth: 0,
}
}
@ -4236,12 +4258,19 @@ impl MutVisitor for CondChecker<'_> {
NotSupportedOr(or_span) => {
self.parser.dcx().emit_err(errors::OrInLetChain { span: or_span })
}
_ => self.parser.dcx().emit_err(errors::ExpectedExpressionFoundLet {
span,
reason,
missing_let: self.missing_let,
comparison: self.comparison,
}),
_ => {
let guar =
self.parser.dcx().emit_err(errors::ExpectedExpressionFoundLet {
span,
reason,
missing_let: self.missing_let,
comparison: self.comparison,
});
if let Some(_) = self.missing_let {
self.found_incorrect_let_chain = Some(guar);
}
guar
}
};
*recovered = Recovered::Yes(error);
} else if self.depth > 1 {

View file

@ -1,8 +1,6 @@
fn a() {
if let x = 1 && i = 2 {}
//~^ ERROR cannot find value `i` in this scope
//~| ERROR mismatched types
//~| ERROR expected expression, found `let` statement
//~^ ERROR expected expression, found `let` statement
}
fn b() {

View file

@ -15,13 +15,7 @@ LL | if let x = 1 && i == 2 {}
| +
error[E0425]: cannot find value `i` in this scope
--> $DIR/bad-if-let-suggestion.rs:2:21
|
LL | if let x = 1 && i = 2 {}
| ^ not found in this scope
error[E0425]: cannot find value `i` in this scope
--> $DIR/bad-if-let-suggestion.rs:9:9
--> $DIR/bad-if-let-suggestion.rs:7:9
|
LL | fn a() {
| ------ similarly named function `a` defined here
@ -36,7 +30,7 @@ LL + if (a + j) = i {}
|
error[E0425]: cannot find value `j` in this scope
--> $DIR/bad-if-let-suggestion.rs:9:13
--> $DIR/bad-if-let-suggestion.rs:7:13
|
LL | fn a() {
| ------ similarly named function `a` defined here
@ -51,7 +45,7 @@ LL + if (i + a) = i {}
|
error[E0425]: cannot find value `i` in this scope
--> $DIR/bad-if-let-suggestion.rs:9:18
--> $DIR/bad-if-let-suggestion.rs:7:18
|
LL | fn a() {
| ------ similarly named function `a` defined here
@ -66,7 +60,7 @@ LL + if (i + j) = a {}
|
error[E0425]: cannot find value `x` in this scope
--> $DIR/bad-if-let-suggestion.rs:16:8
--> $DIR/bad-if-let-suggestion.rs:14:8
|
LL | fn a() {
| ------ similarly named function `a` defined here
@ -80,18 +74,6 @@ LL - if x[0] = 1 {}
LL + if a[0] = 1 {}
|
error[E0308]: mismatched types
--> $DIR/bad-if-let-suggestion.rs:2:8
|
LL | if let x = 1 && i = 2 {}
| ^^^^^^^^^^^^^^^^^^ expected `bool`, found `()`
|
help: you might have meant to compare for equality
|
LL | if let x = 1 && i == 2 {}
| +
error: aborting due to 5 previous errors
error: aborting due to 7 previous errors
Some errors have detailed explanations: E0308, E0425.
For more information about an error, try `rustc --explain E0308`.
For more information about this error, try `rustc --explain E0425`.

View file

@ -0,0 +1,6 @@
fn main() {
let x = Some(42);
if let Some(_) = x
&& Some(x) = x //~^ ERROR expected expression, found `let` statement
{}
}

View file

@ -0,0 +1,18 @@
error: expected expression, found `let` statement
--> $DIR/missing-let.rs:3:8
|
LL | if let Some(_) = x
| ^^^^^^^^^^^^^^^
|
= note: only supported directly in conditions of `if` and `while` expressions
help: you might have meant to continue the let-chain
|
LL | && let Some(x) = x
| +++
help: you might have meant to compare for equality
|
LL | && Some(x) == x
| +
error: aborting due to 1 previous error