Skip to content

Commit 3865e7c

Browse files
authored
Unrolled build for rust-lang#118191
Rollup merge of rust-lang#118191 - estebank:let-chain-typo, r=compiler-errors Suggest `let` or `==` on typo'd let-chain When encountering a bare assignment in a let-chain, suggest turning the assignment into a `let` expression or an equality check. ``` error: expected expression, found `let` statement --> $DIR/bad-if-let-suggestion.rs:5:8 | LL | if let x = 1 && i = 2 {} | ^^^^^^^^^ | = note: only supported directly in conditions of `if` and `while` expressions help: you might have meant to continue the let-chain | LL | if let x = 1 && let i = 2 {} | +++ help: you might have meant to compare for equality | LL | if let x = 1 && i == 2 {} | + ```
2 parents ec1f21c + 55e4e3e commit 3865e7c

File tree

6 files changed

+93
-18
lines changed

6 files changed

+93
-18
lines changed

Diff for: compiler/rustc_parse/messages.ftl

+4
Original file line numberDiff line numberDiff line change
@@ -492,9 +492,13 @@ parse_match_arm_body_without_braces = `match` arm body without braces
492492
} with a body
493493
.suggestion_use_comma_not_semicolon = replace `;` with `,` to end a `match` arm expression
494494
495+
parse_maybe_comparison = you might have meant to compare for equality
496+
495497
parse_maybe_fn_typo_with_impl = you might have meant to write `impl` instead of `fn`
496498
.suggestion = replace `fn` with `impl` here
497499
500+
parse_maybe_missing_let = you might have meant to continue the let-chain
501+
498502
parse_maybe_recover_from_bad_qpath_stage_2 =
499503
missing angle brackets in associated item path
500504
.suggestion = types that don't start with an identifier need to be surrounded with angle brackets in qualified paths

Diff for: compiler/rustc_parse/src/errors.rs

+26
Original file line numberDiff line numberDiff line change
@@ -415,6 +415,32 @@ pub(crate) struct ExpectedExpressionFoundLet {
415415
pub span: Span,
416416
#[subdiagnostic]
417417
pub reason: ForbiddenLetReason,
418+
#[subdiagnostic]
419+
pub missing_let: Option<MaybeMissingLet>,
420+
#[subdiagnostic]
421+
pub comparison: Option<MaybeComparison>,
422+
}
423+
424+
#[derive(Subdiagnostic, Clone, Copy)]
425+
#[multipart_suggestion(
426+
parse_maybe_missing_let,
427+
applicability = "maybe-incorrect",
428+
style = "verbose"
429+
)]
430+
pub(crate) struct MaybeMissingLet {
431+
#[suggestion_part(code = "let ")]
432+
pub span: Span,
433+
}
434+
435+
#[derive(Subdiagnostic, Clone, Copy)]
436+
#[multipart_suggestion(
437+
parse_maybe_comparison,
438+
applicability = "maybe-incorrect",
439+
style = "verbose"
440+
)]
441+
pub(crate) struct MaybeComparison {
442+
#[suggestion_part(code = "=")]
443+
pub span: Span,
418444
}
419445

420446
#[derive(Diagnostic)]

Diff for: compiler/rustc_parse/src/parser/expr.rs

+40-8
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
// ignore-tidy-filelength
12
use super::diagnostics::SnapshotParser;
23
use super::pat::{CommaRecoveryMode, Expected, RecoverColon, RecoverComma};
34
use super::ty::{AllowPlus, RecoverQPath, RecoverReturnSign};
@@ -2477,7 +2478,7 @@ impl<'a> Parser<'a> {
24772478
let mut cond =
24782479
self.parse_expr_res(Restrictions::NO_STRUCT_LITERAL | Restrictions::ALLOW_LET, None)?;
24792480

2480-
CondChecker { parser: self, forbid_let_reason: None }.visit_expr(&mut cond);
2481+
CondChecker::new(self).visit_expr(&mut cond);
24812482

24822483
if let ExprKind::Let(_, _, _, None) = cond.kind {
24832484
// Remove the last feature gating of a `let` expression since it's stable.
@@ -2493,6 +2494,8 @@ impl<'a> Parser<'a> {
24932494
let err = errors::ExpectedExpressionFoundLet {
24942495
span: self.token.span,
24952496
reason: ForbiddenLetReason::OtherForbidden,
2497+
missing_let: None,
2498+
comparison: None,
24962499
};
24972500
if self.prev_token.kind == token::BinOp(token::Or) {
24982501
// This was part of a closure, the that part of the parser recover.
@@ -2876,7 +2879,7 @@ impl<'a> Parser<'a> {
28762879
let if_span = this.prev_token.span;
28772880
let mut cond = this.parse_match_guard_condition()?;
28782881

2879-
CondChecker { parser: this, forbid_let_reason: None }.visit_expr(&mut cond);
2882+
CondChecker::new(this).visit_expr(&mut cond);
28802883

28812884
let (has_let_expr, does_not_have_bin_op) = check_let_expr(&cond);
28822885
if has_let_expr {
@@ -3552,6 +3555,14 @@ pub(crate) enum ForbiddenLetReason {
35523555
struct CondChecker<'a> {
35533556
parser: &'a Parser<'a>,
35543557
forbid_let_reason: Option<ForbiddenLetReason>,
3558+
missing_let: Option<errors::MaybeMissingLet>,
3559+
comparison: Option<errors::MaybeComparison>,
3560+
}
3561+
3562+
impl<'a> CondChecker<'a> {
3563+
fn new(parser: &'a Parser<'a>) -> Self {
3564+
CondChecker { parser, forbid_let_reason: None, missing_let: None, comparison: None }
3565+
}
35553566
}
35563567

35573568
impl MutVisitor for CondChecker<'_> {
@@ -3562,11 +3573,13 @@ impl MutVisitor for CondChecker<'_> {
35623573
match e.kind {
35633574
ExprKind::Let(_, _, _, ref mut is_recovered @ None) => {
35643575
if let Some(reason) = self.forbid_let_reason {
3565-
*is_recovered = Some(
3566-
self.parser
3567-
.sess
3568-
.emit_err(errors::ExpectedExpressionFoundLet { span, reason }),
3569-
);
3576+
*is_recovered =
3577+
Some(self.parser.sess.emit_err(errors::ExpectedExpressionFoundLet {
3578+
span,
3579+
reason,
3580+
missing_let: self.missing_let,
3581+
comparison: self.comparison,
3582+
}));
35703583
} else {
35713584
self.parser.sess.gated_spans.gate(sym::let_chains, span);
35723585
}
@@ -3590,9 +3603,28 @@ impl MutVisitor for CondChecker<'_> {
35903603
noop_visit_expr(e, self);
35913604
self.forbid_let_reason = forbid_let_reason;
35923605
}
3606+
ExprKind::Assign(ref lhs, _, span) => {
3607+
let forbid_let_reason = self.forbid_let_reason;
3608+
self.forbid_let_reason = Some(OtherForbidden);
3609+
let missing_let = self.missing_let;
3610+
if let ExprKind::Binary(_, _, rhs) = &lhs.kind
3611+
&& let ExprKind::Path(_, _)
3612+
| ExprKind::Struct(_)
3613+
| ExprKind::Call(_, _)
3614+
| ExprKind::Array(_) = rhs.kind
3615+
{
3616+
self.missing_let =
3617+
Some(errors::MaybeMissingLet { span: rhs.span.shrink_to_lo() });
3618+
}
3619+
let comparison = self.comparison;
3620+
self.comparison = Some(errors::MaybeComparison { span: span.shrink_to_hi() });
3621+
noop_visit_expr(e, self);
3622+
self.forbid_let_reason = forbid_let_reason;
3623+
self.missing_let = missing_let;
3624+
self.comparison = comparison;
3625+
}
35933626
ExprKind::Unary(_, _)
35943627
| ExprKind::Await(_, _)
3595-
| ExprKind::Assign(_, _, _)
35963628
| ExprKind::AssignOp(_, _, _)
35973629
| ExprKind::Range(_, _, _)
35983630
| ExprKind::Try(_)

Diff for: tests/ui/expr/if/bad-if-let-suggestion.rs

-3
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,3 @@
1-
// FIXME(compiler-errors): This really should suggest `let` on the RHS of the
2-
// `&&` operator, but that's kinda hard to do because of precedence.
3-
// Instead, for now we just make sure not to suggest `if let let`.
41
fn a() {
52
if let x = 1 && i = 2 {}
63
//~^ ERROR cannot find value `i` in this scope

Diff for: tests/ui/expr/if/bad-if-let-suggestion.stderr

+15-7
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,27 @@
11
error: expected expression, found `let` statement
2-
--> $DIR/bad-if-let-suggestion.rs:5:8
2+
--> $DIR/bad-if-let-suggestion.rs:2:8
33
|
44
LL | if let x = 1 && i = 2 {}
55
| ^^^^^^^^^
66
|
77
= note: only supported directly in conditions of `if` and `while` expressions
8+
help: you might have meant to continue the let-chain
9+
|
10+
LL | if let x = 1 && let i = 2 {}
11+
| +++
12+
help: you might have meant to compare for equality
13+
|
14+
LL | if let x = 1 && i == 2 {}
15+
| +
816

917
error[E0425]: cannot find value `i` in this scope
10-
--> $DIR/bad-if-let-suggestion.rs:5:21
18+
--> $DIR/bad-if-let-suggestion.rs:2:21
1119
|
1220
LL | if let x = 1 && i = 2 {}
1321
| ^ not found in this scope
1422

1523
error[E0425]: cannot find value `i` in this scope
16-
--> $DIR/bad-if-let-suggestion.rs:12:9
24+
--> $DIR/bad-if-let-suggestion.rs:9:9
1725
|
1826
LL | fn a() {
1927
| ------ similarly named function `a` defined here
@@ -22,7 +30,7 @@ LL | if (i + j) = i {}
2230
| ^ help: a function with a similar name exists: `a`
2331

2432
error[E0425]: cannot find value `j` in this scope
25-
--> $DIR/bad-if-let-suggestion.rs:12:13
33+
--> $DIR/bad-if-let-suggestion.rs:9:13
2634
|
2735
LL | fn a() {
2836
| ------ similarly named function `a` defined here
@@ -31,7 +39,7 @@ LL | if (i + j) = i {}
3139
| ^ help: a function with a similar name exists: `a`
3240

3341
error[E0425]: cannot find value `i` in this scope
34-
--> $DIR/bad-if-let-suggestion.rs:12:18
42+
--> $DIR/bad-if-let-suggestion.rs:9:18
3543
|
3644
LL | fn a() {
3745
| ------ similarly named function `a` defined here
@@ -40,7 +48,7 @@ LL | if (i + j) = i {}
4048
| ^ help: a function with a similar name exists: `a`
4149

4250
error[E0425]: cannot find value `x` in this scope
43-
--> $DIR/bad-if-let-suggestion.rs:19:8
51+
--> $DIR/bad-if-let-suggestion.rs:16:8
4452
|
4553
LL | fn a() {
4654
| ------ similarly named function `a` defined here
@@ -49,7 +57,7 @@ LL | if x[0] = 1 {}
4957
| ^ help: a function with a similar name exists: `a`
5058

5159
error[E0308]: mismatched types
52-
--> $DIR/bad-if-let-suggestion.rs:5:8
60+
--> $DIR/bad-if-let-suggestion.rs:2:8
5361
|
5462
LL | if let x = 1 && i = 2 {}
5563
| ^^^^^^^^^^^^^^^^^^ expected `bool`, found `()`

Diff for: tests/ui/rfcs/rfc-2497-if-let-chains/invalid-let-in-a-valid-let-context.stderr

+8
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,10 @@ LL | if let Some(elem) = _opt && [1, 2, 3][let _ = &&let Some(x) = Some(
2929
| ^^^^^^^^^^^^^^^^^^^^^
3030
|
3131
= note: only supported directly in conditions of `if` and `while` expressions
32+
help: you might have meant to compare for equality
33+
|
34+
LL | if let Some(elem) = _opt && [1, 2, 3][let _ = &&let Some(x) = Some(42)] == 1 {
35+
| +
3236

3337
error: expected expression, found `let` statement
3438
--> $DIR/invalid-let-in-a-valid-let-context.rs:24:23
@@ -53,6 +57,10 @@ LL | if let Some(elem) = _opt && [1, 2, 3][let _ = ()] = 1 {
5357
| ^^^^^^^^^^^^^^^^^^^^^
5458
|
5559
= note: only supported directly in conditions of `if` and `while` expressions
60+
help: you might have meant to compare for equality
61+
|
62+
LL | if let Some(elem) = _opt && [1, 2, 3][let _ = ()] == 1 {
63+
| +
5664

5765
error: expected expression, found `let` statement
5866
--> $DIR/invalid-let-in-a-valid-let-context.rs:42:21

0 commit comments

Comments
 (0)