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/code_model.rs | 10 +- crates/ra_hir/src/lib.rs | 2 +- crates/ra_hir/src/nameres.rs | 10 +- crates/ra_hir/src/nameres/per_ns.rs | 9 - crates/ra_hir/src/resolve.rs | 405 +++++++++++++++++++++--------------- crates/ra_hir/src/source_binder.rs | 66 +++--- crates/ra_hir/src/ty/infer.rs | 256 +++++++++++------------ crates/ra_hir/src/ty/lower.rs | 108 +++++----- 8 files changed, 458 insertions(+), 408 deletions(-) (limited to 'crates/ra_hir/src') diff --git a/crates/ra_hir/src/code_model.rs b/crates/ra_hir/src/code_model.rs index c1938bd86..dad1c93c4 100644 --- a/crates/ra_hir/src/code_model.rs +++ b/crates/ra_hir/src/code_model.rs @@ -22,7 +22,7 @@ use crate::{ U8, USIZE, }, nameres::{CrateModuleId, ImportId, ModuleScope, Namespace}, - resolve::Resolver, + resolve::{Resolver, TypeNs}, traits::{TraitData, TraitItem}, ty::{ primitive::{FloatBitness, FloatTy, IntBitness, IntTy, Signedness}, @@ -868,11 +868,9 @@ impl Trait { } _ => None, }) - .filter_map(|path| { - match resolver.resolve_path_without_assoc_items(db, path).take_types() { - Some(crate::Resolution::Def(ModuleDef::Trait(t))) => Some(t), - _ => None, - } + .filter_map(|path| match resolver.resolve_path_in_type_ns_fully(db, path) { + Some(TypeNs::Trait(t)) => Some(t), + _ => None, }) .collect() } diff --git a/crates/ra_hir/src/lib.rs b/crates/ra_hir/src/lib.rs index 508da3623..80cf8d9c0 100644 --- a/crates/ra_hir/src/lib.rs +++ b/crates/ra_hir/src/lib.rs @@ -73,7 +73,7 @@ pub use self::{ name::Name, nameres::{ImportId, Namespace, PerNs}, path::{Path, PathKind}, - resolve::Resolution, + resolve::ScopeDef, source_binder::{PathResolution, ScopeEntryWithSyntax, SourceAnalyzer}, source_id::{AstIdMap, ErasedFileAstId}, ty::{ diff --git a/crates/ra_hir/src/nameres.rs b/crates/ra_hir/src/nameres.rs index 3a3bf6b5f..44a4ddba0 100644 --- a/crates/ra_hir/src/nameres.rs +++ b/crates/ra_hir/src/nameres.rs @@ -279,10 +279,6 @@ impl CrateDefMap { self.root } - pub(crate) fn mk_module(&self, module_id: CrateModuleId) -> Module { - Module { krate: self.krate, module_id } - } - pub(crate) fn prelude(&self) -> Option { self.prelude } @@ -389,7 +385,7 @@ impl CrateDefMap { }; for (i, segment) in segments { - let curr = match curr_per_ns.as_ref().take_types() { + let curr = match curr_per_ns.take_types() { Some(r) => r, None => { // we still have path segments left, but the path so far @@ -433,7 +429,7 @@ impl CrateDefMap { Some(variant) => PerNs::both(variant.into(), variant.into()), None => { return ResolvePathResult::with( - PerNs::types((*e).into()), + PerNs::types(e.into()), ReachedFixedPoint::Yes, Some(i), ); @@ -450,7 +446,7 @@ impl CrateDefMap { ); return ResolvePathResult::with( - PerNs::types(*s), + PerNs::types(s), ReachedFixedPoint::Yes, Some(i), ); diff --git a/crates/ra_hir/src/nameres/per_ns.rs b/crates/ra_hir/src/nameres/per_ns.rs index d07cc08f4..b0f4dff33 100644 --- a/crates/ra_hir/src/nameres/per_ns.rs +++ b/crates/ra_hir/src/nameres/per_ns.rs @@ -68,10 +68,6 @@ impl PerNs { PerNs { types: None, values: None, macros: self.macros } } - pub fn as_ref(&self) -> PerNs<&T> { - PerNs { types: self.types.as_ref(), values: self.values.as_ref(), macros: self.macros } - } - pub fn or(self, other: PerNs) -> PerNs { PerNs { types: self.types.or(other.types), @@ -79,9 +75,4 @@ impl PerNs { macros: self.macros.or(other.macros), } } - - /// Map types and values. Leave macros unchanged. - pub fn map(self, f: impl Fn(T) -> U) -> PerNs { - PerNs { types: self.types.map(&f), values: self.values.map(&f), macros: self.macros } - } } 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())); }); } } diff --git a/crates/ra_hir/src/source_binder.rs b/crates/ra_hir/src/source_binder.rs index 65b304b43..cff55b640 100644 --- a/crates/ra_hir/src/source_binder.rs +++ b/crates/ra_hir/src/source_binder.rs @@ -15,7 +15,7 @@ use ra_syntax::{ SyntaxKind::*, SyntaxNode, SyntaxNodePtr, TextRange, TextUnit, }; -use rustc_hash::{FxHashMap, FxHashSet}; +use rustc_hash::FxHashSet; use crate::{ db::HirDatabase, @@ -27,9 +27,10 @@ use crate::{ ids::LocationCtx, name, path::{PathKind, PathSegment}, + resolve::{ScopeDef, TypeNs, ValueNs}, ty::method_resolution::implements_trait, AsName, AstId, Const, Crate, DefWithBody, Either, Enum, Function, HasBody, HirFileId, MacroDef, - Module, Name, Path, PerNs, Resolver, Static, Struct, Trait, Ty, + Module, Name, Path, Resolver, Static, Struct, Trait, Ty, }; /// Locates the module by `FileId`. Picks topmost module in the file. @@ -301,8 +302,41 @@ impl SourceAnalyzer { &self, db: &impl HirDatabase, path: &crate::Path, - ) -> PerNs { - self.resolver.resolve_path_without_assoc_items(db, path) + ) -> Option { + let types = self.resolver.resolve_path_in_type_ns_fully(db, &path).map(|ty| match ty { + TypeNs::SelfType(it) => PathResolution::SelfType(it), + TypeNs::GenericParam(it) => PathResolution::GenericParam(it), + TypeNs::Adt(it) => PathResolution::Def(it.into()), + TypeNs::EnumVariant(it) => PathResolution::Def(it.into()), + TypeNs::TypeAlias(it) => PathResolution::Def(it.into()), + TypeNs::BuiltinType(it) => PathResolution::Def(it.into()), + TypeNs::Trait(it) => PathResolution::Def(it.into()), + }); + let values = self.resolver.resolve_path_in_value_ns_fully(db, &path).and_then(|val| { + let res = match val { + ValueNs::LocalBinding(it) => { + // We get a `PatId` from resolver, but it actually can only + // point at `BindPat`, and not at the arbitrary pattern. + let pat_ptr = self + .body_source_map + .as_ref()? + .pat_syntax(it)? + .ast // FIXME: ignoring file_id here is definitelly wrong + .map_a(|ptr| ptr.cast::().unwrap()); + PathResolution::LocalBinding(pat_ptr) + } + ValueNs::Function(it) => PathResolution::Def(it.into()), + ValueNs::Const(it) => PathResolution::Def(it.into()), + ValueNs::Static(it) => PathResolution::Def(it.into()), + ValueNs::Struct(it) => PathResolution::Def(it.into()), + ValueNs::EnumVariant(it) => PathResolution::Def(it.into()), + }; + Some(res) + }); + + let items = + self.resolver.resolve_module_path(db, &path).take_types().map(PathResolution::Def); + types.or(values).or(items) } pub fn resolve_path(&self, db: &impl HirDatabase, path: &ast::Path) -> Option { @@ -319,25 +353,7 @@ impl SourceAnalyzer { } } let hir_path = crate::Path::from_ast(path.clone())?; - let res = self.resolver.resolve_path_without_assoc_items(db, &hir_path); - let res = res.clone().take_types().or_else(|| res.take_values())?; - let res = match res { - crate::Resolution::Def(it) => PathResolution::Def(it), - crate::Resolution::LocalBinding(it) => { - // We get a `PatId` from resolver, but it actually can only - // point at `BindPat`, and not at the arbitrary pattern. - let pat_ptr = self - .body_source_map - .as_ref()? - .pat_syntax(it)? - .ast // FIXME: ignoring file_id here is definitelly wrong - .map_a(|ptr| ptr.cast::().unwrap()); - PathResolution::LocalBinding(pat_ptr) - } - crate::Resolution::GenericParam(it) => PathResolution::GenericParam(it), - crate::Resolution::SelfType(it) => PathResolution::SelfType(it), - }; - Some(res) + self.resolve_hir_path(db, &hir_path) } pub fn resolve_local_name(&self, name_ref: &ast::NameRef) -> Option { @@ -360,8 +376,8 @@ impl SourceAnalyzer { }) } - pub fn all_names(&self, db: &impl HirDatabase) -> FxHashMap> { - self.resolver.all_names(db) + pub fn process_all_names(&self, db: &impl HirDatabase, f: &mut dyn FnMut(Name, ScopeDef)) { + self.resolver.process_all_names(db, f) } pub fn find_all_refs(&self, pat: &ast::BindPat) -> Vec { diff --git a/crates/ra_hir/src/ty/infer.rs b/crates/ra_hir/src/ty/infer.rs index 540a99b15..3ee083a04 100644 --- a/crates/ra_hir/src/ty/infer.rs +++ b/crates/ra_hir/src/ty/infer.rs @@ -45,11 +45,10 @@ use crate::{ name, nameres::Namespace, path::{GenericArg, GenericArgs, PathKind, PathSegment}, - resolve::{Resolution, Resolver}, + resolve::{Resolver, TypeNs, ValueNs, ValueOrPartial}, ty::infer::diagnostics::InferenceDiagnostic, type_ref::{Mutability, TypeRef}, - Adt, ConstData, DefWithBody, FnData, Function, HasBody, ImplItem, ModuleDef, Name, Path, - StructField, + Adt, ConstData, DefWithBody, FnData, Function, HasBody, ImplItem, Name, Path, StructField, }; mod unify; @@ -472,141 +471,138 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> { } fn infer_path_expr(&mut self, resolver: &Resolver, path: &Path, id: ExprOrPatId) -> Option { - let resolved = resolver.resolve_path_segments(self.db, &path); + let value_or_partial = resolver.resolve_path_in_value_ns(self.db, &path)?; - let (def, remaining_index) = resolved.into_inner(); + let (value, self_subst) = match value_or_partial { + ValueOrPartial::ValueNs(it) => (it, None), + ValueOrPartial::Partial(def, remaining_index) => { + self.resolve_assoc_item(def, path, remaining_index, id)? + } + }; - log::debug!( - "path {:?} resolved to {:?} with remaining index {:?}", - path, - def, - remaining_index - ); + let typable: TypableDef = match value { + ValueNs::LocalBinding(pat) => { + let ty = self.result.type_of_pat.get(pat)?.clone(); + let ty = self.resolve_ty_as_possible(&mut vec![], ty); + return Some(ty); + } + ValueNs::Function(it) => it.into(), + ValueNs::Const(it) => it.into(), + ValueNs::Static(it) => it.into(), + ValueNs::Struct(it) => it.into(), + ValueNs::EnumVariant(it) => it.into(), + }; - // if the remaining_index is None, we expect the path - // to be fully resolved, in this case we continue with - // the default by attempting to `take_values´ from the resolution. - // Otherwise the path was partially resolved, which means - // we might have resolved into a type for which - // we may find some associated item starting at the - // path.segment pointed to by `remaining_index´ - let mut resolved = - if remaining_index.is_none() { def.take_values()? } else { def.take_types()? }; + let mut ty = self.db.type_for_def(typable, Namespace::Values); + if let Some(self_subst) = self_subst { + ty = ty.subst(&self_subst); + } - let remaining_index = remaining_index.unwrap_or_else(|| path.segments.len()); - let mut actual_def_ty: Option = None; + let substs = Ty::substs_from_path(self.db, &self.resolver, path, typable); + let ty = ty.subst(&substs); + let ty = self.insert_type_vars(ty); + let ty = self.normalize_associated_types_in(ty); + Some(ty) + } + + fn resolve_assoc_item( + &mut self, + mut def: TypeNs, + path: &Path, + remaining_index: usize, + id: ExprOrPatId, + ) -> Option<(ValueNs, Option)> { + assert!(remaining_index < path.segments.len()); + let krate = self.resolver.krate()?; + + let mut ty = Ty::Unknown; - let krate = resolver.krate()?; // resolve intermediate segments for (i, segment) in path.segments[remaining_index..].iter().enumerate() { - let ty = match resolved { - Resolution::Def(def) => { - // FIXME resolve associated items from traits as well - let typable: Option = def.into(); - let typable = typable?; - - let ty = self.db.type_for_def(typable, Namespace::Types); - - // For example, this substs will take `Gen::**::make` - assert!(remaining_index > 0); - let substs = Ty::substs_from_path_segment( - self.db, - &self.resolver, - &path.segments[remaining_index + i - 1], - typable, - ); - - ty.subst(&substs) - } - Resolution::LocalBinding(_) => { - // can't have a local binding in an associated item path - return None; - } - Resolution::GenericParam(..) => { - // FIXME associated item of generic param - return None; - } - Resolution::SelfType(_) => { - // FIXME associated item of self type - return None; - } + let is_last_segment = i == path.segments[remaining_index..].len() - 1; + ty = { + let typable: TypableDef = match def { + TypeNs::Adt(it) => it.into(), + TypeNs::TypeAlias(it) => it.into(), + TypeNs::BuiltinType(it) => it.into(), + // FIXME associated item of traits, generics, and Self + TypeNs::Trait(_) | TypeNs::GenericParam(_) | TypeNs::SelfType(_) => { + return None; + } + // FIXME: report error here + TypeNs::EnumVariant(_) => return None, + }; + + let ty = self.db.type_for_def(typable, Namespace::Types); + + // For example, this substs will take `Gen::**::make` + assert!(remaining_index > 0); + let substs = Ty::substs_from_path_segment( + self.db, + &self.resolver, + &path.segments[remaining_index + i - 1], + typable, + ); + ty.subst(&substs) }; + if is_last_segment { + break; + } // Attempt to find an impl_item for the type which has a name matching // the current segment log::debug!("looking for path segment: {:?}", segment); - actual_def_ty = Some(ty.clone()); - - let item: crate::ModuleDef = ty.iterate_impl_items(self.db, krate, |item| { - let matching_def: Option = match item { - crate::ImplItem::Method(func) => { - if segment.name == func.name(self.db) { - Some(func.into()) - } else { - None - } - } - - crate::ImplItem::Const(konst) => { - let data = konst.data(self.db); - if segment.name == *data.name() { - Some(konst.into()) - } else { - None - } - } + let ty = mem::replace(&mut ty, Ty::Unknown); + def = ty.iterate_impl_items(self.db, krate, |item| { + match item { + crate::ImplItem::Method(_) => None, + crate::ImplItem::Const(_) => None, // FIXME: Resolve associated types - crate::ImplItem::TypeAlias(_) => None, - }; - match matching_def { - Some(_) => { - self.write_assoc_resolution(id, item); - matching_def + crate::ImplItem::TypeAlias(_) => { + // Some(TypeNs::TypeAlias(..)) + None:: } - None => None, } })?; - - resolved = Resolution::Def(item); } - match resolved { - Resolution::Def(def) => { - let typable: Option = def.into(); - let typable = typable?; - let mut ty = self.db.type_for_def(typable, Namespace::Values); - if let Some(sts) = self.find_self_types(&def, actual_def_ty) { - ty = ty.subst(&sts); + let segment = path.segments.last().unwrap(); + let def = ty.clone().iterate_impl_items(self.db, krate, |item| { + let matching_def: Option = match item { + crate::ImplItem::Method(func) => { + if segment.name == func.name(self.db) { + Some(ValueNs::Function(func)) + } else { + None + } } - let substs = Ty::substs_from_path(self.db, &self.resolver, path, typable); - let ty = ty.subst(&substs); - let ty = self.insert_type_vars(ty); - let ty = self.normalize_associated_types_in(ty); - Some(ty) - } - Resolution::LocalBinding(pat) => { - let ty = self.result.type_of_pat.get(pat)?.clone(); - let ty = self.resolve_ty_as_possible(&mut vec![], ty); - Some(ty) - } - Resolution::GenericParam(..) => { - // generic params can't refer to values... yet - None - } - Resolution::SelfType(_) => { - log::error!("path expr {:?} resolved to Self type in values ns", path); - None + crate::ImplItem::Const(konst) => { + let data = konst.data(self.db); + if segment.name == *data.name() { + Some(ValueNs::Const(konst)) + } else { + None + } + } + crate::ImplItem::TypeAlias(_) => None, + }; + match matching_def { + Some(_) => { + self.write_assoc_resolution(id, item); + matching_def + } + None => None, } - } + })?; + let self_types = self.find_self_types(&def, ty); + Some((def, self_types)) } - fn find_self_types(&self, def: &ModuleDef, actual_def_ty: Option) -> Option { - let actual_def_ty = actual_def_ty?; - - if let crate::ModuleDef::Function(func) = def { + fn find_self_types(&self, def: &ValueNs, actual_def_ty: Ty) -> Option { + if let ValueNs::Function(func) = def { // We only do the infer if parent has generic params let gen = func.generic_params(self.db); if gen.count_parent_params() == 0 { @@ -641,30 +637,24 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> { None => return (Ty::Unknown, None), }; let resolver = &self.resolver; - let typable: Option = + let def: TypableDef = // FIXME: this should resolve assoc items as well, see this example: // https://play.rust-lang.org/?gist=087992e9e22495446c01c0d4e2d69521 - match resolver.resolve_path_without_assoc_items(self.db, &path).take_types() { - Some(Resolution::Def(def)) => def.into(), - Some(Resolution::LocalBinding(..)) => { - // this cannot happen - log::error!("path resolved to local binding in type ns"); - return (Ty::Unknown, None); - } - Some(Resolution::GenericParam(..)) => { - // generic params can't be used in struct literals - return (Ty::Unknown, None); - } - Some(Resolution::SelfType(..)) => { - // FIXME this is allowed in an impl for a struct, handle this - return (Ty::Unknown, None); + match resolver.resolve_path_in_type_ns_fully(self.db, &path) { + Some(TypeNs::Adt(Adt::Struct(it))) => it.into(), + Some(TypeNs::Adt(Adt::Union(it))) => it.into(), + Some(TypeNs::EnumVariant(it)) => it.into(), + Some(TypeNs::TypeAlias(it)) => it.into(), + + Some(TypeNs::SelfType(_)) | + Some(TypeNs::GenericParam(_)) | + Some(TypeNs::BuiltinType(_)) | + Some(TypeNs::Trait(_)) | + Some(TypeNs::Adt(Adt::Enum(_))) | + None => { + return (Ty::Unknown, None) } - None => return (Ty::Unknown, None), }; - let def = match typable { - None => return (Ty::Unknown, None), - Some(it) => it, - }; // FIXME remove the duplication between here and `Ty::from_path`? let substs = Ty::substs_from_path(self.db, resolver, path, def); match def { diff --git a/crates/ra_hir/src/ty/lower.rs b/crates/ra_hir/src/ty/lower.rs index 946e9e9fb..3fdb2ca92 100644 --- a/crates/ra_hir/src/ty/lower.rs +++ b/crates/ra_hir/src/ty/lower.rs @@ -19,7 +19,7 @@ use crate::{ generics::{GenericDef, WherePredicate}, nameres::Namespace, path::{GenericArg, PathSegment}, - resolve::{Resolution, Resolver}, + resolve::{Resolver, TypeNs}, ty::Adt, type_ref::{TypeBound, TypeRef}, BuiltinType, Const, Enum, EnumVariant, Function, ModuleDef, Path, Static, Struct, StructField, @@ -88,16 +88,47 @@ impl Ty { pub(crate) fn from_hir_path(db: &impl HirDatabase, resolver: &Resolver, path: &Path) -> Ty { // Resolve the path (in type namespace) - let (resolution, remaining_index) = resolver.resolve_path_segments(db, path).into_inner(); - let resolution = resolution.take_types(); - - let def = match resolution { - Some(Resolution::Def(def)) => def, - Some(Resolution::LocalBinding(..)) => { - // this should never happen - panic!("path resolved to local binding in type ns"); + let (resolution, remaining_index) = match resolver.resolve_path_in_type_ns(db, path) { + Some(it) => it, + None => return Ty::Unknown, + }; + + let typable: TypableDef = match resolution { + TypeNs::Trait(trait_) => { + let segment = match remaining_index { + None => path.segments.last().expect("resolved path has at least one element"), + Some(i) => &path.segments[i - 1], + }; + let trait_ref = TraitRef::from_resolved_path(db, resolver, trait_, segment, None); + return if let Some(remaining_index) = remaining_index { + if remaining_index == path.segments.len() - 1 { + let segment = &path.segments[remaining_index]; + match trait_ref + .trait_ + .associated_type_by_name_including_super_traits(db, &segment.name) + { + Some(associated_ty) => { + // FIXME handle type parameters on the segment + Ty::Projection(ProjectionTy { + associated_ty, + parameters: trait_ref.substs, + }) + } + None => { + // associated type not found + Ty::Unknown + } + } + } else { + // FIXME more than one segment remaining, is this possible? + Ty::Unknown + } + } else { + // FIXME dyn Trait without the dyn + Ty::Unknown + }; } - Some(Resolution::GenericParam(idx)) => { + TypeNs::GenericParam(idx) => { if remaining_index.is_some() { // e.g. T::Item return Ty::Unknown; @@ -111,57 +142,24 @@ impl Ty { .clone(), }; } - Some(Resolution::SelfType(impl_block)) => { + TypeNs::SelfType(impl_block) => { if remaining_index.is_some() { // e.g. Self::Item return Ty::Unknown; } return impl_block.target_ty(db); } - None => { - // path did not resolve - return Ty::Unknown; - } + + TypeNs::Adt(it) => it.into(), + TypeNs::BuiltinType(it) => it.into(), + TypeNs::TypeAlias(it) => it.into(), + // FIXME: report error + TypeNs::EnumVariant(_) => return Ty::Unknown, }; - if let ModuleDef::Trait(trait_) = def { - let segment = match remaining_index { - None => path.segments.last().expect("resolved path has at least one element"), - Some(i) => &path.segments[i - 1], - }; - let trait_ref = TraitRef::from_resolved_path(db, resolver, trait_, segment, None); - if let Some(remaining_index) = remaining_index { - if remaining_index == path.segments.len() - 1 { - let segment = &path.segments[remaining_index]; - let associated_ty = match trait_ref - .trait_ - .associated_type_by_name_including_super_traits(db, &segment.name) - { - Some(t) => t, - None => { - // associated type not found - return Ty::Unknown; - } - }; - // FIXME handle type parameters on the segment - Ty::Projection(ProjectionTy { associated_ty, parameters: trait_ref.substs }) - } else { - // FIXME more than one segment remaining, is this possible? - Ty::Unknown - } - } else { - // FIXME dyn Trait without the dyn - Ty::Unknown - } - } else { - let typable: TypableDef = match def.into() { - None => return Ty::Unknown, - Some(it) => it, - }; - let ty = db.type_for_def(typable, Namespace::Types); - let substs = Ty::substs_from_path(db, resolver, path, typable); - ty.subst(&substs) - } + let ty = db.type_for_def(typable, Namespace::Types); + let substs = Ty::substs_from_path(db, resolver, path, typable); + ty.subst(&substs) } pub(super) fn substs_from_path_segment( @@ -278,8 +276,8 @@ impl TraitRef { path: &Path, explicit_self_ty: Option, ) -> Option { - let resolved = match resolver.resolve_path_without_assoc_items(db, &path).take_types()? { - Resolution::Def(ModuleDef::Trait(tr)) => tr, + let resolved = match resolver.resolve_path_in_type_ns_fully(db, &path)? { + TypeNs::Trait(tr) => tr, _ => return None, }; let segment = path.segments.last().expect("path should have at least one segment"); -- cgit v1.2.3