Skip to content

Commit

Permalink
emit diagnostic suggestion for error when if let used with enum varia…
Browse files Browse the repository at this point in the history
…nt 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
  • Loading branch information
Yiming Lei committed Sep 27, 2022
1 parent 4a14677 commit cdcca2c
Show file tree
Hide file tree
Showing 7 changed files with 108 additions and 3 deletions.
74 changes: 71 additions & 3 deletions compiler/rustc_infer/src/infer/error_reporting/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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};
Expand Down Expand Up @@ -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<ty::error::ExpectedFound<Ty<'tcx>>>,
terr: TypeError<'tcx>,
detect_enum_noparm: bool,
) {
match *cause.code() {
ObligationCauseCode::Pattern { origin_expr: true, span: Some(span), root_ty } => {
Expand All @@ -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
Expand Down Expand Up @@ -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();

Expand Down Expand Up @@ -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);
}
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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;
}
_ => {}
}
}
Expand All @@ -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
}

Expand Down
4 changes: 4 additions & 0 deletions compiler/rustc_span/src/span_encoding.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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)]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand Down
3 changes: 3 additions & 0 deletions compiler/rustc_typeck/src/check/compare_method.rs
Original file line number Diff line number Diff line change
Expand Up @@ -405,6 +405,7 @@ fn compare_predicate_entailment<'tcx>(
terr,
false,
false,
false,
);

return Err(diag.emit());
Expand Down Expand Up @@ -520,6 +521,7 @@ pub fn collect_trait_impl_trait_tys<'tcx>(
terr,
false,
false,
false,
);
return Err(diag.emit());
}
Expand Down Expand Up @@ -1389,6 +1391,7 @@ pub(crate) fn compare_const_impl<'tcx>(
terr,
false,
false,
false,
);
diag.emit();
}
Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_typeck/src/check/fn_ctxt/checks.rs
Original file line number Diff line number Diff line change
Expand Up @@ -848,6 +848,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
e,
false,
true,
false,
);
}
}
Expand Down
10 changes: 10 additions & 0 deletions src/test/ui/type/issue-101208.rs
Original file line number Diff line number Diff line change
@@ -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}");
}
}
18 changes: 18 additions & 0 deletions src/test/ui/type/issue-101208.stderr
Original file line number Diff line number Diff line change
@@ -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`.

0 comments on commit cdcca2c

Please sign in to comment.