From eac74104054ed1390f8c92be1d4163af128d38de Mon Sep 17 00:00:00 2001 From: "Zack M. Davis" Date: Tue, 11 Jul 2017 20:03:58 -0700 Subject: [PATCH 1/2] suggest one-argument enum variant to fix type mismatch when applicable Most notably, this will suggest `Some(x)` when the expected type was an Option but we got an x: T. Resolves #42764. --- src/librustc_typeck/check/demand.rs | 27 +++++++++++++++++++++ src/test/ui/did_you_mean/issue-42764.rs | 22 +++++++++++++++++ src/test/ui/did_you_mean/issue-42764.stderr | 17 +++++++++++++ 3 files changed, 66 insertions(+) create mode 100644 src/test/ui/did_you_mean/issue-42764.rs create mode 100644 src/test/ui/did_you_mean/issue-42764.stderr diff --git a/src/librustc_typeck/check/demand.rs b/src/librustc_typeck/check/demand.rs index f54ad54187267..26d51178f4d7b 100644 --- a/src/librustc_typeck/check/demand.rs +++ b/src/librustc_typeck/check/demand.rs @@ -16,6 +16,7 @@ use rustc::traits::ObligationCause; 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,32 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { 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) { + compatible_variants.push(variant.name); + } + } + } + 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); + return Some(err); + } + } + 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 0000000000000..285bd4b25665d --- /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), + None, +} + +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 0000000000000..2d168cd4d01ac --- /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(FirstSome(n)); + | ^^^^^^^^^^^^ +21 | this_function_expects_a_double_option(AlternativeSome(n)); + | ^^^^^^^^^^^^^^^^^^ + +error: aborting due to previous error + From 80c603fc6589aaf70df7c142723eef9a1d28aec5 Mon Sep 17 00:00:00 2001 From: "Zack M. Davis" Date: Sat, 15 Jul 2017 10:26:11 -0700 Subject: [PATCH 2/2] path, not name, in sole-argument variant type mismatch suggestion MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We want the suggested replacement (which IDE tooling and such might offer to automatically swap in) to, like, actually be correct: suggesting `MyVariant(x)` when the actual fix is `MyEnum::MyVariant(x)` might be better than nothing, but Rust is supposed to be the future of computing: we're better than better than nothing. As an exceptional case, we excise the prelude path, preferring to suggest `Some` or `Ok` rather than `std::prelude::v1::Some` and `std::prelude::v2::Ok`. (It's not worth the effort to future-proof against hypothetical preludes v2, v3, &c.: we trust our successors to grep—excuse me, ripgrep—for that.) Also, don't make this preëmpt the existing probe-for-return-type suggestions, despite their being looked unfavorably upon, at least in this situation (https://github.com/rust-lang/rust/issues/42764#issuecomment-311388958): Cody Schafer pointed out that that's a separate issue (https://github.com/rust-lang/rust/pull/43178#issuecomment-314953229). This is in the matter of #42764. --- src/librustc_typeck/check/demand.rs | 6 ++++-- src/test/ui/did_you_mean/issue-42764.rs | 2 +- src/test/ui/did_you_mean/issue-42764.stderr | 8 ++++---- 3 files changed, 9 insertions(+), 7 deletions(-) diff --git a/src/librustc_typeck/check/demand.rs b/src/librustc_typeck/check/demand.rs index 26d51178f4d7b..828106df7821b 100644 --- a/src/librustc_typeck/check/demand.rs +++ b/src/librustc_typeck/check/demand.rs @@ -106,7 +106,10 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { 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) { - compatible_variants.push(variant.name); + 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); } } } @@ -117,7 +120,6 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { err.span_suggestions(expr.span, "perhaps you meant to use a variant of the expected type", suggestions); - return Some(err); } } diff --git a/src/test/ui/did_you_mean/issue-42764.rs b/src/test/ui/did_you_mean/issue-42764.rs index 285bd4b25665d..ecaeb7b1161f7 100644 --- a/src/test/ui/did_you_mean/issue-42764.rs +++ b/src/test/ui/did_you_mean/issue-42764.rs @@ -11,7 +11,7 @@ enum DoubleOption { FirstSome(T), AlternativeSome(T), - None, + Nothing, } fn this_function_expects_a_double_option(d: DoubleOption) {} diff --git a/src/test/ui/did_you_mean/issue-42764.stderr b/src/test/ui/did_you_mean/issue-42764.stderr index 2d168cd4d01ac..7ba129039bc2f 100644 --- a/src/test/ui/did_you_mean/issue-42764.stderr +++ b/src/test/ui/did_you_mean/issue-42764.stderr @@ -8,10 +8,10 @@ error[E0308]: mismatched types found type `usize` help: perhaps you meant to use a variant of the expected type | -21 | this_function_expects_a_double_option(FirstSome(n)); - | ^^^^^^^^^^^^ -21 | this_function_expects_a_double_option(AlternativeSome(n)); - | ^^^^^^^^^^^^^^^^^^ +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