]> git.lizzy.rs Git - rust.git/commitdiff
Do name resolution by namespace (types/values)
authorFlorian Diebold <flodiebold@gmail.com>
Mon, 24 Dec 2018 19:32:39 +0000 (20:32 +0100)
committerFlorian Diebold <flodiebold@gmail.com>
Tue, 25 Dec 2018 14:16:42 +0000 (15:16 +0100)
crates/ra_analysis/src/completion/complete_path.rs
crates/ra_analysis/src/completion/completion_item.rs
crates/ra_analysis/src/db.rs
crates/ra_hir/src/lib.rs
crates/ra_hir/src/module.rs
crates/ra_hir/src/module/nameres.rs
crates/ra_hir/src/module/nameres/tests.rs
crates/ra_hir/src/ty.rs
crates/ra_hir/src/ty/tests/data/0004_struct.txt
crates/ra_lsp_server/src/conv.rs

index ad4d68a3326b123185d1296a43175d803467019f..8c00be499eda28820d4e61fc03807fe7d0d4a1cc 100644 (file)
@@ -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(()),
     };
index 911f08468ea40b4b384e47ce85d8378b172456b7..6d466c8bdd40dfcc897291d271db0809b32f16a5 100644 (file)
@@ -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
     }
 }
index 7043a0f4d099b54cdf13374785432c4bbdf08721..677745d57db73e424e43ffff5d009d3e7a3932f5 100644 (file)
@@ -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;
         }
     }
 }
index 7e9824de9ca96b024d81440950efcf00e2f44373..81526fe9cab91c3d2662694d803b9a7714bbb0a1 100644 (file)
@@ -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<DefKind> {
+    pub(crate) fn for_syntax_kind(kind: SyntaxKind) -> PerNs<DefKind> {
         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<Def> {
                 let enum_def = Enum::new(self);
                 Def::Enum(enum_def)
             }
+            DefKind::StructCtor => Def::Item,
             DefKind::Item => Def::Item,
         };
         Ok(res)
index 89111995306d5cca58ee57029c6dfb665f38f725..e1a0e4b59536bbdce485e838d6c12c0b9910500a 100644 (file)
@@ -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<ModuleScope> {
         Ok(res)
     }
 
-    pub fn resolve_path(&self, db: &impl HirDatabase, path: Path) -> Cancelable<Option<DefId>> {
-        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<PerNs<DefId>> {
+        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<Opti
                     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)?;
-            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.
 ///
index 0b152a406230ab94005589b0afd4dd71c93c70bc..33c9d93c2886e76c3301a550af1be036c903f502 100644 (file)
@@ -118,22 +118,96 @@ enum ImportKind {
 #[derive(Debug, Clone, PartialEq, Eq)]
 pub struct Resolution {
     /// None for unresolved
-    pub def_id: Option<DefId>,
+    pub def_id: PerNs<DefId>,
     /// ident by whitch this is imported into local scope.
     pub import: Option<NamedImport>,
 }
 
-// #[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, Copy, Clone, PartialEq, Eq, Hash)]
+pub struct PerNs<T> {
+    pub types: 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
+    }
 
-// #[derive(Debug)]
-// struct PerNs<T> {
-//     types: Option<T>,
-//     values: Option<T>,
-// }
+    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 {
     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<DefId>) {
         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);
index 3e29c39541ab489423786d8fb978f566b6aff8ce..03ea5c1d6c0ee474a07becefcab8a99d22fb7e3b 100644 (file)
@@ -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]
index f86b749ec19d2e66874fad6ce26f2c6a83bc3b34..429292cfc9460be8745f80fbcc8ef93c17b5abe4 100644 (file)
     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<Option<Ty>> {
         };
 
         // 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))
index 70ad055ff308b782778a7e6891f7e63134eca836..a4371c5a531ad7fb56affb572a51520bab5ffcc8 100644 (file)
@@ -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]
index 051f1f995b3c920badd995c9326408fc83dee76d..af52893114c5a01c3d26dabb709d3cdaa80561bf 100644 (file)
@@ -55,6 +55,8 @@ fn conv(self) -> <Self as Conv>::Output {
             CompletionItemKind::Snippet => Snippet,
             CompletionItemKind::Module => Module,
             CompletionItemKind::Function => Function,
+            CompletionItemKind::Struct => Struct,
+            CompletionItemKind::Enum => Enum,
             CompletionItemKind::Binding => Variable,
         }
     }