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};
Ok(InferOk { obligations, value: () }) => {
self.register_predicates(obligations);
None
- },
+ }
Err(e) => {
Some(self.report_mismatched_types(cause, expected, actual, e))
}
}
}
+ pub fn demand_coerce(&self, expr: &hir::Expr, checked_ty: Ty<'tcx>, expected: Ty<'tcx>) {
+ if let Some(mut err) = self.demand_coerce_diag(expr, checked_ty, expected) {
+ err.emit();
+ }
+ }
+
// Checks that the type of `expr` can be coerced to `expected`.
//
- // NB: This code relies on `self.diverges` to be accurate. In
+ // NB: This code relies on `self.diverges` to be accurate. In
// particular, assignments to `!` will be permitted if the
// diverges flag is currently "always".
- pub fn demand_coerce(&self,
- expr: &hir::Expr,
- checked_ty: Ty<'tcx>,
- expected: Ty<'tcx>) {
+ pub fn demand_coerce_diag(&self,
+ expr: &hir::Expr,
+ checked_ty: Ty<'tcx>,
+ expected: Ty<'tcx>) -> Option<DiagnosticBuilder<'tcx>> {
let expected = self.resolve_type_vars_with_obligations(expected);
if let Err(e) = self.try_coerce(expr, checked_ty, self.diverges.get(), expected) {
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::<Vec<_>>();
+ err.span_suggestions(expr.span,
+ "try using a variant of the expected type",
+ suggestions);
+ }
+ }
+
if let Some(suggestion) = self.check_ref(expr,
checked_ty,
expected) {
self.get_best_match(&suggestions).join("\n")));
}
}
- err.emit();
+ return Some(err);
}
+ None
}
fn format_method_suggestion(&self, method: &AssociatedItem) -> String {
expected: Ty<'tcx>)
-> Option<String> {
match (&expected.sty, &checked_ty.sty) {
- (&ty::TyRef(_, _), &ty::TyRef(_, _)) => None,
+ (&ty::TyRef(_, exp), &ty::TyRef(_, check)) => match (&exp.ty.sty, &check.ty.sty) {
+ (&ty::TyStr, &ty::TyArray(arr, _)) |
+ (&ty::TyStr, &ty::TySlice(arr)) if arr == self.tcx.types.u8 => {
+ if let hir::ExprLit(_) = expr.node {
+ let sp = self.sess().codemap().call_span_if_macro(expr.span);
+ if let Ok(src) = self.tcx.sess.codemap().span_to_snippet(sp) {
+ return Some(format!("try `{}`", &src[1..]));
+ }
+ }
+ None
+ },
+ (&ty::TyArray(arr, _), &ty::TyStr) |
+ (&ty::TySlice(arr), &ty::TyStr) if arr == self.tcx.types.u8 => {
+ if let hir::ExprLit(_) = expr.node {
+ let sp = self.sess().codemap().call_span_if_macro(expr.span);
+ if let Ok(src) = self.tcx.sess.codemap().span_to_snippet(sp) {
+ return Some(format!("try `b{}`", src));
+ }
+ }
+ None
+ }
+ _ => None,
+ },
(&ty::TyRef(_, mutability), _) => {
// Check if it can work when put into a ref. For example:
//
}
None
}
+ (_, &ty::TyRef(_, checked)) => {
+ // We have `&T`, check if what was expected was `T`. If so,
+ // we may want to suggest adding a `*`, or removing
+ // a `&`.
+ //
+ // (But, also check check the `expn_info()` to see if this is
+ // a macro; if so, it's hard to extract the text and make a good
+ // suggestion, so don't bother.)
+ if self.infcx.can_sub(self.param_env, checked.ty, &expected).is_ok() &&
+ expr.span.ctxt().outer().expn_info().is_none() {
+ match expr.node {
+ // Maybe remove `&`?
+ hir::ExprAddrOf(_, ref expr) => {
+ if let Ok(code) = self.tcx.sess.codemap().span_to_snippet(expr.span) {
+ return Some(format!("try with `{}`", code));
+ }
+ }
+
+ // Maybe add `*`? Only if `T: Copy`.
+ _ => {
+ if !self.infcx.type_moves_by_default(self.param_env,
+ checked.ty,
+ expr.span) {
+ let sp = self.sess().codemap().call_span_if_macro(expr.span);
+ if let Ok(code) = self.tcx.sess.codemap().span_to_snippet(sp) {
+ return Some(format!("try with `*{}`", code));
+ }
+ }
+ },
+ }
+ }
+ None
+ }
_ => None,
}
}