diff --git a/crates/ra_analysis/src/completion/complete_path.rs b/crates/ra_analysis/src/completion/complete_path.rs index ad4d68a3326..8c00be499ed 100644 --- a/crates/ra_analysis/src/completion/complete_path.rs +++ b/crates/ra_analysis/src/completion/complete_path.rs @@ -8,7 +8,7 @@ pub(super) fn complete_path(acc: &mut Completions, ctx: &CompletionContext) -> C (Some(path), Some(module)) => (path.clone(), module), _ => return Ok(()), }; - let def_id = match module.resolve_path(ctx.db, path)? { + let def_id = match module.resolve_path(ctx.db, path)?.take_types() { Some(it) => it, None => return Ok(()), }; diff --git a/crates/ra_analysis/src/completion/completion_item.rs b/crates/ra_analysis/src/completion/completion_item.rs index 911f08468ea..6d466c8bdd4 100644 --- a/crates/ra_analysis/src/completion/completion_item.rs +++ b/crates/ra_analysis/src/completion/completion_item.rs @@ -1,5 +1,7 @@ use crate::db; +use hir::PerNs; + /// `CompletionItem` describes a single completion variant in the editor pop-up. /// It is basically a POD with various properties. To construct a /// `CompletionItem`, use `new` method and the `Builder` struct. @@ -25,6 +27,8 @@ pub enum CompletionItemKind { Keyword, Module, Function, + Struct, + Enum, Binding, } @@ -117,16 +121,27 @@ pub(crate) fn from_resolution( db: &db::RootDatabase, resolution: &hir::Resolution, ) -> Builder { - if let Some(def_id) = resolution.def_id { - if let Ok(def) = def_id.resolve(db) { - let kind = match def { - hir::Def::Module(..) => CompletionItemKind::Module, - hir::Def::Function(..) => CompletionItemKind::Function, - _ => return self, - }; - self.kind = Some(kind); - } - } + let resolved = resolution.def_id.and_then(|d| d.resolve(db).ok()); + let kind = match resolved { + PerNs { + types: Some(hir::Def::Module(..)), + .. + } => CompletionItemKind::Module, + PerNs { + types: Some(hir::Def::Struct(..)), + .. + } => CompletionItemKind::Struct, + PerNs { + types: Some(hir::Def::Enum(..)), + .. + } => CompletionItemKind::Enum, + PerNs { + values: Some(hir::Def::Function(..)), + .. + } => CompletionItemKind::Function, + _ => return self, + }; + self.kind = Some(kind); self } } diff --git a/crates/ra_analysis/src/db.rs b/crates/ra_analysis/src/db.rs index 7043a0f4d09..677745d57db 100644 --- a/crates/ra_analysis/src/db.rs +++ b/crates/ra_analysis/src/db.rs @@ -95,8 +95,8 @@ impl hir::db::HirDatabase { fn submodules() for hir::db::SubmodulesQuery; fn infer() for hir::db::InferQuery; fn type_for_def() for hir::db::TypeForDefQuery; - fn struct_data() for db::StructDataQuery; - fn enum_data() for db::EnumDataQuery; + fn struct_data() for hir::db::StructDataQuery; + fn enum_data() for hir::db::EnumDataQuery; } } } diff --git a/crates/ra_hir/src/lib.rs b/crates/ra_hir/src/lib.rs index 7e9824de9ca..81526fe9cab 100644 --- a/crates/ra_hir/src/lib.rs +++ b/crates/ra_hir/src/lib.rs @@ -41,7 +41,7 @@ macro_rules! ctry { pub use self::{ path::{Path, PathKind}, krate::Crate, - module::{Module, ModuleId, Problem, nameres::ItemMap, ModuleScope, Resolution}, + module::{Module, ModuleId, Problem, nameres::{ItemMap, PerNs, Namespace}, ModuleScope, Resolution}, function::{Function, FnScopes}, adt::{Struct, Enum}, }; @@ -61,6 +61,8 @@ pub(crate) enum DefKind { Struct, Enum, Item, + + StructCtor, } #[derive(Clone, Debug, PartialEq, Eq, Hash)] @@ -72,18 +74,18 @@ pub struct DefLoc { } impl DefKind { - pub(crate) fn for_syntax_kind(kind: SyntaxKind) -> Option { + pub(crate) fn for_syntax_kind(kind: SyntaxKind) -> PerNs { match kind { - SyntaxKind::FN_DEF => Some(DefKind::Function), - SyntaxKind::MODULE => Some(DefKind::Module), + SyntaxKind::FN_DEF => PerNs::values(DefKind::Function), + SyntaxKind::MODULE => PerNs::types(DefKind::Module), + SyntaxKind::STRUCT_DEF => PerNs::both(DefKind::Struct, DefKind::StructCtor), + SyntaxKind::ENUM_DEF => PerNs::types(DefKind::Enum), // These define items, but don't have their own DefKinds yet: - SyntaxKind::STRUCT_DEF => Some(DefKind::Struct), - SyntaxKind::ENUM_DEF => Some(DefKind::Enum), - SyntaxKind::TRAIT_DEF => Some(DefKind::Item), - SyntaxKind::TYPE_DEF => Some(DefKind::Item), - SyntaxKind::CONST_DEF => Some(DefKind::Item), - SyntaxKind::STATIC_DEF => Some(DefKind::Item), - _ => None, + SyntaxKind::TRAIT_DEF => PerNs::types(DefKind::Item), + SyntaxKind::TYPE_DEF => PerNs::types(DefKind::Item), + SyntaxKind::CONST_DEF => PerNs::values(DefKind::Item), + SyntaxKind::STATIC_DEF => PerNs::values(DefKind::Item), + _ => PerNs::none(), } } } @@ -128,6 +130,7 @@ pub fn resolve(self, db: &impl HirDatabase) -> Cancelable { let enum_def = Enum::new(self); Def::Enum(enum_def) } + DefKind::StructCtor => Def::Item, DefKind::Item => Def::Item, }; Ok(res) diff --git a/crates/ra_hir/src/module.rs b/crates/ra_hir/src/module.rs index 89111995306..e1a0e4b5953 100644 --- a/crates/ra_hir/src/module.rs +++ b/crates/ra_hir/src/module.rs @@ -17,7 +17,7 @@ arena::{Arena, Id}, }; -pub use self::nameres::{ModuleScope, Resolution}; +pub use self::nameres::{ModuleScope, Resolution, Namespace, PerNs}; /// `Module` is API entry point to get all the information /// about a particular module. @@ -115,16 +115,29 @@ pub fn scope(&self, db: &impl HirDatabase) -> Cancelable { Ok(res) } - pub fn resolve_path(&self, db: &impl HirDatabase, path: Path) -> Cancelable> { - let mut curr = match path.kind { - PathKind::Crate => self.crate_root(), - PathKind::Self_ | PathKind::Plain => self.clone(), - PathKind::Super => ctry!(self.parent()), - } - .def_id(db); + pub fn resolve_path(&self, db: &impl HirDatabase, path: Path) -> Cancelable> { + let mut curr_per_ns = PerNs::types( + match path.kind { + PathKind::Crate => self.crate_root(), + PathKind::Self_ | PathKind::Plain => self.clone(), + PathKind::Super => { + if let Some(p) = self.parent() { + p + } else { + return Ok(PerNs::none()); + } + } + } + .def_id(db), + ); let segments = path.segments; for name in segments.iter() { + let curr = if let Some(r) = curr_per_ns.as_ref().take(Namespace::Types) { + r + } else { + return Ok(PerNs::none()); + }; let module = match curr.loc(db) { DefLoc { kind: DefKind::Module, @@ -132,12 +145,17 @@ pub fn resolve_path(&self, db: &impl HirDatabase, path: Path) -> Cancelable Module::new(db, source_root_id, module_id)?, - _ => return Ok(None), + // TODO here would be the place to handle enum variants... + _ => return Ok(PerNs::none()), }; let scope = module.scope(db)?; - curr = ctry!(ctry!(scope.get(&name)).def_id); + curr_per_ns = if let Some(r) = scope.get(&name) { + r.def_id + } else { + return Ok(PerNs::none()); + }; } - Ok(Some(curr)) + Ok(curr_per_ns) } pub fn problems(&self, db: &impl HirDatabase) -> Vec<(SyntaxNode, Problem)> { @@ -145,7 +163,7 @@ pub fn problems(&self, db: &impl HirDatabase) -> Vec<(SyntaxNode, Problem)> { } } -/// Phisically, rust source is organized as a set of files, but logically it is +/// Physically, rust source is organized as a set of files, but logically it is /// organized as a tree of modules. Usually, a single file corresponds to a /// single module, but it is not nessary the case. /// diff --git a/crates/ra_hir/src/module/nameres.rs b/crates/ra_hir/src/module/nameres.rs index 0b152a40623..33c9d93c288 100644 --- a/crates/ra_hir/src/module/nameres.rs +++ b/crates/ra_hir/src/module/nameres.rs @@ -118,22 +118,96 @@ enum ImportKind { #[derive(Debug, Clone, PartialEq, Eq)] pub struct Resolution { /// None for unresolved - pub def_id: Option, + pub def_id: PerNs, /// ident by whitch this is imported into local scope. pub import: Option, } -// #[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] -// enum Namespace { -// Types, -// Values, -// } +#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub enum Namespace { + Types, + Values, +} -// #[derive(Debug)] -// struct PerNs { -// types: Option, -// values: Option, -// } +#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] +pub struct PerNs { + pub types: Option, + pub values: Option, +} + +impl PerNs { + pub fn none() -> PerNs { + PerNs { + types: None, + values: None, + } + } + + pub fn values(t: T) -> PerNs { + PerNs { + types: None, + values: Some(t), + } + } + + pub fn types(t: T) -> PerNs { + PerNs { + types: Some(t), + values: None, + } + } + + pub fn both(types: T, values: T) -> PerNs { + PerNs { + types: Some(types), + values: Some(values), + } + } + + pub fn is_none(&self) -> bool { + self.types.is_none() && self.values.is_none() + } + + pub fn take(self, namespace: Namespace) -> Option { + match namespace { + Namespace::Types => self.types, + Namespace::Values => self.values, + } + } + + pub fn take_types(self) -> Option { + self.types + } + + pub fn take_values(self) -> Option { + self.values + } + + pub fn get(&self, namespace: Namespace) -> Option<&T> { + self.as_ref().take(namespace) + } + + pub fn as_ref(&self) -> PerNs<&T> { + PerNs { + types: self.types.as_ref(), + values: self.values.as_ref(), + } + } + + pub fn and_then(self, f: impl Fn(T) -> Option) -> PerNs { + PerNs { + types: self.types.and_then(&f), + values: self.values.and_then(&f), + } + } + + pub fn map(self, f: impl Fn(T) -> U) -> PerNs { + PerNs { + types: self.types.map(&f), + values: self.values.map(&f), + } + } +} impl InputModuleItems { pub(crate) fn new<'a>( @@ -254,7 +328,7 @@ fn populate_module(&mut self, module_id: ModuleId, input: &InputModuleItems) -> for dep in krate.dependencies(self.db) { if let Some(module) = dep.krate.root_module(self.db)? { let def_id = module.def_id(self.db); - self.add_module_item(&mut module_items, dep.name, def_id); + self.add_module_item(&mut module_items, dep.name, PerNs::types(def_id)); } } }; @@ -265,7 +339,7 @@ fn populate_module(&mut self, module_id: ModuleId, input: &InputModuleItems) -> module_items.items.insert( name.clone(), Resolution { - def_id: None, + def_id: PerNs::none(), import: Some(import), }, ); @@ -277,18 +351,23 @@ fn populate_module(&mut self, module_id: ModuleId, input: &InputModuleItems) -> if item.kind == MODULE { continue; } - let def_loc = DefLoc { - kind: DefKind::for_syntax_kind(item.kind).unwrap_or(DefKind::Item), - source_root_id: self.source_root, - module_id, - source_item_id: SourceItemId { - file_id, - item_id: Some(item.id), - }, - }; - let def_id = def_loc.id(self.db); + // depending on the item kind, the location can define something in + // the values namespace, the types namespace, or both + let kind = DefKind::for_syntax_kind(item.kind); + let def_id = kind.map(|k| { + let def_loc = DefLoc { + kind: k, + source_root_id: self.source_root, + module_id, + source_item_id: SourceItemId { + file_id, + item_id: Some(item.id), + }, + }; + def_loc.id(self.db) + }); let resolution = Resolution { - def_id: Some(def_id), + def_id, import: None, }; module_items.items.insert(item.name.clone(), resolution); @@ -303,16 +382,16 @@ fn populate_module(&mut self, module_id: ModuleId, input: &InputModuleItems) -> source_item_id: module_id.source(&self.module_tree).0, }; let def_id = def_loc.id(self.db); - self.add_module_item(&mut module_items, name, def_id); + self.add_module_item(&mut module_items, name, PerNs::types(def_id)); } self.result.per_module.insert(module_id, module_items); Ok(()) } - fn add_module_item(&self, module_items: &mut ModuleScope, name: SmolStr, def_id: DefId) { + fn add_module_item(&self, module_items: &mut ModuleScope, name: SmolStr, def_id: PerNs) { let resolution = Resolution { - def_id: Some(def_id), + def_id, import: None, }; module_items.items.insert(name, resolution); @@ -347,15 +426,17 @@ fn resolve_import(&mut self, module_id: ModuleId, import: &Import) -> Cancelable let is_last = i == import.path.segments.len() - 1; let def_id = match self.result.per_module[&curr].items.get(name) { - None => return Ok(()), - Some(res) => match res.def_id { - Some(it) => it, - None => return Ok(()), - }, + Some(res) if !res.def_id.is_none() => res.def_id, + _ => return Ok(()), }; if !is_last { - curr = match def_id.loc(self.db) { + let type_def_id = if let Some(d) = def_id.take(Namespace::Types) { + d + } else { + return Ok(()); + }; + curr = match type_def_id.loc(self.db) { DefLoc { kind: DefKind::Module, module_id: target_module_id, @@ -370,10 +451,11 @@ fn resolve_import(&mut self, module_id: ModuleId, import: &Import) -> Cancelable segments: import.path.segments[i + 1..].iter().cloned().collect(), kind: PathKind::Crate, }; - if let Some(def_id) = module.resolve_path(self.db, path)? { + let def_id = module.resolve_path(self.db, path)?; + if !def_id.is_none() { self.update(module_id, |items| { let res = Resolution { - def_id: Some(def_id), + def_id: def_id, import: Some(ptr), }; items.items.insert(name.clone(), res); @@ -387,7 +469,7 @@ fn resolve_import(&mut self, module_id: ModuleId, import: &Import) -> Cancelable } else { self.update(module_id, |items| { let res = Resolution { - def_id: Some(def_id), + def_id: def_id, import: Some(ptr), }; items.items.insert(name.clone(), res); diff --git a/crates/ra_hir/src/module/nameres/tests.rs b/crates/ra_hir/src/module/nameres/tests.rs index 3e29c39541a..03ea5c1d6c0 100644 --- a/crates/ra_hir/src/module/nameres/tests.rs +++ b/crates/ra_hir/src/module/nameres/tests.rs @@ -40,7 +40,7 @@ fn item_map_smoke_test() { ); let name = SmolStr::from("Baz"); let resolution = &item_map.per_module[&module_id].items[&name]; - assert!(resolution.def_id.is_some()); + assert!(resolution.def_id.take_types().is_some()); } #[test] @@ -59,7 +59,7 @@ fn test_self() { ); let name = SmolStr::from("Baz"); let resolution = &item_map.per_module[&module_id].items[&name]; - assert!(resolution.def_id.is_some()); + assert!(resolution.def_id.take_types().is_some()); } #[test] @@ -92,7 +92,7 @@ fn item_map_across_crates() { let name = SmolStr::from("Baz"); let resolution = &item_map.per_module[&module_id].items[&name]; - assert!(resolution.def_id.is_some()); + assert!(resolution.def_id.take_types().is_some()); } #[test] diff --git a/crates/ra_hir/src/ty.rs b/crates/ra_hir/src/ty.rs index f86b749ec19..429292cfc94 100644 --- a/crates/ra_hir/src/ty.rs +++ b/crates/ra_hir/src/ty.rs @@ -15,7 +15,11 @@ SyntaxNodeRef }; -use crate::{Def, DefId, FnScopes, Module, Function, Path, db::HirDatabase}; +use crate::{ + Def, DefId, FnScopes, Module, Function, + Path, db::HirDatabase, + module::nameres::Namespace +}; #[derive(Clone, PartialEq, Eq, Hash, Debug)] pub enum Ty { @@ -149,11 +153,12 @@ pub(crate) fn new( } // Resolve in module (in type namespace) - let resolved = if let Some(r) = module.resolve_path(db, path)? { - r - } else { - return Ok(Ty::Unknown); - }; + let resolved = + if let Some(r) = module.resolve_path(db, path)?.take(Namespace::Types) { + r + } else { + return Ok(Ty::Unknown); + }; let ty = db.type_for_def(resolved)?; ty } @@ -325,7 +330,10 @@ fn infer_path_expr(&mut self, expr: ast::PathExpr) -> Cancelable> { }; // resolve in module - let resolved = ctry!(self.module.resolve_path(self.db, path)?); + let resolved = ctry!(self + .module + .resolve_path(self.db, path)? + .take(Namespace::Values)); let ty = self.db.type_for_def(resolved)?; // TODO we will need to add type variables for type parameters etc. here Ok(Some(ty)) diff --git a/crates/ra_hir/src/ty/tests/data/0004_struct.txt b/crates/ra_hir/src/ty/tests/data/0004_struct.txt index 70ad055ff30..a4371c5a531 100644 --- a/crates/ra_hir/src/ty/tests/data/0004_struct.txt +++ b/crates/ra_hir/src/ty/tests/data/0004_struct.txt @@ -1,10 +1,10 @@ [86; 90) 'C(1)': [unknown] [72; 153) '{ ...a.c; }': () -[86; 87) 'C': C +[86; 87) 'C': [unknown] [107; 108) 'a': A [114; 132) 'A { b:... C() }': [unknown] [138; 141) 'a.b': [unknown] [147; 150) 'a.c': [unknown] -[96; 97) 'B': B +[96; 97) 'B': [unknown] [88; 89) '1': [unknown] [82; 83) 'c': [unknown] diff --git a/crates/ra_lsp_server/src/conv.rs b/crates/ra_lsp_server/src/conv.rs index 051f1f995b3..af52893114c 100644 --- a/crates/ra_lsp_server/src/conv.rs +++ b/crates/ra_lsp_server/src/conv.rs @@ -55,6 +55,8 @@ fn conv(self) -> ::Output { CompletionItemKind::Snippet => Snippet, CompletionItemKind::Module => Module, CompletionItemKind::Function => Function, + CompletionItemKind::Struct => Struct, + CompletionItemKind::Enum => Enum, CompletionItemKind::Binding => Variable, } }