Skip to content

Commit cdcca2c

Browse files
author
Yiming Lei
committed
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
1 parent 4a14677 commit cdcca2c

File tree

7 files changed

+108
-3
lines changed

7 files changed

+108
-3
lines changed

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

+71-3
Original file line numberDiff line numberDiff line change
@@ -64,8 +64,12 @@ use rustc_errors::{Applicability, DiagnosticBuilder, DiagnosticStyledString, Mul
6464
use rustc_hir as hir;
6565
use rustc_hir::def::DefKind;
6666
use rustc_hir::def_id::{DefId, LocalDefId};
67+
use rustc_hir::intravisit::walk_block;
68+
use rustc_hir::intravisit::walk_expr;
69+
use rustc_hir::intravisit::Visitor;
6770
use rustc_hir::lang_items::LangItem;
68-
use rustc_hir::Node;
71+
use rustc_hir::HirId;
72+
use rustc_hir::{Expr, Node};
6973
use rustc_middle::dep_graph::DepContext;
7074
use rustc_middle::ty::print::with_no_trimmed_paths;
7175
use rustc_middle::ty::relate::{self, RelateResult, TypeRelation};
@@ -564,12 +568,56 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
564568
}
565569
}
566570

571+
fn note_enum_suggestion(
572+
&self,
573+
err: &mut Diagnostic,
574+
span: Span,
575+
body_id: HirId,
576+
arg_size: usize,
577+
) {
578+
let body_node = self.tcx.hir().get(body_id);
579+
let hir::Node::Expr(&hir::Expr{kind:hir::ExprKind::Block(body_expr, ..), ..}) = body_node else {return ()};
580+
struct FindExprVisitor<'tcx> {
581+
target_id: u32,
582+
size: usize,
583+
terr: &'tcx mut Diagnostic,
584+
}
585+
impl<'tcx> Visitor<'tcx> for FindExprVisitor<'tcx> {
586+
fn visit_expr(&mut self, expr: &'tcx Expr<'tcx>) {
587+
if expr.span.get_base_or_index() == self.target_id {
588+
let mut suggest_vec = vec![];
589+
let mut i = 0;
590+
suggest_vec.push((expr.span.shrink_to_hi(), "(".to_string()));
591+
while i < self.size {
592+
suggest_vec.push((expr.span.shrink_to_hi(), "_".to_string()));
593+
if i != self.size - 1 {
594+
suggest_vec.push((expr.span.shrink_to_hi(), ",".to_string()));
595+
}
596+
i = i + 1;
597+
}
598+
suggest_vec.push((expr.span.shrink_to_hi(), ")".to_string()));
599+
600+
self.terr.multipart_suggestion(
601+
"use parentheses to instantiate this tuple variant",
602+
suggest_vec,
603+
Applicability::MachineApplicable,
604+
);
605+
}
606+
walk_expr(self, expr);
607+
}
608+
}
609+
let mut visitor =
610+
FindExprVisitor { target_id: span.get_base_or_index(), size: arg_size, terr: err };
611+
walk_block(&mut visitor, body_expr);
612+
}
613+
567614
fn note_error_origin(
568615
&self,
569616
err: &mut Diagnostic,
570617
cause: &ObligationCause<'tcx>,
571618
exp_found: Option<ty::error::ExpectedFound<Ty<'tcx>>>,
572619
terr: TypeError<'tcx>,
620+
detect_enum_noparm: bool,
573621
) {
574622
match *cause.code() {
575623
ObligationCauseCode::Pattern { origin_expr: true, span: Some(span), root_ty } => {
@@ -584,6 +632,12 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
584632
err.span_label(span, format!("this is an iterator with items of type `{}`", substs.type_at(0)));
585633
} else {
586634
err.span_label(span, format!("this expression has type `{}`", ty));
635+
if detect_enum_noparm &&
636+
let ty::FnDef(def_id, substs) = ty.kind(){
637+
let sig = self.tcx.bound_fn_sig(*def_id).subst(self.tcx, substs);
638+
let sig = self.tcx.erase_late_bound_regions(sig);
639+
self.note_enum_suggestion(err, span, cause.body_id, sig.inputs().len());
640+
}
587641
}
588642
}
589643
if let Some(ty::error::ExpectedFound { found, .. }) = exp_found
@@ -1432,6 +1486,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
14321486
terr: TypeError<'tcx>,
14331487
swap_secondary_and_primary: bool,
14341488
prefer_label: bool,
1489+
detect_enum_noparm: bool,
14351490
) {
14361491
let span = cause.span();
14371492

@@ -1882,7 +1937,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
18821937

18831938
// It reads better to have the error origin as the final
18841939
// thing.
1885-
self.note_error_origin(diag, cause, exp_found, terr);
1940+
self.note_error_origin(diag, cause, exp_found, terr, detect_enum_noparm);
18861941

18871942
debug!(?diag);
18881943
}
@@ -2225,6 +2280,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
22252280

22262281
let span = trace.cause.span();
22272282
let failure_code = trace.cause.as_failure_code(terr);
2283+
let mut detect_enum_noparm = false;
22282284
let mut diag = match failure_code {
22292285
FailureCode::Error0038(did) => {
22302286
let violations = self.tcx.object_safety_violations(did);
@@ -2279,6 +2335,9 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
22792335
}
22802336
}
22812337
}
2338+
(ty::FnDef(_, _), ty::Adt(adt_id, _)) if adt_id.is_enum() => {
2339+
detect_enum_noparm = true;
2340+
}
22822341
_ => {}
22832342
}
22842343
}
@@ -2299,7 +2358,16 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
22992358
struct_span_err!(self.tcx.sess, span, E0644, "{}", failure_str)
23002359
}
23012360
};
2302-
self.note_type_err(&mut diag, &trace.cause, None, Some(trace.values), terr, false, false);
2361+
self.note_type_err(
2362+
&mut diag,
2363+
&trace.cause,
2364+
None,
2365+
Some(trace.values),
2366+
terr,
2367+
false,
2368+
false,
2369+
detect_enum_noparm,
2370+
);
23032371
diag
23042372
}
23052373

compiler/rustc_span/src/span_encoding.rs

+4
Original file line numberDiff line numberDiff line change
@@ -149,6 +149,10 @@ impl Span {
149149
with_span_interner(|interner| interner.spans[index as usize].ctxt)
150150
}
151151
}
152+
153+
pub fn get_base_or_index(self) -> u32 {
154+
self.base_or_index
155+
}
152156
}
153157

154158
#[derive(Default)]

compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs

+1
Original file line numberDiff line numberDiff line change
@@ -1661,6 +1661,7 @@ impl<'a, 'tcx> InferCtxtPrivExt<'a, 'tcx> for InferCtxt<'a, 'tcx> {
16611661
err,
16621662
true,
16631663
false,
1664+
false,
16641665
);
16651666
self.note_obligation_cause(&mut diag, obligation);
16661667
diag.emit();

compiler/rustc_typeck/src/check/compare_method.rs

+3
Original file line numberDiff line numberDiff line change
@@ -405,6 +405,7 @@ fn compare_predicate_entailment<'tcx>(
405405
terr,
406406
false,
407407
false,
408+
false,
408409
);
409410

410411
return Err(diag.emit());
@@ -520,6 +521,7 @@ pub fn collect_trait_impl_trait_tys<'tcx>(
520521
terr,
521522
false,
522523
false,
524+
false,
523525
);
524526
return Err(diag.emit());
525527
}
@@ -1389,6 +1391,7 @@ pub(crate) fn compare_const_impl<'tcx>(
13891391
terr,
13901392
false,
13911393
false,
1394+
false,
13921395
);
13931396
diag.emit();
13941397
}

compiler/rustc_typeck/src/check/fn_ctxt/checks.rs

+1
Original file line numberDiff line numberDiff line change
@@ -848,6 +848,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
848848
e,
849849
false,
850850
true,
851+
false,
851852
);
852853
}
853854
}

src/test/ui/type/issue-101208.rs

+10
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
enum E {
2+
One(i32, i32)
3+
}
4+
fn main() {
5+
let var = E::One;
6+
if let E::One(var1, var2) = var {
7+
//~^ ERROR 0308
8+
println!("{var1} {var2}");
9+
}
10+
}

src/test/ui/type/issue-101208.stderr

+18
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
error[E0308]: mismatched types
2+
--> $DIR/issue-101208.rs:6:12
3+
|
4+
LL | if let E::One(var1, var2) = var {
5+
| ^^^^^^^^^^^^^^^^^^ --- this expression has type `fn(i32, i32) -> E {E::One}`
6+
| |
7+
| expected fn item, found enum `E`
8+
|
9+
= note: expected fn item `fn(i32, i32) -> E {E::One}`
10+
found enum `E`
11+
help: use parentheses to instantiate this tuple variant
12+
|
13+
LL | if let E::One(var1, var2) = var(_,_) {
14+
| +
15+
16+
error: aborting due to previous error
17+
18+
For more information about this error, try `rustc --explain E0308`.

0 commit comments

Comments
 (0)