#![cfg_attr(not(stage0), deny(warnings))]
#![feature(associated_consts)]
-#![feature(borrow_state)]
#![feature(rustc_diagnostic_macros)]
#![feature(rustc_private)]
#![feature(staged_api)]
use rustc_front::hir::{ItemFn, ItemForeignMod, ItemImpl, ItemMod, ItemStatic, ItemDefaultImpl};
use rustc_front::hir::{ItemStruct, ItemTrait, ItemTy, ItemUse};
use rustc_front::hir::Local;
-use rustc_front::hir::{Pat, PatEnum, PatIdent, PatLit, PatQPath};
-use rustc_front::hir::{PatRange, PatStruct, Path, PrimTy};
+use rustc_front::hir::{Pat, PatKind, Path, PrimTy};
+use rustc_front::hir::{PathSegment, PathParameters};
+use rustc_front::hir::HirVec;
use rustc_front::hir::{TraitRef, Ty, TyBool, TyChar, TyFloat, TyInt};
use rustc_front::hir::{TyRptr, TyStr, TyUint, TyPath, TyPtr};
use rustc_front::util::walk_pat;
NotFound,
}
+/// Candidates for a name resolution failure
+pub struct SuggestedCandidates {
+ name: String,
+ candidates: Vec<Path>,
+}
+
pub enum ResolutionError<'a> {
/// error E0401: can't use type parameters from outer function
TypeParametersFromOuterFunction,
/// error E0404: is not a trait
IsNotATrait(&'a str),
/// error E0405: use of undeclared trait name
- UndeclaredTraitName(&'a str),
+ UndeclaredTraitName(&'a str, SuggestedCandidates),
/// error E0406: undeclared associated type
UndeclaredAssociatedType,
/// error E0407: method is not a member of trait
/// error E0411: use of `Self` outside of an impl or trait
SelfUsedOutsideImplOrTrait,
/// error E0412: use of undeclared
- UseOfUndeclared(&'a str, &'a str),
+ UseOfUndeclared(&'a str, &'a str, SuggestedCandidates),
/// error E0413: declaration shadows an enum variant or unit-like struct in scope
DeclarationShadowsEnumVariantOrUnitLikeStruct(Name),
/// error E0414: only irrefutable patterns allowed here
ResolutionError::IsNotATrait(name) => {
struct_span_err!(resolver.session, span, E0404, "`{}` is not a trait", name)
}
- ResolutionError::UndeclaredTraitName(name) => {
- struct_span_err!(resolver.session,
- span,
- E0405,
- "use of undeclared trait name `{}`",
- name)
+ ResolutionError::UndeclaredTraitName(name, candidates) => {
+ let mut err = struct_span_err!(resolver.session,
+ span,
+ E0405,
+ "trait `{}` is not in scope",
+ name);
+ show_candidates(&mut err, span, &candidates);
+ err
}
ResolutionError::UndeclaredAssociatedType => {
struct_span_err!(resolver.session, span, E0406, "undeclared associated type")
E0411,
"use of `Self` outside of an impl or trait")
}
- ResolutionError::UseOfUndeclared(kind, name) => {
- struct_span_err!(resolver.session,
- span,
- E0412,
- "use of undeclared {} `{}`",
- kind,
- name)
+ ResolutionError::UseOfUndeclared(kind, name, candidates) => {
+ let mut err = struct_span_err!(resolver.session,
+ span,
+ E0412,
+ "{} `{}` is undefined or not in scope",
+ kind,
+ name);
+ show_candidates(&mut err, span, &candidates);
+ err
}
ResolutionError::DeclarationShadowsEnumVariantOrUnitLikeStruct(name) => {
struct_span_err!(resolver.session,
is_extern_crate: bool,
resolutions: RefCell<HashMap<(Name, Namespace), NameResolution<'a>>>,
- imports: RefCell<Vec<ImportDirective>>,
+ unresolved_imports: RefCell<Vec<ImportDirective>>,
// The module children of this node, including normal modules and anonymous modules.
// Anonymous children are pseudo-modules that are implicitly created around items
// The number of unresolved pub glob imports in this module
pub_glob_count: Cell<usize>,
- // The index of the import we're resolving.
- resolved_import_count: Cell<usize>,
-
// Whether this module is populated. If not populated, any attempt to
// access the children must be preceded with a
// `populate_module_if_necessary` call.
pub type Module<'a> = &'a ModuleS<'a>;
impl<'a> ModuleS<'a> {
+
fn new(parent_link: ParentLink<'a>, def: Option<Def>, external: bool, is_public: bool) -> Self {
ModuleS {
parent_link: parent_link,
is_public: is_public,
is_extern_crate: false,
resolutions: RefCell::new(HashMap::new()),
- imports: RefCell::new(Vec::new()),
+ unresolved_imports: RefCell::new(Vec::new()),
module_children: RefCell::new(NodeMap()),
shadowed_traits: RefCell::new(Vec::new()),
glob_count: Cell::new(0),
pub_count: Cell::new(0),
pub_glob_count: Cell::new(0),
- resolved_import_count: Cell::new(0),
populated: Cell::new(!external),
}
}
}
}
- fn all_imports_resolved(&self) -> bool {
- if self.imports.borrow_state() == ::std::cell::BorrowState::Writing {
- // it is currently being resolved ! so nope
- false
- } else {
- self.imports.borrow().len() == self.resolved_import_count.get()
- }
- }
-
pub fn inc_glob_count(&self) {
self.glob_count.set(self.glob_count.get() + 1);
}
fn def_and_lp(&self) -> (Def, LastPrivate) {
let def = self.def().unwrap();
+ if let Def::Err = def { return (def, LastMod(AllPublic)) }
(def, LastMod(if self.is_public() { AllPublic } else { DependsOn(def.def_id()) }))
}
span: Span,
lp: LastPrivate)
-> ResolveResult<(Module<'a>, LastPrivate)> {
- fn search_parent_externals<'a>(needle: Name, module: Module<'a>) -> Option<Module<'a>> {
+ fn search_parent_externals(needle: Name, module: Module) -> Option<Module> {
match module.resolve_name(needle, TypeNS, false) {
Success(binding) if binding.is_extern_crate() => Some(module),
_ => match module.parent_link {
}
fn report_unresolved_imports(&mut self, module_: Module<'a>) {
- let index = module_.resolved_import_count.get();
- let imports = module_.imports.borrow();
- let import_count = imports.len();
- if index != import_count {
- resolve_error(self,
- (*imports)[index].span,
- ResolutionError::UnresolvedImport(None));
+ for import in module_.unresolved_imports.borrow().iter() {
+ resolve_error(self, import.span, ResolutionError::UnresolvedImport(None));
+ break;
}
// Descend into children and anonymous children.
Err(())
}
} else {
- resolve_error(self,
- trait_path.span,
- ResolutionError::UndeclaredTraitName(&path_names_to_string(trait_path,
- path_depth)));
+
+ // find possible candidates
+ let trait_name = trait_path.segments.last().unwrap().identifier.name;
+ let candidates =
+ self.lookup_candidates(
+ trait_name,
+ TypeNS,
+ |def| match def {
+ Def::Trait(_) => true,
+ _ => false,
+ },
+ );
+
+ // create error object
+ let name = &path_names_to_string(trait_path, path_depth);
+ let error =
+ ResolutionError::UndeclaredTraitName(
+ name,
+ candidates,
+ );
+
+ resolve_error(self, trait_path.span, error);
Err(())
}
}
ty.span,
ResolutionError::SelfUsedOutsideImplOrTrait);
} else {
- resolve_error(self,
- ty.span,
- ResolutionError::UseOfUndeclared(
- kind,
- &path_names_to_string(path,
- 0))
- );
+ let segment = path.segments.last();
+ let segment = segment.expect("missing name in path");
+ let type_name = segment.identifier.name;
+
+ let candidates =
+ self.lookup_candidates(
+ type_name,
+ TypeNS,
+ |def| match def {
+ Def::Trait(_) |
+ Def::Enum(_) |
+ Def::Struct(_) |
+ Def::TyAlias(_) => true,
+ _ => false,
+ },
+ );
+
+ // create error object
+ let name = &path_names_to_string(path, 0);
+ let error =
+ ResolutionError::UseOfUndeclared(
+ kind,
+ name,
+ candidates,
+ );
+
+ resolve_error(self, ty.span, error);
}
}
}
let pat_id = pattern.id;
walk_pat(pattern, |pattern| {
match pattern.node {
- PatIdent(binding_mode, ref path1, ref at_rhs) => {
- // The meaning of PatIdent with no type parameters
+ PatKind::Ident(binding_mode, ref path1, ref at_rhs) => {
+ // The meaning of PatKind::Ident with no type parameters
// depends on whether an enum variant or unit-like struct
// with that name is in scope. The probing lookup has to
// be careful not to emit spurious errors. Only matching
}
}
- PatEnum(ref path, _) => {
+ PatKind::TupleStruct(ref path, _) | PatKind::Path(ref path) => {
// This must be an enum variant, struct or const.
let resolution = match self.resolve_possibly_assoc_item(pat_id,
None,
ValueNS,
false) {
// The below shouldn't happen because all
- // qualified paths should be in PatQPath.
+ // qualified paths should be in PatKind::QPath.
TypecheckRequired =>
self.session.span_bug(path.span,
- "resolve_possibly_assoc_item claimed
- \
- that a path in PatEnum requires typecheck
- \
- to resolve, but qualified paths should be
- \
- PatQPath"),
+ "resolve_possibly_assoc_item claimed that a path \
+ in PatKind::Path or PatKind::TupleStruct \
+ requires typecheck to resolve, but qualified \
+ paths should be PatKind::QPath"),
ResolveAttempt(resolution) => resolution,
};
if let Some(path_res) = resolution {
intravisit::walk_path(self, path);
}
- PatQPath(ref qself, ref path) => {
+ PatKind::QPath(ref qself, ref path) => {
// Associated constants only.
let resolution = match self.resolve_possibly_assoc_item(pat_id,
Some(qself),
intravisit::walk_pat(self, pattern);
}
- PatStruct(ref path, _, _) => {
+ PatKind::Struct(ref path, _, _) => {
match self.resolve_path(pat_id, path, 0, TypeNS, false) {
Some(definition) => {
self.record_def(pattern.id, definition);
intravisit::walk_path(self, path);
}
- PatLit(_) | PatRange(..) => {
+ PatKind::Lit(_) | PatKind::Range(..) => {
intravisit::walk_pat(self, pattern);
}
found_traits
}
+ /// When name resolution fails, this method can be used to look up candidate
+ /// entities with the expected name. It allows filtering them using the
+ /// supplied predicate (which should be used to only accept the types of
+ /// definitions expected e.g. traits). The lookup spans across all crates.
+ ///
+ /// NOTE: The method does not look into imports, but this is not a problem,
+ /// since we report the definitions (thus, the de-aliased imports).
+ fn lookup_candidates<FilterFn>(&mut self,
+ lookup_name: Name,
+ namespace: Namespace,
+ filter_fn: FilterFn) -> SuggestedCandidates
+ where FilterFn: Fn(Def) -> bool {
+
+ let mut lookup_results = Vec::new();
+ let mut worklist = Vec::new();
+ worklist.push((self.graph_root, Vec::new(), false));
+
+ while let Some((in_module,
+ path_segments,
+ in_module_is_extern)) = worklist.pop() {
+ build_reduced_graph::populate_module_if_necessary(self, &in_module);
+
+ in_module.for_each_child(|name, ns, name_binding| {
+
+ // avoid imports entirely
+ if name_binding.is_import() { return; }
+
+ // collect results based on the filter function
+ if let Some(def) = name_binding.def() {
+ if name == lookup_name && ns == namespace && filter_fn(def) {
+ // create the path
+ let ident = hir::Ident::from_name(name);
+ let params = PathParameters::none();
+ let segment = PathSegment {
+ identifier: ident,
+ parameters: params,
+ };
+ let span = name_binding.span.unwrap_or(syntax::codemap::DUMMY_SP);
+ let mut segms = path_segments.clone();
+ segms.push(segment);
+ let segms = HirVec::from_vec(segms);
+ let path = Path {
+ span: span,
+ global: true,
+ segments: segms,
+ };
+ // the entity is accessible in the following cases:
+ // 1. if it's defined in the same crate, it's always
+ // accessible (since private entities can be made public)
+ // 2. if it's defined in another crate, it's accessible
+ // only if both the module is public and the entity is
+ // declared as public (due to pruning, we don't explore
+ // outside crate private modules => no need to check this)
+ if !in_module_is_extern || name_binding.is_public() {
+ lookup_results.push(path);
+ }
+ }
+ }
+
+ // collect submodules to explore
+ if let Some(module) = name_binding.module() {
+ // form the path
+ let path_segments = match module.parent_link {
+ NoParentLink => path_segments.clone(),
+ ModuleParentLink(_, name) => {
+ let mut paths = path_segments.clone();
+ let ident = hir::Ident::from_name(name);
+ let params = PathParameters::none();
+ let segm = PathSegment {
+ identifier: ident,
+ parameters: params,
+ };
+ paths.push(segm);
+ paths
+ }
+ _ => unreachable!(),
+ };
+
+ if !in_module_is_extern || name_binding.is_public() {
+ // add the module to the lookup
+ let is_extern = in_module_is_extern || module.is_extern_crate;
+ worklist.push((module, path_segments, is_extern));
+ }
+ }
+ })
+ }
+
+ SuggestedCandidates {
+ name: lookup_name.as_str().to_string(),
+ candidates: lookup_results,
+ }
+ }
+
fn record_def(&mut self, node_id: NodeId, resolution: PathResolution) {
debug!("(recording def) recording {:?} for {}", resolution, node_id);
assert!(match resolution.last_private {
names_to_string(&names[..])
}
+/// When an entity with a given name is not available in scope, we search for
+/// entities with that name in all crates. This method allows outputting the
+/// results of this search in a programmer-friendly way
+fn show_candidates(session: &mut DiagnosticBuilder,
+ span: syntax::codemap::Span,
+ candidates: &SuggestedCandidates) {
+
+ let paths = &candidates.candidates;
+
+ if paths.len() > 0 {
+ // don't show more than MAX_CANDIDATES results, so
+ // we're consistent with the trait suggestions
+ const MAX_CANDIDATES: usize = 5;
+
+ // we want consistent results across executions, but candidates are produced
+ // by iterating through a hash map, so make sure they are ordered:
+ let mut path_strings: Vec<_> = paths.into_iter()
+ .map(|p| path_names_to_string(&p, 0))
+ .collect();
+ path_strings.sort();
+
+ // behave differently based on how many candidates we have:
+ if !paths.is_empty() {
+ if paths.len() == 1 {
+ session.fileline_help(
+ span,
+ &format!("you can to import it into scope: `use {};`.",
+ &path_strings[0]),
+ );
+ } else {
+ session.fileline_help(span, "you can import several candidates \
+ into scope (`use ...;`):");
+ let count = path_strings.len() as isize - MAX_CANDIDATES as isize + 1;
+
+ for (idx, path_string) in path_strings.iter().enumerate() {
+ if idx == MAX_CANDIDATES - 1 && count > 1 {
+ session.fileline_help(
+ span,
+ &format!(" and {} other candidates", count).to_string(),
+ );
+ break;
+ } else {
+ session.fileline_help(
+ span,
+ &format!(" `{}`", path_string).to_string(),
+ );
+ }
+ }
+ }
+ }
+ } else {
+ // nothing found:
+ session.fileline_help(
+ span,
+ &format!("no candidates by the name of `{}` found in your \
+ project; maybe you misspelled the name or forgot to import \
+ an external crate?", candidates.name.to_string()),
+ );
+ };
+}
+
/// A somewhat inefficient routine to obtain the name of a module.
-fn module_to_string<'a>(module: Module<'a>) -> String {
+fn module_to_string(module: Module) -> String {
let mut names = Vec::new();
- fn collect_mod<'a>(names: &mut Vec<ast::Name>, module: Module<'a>) {
+ fn collect_mod(names: &mut Vec<ast::Name>, module: Module) {
match module.parent_link {
NoParentLink => {}
ModuleParentLink(ref module, name) => {