diff --git a/src/librustc_typeck/check/demand.rs b/src/librustc_typeck/check/demand.rs index f54ad54187267fd6d96ee6f48a22c9de61ad573a..828106df7821b2e1b435c295b4b3a40c471d36c4 100644 --- a/src/librustc_typeck/check/demand.rs +++ b/src/librustc_typeck/check/demand.rs @@ -16,6 +16,7 @@ use syntax::ast; use syntax_pos::{self, Span}; use rustc::hir; +use rustc::hir::print; use rustc::hir::def::Def; use rustc::ty::{self, Ty, AssociatedItem}; use errors::{DiagnosticBuilder, CodeMapper}; @@ -94,6 +95,34 @@ pub fn demand_coerce_diag(&self, let cause = self.misc(expr.span); 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 let ty::TyAdt(expected_adt, substs) = expected.sty { + let mut compatible_variants = vec![]; + for variant in &expected_adt.variants { + if variant.fields.len() == 1 { + 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 mut variant_path = self.tcx.item_path_str(variant.did); + variant_path = variant_path.trim_left_matches("std::prelude::v1::") + .to_string(); + compatible_variants.push(variant_path); + } + } + } + if !compatible_variants.is_empty() { + let expr_text = print::to_string(print::NO_ANN, |s| s.print_expr(expr)); + let suggestions = compatible_variants.iter() + .map(|v| format!("{}({})", v, expr_text)).collect::>(); + err.span_suggestions(expr.span, + "perhaps you meant to use a variant of the expected type", + suggestions); + } + } + if let Some(suggestion) = self.check_ref(expr, checked_ty, expected) { diff --git a/src/test/ui/did_you_mean/issue-42764.rs b/src/test/ui/did_you_mean/issue-42764.rs new file mode 100644 index 0000000000000000000000000000000000000000..ecaeb7b1161f7fc332a4a61011033a260606b3c4 --- /dev/null +++ b/src/test/ui/did_you_mean/issue-42764.rs @@ -0,0 +1,22 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +enum DoubleOption { + FirstSome(T), + AlternativeSome(T), + Nothing, +} + +fn this_function_expects_a_double_option(d: DoubleOption) {} + +fn main() { + let n: usize = 42; + this_function_expects_a_double_option(n); +} diff --git a/src/test/ui/did_you_mean/issue-42764.stderr b/src/test/ui/did_you_mean/issue-42764.stderr new file mode 100644 index 0000000000000000000000000000000000000000..7ba129039bc2f028795fc0178edaee4133b9e780 --- /dev/null +++ b/src/test/ui/did_you_mean/issue-42764.stderr @@ -0,0 +1,17 @@ +error[E0308]: mismatched types + --> $DIR/issue-42764.rs:21:43 + | +21 | this_function_expects_a_double_option(n); + | ^ expected enum `DoubleOption`, found usize + | + = note: expected type `DoubleOption<_>` + found type `usize` +help: perhaps you meant to use a variant of the expected type + | +21 | this_function_expects_a_double_option(DoubleOption::FirstSome(n)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ +21 | this_function_expects_a_double_option(DoubleOption::AlternativeSome(n)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to previous error +