Skip to content

Commit f77bad6

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

13 files changed

Lines changed: 188 additions & 96 deletions

compiler/rustc_parse/messages.ftl

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -534,6 +534,14 @@ parse_leading_underscore_unicode_escape_label = invalid start of unicode escape
534534
parse_left_arrow_operator = unexpected token: `<-`
535535
.suggestion = if you meant to write a comparison against a negative value, add a space in between `<` and `-`
536536
537+
parse_let_chain_missing_let = let-chain with missing `let`
538+
.label = expected `let` expression, found assignment
539+
.suggestion = add `let` before the expression
540+
541+
parse_missing_let = missing `let`
542+
.label = expected `let` expression, found assignment
543+
.suggestion = add `let` before the expression
544+
537545
parse_let_chain_pre_2024 = let chains are only allowed in Rust 2024 or later
538546
539547
parse_lifetime_after_mut = lifetime must precede `mut`

compiler/rustc_parse/src/errors.rs

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -512,6 +512,28 @@ pub(crate) struct ExpectedExpressionFoundLet {
512512
pub comparison: Option<MaybeComparison>,
513513
}
514514

515+
#[derive(Diagnostic)]
516+
#[diag(parse_let_chain_missing_let)]
517+
pub(crate) struct LetChainMissingLet {
518+
#[primary_span]
519+
pub span: Span,
520+
#[label]
521+
pub label_span: Span,
522+
#[suggestion(applicability = "maybe-incorrect", code = "let ", style = "verbose")]
523+
pub sug_span: Span,
524+
}
525+
526+
#[derive(Diagnostic)]
527+
#[diag(parse_missing_let)]
528+
pub(crate) struct MissingLet {
529+
#[primary_span]
530+
pub span: Span,
531+
#[label]
532+
pub label_span: Span,
533+
#[suggestion(applicability = "maybe-incorrect", code = "let ", style = "verbose")]
534+
pub sug_span: Span,
535+
}
536+
515537
#[derive(Diagnostic)]
516538
#[diag(parse_or_in_let_chain)]
517539
pub(crate) struct OrInLetChain {

compiler/rustc_parse/src/parser/expr.rs

Lines changed: 39 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4245,6 +4245,20 @@ impl<'a> CondChecker<'a> {
42454245
}
42464246
}
42474247

4248+
impl CondChecker<'_> {
4249+
fn get_path_from_rhs(e: &Expr) -> Option<(u32, &Path)> {
4250+
Self::get_path_from_rhs_inner(e, 0)
4251+
}
4252+
4253+
fn get_path_from_rhs_inner(e: &Expr, depth: u32) -> Option<(u32, &Path)> {
4254+
match &e.kind {
4255+
ExprKind::Binary(_, lhs, _) => Self::get_path_from_rhs_inner(lhs, depth + 1),
4256+
ExprKind::Path(_, path) => Some((depth, path)),
4257+
_ => None,
4258+
}
4259+
}
4260+
}
4261+
42484262
impl MutVisitor for CondChecker<'_> {
42494263
fn visit_expr(&mut self, e: &mut Expr) {
42504264
self.depth += 1;
@@ -4304,7 +4318,31 @@ impl MutVisitor for CondChecker<'_> {
43044318
mut_visit::walk_expr(self, e);
43054319
self.forbid_let_reason = forbid_let_reason;
43064320
}
4307-
ExprKind::Assign(ref lhs, _, span) => {
4321+
ExprKind::Assign(ref lhs, ref rhs, span) => {
4322+
tracing::info!("Assigning to lhs {:#?}", lhs);
4323+
tracing::info!("Assigning to rhs {:#?}", rhs);
4324+
if let ExprKind::Call(_, _) = &lhs.kind {
4325+
if let Some((depth, path)) = CondChecker::get_path_from_rhs(rhs) {
4326+
// if let Some(path) = path {
4327+
let expr_span = lhs.span.to(path.span);
4328+
4329+
let guar = if depth > 0 {
4330+
self.parser.dcx().emit_err(errors::LetChainMissingLet {
4331+
span: lhs.span,
4332+
label_span: expr_span,
4333+
sug_span: lhs.span.shrink_to_lo(),
4334+
})
4335+
} else {
4336+
self.parser.dcx().emit_err(errors::MissingLet {
4337+
span: lhs.span,
4338+
label_span: expr_span,
4339+
sug_span: lhs.span.shrink_to_lo(),
4340+
})
4341+
};
4342+
self.found_incorrect_let_chain = Some(guar);
4343+
}
4344+
}
4345+
43084346
let forbid_let_reason = self.forbid_let_reason;
43094347
self.forbid_let_reason = Some(OtherForbidden);
43104348
let missing_let = self.missing_let;

tests/ui/inference/issue-103587.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ fn main() {
55
//~^ ERROR expected `=`, found `==`
66

77
if Some(_) = x {}
8-
//~^ ERROR mismatched types
8+
//~^ ERROR missing `let`
99

1010
if None = x { }
1111
//~^ ERROR mismatched types

tests/ui/inference/issue-103587.stderr

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,13 +10,15 @@ LL - if let Some(_) == x {}
1010
LL + if let Some(_) = x {}
1111
|
1212

13-
error[E0308]: mismatched types
13+
error: missing `let`
1414
--> $DIR/issue-103587.rs:7:8
1515
|
1616
LL | if Some(_) = x {}
17-
| ^^^^^^^^^^^ expected `bool`, found `()`
17+
| ^^^^^^^----
18+
| |
19+
| expected `let` expression, found assignment
1820
|
19-
help: consider adding `let`
21+
help: add `let` before the expression
2022
|
2123
LL | if let Some(_) = x {}
2224
| +++

tests/ui/missing/missing-let.rs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,17 @@
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: only supported directly in conditions of `if` and `while` expressions
516
{}
617
}

tests/ui/missing/missing-let.stderr

Lines changed: 23 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,26 @@ 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+
help: add `let` before the expression
34+
|
35+
LL | if let Some(_) = y &&
36+
| +++
37+
38+
error: aborting due to 3 previous errors
1839

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,9 @@
11
fn main() {
22
let foo = Some(0);
33
let bar = None;
4-
if Some(x) = foo {} //~ ERROR cannot find value `x` in this scope
5-
//~^ ERROR mismatched types
6-
if Some(foo) = bar {} //~ ERROR mismatched types
4+
if Some(x) = foo {} //~ ERROR missing `let`
5+
if Some(foo) = bar {} //~ ERROR missing `let`
76
if 3 = foo {} //~ ERROR mismatched types
8-
if Some(3) = foo {} //~ ERROR mismatched types
9-
//~^ ERROR invalid left-hand side of assignment
7+
if Some(3) = foo {} //~ ERROR missing `let`
108
if x = 5 {} //~ ERROR cannot find value `x` in this scope
119
}
Lines changed: 32 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -1,73 +1,60 @@
1-
error[E0425]: cannot find value `x` in this scope
2-
--> $DIR/if-let-typo.rs:4:13
1+
error: missing `let`
2+
--> $DIR/if-let-typo.rs:4:8
33
|
44
LL | if Some(x) = foo {}
5-
| ^ not found in this scope
5+
| ^^^^^^^------
6+
| |
7+
| expected `let` expression, found assignment
68
|
7-
help: you might have meant to use pattern matching
9+
help: add `let` before the expression
810
|
911
LL | if let Some(x) = foo {}
1012
| +++
1113

12-
error[E0425]: cannot find value `x` in this scope
13-
--> $DIR/if-let-typo.rs:10:8
14+
error: missing `let`
15+
--> $DIR/if-let-typo.rs:5:8
1416
|
15-
LL | if x = 5 {}
16-
| ^ not found in this scope
17+
LL | if Some(foo) = bar {}
18+
| ^^^^^^^^^------
19+
| |
20+
| expected `let` expression, found assignment
1721
|
18-
help: you might have meant to use pattern matching
22+
help: add `let` before the expression
1923
|
20-
LL | if let x = 5 {}
24+
LL | if let Some(foo) = bar {}
2125
| +++
2226

23-
error[E0308]: mismatched types
24-
--> $DIR/if-let-typo.rs:4:8
27+
error: missing `let`
28+
--> $DIR/if-let-typo.rs:7:8
2529
|
26-
LL | if Some(x) = foo {}
27-
| ^^^^^^^^^^^^^ expected `bool`, found `()`
30+
LL | if Some(3) = foo {}
31+
| ^^^^^^^------
32+
| |
33+
| expected `let` expression, found assignment
2834
|
29-
help: consider adding `let`
35+
help: add `let` before the expression
3036
|
31-
LL | if let Some(x) = foo {}
37+
LL | if let Some(3) = foo {}
3238
| +++
3339

34-
error[E0308]: mismatched types
35-
--> $DIR/if-let-typo.rs:6:8
40+
error[E0425]: cannot find value `x` in this scope
41+
--> $DIR/if-let-typo.rs:8:8
3642
|
37-
LL | if Some(foo) = bar {}
38-
| ^^^^^^^^^^^^^^^ expected `bool`, found `()`
43+
LL | if x = 5 {}
44+
| ^ not found in this scope
3945
|
40-
help: consider adding `let`
46+
help: you might have meant to use pattern matching
4147
|
42-
LL | if let Some(foo) = bar {}
48+
LL | if let x = 5 {}
4349
| +++
4450

4551
error[E0308]: mismatched types
46-
--> $DIR/if-let-typo.rs:7:8
52+
--> $DIR/if-let-typo.rs:6:8
4753
|
4854
LL | if 3 = foo {}
4955
| ^^^^^^^ expected `bool`, found `()`
5056

51-
error[E0070]: invalid left-hand side of assignment
52-
--> $DIR/if-let-typo.rs:8:16
53-
|
54-
LL | if Some(3) = foo {}
55-
| - ^
56-
| |
57-
| cannot assign to this expression
58-
59-
error[E0308]: mismatched types
60-
--> $DIR/if-let-typo.rs:8:8
61-
|
62-
LL | if Some(3) = foo {}
63-
| ^^^^^^^^^^^^^ expected `bool`, found `()`
64-
|
65-
help: consider adding `let`
66-
|
67-
LL | if let Some(3) = foo {}
68-
| +++
69-
70-
error: aborting due to 7 previous errors
57+
error: aborting due to 5 previous errors
7158

72-
Some errors have detailed explanations: E0070, E0308, E0425.
73-
For more information about an error, try `rustc --explain E0070`.
59+
Some errors have detailed explanations: E0308, E0425.
60+
For more information about an error, try `rustc --explain E0308`.
Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
fn main() {
22
let foo = Some(0);
33
let bar = None;
4-
while Some(x) = foo {} //~ ERROR cannot find value `x` in this scope
5-
while Some(foo) = bar {} //~ ERROR mismatched types
6-
while 3 = foo {} //~ ERROR mismatched types
7-
while Some(3) = foo {} //~ ERROR invalid left-hand side of assignment
4+
while Some(x) = foo {} //~ ERROR missing `let`
5+
while Some(foo) = bar {} //~ ERROR missing `let`
6+
while 3 = foo {} //~ ERROR mismatched types [E0308]
7+
while Some(3) = foo {} //~ ERROR missing `let`
88
while x = 5 {} //~ ERROR cannot find value `x` in this scope
99
}

0 commit comments

Comments
 (0)