Skip to content

Commit eac7410

Browse files
committed
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<T> but we got an x: T. Resolves rust-lang#42764.
1 parent f85579d commit eac7410

File tree

3 files changed

+66
-0
lines changed

3 files changed

+66
-0
lines changed

src/librustc_typeck/check/demand.rs

+27
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ use rustc::traits::ObligationCause;
1616
use syntax::ast;
1717
use syntax_pos::{self, Span};
1818
use rustc::hir;
19+
use rustc::hir::print;
1920
use rustc::hir::def::Def;
2021
use rustc::ty::{self, Ty, AssociatedItem};
2122
use errors::{DiagnosticBuilder, CodeMapper};
@@ -94,6 +95,32 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
9495
let cause = self.misc(expr.span);
9596
let expr_ty = self.resolve_type_vars_with_obligations(checked_ty);
9697
let mut err = self.report_mismatched_types(&cause, expected, expr_ty, e);
98+
99+
// If the expected type is an enum with any variants whose sole
100+
// field is of the found type, suggest such variants. See Issue
101+
// #42764.
102+
if let ty::TyAdt(expected_adt, substs) = expected.sty {
103+
let mut compatible_variants = vec![];
104+
for variant in &expected_adt.variants {
105+
if variant.fields.len() == 1 {
106+
let sole_field = &variant.fields[0];
107+
let sole_field_ty = sole_field.ty(self.tcx, substs);
108+
if self.can_coerce(expr_ty, sole_field_ty) {
109+
compatible_variants.push(variant.name);
110+
}
111+
}
112+
}
113+
if !compatible_variants.is_empty() {
114+
let expr_text = print::to_string(print::NO_ANN, |s| s.print_expr(expr));
115+
let suggestions = compatible_variants.iter()
116+
.map(|v| format!("{}({})", v, expr_text)).collect::<Vec<_>>();
117+
err.span_suggestions(expr.span,
118+
"perhaps you meant to use a variant of the expected type",
119+
suggestions);
120+
return Some(err);
121+
}
122+
}
123+
97124
if let Some(suggestion) = self.check_ref(expr,
98125
checked_ty,
99126
expected) {
+22
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
enum DoubleOption<T> {
12+
FirstSome(T),
13+
AlternativeSome(T),
14+
None,
15+
}
16+
17+
fn this_function_expects_a_double_option<T>(d: DoubleOption<T>) {}
18+
19+
fn main() {
20+
let n: usize = 42;
21+
this_function_expects_a_double_option(n);
22+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
error[E0308]: mismatched types
2+
--> $DIR/issue-42764.rs:21:43
3+
|
4+
21 | this_function_expects_a_double_option(n);
5+
| ^ expected enum `DoubleOption`, found usize
6+
|
7+
= note: expected type `DoubleOption<_>`
8+
found type `usize`
9+
help: perhaps you meant to use a variant of the expected type
10+
|
11+
21 | this_function_expects_a_double_option(FirstSome(n));
12+
| ^^^^^^^^^^^^
13+
21 | this_function_expects_a_double_option(AlternativeSome(n));
14+
| ^^^^^^^^^^^^^^^^^^
15+
16+
error: aborting due to previous error
17+

0 commit comments

Comments
 (0)