Rollup merge of #80765 - petrochenkov:traitsinscope, r=matthewjasper
resolve: Simplify collection of traits in scope "Traits in scope" for a given location are collected by walking all scopes in type namespace, collecting traits in them and pruning traits that don't have an associated item with the given name and namespace. Previously we tried to prune traits using some kind of hygienic resolution for associated items, but that was complex and likely incorrect, e.g. in #80762 correction to visibilites of trait items caused some traits to not be in scope anymore. I previously had some comments and concerns about this in https://github.com/rust-lang/rust/pull/65351. In this PR we are doing some much simpler pruning based on `Symbol` and `Namespace` comparisons, it should be enough to throw away 99.9% of unnecessary traits. It is not necessary for pruning to be precise because for trait aliases, for example, we don't do any pruning at all, and precise hygienic resolution for associated items needs to be done in typeck anyway. The somewhat unexpected effect is that trait imports introduced by macros 2.0 now bring traits into scope due to the removed hygienic check on associated item names. I'm not sure whether it is desirable or not, but I think it's acceptable for now. The old check was certainly incorrect because macros 2.0 did bring trait aliases into scope. If doing this is not desirable, then we should come up with some other way to avoid bringing traits from macros 2.0 into scope, that would accommodate for trait aliases as well. --- The PR also contains a couple of pure refactorings - Scope walk is done by using `visit_scopes` instead of a hand-rolled version. - Code is restructured to accomodate for rustdoc that also wants to query traits in scope, but doesn't want to filter them by associated items at all. r? ```@matthewjasper```
This commit is contained in:
commit
ffcbeefd64
7 changed files with 141 additions and 163 deletions
|
@ -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