From cdcca2c127bcee71383090bf17efe87f0cf32272 Mon Sep 17 00:00:00 2001 From: Yiming Lei Date: Tue, 20 Sep 2022 10:22:00 -0700 Subject: [PATCH] emit diagnostic suggestion for error when if let used with enum variant without being initialized compare the span base id to get the correct expression and add suggestion to it modified: compiler/rustc_infer/src/infer/error_reporting/mod.rs modified: compiler/rustc_span/src/span_encoding.rs modified: compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs modified: compiler/rustc_typeck/src/check/compare_method.rs modified: compiler/rustc_typeck/src/check/fn_ctxt/checks.rs new file: src/test/ui/type/issue-101208.rs new file: src/test/ui/type/issue-101208.stderr --- .../src/infer/error_reporting/mod.rs | 74 ++++++++++++++++++- compiler/rustc_span/src/span_encoding.rs | 4 + .../src/traits/error_reporting/mod.rs | 1 + .../rustc_typeck/src/check/compare_method.rs | 3 + .../rustc_typeck/src/check/fn_ctxt/checks.rs | 1 + src/test/ui/type/issue-101208.rs | 10 +++ src/test/ui/type/issue-101208.stderr | 18 +++++ 7 files changed, 108 insertions(+), 3 deletions(-) create mode 100644 src/test/ui/type/issue-101208.rs create mode 100644 src/test/ui/type/issue-101208.stderr diff --git a/compiler/rustc_infer/src/infer/error_reporting/mod.rs b/compiler/rustc_infer/src/infer/error_reporting/mod.rs index 82099d9e3f345..b2f14247bc976 100644 --- a/compiler/rustc_infer/src/infer/error_reporting/mod.rs +++ b/compiler/rustc_infer/src/infer/error_reporting/mod.rs @@ -64,8 +64,12 @@ use rustc_errors::{Applicability, DiagnosticBuilder, DiagnosticStyledString, Mul use rustc_hir as hir; use rustc_hir::def::DefKind; use rustc_hir::def_id::{DefId, LocalDefId}; +use rustc_hir::intravisit::walk_block; +use rustc_hir::intravisit::walk_expr; +use rustc_hir::intravisit::Visitor; use rustc_hir::lang_items::LangItem; -use rustc_hir::Node; +use rustc_hir::HirId; +use rustc_hir::{Expr, Node}; use rustc_middle::dep_graph::DepContext; use rustc_middle::ty::print::with_no_trimmed_paths; use rustc_middle::ty::relate::{self, RelateResult, TypeRelation}; @@ -564,12 +568,56 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { } } + fn note_enum_suggestion( + &self, + err: &mut Diagnostic, + span: Span, + body_id: HirId, + arg_size: usize, + ) { + let body_node = self.tcx.hir().get(body_id); + let hir::Node::Expr(&hir::Expr{kind:hir::ExprKind::Block(body_expr, ..), ..}) = body_node else {return ()}; + struct FindExprVisitor<'tcx> { + target_id: u32, + size: usize, + terr: &'tcx mut Diagnostic, + } + impl<'tcx> Visitor<'tcx> for FindExprVisitor<'tcx> { + fn visit_expr(&mut self, expr: &'tcx Expr<'tcx>) { + if expr.span.get_base_or_index() == self.target_id { + let mut suggest_vec = vec![]; + let mut i = 0; + suggest_vec.push((expr.span.shrink_to_hi(), "(".to_string())); + while i < self.size { + suggest_vec.push((expr.span.shrink_to_hi(), "_".to_string())); + if i != self.size - 1 { + suggest_vec.push((expr.span.shrink_to_hi(), ",".to_string())); + } + i = i + 1; + } + suggest_vec.push((expr.span.shrink_to_hi(), ")".to_string())); + + self.terr.multipart_suggestion( + "use parentheses to instantiate this tuple variant", + suggest_vec, + Applicability::MachineApplicable, + ); + } + walk_expr(self, expr); + } + } + let mut visitor = + FindExprVisitor { target_id: span.get_base_or_index(), size: arg_size, terr: err }; + walk_block(&mut visitor, body_expr); + } + fn note_error_origin( &self, err: &mut Diagnostic, cause: &ObligationCause<'tcx>, exp_found: Option>>, terr: TypeError<'tcx>, + detect_enum_noparm: bool, ) { match *cause.code() { ObligationCauseCode::Pattern { origin_expr: true, span: Some(span), root_ty } => { @@ -584,6 +632,12 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { err.span_label(span, format!("this is an iterator with items of type `{}`", substs.type_at(0))); } else { err.span_label(span, format!("this expression has type `{}`", ty)); + if detect_enum_noparm && + let ty::FnDef(def_id, substs) = ty.kind(){ + let sig = self.tcx.bound_fn_sig(*def_id).subst(self.tcx, substs); + let sig = self.tcx.erase_late_bound_regions(sig); + self.note_enum_suggestion(err, span, cause.body_id, sig.inputs().len()); + } } } if let Some(ty::error::ExpectedFound { found, .. }) = exp_found @@ -1432,6 +1486,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { terr: TypeError<'tcx>, swap_secondary_and_primary: bool, prefer_label: bool, + detect_enum_noparm: bool, ) { let span = cause.span(); @@ -1882,7 +1937,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { // It reads better to have the error origin as the final // thing. - self.note_error_origin(diag, cause, exp_found, terr); + self.note_error_origin(diag, cause, exp_found, terr, detect_enum_noparm); debug!(?diag); } @@ -2225,6 +2280,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { let span = trace.cause.span(); let failure_code = trace.cause.as_failure_code(terr); + let mut detect_enum_noparm = false; let mut diag = match failure_code { FailureCode::Error0038(did) => { let violations = self.tcx.object_safety_violations(did); @@ -2279,6 +2335,9 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { } } } + (ty::FnDef(_, _), ty::Adt(adt_id, _)) if adt_id.is_enum() => { + detect_enum_noparm = true; + } _ => {} } } @@ -2299,7 +2358,16 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { struct_span_err!(self.tcx.sess, span, E0644, "{}", failure_str) } }; - self.note_type_err(&mut diag, &trace.cause, None, Some(trace.values), terr, false, false); + self.note_type_err( + &mut diag, + &trace.cause, + None, + Some(trace.values), + terr, + false, + false, + detect_enum_noparm, + ); diag } diff --git a/compiler/rustc_span/src/span_encoding.rs b/compiler/rustc_span/src/span_encoding.rs index b3de674159409..00ee9fb22b1f2 100644 --- a/compiler/rustc_span/src/span_encoding.rs +++ b/compiler/rustc_span/src/span_encoding.rs @@ -149,6 +149,10 @@ impl Span { with_span_interner(|interner| interner.spans[index as usize].ctxt) } } + + pub fn get_base_or_index(self) -> u32 { + self.base_or_index + } } #[derive(Default)] diff --git a/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs b/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs index d62b399c1b562..8475dba06d8a1 100644 --- a/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs @@ -1661,6 +1661,7 @@ impl<'a, 'tcx> InferCtxtPrivExt<'a, 'tcx> for InferCtxt<'a, 'tcx> { err, true, false, + false, ); self.note_obligation_cause(&mut diag, obligation); diag.emit(); diff --git a/compiler/rustc_typeck/src/check/compare_method.rs b/compiler/rustc_typeck/src/check/compare_method.rs index 7c0b815e42a49..ce355020ba026 100644 --- a/compiler/rustc_typeck/src/check/compare_method.rs +++ b/compiler/rustc_typeck/src/check/compare_method.rs @@ -405,6 +405,7 @@ fn compare_predicate_entailment<'tcx>( terr, false, false, + false, ); return Err(diag.emit()); @@ -520,6 +521,7 @@ pub fn collect_trait_impl_trait_tys<'tcx>( terr, false, false, + false, ); return Err(diag.emit()); } @@ -1389,6 +1391,7 @@ pub(crate) fn compare_const_impl<'tcx>( terr, false, false, + false, ); diag.emit(); } diff --git a/compiler/rustc_typeck/src/check/fn_ctxt/checks.rs b/compiler/rustc_typeck/src/check/fn_ctxt/checks.rs index 311fcaadaa98b..865a3ed5f6d80 100644 --- a/compiler/rustc_typeck/src/check/fn_ctxt/checks.rs +++ b/compiler/rustc_typeck/src/check/fn_ctxt/checks.rs @@ -848,6 +848,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { e, false, true, + false, ); } } diff --git a/src/test/ui/type/issue-101208.rs b/src/test/ui/type/issue-101208.rs new file mode 100644 index 0000000000000..49654a9e32734 --- /dev/null +++ b/src/test/ui/type/issue-101208.rs @@ -0,0 +1,10 @@ +enum E { + One(i32, i32) +} +fn main() { + let var = E::One; + if let E::One(var1, var2) = var { + //~^ ERROR 0308 + println!("{var1} {var2}"); + } +} diff --git a/src/test/ui/type/issue-101208.stderr b/src/test/ui/type/issue-101208.stderr new file mode 100644 index 0000000000000..ae6065df2f29e --- /dev/null +++ b/src/test/ui/type/issue-101208.stderr @@ -0,0 +1,18 @@ +error[E0308]: mismatched types + --> $DIR/issue-101208.rs:6:12 + | +LL | if let E::One(var1, var2) = var { + | ^^^^^^^^^^^^^^^^^^ --- this expression has type `fn(i32, i32) -> E {E::One}` + | | + | expected fn item, found enum `E` + | + = note: expected fn item `fn(i32, i32) -> E {E::One}` + found enum `E` +help: use parentheses to instantiate this tuple variant + | +LL | if let E::One(var1, var2) = var(_,_) { + | + + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0308`.