Skip to content

Commit e040920

Browse files
Rollup merge of #97856 - compiler-errors:bad-let-suggestions, r=estebank
Don't suggest adding `let` in certain `if` conditions Avoid being too eager to suggest `let` in an `if` condition with an `=`, namely when the LHS of the `=` isn't even valid as a pattern (to a first degree approximation). This heustic I came up with kinda sucks. Let me know if it needs to be refined.
2 parents 888d72c + 2ae1ec9 commit e040920

File tree

6 files changed

+142
-8
lines changed

6 files changed

+142
-8
lines changed

compiler/rustc_ast/src/ast.rs

+16
Original file line numberDiff line numberDiff line change
@@ -1278,6 +1278,22 @@ impl Expr {
12781278
},
12791279
)
12801280
}
1281+
1282+
// To a first-order approximation, is this a pattern
1283+
pub fn is_approximately_pattern(&self) -> bool {
1284+
match &self.peel_parens().kind {
1285+
ExprKind::Box(_)
1286+
| ExprKind::Array(_)
1287+
| ExprKind::Call(_, _)
1288+
| ExprKind::Tup(_)
1289+
| ExprKind::Lit(_)
1290+
| ExprKind::Range(_, _, _)
1291+
| ExprKind::Underscore
1292+
| ExprKind::Path(_, _)
1293+
| ExprKind::Struct(_) => true,
1294+
_ => false,
1295+
}
1296+
}
12811297
}
12821298

12831299
/// Limit types of a range (inclusive or exclusive)

compiler/rustc_hir/src/hir.rs

+14
Original file line numberDiff line numberDiff line change
@@ -1813,6 +1813,20 @@ impl Expr<'_> {
18131813
| ExprKind::Err => true,
18141814
}
18151815
}
1816+
1817+
// To a first-order approximation, is this a pattern
1818+
pub fn is_approximately_pattern(&self) -> bool {
1819+
match &self.kind {
1820+
ExprKind::Box(_)
1821+
| ExprKind::Array(_)
1822+
| ExprKind::Call(..)
1823+
| ExprKind::Tup(_)
1824+
| ExprKind::Lit(_)
1825+
| ExprKind::Path(_)
1826+
| ExprKind::Struct(..) => true,
1827+
_ => false,
1828+
}
1829+
}
18161830
}
18171831

18181832
/// Checks if the specified expression is a built-in range literal.

compiler/rustc_resolve/src/late/diagnostics.rs

+15-7
Original file line numberDiff line numberDiff line change
@@ -265,13 +265,21 @@ impl<'a: 'ast, 'ast> LateResolutionVisitor<'a, '_, 'ast> {
265265
);
266266
}
267267
match (source, self.diagnostic_metadata.in_if_condition) {
268-
(PathSource::Expr(_), Some(Expr { span, kind: ExprKind::Assign(..), .. })) => {
269-
err.span_suggestion_verbose(
270-
span.shrink_to_lo(),
271-
"you might have meant to use pattern matching",
272-
"let ".to_string(),
273-
Applicability::MaybeIncorrect,
274-
);
268+
(
269+
PathSource::Expr(_),
270+
Some(Expr { span: expr_span, kind: ExprKind::Assign(lhs, _, _), .. }),
271+
) => {
272+
// Icky heuristic so we don't suggest:
273+
// `if (i + 2) = 2` => `if let (i + 2) = 2` (approximately pattern)
274+
// `if 2 = i` => `if let 2 = i` (lhs needs to contain error span)
275+
if lhs.is_approximately_pattern() && lhs.span.contains(span) {
276+
err.span_suggestion_verbose(
277+
expr_span.shrink_to_lo(),
278+
"you might have meant to use pattern matching",
279+
"let ".to_string(),
280+
Applicability::MaybeIncorrect,
281+
);
282+
}
275283
}
276284
_ => {}
277285
}

compiler/rustc_typeck/src/check/expr.rs

+4-1
Original file line numberDiff line numberDiff line change
@@ -1035,7 +1035,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
10351035
} else {
10361036
(Applicability::MaybeIncorrect, false)
10371037
};
1038-
if !lhs.is_syntactic_place_expr() && !matches!(lhs.kind, hir::ExprKind::Lit(_)) {
1038+
if !lhs.is_syntactic_place_expr()
1039+
&& lhs.is_approximately_pattern()
1040+
&& !matches!(lhs.kind, hir::ExprKind::Lit(_))
1041+
{
10391042
// Do not suggest `if let x = y` as `==` is way more likely to be the intention.
10401043
let hir = self.tcx.hir();
10411044
if let hir::Node::Expr(hir::Expr { kind: ExprKind::If { .. }, .. }) =
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
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`.
4+
fn a() {
5+
if let x = 1 && i = 2 {}
6+
//~^ ERROR cannot find value `i` in this scope
7+
//~| ERROR `let` expressions in this position are unstable
8+
//~| ERROR mismatched types
9+
//~| ERROR `let` expressions are not supported here
10+
}
11+
12+
fn b() {
13+
if (i + j) = i {}
14+
//~^ ERROR cannot find value `i` in this scope
15+
//~| ERROR cannot find value `i` in this scope
16+
//~| ERROR cannot find value `j` in this scope
17+
}
18+
19+
fn c() {
20+
if x[0] = 1 {}
21+
//~^ ERROR cannot find value `x` in this scope
22+
}
23+
24+
fn main() {}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
error: `let` expressions are not supported here
2+
--> $DIR/bad-if-let-suggestion.rs:5:8
3+
|
4+
LL | if let x = 1 && i = 2 {}
5+
| ^^^^^^^^^
6+
|
7+
= note: only supported directly in conditions of `if` and `while` expressions
8+
9+
error[E0425]: cannot find value `i` in this scope
10+
--> $DIR/bad-if-let-suggestion.rs:5:21
11+
|
12+
LL | if let x = 1 && i = 2 {}
13+
| ^ not found in this scope
14+
15+
error[E0425]: cannot find value `i` in this scope
16+
--> $DIR/bad-if-let-suggestion.rs:13:9
17+
|
18+
LL | fn a() {
19+
| ------ similarly named function `a` defined here
20+
...
21+
LL | if (i + j) = i {}
22+
| ^ help: a function with a similar name exists: `a`
23+
24+
error[E0425]: cannot find value `j` in this scope
25+
--> $DIR/bad-if-let-suggestion.rs:13:13
26+
|
27+
LL | fn a() {
28+
| ------ similarly named function `a` defined here
29+
...
30+
LL | if (i + j) = i {}
31+
| ^ help: a function with a similar name exists: `a`
32+
33+
error[E0425]: cannot find value `i` in this scope
34+
--> $DIR/bad-if-let-suggestion.rs:13:18
35+
|
36+
LL | fn a() {
37+
| ------ similarly named function `a` defined here
38+
...
39+
LL | if (i + j) = i {}
40+
| ^ help: a function with a similar name exists: `a`
41+
42+
error[E0425]: cannot find value `x` in this scope
43+
--> $DIR/bad-if-let-suggestion.rs:20:8
44+
|
45+
LL | fn a() {
46+
| ------ similarly named function `a` defined here
47+
...
48+
LL | if x[0] = 1 {}
49+
| ^ help: a function with a similar name exists: `a`
50+
51+
error[E0658]: `let` expressions in this position are unstable
52+
--> $DIR/bad-if-let-suggestion.rs:5:8
53+
|
54+
LL | if let x = 1 && i = 2 {}
55+
| ^^^^^^^^^
56+
|
57+
= note: see issue #53667 <https://github.com/rust-lang/rust/issues/53667> for more information
58+
= help: add `#![feature(let_chains)]` to the crate attributes to enable
59+
60+
error[E0308]: mismatched types
61+
--> $DIR/bad-if-let-suggestion.rs:5:8
62+
|
63+
LL | if let x = 1 && i = 2 {}
64+
| ^^^^^^^^^^^^^^^^^^ expected `bool`, found `()`
65+
66+
error: aborting due to 8 previous errors
67+
68+
Some errors have detailed explanations: E0308, E0425, E0658.
69+
For more information about an error, try `rustc --explain E0308`.

0 commit comments

Comments
 (0)