Provide existing ref suggestions for more E0308 errors

This commit is contained in:
Esteban Küber 2018-06-26 11:39:37 -07:00
parent 5d95db34a4
commit 54a04b3b03
4 changed files with 59 additions and 38 deletions

View File

@ -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);

View File

@ -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::<Vec<_>>();
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<AssociatedItem> {
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>,

View File

@ -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::<Vec<_>>();
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:

View File

@ -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`