Create check_ref method to allow to check coercion with & types
This commit is contained in:
parent
b2d0ec0eb4
commit
c11fe553df
|
@ -28,7 +28,6 @@
|
|||
|
||||
#![cfg_attr(test, allow(deprecated))] // rand
|
||||
#![cfg_attr(not(stage0), deny(warnings))]
|
||||
#![cfg_attr(not(stage0), feature(safe_suggestion))]
|
||||
|
||||
#![feature(alloc)]
|
||||
#![feature(allow_internal_unstable)]
|
||||
|
|
|
@ -1231,7 +1231,6 @@ impl String {
|
|||
/// assert_eq!(a.len(), 3);
|
||||
/// ```
|
||||
#[inline]
|
||||
#[cfg_attr(not(stage0), safe_suggestion)]
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
pub fn len(&self) -> usize {
|
||||
self.vec.len()
|
||||
|
|
|
@ -549,7 +549,7 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
|
|||
{
|
||||
let expected_found = match values {
|
||||
None => None,
|
||||
Some(ref values) => match self.values_str(&values) {
|
||||
Some(values) => match self.values_str(&values) {
|
||||
Some((expected, found)) => Some((expected, found)),
|
||||
None => {
|
||||
// Derived error. Cancel the emitter.
|
||||
|
|
|
@ -19,28 +19,10 @@ use syntax_pos::{self, Span};
|
|||
use rustc::hir;
|
||||
use rustc::ty::{self, ImplOrTraitItem};
|
||||
|
||||
use hir::def_id::DefId;
|
||||
|
||||
use std::rc::Rc;
|
||||
|
||||
use super::method::probe;
|
||||
|
||||
struct MethodInfo<'tcx> {
|
||||
ast: Option<ast::Attribute>,
|
||||
id: DefId,
|
||||
item: Rc<ImplOrTraitItem<'tcx>>,
|
||||
}
|
||||
|
||||
impl<'tcx> MethodInfo<'tcx> {
|
||||
fn new(ast: Option<ast::Attribute>, id: DefId, item: Rc<ImplOrTraitItem<'tcx>>) -> MethodInfo {
|
||||
MethodInfo {
|
||||
ast: ast,
|
||||
id: id,
|
||||
item: item,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
|
||||
// Requires that the two types unify, and prints an error message if
|
||||
// they don't.
|
||||
|
@ -79,6 +61,46 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
|
|||
}
|
||||
}
|
||||
|
||||
fn check_ref(&self, expr: &hir::Expr, checked_ty: Ty<'tcx>,
|
||||
expected: Ty<'tcx>) -> Option<String> {
|
||||
match (&checked_ty.sty, &expected.sty) {
|
||||
(&ty::TyRef(_, x_mutability), &ty::TyRef(_, y_mutability)) => {
|
||||
// check if there is a mutability difference
|
||||
if x_mutability.mutbl == hir::Mutability::MutImmutable &&
|
||||
x_mutability.mutbl != y_mutability.mutbl &&
|
||||
self.can_sub_types(&x_mutability.ty, y_mutability.ty).is_ok() {
|
||||
if let Ok(src) = self.tcx.sess.codemap().span_to_snippet(expr.span) {
|
||||
return Some(format!("try with `&mut {}`", &src.replace("&", "")));
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
(_, &ty::TyRef(_, mutability)) => {
|
||||
// check if it can work when put into a ref
|
||||
let ref_ty = match mutability.mutbl {
|
||||
hir::Mutability::MutMutable => self.tcx.mk_mut_ref(
|
||||
self.tcx.mk_region(ty::ReStatic),
|
||||
checked_ty),
|
||||
hir::Mutability::MutImmutable => self.tcx.mk_imm_ref(
|
||||
self.tcx.mk_region(ty::ReStatic),
|
||||
checked_ty),
|
||||
};
|
||||
if self.try_coerce(expr, ref_ty, expected).is_ok() {
|
||||
if let Ok(src) = self.tcx.sess.codemap().span_to_snippet(expr.span) {
|
||||
return Some(format!("try with `{}{}`",
|
||||
match mutability.mutbl {
|
||||
hir::Mutability::MutMutable => "&mut ",
|
||||
hir::Mutability::MutImmutable => "&",
|
||||
},
|
||||
&src));
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
// Checks that the type of `expr` can be coerced to `expected`.
|
||||
pub fn demand_coerce(&self, expr: &hir::Expr, checked_ty: Ty<'tcx>, expected: Ty<'tcx>) {
|
||||
let expected = self.resolve_type_vars_with_obligations(expected);
|
||||
|
@ -86,34 +108,23 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
|
|||
let cause = self.misc(expr.span);
|
||||
let expr_ty = self.resolve_type_vars_with_obligations(checked_ty);
|
||||
let mode = probe::Mode::MethodCall;
|
||||
let suggestions =
|
||||
if let Ok(methods) = self.probe_return(syntax_pos::DUMMY_SP, mode, expected,
|
||||
checked_ty, ast::DUMMY_NODE_ID) {
|
||||
let suggestions = if let Some(s) = self.check_ref(expr, checked_ty, expected) {
|
||||
Some(s)
|
||||
} else if let Ok(methods) = self.probe_return(syntax_pos::DUMMY_SP,
|
||||
mode,
|
||||
expected,
|
||||
checked_ty,
|
||||
ast::DUMMY_NODE_ID) {
|
||||
let suggestions: Vec<_> =
|
||||
methods.iter()
|
||||
.filter_map(|ref x| {
|
||||
if let Some(id) = self.get_impl_id(&x.item) {
|
||||
Some(MethodInfo::new(None, id, Rc::new(x.item.clone())))
|
||||
} else {
|
||||
None
|
||||
}})
|
||||
.map(|ref x| {
|
||||
Rc::new(x.item.clone())
|
||||
})
|
||||
.collect();
|
||||
if suggestions.len() > 0 {
|
||||
let safe_suggestions: Vec<_> =
|
||||
suggestions.iter()
|
||||
.map(|ref x| MethodInfo::new(
|
||||
self.find_attr(x.id, "safe_suggestion"),
|
||||
x.id,
|
||||
x.item.clone()))
|
||||
.filter(|ref x| x.ast.is_some())
|
||||
.collect();
|
||||
Some(if safe_suggestions.len() > 0 {
|
||||
self.get_best_match(&safe_suggestions)
|
||||
} else {
|
||||
format!("no safe suggestion found, here are functions which match your \
|
||||
needs but be careful:\n - {}",
|
||||
self.get_best_match(&suggestions))
|
||||
})
|
||||
Some(format!("here are some functions which \
|
||||
might fulfill your needs:\n - {}",
|
||||
self.get_best_match(&suggestions)))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
|
@ -132,34 +143,29 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
|
|||
}
|
||||
}
|
||||
|
||||
fn get_best_match(&self, methods: &[MethodInfo<'tcx>]) -> String {
|
||||
fn get_best_match(&self, methods: &[Rc<ImplOrTraitItem<'tcx>>]) -> String {
|
||||
if methods.len() == 1 {
|
||||
return format!(" - {}", methods[0].item.name());
|
||||
return format!(" - {}", methods[0].name());
|
||||
}
|
||||
let no_argument_methods: Vec<&MethodInfo> =
|
||||
let no_argument_methods: Vec<&Rc<ImplOrTraitItem<'tcx>>> =
|
||||
methods.iter()
|
||||
.filter(|ref x| self.has_not_input_arg(&*x.item))
|
||||
.filter(|ref x| self.has_not_input_arg(&*x))
|
||||
.collect();
|
||||
if no_argument_methods.len() > 0 {
|
||||
no_argument_methods.iter()
|
||||
.map(|method| format!("{}", method.item.name()))
|
||||
.take(5)
|
||||
.map(|method| format!("{}", method.name()))
|
||||
.collect::<Vec<String>>()
|
||||
.join("\n - ")
|
||||
} else {
|
||||
methods.iter()
|
||||
.map(|method| format!("{}", method.item.name()))
|
||||
.take(5)
|
||||
.map(|method| format!("{}", method.name()))
|
||||
.collect::<Vec<String>>()
|
||||
.join("\n - ")
|
||||
}
|
||||
}
|
||||
|
||||
fn get_impl_id(&self, impl_: &ImplOrTraitItem<'tcx>) -> Option<DefId> {
|
||||
match *impl_ {
|
||||
ty::ImplOrTraitItem::MethodTraitItem(ref m) => Some((*m).def_id),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
fn has_not_input_arg(&self, method: &ImplOrTraitItem<'tcx>) -> bool {
|
||||
match *method {
|
||||
ImplOrTraitItem::MethodTraitItem(ref x) => {
|
||||
|
|
|
@ -194,7 +194,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
|
|||
// think cause spurious errors. Really though this part should
|
||||
// take place in the `self.probe` below.
|
||||
let steps = if mode == Mode::MethodCall {
|
||||
match self.create_steps(span, self_ty) {
|
||||
match self.create_steps(span, self_ty, &looking_for) {
|
||||
Some(steps) => steps,
|
||||
None => {
|
||||
return Err(MethodError::NoMatch(NoMatchData::new(Vec::new(),
|
||||
|
@ -247,7 +247,8 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
|
|||
|
||||
fn create_steps(&self,
|
||||
span: Span,
|
||||
self_ty: Ty<'tcx>)
|
||||
self_ty: Ty<'tcx>,
|
||||
looking_for: &LookingFor<'tcx>)
|
||||
-> Option<Vec<CandidateStep<'tcx>>> {
|
||||
// FIXME: we don't need to create the entire steps in one pass
|
||||
|
||||
|
@ -262,7 +263,10 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
|
|||
})
|
||||
.collect();
|
||||
|
||||
let final_ty = autoderef.unambiguous_final_ty();
|
||||
let final_ty = match looking_for {
|
||||
&LookingFor::MethodName(_) => autoderef.unambiguous_final_ty(),
|
||||
&LookingFor::ReturnType(_) => self_ty,
|
||||
};
|
||||
match final_ty.sty {
|
||||
ty::TyArray(elem_ty, _) => {
|
||||
let dereferences = steps.len() - 1;
|
||||
|
@ -628,13 +632,15 @@ impl<'a, 'gcx, 'tcx> ProbeContext<'a, 'gcx, 'tcx> {
|
|||
}
|
||||
|
||||
pub fn matches_return_type(&self, method: &ty::ImplOrTraitItem<'tcx>,
|
||||
expected: ty::Ty<'tcx>) -> bool {
|
||||
expected: ty::Ty<'tcx>) -> bool {
|
||||
match *method {
|
||||
ty::ImplOrTraitItem::MethodTraitItem(ref x) => {
|
||||
self.probe(|_| {
|
||||
let output = self.replace_late_bound_regions_with_fresh_var(
|
||||
self.span, infer::FnCall, &x.fty.sig.output());
|
||||
self.can_sub_types(output.0, expected).is_ok()
|
||||
let substs = self.fresh_substs_for_item(self.span, method.def_id());
|
||||
let output = output.0.subst(self.tcx, substs);
|
||||
self.can_sub_types(output, expected).is_ok()
|
||||
})
|
||||
}
|
||||
_ => false,
|
||||
|
|
|
@ -652,12 +652,6 @@ pub const BUILTIN_ATTRIBUTES: &'static [(&'static str, AttributeType, AttributeG
|
|||
"internal implementation detail",
|
||||
cfg_fn!(rustc_attrs))),
|
||||
|
||||
("safe_suggestion", Whitelisted, Gated(Stability::Unstable,
|
||||
"safe_suggestion",
|
||||
"the `#[safe_suggestion]` attribute \
|
||||
is an experimental feature",
|
||||
cfg_fn!(safe_suggestion))),
|
||||
|
||||
// FIXME: #14408 whitelist docs since rustdoc looks at them
|
||||
("doc", Whitelisted, Ungated),
|
||||
|
||||
|
|
|
@ -0,0 +1,41 @@
|
|||
// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
|
||||
// file at the top-level directory of this distribution and at
|
||||
// http://rust-lang.org/COPYRIGHT.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
fn test(_x: &mut String) {}
|
||||
fn test2(_x: &mut i32) {}
|
||||
|
||||
fn main() {
|
||||
let x: usize = String::new();
|
||||
//^ ERROR E0308
|
||||
//| NOTE expected type `usize`
|
||||
//| NOTE found type `std::string::String`
|
||||
//| NOTE here are some functions which might fulfill your needs:
|
||||
let x: &str = String::new();
|
||||
//^ ERROR E0308
|
||||
//| NOTE expected type `&str`
|
||||
//| NOTE found type `std::string::String`
|
||||
//| NOTE try with `&String::new()`
|
||||
let y = String::new();
|
||||
test(&y);
|
||||
//^ ERROR E0308
|
||||
//| NOTE expected type `&mut std::string::String`
|
||||
//| NOTE found type `&std::string::String`
|
||||
//| NOTE try with `&mut y`
|
||||
test2(&y);
|
||||
//^ ERROR E0308
|
||||
//| NOTE expected type `&mut i32`
|
||||
//| NOTE found type `&std::string::String`
|
||||
//| NOTE try with `&mut y`
|
||||
let f;
|
||||
f = box f;
|
||||
//^ ERROR E0308
|
||||
//| NOTE expected type `_`
|
||||
//| NOTE found type `Box<_>`
|
||||
}
|
Loading…
Reference in New Issue