Provide existing ref suggestions for more E0308 errors
This commit is contained in:
parent
5d95db34a4
commit
54a04b3b03
@ -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);
|
||||
|
@ -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>,
|
||||
|
@ -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:
|
||||
|
@ -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`
|
||||
|
Loading…
Reference in New Issue
Block a user