Auto merge of #103636 - chenyukang:yukang/fix-103587-sugg-if-let, r=jackh276,davidtwco
Recover from common if let syntax mistakes/typos Fixes #103587
This commit is contained in:
commit
11fa0850f0
11 changed files with 184 additions and 4 deletions
|
@ -171,3 +171,4 @@ infer_msl_introduces_static = introduces a `'static` lifetime requirement
|
||||||
infer_msl_unmet_req = because this has an unmet lifetime requirement
|
infer_msl_unmet_req = because this has an unmet lifetime requirement
|
||||||
infer_msl_trait_note = this has an implicit `'static` lifetime requirement
|
infer_msl_trait_note = this has an implicit `'static` lifetime requirement
|
||||||
infer_msl_trait_sugg = consider relaxing the implicit `'static` requirement
|
infer_msl_trait_sugg = consider relaxing the implicit `'static` requirement
|
||||||
|
infer_suggest_add_let_for_letchains = consider adding `let`
|
||||||
|
|
|
@ -125,6 +125,9 @@ parser_if_expression_missing_condition = missing condition for `if` expression
|
||||||
|
|
||||||
parser_expected_expression_found_let = expected expression, found `let` statement
|
parser_expected_expression_found_let = expected expression, found `let` statement
|
||||||
|
|
||||||
|
parser_expect_eq_instead_of_eqeq = expected `=`, found `==`
|
||||||
|
.suggestion = consider using `=` here
|
||||||
|
|
||||||
parser_expected_else_block = expected `{"{"}`, found {$first_tok}
|
parser_expected_else_block = expected `{"{"}`, found {$first_tok}
|
||||||
.label = expected an `if` or a block after this `else`
|
.label = expected an `if` or a block after this `else`
|
||||||
.suggestion = add an `if` if this is the condition of a chained `else if` statement
|
.suggestion = add an `if` if this is the condition of a chained `else if` statement
|
||||||
|
|
|
@ -180,6 +180,18 @@ pub enum SourceKindMultiSuggestion<'a> {
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Subdiagnostic)]
|
||||||
|
#[suggestion(
|
||||||
|
infer_suggest_add_let_for_letchains,
|
||||||
|
style = "verbose",
|
||||||
|
applicability = "machine-applicable",
|
||||||
|
code = "let "
|
||||||
|
)]
|
||||||
|
pub(crate) struct SuggAddLetForLetChains {
|
||||||
|
#[primary_span]
|
||||||
|
pub span: Span,
|
||||||
|
}
|
||||||
|
|
||||||
impl<'a> SourceKindMultiSuggestion<'a> {
|
impl<'a> SourceKindMultiSuggestion<'a> {
|
||||||
pub fn new_fully_qualified(
|
pub fn new_fully_qualified(
|
||||||
span: Span,
|
span: Span,
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
// ignore-tidy-filelength
|
||||||
//! Error Reporting Code for the inference engine
|
//! Error Reporting Code for the inference engine
|
||||||
//!
|
//!
|
||||||
//! Because of the way inference, and in particular region inference,
|
//! Because of the way inference, and in particular region inference,
|
||||||
|
@ -58,12 +59,15 @@ use crate::traits::{
|
||||||
StatementAsExpression,
|
StatementAsExpression,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
use crate::errors::SuggAddLetForLetChains;
|
||||||
|
use hir::intravisit::{walk_expr, walk_stmt};
|
||||||
use rustc_data_structures::fx::{FxIndexMap, FxIndexSet};
|
use rustc_data_structures::fx::{FxIndexMap, FxIndexSet};
|
||||||
use rustc_errors::{pluralize, struct_span_err, Diagnostic, ErrorGuaranteed, IntoDiagnosticArg};
|
use rustc_errors::{pluralize, struct_span_err, Diagnostic, ErrorGuaranteed, IntoDiagnosticArg};
|
||||||
use rustc_errors::{Applicability, DiagnosticBuilder, DiagnosticStyledString, MultiSpan};
|
use rustc_errors::{Applicability, DiagnosticBuilder, DiagnosticStyledString, MultiSpan};
|
||||||
use rustc_hir as hir;
|
use rustc_hir as hir;
|
||||||
use rustc_hir::def::DefKind;
|
use rustc_hir::def::DefKind;
|
||||||
use rustc_hir::def_id::{DefId, LocalDefId};
|
use rustc_hir::def_id::{DefId, LocalDefId};
|
||||||
|
use rustc_hir::intravisit::Visitor;
|
||||||
use rustc_hir::lang_items::LangItem;
|
use rustc_hir::lang_items::LangItem;
|
||||||
use rustc_hir::Node;
|
use rustc_hir::Node;
|
||||||
use rustc_middle::dep_graph::DepContext;
|
use rustc_middle::dep_graph::DepContext;
|
||||||
|
@ -2336,6 +2340,11 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// For code `if Some(..) = expr `, the type mismatch may be expected `bool` but found `()`,
|
||||||
|
// we try to suggest to add the missing `let` for `if let Some(..) = expr`
|
||||||
|
(ty::Bool, ty::Tuple(list)) => if list.len() == 0 {
|
||||||
|
self.suggest_let_for_letchains(&mut err, &trace.cause, span);
|
||||||
|
}
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2360,6 +2369,67 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
|
||||||
diag
|
diag
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Try to find code with pattern `if Some(..) = expr`
|
||||||
|
/// use a `visitor` to mark the `if` which its span contains given error span,
|
||||||
|
/// and then try to find a assignment in the `cond` part, which span is equal with error span
|
||||||
|
fn suggest_let_for_letchains(
|
||||||
|
&self,
|
||||||
|
err: &mut Diagnostic,
|
||||||
|
cause: &ObligationCause<'_>,
|
||||||
|
span: Span,
|
||||||
|
) {
|
||||||
|
let hir = self.tcx.hir();
|
||||||
|
let fn_hir_id = hir.get_parent_node(cause.body_id);
|
||||||
|
if let Some(node) = self.tcx.hir().find(fn_hir_id) &&
|
||||||
|
let hir::Node::Item(hir::Item {
|
||||||
|
kind: hir::ItemKind::Fn(_sig, _, body_id), ..
|
||||||
|
}) = node {
|
||||||
|
let body = hir.body(*body_id);
|
||||||
|
|
||||||
|
/// Find the if expression with given span
|
||||||
|
struct IfVisitor {
|
||||||
|
pub result: bool,
|
||||||
|
pub found_if: bool,
|
||||||
|
pub err_span: Span,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'v> Visitor<'v> for IfVisitor {
|
||||||
|
fn visit_expr(&mut self, ex: &'v hir::Expr<'v>) {
|
||||||
|
if self.result { return; }
|
||||||
|
match ex.kind {
|
||||||
|
hir::ExprKind::If(cond, _, _) => {
|
||||||
|
self.found_if = true;
|
||||||
|
walk_expr(self, cond);
|
||||||
|
self.found_if = false;
|
||||||
|
}
|
||||||
|
_ => walk_expr(self, ex),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn visit_stmt(&mut self, ex: &'v hir::Stmt<'v>) {
|
||||||
|
if let hir::StmtKind::Local(hir::Local {
|
||||||
|
span, pat: hir::Pat{..}, ty: None, init: Some(_), ..
|
||||||
|
}) = &ex.kind
|
||||||
|
&& self.found_if
|
||||||
|
&& span.eq(&self.err_span) {
|
||||||
|
self.result = true;
|
||||||
|
}
|
||||||
|
walk_stmt(self, ex);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn visit_body(&mut self, body: &'v hir::Body<'v>) {
|
||||||
|
hir::intravisit::walk_body(self, body);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut visitor = IfVisitor { err_span: span, found_if: false, result: false };
|
||||||
|
visitor.visit_body(&body);
|
||||||
|
if visitor.result {
|
||||||
|
err.subdiagnostic(SuggAddLetForLetChains{span: span.shrink_to_lo()});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn emit_tuple_wrap_err(
|
fn emit_tuple_wrap_err(
|
||||||
&self,
|
&self,
|
||||||
err: &mut Diagnostic,
|
err: &mut Diagnostic,
|
||||||
|
|
|
@ -420,6 +420,15 @@ pub(crate) struct ExpectedExpressionFoundLet {
|
||||||
pub span: Span,
|
pub span: Span,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Diagnostic)]
|
||||||
|
#[diag(parser_expect_eq_instead_of_eqeq)]
|
||||||
|
pub(crate) struct ExpectedEqForLetExpr {
|
||||||
|
#[primary_span]
|
||||||
|
pub span: Span,
|
||||||
|
#[suggestion(applicability = "maybe-incorrect", code = "=", style = "verbose")]
|
||||||
|
pub sugg_span: Span,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Diagnostic)]
|
#[derive(Diagnostic)]
|
||||||
#[diag(parser_expected_else_block)]
|
#[diag(parser_expected_else_block)]
|
||||||
pub(crate) struct ExpectedElseBlock {
|
pub(crate) struct ExpectedElseBlock {
|
||||||
|
|
|
@ -9,9 +9,9 @@ use crate::errors::{
|
||||||
ArrayBracketsInsteadOfSpaces, ArrayBracketsInsteadOfSpacesSugg, AsyncMoveOrderIncorrect,
|
ArrayBracketsInsteadOfSpaces, ArrayBracketsInsteadOfSpacesSugg, AsyncMoveOrderIncorrect,
|
||||||
BinaryFloatLiteralNotSupported, BracesForStructLiteral, CatchAfterTry, CommaAfterBaseStruct,
|
BinaryFloatLiteralNotSupported, BracesForStructLiteral, CatchAfterTry, CommaAfterBaseStruct,
|
||||||
ComparisonInterpretedAsGeneric, ComparisonOrShiftInterpretedAsGenericSugg,
|
ComparisonInterpretedAsGeneric, ComparisonOrShiftInterpretedAsGenericSugg,
|
||||||
DoCatchSyntaxRemoved, DotDotDot, EqFieldInit, ExpectedElseBlock, ExpectedExpressionFoundLet,
|
DoCatchSyntaxRemoved, DotDotDot, EqFieldInit, ExpectedElseBlock, ExpectedEqForLetExpr,
|
||||||
FieldExpressionWithGeneric, FloatLiteralRequiresIntegerPart, FoundExprWouldBeStmt,
|
ExpectedExpressionFoundLet, FieldExpressionWithGeneric, FloatLiteralRequiresIntegerPart,
|
||||||
HexadecimalFloatLiteralNotSupported, IfExpressionMissingCondition,
|
FoundExprWouldBeStmt, HexadecimalFloatLiteralNotSupported, IfExpressionMissingCondition,
|
||||||
IfExpressionMissingThenBlock, IfExpressionMissingThenBlockSub, IntLiteralTooLarge,
|
IfExpressionMissingThenBlock, IfExpressionMissingThenBlockSub, IntLiteralTooLarge,
|
||||||
InvalidBlockMacroSegment, InvalidComparisonOperator, InvalidComparisonOperatorSub,
|
InvalidBlockMacroSegment, InvalidComparisonOperator, InvalidComparisonOperatorSub,
|
||||||
InvalidFloatLiteralSuffix, InvalidFloatLiteralWidth, InvalidIntLiteralWidth,
|
InvalidFloatLiteralSuffix, InvalidFloatLiteralWidth, InvalidIntLiteralWidth,
|
||||||
|
@ -2329,7 +2329,15 @@ impl<'a> Parser<'a> {
|
||||||
RecoverColon::Yes,
|
RecoverColon::Yes,
|
||||||
CommaRecoveryMode::LikelyTuple,
|
CommaRecoveryMode::LikelyTuple,
|
||||||
)?;
|
)?;
|
||||||
self.expect(&token::Eq)?;
|
if self.token == token::EqEq {
|
||||||
|
self.sess.emit_err(ExpectedEqForLetExpr {
|
||||||
|
span: self.token.span,
|
||||||
|
sugg_span: self.token.span,
|
||||||
|
});
|
||||||
|
self.bump();
|
||||||
|
} else {
|
||||||
|
self.expect(&token::Eq)?;
|
||||||
|
}
|
||||||
let expr = self.with_res(self.restrictions | Restrictions::NO_STRUCT_LITERAL, |this| {
|
let expr = self.with_res(self.restrictions | Restrictions::NO_STRUCT_LITERAL, |this| {
|
||||||
this.parse_assoc_expr_with(1 + prec_let_scrutinee_needs_par(), None.into())
|
this.parse_assoc_expr_with(1 + prec_let_scrutinee_needs_par(), None.into())
|
||||||
})?;
|
})?;
|
||||||
|
|
|
@ -14,6 +14,11 @@ error[E0308]: mismatched types
|
||||||
|
|
|
|
||||||
LL | if Err(err) = File::open("hello.txt") {
|
LL | if Err(err) = File::open("hello.txt") {
|
||||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `bool`, found `()`
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `bool`, found `()`
|
||||||
|
|
|
||||||
|
help: consider adding `let`
|
||||||
|
|
|
||||||
|
LL | if let Err(err) = File::open("hello.txt") {
|
||||||
|
| +++
|
||||||
|
|
||||||
error: aborting due to 2 previous errors
|
error: aborting due to 2 previous errors
|
||||||
|
|
||||||
|
|
12
src/test/ui/inference/issue-103587.rs
Normal file
12
src/test/ui/inference/issue-103587.rs
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
fn main() {
|
||||||
|
let x = Some(123);
|
||||||
|
|
||||||
|
if let Some(_) == x {}
|
||||||
|
//~^ ERROR expected `=`, found `==`
|
||||||
|
|
||||||
|
if Some(_) = x {}
|
||||||
|
//~^ ERROR mismatched types
|
||||||
|
|
||||||
|
if None = x { }
|
||||||
|
//~^ ERROR mismatched types
|
||||||
|
}
|
40
src/test/ui/inference/issue-103587.stderr
Normal file
40
src/test/ui/inference/issue-103587.stderr
Normal file
|
@ -0,0 +1,40 @@
|
||||||
|
error: expected `=`, found `==`
|
||||||
|
--> $DIR/issue-103587.rs:4:20
|
||||||
|
|
|
||||||
|
LL | if let Some(_) == x {}
|
||||||
|
| ^^
|
||||||
|
|
|
||||||
|
help: consider using `=` here
|
||||||
|
|
|
||||||
|
LL | if let Some(_) = x {}
|
||||||
|
| ~
|
||||||
|
|
||||||
|
error[E0308]: mismatched types
|
||||||
|
--> $DIR/issue-103587.rs:7:8
|
||||||
|
|
|
||||||
|
LL | if Some(_) = x {}
|
||||||
|
| ^^^^^^^^^^^ expected `bool`, found `()`
|
||||||
|
|
|
||||||
|
help: consider adding `let`
|
||||||
|
|
|
||||||
|
LL | if let Some(_) = x {}
|
||||||
|
| +++
|
||||||
|
|
||||||
|
error[E0308]: mismatched types
|
||||||
|
--> $DIR/issue-103587.rs:10:8
|
||||||
|
|
|
||||||
|
LL | if None = x { }
|
||||||
|
| ^^^^^^^^ expected `bool`, found `()`
|
||||||
|
|
|
||||||
|
help: you might have meant to use pattern matching
|
||||||
|
|
|
||||||
|
LL | if let None = x { }
|
||||||
|
| +++
|
||||||
|
help: you might have meant to compare for equality
|
||||||
|
|
|
||||||
|
LL | if None == x { }
|
||||||
|
| +
|
||||||
|
|
||||||
|
error: aborting due to 3 previous errors
|
||||||
|
|
||||||
|
For more information about this error, try `rustc --explain E0308`.
|
|
@ -25,12 +25,22 @@ error[E0308]: mismatched types
|
||||||
|
|
|
|
||||||
LL | if Some(x) = foo {}
|
LL | if Some(x) = foo {}
|
||||||
| ^^^^^^^^^^^^^ expected `bool`, found `()`
|
| ^^^^^^^^^^^^^ expected `bool`, found `()`
|
||||||
|
|
|
||||||
|
help: consider adding `let`
|
||||||
|
|
|
||||||
|
LL | if let Some(x) = foo {}
|
||||||
|
| +++
|
||||||
|
|
||||||
error[E0308]: mismatched types
|
error[E0308]: mismatched types
|
||||||
--> $DIR/if-let-typo.rs:6:8
|
--> $DIR/if-let-typo.rs:6:8
|
||||||
|
|
|
|
||||||
LL | if Some(foo) = bar {}
|
LL | if Some(foo) = bar {}
|
||||||
| ^^^^^^^^^^^^^^^ expected `bool`, found `()`
|
| ^^^^^^^^^^^^^^^ expected `bool`, found `()`
|
||||||
|
|
|
||||||
|
help: consider adding `let`
|
||||||
|
|
|
||||||
|
LL | if let Some(foo) = bar {}
|
||||||
|
| +++
|
||||||
|
|
||||||
error[E0308]: mismatched types
|
error[E0308]: mismatched types
|
||||||
--> $DIR/if-let-typo.rs:7:8
|
--> $DIR/if-let-typo.rs:7:8
|
||||||
|
@ -51,6 +61,11 @@ error[E0308]: mismatched types
|
||||||
|
|
|
|
||||||
LL | if Some(3) = foo {}
|
LL | if Some(3) = foo {}
|
||||||
| ^^^^^^^^^^^^^ expected `bool`, found `()`
|
| ^^^^^^^^^^^^^ expected `bool`, found `()`
|
||||||
|
|
|
||||||
|
help: consider adding `let`
|
||||||
|
|
|
||||||
|
LL | if let Some(3) = foo {}
|
||||||
|
| +++
|
||||||
|
|
||||||
error: aborting due to 7 previous errors
|
error: aborting due to 7 previous errors
|
||||||
|
|
||||||
|
|
|
@ -23,6 +23,11 @@ error[E0308]: mismatched types
|
||||||
|
|
|
|
||||||
LL | Some(reference) = cache.data.get(key) {
|
LL | Some(reference) = cache.data.get(key) {
|
||||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `bool`, found `()`
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `bool`, found `()`
|
||||||
|
|
|
||||||
|
help: consider adding `let`
|
||||||
|
|
|
||||||
|
LL | let Some(reference) = cache.data.get(key) {
|
||||||
|
| +++
|
||||||
|
|
||||||
error: aborting due to 3 previous errors
|
error: aborting due to 3 previous errors
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue