mirror of
https://github.com/rust-lang/rust-analyzer.git
synced 2025-09-28 11:20:54 +00:00
1481 lines
54 KiB
Rust
1481 lines
54 KiB
Rust
//! Name resolution façade.
|
|
use std::{fmt, mem};
|
|
|
|
use base_db::Crate;
|
|
use hir_expand::{
|
|
MacroDefId,
|
|
mod_path::{ModPath, PathKind},
|
|
name::Name,
|
|
};
|
|
use intern::{Symbol, sym};
|
|
use itertools::Itertools as _;
|
|
use rustc_hash::FxHashSet;
|
|
use smallvec::{SmallVec, smallvec};
|
|
use span::SyntaxContext;
|
|
use triomphe::Arc;
|
|
|
|
use crate::{
|
|
AdtId, ConstId, ConstParamId, CrateRootModuleId, DefWithBodyId, EnumId, EnumVariantId,
|
|
ExternBlockId, ExternCrateId, FunctionId, FxIndexMap, GenericDefId, GenericParamId, HasModule,
|
|
ImplId, ItemContainerId, ItemTreeLoc, LifetimeParamId, LocalModuleId, Lookup, Macro2Id,
|
|
MacroId, MacroRulesId, ModuleDefId, ModuleId, ProcMacroId, StaticId, StructId, TraitAliasId,
|
|
TraitId, TypeAliasId, TypeOrConstParamId, TypeParamId, UseId, VariantId,
|
|
builtin_type::BuiltinType,
|
|
db::DefDatabase,
|
|
expr_store::{
|
|
HygieneId,
|
|
path::Path,
|
|
scope::{ExprScopes, ScopeId},
|
|
},
|
|
hir::{
|
|
BindingId, ExprId, LabelId,
|
|
generics::{GenericParams, TypeOrConstParamData},
|
|
},
|
|
item_scope::{BUILTIN_SCOPE, BuiltinShadowMode, ImportOrExternCrate, ImportOrGlob, ItemScope},
|
|
item_tree::ImportAlias,
|
|
lang_item::LangItemTarget,
|
|
nameres::{DefMap, LocalDefMap, MacroSubNs, ResolvePathResultPrefixInfo, block_def_map},
|
|
per_ns::PerNs,
|
|
type_ref::LifetimeRef,
|
|
visibility::{RawVisibility, Visibility},
|
|
};
|
|
|
|
#[derive(Debug, Clone)]
|
|
pub struct Resolver<'db> {
|
|
/// The stack of scopes, where the inner-most scope is the last item.
|
|
///
|
|
/// When using, you generally want to process the scopes in reverse order,
|
|
/// there's `scopes` *method* for that.
|
|
scopes: Vec<Scope<'db>>,
|
|
module_scope: ModuleItemMap<'db>,
|
|
}
|
|
|
|
#[derive(Clone)]
|
|
struct ModuleItemMap<'db> {
|
|
def_map: &'db DefMap,
|
|
local_def_map: &'db LocalDefMap,
|
|
module_id: LocalModuleId,
|
|
}
|
|
|
|
impl fmt::Debug for ModuleItemMap<'_> {
|
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
f.debug_struct("ModuleItemMap").field("module_id", &self.module_id).finish()
|
|
}
|
|
}
|
|
|
|
#[derive(Clone)]
|
|
struct ExprScope {
|
|
owner: DefWithBodyId,
|
|
expr_scopes: Arc<ExprScopes>,
|
|
scope_id: ScopeId,
|
|
}
|
|
|
|
impl fmt::Debug for ExprScope {
|
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
f.debug_struct("ExprScope")
|
|
.field("owner", &self.owner)
|
|
.field("scope_id", &self.scope_id)
|
|
.finish()
|
|
}
|
|
}
|
|
|
|
#[derive(Debug, Clone)]
|
|
enum Scope<'db> {
|
|
/// All the items and imported names of a module
|
|
BlockScope(ModuleItemMap<'db>),
|
|
/// Brings the generic parameters of an item into scope as well as the `Self` type alias /
|
|
/// generic for ADTs and impls.
|
|
GenericParams { def: GenericDefId, params: Arc<GenericParams> },
|
|
/// Local bindings
|
|
ExprScope(ExprScope),
|
|
/// Macro definition inside bodies that affects all paths after it in the same block.
|
|
MacroDefScope(MacroDefId),
|
|
}
|
|
|
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
|
pub enum TypeNs {
|
|
SelfType(ImplId),
|
|
GenericParam(TypeParamId),
|
|
AdtId(AdtId),
|
|
AdtSelfType(AdtId),
|
|
// Yup, enum variants are added to the types ns, but any usage of variant as
|
|
// type is an error.
|
|
EnumVariantId(EnumVariantId),
|
|
TypeAliasId(TypeAliasId),
|
|
BuiltinType(BuiltinType),
|
|
TraitId(TraitId),
|
|
TraitAliasId(TraitAliasId),
|
|
|
|
ModuleId(ModuleId),
|
|
}
|
|
|
|
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
|
pub enum ResolveValueResult {
|
|
ValueNs(ValueNs, Option<ImportOrGlob>),
|
|
Partial(TypeNs, usize, Option<ImportOrExternCrate>),
|
|
}
|
|
|
|
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
|
|
pub enum ValueNs {
|
|
ImplSelf(ImplId),
|
|
LocalBinding(BindingId),
|
|
FunctionId(FunctionId),
|
|
ConstId(ConstId),
|
|
StaticId(StaticId),
|
|
StructId(StructId),
|
|
EnumVariantId(EnumVariantId),
|
|
GenericParam(ConstParamId),
|
|
}
|
|
|
|
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
|
|
pub enum LifetimeNs {
|
|
Static,
|
|
LifetimeParam(LifetimeParamId),
|
|
}
|
|
|
|
impl<'db> Resolver<'db> {
|
|
/// Resolve known trait from std, like `std::futures::Future`
|
|
pub fn resolve_known_trait(&self, db: &dyn DefDatabase, path: &ModPath) -> Option<TraitId> {
|
|
let res = self.resolve_module_path(db, path, BuiltinShadowMode::Other).take_types()?;
|
|
match res {
|
|
ModuleDefId::TraitId(it) => Some(it),
|
|
_ => None,
|
|
}
|
|
}
|
|
|
|
/// Resolve known struct from std, like `std::boxed::Box`
|
|
pub fn resolve_known_struct(&self, db: &dyn DefDatabase, path: &ModPath) -> Option<StructId> {
|
|
let res = self.resolve_module_path(db, path, BuiltinShadowMode::Other).take_types()?;
|
|
match res {
|
|
ModuleDefId::AdtId(AdtId::StructId(it)) => Some(it),
|
|
_ => None,
|
|
}
|
|
}
|
|
|
|
/// Resolve known enum from std, like `std::result::Result`
|
|
pub fn resolve_known_enum(&self, db: &dyn DefDatabase, path: &ModPath) -> Option<EnumId> {
|
|
let res = self.resolve_module_path(db, path, BuiltinShadowMode::Other).take_types()?;
|
|
match res {
|
|
ModuleDefId::AdtId(AdtId::EnumId(it)) => Some(it),
|
|
_ => None,
|
|
}
|
|
}
|
|
|
|
pub fn resolve_module_path_in_items(&self, db: &dyn DefDatabase, path: &ModPath) -> PerNs {
|
|
self.resolve_module_path(db, path, BuiltinShadowMode::Module)
|
|
}
|
|
|
|
pub fn resolve_path_in_type_ns(
|
|
&self,
|
|
db: &dyn DefDatabase,
|
|
path: &Path,
|
|
) -> Option<(TypeNs, Option<usize>, Option<ImportOrExternCrate>)> {
|
|
self.resolve_path_in_type_ns_with_prefix_info(db, path).map(
|
|
|(resolution, remaining_segments, import, _)| (resolution, remaining_segments, import),
|
|
)
|
|
}
|
|
|
|
pub fn resolve_path_in_type_ns_with_prefix_info(
|
|
&self,
|
|
db: &dyn DefDatabase,
|
|
path: &Path,
|
|
) -> Option<(TypeNs, Option<usize>, Option<ImportOrExternCrate>, ResolvePathResultPrefixInfo)>
|
|
{
|
|
let path = match path {
|
|
Path::BarePath(mod_path) => mod_path,
|
|
Path::Normal(it) => &it.mod_path,
|
|
Path::LangItem(l, seg) => {
|
|
let type_ns = match *l {
|
|
LangItemTarget::Union(it) => TypeNs::AdtId(it.into()),
|
|
LangItemTarget::TypeAlias(it) => TypeNs::TypeAliasId(it),
|
|
LangItemTarget::Struct(it) => TypeNs::AdtId(it.into()),
|
|
LangItemTarget::EnumVariant(it) => TypeNs::EnumVariantId(it),
|
|
LangItemTarget::EnumId(it) => TypeNs::AdtId(it.into()),
|
|
LangItemTarget::Trait(it) => TypeNs::TraitId(it),
|
|
LangItemTarget::Function(_)
|
|
| LangItemTarget::ImplDef(_)
|
|
| LangItemTarget::Static(_) => return None,
|
|
};
|
|
return Some((
|
|
type_ns,
|
|
seg.as_ref().map(|_| 1),
|
|
None,
|
|
ResolvePathResultPrefixInfo::default(),
|
|
));
|
|
}
|
|
};
|
|
let first_name = path.segments().first()?;
|
|
let skip_to_mod = path.kind != PathKind::Plain;
|
|
if skip_to_mod {
|
|
return self.module_scope.resolve_path_in_type_ns(db, path);
|
|
}
|
|
|
|
let remaining_idx = || {
|
|
if path.segments().len() == 1 { None } else { Some(1) }
|
|
};
|
|
|
|
for scope in self.scopes() {
|
|
match scope {
|
|
Scope::ExprScope(_) | Scope::MacroDefScope(_) => continue,
|
|
Scope::GenericParams { params, def } => {
|
|
if let &GenericDefId::ImplId(impl_) = def {
|
|
if *first_name == sym::Self_ {
|
|
return Some((
|
|
TypeNs::SelfType(impl_),
|
|
remaining_idx(),
|
|
None,
|
|
ResolvePathResultPrefixInfo::default(),
|
|
));
|
|
}
|
|
} else if let &GenericDefId::AdtId(adt) = def {
|
|
if *first_name == sym::Self_ {
|
|
return Some((
|
|
TypeNs::AdtSelfType(adt),
|
|
remaining_idx(),
|
|
None,
|
|
ResolvePathResultPrefixInfo::default(),
|
|
));
|
|
}
|
|
}
|
|
if let Some(id) = params.find_type_by_name(first_name, *def) {
|
|
return Some((
|
|
TypeNs::GenericParam(id),
|
|
remaining_idx(),
|
|
None,
|
|
ResolvePathResultPrefixInfo::default(),
|
|
));
|
|
}
|
|
}
|
|
Scope::BlockScope(m) => {
|
|
if let Some(res) = m.resolve_path_in_type_ns(db, path) {
|
|
let res = match res.0 {
|
|
TypeNs::ModuleId(_) if res.1.is_none() => {
|
|
if let Some(ModuleDefId::BuiltinType(builtin)) = BUILTIN_SCOPE
|
|
.get(first_name)
|
|
.and_then(|builtin| builtin.take_types())
|
|
{
|
|
(
|
|
TypeNs::BuiltinType(builtin),
|
|
remaining_idx(),
|
|
None,
|
|
ResolvePathResultPrefixInfo::default(),
|
|
)
|
|
} else {
|
|
res
|
|
}
|
|
}
|
|
_ => res,
|
|
};
|
|
return Some(res);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
self.module_scope.resolve_path_in_type_ns(db, path)
|
|
}
|
|
|
|
pub fn resolve_path_in_type_ns_fully(
|
|
&self,
|
|
db: &dyn DefDatabase,
|
|
path: &Path,
|
|
) -> Option<TypeNs> {
|
|
let (res, unresolved, _) = self.resolve_path_in_type_ns(db, path)?;
|
|
if unresolved.is_some() {
|
|
return None;
|
|
}
|
|
Some(res)
|
|
}
|
|
|
|
pub fn resolve_visibility(
|
|
&self,
|
|
db: &dyn DefDatabase,
|
|
visibility: &RawVisibility,
|
|
) -> Option<Visibility> {
|
|
match visibility {
|
|
RawVisibility::Module(_, _) => {
|
|
let (item_map, item_local_map, module) = self.item_scope_();
|
|
item_map.resolve_visibility(
|
|
item_local_map,
|
|
db,
|
|
module,
|
|
visibility,
|
|
self.scopes().any(|scope| {
|
|
matches!(scope, Scope::GenericParams { def: GenericDefId::ImplId(_), .. })
|
|
}),
|
|
)
|
|
}
|
|
RawVisibility::Public => Some(Visibility::Public),
|
|
}
|
|
}
|
|
|
|
pub fn resolve_path_in_value_ns(
|
|
&self,
|
|
db: &dyn DefDatabase,
|
|
path: &Path,
|
|
hygiene_id: HygieneId,
|
|
) -> Option<ResolveValueResult> {
|
|
self.resolve_path_in_value_ns_with_prefix_info(db, path, hygiene_id).map(|(it, _)| it)
|
|
}
|
|
|
|
pub fn resolve_path_in_value_ns_with_prefix_info(
|
|
&self,
|
|
db: &dyn DefDatabase,
|
|
path: &Path,
|
|
mut hygiene_id: HygieneId,
|
|
) -> Option<(ResolveValueResult, ResolvePathResultPrefixInfo)> {
|
|
let path = match path {
|
|
Path::BarePath(mod_path) => mod_path,
|
|
Path::Normal(it) => &it.mod_path,
|
|
Path::LangItem(l, None) => {
|
|
return Some((
|
|
ResolveValueResult::ValueNs(
|
|
match *l {
|
|
LangItemTarget::Function(it) => ValueNs::FunctionId(it),
|
|
LangItemTarget::Static(it) => ValueNs::StaticId(it),
|
|
LangItemTarget::Struct(it) => ValueNs::StructId(it),
|
|
LangItemTarget::EnumVariant(it) => ValueNs::EnumVariantId(it),
|
|
LangItemTarget::Union(_)
|
|
| LangItemTarget::ImplDef(_)
|
|
| LangItemTarget::TypeAlias(_)
|
|
| LangItemTarget::Trait(_)
|
|
| LangItemTarget::EnumId(_) => return None,
|
|
},
|
|
None,
|
|
),
|
|
ResolvePathResultPrefixInfo::default(),
|
|
));
|
|
}
|
|
Path::LangItem(l, Some(_)) => {
|
|
let type_ns = match *l {
|
|
LangItemTarget::Union(it) => TypeNs::AdtId(it.into()),
|
|
LangItemTarget::TypeAlias(it) => TypeNs::TypeAliasId(it),
|
|
LangItemTarget::Struct(it) => TypeNs::AdtId(it.into()),
|
|
LangItemTarget::EnumVariant(it) => TypeNs::EnumVariantId(it),
|
|
LangItemTarget::EnumId(it) => TypeNs::AdtId(it.into()),
|
|
LangItemTarget::Trait(it) => TypeNs::TraitId(it),
|
|
LangItemTarget::Function(_)
|
|
| LangItemTarget::ImplDef(_)
|
|
| LangItemTarget::Static(_) => return None,
|
|
};
|
|
// Remaining segments start from 0 because lang paths have no segments other than the remaining.
|
|
return Some((
|
|
ResolveValueResult::Partial(type_ns, 0, None),
|
|
ResolvePathResultPrefixInfo::default(),
|
|
));
|
|
}
|
|
};
|
|
let n_segments = path.segments().len();
|
|
let tmp = Name::new_symbol_root(sym::self_);
|
|
let first_name = if path.is_self() { &tmp } else { path.segments().first()? };
|
|
let skip_to_mod = path.kind != PathKind::Plain && !path.is_self();
|
|
if skip_to_mod {
|
|
return self.module_scope.resolve_path_in_value_ns(db, path);
|
|
}
|
|
|
|
if n_segments <= 1 {
|
|
let mut hygiene_info = hygiene_info(db, hygiene_id);
|
|
for scope in self.scopes() {
|
|
match scope {
|
|
Scope::ExprScope(scope) => {
|
|
let entry =
|
|
scope.expr_scopes.entries(scope.scope_id).iter().find(|entry| {
|
|
entry.name() == first_name && entry.hygiene() == hygiene_id
|
|
});
|
|
|
|
if let Some(e) = entry {
|
|
return Some((
|
|
ResolveValueResult::ValueNs(
|
|
ValueNs::LocalBinding(e.binding()),
|
|
None,
|
|
),
|
|
ResolvePathResultPrefixInfo::default(),
|
|
));
|
|
}
|
|
}
|
|
Scope::MacroDefScope(macro_id) => {
|
|
handle_macro_def_scope(db, &mut hygiene_id, &mut hygiene_info, macro_id)
|
|
}
|
|
Scope::GenericParams { params, def } => {
|
|
if let &GenericDefId::ImplId(impl_) = def {
|
|
if *first_name == sym::Self_ {
|
|
return Some((
|
|
ResolveValueResult::ValueNs(ValueNs::ImplSelf(impl_), None),
|
|
ResolvePathResultPrefixInfo::default(),
|
|
));
|
|
}
|
|
}
|
|
if let Some(id) = params.find_const_by_name(first_name, *def) {
|
|
let val = ValueNs::GenericParam(id);
|
|
return Some((
|
|
ResolveValueResult::ValueNs(val, None),
|
|
ResolvePathResultPrefixInfo::default(),
|
|
));
|
|
}
|
|
}
|
|
Scope::BlockScope(m) => {
|
|
if let Some(def) = m.resolve_path_in_value_ns(db, path) {
|
|
return Some(def);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
for scope in self.scopes() {
|
|
match scope {
|
|
Scope::ExprScope(_) | Scope::MacroDefScope(_) => continue,
|
|
Scope::GenericParams { params, def } => {
|
|
if let &GenericDefId::ImplId(impl_) = def {
|
|
if *first_name == sym::Self_ {
|
|
return Some((
|
|
ResolveValueResult::Partial(TypeNs::SelfType(impl_), 1, None),
|
|
ResolvePathResultPrefixInfo::default(),
|
|
));
|
|
}
|
|
} else if let &GenericDefId::AdtId(adt) = def {
|
|
if *first_name == sym::Self_ {
|
|
let ty = TypeNs::AdtSelfType(adt);
|
|
return Some((
|
|
ResolveValueResult::Partial(ty, 1, None),
|
|
ResolvePathResultPrefixInfo::default(),
|
|
));
|
|
}
|
|
}
|
|
if let Some(id) = params.find_type_by_name(first_name, *def) {
|
|
let ty = TypeNs::GenericParam(id);
|
|
return Some((
|
|
ResolveValueResult::Partial(ty, 1, None),
|
|
ResolvePathResultPrefixInfo::default(),
|
|
));
|
|
}
|
|
}
|
|
Scope::BlockScope(m) => {
|
|
if let Some(def) = m.resolve_path_in_value_ns(db, path) {
|
|
return Some(def);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if let Some(res) = self.module_scope.resolve_path_in_value_ns(db, path) {
|
|
return Some(res);
|
|
}
|
|
|
|
// If a path of the shape `u16::from_le_bytes` failed to resolve at all, then we fall back
|
|
// to resolving to the primitive type, to allow this to still work in the presence of
|
|
// `use core::u16;`.
|
|
if path.kind == PathKind::Plain && n_segments > 1 {
|
|
if let Some(builtin) = BuiltinType::by_name(first_name) {
|
|
return Some((
|
|
ResolveValueResult::Partial(TypeNs::BuiltinType(builtin), 1, None),
|
|
ResolvePathResultPrefixInfo::default(),
|
|
));
|
|
}
|
|
}
|
|
|
|
None
|
|
}
|
|
|
|
pub fn resolve_path_in_value_ns_fully(
|
|
&self,
|
|
db: &dyn DefDatabase,
|
|
path: &Path,
|
|
hygiene: HygieneId,
|
|
) -> Option<ValueNs> {
|
|
match self.resolve_path_in_value_ns(db, path, hygiene)? {
|
|
ResolveValueResult::ValueNs(it, _) => Some(it),
|
|
ResolveValueResult::Partial(..) => None,
|
|
}
|
|
}
|
|
|
|
pub fn resolve_path_as_macro(
|
|
&self,
|
|
db: &dyn DefDatabase,
|
|
path: &ModPath,
|
|
expected_macro_kind: Option<MacroSubNs>,
|
|
) -> Option<(MacroId, Option<ImportOrExternCrate>)> {
|
|
let (item_map, item_local_map, module) = self.item_scope_();
|
|
item_map
|
|
.resolve_path(
|
|
item_local_map,
|
|
db,
|
|
module,
|
|
path,
|
|
BuiltinShadowMode::Other,
|
|
expected_macro_kind,
|
|
)
|
|
.0
|
|
.take_macros_import()
|
|
}
|
|
|
|
pub fn resolve_path_as_macro_def(
|
|
&self,
|
|
db: &dyn DefDatabase,
|
|
path: &ModPath,
|
|
expected_macro_kind: Option<MacroSubNs>,
|
|
) -> Option<MacroDefId> {
|
|
self.resolve_path_as_macro(db, path, expected_macro_kind).map(|(it, _)| db.macro_def(it))
|
|
}
|
|
|
|
pub fn resolve_lifetime(&self, lifetime: &LifetimeRef) -> Option<LifetimeNs> {
|
|
match lifetime {
|
|
LifetimeRef::Static => Some(LifetimeNs::Static),
|
|
LifetimeRef::Named(name) => self.scopes().find_map(|scope| match scope {
|
|
Scope::GenericParams { def, params } => {
|
|
params.find_lifetime_by_name(name, *def).map(LifetimeNs::LifetimeParam)
|
|
}
|
|
_ => None,
|
|
}),
|
|
LifetimeRef::Placeholder | LifetimeRef::Error => None,
|
|
LifetimeRef::Param(lifetime_param_id) => {
|
|
Some(LifetimeNs::LifetimeParam(*lifetime_param_id))
|
|
}
|
|
}
|
|
}
|
|
|
|
/// Returns a set of names available in the current scope.
|
|
///
|
|
/// Note that this is a somewhat fuzzy concept -- internally, the compiler
|
|
/// doesn't necessary follow a strict scoping discipline. Rather, it just
|
|
/// tells for each ident what it resolves to.
|
|
///
|
|
/// A good example is something like `str::from_utf8`. From scopes point of
|
|
/// view, this code is erroneous -- both `str` module and `str` type occupy
|
|
/// the same type namespace.
|
|
///
|
|
/// We don't try to model that super-correctly -- this functionality is
|
|
/// primarily exposed for completions.
|
|
///
|
|
/// Note that in Rust one name can be bound to several items:
|
|
///
|
|
/// ```
|
|
/// # #![allow(non_camel_case_types)]
|
|
/// macro_rules! t { () => (()) }
|
|
/// type t = t!();
|
|
/// const t: t = t!();
|
|
/// ```
|
|
///
|
|
/// That's why we return a multimap.
|
|
///
|
|
/// The shadowing is accounted for: in
|
|
///
|
|
/// ```ignore
|
|
/// let it = 92;
|
|
/// {
|
|
/// let it = 92;
|
|
/// $0
|
|
/// }
|
|
/// ```
|
|
///
|
|
/// there will be only one entry for `it` in the result.
|
|
///
|
|
/// The result is ordered *roughly* from the innermost scope to the
|
|
/// outermost: when the name is introduced in two namespaces in two scopes,
|
|
/// we use the position of the first scope.
|
|
pub fn names_in_scope(
|
|
&self,
|
|
db: &dyn DefDatabase,
|
|
) -> FxIndexMap<Name, SmallVec<[ScopeDef; 1]>> {
|
|
let mut res = ScopeNames::default();
|
|
for scope in self.scopes() {
|
|
scope.process_names(&mut res, db);
|
|
}
|
|
let ModuleItemMap { def_map, module_id, local_def_map } = self.module_scope;
|
|
// FIXME: should we provide `self` here?
|
|
// f(
|
|
// Name::self_param(),
|
|
// PerNs::types(Resolution::Def {
|
|
// def: m.module.into(),
|
|
// }),
|
|
// );
|
|
def_map[module_id].scope.entries().for_each(|(name, def)| {
|
|
res.add_per_ns(name, def);
|
|
});
|
|
|
|
def_map[module_id].scope.legacy_macros().for_each(|(name, macs)| {
|
|
macs.iter().for_each(|&mac| {
|
|
res.add(name, ScopeDef::ModuleDef(ModuleDefId::MacroId(mac)));
|
|
})
|
|
});
|
|
def_map.macro_use_prelude().iter().sorted_by_key(|&(k, _)| k.clone()).for_each(
|
|
|(name, &(def, _extern_crate))| {
|
|
res.add(name, ScopeDef::ModuleDef(def.into()));
|
|
},
|
|
);
|
|
local_def_map.extern_prelude().for_each(|(name, (def, _extern_crate))| {
|
|
res.add(name, ScopeDef::ModuleDef(ModuleDefId::ModuleId(def.into())));
|
|
});
|
|
BUILTIN_SCOPE.iter().for_each(|(name, &def)| {
|
|
res.add_per_ns(name, def);
|
|
});
|
|
if let Some((prelude, _use)) = def_map.prelude() {
|
|
let prelude_def_map = prelude.def_map(db);
|
|
for (name, def) in prelude_def_map[prelude.local_id].scope.entries() {
|
|
res.add_per_ns(name, def)
|
|
}
|
|
}
|
|
res.map
|
|
}
|
|
|
|
/// Note: Not to be used directly within hir-def/hir-ty
|
|
pub fn extern_crate_decls_in_scope<'a>(
|
|
&'a self,
|
|
db: &'a dyn DefDatabase,
|
|
) -> impl Iterator<Item = Name> + 'a {
|
|
self.module_scope.def_map[self.module_scope.module_id]
|
|
.scope
|
|
.extern_crate_decls()
|
|
.filter_map(|id| {
|
|
let loc = id.lookup(db);
|
|
let tree = loc.item_tree_id().item_tree(db);
|
|
match &tree[loc.id.value].alias {
|
|
Some(alias) => match alias {
|
|
ImportAlias::Underscore => None,
|
|
ImportAlias::Alias(name) => Some(name.clone()),
|
|
},
|
|
None => Some(tree[loc.id.value].name.clone()),
|
|
}
|
|
})
|
|
}
|
|
|
|
pub fn extern_crates_in_scope(&self) -> impl Iterator<Item = (Name, ModuleId)> + '_ {
|
|
self.module_scope
|
|
.local_def_map
|
|
.extern_prelude()
|
|
.map(|(name, module_id)| (name.clone(), module_id.0.into()))
|
|
}
|
|
|
|
pub fn traits_in_scope(&self, db: &dyn DefDatabase) -> FxHashSet<TraitId> {
|
|
// FIXME(trait_alias): Trait alias brings aliased traits in scope! Note that supertraits of
|
|
// aliased traits are NOT brought in scope (unless also aliased).
|
|
let mut traits = FxHashSet::default();
|
|
|
|
for scope in self.scopes() {
|
|
match scope {
|
|
Scope::BlockScope(m) => traits.extend(m.def_map[m.module_id].scope.traits()),
|
|
&Scope::GenericParams { def: GenericDefId::ImplId(impl_), .. } => {
|
|
let impl_data = db.impl_signature(impl_);
|
|
if let Some(target_trait) = impl_data.target_trait {
|
|
if let Some(TypeNs::TraitId(trait_)) = self
|
|
.resolve_path_in_type_ns_fully(db, &impl_data.store[target_trait.path])
|
|
{
|
|
traits.insert(trait_);
|
|
}
|
|
}
|
|
}
|
|
_ => (),
|
|
}
|
|
}
|
|
|
|
// Fill in the prelude traits
|
|
if let Some((prelude, _use)) = self.module_scope.def_map.prelude() {
|
|
let prelude_def_map = prelude.def_map(db);
|
|
traits.extend(prelude_def_map[prelude.local_id].scope.traits());
|
|
}
|
|
// Fill in module visible traits
|
|
traits.extend(self.module_scope.def_map[self.module_scope.module_id].scope.traits());
|
|
traits
|
|
}
|
|
|
|
pub fn traits_in_scope_from_block_scopes(&self) -> impl Iterator<Item = TraitId> + '_ {
|
|
self.scopes()
|
|
.filter_map(|scope| match scope {
|
|
Scope::BlockScope(m) => Some(m.def_map[m.module_id].scope.traits()),
|
|
_ => None,
|
|
})
|
|
.flatten()
|
|
}
|
|
|
|
pub fn module(&self) -> ModuleId {
|
|
let (def_map, _, local_id) = self.item_scope_();
|
|
def_map.module_id(local_id)
|
|
}
|
|
|
|
pub fn item_scope(&self) -> &ItemScope {
|
|
let (def_map, _, local_id) = self.item_scope_();
|
|
&def_map[local_id].scope
|
|
}
|
|
|
|
pub fn krate(&self) -> Crate {
|
|
self.module_scope.def_map.krate()
|
|
}
|
|
|
|
pub fn def_map(&self) -> &DefMap {
|
|
self.item_scope_().0
|
|
}
|
|
|
|
pub fn generic_def(&self) -> Option<GenericDefId> {
|
|
self.scopes().find_map(|scope| match scope {
|
|
Scope::GenericParams { def, .. } => Some(*def),
|
|
_ => None,
|
|
})
|
|
}
|
|
|
|
pub fn generic_params(&self) -> Option<&GenericParams> {
|
|
self.scopes().find_map(|scope| match scope {
|
|
Scope::GenericParams { params, .. } => Some(&**params),
|
|
_ => None,
|
|
})
|
|
}
|
|
|
|
pub fn all_generic_params(&self) -> impl Iterator<Item = (&GenericParams, &GenericDefId)> {
|
|
self.scopes().filter_map(|scope| match scope {
|
|
Scope::GenericParams { params, def } => Some((&**params, def)),
|
|
_ => None,
|
|
})
|
|
}
|
|
|
|
pub fn body_owner(&self) -> Option<DefWithBodyId> {
|
|
self.scopes().find_map(|scope| match scope {
|
|
Scope::ExprScope(it) => Some(it.owner),
|
|
_ => None,
|
|
})
|
|
}
|
|
|
|
pub fn impl_def(&self) -> Option<ImplId> {
|
|
self.scopes().find_map(|scope| match scope {
|
|
&Scope::GenericParams { def: GenericDefId::ImplId(def), .. } => Some(def),
|
|
_ => None,
|
|
})
|
|
}
|
|
|
|
/// Checks if we rename `renamed` (currently named `current_name`) to `new_name`, will the meaning of this reference
|
|
/// (that contains `current_name` path) change from `renamed` to some another variable (returned as `Some`).
|
|
pub fn rename_will_conflict_with_another_variable(
|
|
&self,
|
|
db: &dyn DefDatabase,
|
|
current_name: &Name,
|
|
current_name_as_path: &ModPath,
|
|
mut hygiene_id: HygieneId,
|
|
new_name: &Symbol,
|
|
to_be_renamed: BindingId,
|
|
) -> Option<BindingId> {
|
|
let mut hygiene_info = hygiene_info(db, hygiene_id);
|
|
let mut will_be_resolved_to = None;
|
|
for scope in self.scopes() {
|
|
match scope {
|
|
Scope::ExprScope(scope) => {
|
|
for entry in scope.expr_scopes.entries(scope.scope_id) {
|
|
if entry.hygiene() == hygiene_id {
|
|
if entry.binding() == to_be_renamed {
|
|
// This currently resolves to our renamed variable, now `will_be_resolved_to`
|
|
// contains `Some` if the meaning will change or `None` if not.
|
|
return will_be_resolved_to;
|
|
} else if entry.name().symbol() == new_name {
|
|
will_be_resolved_to = Some(entry.binding());
|
|
}
|
|
}
|
|
}
|
|
}
|
|
Scope::MacroDefScope(macro_id) => {
|
|
handle_macro_def_scope(db, &mut hygiene_id, &mut hygiene_info, macro_id)
|
|
}
|
|
Scope::GenericParams { params, def } => {
|
|
if params.find_const_by_name(current_name, *def).is_some() {
|
|
// It does not resolve to our renamed variable.
|
|
return None;
|
|
}
|
|
}
|
|
Scope::BlockScope(m) => {
|
|
if m.resolve_path_in_value_ns(db, current_name_as_path).is_some() {
|
|
// It does not resolve to our renamed variable.
|
|
return None;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
// It does not resolve to our renamed variable.
|
|
None
|
|
}
|
|
|
|
/// Checks if we rename `renamed` to `name`, will the meaning of this reference (that contains `name` path) change
|
|
/// from some other variable (returned as `Some`) to `renamed`.
|
|
pub fn rename_will_conflict_with_renamed(
|
|
&self,
|
|
db: &dyn DefDatabase,
|
|
name: &Name,
|
|
name_as_path: &ModPath,
|
|
mut hygiene_id: HygieneId,
|
|
to_be_renamed: BindingId,
|
|
) -> Option<BindingId> {
|
|
let mut hygiene_info = hygiene_info(db, hygiene_id);
|
|
let mut will_resolve_to_renamed = false;
|
|
for scope in self.scopes() {
|
|
match scope {
|
|
Scope::ExprScope(scope) => {
|
|
for entry in scope.expr_scopes.entries(scope.scope_id) {
|
|
if entry.binding() == to_be_renamed {
|
|
will_resolve_to_renamed = true;
|
|
} else if entry.hygiene() == hygiene_id && entry.name() == name {
|
|
if will_resolve_to_renamed {
|
|
// This will resolve to the renamed variable before it resolves to the original variable.
|
|
return Some(entry.binding());
|
|
} else {
|
|
// This will resolve to the original variable.
|
|
return None;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
Scope::MacroDefScope(macro_id) => {
|
|
handle_macro_def_scope(db, &mut hygiene_id, &mut hygiene_info, macro_id)
|
|
}
|
|
Scope::GenericParams { params, def } => {
|
|
if params.find_const_by_name(name, *def).is_some() {
|
|
// Here and below, it might actually resolve to our renamed variable - in which case it'll
|
|
// hide the generic parameter or some other thing (not a variable). We don't check for that
|
|
// because due to naming conventions, it is rare that variable will shadow a non-variable.
|
|
return None;
|
|
}
|
|
}
|
|
Scope::BlockScope(m) => {
|
|
if m.resolve_path_in_value_ns(db, name_as_path).is_some() {
|
|
return None;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
None
|
|
}
|
|
|
|
/// `expr_id` is required to be an expression id that comes after the top level expression scope in the given resolver
|
|
#[must_use]
|
|
pub fn update_to_inner_scope(
|
|
&mut self,
|
|
db: &'db dyn DefDatabase,
|
|
owner: DefWithBodyId,
|
|
expr_id: ExprId,
|
|
) -> UpdateGuard {
|
|
#[inline(always)]
|
|
fn append_expr_scope<'db>(
|
|
db: &'db dyn DefDatabase,
|
|
resolver: &mut Resolver<'db>,
|
|
owner: DefWithBodyId,
|
|
expr_scopes: &Arc<ExprScopes>,
|
|
scope_id: ScopeId,
|
|
) {
|
|
if let Some(macro_id) = expr_scopes.macro_def(scope_id) {
|
|
resolver.scopes.push(Scope::MacroDefScope(**macro_id));
|
|
}
|
|
resolver.scopes.push(Scope::ExprScope(ExprScope {
|
|
owner,
|
|
expr_scopes: expr_scopes.clone(),
|
|
scope_id,
|
|
}));
|
|
if let Some(block) = expr_scopes.block(scope_id) {
|
|
let def_map = block_def_map(db, block);
|
|
let local_def_map = block.lookup(db).module.only_local_def_map(db);
|
|
resolver.scopes.push(Scope::BlockScope(ModuleItemMap {
|
|
def_map,
|
|
local_def_map,
|
|
module_id: DefMap::ROOT,
|
|
}));
|
|
// FIXME: This adds as many module scopes as there are blocks, but resolving in each
|
|
// already traverses all parents, so this is O(n²). I think we could only store the
|
|
// innermost module scope instead?
|
|
}
|
|
}
|
|
|
|
let start = self.scopes.len();
|
|
let innermost_scope = self.scopes().find(|scope| !matches!(scope, Scope::MacroDefScope(_)));
|
|
match innermost_scope {
|
|
Some(&Scope::ExprScope(ExprScope { scope_id, ref expr_scopes, owner })) => {
|
|
let expr_scopes = expr_scopes.clone();
|
|
let scope_chain = expr_scopes
|
|
.scope_chain(expr_scopes.scope_for(expr_id))
|
|
.take_while(|&it| it != scope_id);
|
|
for scope_id in scope_chain {
|
|
append_expr_scope(db, self, owner, &expr_scopes, scope_id);
|
|
}
|
|
}
|
|
_ => {
|
|
let expr_scopes = db.expr_scopes(owner);
|
|
let scope_chain = expr_scopes.scope_chain(expr_scopes.scope_for(expr_id));
|
|
|
|
for scope_id in scope_chain {
|
|
append_expr_scope(db, self, owner, &expr_scopes, scope_id);
|
|
}
|
|
}
|
|
}
|
|
self.scopes[start..].reverse();
|
|
UpdateGuard(start)
|
|
}
|
|
|
|
pub fn reset_to_guard(&mut self, UpdateGuard(start): UpdateGuard) {
|
|
self.scopes.truncate(start);
|
|
}
|
|
}
|
|
|
|
#[inline]
|
|
fn handle_macro_def_scope(
|
|
db: &dyn DefDatabase,
|
|
hygiene_id: &mut HygieneId,
|
|
hygiene_info: &mut Option<(SyntaxContext, MacroDefId)>,
|
|
macro_id: &MacroDefId,
|
|
) {
|
|
if let Some((parent_ctx, label_macro_id)) = hygiene_info {
|
|
if label_macro_id == macro_id {
|
|
// A macro is allowed to refer to variables from before its declaration.
|
|
// Therefore, if we got to the rib of its declaration, give up its hygiene
|
|
// and use its parent expansion.
|
|
*hygiene_id = HygieneId::new(parent_ctx.opaque_and_semitransparent(db));
|
|
*hygiene_info = parent_ctx.outer_expn(db).map(|expansion| {
|
|
let expansion = db.lookup_intern_macro_call(expansion.into());
|
|
(parent_ctx.parent(db), expansion.def)
|
|
});
|
|
}
|
|
}
|
|
}
|
|
|
|
#[inline]
|
|
fn hygiene_info(
|
|
db: &dyn DefDatabase,
|
|
hygiene_id: HygieneId,
|
|
) -> Option<(SyntaxContext, MacroDefId)> {
|
|
if !hygiene_id.is_root() {
|
|
let ctx = hygiene_id.lookup();
|
|
ctx.outer_expn(db).map(|expansion| {
|
|
let expansion = db.lookup_intern_macro_call(expansion.into());
|
|
(ctx.parent(db), expansion.def)
|
|
})
|
|
} else {
|
|
None
|
|
}
|
|
}
|
|
|
|
pub struct UpdateGuard(usize);
|
|
|
|
impl<'db> Resolver<'db> {
|
|
fn scopes(&self) -> impl Iterator<Item = &Scope<'db>> {
|
|
self.scopes.iter().rev()
|
|
}
|
|
|
|
fn resolve_module_path(
|
|
&self,
|
|
db: &dyn DefDatabase,
|
|
path: &ModPath,
|
|
shadow: BuiltinShadowMode,
|
|
) -> PerNs {
|
|
let (item_map, item_local_map, module) = self.item_scope_();
|
|
// This method resolves `path` just like import paths, so no expected macro subns is given.
|
|
let (module_res, segment_index) =
|
|
item_map.resolve_path(item_local_map, db, module, path, shadow, None);
|
|
if segment_index.is_some() {
|
|
return PerNs::none();
|
|
}
|
|
module_res
|
|
}
|
|
|
|
/// The innermost block scope that contains items or the module scope that contains this resolver.
|
|
fn item_scope_(&self) -> (&DefMap, &LocalDefMap, LocalModuleId) {
|
|
self.scopes()
|
|
.find_map(|scope| match scope {
|
|
Scope::BlockScope(m) => Some((m.def_map, m.local_def_map, m.module_id)),
|
|
_ => None,
|
|
})
|
|
.unwrap_or((
|
|
self.module_scope.def_map,
|
|
self.module_scope.local_def_map,
|
|
self.module_scope.module_id,
|
|
))
|
|
}
|
|
}
|
|
|
|
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
|
|
pub enum ScopeDef {
|
|
ModuleDef(ModuleDefId),
|
|
Unknown,
|
|
ImplSelfType(ImplId),
|
|
AdtSelfType(AdtId),
|
|
GenericParam(GenericParamId),
|
|
Local(BindingId),
|
|
Label(LabelId),
|
|
}
|
|
|
|
impl<'db> Scope<'db> {
|
|
fn process_names(&self, acc: &mut ScopeNames, db: &'db dyn DefDatabase) {
|
|
match self {
|
|
Scope::BlockScope(m) => {
|
|
m.def_map[m.module_id].scope.entries().for_each(|(name, def)| {
|
|
acc.add_per_ns(name, def);
|
|
});
|
|
m.def_map[m.module_id].scope.legacy_macros().for_each(|(name, macs)| {
|
|
macs.iter().for_each(|&mac| {
|
|
acc.add(name, ScopeDef::ModuleDef(ModuleDefId::MacroId(mac)));
|
|
})
|
|
});
|
|
}
|
|
&Scope::GenericParams { ref params, def: parent } => {
|
|
if let GenericDefId::ImplId(impl_) = parent {
|
|
acc.add(&Name::new_symbol_root(sym::Self_), ScopeDef::ImplSelfType(impl_));
|
|
} else if let GenericDefId::AdtId(adt) = parent {
|
|
acc.add(&Name::new_symbol_root(sym::Self_), ScopeDef::AdtSelfType(adt));
|
|
}
|
|
|
|
for (local_id, param) in params.iter_type_or_consts() {
|
|
if let Some(name) = ¶m.name() {
|
|
let id = TypeOrConstParamId { parent, local_id };
|
|
let data = &db.generic_params(parent)[local_id];
|
|
acc.add(
|
|
name,
|
|
ScopeDef::GenericParam(match data {
|
|
TypeOrConstParamData::TypeParamData(_) => {
|
|
GenericParamId::TypeParamId(TypeParamId::from_unchecked(id))
|
|
}
|
|
TypeOrConstParamData::ConstParamData(_) => {
|
|
GenericParamId::ConstParamId(ConstParamId::from_unchecked(id))
|
|
}
|
|
}),
|
|
);
|
|
}
|
|
}
|
|
for (local_id, param) in params.iter_lt() {
|
|
let id = LifetimeParamId { parent, local_id };
|
|
acc.add(¶m.name, ScopeDef::GenericParam(id.into()))
|
|
}
|
|
}
|
|
Scope::ExprScope(scope) => {
|
|
if let Some((label, name)) = scope.expr_scopes.label(scope.scope_id) {
|
|
acc.add(&name, ScopeDef::Label(label))
|
|
}
|
|
scope.expr_scopes.entries(scope.scope_id).iter().for_each(|e| {
|
|
acc.add_local(e.name(), e.binding());
|
|
});
|
|
}
|
|
Scope::MacroDefScope(_) => {}
|
|
}
|
|
}
|
|
}
|
|
|
|
pub fn resolver_for_expr(
|
|
db: &dyn DefDatabase,
|
|
owner: DefWithBodyId,
|
|
expr_id: ExprId,
|
|
) -> Resolver<'_> {
|
|
let r = owner.resolver(db);
|
|
let scopes = db.expr_scopes(owner);
|
|
let scope_id = scopes.scope_for(expr_id);
|
|
resolver_for_scope_(db, scopes, scope_id, r, owner)
|
|
}
|
|
|
|
pub fn resolver_for_scope(
|
|
db: &dyn DefDatabase,
|
|
owner: DefWithBodyId,
|
|
scope_id: Option<ScopeId>,
|
|
) -> Resolver<'_> {
|
|
let r = owner.resolver(db);
|
|
let scopes = db.expr_scopes(owner);
|
|
resolver_for_scope_(db, scopes, scope_id, r, owner)
|
|
}
|
|
|
|
fn resolver_for_scope_<'db>(
|
|
db: &'db dyn DefDatabase,
|
|
scopes: Arc<ExprScopes>,
|
|
scope_id: Option<ScopeId>,
|
|
mut r: Resolver<'db>,
|
|
owner: DefWithBodyId,
|
|
) -> Resolver<'db> {
|
|
let scope_chain = scopes.scope_chain(scope_id).collect::<Vec<_>>();
|
|
r.scopes.reserve(scope_chain.len());
|
|
|
|
for scope in scope_chain.into_iter().rev() {
|
|
if let Some(block) = scopes.block(scope) {
|
|
let def_map = block_def_map(db, block);
|
|
let local_def_map = block.lookup(db).module.only_local_def_map(db);
|
|
r = r.push_block_scope(def_map, local_def_map);
|
|
// FIXME: This adds as many module scopes as there are blocks, but resolving in each
|
|
// already traverses all parents, so this is O(n²). I think we could only store the
|
|
// innermost module scope instead?
|
|
}
|
|
if let Some(macro_id) = scopes.macro_def(scope) {
|
|
r = r.push_scope(Scope::MacroDefScope(**macro_id));
|
|
}
|
|
|
|
r = r.push_expr_scope(owner, Arc::clone(&scopes), scope);
|
|
}
|
|
r
|
|
}
|
|
|
|
impl<'db> Resolver<'db> {
|
|
fn push_scope(mut self, scope: Scope<'db>) -> Resolver<'db> {
|
|
self.scopes.push(scope);
|
|
self
|
|
}
|
|
|
|
fn push_generic_params_scope(
|
|
self,
|
|
db: &'db dyn DefDatabase,
|
|
def: GenericDefId,
|
|
) -> Resolver<'db> {
|
|
let params = db.generic_params(def);
|
|
self.push_scope(Scope::GenericParams { def, params })
|
|
}
|
|
|
|
fn push_block_scope(
|
|
self,
|
|
def_map: &'db DefMap,
|
|
local_def_map: &'db LocalDefMap,
|
|
) -> Resolver<'db> {
|
|
self.push_scope(Scope::BlockScope(ModuleItemMap {
|
|
def_map,
|
|
local_def_map,
|
|
module_id: DefMap::ROOT,
|
|
}))
|
|
}
|
|
|
|
fn push_expr_scope(
|
|
self,
|
|
owner: DefWithBodyId,
|
|
expr_scopes: Arc<ExprScopes>,
|
|
scope_id: ScopeId,
|
|
) -> Resolver<'db> {
|
|
self.push_scope(Scope::ExprScope(ExprScope { owner, expr_scopes, scope_id }))
|
|
}
|
|
}
|
|
|
|
impl<'db> ModuleItemMap<'db> {
|
|
fn resolve_path_in_value_ns(
|
|
&self,
|
|
db: &'db dyn DefDatabase,
|
|
path: &ModPath,
|
|
) -> Option<(ResolveValueResult, ResolvePathResultPrefixInfo)> {
|
|
let (module_def, unresolved_idx, prefix_info) = self.def_map.resolve_path_locally(
|
|
self.local_def_map,
|
|
db,
|
|
self.module_id,
|
|
path,
|
|
BuiltinShadowMode::Other,
|
|
);
|
|
match unresolved_idx {
|
|
None => {
|
|
let (value, import) = to_value_ns(module_def)?;
|
|
Some((ResolveValueResult::ValueNs(value, import), prefix_info))
|
|
}
|
|
Some(unresolved_idx) => {
|
|
let def = module_def.take_types_full()?;
|
|
let ty = match def.def {
|
|
ModuleDefId::AdtId(it) => TypeNs::AdtId(it),
|
|
ModuleDefId::TraitId(it) => TypeNs::TraitId(it),
|
|
ModuleDefId::TraitAliasId(it) => TypeNs::TraitAliasId(it),
|
|
ModuleDefId::TypeAliasId(it) => TypeNs::TypeAliasId(it),
|
|
ModuleDefId::BuiltinType(it) => TypeNs::BuiltinType(it),
|
|
|
|
ModuleDefId::ModuleId(_)
|
|
| ModuleDefId::FunctionId(_)
|
|
| ModuleDefId::EnumVariantId(_)
|
|
| ModuleDefId::ConstId(_)
|
|
| ModuleDefId::MacroId(_)
|
|
| ModuleDefId::StaticId(_) => return None,
|
|
};
|
|
Some((ResolveValueResult::Partial(ty, unresolved_idx, def.import), prefix_info))
|
|
}
|
|
}
|
|
}
|
|
|
|
fn resolve_path_in_type_ns(
|
|
&self,
|
|
db: &dyn DefDatabase,
|
|
path: &ModPath,
|
|
) -> Option<(TypeNs, Option<usize>, Option<ImportOrExternCrate>, ResolvePathResultPrefixInfo)>
|
|
{
|
|
let (module_def, idx, prefix_info) = self.def_map.resolve_path_locally(
|
|
self.local_def_map,
|
|
db,
|
|
self.module_id,
|
|
path,
|
|
BuiltinShadowMode::Other,
|
|
);
|
|
let (res, import) = to_type_ns(module_def)?;
|
|
Some((res, idx, import, prefix_info))
|
|
}
|
|
}
|
|
|
|
fn to_value_ns(per_ns: PerNs) -> Option<(ValueNs, Option<ImportOrGlob>)> {
|
|
let (def, import) = per_ns.take_values_import()?;
|
|
let res = match def {
|
|
ModuleDefId::FunctionId(it) => ValueNs::FunctionId(it),
|
|
ModuleDefId::AdtId(AdtId::StructId(it)) => ValueNs::StructId(it),
|
|
ModuleDefId::EnumVariantId(it) => ValueNs::EnumVariantId(it),
|
|
ModuleDefId::ConstId(it) => ValueNs::ConstId(it),
|
|
ModuleDefId::StaticId(it) => ValueNs::StaticId(it),
|
|
|
|
ModuleDefId::AdtId(AdtId::EnumId(_) | AdtId::UnionId(_))
|
|
| ModuleDefId::TraitId(_)
|
|
| ModuleDefId::TraitAliasId(_)
|
|
| ModuleDefId::TypeAliasId(_)
|
|
| ModuleDefId::BuiltinType(_)
|
|
| ModuleDefId::MacroId(_)
|
|
| ModuleDefId::ModuleId(_) => return None,
|
|
};
|
|
Some((res, import))
|
|
}
|
|
|
|
fn to_type_ns(per_ns: PerNs) -> Option<(TypeNs, Option<ImportOrExternCrate>)> {
|
|
let def = per_ns.take_types_full()?;
|
|
let res = match def.def {
|
|
ModuleDefId::AdtId(it) => TypeNs::AdtId(it),
|
|
ModuleDefId::EnumVariantId(it) => TypeNs::EnumVariantId(it),
|
|
|
|
ModuleDefId::TypeAliasId(it) => TypeNs::TypeAliasId(it),
|
|
ModuleDefId::BuiltinType(it) => TypeNs::BuiltinType(it),
|
|
|
|
ModuleDefId::TraitId(it) => TypeNs::TraitId(it),
|
|
ModuleDefId::TraitAliasId(it) => TypeNs::TraitAliasId(it),
|
|
|
|
ModuleDefId::ModuleId(it) => TypeNs::ModuleId(it),
|
|
|
|
ModuleDefId::FunctionId(_)
|
|
| ModuleDefId::ConstId(_)
|
|
| ModuleDefId::MacroId(_)
|
|
| ModuleDefId::StaticId(_) => return None,
|
|
};
|
|
Some((res, def.import))
|
|
}
|
|
|
|
#[derive(Default)]
|
|
struct ScopeNames {
|
|
map: FxIndexMap<Name, SmallVec<[ScopeDef; 1]>>,
|
|
}
|
|
|
|
impl ScopeNames {
|
|
fn add(&mut self, name: &Name, def: ScopeDef) {
|
|
let set = self.map.entry(name.clone()).or_default();
|
|
if !set.contains(&def) {
|
|
set.push(def)
|
|
}
|
|
}
|
|
fn add_per_ns(&mut self, name: &Name, def: PerNs) {
|
|
if let Some(ty) = &def.types {
|
|
self.add(name, ScopeDef::ModuleDef(ty.def))
|
|
}
|
|
if let Some(def) = &def.values {
|
|
self.add(name, ScopeDef::ModuleDef(def.def))
|
|
}
|
|
if let Some(mac) = &def.macros {
|
|
self.add(name, ScopeDef::ModuleDef(ModuleDefId::MacroId(mac.def)))
|
|
}
|
|
if def.is_none() {
|
|
self.add(name, ScopeDef::Unknown)
|
|
}
|
|
}
|
|
fn add_local(&mut self, name: &Name, binding: BindingId) {
|
|
let set = self.map.entry(name.clone()).or_default();
|
|
// XXX: hack, account for local (and only local) shadowing.
|
|
//
|
|
// This should be somewhat more principled and take namespaces into
|
|
// accounts, but, alas, scoping rules are a hoax. `str` type and `str`
|
|
// module can be both available in the same scope.
|
|
if set.iter().any(|it| matches!(it, &ScopeDef::Local(_))) {
|
|
cov_mark::hit!(shadowing_shows_single_completion);
|
|
return;
|
|
}
|
|
set.push(ScopeDef::Local(binding))
|
|
}
|
|
}
|
|
|
|
pub trait HasResolver: Copy {
|
|
/// Builds a resolver for type references inside this def.
|
|
fn resolver(self, db: &dyn DefDatabase) -> Resolver<'_>;
|
|
}
|
|
|
|
impl HasResolver for ModuleId {
|
|
fn resolver(self, db: &dyn DefDatabase) -> Resolver<'_> {
|
|
let (mut def_map, local_def_map) = self.local_def_map(db);
|
|
let mut module_id = self.local_id;
|
|
|
|
if !self.is_block_module() {
|
|
return Resolver {
|
|
scopes: vec![],
|
|
module_scope: ModuleItemMap { def_map, local_def_map, module_id },
|
|
};
|
|
}
|
|
|
|
let mut modules: SmallVec<[_; 1]> = smallvec![];
|
|
while let Some(parent) = def_map.parent() {
|
|
let block_def_map = mem::replace(&mut def_map, parent.def_map(db));
|
|
modules.push(block_def_map);
|
|
if !parent.is_block_module() {
|
|
module_id = parent.local_id;
|
|
break;
|
|
}
|
|
}
|
|
let mut resolver = Resolver {
|
|
scopes: Vec::with_capacity(modules.len()),
|
|
module_scope: ModuleItemMap { def_map, local_def_map, module_id },
|
|
};
|
|
for def_map in modules.into_iter().rev() {
|
|
resolver = resolver.push_block_scope(def_map, local_def_map);
|
|
}
|
|
resolver
|
|
}
|
|
}
|
|
|
|
impl HasResolver for CrateRootModuleId {
|
|
fn resolver(self, db: &dyn DefDatabase) -> Resolver<'_> {
|
|
let (def_map, local_def_map) = self.local_def_map(db);
|
|
Resolver {
|
|
scopes: vec![],
|
|
module_scope: ModuleItemMap { def_map, local_def_map, module_id: DefMap::ROOT },
|
|
}
|
|
}
|
|
}
|
|
|
|
impl HasResolver for TraitId {
|
|
fn resolver(self, db: &dyn DefDatabase) -> Resolver<'_> {
|
|
lookup_resolver(db, self).push_generic_params_scope(db, self.into())
|
|
}
|
|
}
|
|
|
|
impl HasResolver for TraitAliasId {
|
|
fn resolver(self, db: &dyn DefDatabase) -> Resolver<'_> {
|
|
lookup_resolver(db, self).push_generic_params_scope(db, self.into())
|
|
}
|
|
}
|
|
|
|
impl<T: Into<AdtId> + Copy> HasResolver for T {
|
|
fn resolver(self, db: &dyn DefDatabase) -> Resolver<'_> {
|
|
let def = self.into();
|
|
def.module(db).resolver(db).push_generic_params_scope(db, def.into())
|
|
}
|
|
}
|
|
|
|
impl HasResolver for FunctionId {
|
|
fn resolver(self, db: &dyn DefDatabase) -> Resolver<'_> {
|
|
lookup_resolver(db, self).push_generic_params_scope(db, self.into())
|
|
}
|
|
}
|
|
|
|
impl HasResolver for ConstId {
|
|
fn resolver(self, db: &dyn DefDatabase) -> Resolver<'_> {
|
|
lookup_resolver(db, self)
|
|
}
|
|
}
|
|
|
|
impl HasResolver for StaticId {
|
|
fn resolver(self, db: &dyn DefDatabase) -> Resolver<'_> {
|
|
lookup_resolver(db, self)
|
|
}
|
|
}
|
|
|
|
impl HasResolver for TypeAliasId {
|
|
fn resolver(self, db: &dyn DefDatabase) -> Resolver<'_> {
|
|
lookup_resolver(db, self).push_generic_params_scope(db, self.into())
|
|
}
|
|
}
|
|
|
|
impl HasResolver for ImplId {
|
|
fn resolver(self, db: &dyn DefDatabase) -> Resolver<'_> {
|
|
self.lookup(db).container.resolver(db).push_generic_params_scope(db, self.into())
|
|
}
|
|
}
|
|
|
|
impl HasResolver for ExternBlockId {
|
|
fn resolver(self, db: &dyn DefDatabase) -> Resolver<'_> {
|
|
// Same as parent's
|
|
lookup_resolver(db, self)
|
|
}
|
|
}
|
|
|
|
impl HasResolver for ExternCrateId {
|
|
fn resolver(self, db: &dyn DefDatabase) -> Resolver<'_> {
|
|
lookup_resolver(db, self)
|
|
}
|
|
}
|
|
|
|
impl HasResolver for UseId {
|
|
fn resolver(self, db: &dyn DefDatabase) -> Resolver<'_> {
|
|
lookup_resolver(db, self)
|
|
}
|
|
}
|
|
|
|
impl HasResolver for DefWithBodyId {
|
|
fn resolver(self, db: &dyn DefDatabase) -> Resolver<'_> {
|
|
match self {
|
|
DefWithBodyId::ConstId(c) => c.resolver(db),
|
|
DefWithBodyId::FunctionId(f) => f.resolver(db),
|
|
DefWithBodyId::StaticId(s) => s.resolver(db),
|
|
DefWithBodyId::VariantId(v) => v.resolver(db),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl HasResolver for ItemContainerId {
|
|
fn resolver(self, db: &dyn DefDatabase) -> Resolver<'_> {
|
|
match self {
|
|
ItemContainerId::ModuleId(it) => it.resolver(db),
|
|
ItemContainerId::TraitId(it) => it.resolver(db),
|
|
ItemContainerId::ImplId(it) => it.resolver(db),
|
|
ItemContainerId::ExternBlockId(it) => it.resolver(db),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl HasResolver for GenericDefId {
|
|
fn resolver(self, db: &dyn DefDatabase) -> Resolver<'_> {
|
|
match self {
|
|
GenericDefId::FunctionId(inner) => inner.resolver(db),
|
|
GenericDefId::AdtId(adt) => adt.resolver(db),
|
|
GenericDefId::TraitId(inner) => inner.resolver(db),
|
|
GenericDefId::TraitAliasId(inner) => inner.resolver(db),
|
|
GenericDefId::TypeAliasId(inner) => inner.resolver(db),
|
|
GenericDefId::ImplId(inner) => inner.resolver(db),
|
|
GenericDefId::ConstId(inner) => inner.resolver(db),
|
|
GenericDefId::StaticId(inner) => inner.resolver(db),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl HasResolver for EnumVariantId {
|
|
fn resolver(self, db: &dyn DefDatabase) -> Resolver<'_> {
|
|
self.lookup(db).parent.resolver(db)
|
|
}
|
|
}
|
|
|
|
impl HasResolver for VariantId {
|
|
fn resolver(self, db: &dyn DefDatabase) -> Resolver<'_> {
|
|
match self {
|
|
VariantId::EnumVariantId(it) => it.resolver(db),
|
|
VariantId::StructId(it) => it.resolver(db),
|
|
VariantId::UnionId(it) => it.resolver(db),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl HasResolver for MacroId {
|
|
fn resolver(self, db: &dyn DefDatabase) -> Resolver<'_> {
|
|
match self {
|
|
MacroId::Macro2Id(it) => it.resolver(db),
|
|
MacroId::MacroRulesId(it) => it.resolver(db),
|
|
MacroId::ProcMacroId(it) => it.resolver(db),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl HasResolver for Macro2Id {
|
|
fn resolver(self, db: &dyn DefDatabase) -> Resolver<'_> {
|
|
lookup_resolver(db, self)
|
|
}
|
|
}
|
|
|
|
impl HasResolver for ProcMacroId {
|
|
fn resolver(self, db: &dyn DefDatabase) -> Resolver<'_> {
|
|
lookup_resolver(db, self)
|
|
}
|
|
}
|
|
|
|
impl HasResolver for MacroRulesId {
|
|
fn resolver(self, db: &dyn DefDatabase) -> Resolver<'_> {
|
|
lookup_resolver(db, self)
|
|
}
|
|
}
|
|
|
|
fn lookup_resolver(
|
|
db: &dyn DefDatabase,
|
|
lookup: impl Lookup<
|
|
Database = dyn DefDatabase,
|
|
Data = impl ItemTreeLoc<Container = impl HasResolver>,
|
|
>,
|
|
) -> Resolver<'_> {
|
|
lookup.lookup(db).container().resolver(db)
|
|
}
|