Skip to content

Commit

Permalink
only issue "variant of the expected type" suggestion for enums
Browse files Browse the repository at this point in the history
Felix S. Klock II pointed out that this suggestion (introduced in
pull-request rust-lang#43178 / eac7410) was being issued for one-field-struct
expected types (in which case it is misleading and outright wrong),
even though it was only intended for one-field enum-variants (most
notably, `Some`). Particularly tender-hearted code-historians may be
inclined to show mercy towards the author of rust-lang#43178 on the grounds
that it's somewhat confusing that struct field definitions are given
in a type called `ty::VariantDef`.

Add a conditional to adhere to the original intent. (It would be
possible to generalize to structs, but not obviously net desirable.)
This adds a level of indentation, so the diff here is going to be
easier to read in ignore-whitespace mode (`-w`).

Resolves rust-lang#55250.
  • Loading branch information
zackmdavis committed Oct 21, 2018
1 parent 22cc2ae commit b0d3d3b
Show file tree
Hide file tree
Showing 3 changed files with 53 additions and 27 deletions.
53 changes: 27 additions & 26 deletions src/librustc_typeck/check/demand.rs
Original file line number Diff line number Diff line change
Expand Up @@ -111,34 +111,35 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
let expr_ty = self.resolve_type_vars_with_obligations(checked_ty);
let mut err = self.report_mismatched_types(&cause, expected, expr_ty, e);

// If the expected type is an enum with any variants whose sole
// field is of the found type, suggest such variants. See Issue
// #42764.
// If the expected type is an enum (Issue #55250) with any variants whose
// sole field is of the found type, suggest such variants. (Issue #42764)
if let ty::Adt(expected_adt, substs) = expected.sty {
let mut compatible_variants = expected_adt.variants
.iter()
.filter(|variant| variant.fields.len() == 1)
.filter_map(|variant| {
let sole_field = &variant.fields[0];
let sole_field_ty = sole_field.ty(self.tcx, substs);
if self.can_coerce(expr_ty, sole_field_ty) {
let variant_path = self.tcx.item_path_str(variant.did);
Some(variant_path.trim_left_matches("std::prelude::v1::").to_string())
} else {
None
if expected_adt.is_enum() {
let mut compatible_variants = expected_adt.variants
.iter()
.filter(|variant| variant.fields.len() == 1)
.filter_map(|variant| {
let sole_field = &variant.fields[0];
let sole_field_ty = sole_field.ty(self.tcx, substs);
if self.can_coerce(expr_ty, sole_field_ty) {
let variant_path = self.tcx.item_path_str(variant.did);
Some(variant_path.trim_left_matches("std::prelude::v1::").to_string())
} else {
None
}
}).peekable();

if compatible_variants.peek().is_some() {
let expr_text = print::to_string(print::NO_ANN, |s| s.print_expr(expr));
let suggestions = compatible_variants
.map(|v| format!("{}({})", v, expr_text)).collect::<Vec<_>>();
err.span_suggestions_with_applicability(
expr.span,
"try using a variant of the expected type",
suggestions,
Applicability::MaybeIncorrect,
);
}
}).peekable();

if compatible_variants.peek().is_some() {
let expr_text = print::to_string(print::NO_ANN, |s| s.print_expr(expr));
let suggestions = compatible_variants.map(|v|
format!("{}({})", v, expr_text)).collect::<Vec<_>>();
err.span_suggestions_with_applicability(
expr.span,
"try using a variant of the expected type",
suggestions,
Applicability::MaybeIncorrect,
);
}
}

Expand Down
16 changes: 16 additions & 0 deletions src/test/ui/did_you_mean/issue-42764.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,4 +20,20 @@ fn main() {
let n: usize = 42;
this_function_expects_a_double_option(n);
//~^ ERROR mismatched types
//~| HELP try using a variant of the expected type
}


// But don't issue the "try using a variant" help if the one-"variant" ADT is
// actually a one-field struct.

struct Payload;

struct Wrapper { payload: Payload }

struct Context { wrapper: Wrapper }

fn overton() {
let _c = Context { wrapper: Payload{} };
//~^ ERROR mismatched types
}
11 changes: 10 additions & 1 deletion src/test/ui/did_you_mean/issue-42764.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,15 @@ LL | this_function_expects_a_double_option(DoubleOption::FirstSome(n));
LL | this_function_expects_a_double_option(DoubleOption::AlternativeSome(n));
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

error: aborting due to previous error
error[E0308]: mismatched types
--> $DIR/issue-42764.rs:37:33
|
LL | let _c = Context { wrapper: Payload{} };
| ^^^^^^^^^ expected struct `Wrapper`, found struct `Payload`
|
= note: expected type `Wrapper`
found type `Payload`

error: aborting due to 2 previous errors

For more information about this error, try `rustc --explain E0308`.

0 comments on commit b0d3d3b

Please sign in to comment.