Skip to content

Commit 11fa085

Browse files
committed
Auto merge of rust-lang#103636 - chenyukang:yukang/fix-103587-sugg-if-let, r=jackh276,davidtwco
Recover from common if let syntax mistakes/typos Fixes rust-lang#103587
2 parents 5eef9b2 + 91b4e7c commit 11fa085

File tree

11 files changed

+184
-4
lines changed

11 files changed

+184
-4
lines changed

compiler/rustc_error_messages/locales/en-US/infer.ftl

+1
Original file line numberDiff line numberDiff line change
@@ -171,3 +171,4 @@ infer_msl_introduces_static = introduces a `'static` lifetime requirement
171171
infer_msl_unmet_req = because this has an unmet lifetime requirement
172172
infer_msl_trait_note = this has an implicit `'static` lifetime requirement
173173
infer_msl_trait_sugg = consider relaxing the implicit `'static` requirement
174+
infer_suggest_add_let_for_letchains = consider adding `let`

compiler/rustc_error_messages/locales/en-US/parser.ftl

+3
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,9 @@ parser_if_expression_missing_condition = missing condition for `if` expression
125125
126126
parser_expected_expression_found_let = expected expression, found `let` statement
127127
128+
parser_expect_eq_instead_of_eqeq = expected `=`, found `==`
129+
.suggestion = consider using `=` here
130+
128131
parser_expected_else_block = expected `{"{"}`, found {$first_tok}
129132
.label = expected an `if` or a block after this `else`
130133
.suggestion = add an `if` if this is the condition of a chained `else if` statement

compiler/rustc_infer/src/errors/mod.rs

+12
Original file line numberDiff line numberDiff line change
@@ -180,6 +180,18 @@ pub enum SourceKindMultiSuggestion<'a> {
180180
},
181181
}
182182

183+
#[derive(Subdiagnostic)]
184+
#[suggestion(
185+
infer_suggest_add_let_for_letchains,
186+
style = "verbose",
187+
applicability = "machine-applicable",
188+
code = "let "
189+
)]
190+
pub(crate) struct SuggAddLetForLetChains {
191+
#[primary_span]
192+
pub span: Span,
193+
}
194+
183195
impl<'a> SourceKindMultiSuggestion<'a> {
184196
pub fn new_fully_qualified(
185197
span: Span,

compiler/rustc_infer/src/infer/error_reporting/mod.rs

+70
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
// ignore-tidy-filelength
12
//! Error Reporting Code for the inference engine
23
//!
34
//! Because of the way inference, and in particular region inference,
@@ -58,12 +59,15 @@ use crate::traits::{
5859
StatementAsExpression,
5960
};
6061

62+
use crate::errors::SuggAddLetForLetChains;
63+
use hir::intravisit::{walk_expr, walk_stmt};
6164
use rustc_data_structures::fx::{FxIndexMap, FxIndexSet};
6265
use rustc_errors::{pluralize, struct_span_err, Diagnostic, ErrorGuaranteed, IntoDiagnosticArg};
6366
use rustc_errors::{Applicability, DiagnosticBuilder, DiagnosticStyledString, MultiSpan};
6467
use rustc_hir as hir;
6568
use rustc_hir::def::DefKind;
6669
use rustc_hir::def_id::{DefId, LocalDefId};
70+
use rustc_hir::intravisit::Visitor;
6771
use rustc_hir::lang_items::LangItem;
6872
use rustc_hir::Node;
6973
use rustc_middle::dep_graph::DepContext;
@@ -2336,6 +2340,11 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
23362340
}
23372341
}
23382342
}
2343+
// For code `if Some(..) = expr `, the type mismatch may be expected `bool` but found `()`,
2344+
// we try to suggest to add the missing `let` for `if let Some(..) = expr`
2345+
(ty::Bool, ty::Tuple(list)) => if list.len() == 0 {
2346+
self.suggest_let_for_letchains(&mut err, &trace.cause, span);
2347+
}
23392348
_ => {}
23402349
}
23412350
}
@@ -2360,6 +2369,67 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
23602369
diag
23612370
}
23622371

2372+
/// Try to find code with pattern `if Some(..) = expr`
2373+
/// use a `visitor` to mark the `if` which its span contains given error span,
2374+
/// and then try to find a assignment in the `cond` part, which span is equal with error span
2375+
fn suggest_let_for_letchains(
2376+
&self,
2377+
err: &mut Diagnostic,
2378+
cause: &ObligationCause<'_>,
2379+
span: Span,
2380+
) {
2381+
let hir = self.tcx.hir();
2382+
let fn_hir_id = hir.get_parent_node(cause.body_id);
2383+
if let Some(node) = self.tcx.hir().find(fn_hir_id) &&
2384+
let hir::Node::Item(hir::Item {
2385+
kind: hir::ItemKind::Fn(_sig, _, body_id), ..
2386+
}) = node {
2387+
let body = hir.body(*body_id);
2388+
2389+
/// Find the if expression with given span
2390+
struct IfVisitor {
2391+
pub result: bool,
2392+
pub found_if: bool,
2393+
pub err_span: Span,
2394+
}
2395+
2396+
impl<'v> Visitor<'v> for IfVisitor {
2397+
fn visit_expr(&mut self, ex: &'v hir::Expr<'v>) {
2398+
if self.result { return; }
2399+
match ex.kind {
2400+
hir::ExprKind::If(cond, _, _) => {
2401+
self.found_if = true;
2402+
walk_expr(self, cond);
2403+
self.found_if = false;
2404+
}
2405+
_ => walk_expr(self, ex),
2406+
}
2407+
}
2408+
2409+
fn visit_stmt(&mut self, ex: &'v hir::Stmt<'v>) {
2410+
if let hir::StmtKind::Local(hir::Local {
2411+
span, pat: hir::Pat{..}, ty: None, init: Some(_), ..
2412+
}) = &ex.kind
2413+
&& self.found_if
2414+
&& span.eq(&self.err_span) {
2415+
self.result = true;
2416+
}
2417+
walk_stmt(self, ex);
2418+
}
2419+
2420+
fn visit_body(&mut self, body: &'v hir::Body<'v>) {
2421+
hir::intravisit::walk_body(self, body);
2422+
}
2423+
}
2424+
2425+
let mut visitor = IfVisitor { err_span: span, found_if: false, result: false };
2426+
visitor.visit_body(&body);
2427+
if visitor.result {
2428+
err.subdiagnostic(SuggAddLetForLetChains{span: span.shrink_to_lo()});
2429+
}
2430+
}
2431+
}
2432+
23632433
fn emit_tuple_wrap_err(
23642434
&self,
23652435
err: &mut Diagnostic,

compiler/rustc_parse/src/errors.rs

+9
Original file line numberDiff line numberDiff line change
@@ -420,6 +420,15 @@ pub(crate) struct ExpectedExpressionFoundLet {
420420
pub span: Span,
421421
}
422422

423+
#[derive(Diagnostic)]
424+
#[diag(parser_expect_eq_instead_of_eqeq)]
425+
pub(crate) struct ExpectedEqForLetExpr {
426+
#[primary_span]
427+
pub span: Span,
428+
#[suggestion(applicability = "maybe-incorrect", code = "=", style = "verbose")]
429+
pub sugg_span: Span,
430+
}
431+
423432
#[derive(Diagnostic)]
424433
#[diag(parser_expected_else_block)]
425434
pub(crate) struct ExpectedElseBlock {

compiler/rustc_parse/src/parser/expr.rs

+12-4
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,9 @@ use crate::errors::{
99
ArrayBracketsInsteadOfSpaces, ArrayBracketsInsteadOfSpacesSugg, AsyncMoveOrderIncorrect,
1010
BinaryFloatLiteralNotSupported, BracesForStructLiteral, CatchAfterTry, CommaAfterBaseStruct,
1111
ComparisonInterpretedAsGeneric, ComparisonOrShiftInterpretedAsGenericSugg,
12-
DoCatchSyntaxRemoved, DotDotDot, EqFieldInit, ExpectedElseBlock, ExpectedExpressionFoundLet,
13-
FieldExpressionWithGeneric, FloatLiteralRequiresIntegerPart, FoundExprWouldBeStmt,
14-
HexadecimalFloatLiteralNotSupported, IfExpressionMissingCondition,
12+
DoCatchSyntaxRemoved, DotDotDot, EqFieldInit, ExpectedElseBlock, ExpectedEqForLetExpr,
13+
ExpectedExpressionFoundLet, FieldExpressionWithGeneric, FloatLiteralRequiresIntegerPart,
14+
FoundExprWouldBeStmt, HexadecimalFloatLiteralNotSupported, IfExpressionMissingCondition,
1515
IfExpressionMissingThenBlock, IfExpressionMissingThenBlockSub, IntLiteralTooLarge,
1616
InvalidBlockMacroSegment, InvalidComparisonOperator, InvalidComparisonOperatorSub,
1717
InvalidFloatLiteralSuffix, InvalidFloatLiteralWidth, InvalidIntLiteralWidth,
@@ -2329,7 +2329,15 @@ impl<'a> Parser<'a> {
23292329
RecoverColon::Yes,
23302330
CommaRecoveryMode::LikelyTuple,
23312331
)?;
2332-
self.expect(&token::Eq)?;
2332+
if self.token == token::EqEq {
2333+
self.sess.emit_err(ExpectedEqForLetExpr {
2334+
span: self.token.span,
2335+
sugg_span: self.token.span,
2336+
});
2337+
self.bump();
2338+
} else {
2339+
self.expect(&token::Eq)?;
2340+
}
23332341
let expr = self.with_res(self.restrictions | Restrictions::NO_STRUCT_LITERAL, |this| {
23342342
this.parse_assoc_expr_with(1 + prec_let_scrutinee_needs_par(), None.into())
23352343
})?;

src/test/ui/did_you_mean/issue-103909.stderr

+5
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,11 @@ error[E0308]: mismatched types
1414
|
1515
LL | if Err(err) = File::open("hello.txt") {
1616
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `bool`, found `()`
17+
|
18+
help: consider adding `let`
19+
|
20+
LL | if let Err(err) = File::open("hello.txt") {
21+
| +++
1722

1823
error: aborting due to 2 previous errors
1924

src/test/ui/inference/issue-103587.rs

+12
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
fn main() {
2+
let x = Some(123);
3+
4+
if let Some(_) == x {}
5+
//~^ ERROR expected `=`, found `==`
6+
7+
if Some(_) = x {}
8+
//~^ ERROR mismatched types
9+
10+
if None = x { }
11+
//~^ ERROR mismatched types
12+
}
+40
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
error: expected `=`, found `==`
2+
--> $DIR/issue-103587.rs:4:20
3+
|
4+
LL | if let Some(_) == x {}
5+
| ^^
6+
|
7+
help: consider using `=` here
8+
|
9+
LL | if let Some(_) = x {}
10+
| ~
11+
12+
error[E0308]: mismatched types
13+
--> $DIR/issue-103587.rs:7:8
14+
|
15+
LL | if Some(_) = x {}
16+
| ^^^^^^^^^^^ expected `bool`, found `()`
17+
|
18+
help: consider adding `let`
19+
|
20+
LL | if let Some(_) = x {}
21+
| +++
22+
23+
error[E0308]: mismatched types
24+
--> $DIR/issue-103587.rs:10:8
25+
|
26+
LL | if None = x { }
27+
| ^^^^^^^^ expected `bool`, found `()`
28+
|
29+
help: you might have meant to use pattern matching
30+
|
31+
LL | if let None = x { }
32+
| +++
33+
help: you might have meant to compare for equality
34+
|
35+
LL | if None == x { }
36+
| +
37+
38+
error: aborting due to 3 previous errors
39+
40+
For more information about this error, try `rustc --explain E0308`.

src/test/ui/suggestions/if-let-typo.stderr

+15
Original file line numberDiff line numberDiff line change
@@ -25,12 +25,22 @@ error[E0308]: mismatched types
2525
|
2626
LL | if Some(x) = foo {}
2727
| ^^^^^^^^^^^^^ expected `bool`, found `()`
28+
|
29+
help: consider adding `let`
30+
|
31+
LL | if let Some(x) = foo {}
32+
| +++
2833

2934
error[E0308]: mismatched types
3035
--> $DIR/if-let-typo.rs:6:8
3136
|
3237
LL | if Some(foo) = bar {}
3338
| ^^^^^^^^^^^^^^^ expected `bool`, found `()`
39+
|
40+
help: consider adding `let`
41+
|
42+
LL | if let Some(foo) = bar {}
43+
| +++
3444

3545
error[E0308]: mismatched types
3646
--> $DIR/if-let-typo.rs:7:8
@@ -51,6 +61,11 @@ error[E0308]: mismatched types
5161
|
5262
LL | if Some(3) = foo {}
5363
| ^^^^^^^^^^^^^ expected `bool`, found `()`
64+
|
65+
help: consider adding `let`
66+
|
67+
LL | if let Some(3) = foo {}
68+
| +++
5469

5570
error: aborting due to 7 previous errors
5671

src/tools/clippy/tests/ui/crashes/ice-6250.stderr

+5
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,11 @@ error[E0308]: mismatched types
2323
|
2424
LL | Some(reference) = cache.data.get(key) {
2525
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `bool`, found `()`
26+
|
27+
help: consider adding `let`
28+
|
29+
LL | let Some(reference) = cache.data.get(key) {
30+
| +++
2631

2732
error: aborting due to 3 previous errors
2833

0 commit comments

Comments
 (0)