From 51e2d76b9839410020c75ac02ad602675b0a5aa9 Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Thu, 12 Sep 2019 23:35:53 +0300 Subject: Specify desirable namespace when calling resolve That way, we are able to get rid of a number of unreachable statements --- crates/ra_hir/src/resolve.rs | 405 +++++++++++++++++++++++++------------------ 1 file changed, 233 insertions(+), 172 deletions(-) (limited to 'crates/ra_hir/src/resolve.rs') diff --git a/crates/ra_hir/src/resolve.rs b/crates/ra_hir/src/resolve.rs index a77a9aeb1..d841593f8 100644 --- a/crates/ra_hir/src/resolve.rs +++ b/crates/ra_hir/src/resolve.rs @@ -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` type doesn't make sense, as not every - // Resolution variant can appear in every namespace - resolution: PerNs, - /// 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 { - if self.remaining_index > 0 { - Some(self.remaining_index) - } else { - None - } - } - - /// Consumes `PathResult` and returns the contained `PerNs` - /// if the path was fully resolved, meaning we have no remaining items - pub(crate) fn into_fully_resolved(self) -> PerNs { - 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, Option) { - 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) -> PathResult { - PathResult::from_resolution_with_index(res, 0) - } - - fn from_resolution_with_index(res: PerNs, 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), +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), +} - // 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) +#[derive(Debug)] +pub enum ValueNs { LocalBinding(PatId), - /// A generic parameter - GenericParam(u32), - SelfType(ImplBlock), + 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 { - 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,94 +95,214 @@ impl Resolver { db: &impl HirDatabase, path: &Path, ) -> Option { - 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 { - 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 { - let mut resolution = PerNs::none(); + /// pub only for source-binder + pub(crate) fn resolve_module_path( + &self, + db: &impl HirDatabase, + path: &Path, + ) -> PerNs { + 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)> { + 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_as_macro( + pub(crate) fn resolve_path_in_type_ns_fully( &self, db: &impl HirDatabase, path: &Path, - ) -> Option { - let (item_map, module) = self.module()?; - item_map.resolve_path(db, module, path).0.get_macros() + ) -> Option { + let (res, unresolved) = self.resolve_path_in_type_ns(db, path)?; + if unresolved.is_some() { + return None; + } + Some(res) } - /// 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) + pub(crate) fn resolve_path_in_value_ns( + &self, + db: &impl HirDatabase, + path: &Path, + ) -> Option { + 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 } - /// 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 resolve_path_in_value_ns_fully( &self, db: &impl HirDatabase, path: &Path, - ) -> PerNs { - // into_fully_resolved() returns the fully resolved path or PerNs::none() otherwise - self.resolve_path_segments(db, path).into_fully_resolved() + ) -> Option { + match self.resolve_path_in_value_ns(db, path)? { + ValueOrPartial::ValueNs(it) => Some(it), + ValueOrPartial::Partial(..) => None, + } } - pub(crate) fn all_names(&self, db: &impl HirDatabase) -> FxHashMap> { - let mut names = FxHashMap::default(); + pub(crate) fn resolve_path_as_macro( + &self, + db: &impl HirDatabase, + path: &Path, + ) -> Option { + let (item_map, module) = self.module()?; + item_map.resolve_path(db, module, path).0.get_macros() + } + + pub(crate) fn process_all_names( + &self, + db: &impl HirDatabase, + f: &mut dyn FnMut(Name, ScopeDef), + ) { for scope in self.scopes.iter().rev() { - scope.collect_names(db, &mut |name, res| { - let current: &mut PerNs = 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 { @@ -301,41 +375,28 @@ impl Resolver { } } -impl Scope { - fn resolve_name(&self, db: &impl HirDatabase, name: &Name) -> PerNs { - 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, +} + +impl From> for ScopeDef { + fn from(def: PerNs) -> Self { + def.take_types() + .or_else(|| def.take_values()) + .map(ScopeDef::ModuleDef) + .or_else(|| def.get_macros().map(ScopeDef::MacroDef)) + .unwrap_or(ScopeDef::Unknown) } +} - fn collect_names(&self, db: &impl HirDatabase, f: &mut dyn FnMut(Name, PerNs)) { +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())); }); } } -- cgit v1.2.3