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`.