resolve: Simplify collection of traits in scope
This commit is contained in:
parent
6526e5c772
commit
b7071b2353
7 changed files with 141 additions and 163 deletions
|
@ -115,7 +115,7 @@ impl<'a> Resolver<'a> {
|
|||
self.get_module(parent_id)
|
||||
}
|
||||
|
||||
crate fn get_module(&mut self, def_id: DefId) -> Module<'a> {
|
||||
pub fn get_module(&mut self, def_id: DefId) -> Module<'a> {
|
||||
// If this is a local module, it will be in `module_map`, no need to recalculate it.
|
||||
if let Some(def_id) = def_id.as_local() {
|
||||
return self.module_map[&def_id];
|
||||
|
|
|
@ -14,7 +14,6 @@ use crate::{ResolutionError, Resolver, Segment, UseError};
|
|||
use rustc_ast::ptr::P;
|
||||
use rustc_ast::visit::{self, AssocCtxt, FnCtxt, FnKind, Visitor};
|
||||
use rustc_ast::*;
|
||||
use rustc_ast::{unwrap_or, walk_list};
|
||||
use rustc_ast_lowering::ResolverAstLowering;
|
||||
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
|
||||
use rustc_errors::DiagnosticId;
|
||||
|
@ -1911,7 +1910,7 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> {
|
|||
// it needs to be added to the trait map.
|
||||
if ns == ValueNS {
|
||||
let item_name = path.last().unwrap().ident;
|
||||
let traits = self.get_traits_containing_item(item_name, ns);
|
||||
let traits = self.traits_in_scope(item_name, ns);
|
||||
self.r.trait_map.insert(id, traits);
|
||||
}
|
||||
|
||||
|
@ -2372,12 +2371,12 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> {
|
|||
// field, we need to add any trait methods we find that match
|
||||
// the field name so that we can do some nice error reporting
|
||||
// later on in typeck.
|
||||
let traits = self.get_traits_containing_item(ident, ValueNS);
|
||||
let traits = self.traits_in_scope(ident, ValueNS);
|
||||
self.r.trait_map.insert(expr.id, traits);
|
||||
}
|
||||
ExprKind::MethodCall(ref segment, ..) => {
|
||||
debug!("(recording candidate traits for expr) recording traits for {}", expr.id);
|
||||
let traits = self.get_traits_containing_item(segment.ident, ValueNS);
|
||||
let traits = self.traits_in_scope(segment.ident, ValueNS);
|
||||
self.r.trait_map.insert(expr.id, traits);
|
||||
}
|
||||
_ => {
|
||||
|
@ -2386,64 +2385,13 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> {
|
|||
}
|
||||
}
|
||||
|
||||
fn get_traits_containing_item(
|
||||
&mut self,
|
||||
mut ident: Ident,
|
||||
ns: Namespace,
|
||||
) -> Vec<TraitCandidate> {
|
||||
debug!("(getting traits containing item) looking for '{}'", ident.name);
|
||||
|
||||
let mut found_traits = Vec::new();
|
||||
// Look for the current trait.
|
||||
if let Some((module, _)) = self.current_trait_ref {
|
||||
if self
|
||||
.r
|
||||
.resolve_ident_in_module(
|
||||
ModuleOrUniformRoot::Module(module),
|
||||
ident,
|
||||
ns,
|
||||
&self.parent_scope,
|
||||
false,
|
||||
module.span,
|
||||
)
|
||||
.is_ok()
|
||||
{
|
||||
let def_id = module.def_id().unwrap();
|
||||
found_traits.push(TraitCandidate { def_id, import_ids: smallvec![] });
|
||||
}
|
||||
}
|
||||
|
||||
ident.span = ident.span.normalize_to_macros_2_0();
|
||||
let mut search_module = self.parent_scope.module;
|
||||
loop {
|
||||
self.r.get_traits_in_module_containing_item(
|
||||
ident,
|
||||
ns,
|
||||
search_module,
|
||||
&mut found_traits,
|
||||
&self.parent_scope,
|
||||
);
|
||||
let mut span_data = ident.span.data();
|
||||
search_module = unwrap_or!(
|
||||
self.r.hygienic_lexical_parent(search_module, &mut span_data.ctxt),
|
||||
break
|
||||
);
|
||||
ident.span = span_data.span();
|
||||
}
|
||||
|
||||
if let Some(prelude) = self.r.prelude {
|
||||
if !search_module.no_implicit_prelude {
|
||||
self.r.get_traits_in_module_containing_item(
|
||||
ident,
|
||||
ns,
|
||||
prelude,
|
||||
&mut found_traits,
|
||||
&self.parent_scope,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
found_traits
|
||||
fn traits_in_scope(&mut self, ident: Ident, ns: Namespace) -> Vec<TraitCandidate> {
|
||||
self.r.traits_in_scope(
|
||||
self.current_trait_ref.as_ref().map(|(module, _)| *module),
|
||||
&self.parent_scope,
|
||||
ident.span.ctxt(),
|
||||
Some((ident.name, ns)),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -44,9 +44,9 @@ use rustc_index::vec::IndexVec;
|
|||
use rustc_metadata::creader::{CStore, CrateLoader};
|
||||
use rustc_middle::hir::exports::ExportMap;
|
||||
use rustc_middle::middle::cstore::{CrateStore, MetadataLoaderDyn};
|
||||
use rustc_middle::span_bug;
|
||||
use rustc_middle::ty::query::Providers;
|
||||
use rustc_middle::ty::{self, DefIdTree, ResolverOutputs};
|
||||
use rustc_middle::{bug, span_bug};
|
||||
use rustc_session::lint;
|
||||
use rustc_session::lint::{BuiltinLintDiagnostics, LintBuffer};
|
||||
use rustc_session::Session;
|
||||
|
@ -1477,52 +1477,79 @@ impl<'a> Resolver<'a> {
|
|||
self.crate_loader.postprocess(krate);
|
||||
}
|
||||
|
||||
fn get_traits_in_module_containing_item(
|
||||
pub fn traits_in_scope(
|
||||
&mut self,
|
||||
ident: Ident,
|
||||
ns: Namespace,
|
||||
module: Module<'a>,
|
||||
found_traits: &mut Vec<TraitCandidate>,
|
||||
current_trait: Option<Module<'a>>,
|
||||
parent_scope: &ParentScope<'a>,
|
||||
ctxt: SyntaxContext,
|
||||
assoc_item: Option<(Symbol, Namespace)>,
|
||||
) -> Vec<TraitCandidate> {
|
||||
let mut found_traits = Vec::new();
|
||||
|
||||
if let Some(module) = current_trait {
|
||||
if self.trait_may_have_item(Some(module), assoc_item) {
|
||||
let def_id = module.def_id().unwrap();
|
||||
found_traits.push(TraitCandidate { def_id, import_ids: smallvec![] });
|
||||
}
|
||||
}
|
||||
|
||||
self.visit_scopes(ScopeSet::All(TypeNS, false), parent_scope, ctxt, |this, scope, _, _| {
|
||||
match scope {
|
||||
Scope::Module(module) => {
|
||||
this.traits_in_module(module, assoc_item, &mut found_traits);
|
||||
}
|
||||
Scope::StdLibPrelude => {
|
||||
if let Some(module) = this.prelude {
|
||||
this.traits_in_module(module, assoc_item, &mut found_traits);
|
||||
}
|
||||
}
|
||||
Scope::ExternPrelude | Scope::ToolPrelude | Scope::BuiltinTypes => {}
|
||||
_ => unreachable!(),
|
||||
}
|
||||
None::<()>
|
||||
});
|
||||
|
||||
found_traits
|
||||
}
|
||||
|
||||
fn traits_in_module(
|
||||
&mut self,
|
||||
module: Module<'a>,
|
||||
assoc_item: Option<(Symbol, Namespace)>,
|
||||
found_traits: &mut Vec<TraitCandidate>,
|
||||
) {
|
||||
assert!(ns == TypeNS || ns == ValueNS);
|
||||
module.ensure_traits(self);
|
||||
let traits = module.traits.borrow();
|
||||
|
||||
for &(trait_name, binding) in traits.as_ref().unwrap().iter() {
|
||||
// Traits have pseudo-modules that can be used to search for the given ident.
|
||||
if let Some(module) = binding.module() {
|
||||
let mut ident = ident;
|
||||
if ident.span.glob_adjust(module.expansion, binding.span).is_none() {
|
||||
continue;
|
||||
}
|
||||
if self
|
||||
.resolve_ident_in_module_unadjusted(
|
||||
ModuleOrUniformRoot::Module(module),
|
||||
ident,
|
||||
ns,
|
||||
parent_scope,
|
||||
false,
|
||||
module.span,
|
||||
)
|
||||
.is_ok()
|
||||
{
|
||||
let import_ids = self.find_transitive_imports(&binding.kind, trait_name);
|
||||
let trait_def_id = module.def_id().unwrap();
|
||||
found_traits.push(TraitCandidate { def_id: trait_def_id, import_ids });
|
||||
}
|
||||
} else if let Res::Def(DefKind::TraitAlias, _) = binding.res() {
|
||||
// For now, just treat all trait aliases as possible candidates, since we don't
|
||||
// know if the ident is somewhere in the transitive bounds.
|
||||
let import_ids = self.find_transitive_imports(&binding.kind, trait_name);
|
||||
let trait_def_id = binding.res().def_id();
|
||||
found_traits.push(TraitCandidate { def_id: trait_def_id, import_ids });
|
||||
} else {
|
||||
bug!("candidate is not trait or trait alias?")
|
||||
for (trait_name, trait_binding) in traits.as_ref().unwrap().iter() {
|
||||
if self.trait_may_have_item(trait_binding.module(), assoc_item) {
|
||||
let def_id = trait_binding.res().def_id();
|
||||
let import_ids = self.find_transitive_imports(&trait_binding.kind, *trait_name);
|
||||
found_traits.push(TraitCandidate { def_id, import_ids });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// List of traits in scope is pruned on best effort basis. We reject traits not having an
|
||||
// associated item with the given name and namespace (if specified). This is a conservative
|
||||
// optimization, proper hygienic type-based resolution of associated items is done in typeck.
|
||||
// We don't reject trait aliases (`trait_module == None`) because we don't have access to their
|
||||
// associated items.
|
||||
fn trait_may_have_item(
|
||||
&mut self,
|
||||
trait_module: Option<Module<'a>>,
|
||||
assoc_item: Option<(Symbol, Namespace)>,
|
||||
) -> bool {
|
||||
match (trait_module, assoc_item) {
|
||||
(Some(trait_module), Some((name, ns))) => {
|
||||
self.resolutions(trait_module).borrow().iter().any(|resolution| {
|
||||
let (&BindingKey { ident: assoc_ident, ns: assoc_ns, .. }, _) = resolution;
|
||||
assoc_ns == ns && assoc_ident.name == name
|
||||
})
|
||||
}
|
||||
_ => true,
|
||||
}
|
||||
}
|
||||
|
||||
fn find_transitive_imports(
|
||||
&mut self,
|
||||
mut kind: &NameBindingKind<'_>,
|
||||
|
@ -3227,34 +3254,6 @@ impl<'a> Resolver<'a> {
|
|||
})
|
||||
}
|
||||
|
||||
/// This is equivalent to `get_traits_in_module_containing_item`, but without filtering by the associated item.
|
||||
///
|
||||
/// This is used by rustdoc for intra-doc links.
|
||||
pub fn traits_in_scope(&mut self, module_id: DefId) -> Vec<TraitCandidate> {
|
||||
let module = self.get_module(module_id);
|
||||
module.ensure_traits(self);
|
||||
let traits = module.traits.borrow();
|
||||
let to_candidate =
|
||||
|this: &mut Self, &(trait_name, binding): &(Ident, &NameBinding<'_>)| TraitCandidate {
|
||||
def_id: binding.res().def_id(),
|
||||
import_ids: this.find_transitive_imports(&binding.kind, trait_name),
|
||||
};
|
||||
|
||||
let mut candidates: Vec<_> =
|
||||
traits.as_ref().unwrap().iter().map(|x| to_candidate(self, x)).collect();
|
||||
|
||||
if let Some(prelude) = self.prelude {
|
||||
if !module.no_implicit_prelude {
|
||||
prelude.ensure_traits(self);
|
||||
candidates.extend(
|
||||
prelude.traits.borrow().as_ref().unwrap().iter().map(|x| to_candidate(self, x)),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
candidates
|
||||
}
|
||||
|
||||
/// Rustdoc uses this to resolve things in a recoverable way. `ResolutionError<'a>`
|
||||
/// isn't something that can be returned because it can't be made to live that long,
|
||||
/// and also it's a private type. Fortunately rustdoc doesn't need to know the error,
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue