blob: 346449d0a51331465bde2b99b5eec3048bb74e7c [file] [log] [blame]
// Copyright 2014 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.
//! Give useful errors and suggestions to users when an item can't be
//! found or is otherwise invalid.
use CrateCtxt;
use check::{FnCtxt};
use rustc::hir::map as hir_map;
use rustc::ty::{self, Ty, ToPolyTraitRef, ToPredicate, TypeFoldable};
use middle::cstore;
use hir::def::Def;
use hir::def_id::DefId;
use middle::lang_items::FnOnceTraitLangItem;
use rustc::ty::subst::Substs;
use rustc::traits::{Obligation, SelectionContext};
use util::nodemap::{FnvHashSet};
use syntax::ast;
use errors::DiagnosticBuilder;
use syntax_pos::Span;
use rustc::hir::print as pprust;
use rustc::hir;
use rustc::hir::Expr_;
use std::cell;
use std::cmp::Ordering;
use super::{MethodError, NoMatchData, CandidateSource};
use super::probe::Mode;
impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
fn is_fn_ty(&self, ty: &Ty<'tcx>, span: Span) -> bool {
let tcx = self.tcx;
match ty.sty {
// Not all of these (e.g. unsafe fns) implement FnOnce
// so we look for these beforehand
ty::TyClosure(..) | ty::TyFnDef(..) | ty::TyFnPtr(_) => true,
// If it's not a simple function, look for things which implement FnOnce
_ => {
let fn_once = match tcx.lang_items.require(FnOnceTraitLangItem) {
Ok(fn_once) => fn_once,
Err(..) => return false
};
self.autoderef(span, ty).any(|(ty, _)| self.probe(|_| {
let fn_once_substs =
Substs::new_trait(vec![self.next_ty_var()], vec![], ty);
let trait_ref =
ty::TraitRef::new(fn_once,
tcx.mk_substs(fn_once_substs));
let poly_trait_ref = trait_ref.to_poly_trait_ref();
let obligation = Obligation::misc(span,
self.body_id,
poly_trait_ref
.to_predicate());
SelectionContext::new(self).evaluate_obligation(&obligation)
}))
}
}
}
pub fn report_method_error(&self,
span: Span,
rcvr_ty: Ty<'tcx>,
item_name: ast::Name,
rcvr_expr: Option<&hir::Expr>,
error: MethodError<'tcx>)
{
// avoid suggestions when we don't know what's going on.
if rcvr_ty.references_error() {
return
}
let report_candidates = |err: &mut DiagnosticBuilder,
mut sources: Vec<CandidateSource>| {
sources.sort();
sources.dedup();
// Dynamic limit to avoid hiding just one candidate, which is silly.
let limit = if sources.len() == 5 { 5 } else { 4 };
for (idx, source) in sources.iter().take(limit).enumerate() {
match *source {
CandidateSource::ImplSource(impl_did) => {
// Provide the best span we can. Use the item, if local to crate, else
// the impl, if local to crate (item may be defaulted), else nothing.
let item = self.impl_item(impl_did, item_name)
.or_else(|| {
self.trait_item(
self.tcx.impl_trait_ref(impl_did).unwrap().def_id,
item_name
)
}).unwrap();
let note_span = self.tcx.map.span_if_local(item.def_id()).or_else(|| {
self.tcx.map.span_if_local(impl_did)
});
let impl_ty = self.impl_self_ty(span, impl_did).ty;
let insertion = match self.tcx.impl_trait_ref(impl_did) {
None => format!(""),
Some(trait_ref) => {
format!(" of the trait `{}`",
self.tcx.item_path_str(trait_ref.def_id))
}
};
let note_str = format!("candidate #{} is defined in an impl{} \
for the type `{}`",
idx + 1,
insertion,
impl_ty);
if let Some(note_span) = note_span {
// We have a span pointing to the method. Show note with snippet.
err.span_note(note_span, &note_str);
} else {
err.note(&note_str);
}
}
CandidateSource::TraitSource(trait_did) => {
let item = self.trait_item(trait_did, item_name).unwrap();
let item_span = self.tcx.map.def_id_span(item.def_id(), span);
span_note!(err, item_span,
"candidate #{} is defined in the trait `{}`",
idx + 1,
self.tcx.item_path_str(trait_did));
}
}
}
if sources.len() > limit {
err.note(&format!("and {} others", sources.len() - limit));
}
};
match error {
MethodError::NoMatch(NoMatchData { static_candidates: static_sources,
unsatisfied_predicates,
out_of_scope_traits,
mode, .. }) => {
let tcx = self.tcx;
let mut err = self.type_error_struct(
span,
|actual| {
format!("no {} named `{}` found for type `{}` \
in the current scope",
if mode == Mode::MethodCall { "method" }
else { "associated item" },
item_name,
actual)
},
rcvr_ty);
// If the item has the name of a field, give a help note
if let (&ty::TyStruct(def, substs), Some(expr)) = (&rcvr_ty.sty, rcvr_expr) {
if let Some(field) = def.struct_variant().find_field_named(item_name) {
let expr_string = match tcx.sess.codemap().span_to_snippet(expr.span) {
Ok(expr_string) => expr_string,
_ => "s".into() // Default to a generic placeholder for the
// expression when we can't generate a string
// snippet
};
let field_ty = field.ty(tcx, substs);
if self.is_fn_ty(&field_ty, span) {
err.span_note(span,
&format!("use `({0}.{1})(...)` if you meant to call \
the function stored in the `{1}` field",
expr_string, item_name));
} else {
err.span_note(span, &format!("did you mean to write `{0}.{1}`?",
expr_string, item_name));
}
}
}
if self.is_fn_ty(&rcvr_ty, span) {
macro_rules! report_function {
($span:expr, $name:expr) => {
err.note(&format!("{} is a function, perhaps you wish to call it",
$name));
}
}
if let Some(expr) = rcvr_expr {
if let Ok (expr_string) = tcx.sess.codemap().span_to_snippet(expr.span) {
report_function!(expr.span, expr_string);
}
else if let Expr_::ExprPath(_, path) = expr.node.clone() {
if let Some(segment) = path.segments.last() {
report_function!(expr.span, segment.name);
}
}
}
}
if !static_sources.is_empty() {
err.note(
"found the following associated functions; to be used as \
methods, functions must have a `self` parameter");
report_candidates(&mut err, static_sources);
}
if !unsatisfied_predicates.is_empty() {
let bound_list = unsatisfied_predicates.iter()
.map(|p| format!("`{} : {}`",
p.self_ty(),
p))
.collect::<Vec<_>>()
.join(", ");
err.note(
&format!("the method `{}` exists but the \
following trait bounds were not satisfied: {}",
item_name,
bound_list));
}
self.suggest_traits_to_import(&mut err, span, rcvr_ty, item_name,
rcvr_expr, out_of_scope_traits);
err.emit();
}
MethodError::Ambiguity(sources) => {
let mut err = struct_span_err!(self.sess(), span, E0034,
"multiple applicable items in scope");
report_candidates(&mut err, sources);
err.emit();
}
MethodError::ClosureAmbiguity(trait_def_id) => {
let msg = format!("the `{}` method from the `{}` trait cannot be explicitly \
invoked on this closure as we have not yet inferred what \
kind of closure it is",
item_name,
self.tcx.item_path_str(trait_def_id));
let msg = if let Some(callee) = rcvr_expr {
format!("{}; use overloaded call notation instead (e.g., `{}()`)",
msg, pprust::expr_to_string(callee))
} else {
msg
};
self.sess().span_err(span, &msg);
}
MethodError::PrivateMatch(def) => {
let msg = format!("{} `{}` is private", def.kind_name(), item_name);
self.tcx.sess.span_err(span, &msg);
}
}
}
fn suggest_traits_to_import(&self,
err: &mut DiagnosticBuilder,
span: Span,
rcvr_ty: Ty<'tcx>,
item_name: ast::Name,
rcvr_expr: Option<&hir::Expr>,
valid_out_of_scope_traits: Vec<DefId>)
{
if !valid_out_of_scope_traits.is_empty() {
let mut candidates = valid_out_of_scope_traits;
candidates.sort();
candidates.dedup();
let msg = format!(
"items from traits can only be used if the trait is in scope; \
the following {traits_are} implemented but not in scope, \
perhaps add a `use` for {one_of_them}:",
traits_are = if candidates.len() == 1 {"trait is"} else {"traits are"},
one_of_them = if candidates.len() == 1 {"it"} else {"one of them"});
err.help(&msg[..]);
let limit = if candidates.len() == 5 { 5 } else { 4 };
for (i, trait_did) in candidates.iter().take(limit).enumerate() {
err.help(&format!("candidate #{}: `use {}`",
i + 1,
self.tcx.item_path_str(*trait_did)));
}
if candidates.len() > limit {
err.note(&format!("and {} others", candidates.len() - limit));
}
return
}
let type_is_local = self.type_derefs_to_local(span, rcvr_ty, rcvr_expr);
// there's no implemented traits, so lets suggest some traits to
// implement, by finding ones that have the item name, and are
// legal to implement.
let mut candidates = all_traits(self.ccx)
.filter(|info| {
// we approximate the coherence rules to only suggest
// traits that are legal to implement by requiring that
// either the type or trait is local. Multidispatch means
// this isn't perfect (that is, there are cases when
// implementing a trait would be legal but is rejected
// here).
(type_is_local || info.def_id.is_local())
&& self.trait_item(info.def_id, item_name).is_some()
})
.collect::<Vec<_>>();
if !candidates.is_empty() {
// sort from most relevant to least relevant
candidates.sort_by(|a, b| a.cmp(b).reverse());
candidates.dedup();
// FIXME #21673 this help message could be tuned to the case
// of a type parameter: suggest adding a trait bound rather
// than implementing.
let msg = format!(
"items from traits can only be used if the trait is implemented and in scope; \
the following {traits_define} an item `{name}`, \
perhaps you need to implement {one_of_them}:",
traits_define = if candidates.len() == 1 {"trait defines"} else {"traits define"},
one_of_them = if candidates.len() == 1 {"it"} else {"one of them"},
name = item_name);
err.help(&msg[..]);
for (i, trait_info) in candidates.iter().enumerate() {
err.help(&format!("candidate #{}: `{}`",
i + 1,
self.tcx.item_path_str(trait_info.def_id)));
}
}
}
/// Checks whether there is a local type somewhere in the chain of
/// autoderefs of `rcvr_ty`.
fn type_derefs_to_local(&self,
span: Span,
rcvr_ty: Ty<'tcx>,
rcvr_expr: Option<&hir::Expr>) -> bool {
fn is_local(ty: Ty) -> bool {
match ty.sty {
ty::TyEnum(def, _) | ty::TyStruct(def, _) => def.did.is_local(),
ty::TyTrait(ref tr) => tr.principal_def_id().is_local(),
ty::TyParam(_) => true,
// everything else (primitive types etc.) is effectively
// non-local (there are "edge" cases, e.g. (LocalType,), but
// the noise from these sort of types is usually just really
// annoying, rather than any sort of help).
_ => false
}
}
// This occurs for UFCS desugaring of `T::method`, where there is no
// receiver expression for the method call, and thus no autoderef.
if rcvr_expr.is_none() {
return is_local(self.resolve_type_vars_with_obligations(rcvr_ty));
}
self.autoderef(span, rcvr_ty).any(|(ty, _)| is_local(ty))
}
}
pub type AllTraitsVec = Vec<TraitInfo>;
#[derive(Copy, Clone)]
pub struct TraitInfo {
pub def_id: DefId,
}
impl TraitInfo {
fn new(def_id: DefId) -> TraitInfo {
TraitInfo {
def_id: def_id,
}
}
}
impl PartialEq for TraitInfo {
fn eq(&self, other: &TraitInfo) -> bool {
self.cmp(other) == Ordering::Equal
}
}
impl Eq for TraitInfo {}
impl PartialOrd for TraitInfo {
fn partial_cmp(&self, other: &TraitInfo) -> Option<Ordering> { Some(self.cmp(other)) }
}
impl Ord for TraitInfo {
fn cmp(&self, other: &TraitInfo) -> Ordering {
// local crates are more important than remote ones (local:
// cnum == 0), and otherwise we throw in the defid for totality
let lhs = (other.def_id.krate, other.def_id);
let rhs = (self.def_id.krate, self.def_id);
lhs.cmp(&rhs)
}
}
/// Retrieve all traits in this crate and any dependent crates.
pub fn all_traits<'a>(ccx: &'a CrateCtxt) -> AllTraits<'a> {
if ccx.all_traits.borrow().is_none() {
use rustc::hir::intravisit;
let mut traits = vec![];
// Crate-local:
//
// meh.
struct Visitor<'a, 'tcx:'a> {
map: &'a hir_map::Map<'tcx>,
traits: &'a mut AllTraitsVec,
}
impl<'v, 'a, 'tcx> intravisit::Visitor<'v> for Visitor<'a, 'tcx> {
fn visit_item(&mut self, i: &'v hir::Item) {
match i.node {
hir::ItemTrait(..) => {
let def_id = self.map.local_def_id(i.id);
self.traits.push(TraitInfo::new(def_id));
}
_ => {}
}
}
}
ccx.tcx.map.krate().visit_all_items(&mut Visitor {
map: &ccx.tcx.map,
traits: &mut traits
});
// Cross-crate:
let mut external_mods = FnvHashSet();
fn handle_external_def(traits: &mut AllTraitsVec,
external_mods: &mut FnvHashSet<DefId>,
ccx: &CrateCtxt,
cstore: &for<'a> cstore::CrateStore<'a>,
dl: cstore::DefLike) {
match dl {
cstore::DlDef(Def::Trait(did)) => {
traits.push(TraitInfo::new(did));
}
cstore::DlDef(Def::Mod(did)) => {
if !external_mods.insert(did) {
return;
}
for child in cstore.item_children(did) {
handle_external_def(traits, external_mods,
ccx, cstore, child.def)
}
}
_ => {}
}
}
let cstore = &*ccx.tcx.sess.cstore;
for cnum in ccx.tcx.sess.cstore.crates() {
for child in cstore.crate_top_level_items(cnum) {
handle_external_def(&mut traits, &mut external_mods,
ccx, cstore, child.def)
}
}
*ccx.all_traits.borrow_mut() = Some(traits);
}
let borrow = ccx.all_traits.borrow();
assert!(borrow.is_some());
AllTraits {
borrow: borrow,
idx: 0
}
}
pub struct AllTraits<'a> {
borrow: cell::Ref<'a, Option<AllTraitsVec>>,
idx: usize
}
impl<'a> Iterator for AllTraits<'a> {
type Item = TraitInfo;
fn next(&mut self) -> Option<TraitInfo> {
let AllTraits { ref borrow, ref mut idx } = *self;
// ugh.
borrow.as_ref().unwrap().get(*idx).map(|info| {
*idx += 1;
*info
})
}
}