mirror of
https://github.com/rust-lang/rust-analyzer.git
synced 2025-09-27 04:19:13 +00:00
Specify desirable namespace when calling resolve
That way, we are able to get rid of a number of unreachable statements
This commit is contained in:
parent
1adf0519bc
commit
51e2d76b98
13 changed files with 542 additions and 472 deletions
|
@ -1,7 +1,7 @@
|
|||
//! Name resolution.
|
||||
use std::sync::Arc;
|
||||
|
||||
use rustc_hash::{FxHashMap, FxHashSet};
|
||||
use rustc_hash::FxHashSet;
|
||||
|
||||
use crate::{
|
||||
code_model::Crate,
|
||||
|
@ -14,8 +14,9 @@ use crate::{
|
|||
impl_block::ImplBlock,
|
||||
name::{Name, SELF_PARAM, SELF_TYPE},
|
||||
nameres::{CrateDefMap, CrateModuleId, PerNs},
|
||||
path::Path,
|
||||
Adt, Enum, MacroDef, ModuleDef, Struct, Trait,
|
||||
path::{Path, PathKind},
|
||||
Adt, BuiltinType, Const, Enum, EnumVariant, Function, MacroDef, ModuleDef, Static, Struct,
|
||||
Trait, TypeAlias,
|
||||
};
|
||||
|
||||
#[derive(Debug, Clone, Default)]
|
||||
|
@ -36,69 +37,6 @@ pub(crate) struct ExprScope {
|
|||
scope_id: ScopeId,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub(crate) struct PathResult {
|
||||
/// The actual path resolution
|
||||
// FIXME: `PerNs<Resolution>` type doesn't make sense, as not every
|
||||
// Resolution variant can appear in every namespace
|
||||
resolution: PerNs<Resolution>,
|
||||
/// The first index in the path that we
|
||||
/// were unable to resolve.
|
||||
/// When path is fully resolved, this is 0.
|
||||
remaining_index: usize,
|
||||
}
|
||||
|
||||
impl PathResult {
|
||||
/// Returns the remaining index in the result
|
||||
/// returns None if the path was fully resolved
|
||||
pub(crate) fn remaining_index(&self) -> Option<usize> {
|
||||
if self.remaining_index > 0 {
|
||||
Some(self.remaining_index)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
/// Consumes `PathResult` and returns the contained `PerNs<Resolution>`
|
||||
/// if the path was fully resolved, meaning we have no remaining items
|
||||
pub(crate) fn into_fully_resolved(self) -> PerNs<Resolution> {
|
||||
if self.is_fully_resolved() {
|
||||
self.resolution
|
||||
} else {
|
||||
PerNs::none()
|
||||
}
|
||||
}
|
||||
|
||||
/// Consumes `PathResult` and returns the resolution and the
|
||||
/// remaining_index as a tuple.
|
||||
pub(crate) fn into_inner(self) -> (PerNs<Resolution>, Option<usize>) {
|
||||
let index = self.remaining_index();
|
||||
(self.resolution, index)
|
||||
}
|
||||
|
||||
/// Path is fully resolved when `remaining_index` is none
|
||||
/// and the resolution contains anything
|
||||
pub(crate) fn is_fully_resolved(&self) -> bool {
|
||||
!self.resolution.is_none() && self.remaining_index().is_none()
|
||||
}
|
||||
|
||||
fn empty() -> PathResult {
|
||||
PathResult { resolution: PerNs::none(), remaining_index: 0 }
|
||||
}
|
||||
|
||||
fn from_resolution(res: PerNs<Resolution>) -> PathResult {
|
||||
PathResult::from_resolution_with_index(res, 0)
|
||||
}
|
||||
|
||||
fn from_resolution_with_index(res: PerNs<Resolution>, remaining_index: usize) -> PathResult {
|
||||
if res.is_none() {
|
||||
PathResult::empty()
|
||||
} else {
|
||||
PathResult { resolution: res, remaining_index }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub(crate) enum Scope {
|
||||
/// All the items and imported names of a module
|
||||
|
@ -112,25 +50,41 @@ pub(crate) enum Scope {
|
|||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub enum Resolution {
|
||||
/// An item
|
||||
Def(ModuleDef),
|
||||
|
||||
// FIXME: there's no way we can syntactically confuse a local with generic
|
||||
// param, so these two should not be members of the single enum
|
||||
/// A local binding (only value namespace)
|
||||
LocalBinding(PatId),
|
||||
/// A generic parameter
|
||||
GenericParam(u32),
|
||||
pub enum TypeNs {
|
||||
SelfType(ImplBlock),
|
||||
GenericParam(u32),
|
||||
Adt(Adt),
|
||||
EnumVariant(EnumVariant),
|
||||
TypeAlias(TypeAlias),
|
||||
BuiltinType(BuiltinType),
|
||||
Trait(Trait),
|
||||
// Module belong to type ns, but the resovler is used when all module paths
|
||||
// are fully resolved.
|
||||
// Module(Module)
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum ValueOrPartial {
|
||||
ValueNs(ValueNs),
|
||||
Partial(TypeNs, usize),
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum ValueNs {
|
||||
LocalBinding(PatId),
|
||||
Function(Function),
|
||||
Const(Const),
|
||||
Static(Static),
|
||||
Struct(Struct),
|
||||
EnumVariant(EnumVariant),
|
||||
}
|
||||
|
||||
impl Resolver {
|
||||
/// Resolve known trait from std, like `std::futures::Future`
|
||||
pub(crate) fn resolve_known_trait(&self, db: &impl HirDatabase, path: &Path) -> Option<Trait> {
|
||||
let res = self.resolve_path_segments(db, path).into_fully_resolved().take_types()?;
|
||||
let res = self.resolve_module_path(db, path).take_types()?;
|
||||
match res {
|
||||
Resolution::Def(ModuleDef::Trait(it)) => Some(it),
|
||||
ModuleDef::Trait(it) => Some(it),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
@ -141,31 +95,195 @@ impl Resolver {
|
|||
db: &impl HirDatabase,
|
||||
path: &Path,
|
||||
) -> Option<Struct> {
|
||||
let res = self.resolve_path_segments(db, path).into_fully_resolved().take_types()?;
|
||||
let res = self.resolve_module_path(db, path).take_types()?;
|
||||
match res {
|
||||
Resolution::Def(ModuleDef::Adt(Adt::Struct(it))) => Some(it),
|
||||
ModuleDef::Adt(Adt::Struct(it)) => Some(it),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Resolve known enum from std, like `std::result::Result`
|
||||
pub(crate) fn resolve_known_enum(&self, db: &impl HirDatabase, path: &Path) -> Option<Enum> {
|
||||
let res = self.resolve_path_segments(db, path).into_fully_resolved().take_types()?;
|
||||
let res = self.resolve_module_path(db, path).take_types()?;
|
||||
match res {
|
||||
Resolution::Def(ModuleDef::Adt(Adt::Enum(it))) => Some(it),
|
||||
ModuleDef::Adt(Adt::Enum(it)) => Some(it),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn resolve_name(&self, db: &impl HirDatabase, name: &Name) -> PerNs<Resolution> {
|
||||
let mut resolution = PerNs::none();
|
||||
/// pub only for source-binder
|
||||
pub(crate) fn resolve_module_path(
|
||||
&self,
|
||||
db: &impl HirDatabase,
|
||||
path: &Path,
|
||||
) -> PerNs<ModuleDef> {
|
||||
let (item_map, module) = match self.module() {
|
||||
Some(it) => it,
|
||||
None => return PerNs::none(),
|
||||
};
|
||||
let (module_res, segment_index) = item_map.resolve_path(db, module, path);
|
||||
if segment_index.is_some() {
|
||||
return PerNs::none();
|
||||
}
|
||||
module_res
|
||||
}
|
||||
|
||||
pub(crate) fn resolve_path_in_type_ns(
|
||||
&self,
|
||||
db: &impl HirDatabase,
|
||||
path: &Path,
|
||||
) -> Option<(TypeNs, Option<usize>)> {
|
||||
let first_name = &path.segments.first()?.name;
|
||||
let skip_to_mod = path.kind != PathKind::Plain;
|
||||
for scope in self.scopes.iter().rev() {
|
||||
resolution = resolution.or(scope.resolve_name(db, name));
|
||||
if resolution.is_all() {
|
||||
return resolution;
|
||||
match scope {
|
||||
Scope::ExprScope(_) => continue,
|
||||
Scope::GenericParams(_) | Scope::ImplBlockScope(_) if skip_to_mod => continue,
|
||||
|
||||
Scope::GenericParams(params) => {
|
||||
if let Some(param) = params.find_by_name(first_name) {
|
||||
let idx = if path.segments.len() == 1 { None } else { Some(1) };
|
||||
return Some((TypeNs::GenericParam(param.idx), idx));
|
||||
}
|
||||
}
|
||||
Scope::ImplBlockScope(impl_) => {
|
||||
if first_name == &SELF_TYPE {
|
||||
let idx = if path.segments.len() == 1 { None } else { Some(1) };
|
||||
return Some((TypeNs::SelfType(*impl_), idx));
|
||||
}
|
||||
}
|
||||
Scope::ModuleScope(m) => {
|
||||
let (module_def, idx) = m.crate_def_map.resolve_path(db, m.module_id, path);
|
||||
let res = match module_def.take_types()? {
|
||||
ModuleDef::Adt(it) => TypeNs::Adt(it),
|
||||
ModuleDef::EnumVariant(it) => TypeNs::EnumVariant(it),
|
||||
|
||||
ModuleDef::TypeAlias(it) => TypeNs::TypeAlias(it),
|
||||
ModuleDef::BuiltinType(it) => TypeNs::BuiltinType(it),
|
||||
|
||||
ModuleDef::Trait(it) => TypeNs::Trait(it),
|
||||
|
||||
ModuleDef::Function(_)
|
||||
| ModuleDef::Const(_)
|
||||
| ModuleDef::Static(_)
|
||||
| ModuleDef::Module(_) => return None,
|
||||
};
|
||||
return Some((res, idx));
|
||||
}
|
||||
}
|
||||
}
|
||||
resolution
|
||||
None
|
||||
}
|
||||
|
||||
pub(crate) fn resolve_path_in_type_ns_fully(
|
||||
&self,
|
||||
db: &impl HirDatabase,
|
||||
path: &Path,
|
||||
) -> Option<TypeNs> {
|
||||
let (res, unresolved) = self.resolve_path_in_type_ns(db, path)?;
|
||||
if unresolved.is_some() {
|
||||
return None;
|
||||
}
|
||||
Some(res)
|
||||
}
|
||||
|
||||
pub(crate) fn resolve_path_in_value_ns(
|
||||
&self,
|
||||
db: &impl HirDatabase,
|
||||
path: &Path,
|
||||
) -> Option<ValueOrPartial> {
|
||||
let n_segments = path.segments.len();
|
||||
let tmp = SELF_PARAM;
|
||||
let first_name = if path.is_self() { &tmp } else { &path.segments.first()?.name };
|
||||
let skip_to_mod = path.kind != PathKind::Plain && !path.is_self();
|
||||
for scope in self.scopes.iter().rev() {
|
||||
match scope {
|
||||
Scope::ExprScope(_) | Scope::GenericParams(_) | Scope::ImplBlockScope(_)
|
||||
if skip_to_mod =>
|
||||
{
|
||||
continue
|
||||
}
|
||||
|
||||
Scope::ExprScope(scope) if n_segments <= 1 => {
|
||||
let entry = scope
|
||||
.expr_scopes
|
||||
.entries(scope.scope_id)
|
||||
.iter()
|
||||
.find(|entry| entry.name() == first_name);
|
||||
|
||||
if let Some(e) = entry {
|
||||
return Some(ValueOrPartial::ValueNs(ValueNs::LocalBinding(e.pat())));
|
||||
}
|
||||
}
|
||||
Scope::ExprScope(_) => continue,
|
||||
|
||||
Scope::GenericParams(params) if n_segments > 1 => {
|
||||
if let Some(param) = params.find_by_name(first_name) {
|
||||
let ty = TypeNs::GenericParam(param.idx);
|
||||
return Some(ValueOrPartial::Partial(ty, 1));
|
||||
}
|
||||
}
|
||||
Scope::GenericParams(_) => continue,
|
||||
|
||||
Scope::ImplBlockScope(impl_) if n_segments > 1 => {
|
||||
if first_name == &SELF_TYPE {
|
||||
let ty = TypeNs::SelfType(*impl_);
|
||||
return Some(ValueOrPartial::Partial(ty, 1));
|
||||
}
|
||||
}
|
||||
Scope::ImplBlockScope(_) => continue,
|
||||
|
||||
Scope::ModuleScope(m) => {
|
||||
let (module_def, idx) = m.crate_def_map.resolve_path(db, m.module_id, path);
|
||||
return match idx {
|
||||
None => {
|
||||
let value = match module_def.take_values()? {
|
||||
ModuleDef::Function(it) => ValueNs::Function(it),
|
||||
ModuleDef::Adt(Adt::Struct(it)) => ValueNs::Struct(it),
|
||||
ModuleDef::EnumVariant(it) => ValueNs::EnumVariant(it),
|
||||
ModuleDef::Const(it) => ValueNs::Const(it),
|
||||
ModuleDef::Static(it) => ValueNs::Static(it),
|
||||
|
||||
ModuleDef::Adt(Adt::Enum(_))
|
||||
| ModuleDef::Adt(Adt::Union(_))
|
||||
| ModuleDef::Trait(_)
|
||||
| ModuleDef::TypeAlias(_)
|
||||
| ModuleDef::BuiltinType(_)
|
||||
| ModuleDef::Module(_) => return None,
|
||||
};
|
||||
Some(ValueOrPartial::ValueNs(value))
|
||||
}
|
||||
Some(idx) => {
|
||||
let ty = match module_def.take_types()? {
|
||||
ModuleDef::Adt(it) => TypeNs::Adt(it),
|
||||
ModuleDef::Trait(it) => TypeNs::Trait(it),
|
||||
ModuleDef::TypeAlias(it) => TypeNs::TypeAlias(it),
|
||||
ModuleDef::BuiltinType(it) => TypeNs::BuiltinType(it),
|
||||
|
||||
ModuleDef::Module(_)
|
||||
| ModuleDef::Function(_)
|
||||
| ModuleDef::EnumVariant(_)
|
||||
| ModuleDef::Const(_)
|
||||
| ModuleDef::Static(_) => return None,
|
||||
};
|
||||
Some(ValueOrPartial::Partial(ty, idx))
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
pub(crate) fn resolve_path_in_value_ns_fully(
|
||||
&self,
|
||||
db: &impl HirDatabase,
|
||||
path: &Path,
|
||||
) -> Option<ValueNs> {
|
||||
match self.resolve_path_in_value_ns(db, path)? {
|
||||
ValueOrPartial::ValueNs(it) => Some(it),
|
||||
ValueOrPartial::Partial(..) => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn resolve_path_as_macro(
|
||||
|
@ -177,58 +295,14 @@ impl Resolver {
|
|||
item_map.resolve_path(db, module, path).0.get_macros()
|
||||
}
|
||||
|
||||
/// Returns the resolved path segments
|
||||
/// Which may be fully resolved, empty or partially resolved.
|
||||
pub(crate) fn resolve_path_segments(&self, db: &impl HirDatabase, path: &Path) -> PathResult {
|
||||
if let Some(name) = path.as_ident() {
|
||||
PathResult::from_resolution(self.resolve_name(db, name))
|
||||
} else if path.is_self() {
|
||||
PathResult::from_resolution(self.resolve_name(db, &SELF_PARAM))
|
||||
} else {
|
||||
let (item_map, module) = match self.module() {
|
||||
Some(it) => it,
|
||||
None => return PathResult::empty(),
|
||||
};
|
||||
let (module_res, segment_index) = item_map.resolve_path(db, module, path);
|
||||
|
||||
let def = module_res.map(Resolution::Def);
|
||||
|
||||
if let Some(index) = segment_index {
|
||||
PathResult::from_resolution_with_index(def, index)
|
||||
} else {
|
||||
PathResult::from_resolution(def)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the fully resolved path if we were able to resolve it.
|
||||
/// otherwise returns `PerNs::none`
|
||||
pub(crate) fn resolve_path_without_assoc_items(
|
||||
pub(crate) fn process_all_names(
|
||||
&self,
|
||||
db: &impl HirDatabase,
|
||||
path: &Path,
|
||||
) -> PerNs<Resolution> {
|
||||
// into_fully_resolved() returns the fully resolved path or PerNs::none() otherwise
|
||||
self.resolve_path_segments(db, path).into_fully_resolved()
|
||||
}
|
||||
|
||||
pub(crate) fn all_names(&self, db: &impl HirDatabase) -> FxHashMap<Name, PerNs<Resolution>> {
|
||||
let mut names = FxHashMap::default();
|
||||
f: &mut dyn FnMut(Name, ScopeDef),
|
||||
) {
|
||||
for scope in self.scopes.iter().rev() {
|
||||
scope.collect_names(db, &mut |name, res| {
|
||||
let current: &mut PerNs<Resolution> = names.entry(name).or_default();
|
||||
if current.types.is_none() {
|
||||
current.types = res.types;
|
||||
}
|
||||
if current.values.is_none() {
|
||||
current.values = res.values;
|
||||
}
|
||||
if current.macros.is_none() {
|
||||
current.macros = res.macros;
|
||||
}
|
||||
});
|
||||
scope.process_names(db, f);
|
||||
}
|
||||
names
|
||||
}
|
||||
|
||||
pub(crate) fn traits_in_scope(&self, db: &impl HirDatabase) -> FxHashSet<Trait> {
|
||||
|
@ -301,41 +375,28 @@ impl Resolver {
|
|||
}
|
||||
}
|
||||
|
||||
impl Scope {
|
||||
fn resolve_name(&self, db: &impl HirDatabase, name: &Name) -> PerNs<Resolution> {
|
||||
match self {
|
||||
Scope::ModuleScope(m) => {
|
||||
if name == &SELF_PARAM {
|
||||
PerNs::types(Resolution::Def(m.crate_def_map.mk_module(m.module_id).into()))
|
||||
} else {
|
||||
m.crate_def_map
|
||||
.resolve_name_in_module(db, m.module_id, name)
|
||||
.map(Resolution::Def)
|
||||
}
|
||||
}
|
||||
Scope::GenericParams(gp) => match gp.find_by_name(name) {
|
||||
Some(gp) => PerNs::types(Resolution::GenericParam(gp.idx)),
|
||||
None => PerNs::none(),
|
||||
},
|
||||
Scope::ImplBlockScope(i) => {
|
||||
if name == &SELF_TYPE {
|
||||
PerNs::types(Resolution::SelfType(*i))
|
||||
} else {
|
||||
PerNs::none()
|
||||
}
|
||||
}
|
||||
Scope::ExprScope(e) => {
|
||||
let entry =
|
||||
e.expr_scopes.entries(e.scope_id).iter().find(|entry| entry.name() == name);
|
||||
match entry {
|
||||
Some(e) => PerNs::values(Resolution::LocalBinding(e.pat())),
|
||||
None => PerNs::none(),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
/// For IDE only
|
||||
pub enum ScopeDef {
|
||||
ModuleDef(ModuleDef),
|
||||
MacroDef(MacroDef),
|
||||
GenericParam(u32),
|
||||
SelfType(ImplBlock),
|
||||
LocalBinding(PatId),
|
||||
Unknown,
|
||||
}
|
||||
|
||||
fn collect_names(&self, db: &impl HirDatabase, f: &mut dyn FnMut(Name, PerNs<Resolution>)) {
|
||||
impl From<PerNs<ModuleDef>> for ScopeDef {
|
||||
fn from(def: PerNs<ModuleDef>) -> Self {
|
||||
def.take_types()
|
||||
.or_else(|| def.take_values())
|
||||
.map(ScopeDef::ModuleDef)
|
||||
.or_else(|| def.get_macros().map(ScopeDef::MacroDef))
|
||||
.unwrap_or(ScopeDef::Unknown)
|
||||
}
|
||||
}
|
||||
|
||||
impl Scope {
|
||||
fn process_names(&self, db: &impl HirDatabase, f: &mut dyn FnMut(Name, ScopeDef)) {
|
||||
match self {
|
||||
Scope::ModuleScope(m) => {
|
||||
// FIXME: should we provide `self` here?
|
||||
|
@ -346,32 +407,32 @@ impl Scope {
|
|||
// }),
|
||||
// );
|
||||
m.crate_def_map[m.module_id].scope.entries().for_each(|(name, res)| {
|
||||
f(name.clone(), res.def.map(Resolution::Def));
|
||||
f(name.clone(), res.def.into());
|
||||
});
|
||||
m.crate_def_map[m.module_id].scope.legacy_macros().for_each(|(name, macro_)| {
|
||||
f(name.clone(), PerNs::macros(macro_));
|
||||
f(name.clone(), ScopeDef::MacroDef(macro_));
|
||||
});
|
||||
m.crate_def_map.extern_prelude().iter().for_each(|(name, def)| {
|
||||
f(name.clone(), PerNs::types(Resolution::Def(*def)));
|
||||
f(name.clone(), ScopeDef::ModuleDef(*def));
|
||||
});
|
||||
if let Some(prelude) = m.crate_def_map.prelude() {
|
||||
let prelude_def_map = db.crate_def_map(prelude.krate);
|
||||
prelude_def_map[prelude.module_id].scope.entries().for_each(|(name, res)| {
|
||||
f(name.clone(), res.def.map(Resolution::Def));
|
||||
f(name.clone(), res.def.into());
|
||||
});
|
||||
}
|
||||
}
|
||||
Scope::GenericParams(gp) => {
|
||||
for param in &gp.params {
|
||||
f(param.name.clone(), PerNs::types(Resolution::GenericParam(param.idx)))
|
||||
f(param.name.clone(), ScopeDef::GenericParam(param.idx))
|
||||
}
|
||||
}
|
||||
Scope::ImplBlockScope(i) => {
|
||||
f(SELF_TYPE, PerNs::types(Resolution::SelfType(*i)));
|
||||
f(SELF_TYPE, ScopeDef::SelfType(*i));
|
||||
}
|
||||
Scope::ExprScope(e) => {
|
||||
e.expr_scopes.entries(e.scope_id).iter().for_each(|e| {
|
||||
f(e.name().clone(), PerNs::values(Resolution::LocalBinding(e.pat())));
|
||||
f(e.name().clone(), ScopeDef::LocalBinding(e.pat()));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue