Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Provide existing ref suggestions for more E0308 errors #51822

Merged
merged 1 commit into from
Jun 29, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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() {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

since we switched to call_site_if_macro here, do we know that is_none() will always be true?

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