Do name resolution by namespace (types/values)

This commit is contained in:
Florian Diebold 2018-12-24 20:32:39 +01:00
parent b5b68f2094
commit 4ff1618520
10 changed files with 212 additions and 84 deletions

View file

@ -8,7 +8,7 @@ pub(super) fn complete_path(acc: &mut Completions, ctx: &CompletionContext) -> C
(Some(path), Some(module)) => (path.clone(), module), (Some(path), Some(module)) => (path.clone(), module),
_ => return Ok(()), _ => 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, Some(it) => it,
None => return Ok(()), None => return Ok(()),
}; };

View file

@ -1,5 +1,7 @@
use crate::db; use crate::db;
use hir::PerNs;
/// `CompletionItem` describes a single completion variant in the editor pop-up. /// `CompletionItem` describes a single completion variant in the editor pop-up.
/// It is basically a POD with various properties. To construct a /// It is basically a POD with various properties. To construct a
/// `CompletionItem`, use `new` method and the `Builder` struct. /// `CompletionItem`, use `new` method and the `Builder` struct.
@ -25,6 +27,8 @@ pub enum CompletionItemKind {
Keyword, Keyword,
Module, Module,
Function, Function,
Struct,
Enum,
Binding, Binding,
} }
@ -117,16 +121,27 @@ impl Builder {
db: &db::RootDatabase, db: &db::RootDatabase,
resolution: &hir::Resolution, resolution: &hir::Resolution,
) -> Builder { ) -> Builder {
if let Some(def_id) = resolution.def_id { let resolved = resolution.def_id.and_then(|d| d.resolve(db).ok());
if let Ok(def) = def_id.resolve(db) { let kind = match resolved {
let kind = match def { PerNs {
hir::Def::Module(..) => CompletionItemKind::Module, types: Some(hir::Def::Module(..)),
hir::Def::Function(..) => CompletionItemKind::Function, ..
} => 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, _ => return self,
}; };
self.kind = Some(kind); self.kind = Some(kind);
}
}
self self
} }
} }

View file

@ -95,8 +95,8 @@ salsa::database_storage! {
fn submodules() for hir::db::SubmodulesQuery; fn submodules() for hir::db::SubmodulesQuery;
fn infer() for hir::db::InferQuery; fn infer() for hir::db::InferQuery;
fn type_for_def() for hir::db::TypeForDefQuery; fn type_for_def() for hir::db::TypeForDefQuery;
fn struct_data() for db::StructDataQuery; fn struct_data() for hir::db::StructDataQuery;
fn enum_data() for db::EnumDataQuery; fn enum_data() for hir::db::EnumDataQuery;
} }
} }
} }

View file

@ -41,7 +41,7 @@ use crate::{
pub use self::{ pub use self::{
path::{Path, PathKind}, path::{Path, PathKind},
krate::Crate, krate::Crate,
module::{Module, ModuleId, Problem, nameres::ItemMap, ModuleScope, Resolution}, module::{Module, ModuleId, Problem, nameres::{ItemMap, PerNs, Namespace}, ModuleScope, Resolution},
function::{Function, FnScopes}, function::{Function, FnScopes},
adt::{Struct, Enum}, adt::{Struct, Enum},
}; };
@ -61,6 +61,8 @@ pub(crate) enum DefKind {
Struct, Struct,
Enum, Enum,
Item, Item,
StructCtor,
} }
#[derive(Clone, Debug, PartialEq, Eq, Hash)] #[derive(Clone, Debug, PartialEq, Eq, Hash)]
@ -72,18 +74,18 @@ pub struct DefLoc {
} }
impl DefKind { impl DefKind {
pub(crate) fn for_syntax_kind(kind: SyntaxKind) -> Option<DefKind> { pub(crate) fn for_syntax_kind(kind: SyntaxKind) -> PerNs<DefKind> {
match kind { match kind {
SyntaxKind::FN_DEF => Some(DefKind::Function), SyntaxKind::FN_DEF => PerNs::values(DefKind::Function),
SyntaxKind::MODULE => Some(DefKind::Module), 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: // These define items, but don't have their own DefKinds yet:
SyntaxKind::STRUCT_DEF => Some(DefKind::Struct), SyntaxKind::TRAIT_DEF => PerNs::types(DefKind::Item),
SyntaxKind::ENUM_DEF => Some(DefKind::Enum), SyntaxKind::TYPE_DEF => PerNs::types(DefKind::Item),
SyntaxKind::TRAIT_DEF => Some(DefKind::Item), SyntaxKind::CONST_DEF => PerNs::values(DefKind::Item),
SyntaxKind::TYPE_DEF => Some(DefKind::Item), SyntaxKind::STATIC_DEF => PerNs::values(DefKind::Item),
SyntaxKind::CONST_DEF => Some(DefKind::Item), _ => PerNs::none(),
SyntaxKind::STATIC_DEF => Some(DefKind::Item),
_ => None,
} }
} }
} }
@ -128,6 +130,7 @@ impl DefId {
let enum_def = Enum::new(self); let enum_def = Enum::new(self);
Def::Enum(enum_def) Def::Enum(enum_def)
} }
DefKind::StructCtor => Def::Item,
DefKind::Item => Def::Item, DefKind::Item => Def::Item,
}; };
Ok(res) Ok(res)

View file

@ -17,7 +17,7 @@ use crate::{
arena::{Arena, Id}, 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 /// `Module` is API entry point to get all the information
/// about a particular module. /// about a particular module.
@ -115,16 +115,29 @@ impl Module {
Ok(res) Ok(res)
} }
pub fn resolve_path(&self, db: &impl HirDatabase, path: Path) -> Cancelable<Option<DefId>> { pub fn resolve_path(&self, db: &impl HirDatabase, path: Path) -> Cancelable<PerNs<DefId>> {
let mut curr = match path.kind { let mut curr_per_ns = PerNs::types(
match path.kind {
PathKind::Crate => self.crate_root(), PathKind::Crate => self.crate_root(),
PathKind::Self_ | PathKind::Plain => self.clone(), PathKind::Self_ | PathKind::Plain => self.clone(),
PathKind::Super => ctry!(self.parent()), PathKind::Super => {
if let Some(p) = self.parent() {
p
} else {
return Ok(PerNs::none());
} }
.def_id(db); }
}
.def_id(db),
);
let segments = path.segments; let segments = path.segments;
for name in segments.iter() { 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) { let module = match curr.loc(db) {
DefLoc { DefLoc {
kind: DefKind::Module, kind: DefKind::Module,
@ -132,12 +145,17 @@ impl Module {
module_id, module_id,
.. ..
} => Module::new(db, source_root_id, module_id)?, } => 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)?; 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)> { pub fn problems(&self, db: &impl HirDatabase) -> Vec<(SyntaxNode, Problem)> {
@ -145,7 +163,7 @@ impl Module {
} }
} }
/// 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 /// organized as a tree of modules. Usually, a single file corresponds to a
/// single module, but it is not nessary the case. /// single module, but it is not nessary the case.
/// ///

View file

@ -118,22 +118,96 @@ enum ImportKind {
#[derive(Debug, Clone, PartialEq, Eq)] #[derive(Debug, Clone, PartialEq, Eq)]
pub struct Resolution { pub struct Resolution {
/// None for unresolved /// None for unresolved
pub def_id: Option<DefId>, pub def_id: PerNs<DefId>,
/// ident by whitch this is imported into local scope. /// ident by whitch this is imported into local scope.
pub import: Option<NamedImport>, pub import: Option<NamedImport>,
} }
// #[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] #[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
// enum Namespace { pub enum Namespace {
// Types, Types,
// Values, Values,
// } }
// #[derive(Debug)] #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
// struct PerNs<T> { pub struct PerNs<T> {
// types: Option<T>, pub types: Option<T>,
// values: Option<T>, pub values: Option<T>,
// } }
impl<T> PerNs<T> {
pub fn none() -> PerNs<T> {
PerNs {
types: None,
values: None,
}
}
pub fn values(t: T) -> PerNs<T> {
PerNs {
types: None,
values: Some(t),
}
}
pub fn types(t: T) -> PerNs<T> {
PerNs {
types: Some(t),
values: None,
}
}
pub fn both(types: T, values: T) -> PerNs<T> {
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<T> {
match namespace {
Namespace::Types => self.types,
Namespace::Values => self.values,
}
}
pub fn take_types(self) -> Option<T> {
self.types
}
pub fn take_values(self) -> Option<T> {
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<U>(self, f: impl Fn(T) -> Option<U>) -> PerNs<U> {
PerNs {
types: self.types.and_then(&f),
values: self.values.and_then(&f),
}
}
pub fn map<U>(self, f: impl Fn(T) -> U) -> PerNs<U> {
PerNs {
types: self.types.map(&f),
values: self.values.map(&f),
}
}
}
impl InputModuleItems { impl InputModuleItems {
pub(crate) fn new<'a>( pub(crate) fn new<'a>(
@ -254,7 +328,7 @@ where
for dep in krate.dependencies(self.db) { for dep in krate.dependencies(self.db) {
if let Some(module) = dep.krate.root_module(self.db)? { if let Some(module) = dep.krate.root_module(self.db)? {
let def_id = module.def_id(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 @@ where
module_items.items.insert( module_items.items.insert(
name.clone(), name.clone(),
Resolution { Resolution {
def_id: None, def_id: PerNs::none(),
import: Some(import), import: Some(import),
}, },
); );
@ -277,8 +351,12 @@ where
if item.kind == MODULE { if item.kind == MODULE {
continue; continue;
} }
// 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 { let def_loc = DefLoc {
kind: DefKind::for_syntax_kind(item.kind).unwrap_or(DefKind::Item), kind: k,
source_root_id: self.source_root, source_root_id: self.source_root,
module_id, module_id,
source_item_id: SourceItemId { source_item_id: SourceItemId {
@ -286,9 +364,10 @@ where
item_id: Some(item.id), item_id: Some(item.id),
}, },
}; };
let def_id = def_loc.id(self.db); def_loc.id(self.db)
});
let resolution = Resolution { let resolution = Resolution {
def_id: Some(def_id), def_id,
import: None, import: None,
}; };
module_items.items.insert(item.name.clone(), resolution); module_items.items.insert(item.name.clone(), resolution);
@ -303,16 +382,16 @@ where
source_item_id: module_id.source(&self.module_tree).0, source_item_id: module_id.source(&self.module_tree).0,
}; };
let def_id = def_loc.id(self.db); 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); self.result.per_module.insert(module_id, module_items);
Ok(()) 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<DefId>) {
let resolution = Resolution { let resolution = Resolution {
def_id: Some(def_id), def_id,
import: None, import: None,
}; };
module_items.items.insert(name, resolution); module_items.items.insert(name, resolution);
@ -347,15 +426,17 @@ where
let is_last = i == import.path.segments.len() - 1; let is_last = i == import.path.segments.len() - 1;
let def_id = match self.result.per_module[&curr].items.get(name) { let def_id = match self.result.per_module[&curr].items.get(name) {
None => return Ok(()), Some(res) if !res.def_id.is_none() => res.def_id,
Some(res) => match res.def_id { _ => return Ok(()),
Some(it) => it,
None => return Ok(()),
},
}; };
if !is_last { 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 { DefLoc {
kind: DefKind::Module, kind: DefKind::Module,
module_id: target_module_id, module_id: target_module_id,
@ -370,10 +451,11 @@ where
segments: import.path.segments[i + 1..].iter().cloned().collect(), segments: import.path.segments[i + 1..].iter().cloned().collect(),
kind: PathKind::Crate, 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| { self.update(module_id, |items| {
let res = Resolution { let res = Resolution {
def_id: Some(def_id), def_id: def_id,
import: Some(ptr), import: Some(ptr),
}; };
items.items.insert(name.clone(), res); items.items.insert(name.clone(), res);
@ -387,7 +469,7 @@ where
} else { } else {
self.update(module_id, |items| { self.update(module_id, |items| {
let res = Resolution { let res = Resolution {
def_id: Some(def_id), def_id: def_id,
import: Some(ptr), import: Some(ptr),
}; };
items.items.insert(name.clone(), res); items.items.insert(name.clone(), res);

View file

@ -40,7 +40,7 @@ fn item_map_smoke_test() {
); );
let name = SmolStr::from("Baz"); let name = SmolStr::from("Baz");
let resolution = &item_map.per_module[&module_id].items[&name]; 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] #[test]
@ -59,7 +59,7 @@ fn test_self() {
); );
let name = SmolStr::from("Baz"); let name = SmolStr::from("Baz");
let resolution = &item_map.per_module[&module_id].items[&name]; 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] #[test]
@ -92,7 +92,7 @@ fn item_map_across_crates() {
let name = SmolStr::from("Baz"); let name = SmolStr::from("Baz");
let resolution = &item_map.per_module[&module_id].items[&name]; 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] #[test]

View file

@ -15,7 +15,11 @@ use ra_syntax::{
SyntaxNodeRef 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)] #[derive(Clone, PartialEq, Eq, Hash, Debug)]
pub enum Ty { pub enum Ty {
@ -149,7 +153,8 @@ impl Ty {
} }
// Resolve in module (in type namespace) // Resolve in module (in type namespace)
let resolved = if let Some(r) = module.resolve_path(db, path)? { let resolved =
if let Some(r) = module.resolve_path(db, path)?.take(Namespace::Types) {
r r
} else { } else {
return Ok(Ty::Unknown); return Ok(Ty::Unknown);
@ -325,7 +330,10 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> {
}; };
// resolve in module // 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)?; let ty = self.db.type_for_def(resolved)?;
// TODO we will need to add type variables for type parameters etc. here // TODO we will need to add type variables for type parameters etc. here
Ok(Some(ty)) Ok(Some(ty))

View file

@ -1,10 +1,10 @@
[86; 90) 'C(1)': [unknown] [86; 90) 'C(1)': [unknown]
[72; 153) '{ ...a.c; }': () [72; 153) '{ ...a.c; }': ()
[86; 87) 'C': C [86; 87) 'C': [unknown]
[107; 108) 'a': A [107; 108) 'a': A
[114; 132) 'A { b:... C() }': [unknown] [114; 132) 'A { b:... C() }': [unknown]
[138; 141) 'a.b': [unknown] [138; 141) 'a.b': [unknown]
[147; 150) 'a.c': [unknown] [147; 150) 'a.c': [unknown]
[96; 97) 'B': B [96; 97) 'B': [unknown]
[88; 89) '1': [unknown] [88; 89) '1': [unknown]
[82; 83) 'c': [unknown] [82; 83) 'c': [unknown]

View file

@ -55,6 +55,8 @@ impl Conv for CompletionItemKind {
CompletionItemKind::Snippet => Snippet, CompletionItemKind::Snippet => Snippet,
CompletionItemKind::Module => Module, CompletionItemKind::Module => Module,
CompletionItemKind::Function => Function, CompletionItemKind::Function => Function,
CompletionItemKind::Struct => Struct,
CompletionItemKind::Enum => Enum,
CompletionItemKind::Binding => Variable, CompletionItemKind::Binding => Variable,
} }
} }