diff --git a/src/librustc_typeck/check/coercion.rs b/src/librustc_typeck/check/coercion.rs index 76219c6971b..e276dcff060 100644 --- a/src/librustc_typeck/check/coercion.rs +++ b/src/librustc_typeck/check/coercion.rs @@ -1203,9 +1203,14 @@ fn coerce_inner<'a>(&mut self, "supposed to be part of a block tail expression, but the \ expression is empty"); }); - fcx.suggest_mismatched_types_on_tail(&mut db, expr, - expected, found, - cause.span, blk_id); + fcx.suggest_mismatched_types_on_tail( + &mut db, + expr, + expected, + found, + cause.span, + blk_id, + ); } _ => { db = fcx.report_mismatched_types(cause, expected, found, err); diff --git a/src/librustc_typeck/check/demand.rs b/src/librustc_typeck/check/demand.rs index 8582eb3fbd8..08d8dd2e498 100644 --- a/src/librustc_typeck/check/demand.rs +++ b/src/librustc_typeck/check/demand.rs @@ -8,8 +8,6 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -use std::iter; - use check::FnCtxt; use rustc::infer::InferOk; use rustc::traits::ObligationCause; @@ -140,25 +138,12 @@ pub fn demand_coerce_diag(&self, } } - if let Some((sp, msg, suggestion)) = self.check_ref(expr, checked_ty, expected) { - err.span_suggestion(sp, msg, suggestion); - } else if !self.check_for_cast(&mut err, expr, expr_ty, expected) { - let methods = self.get_conversion_methods(expr.span, expected, checked_ty); - if let Ok(expr_text) = self.tcx.sess.codemap().span_to_snippet(expr.span) { - let suggestions = iter::repeat(expr_text).zip(methods.iter()) - .map(|(receiver, method)| format!("{}.{}()", receiver, method.ident)) - .collect::>(); - if !suggestions.is_empty() { - err.span_suggestions(expr.span, - "try using a conversion method", - suggestions); - } - } - } + self.suggest_ref_or_into(&mut err, expr, expected, expr_ty); + (expected, Some(err)) } - fn get_conversion_methods(&self, span: Span, expected: Ty<'tcx>, checked_ty: Ty<'tcx>) + pub fn get_conversion_methods(&self, span: Span, expected: Ty<'tcx>, checked_ty: Ty<'tcx>) -> Vec { let mut methods = self.probe_for_return_type(span, probe::Mode::MethodCall, @@ -261,19 +246,24 @@ fn can_use_as_ref(&self, expr: &hir::Expr) -> Option<(Span, &'static str, String /// In addition of this check, it also checks between references mutability state. If the /// expected is mutable but the provided isn't, maybe we could just say "Hey, try with /// `&mut`!". - fn check_ref(&self, + pub fn check_ref(&self, expr: &hir::Expr, checked_ty: Ty<'tcx>, expected: Ty<'tcx>) -> Option<(Span, &'static str, String)> { - let sp = expr.span; + let cm = self.sess().codemap(); + // Use the callsite's span if this is a macro call. #41858 + let sp = cm.call_span_if_macro(expr.span); + if !cm.span_to_filename(sp).is_real() { + return None; + } + match (&expected.sty, &checked_ty.sty) { (&ty::TyRef(_, exp, _), &ty::TyRef(_, check, _)) => match (&exp.sty, &check.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) { + if let Ok(src) = cm.span_to_snippet(sp) { return Some((sp, "consider removing the leading `b`", src[1..].to_string())); @@ -283,8 +273,7 @@ fn check_ref(&self, (&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) { + if let Ok(src) = cm.span_to_snippet(sp) { return Some((sp, "consider adding a leading `b`", format!("b{}", src))); @@ -311,9 +300,7 @@ fn check_ref(&self, checked_ty), }; if self.can_coerce(ref_ty, expected) { - // Use the callsite's span if this is a macro call. #41858 - let sp = self.sess().codemap().call_span_if_macro(expr.span); - if let Ok(src) = self.tcx.sess.codemap().span_to_snippet(sp) { + if let Ok(src) = cm.span_to_snippet(sp) { let sugg_expr = match expr.node { // parenthesize if needed (Issue #46756) hir::ExprCast(_, _) | hir::ExprBinary(_, _, _) => format!("({})", src), _ => src, @@ -342,11 +329,14 @@ fn check_ref(&self, // 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, &expected).is_ok() && - expr.span.ctxt().outer().expn_info().is_none() { + sp.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) { + if !cm.span_to_filename(expr.span).is_real() { + return None; + } + if let Ok(code) = cm.span_to_snippet(expr.span) { return Some((sp, "consider removing the borrow", code)); } } @@ -355,9 +345,9 @@ fn check_ref(&self, _ => { if !self.infcx.type_moves_by_default(self.param_env, checked, - 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) { + sp) { + let sp = cm.call_span_if_macro(sp); + if let Ok(code) = cm.span_to_snippet(sp) { return Some((sp, "consider dereferencing the borrow", format!("*{}", code))); @@ -372,7 +362,7 @@ fn check_ref(&self, None } - fn check_for_cast(&self, + pub fn check_for_cast(&self, err: &mut DiagnosticBuilder<'tcx>, expr: &hir::Expr, checked_ty: Ty<'tcx>, diff --git a/src/librustc_typeck/check/mod.rs b/src/librustc_typeck/check/mod.rs index f8c4bfc9ad7..0185d005186 100644 --- a/src/librustc_typeck/check/mod.rs +++ b/src/librustc_typeck/check/mod.rs @@ -116,6 +116,7 @@ use std::collections::hash_map::Entry; use std::cmp; use std::fmt::Display; +use std::iter; use std::mem::replace; use std::ops::{self, Deref}; use rustc_target::spec::abi::Abi; @@ -4539,10 +4540,32 @@ pub fn suggest_mismatched_types_on_tail(&self, cause_span: Span, blk_id: ast::NodeId) { self.suggest_missing_semicolon(err, expression, expected, cause_span); - if let Some((fn_decl, can_suggest)) = self.get_fn_decl(blk_id) { self.suggest_missing_return_type(err, &fn_decl, expected, found, can_suggest); } + self.suggest_ref_or_into(err, expression, expected, found); + } + + pub fn suggest_ref_or_into( + &self, + err: &mut DiagnosticBuilder<'tcx>, + expr: &hir::Expr, + expected: Ty<'tcx>, + found: Ty<'tcx>, + ) { + if let Some((sp, msg, suggestion)) = self.check_ref(expr, found, expected) { + err.span_suggestion(sp, msg, suggestion); + } else if !self.check_for_cast(err, expr, found, expected) { + let methods = self.get_conversion_methods(expr.span, expected, found); + if let Ok(expr_text) = self.sess().codemap().span_to_snippet(expr.span) { + let suggestions = iter::repeat(expr_text).zip(methods.iter()) + .map(|(receiver, method)| format!("{}.{}()", receiver, method.ident)) + .collect::>(); + if !suggestions.is_empty() { + err.span_suggestions(expr.span, "try using a conversion method", suggestions); + } + } + } } /// A common error is to forget to add a semicolon at the end of a block: diff --git a/src/test/ui/suggestions/str-array-assignment.stderr b/src/test/ui/suggestions/str-array-assignment.stderr index 12699d8b25f..041bae4a421 100644 --- a/src/test/ui/suggestions/str-array-assignment.stderr +++ b/src/test/ui/suggestions/str-array-assignment.stderr @@ -14,7 +14,10 @@ LL | fn main() { | - expected `()` because of default return type ... LL | let u: &str = if true { s[..2] } else { s }; - | ^^^^^^ expected &str, found str + | ^^^^^^ + | | + | expected &str, found str + | help: consider borrowing here: `&s[..2]` | = note: expected type `&str` found type `str`