1
Fork 0

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:
bors 2022-11-10 05:19:10 +00:00
commit 11fa0850f0
11 changed files with 184 additions and 4 deletions

View file

@ -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> {
pub fn new_fully_qualified(
span: Span,

View file

@ -1,3 +1,4 @@
// ignore-tidy-filelength
//! Error Reporting Code for the inference engine
//!
//! Because of the way inference, and in particular region inference,
@ -58,12 +59,15 @@ use crate::traits::{
StatementAsExpression,
};
use crate::errors::SuggAddLetForLetChains;
use hir::intravisit::{walk_expr, walk_stmt};
use rustc_data_structures::fx::{FxIndexMap, FxIndexSet};
use rustc_errors::{pluralize, struct_span_err, Diagnostic, ErrorGuaranteed, IntoDiagnosticArg};
use rustc_errors::{Applicability, DiagnosticBuilder, DiagnosticStyledString, MultiSpan};
use rustc_hir as hir;
use rustc_hir::def::DefKind;
use rustc_hir::def_id::{DefId, LocalDefId};
use rustc_hir::intravisit::Visitor;
use rustc_hir::lang_items::LangItem;
use rustc_hir::Node;
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
}
/// 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(
&self,
err: &mut Diagnostic,