Skip to content

Commit

Permalink
suggest one-argument enum variant to fix type mismatch when applicable
Browse files Browse the repository at this point in the history
Most notably, this will suggest `Some(x)` when the expected type was
an Option<T> but we got an x: T.

Resolves #42764.
  • Loading branch information
zackmdavis committed Jul 13, 2017
1 parent f85579d commit eac7410
Show file tree
Hide file tree
Showing 3 changed files with 66 additions and 0 deletions.
27 changes: 27 additions & 0 deletions src/librustc_typeck/check/demand.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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};
Expand Down Expand Up @@ -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::<Vec<_>>();
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) {
Expand Down
22 changes: 22 additions & 0 deletions src/test/ui/did_you_mean/issue-42764.rs
Original file line number Diff line number Diff line change
@@ -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 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

enum DoubleOption<T> {
FirstSome(T),
AlternativeSome(T),
None,
}

fn this_function_expects_a_double_option<T>(d: DoubleOption<T>) {}

fn main() {
let n: usize = 42;
this_function_expects_a_double_option(n);
}
17 changes: 17 additions & 0 deletions src/test/ui/did_you_mean/issue-42764.stderr
Original file line number Diff line number Diff line change
@@ -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

0 comments on commit eac7410

Please sign in to comment.