Skip to content

Commit

Permalink
Provide existing ref suggestions for more E0308 errors
Browse files Browse the repository at this point in the history
  • Loading branch information
estebank committed Jun 28, 2018
1 parent 5d95db3 commit 54a04b3
Show file tree
Hide file tree
Showing 4 changed files with 59 additions and 38 deletions.
11 changes: 8 additions & 3 deletions src/librustc_typeck/check/coercion.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1203,9 +1203,14 @@ impl<'gcx, 'tcx, 'exprs, E> CoerceMany<'gcx, 'tcx, 'exprs, E>
"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);
Expand Down
56 changes: 23 additions & 33 deletions src/librustc_typeck/check/demand.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -140,25 +138,12 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
}
}

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,
Expand Down Expand Up @@ -261,19 +246,24 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
/// 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()));
Expand All @@ -283,8 +273,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
(&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)));
Expand All @@ -311,9 +300,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
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,
Expand Down Expand Up @@ -342,11 +329,14 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
// 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));
}
}
Expand All @@ -355,9 +345,9 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
_ => {
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)));
Expand All @@ -372,7 +362,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
None
}

fn check_for_cast(&self,
pub fn check_for_cast(&self,
err: &mut DiagnosticBuilder<'tcx>,
expr: &hir::Expr,
checked_ty: Ty<'tcx>,
Expand Down
25 changes: 24 additions & 1 deletion src/librustc_typeck/check/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,7 @@ use rustc_data_structures::sync::Lrc;
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;
Expand Down Expand Up @@ -4539,10 +4540,32 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
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:
Expand Down
5 changes: 4 additions & 1 deletion src/test/ui/suggestions/str-array-assignment.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -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`
Expand Down

0 comments on commit 54a04b3

Please sign in to comment.