diff options
Diffstat (limited to 'crates/ra_hir/src')
23 files changed, 2008 insertions, 1846 deletions
diff --git a/crates/ra_hir/src/code_model_api.rs b/crates/ra_hir/src/code_model_api.rs index 87a7195e6..b00481cd5 100644 --- a/crates/ra_hir/src/code_model_api.rs +++ b/crates/ra_hir/src/code_model_api.rs | |||
@@ -8,13 +8,12 @@ use crate::{ | |||
8 | Name, ScopesWithSourceMap, Ty, HirFileId, | 8 | Name, ScopesWithSourceMap, Ty, HirFileId, |
9 | HirDatabase, PersistentHirDatabase, | 9 | HirDatabase, PersistentHirDatabase, |
10 | type_ref::TypeRef, | 10 | type_ref::TypeRef, |
11 | nameres::{ModuleScope, Namespace, lower::ImportId}, | 11 | nameres::{ModuleScope, Namespace, ImportId, CrateModuleId}, |
12 | expr::{Body, BodySourceMap}, | 12 | expr::{Body, BodySourceMap}, |
13 | ty::InferenceResult, | 13 | ty::InferenceResult, |
14 | adt::{EnumVariantId, StructFieldId, VariantDef}, | 14 | adt::{EnumVariantId, StructFieldId, VariantDef}, |
15 | generics::GenericParams, | 15 | generics::GenericParams, |
16 | docs::{Documentation, Docs, docs_from_ast}, | 16 | docs::{Documentation, Docs, docs_from_ast}, |
17 | module_tree::ModuleId, | ||
18 | ids::{FunctionId, StructId, EnumId, AstItemDef, ConstId, StaticId, TraitId, TypeId}, | 17 | ids::{FunctionId, StructId, EnumId, AstItemDef, ConstId, StaticId, TraitId, TypeId}, |
19 | impl_block::ImplBlock, | 18 | impl_block::ImplBlock, |
20 | resolve::Resolver, | 19 | resolve::Resolver, |
@@ -65,7 +64,7 @@ impl Crate { | |||
65 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] | 64 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] |
66 | pub struct Module { | 65 | pub struct Module { |
67 | pub(crate) krate: Crate, | 66 | pub(crate) krate: Crate, |
68 | pub(crate) module_id: ModuleId, | 67 | pub(crate) module_id: CrateModuleId, |
69 | } | 68 | } |
70 | 69 | ||
71 | /// The defs which can be visible in the module. | 70 | /// The defs which can be visible in the module. |
@@ -173,7 +172,7 @@ impl Module { | |||
173 | 172 | ||
174 | /// Returns a `ModuleScope`: a set of items, visible in this module. | 173 | /// Returns a `ModuleScope`: a set of items, visible in this module. |
175 | pub fn scope(&self, db: &impl HirDatabase) -> ModuleScope { | 174 | pub fn scope(&self, db: &impl HirDatabase) -> ModuleScope { |
176 | db.item_map(self.krate)[self.module_id].clone() | 175 | db.crate_def_map(self.krate)[self.module_id].scope.clone() |
177 | } | 176 | } |
178 | 177 | ||
179 | pub fn problems(&self, db: &impl HirDatabase) -> Vec<(TreeArc<SyntaxNode>, Problem)> { | 178 | pub fn problems(&self, db: &impl HirDatabase) -> Vec<(TreeArc<SyntaxNode>, Problem)> { |
@@ -181,16 +180,16 @@ impl Module { | |||
181 | } | 180 | } |
182 | 181 | ||
183 | pub fn resolver(&self, db: &impl HirDatabase) -> Resolver { | 182 | pub fn resolver(&self, db: &impl HirDatabase) -> Resolver { |
184 | let item_map = db.item_map(self.krate); | 183 | let def_map = db.crate_def_map(self.krate); |
185 | Resolver::default().push_module_scope(item_map, *self) | 184 | Resolver::default().push_module_scope(def_map, self.module_id) |
186 | } | 185 | } |
187 | 186 | ||
188 | pub fn declarations(self, db: &impl HirDatabase) -> Vec<ModuleDef> { | 187 | pub fn declarations(self, db: &impl HirDatabase) -> Vec<ModuleDef> { |
189 | let lowered_module = db.lower_module(self); | 188 | let def_map = db.crate_def_map(self.krate); |
190 | lowered_module | 189 | def_map[self.module_id] |
191 | .declarations | 190 | .scope |
192 | .values() | 191 | .entries() |
193 | .cloned() | 192 | .filter_map(|(_name, res)| if res.import.is_none() { Some(res.def) } else { None }) |
194 | .flat_map(|per_ns| { | 193 | .flat_map(|per_ns| { |
195 | per_ns.take_types().into_iter().chain(per_ns.take_values().into_iter()) | 194 | per_ns.take_types().into_iter().chain(per_ns.take_values().into_iter()) |
196 | }) | 195 | }) |
diff --git a/crates/ra_hir/src/code_model_impl/krate.rs b/crates/ra_hir/src/code_model_impl/krate.rs index 161ae6e18..cc87c6f14 100644 --- a/crates/ra_hir/src/code_model_impl/krate.rs +++ b/crates/ra_hir/src/code_model_impl/krate.rs | |||
@@ -18,9 +18,7 @@ impl Crate { | |||
18 | .collect() | 18 | .collect() |
19 | } | 19 | } |
20 | pub(crate) fn root_module_impl(&self, db: &impl PersistentHirDatabase) -> Option<Module> { | 20 | pub(crate) fn root_module_impl(&self, db: &impl PersistentHirDatabase) -> Option<Module> { |
21 | let module_tree = db.module_tree(*self); | 21 | let module_id = db.crate_def_map(*self).root(); |
22 | let module_id = module_tree.modules().next()?; | ||
23 | |||
24 | let module = Module { krate: *self, module_id }; | 22 | let module = Module { krate: *self, module_id }; |
25 | Some(module) | 23 | Some(module) |
26 | } | 24 | } |
diff --git a/crates/ra_hir/src/code_model_impl/module.rs b/crates/ra_hir/src/code_model_impl/module.rs index 437f96942..f7d15c55e 100644 --- a/crates/ra_hir/src/code_model_impl/module.rs +++ b/crates/ra_hir/src/code_model_impl/module.rs | |||
@@ -1,33 +1,61 @@ | |||
1 | use ra_syntax::{ast, SyntaxNode, TreeArc}; | 1 | use ra_db::FileId; |
2 | use ra_syntax::{ast, SyntaxNode, TreeArc, AstNode}; | ||
2 | 3 | ||
3 | use crate::{ | 4 | use crate::{ |
4 | Module, ModuleSource, Problem, | 5 | Module, ModuleSource, Problem, Name, |
5 | Name, | 6 | nameres::{CrateModuleId, ImportId}, |
6 | module_tree::ModuleId, | ||
7 | nameres::lower::ImportId, | ||
8 | HirDatabase, PersistentHirDatabase, | 7 | HirDatabase, PersistentHirDatabase, |
9 | HirFileId | 8 | HirFileId, SourceItemId, |
10 | }; | 9 | }; |
11 | 10 | ||
11 | impl ModuleSource { | ||
12 | pub(crate) fn new( | ||
13 | db: &impl PersistentHirDatabase, | ||
14 | file_id: Option<FileId>, | ||
15 | decl_id: Option<SourceItemId>, | ||
16 | ) -> ModuleSource { | ||
17 | match (file_id, decl_id) { | ||
18 | (Some(file_id), _) => { | ||
19 | let source_file = db.parse(file_id); | ||
20 | ModuleSource::SourceFile(source_file) | ||
21 | } | ||
22 | (None, Some(item_id)) => { | ||
23 | let module = db.file_item(item_id); | ||
24 | let module = ast::Module::cast(&*module).unwrap(); | ||
25 | assert!(module.item_list().is_some(), "expected inline module"); | ||
26 | ModuleSource::Module(module.to_owned()) | ||
27 | } | ||
28 | (None, None) => panic!(), | ||
29 | } | ||
30 | } | ||
31 | } | ||
32 | |||
12 | impl Module { | 33 | impl Module { |
13 | fn with_module_id(&self, module_id: ModuleId) -> Module { | 34 | fn with_module_id(&self, module_id: CrateModuleId) -> Module { |
14 | Module { module_id, krate: self.krate } | 35 | Module { module_id, krate: self.krate } |
15 | } | 36 | } |
16 | 37 | ||
17 | pub(crate) fn name_impl(&self, db: &impl HirDatabase) -> Option<Name> { | 38 | pub(crate) fn name_impl(&self, db: &impl HirDatabase) -> Option<Name> { |
18 | let module_tree = db.module_tree(self.krate); | 39 | let def_map = db.crate_def_map(self.krate); |
19 | let link = self.module_id.parent_link(&module_tree)?; | 40 | let parent = def_map[self.module_id].parent?; |
20 | Some(link.name(&module_tree).clone()) | 41 | def_map[parent].children.iter().find_map(|(name, module_id)| { |
42 | if *module_id == self.module_id { | ||
43 | Some(name.clone()) | ||
44 | } else { | ||
45 | None | ||
46 | } | ||
47 | }) | ||
21 | } | 48 | } |
22 | 49 | ||
23 | pub(crate) fn definition_source_impl( | 50 | pub(crate) fn definition_source_impl( |
24 | &self, | 51 | &self, |
25 | db: &impl PersistentHirDatabase, | 52 | db: &impl PersistentHirDatabase, |
26 | ) -> (HirFileId, ModuleSource) { | 53 | ) -> (HirFileId, ModuleSource) { |
27 | let module_tree = db.module_tree(self.krate); | 54 | let def_map = db.crate_def_map(self.krate); |
28 | let file_id = self.module_id.file_id(&module_tree); | 55 | let decl_id = def_map[self.module_id].declaration; |
29 | let decl_id = self.module_id.decl_id(&module_tree); | 56 | let file_id = def_map[self.module_id].definition; |
30 | let module_source = ModuleSource::new(db, file_id, decl_id); | 57 | let module_source = ModuleSource::new(db, file_id, decl_id); |
58 | let file_id = file_id.map(HirFileId::from).unwrap_or_else(|| decl_id.unwrap().file_id); | ||
31 | (file_id, module_source) | 59 | (file_id, module_source) |
32 | } | 60 | } |
33 | 61 | ||
@@ -35,11 +63,11 @@ impl Module { | |||
35 | &self, | 63 | &self, |
36 | db: &impl HirDatabase, | 64 | db: &impl HirDatabase, |
37 | ) -> Option<(HirFileId, TreeArc<ast::Module>)> { | 65 | ) -> Option<(HirFileId, TreeArc<ast::Module>)> { |
38 | let module_tree = db.module_tree(self.krate); | 66 | let def_map = db.crate_def_map(self.krate); |
39 | let link = self.module_id.parent_link(&module_tree)?; | 67 | let decl = def_map[self.module_id].declaration?; |
40 | let file_id = link.owner(&module_tree).file_id(&module_tree); | 68 | let syntax_node = db.file_item(decl); |
41 | let src = link.source(&module_tree, db); | 69 | let ast = ast::Module::cast(&syntax_node).unwrap().to_owned(); |
42 | Some((file_id, src)) | 70 | Some((decl.file_id, ast)) |
43 | } | 71 | } |
44 | 72 | ||
45 | pub(crate) fn import_source_impl( | 73 | pub(crate) fn import_source_impl( |
@@ -47,22 +75,21 @@ impl Module { | |||
47 | db: &impl HirDatabase, | 75 | db: &impl HirDatabase, |
48 | import: ImportId, | 76 | import: ImportId, |
49 | ) -> TreeArc<ast::PathSegment> { | 77 | ) -> TreeArc<ast::PathSegment> { |
50 | let (_, source_map) = db.lower_module_with_source_map(*self); | 78 | let (file_id, source) = self.definition_source(db); |
51 | let (_, source) = self.definition_source(db); | 79 | let (_, source_map) = db.raw_items_with_source_map(file_id.original_file(db)); |
52 | source_map.get(&source, import) | 80 | source_map.get(&source, import) |
53 | } | 81 | } |
54 | 82 | ||
55 | pub(crate) fn crate_root_impl(&self, db: &impl PersistentHirDatabase) -> Module { | 83 | pub(crate) fn crate_root_impl(&self, db: &impl PersistentHirDatabase) -> Module { |
56 | let module_tree = db.module_tree(self.krate); | 84 | let def_map = db.crate_def_map(self.krate); |
57 | let module_id = self.module_id.crate_root(&module_tree); | 85 | self.with_module_id(def_map.root()) |
58 | self.with_module_id(module_id) | ||
59 | } | 86 | } |
60 | 87 | ||
61 | /// Finds a child module with the specified name. | 88 | /// Finds a child module with the specified name. |
62 | pub(crate) fn child_impl(&self, db: &impl HirDatabase, name: &Name) -> Option<Module> { | 89 | pub(crate) fn child_impl(&self, db: &impl HirDatabase, name: &Name) -> Option<Module> { |
63 | let module_tree = db.module_tree(self.krate); | 90 | let def_map = db.crate_def_map(self.krate); |
64 | let child_id = self.module_id.child(&module_tree, name)?; | 91 | let child_id = def_map[self.module_id].children.get(name)?; |
65 | Some(self.with_module_id(child_id)) | 92 | Some(self.with_module_id(*child_id)) |
66 | } | 93 | } |
67 | 94 | ||
68 | /// Iterates over all child modules. | 95 | /// Iterates over all child modules. |
@@ -70,18 +97,18 @@ impl Module { | |||
70 | &self, | 97 | &self, |
71 | db: &impl PersistentHirDatabase, | 98 | db: &impl PersistentHirDatabase, |
72 | ) -> impl Iterator<Item = Module> { | 99 | ) -> impl Iterator<Item = Module> { |
73 | let module_tree = db.module_tree(self.krate); | 100 | let def_map = db.crate_def_map(self.krate); |
74 | let children = self | 101 | let children = def_map[self.module_id] |
75 | .module_id | 102 | .children |
76 | .children(&module_tree) | 103 | .iter() |
77 | .map(|(_, module_id)| self.with_module_id(module_id)) | 104 | .map(|(_, module_id)| self.with_module_id(*module_id)) |
78 | .collect::<Vec<_>>(); | 105 | .collect::<Vec<_>>(); |
79 | children.into_iter() | 106 | children.into_iter() |
80 | } | 107 | } |
81 | 108 | ||
82 | pub(crate) fn parent_impl(&self, db: &impl PersistentHirDatabase) -> Option<Module> { | 109 | pub(crate) fn parent_impl(&self, db: &impl PersistentHirDatabase) -> Option<Module> { |
83 | let module_tree = db.module_tree(self.krate); | 110 | let def_map = db.crate_def_map(self.krate); |
84 | let parent_id = self.module_id.parent(&module_tree)?; | 111 | let parent_id = def_map[self.module_id].parent?; |
85 | Some(self.with_module_id(parent_id)) | 112 | Some(self.with_module_id(parent_id)) |
86 | } | 113 | } |
87 | 114 | ||
@@ -89,7 +116,14 @@ impl Module { | |||
89 | &self, | 116 | &self, |
90 | db: &impl HirDatabase, | 117 | db: &impl HirDatabase, |
91 | ) -> Vec<(TreeArc<SyntaxNode>, Problem)> { | 118 | ) -> Vec<(TreeArc<SyntaxNode>, Problem)> { |
92 | let module_tree = db.module_tree(self.krate); | 119 | let def_map = db.crate_def_map(self.krate); |
93 | self.module_id.problems(&module_tree, db) | 120 | let (my_file_id, _) = self.definition_source(db); |
121 | // FIXME: not entirely corret filterint by module | ||
122 | def_map | ||
123 | .problems() | ||
124 | .iter() | ||
125 | .filter(|(source_item_id, _problem)| my_file_id == source_item_id.file_id) | ||
126 | .map(|(source_item_id, problem)| (db.file_item(*source_item_id), problem.clone())) | ||
127 | .collect() | ||
94 | } | 128 | } |
95 | } | 129 | } |
diff --git a/crates/ra_hir/src/db.rs b/crates/ra_hir/src/db.rs index 5ad9547f1..c7bad7e2b 100644 --- a/crates/ra_hir/src/db.rs +++ b/crates/ra_hir/src/db.rs | |||
@@ -1,23 +1,18 @@ | |||
1 | use std::sync::Arc; | 1 | use std::sync::Arc; |
2 | 2 | ||
3 | use ra_syntax::{SyntaxNode, TreeArc, SourceFile}; | 3 | use ra_syntax::{SyntaxNode, TreeArc, SourceFile}; |
4 | use ra_db::{SourceDatabase, salsa}; | 4 | use ra_db::{SourceDatabase, salsa, FileId}; |
5 | 5 | ||
6 | use crate::{ | 6 | use crate::{ |
7 | MacroCallId, HirFileId, | 7 | HirFileId, SourceFileItems, SourceItemId, Crate, Module, HirInterner, |
8 | SourceFileItems, SourceItemId, Crate, Module, HirInterner, | ||
9 | Function, FnSignature, ExprScopes, TypeAlias, | 8 | Function, FnSignature, ExprScopes, TypeAlias, |
10 | Struct, Enum, StructField, | 9 | Struct, Enum, StructField, |
11 | Const, ConstSignature, Static, | 10 | Const, ConstSignature, Static, |
12 | macros::MacroExpansion, | 11 | nameres::{Namespace, ImportSourceMap, RawItems, CrateDefMap}, |
13 | module_tree::ModuleTree, | ||
14 | nameres::{ItemMap, lower::{LoweredModule, ImportSourceMap}}, | ||
15 | ty::{InferenceResult, Ty, method_resolution::CrateImplBlocks, TypableDef, CallableDef, FnSig}, | 12 | ty::{InferenceResult, Ty, method_resolution::CrateImplBlocks, TypableDef, CallableDef, FnSig}, |
16 | adt::{StructData, EnumData}, | 13 | adt::{StructData, EnumData}, |
17 | impl_block::{ModuleImplBlocks, ImplSourceMap}, | 14 | impl_block::{ModuleImplBlocks, ImplSourceMap}, |
18 | generics::{GenericParams, GenericDef}, | 15 | generics::{GenericParams, GenericDef}, |
19 | ids::SourceFileItemId, | ||
20 | nameres::Namespace, | ||
21 | type_ref::TypeRef, | 16 | type_ref::TypeRef, |
22 | }; | 17 | }; |
23 | 18 | ||
@@ -26,9 +21,6 @@ pub trait PersistentHirDatabase: SourceDatabase + AsRef<HirInterner> { | |||
26 | #[salsa::invoke(HirFileId::hir_parse)] | 21 | #[salsa::invoke(HirFileId::hir_parse)] |
27 | fn hir_parse(&self, file_id: HirFileId) -> TreeArc<SourceFile>; | 22 | fn hir_parse(&self, file_id: HirFileId) -> TreeArc<SourceFile>; |
28 | 23 | ||
29 | #[salsa::invoke(crate::macros::expand_macro_invocation)] | ||
30 | fn expand_macro_invocation(&self, invoc: MacroCallId) -> Option<Arc<MacroExpansion>>; | ||
31 | |||
32 | #[salsa::invoke(crate::adt::StructData::struct_data_query)] | 24 | #[salsa::invoke(crate::adt::StructData::struct_data_query)] |
33 | fn struct_data(&self, s: Struct) -> Arc<StructData>; | 25 | fn struct_data(&self, s: Struct) -> Arc<StructData>; |
34 | 26 | ||
@@ -41,27 +33,14 @@ pub trait PersistentHirDatabase: SourceDatabase + AsRef<HirInterner> { | |||
41 | #[salsa::invoke(crate::ids::SourceFileItems::file_item_query)] | 33 | #[salsa::invoke(crate::ids::SourceFileItems::file_item_query)] |
42 | fn file_item(&self, source_item_id: SourceItemId) -> TreeArc<SyntaxNode>; | 34 | fn file_item(&self, source_item_id: SourceItemId) -> TreeArc<SyntaxNode>; |
43 | 35 | ||
44 | #[salsa::invoke(crate::module_tree::Submodule::submodules_query)] | 36 | #[salsa::invoke(RawItems::raw_items_query)] |
45 | fn submodules( | 37 | fn raw_items(&self, file_id: FileId) -> Arc<RawItems>; |
46 | &self, | ||
47 | file_id: HirFileId, | ||
48 | delc_id: Option<SourceFileItemId>, | ||
49 | ) -> Arc<Vec<crate::module_tree::Submodule>>; | ||
50 | |||
51 | #[salsa::invoke(crate::nameres::lower::LoweredModule::lower_module_with_source_map_query)] | ||
52 | fn lower_module_with_source_map( | ||
53 | &self, | ||
54 | module: Module, | ||
55 | ) -> (Arc<LoweredModule>, Arc<ImportSourceMap>); | ||
56 | |||
57 | #[salsa::invoke(crate::nameres::lower::LoweredModule::lower_module_query)] | ||
58 | fn lower_module(&self, module: Module) -> Arc<LoweredModule>; | ||
59 | 38 | ||
60 | #[salsa::invoke(crate::nameres::ItemMap::item_map_query)] | 39 | #[salsa::invoke(RawItems::raw_items_with_source_map_query)] |
61 | fn item_map(&self, krate: Crate) -> Arc<ItemMap>; | 40 | fn raw_items_with_source_map(&self, file_id: FileId) -> (Arc<RawItems>, Arc<ImportSourceMap>); |
62 | 41 | ||
63 | #[salsa::invoke(crate::module_tree::ModuleTree::module_tree_query)] | 42 | #[salsa::invoke(CrateDefMap::crate_def_map_query)] |
64 | fn module_tree(&self, krate: Crate) -> Arc<ModuleTree>; | 43 | fn crate_def_map(&self, krate: Crate) -> Arc<CrateDefMap>; |
65 | 44 | ||
66 | #[salsa::invoke(crate::impl_block::impls_in_module)] | 45 | #[salsa::invoke(crate::impl_block::impls_in_module)] |
67 | fn impls_in_module(&self, module: Module) -> Arc<ModuleImplBlocks>; | 46 | fn impls_in_module(&self, module: Module) -> Arc<ModuleImplBlocks>; |
diff --git a/crates/ra_hir/src/ids.rs b/crates/ra_hir/src/ids.rs index 5b00330c6..9596488d3 100644 --- a/crates/ra_hir/src/ids.rs +++ b/crates/ra_hir/src/ids.rs | |||
@@ -83,30 +83,37 @@ impl HirFileId { | |||
83 | } | 83 | } |
84 | } | 84 | } |
85 | 85 | ||
86 | pub(crate) fn as_macro_call_id(self) -> Option<MacroCallId> { | ||
87 | match self.0 { | ||
88 | HirFileIdRepr::Macro(it) => Some(it), | ||
89 | _ => None, | ||
90 | } | ||
91 | } | ||
92 | |||
93 | pub(crate) fn hir_parse( | 86 | pub(crate) fn hir_parse( |
94 | db: &impl PersistentHirDatabase, | 87 | db: &impl PersistentHirDatabase, |
95 | file_id: HirFileId, | 88 | file_id: HirFileId, |
96 | ) -> TreeArc<SourceFile> { | 89 | ) -> TreeArc<SourceFile> { |
97 | match file_id.0 { | 90 | match file_id.0 { |
98 | HirFileIdRepr::File(file_id) => db.parse(file_id), | 91 | HirFileIdRepr::File(file_id) => db.parse(file_id), |
99 | HirFileIdRepr::Macro(m) => { | 92 | HirFileIdRepr::Macro(macro_call_id) => { |
100 | if let Some(exp) = db.expand_macro_invocation(m) { | ||
101 | return exp.file(); | ||
102 | } | ||
103 | // returning an empty string looks fishy... | 93 | // returning an empty string looks fishy... |
104 | SourceFile::parse("") | 94 | parse_macro(db, macro_call_id).unwrap_or_else(|| SourceFile::parse("")) |
105 | } | 95 | } |
106 | } | 96 | } |
107 | } | 97 | } |
108 | } | 98 | } |
109 | 99 | ||
100 | fn parse_macro( | ||
101 | db: &impl PersistentHirDatabase, | ||
102 | macro_call_id: MacroCallId, | ||
103 | ) -> Option<TreeArc<SourceFile>> { | ||
104 | let loc = macro_call_id.loc(db); | ||
105 | let syntax = db.file_item(loc.source_item_id); | ||
106 | let macro_call = ast::MacroCall::cast(&syntax).unwrap(); | ||
107 | let (macro_arg, _) = macro_call.token_tree().and_then(mbe::ast_to_token_tree)?; | ||
108 | |||
109 | let def_map = db.crate_def_map(loc.module.krate); | ||
110 | let (krate, macro_id) = def_map.resolve_macro(macro_call_id)?; | ||
111 | let def_map = db.crate_def_map(krate); | ||
112 | let macro_rules = &def_map[macro_id]; | ||
113 | let tt = macro_rules.expand(¯o_arg).ok()?; | ||
114 | Some(mbe::token_tree_to_ast_item_list(&tt)) | ||
115 | } | ||
116 | |||
110 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] | 117 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] |
111 | enum HirFileIdRepr { | 118 | enum HirFileIdRepr { |
112 | File(FileId), | 119 | File(FileId), |
@@ -200,8 +207,14 @@ pub(crate) trait AstItemDef<N: AstNode>: ArenaId + Clone { | |||
200 | fn interner(interner: &HirInterner) -> &LocationIntener<ItemLoc<N>, Self>; | 207 | fn interner(interner: &HirInterner) -> &LocationIntener<ItemLoc<N>, Self>; |
201 | fn from_ast(ctx: LocationCtx<&impl PersistentHirDatabase>, ast: &N) -> Self { | 208 | fn from_ast(ctx: LocationCtx<&impl PersistentHirDatabase>, ast: &N) -> Self { |
202 | let items = ctx.db.file_items(ctx.file_id); | 209 | let items = ctx.db.file_items(ctx.file_id); |
203 | let raw = | 210 | let item_id = items.id_of(ctx.file_id, ast.syntax()); |
204 | SourceItemId { file_id: ctx.file_id, item_id: items.id_of(ctx.file_id, ast.syntax()) }; | 211 | Self::from_source_item_id_unchecked(ctx, item_id) |
212 | } | ||
213 | fn from_source_item_id_unchecked( | ||
214 | ctx: LocationCtx<&impl PersistentHirDatabase>, | ||
215 | item_id: SourceFileItemId, | ||
216 | ) -> Self { | ||
217 | let raw = SourceItemId { file_id: ctx.file_id, item_id }; | ||
205 | let loc = ItemLoc { module: ctx.module, raw, _ty: PhantomData }; | 218 | let loc = ItemLoc { module: ctx.module, raw, _ty: PhantomData }; |
206 | 219 | ||
207 | Self::interner(ctx.db.as_ref()).loc2id(&loc) | 220 | Self::interner(ctx.db.as_ref()).loc2id(&loc) |
@@ -290,6 +303,12 @@ impl AstItemDef<ast::TypeAliasDef> for TypeId { | |||
290 | pub struct SourceFileItemId(RawId); | 303 | pub struct SourceFileItemId(RawId); |
291 | impl_arena_id!(SourceFileItemId); | 304 | impl_arena_id!(SourceFileItemId); |
292 | 305 | ||
306 | impl SourceFileItemId { | ||
307 | pub(crate) fn with_file_id(self, file_id: HirFileId) -> SourceItemId { | ||
308 | SourceItemId { file_id, item_id: self } | ||
309 | } | ||
310 | } | ||
311 | |||
293 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] | 312 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] |
294 | pub struct SourceItemId { | 313 | pub struct SourceItemId { |
295 | pub(crate) file_id: HirFileId, | 314 | pub(crate) file_id: HirFileId, |
@@ -309,9 +328,7 @@ impl SourceFileItems { | |||
309 | file_id: HirFileId, | 328 | file_id: HirFileId, |
310 | ) -> Arc<SourceFileItems> { | 329 | ) -> Arc<SourceFileItems> { |
311 | let source_file = db.hir_parse(file_id); | 330 | let source_file = db.hir_parse(file_id); |
312 | let mut res = SourceFileItems { file_id, arena: Arena::default() }; | 331 | Arc::new(SourceFileItems::from_source_file(&source_file, file_id)) |
313 | res.init(&source_file); | ||
314 | Arc::new(res) | ||
315 | } | 332 | } |
316 | 333 | ||
317 | pub(crate) fn file_item_query( | 334 | pub(crate) fn file_item_query( |
@@ -324,18 +341,23 @@ impl SourceFileItems { | |||
324 | .to_owned() | 341 | .to_owned() |
325 | } | 342 | } |
326 | 343 | ||
327 | fn init(&mut self, source_file: &SourceFile) { | 344 | pub(crate) fn from_source_file( |
345 | source_file: &SourceFile, | ||
346 | file_id: HirFileId, | ||
347 | ) -> SourceFileItems { | ||
348 | let mut res = SourceFileItems { file_id, arena: Arena::default() }; | ||
328 | // By walking the tree in bread-first order we make sure that parents | 349 | // By walking the tree in bread-first order we make sure that parents |
329 | // get lower ids then children. That is, adding a new child does not | 350 | // get lower ids then children. That is, adding a new child does not |
330 | // change parent's id. This means that, say, adding a new function to a | 351 | // change parent's id. This means that, say, adding a new function to a |
331 | // trait does not change ids of top-level items, which helps caching. | 352 | // trait does not change ids of top-level items, which helps caching. |
332 | bfs(source_file.syntax(), |it| { | 353 | bfs(source_file.syntax(), |it| { |
333 | if let Some(module_item) = ast::ModuleItem::cast(it) { | 354 | if let Some(module_item) = ast::ModuleItem::cast(it) { |
334 | self.alloc(module_item.syntax()); | 355 | res.alloc(module_item.syntax()); |
335 | } else if let Some(macro_call) = ast::MacroCall::cast(it) { | 356 | } else if let Some(macro_call) = ast::MacroCall::cast(it) { |
336 | self.alloc(macro_call.syntax()); | 357 | res.alloc(macro_call.syntax()); |
337 | } | 358 | } |
338 | }) | 359 | }); |
360 | res | ||
339 | } | 361 | } |
340 | 362 | ||
341 | fn alloc(&mut self, item: &SyntaxNode) -> SourceFileItemId { | 363 | fn alloc(&mut self, item: &SyntaxNode) -> SourceFileItemId { |
diff --git a/crates/ra_hir/src/impl_block.rs b/crates/ra_hir/src/impl_block.rs index eb2d4ed8d..8807a4b56 100644 --- a/crates/ra_hir/src/impl_block.rs +++ b/crates/ra_hir/src/impl_block.rs | |||
@@ -4,7 +4,8 @@ use rustc_hash::FxHashMap; | |||
4 | use ra_arena::{Arena, RawId, impl_arena_id, map::ArenaMap}; | 4 | use ra_arena::{Arena, RawId, impl_arena_id, map::ArenaMap}; |
5 | use ra_syntax::{ | 5 | use ra_syntax::{ |
6 | AstPtr, SourceFile, TreeArc, | 6 | AstPtr, SourceFile, TreeArc, |
7 | ast::{self, AstNode}}; | 7 | ast::{self, AstNode} |
8 | }; | ||
8 | 9 | ||
9 | use crate::{ | 10 | use crate::{ |
10 | Const, TypeAlias, Function, HirFileId, | 11 | Const, TypeAlias, Function, HirFileId, |
@@ -13,7 +14,7 @@ use crate::{ | |||
13 | type_ref::TypeRef, | 14 | type_ref::TypeRef, |
14 | ids::LocationCtx, | 15 | ids::LocationCtx, |
15 | resolve::Resolver, | 16 | resolve::Resolver, |
16 | ty::Ty, generics::GenericParams | 17 | ty::Ty, generics::GenericParams, |
17 | }; | 18 | }; |
18 | 19 | ||
19 | use crate::code_model_api::{Module, ModuleSource}; | 20 | use crate::code_model_api::{Module, ModuleSource}; |
diff --git a/crates/ra_hir/src/lib.rs b/crates/ra_hir/src/lib.rs index 4508a873e..75c977d32 100644 --- a/crates/ra_hir/src/lib.rs +++ b/crates/ra_hir/src/lib.rs | |||
@@ -24,9 +24,7 @@ mod path; | |||
24 | pub mod source_binder; | 24 | pub mod source_binder; |
25 | 25 | ||
26 | mod ids; | 26 | mod ids; |
27 | mod macros; | ||
28 | mod name; | 27 | mod name; |
29 | mod module_tree; | ||
30 | mod nameres; | 28 | mod nameres; |
31 | mod adt; | 29 | mod adt; |
32 | mod type_alias; | 30 | mod type_alias; |
@@ -54,8 +52,7 @@ pub use self::{ | |||
54 | path::{Path, PathKind}, | 52 | path::{Path, PathKind}, |
55 | name::Name, | 53 | name::Name, |
56 | ids::{HirFileId, MacroCallId, MacroCallLoc, HirInterner}, | 54 | ids::{HirFileId, MacroCallId, MacroCallLoc, HirInterner}, |
57 | macros::{MacroDef, MacroInput, MacroExpansion}, | 55 | nameres::{PerNs, Namespace}, |
58 | nameres::{ItemMap, PerNs, Namespace}, | ||
59 | ty::{Ty, Substs, display::HirDisplay}, | 56 | ty::{Ty, Substs, display::HirDisplay}, |
60 | impl_block::{ImplBlock, ImplItem}, | 57 | impl_block::{ImplBlock, ImplItem}, |
61 | docs::{Docs, Documentation}, | 58 | docs::{Docs, Documentation}, |
diff --git a/crates/ra_hir/src/macros.rs b/crates/ra_hir/src/macros.rs deleted file mode 100644 index 45128c7df..000000000 --- a/crates/ra_hir/src/macros.rs +++ /dev/null | |||
@@ -1,135 +0,0 @@ | |||
1 | /// Machinery for macro expansion. | ||
2 | /// | ||
3 | /// One of the more complicated things about macros is managing the source code | ||
4 | /// that is produced after expansion. See `HirFileId` and `MacroCallId` for how | ||
5 | /// do we do that. | ||
6 | /// | ||
7 | /// When the file-management question is resolved, all that is left is a | ||
8 | /// token-tree-to-token-tree transformation plus hygiene. We don't have either of | ||
9 | /// those yet, so all macros are string based at the moment! | ||
10 | use std::sync::Arc; | ||
11 | |||
12 | use ra_syntax::{ | ||
13 | TextRange, TextUnit, SourceFile, AstNode, SyntaxNode, TreeArc, SyntaxNodePtr, | ||
14 | ast, | ||
15 | }; | ||
16 | |||
17 | use crate::{MacroCallId, PersistentHirDatabase}; | ||
18 | |||
19 | // Hard-coded defs for now :-( | ||
20 | #[derive(Debug, Clone, PartialEq, Eq)] | ||
21 | pub enum MacroDef { | ||
22 | Vec, | ||
23 | } | ||
24 | |||
25 | impl MacroDef { | ||
26 | /// Expands macro call, returning the expansion and offset to be used to | ||
27 | /// convert ranges between expansion and original source. | ||
28 | pub fn ast_expand(macro_call: &ast::MacroCall) -> Option<(TextUnit, MacroExpansion)> { | ||
29 | let (def, input) = MacroDef::from_call(macro_call)?; | ||
30 | let exp = def.expand(input)?; | ||
31 | let off = macro_call.token_tree()?.syntax().range().start(); | ||
32 | Some((off, exp)) | ||
33 | } | ||
34 | |||
35 | fn from_call(macro_call: &ast::MacroCall) -> Option<(MacroDef, MacroInput)> { | ||
36 | let def = { | ||
37 | let path = macro_call.path()?; | ||
38 | let name_ref = path.segment()?.name_ref()?; | ||
39 | if name_ref.text() == "vec" { | ||
40 | MacroDef::Vec | ||
41 | } else { | ||
42 | return None; | ||
43 | } | ||
44 | }; | ||
45 | |||
46 | let input = { | ||
47 | let arg = macro_call.token_tree()?.syntax(); | ||
48 | MacroInput { text: arg.text().to_string() } | ||
49 | }; | ||
50 | Some((def, input)) | ||
51 | } | ||
52 | |||
53 | fn expand(self, input: MacroInput) -> Option<MacroExpansion> { | ||
54 | match self { | ||
55 | MacroDef::Vec => self.expand_vec(input), | ||
56 | } | ||
57 | } | ||
58 | fn expand_vec(self, input: MacroInput) -> Option<MacroExpansion> { | ||
59 | let text = format!(r"fn dummy() {{ {}; }}", input.text); | ||
60 | let file = SourceFile::parse(&text); | ||
61 | let array_expr = file.syntax().descendants().find_map(ast::ArrayExpr::cast)?; | ||
62 | let ptr = SyntaxNodePtr::new(array_expr.syntax()); | ||
63 | let src_range = TextRange::offset_len(0.into(), TextUnit::of_str(&input.text)); | ||
64 | let ranges_map = vec![(src_range, array_expr.syntax().range())]; | ||
65 | let res = MacroExpansion { text, ranges_map, ptr }; | ||
66 | Some(res) | ||
67 | } | ||
68 | } | ||
69 | |||
70 | #[derive(Debug, Clone, PartialEq, Eq)] | ||
71 | pub struct MacroInput { | ||
72 | // Should be token trees | ||
73 | pub text: String, | ||
74 | } | ||
75 | |||
76 | #[derive(Debug, Clone, PartialEq, Eq)] | ||
77 | pub struct MacroExpansion { | ||
78 | /// The result of macro expansion. Should be token tree as well. | ||
79 | text: String, | ||
80 | /// Correspondence between ranges in the original source code and ranges in | ||
81 | /// the macro. | ||
82 | ranges_map: Vec<(TextRange, TextRange)>, | ||
83 | /// Implementation detail: internally, a macro is expanded to the whole file, | ||
84 | /// even if it is an expression. This `ptr` selects the actual expansion from | ||
85 | /// the expanded file. | ||
86 | ptr: SyntaxNodePtr, | ||
87 | } | ||
88 | |||
89 | impl MacroExpansion { | ||
90 | // FIXME: does not really make sense, macro expansion is not necessary a | ||
91 | // whole file. See `MacroExpansion::ptr` as well. | ||
92 | pub(crate) fn file(&self) -> TreeArc<SourceFile> { | ||
93 | SourceFile::parse(&self.text) | ||
94 | } | ||
95 | |||
96 | pub fn syntax(&self) -> TreeArc<SyntaxNode> { | ||
97 | self.ptr.to_node(&self.file()).to_owned() | ||
98 | } | ||
99 | /// Maps range in the source code to the range in the expanded code. | ||
100 | pub fn map_range_forward(&self, src_range: TextRange) -> Option<TextRange> { | ||
101 | for (s_range, t_range) in self.ranges_map.iter() { | ||
102 | if src_range.is_subrange(&s_range) { | ||
103 | let src_at_zero_range = src_range - src_range.start(); | ||
104 | let src_range_offset = src_range.start() - s_range.start(); | ||
105 | let src_range = src_at_zero_range + src_range_offset + t_range.start(); | ||
106 | return Some(src_range); | ||
107 | } | ||
108 | } | ||
109 | None | ||
110 | } | ||
111 | /// Maps range in the expanded code to the range in the source code. | ||
112 | pub fn map_range_back(&self, tgt_range: TextRange) -> Option<TextRange> { | ||
113 | for (s_range, t_range) in self.ranges_map.iter() { | ||
114 | if tgt_range.is_subrange(&t_range) { | ||
115 | let tgt_at_zero_range = tgt_range - tgt_range.start(); | ||
116 | let tgt_range_offset = tgt_range.start() - t_range.start(); | ||
117 | let src_range = tgt_at_zero_range + tgt_range_offset + s_range.start(); | ||
118 | return Some(src_range); | ||
119 | } | ||
120 | } | ||
121 | None | ||
122 | } | ||
123 | } | ||
124 | |||
125 | pub(crate) fn expand_macro_invocation( | ||
126 | db: &impl PersistentHirDatabase, | ||
127 | invoc: MacroCallId, | ||
128 | ) -> Option<Arc<MacroExpansion>> { | ||
129 | let loc = invoc.loc(db); | ||
130 | let syntax = db.file_item(loc.source_item_id); | ||
131 | let macro_call = ast::MacroCall::cast(&syntax).unwrap(); | ||
132 | |||
133 | let (def, input) = MacroDef::from_call(macro_call)?; | ||
134 | def.expand(input).map(Arc::new) | ||
135 | } | ||
diff --git a/crates/ra_hir/src/marks.rs b/crates/ra_hir/src/marks.rs index 16852a6a1..bbf57004d 100644 --- a/crates/ra_hir/src/marks.rs +++ b/crates/ra_hir/src/marks.rs | |||
@@ -1,6 +1,7 @@ | |||
1 | test_utils::marks!( | 1 | test_utils::marks!( |
2 | bogus_paths | ||
2 | name_res_works_for_broken_modules | 3 | name_res_works_for_broken_modules |
3 | item_map_enum_importing | 4 | can_import_enum_variant |
4 | type_var_cycles_resolve_completely | 5 | type_var_cycles_resolve_completely |
5 | type_var_cycles_resolve_as_possible | 6 | type_var_cycles_resolve_as_possible |
6 | type_var_resolves_to_int_var | 7 | type_var_resolves_to_int_var |
diff --git a/crates/ra_hir/src/module_tree.rs b/crates/ra_hir/src/module_tree.rs index 99c2115e1..e69de29bb 100644 --- a/crates/ra_hir/src/module_tree.rs +++ b/crates/ra_hir/src/module_tree.rs | |||
@@ -1,331 +0,0 @@ | |||
1 | use std::sync::Arc; | ||
2 | |||
3 | use arrayvec::ArrayVec; | ||
4 | use relative_path::RelativePathBuf; | ||
5 | use ra_db::{FileId, SourceRoot}; | ||
6 | use ra_syntax::{ | ||
7 | SyntaxNode, TreeArc, | ||
8 | algo::generate, | ||
9 | ast::{self, AstNode, NameOwner}, | ||
10 | }; | ||
11 | use ra_arena::{Arena, RawId, impl_arena_id}; | ||
12 | use test_utils::tested_by; | ||
13 | |||
14 | use crate::{ | ||
15 | Name, AsName, HirDatabase, SourceItemId, HirFileId, Problem, SourceFileItems, ModuleSource, | ||
16 | PersistentHirDatabase, | ||
17 | Crate, | ||
18 | ids::SourceFileItemId, | ||
19 | }; | ||
20 | |||
21 | impl ModuleSource { | ||
22 | pub(crate) fn new( | ||
23 | db: &impl PersistentHirDatabase, | ||
24 | file_id: HirFileId, | ||
25 | decl_id: Option<SourceFileItemId>, | ||
26 | ) -> ModuleSource { | ||
27 | match decl_id { | ||
28 | Some(item_id) => { | ||
29 | let module = db.file_item(SourceItemId { file_id, item_id }); | ||
30 | let module = ast::Module::cast(&*module).unwrap(); | ||
31 | assert!(module.item_list().is_some(), "expected inline module"); | ||
32 | ModuleSource::Module(module.to_owned()) | ||
33 | } | ||
34 | None => { | ||
35 | let source_file = db.hir_parse(file_id); | ||
36 | ModuleSource::SourceFile(source_file) | ||
37 | } | ||
38 | } | ||
39 | } | ||
40 | } | ||
41 | |||
42 | #[derive(Clone, Hash, PartialEq, Eq, Debug)] | ||
43 | pub struct Submodule { | ||
44 | name: Name, | ||
45 | is_declaration: bool, | ||
46 | decl_id: SourceFileItemId, | ||
47 | } | ||
48 | |||
49 | impl Submodule { | ||
50 | pub(crate) fn submodules_query( | ||
51 | db: &impl PersistentHirDatabase, | ||
52 | file_id: HirFileId, | ||
53 | decl_id: Option<SourceFileItemId>, | ||
54 | ) -> Arc<Vec<Submodule>> { | ||
55 | db.check_canceled(); | ||
56 | let file_items = db.file_items(file_id); | ||
57 | let module_source = ModuleSource::new(db, file_id, decl_id); | ||
58 | let submodules = match module_source { | ||
59 | ModuleSource::SourceFile(source_file) => { | ||
60 | collect_submodules(file_id, &file_items, &*source_file) | ||
61 | } | ||
62 | ModuleSource::Module(module) => { | ||
63 | collect_submodules(file_id, &file_items, module.item_list().unwrap()) | ||
64 | } | ||
65 | }; | ||
66 | |||
67 | return Arc::new(submodules); | ||
68 | |||
69 | fn collect_submodules( | ||
70 | file_id: HirFileId, | ||
71 | file_items: &SourceFileItems, | ||
72 | root: &impl ast::ModuleItemOwner, | ||
73 | ) -> Vec<Submodule> { | ||
74 | root.items() | ||
75 | .filter_map(|item| match item.kind() { | ||
76 | ast::ModuleItemKind::Module(m) => Some(m), | ||
77 | _ => None, | ||
78 | }) | ||
79 | .filter_map(|module| { | ||
80 | let name = module.name()?.as_name(); | ||
81 | if !module.has_semi() && module.item_list().is_none() { | ||
82 | tested_by!(name_res_works_for_broken_modules); | ||
83 | return None; | ||
84 | } | ||
85 | let sub = Submodule { | ||
86 | name, | ||
87 | is_declaration: module.has_semi(), | ||
88 | decl_id: file_items.id_of(file_id, module.syntax()), | ||
89 | }; | ||
90 | Some(sub) | ||
91 | }) | ||
92 | .collect() | ||
93 | } | ||
94 | } | ||
95 | } | ||
96 | |||
97 | #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] | ||
98 | pub struct ModuleId(RawId); | ||
99 | impl_arena_id!(ModuleId); | ||
100 | |||
101 | #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] | ||
102 | pub struct LinkId(RawId); | ||
103 | impl_arena_id!(LinkId); | ||
104 | |||
105 | /// Physically, rust source is organized as a set of files, but logically it is | ||
106 | /// organized as a tree of modules. Usually, a single file corresponds to a | ||
107 | /// single module, but it is not neccessarily always the case. | ||
108 | /// | ||
109 | /// `ModuleTree` encapsulates the logic of transitioning from the fuzzy world of files | ||
110 | /// (which can have multiple parents) to the precise world of modules (which | ||
111 | /// always have one parent). | ||
112 | #[derive(Default, Debug, PartialEq, Eq)] | ||
113 | pub struct ModuleTree { | ||
114 | mods: Arena<ModuleId, ModuleData>, | ||
115 | links: Arena<LinkId, LinkData>, | ||
116 | } | ||
117 | |||
118 | #[derive(Debug, PartialEq, Eq)] | ||
119 | pub struct ModuleData { | ||
120 | file_id: HirFileId, | ||
121 | /// Points to `ast::Module`, `None` for the whole file. | ||
122 | decl_id: Option<SourceFileItemId>, | ||
123 | parent: Option<LinkId>, | ||
124 | children: Vec<LinkId>, | ||
125 | } | ||
126 | |||
127 | #[derive(Hash, Debug, PartialEq, Eq)] | ||
128 | struct LinkData { | ||
129 | source: SourceItemId, | ||
130 | owner: ModuleId, | ||
131 | name: Name, | ||
132 | points_to: Vec<ModuleId>, | ||
133 | problem: Option<Problem>, | ||
134 | } | ||
135 | |||
136 | impl ModuleTree { | ||
137 | pub(crate) fn module_tree_query( | ||
138 | db: &impl PersistentHirDatabase, | ||
139 | krate: Crate, | ||
140 | ) -> Arc<ModuleTree> { | ||
141 | db.check_canceled(); | ||
142 | let mut res = ModuleTree::default(); | ||
143 | res.init_crate(db, krate); | ||
144 | Arc::new(res) | ||
145 | } | ||
146 | |||
147 | pub(crate) fn modules<'a>(&'a self) -> impl Iterator<Item = ModuleId> + 'a { | ||
148 | self.mods.iter().map(|(id, _)| id) | ||
149 | } | ||
150 | |||
151 | pub(crate) fn find_module_by_source( | ||
152 | &self, | ||
153 | file_id: HirFileId, | ||
154 | decl_id: Option<SourceFileItemId>, | ||
155 | ) -> Option<ModuleId> { | ||
156 | let (res, _) = | ||
157 | self.mods.iter().find(|(_, m)| (m.file_id, m.decl_id) == (file_id, decl_id))?; | ||
158 | Some(res) | ||
159 | } | ||
160 | |||
161 | fn init_crate(&mut self, db: &impl PersistentHirDatabase, krate: Crate) { | ||
162 | let crate_graph = db.crate_graph(); | ||
163 | let file_id = crate_graph.crate_root(krate.crate_id); | ||
164 | let source_root_id = db.file_source_root(file_id); | ||
165 | |||
166 | let source_root = db.source_root(source_root_id); | ||
167 | self.init_subtree(db, &source_root, None, file_id.into(), None); | ||
168 | } | ||
169 | |||
170 | fn init_subtree( | ||
171 | &mut self, | ||
172 | db: &impl PersistentHirDatabase, | ||
173 | source_root: &SourceRoot, | ||
174 | parent: Option<LinkId>, | ||
175 | file_id: HirFileId, | ||
176 | decl_id: Option<SourceFileItemId>, | ||
177 | ) -> ModuleId { | ||
178 | let is_root = parent.is_none(); | ||
179 | let id = self.alloc_mod(ModuleData { file_id, decl_id, parent, children: Vec::new() }); | ||
180 | for sub in db.submodules(file_id, decl_id).iter() { | ||
181 | let link = self.alloc_link(LinkData { | ||
182 | source: SourceItemId { file_id, item_id: sub.decl_id }, | ||
183 | name: sub.name.clone(), | ||
184 | owner: id, | ||
185 | points_to: Vec::new(), | ||
186 | problem: None, | ||
187 | }); | ||
188 | |||
189 | let (points_to, problem) = if sub.is_declaration { | ||
190 | let (points_to, problem) = resolve_submodule(db, file_id, &sub.name, is_root); | ||
191 | let points_to = points_to | ||
192 | .into_iter() | ||
193 | .map(|file_id| { | ||
194 | self.init_subtree(db, source_root, Some(link), file_id.into(), None) | ||
195 | }) | ||
196 | .collect::<Vec<_>>(); | ||
197 | (points_to, problem) | ||
198 | } else { | ||
199 | let points_to = | ||
200 | self.init_subtree(db, source_root, Some(link), file_id, Some(sub.decl_id)); | ||
201 | (vec![points_to], None) | ||
202 | }; | ||
203 | |||
204 | self.links[link].points_to = points_to; | ||
205 | self.links[link].problem = problem; | ||
206 | } | ||
207 | id | ||
208 | } | ||
209 | |||
210 | fn alloc_mod(&mut self, data: ModuleData) -> ModuleId { | ||
211 | self.mods.alloc(data) | ||
212 | } | ||
213 | |||
214 | fn alloc_link(&mut self, data: LinkData) -> LinkId { | ||
215 | let owner = data.owner; | ||
216 | let id = self.links.alloc(data); | ||
217 | self.mods[owner].children.push(id); | ||
218 | id | ||
219 | } | ||
220 | } | ||
221 | |||
222 | impl ModuleId { | ||
223 | pub(crate) fn file_id(self, tree: &ModuleTree) -> HirFileId { | ||
224 | tree.mods[self].file_id | ||
225 | } | ||
226 | pub(crate) fn decl_id(self, tree: &ModuleTree) -> Option<SourceFileItemId> { | ||
227 | tree.mods[self].decl_id | ||
228 | } | ||
229 | pub(crate) fn parent_link(self, tree: &ModuleTree) -> Option<LinkId> { | ||
230 | tree.mods[self].parent | ||
231 | } | ||
232 | pub(crate) fn parent(self, tree: &ModuleTree) -> Option<ModuleId> { | ||
233 | let link = self.parent_link(tree)?; | ||
234 | Some(tree.links[link].owner) | ||
235 | } | ||
236 | pub(crate) fn crate_root(self, tree: &ModuleTree) -> ModuleId { | ||
237 | generate(Some(self), move |it| it.parent(tree)).last().unwrap() | ||
238 | } | ||
239 | pub(crate) fn child(self, tree: &ModuleTree, name: &Name) -> Option<ModuleId> { | ||
240 | let link = tree.mods[self] | ||
241 | .children | ||
242 | .iter() | ||
243 | .map(|&it| &tree.links[it]) | ||
244 | .find(|it| it.name == *name)?; | ||
245 | Some(*link.points_to.first()?) | ||
246 | } | ||
247 | pub(crate) fn children<'a>( | ||
248 | self, | ||
249 | tree: &'a ModuleTree, | ||
250 | ) -> impl Iterator<Item = (Name, ModuleId)> + 'a { | ||
251 | tree.mods[self].children.iter().filter_map(move |&it| { | ||
252 | let link = &tree.links[it]; | ||
253 | let module = *link.points_to.first()?; | ||
254 | Some((link.name.clone(), module)) | ||
255 | }) | ||
256 | } | ||
257 | pub(crate) fn problems( | ||
258 | self, | ||
259 | tree: &ModuleTree, | ||
260 | db: &impl HirDatabase, | ||
261 | ) -> Vec<(TreeArc<SyntaxNode>, Problem)> { | ||
262 | tree.mods[self] | ||
263 | .children | ||
264 | .iter() | ||
265 | .filter_map(|&link| { | ||
266 | let p = tree.links[link].problem.clone()?; | ||
267 | let s = link.source(tree, db); | ||
268 | let s = s.name().unwrap().syntax().to_owned(); | ||
269 | Some((s, p)) | ||
270 | }) | ||
271 | .collect() | ||
272 | } | ||
273 | } | ||
274 | |||
275 | impl LinkId { | ||
276 | pub(crate) fn owner(self, tree: &ModuleTree) -> ModuleId { | ||
277 | tree.links[self].owner | ||
278 | } | ||
279 | pub(crate) fn name(self, tree: &ModuleTree) -> &Name { | ||
280 | &tree.links[self].name | ||
281 | } | ||
282 | pub(crate) fn source( | ||
283 | self, | ||
284 | tree: &ModuleTree, | ||
285 | db: &impl PersistentHirDatabase, | ||
286 | ) -> TreeArc<ast::Module> { | ||
287 | let syntax_node = db.file_item(tree.links[self].source); | ||
288 | ast::Module::cast(&syntax_node).unwrap().to_owned() | ||
289 | } | ||
290 | } | ||
291 | |||
292 | fn resolve_submodule( | ||
293 | db: &impl PersistentHirDatabase, | ||
294 | file_id: HirFileId, | ||
295 | name: &Name, | ||
296 | is_root: bool, | ||
297 | ) -> (Vec<FileId>, Option<Problem>) { | ||
298 | // FIXME: handle submodules of inline modules properly | ||
299 | let file_id = file_id.original_file(db); | ||
300 | let source_root_id = db.file_source_root(file_id); | ||
301 | let path = db.file_relative_path(file_id); | ||
302 | let root = RelativePathBuf::default(); | ||
303 | let dir_path = path.parent().unwrap_or(&root); | ||
304 | let mod_name = path.file_stem().unwrap_or("unknown"); | ||
305 | let is_dir_owner = is_root || mod_name == "mod"; | ||
306 | |||
307 | let file_mod = dir_path.join(format!("{}.rs", name)); | ||
308 | let dir_mod = dir_path.join(format!("{}/mod.rs", name)); | ||
309 | let file_dir_mod = dir_path.join(format!("{}/{}.rs", mod_name, name)); | ||
310 | let mut candidates = ArrayVec::<[_; 2]>::new(); | ||
311 | if is_dir_owner { | ||
312 | candidates.push(file_mod.clone()); | ||
313 | candidates.push(dir_mod); | ||
314 | } else { | ||
315 | candidates.push(file_dir_mod.clone()); | ||
316 | }; | ||
317 | let sr = db.source_root(source_root_id); | ||
318 | let points_to = candidates | ||
319 | .into_iter() | ||
320 | .filter_map(|path| sr.files.get(&path)) | ||
321 | .map(|&it| it) | ||
322 | .collect::<Vec<_>>(); | ||
323 | let problem = if points_to.is_empty() { | ||
324 | Some(Problem::UnresolvedModule { | ||
325 | candidate: if is_dir_owner { file_mod } else { file_dir_mod }, | ||
326 | }) | ||
327 | } else { | ||
328 | None | ||
329 | }; | ||
330 | (points_to, problem) | ||
331 | } | ||
diff --git a/crates/ra_hir/src/name.rs b/crates/ra_hir/src/name.rs index 8d786d2ac..06bafa6f0 100644 --- a/crates/ra_hir/src/name.rs +++ b/crates/ra_hir/src/name.rs | |||
@@ -64,6 +64,7 @@ impl Name { | |||
64 | "str" => KnownName::Str, | 64 | "str" => KnownName::Str, |
65 | "Self" => KnownName::SelfType, | 65 | "Self" => KnownName::SelfType, |
66 | "self" => KnownName::SelfParam, | 66 | "self" => KnownName::SelfParam, |
67 | "macro_rules" => KnownName::MacroRules, | ||
67 | _ => return None, | 68 | _ => return None, |
68 | }; | 69 | }; |
69 | Some(name) | 70 | Some(name) |
@@ -122,4 +123,6 @@ pub(crate) enum KnownName { | |||
122 | 123 | ||
123 | SelfType, | 124 | SelfType, |
124 | SelfParam, | 125 | SelfParam, |
126 | |||
127 | MacroRules, | ||
125 | } | 128 | } |
diff --git a/crates/ra_hir/src/nameres.rs b/crates/ra_hir/src/nameres.rs index 73919ee37..edd2f25f7 100644 --- a/crates/ra_hir/src/nameres.rs +++ b/crates/ra_hir/src/nameres.rs | |||
@@ -1,60 +1,148 @@ | |||
1 | //! Name resolution algorithm. The end result of the algorithm is an `ItemMap`: | 1 | /// This module implements import-resolution/macro expansion algorithm. |
2 | //! a map which maps each module to its scope: the set of items visible in the | 2 | /// |
3 | //! module. That is, we only resolve imports here, name resolution of item | 3 | /// The result of this module is `CrateDefMap`: a datastructure which contains: |
4 | //! bodies will be done in a separate step. | 4 | /// |
5 | //! | 5 | /// * a tree of modules for the crate |
6 | //! Like Rustc, we use an interactive per-crate algorithm: we start with scopes | 6 | /// * for each module, a set of items visible in the module (directly declared |
7 | //! containing only directly defined items, and then iteratively resolve | 7 | /// or imported) |
8 | //! imports. | 8 | /// |
9 | //! | 9 | /// Note that `CrateDefMap` contains fully macro expanded code. |
10 | //! To make this work nicely in the IDE scenario, we place `InputModuleItems` | 10 | /// |
11 | //! in between raw syntax and name resolution. `InputModuleItems` are computed | 11 | /// Computing `CrateDefMap` can be partitioned into several logically |
12 | //! using only the module's syntax, and it is all directly defined items plus | 12 | /// independent "phases". The phases are mutually recursive though, there's no |
13 | //! imports. The plan is to make `InputModuleItems` independent of local | 13 | /// strict ordering. |
14 | //! modifications (that is, typing inside a function should not change IMIs), | 14 | /// |
15 | //! so that the results of name resolution can be preserved unless the module | 15 | /// ## Collecting RawItems |
16 | //! structure itself is modified. | 16 | /// |
17 | pub(crate) mod lower; | 17 | /// This happens in the `raw` module, which parses a single source file into a |
18 | 18 | /// set of top-level items. Nested imports are desugared to flat imports in | |
19 | use std::{time, sync::Arc}; | 19 | /// this phase. Macro calls are represented as a triple of (Path, Option<Name>, |
20 | 20 | /// TokenTree). | |
21 | use rustc_hash::{FxHashMap, FxHashSet}; | 21 | /// |
22 | 22 | /// ## Collecting Modules | |
23 | use ra_arena::map::ArenaMap; | 23 | /// |
24 | use ra_db::Edition; | 24 | /// This happens in the `collector` module. In this phase, we recursively walk |
25 | /// tree of modules, collect raw items from submodules, populate module scopes | ||
26 | /// with defined items (so, we assign item ids in this phase) and record the set | ||
27 | /// of unresolved imports and macros. | ||
28 | /// | ||
29 | /// While we walk tree of modules, we also record macro_rules definitions and | ||
30 | /// expand calls to macro_rules defined macros. | ||
31 | /// | ||
32 | /// ## Resolving Imports | ||
33 | /// | ||
34 | /// We maintain a list of currently unresolved imports. On every iteration, we | ||
35 | /// try to resolve some imports from this list. If the import is resolved, we | ||
36 | /// record it, by adding an item to current module scope and, if necessary, by | ||
37 | /// recursively populating glob imports. | ||
38 | /// | ||
39 | /// ## Resolving Macros | ||
40 | /// | ||
41 | /// macro_rules from the same crate use a global mutable namespace. We expand | ||
42 | /// them immediately, when we collect modules. | ||
43 | /// | ||
44 | /// Macros from other crates (including proc-macros) can be used with | ||
45 | /// `foo::bar!` syntax. We handle them similarly to imports. There's a list of | ||
46 | /// unexpanded macros. On every iteration, we try to resolve each macro call | ||
47 | /// path and, upon success, we run macro expansion and "collect module" phase | ||
48 | /// on the result | ||
49 | |||
50 | mod per_ns; | ||
51 | mod raw; | ||
52 | mod collector; | ||
53 | #[cfg(test)] | ||
54 | mod tests; | ||
55 | |||
56 | use std::sync::Arc; | ||
57 | |||
58 | use rustc_hash::FxHashMap; | ||
59 | use ra_arena::{Arena, RawId, impl_arena_id}; | ||
60 | use ra_db::{FileId, Edition}; | ||
25 | use test_utils::tested_by; | 61 | use test_utils::tested_by; |
26 | 62 | ||
27 | use crate::{ | 63 | use crate::{ |
28 | Module, ModuleDef, | 64 | ModuleDef, Name, Crate, Module, Problem, |
29 | Path, PathKind, PersistentHirDatabase, | 65 | PersistentHirDatabase, Path, PathKind, HirFileId, |
30 | Crate, Name, | 66 | ids::{SourceItemId, SourceFileItemId, MacroCallId}, |
31 | module_tree::{ModuleId, ModuleTree}, | ||
32 | nameres::lower::{ImportId, LoweredModule, ImportData}, | ||
33 | }; | 67 | }; |
34 | 68 | ||
35 | /// `ItemMap` is the result of module name resolution. It contains, for each | 69 | pub(crate) use self::raw::{RawItems, ImportId, ImportSourceMap}; |
36 | /// module, the set of visible items. | 70 | |
71 | pub use self::per_ns::{PerNs, Namespace}; | ||
72 | |||
73 | /// Contans all top-level defs from a macro-expanded crate | ||
37 | #[derive(Debug, PartialEq, Eq)] | 74 | #[derive(Debug, PartialEq, Eq)] |
38 | pub struct ItemMap { | 75 | pub struct CrateDefMap { |
76 | krate: Crate, | ||
39 | edition: Edition, | 77 | edition: Edition, |
40 | /// The prelude module for this crate. This either comes from an import | 78 | /// The prelude module for this crate. This either comes from an import |
41 | /// marked with the `prelude_import` attribute, or (in the normal case) from | 79 | /// marked with the `prelude_import` attribute, or (in the normal case) from |
42 | /// a dependency (`std` or `core`). | 80 | /// a dependency (`std` or `core`). |
43 | pub(crate) prelude: Option<Module>, | 81 | prelude: Option<Module>, |
44 | pub(crate) extern_prelude: FxHashMap<Name, ModuleDef>, | 82 | extern_prelude: FxHashMap<Name, ModuleDef>, |
45 | per_module: ArenaMap<ModuleId, ModuleScope>, | 83 | root: CrateModuleId, |
84 | modules: Arena<CrateModuleId, ModuleData>, | ||
85 | macros: Arena<CrateMacroId, mbe::MacroRules>, | ||
86 | public_macros: FxHashMap<Name, CrateMacroId>, | ||
87 | macro_resolutions: FxHashMap<MacroCallId, (Crate, CrateMacroId)>, | ||
88 | problems: CrateDefMapProblems, | ||
89 | } | ||
90 | |||
91 | impl std::ops::Index<CrateModuleId> for CrateDefMap { | ||
92 | type Output = ModuleData; | ||
93 | fn index(&self, id: CrateModuleId) -> &ModuleData { | ||
94 | &self.modules[id] | ||
95 | } | ||
96 | } | ||
97 | |||
98 | impl std::ops::Index<CrateMacroId> for CrateDefMap { | ||
99 | type Output = mbe::MacroRules; | ||
100 | fn index(&self, id: CrateMacroId) -> &mbe::MacroRules { | ||
101 | &self.macros[id] | ||
102 | } | ||
103 | } | ||
104 | |||
105 | /// An ID of a macro, **local** to a specific crate | ||
106 | #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] | ||
107 | pub(crate) struct CrateMacroId(RawId); | ||
108 | impl_arena_id!(CrateMacroId); | ||
109 | |||
110 | /// An ID of a module, **local** to a specific crate | ||
111 | #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] | ||
112 | pub(crate) struct CrateModuleId(RawId); | ||
113 | impl_arena_id!(CrateModuleId); | ||
114 | |||
115 | #[derive(Default, Debug, PartialEq, Eq)] | ||
116 | pub(crate) struct ModuleData { | ||
117 | pub(crate) parent: Option<CrateModuleId>, | ||
118 | pub(crate) children: FxHashMap<Name, CrateModuleId>, | ||
119 | pub(crate) scope: ModuleScope, | ||
120 | /// None for root | ||
121 | pub(crate) declaration: Option<SourceItemId>, | ||
122 | /// None for inline modules. | ||
123 | /// | ||
124 | /// Note that non-inline modules, by definition, live inside non-macro file. | ||
125 | pub(crate) definition: Option<FileId>, | ||
126 | } | ||
127 | |||
128 | #[derive(Default, Debug, PartialEq, Eq)] | ||
129 | pub(crate) struct CrateDefMapProblems { | ||
130 | problems: Vec<(SourceItemId, Problem)>, | ||
46 | } | 131 | } |
47 | 132 | ||
48 | impl std::ops::Index<ModuleId> for ItemMap { | 133 | impl CrateDefMapProblems { |
49 | type Output = ModuleScope; | 134 | fn add(&mut self, source_item_id: SourceItemId, problem: Problem) { |
50 | fn index(&self, id: ModuleId) -> &ModuleScope { | 135 | self.problems.push((source_item_id, problem)) |
51 | &self.per_module[id] | 136 | } |
137 | |||
138 | pub(crate) fn iter<'a>(&'a self) -> impl Iterator<Item = (&'a SourceItemId, &'a Problem)> + 'a { | ||
139 | self.problems.iter().map(|(s, p)| (s, p)) | ||
52 | } | 140 | } |
53 | } | 141 | } |
54 | 142 | ||
55 | #[derive(Debug, Default, PartialEq, Eq, Clone)] | 143 | #[derive(Debug, Default, PartialEq, Eq, Clone)] |
56 | pub struct ModuleScope { | 144 | pub struct ModuleScope { |
57 | pub(crate) items: FxHashMap<Name, Resolution>, | 145 | items: FxHashMap<Name, Resolution>, |
58 | } | 146 | } |
59 | 147 | ||
60 | impl ModuleScope { | 148 | impl ModuleScope { |
@@ -66,8 +154,6 @@ impl ModuleScope { | |||
66 | } | 154 | } |
67 | } | 155 | } |
68 | 156 | ||
69 | /// `Resolution` is basically `DefId` atm, but it should account for stuff like | ||
70 | /// multiple namespaces, ambiguity and errors. | ||
71 | #[derive(Debug, Clone, PartialEq, Eq, Default)] | 157 | #[derive(Debug, Clone, PartialEq, Eq, Default)] |
72 | pub struct Resolution { | 158 | pub struct Resolution { |
73 | /// None for unresolved | 159 | /// None for unresolved |
@@ -76,372 +162,6 @@ pub struct Resolution { | |||
76 | pub import: Option<ImportId>, | 162 | pub import: Option<ImportId>, |
77 | } | 163 | } |
78 | 164 | ||
79 | #[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] | ||
80 | pub enum Namespace { | ||
81 | Types, | ||
82 | Values, | ||
83 | } | ||
84 | |||
85 | #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] | ||
86 | pub struct PerNs<T> { | ||
87 | pub types: Option<T>, | ||
88 | pub values: Option<T>, | ||
89 | } | ||
90 | |||
91 | impl<T> Default for PerNs<T> { | ||
92 | fn default() -> Self { | ||
93 | PerNs { types: None, values: None } | ||
94 | } | ||
95 | } | ||
96 | |||
97 | impl<T> PerNs<T> { | ||
98 | pub fn none() -> PerNs<T> { | ||
99 | PerNs { types: None, values: None } | ||
100 | } | ||
101 | |||
102 | pub fn values(t: T) -> PerNs<T> { | ||
103 | PerNs { types: None, values: Some(t) } | ||
104 | } | ||
105 | |||
106 | pub fn types(t: T) -> PerNs<T> { | ||
107 | PerNs { types: Some(t), values: None } | ||
108 | } | ||
109 | |||
110 | pub fn both(types: T, values: T) -> PerNs<T> { | ||
111 | PerNs { types: Some(types), values: Some(values) } | ||
112 | } | ||
113 | |||
114 | pub fn is_none(&self) -> bool { | ||
115 | self.types.is_none() && self.values.is_none() | ||
116 | } | ||
117 | |||
118 | pub fn is_both(&self) -> bool { | ||
119 | self.types.is_some() && self.values.is_some() | ||
120 | } | ||
121 | |||
122 | pub fn take(self, namespace: Namespace) -> Option<T> { | ||
123 | match namespace { | ||
124 | Namespace::Types => self.types, | ||
125 | Namespace::Values => self.values, | ||
126 | } | ||
127 | } | ||
128 | |||
129 | pub fn take_types(self) -> Option<T> { | ||
130 | self.take(Namespace::Types) | ||
131 | } | ||
132 | |||
133 | pub fn take_values(self) -> Option<T> { | ||
134 | self.take(Namespace::Values) | ||
135 | } | ||
136 | |||
137 | pub fn get(&self, namespace: Namespace) -> Option<&T> { | ||
138 | self.as_ref().take(namespace) | ||
139 | } | ||
140 | |||
141 | pub fn as_ref(&self) -> PerNs<&T> { | ||
142 | PerNs { types: self.types.as_ref(), values: self.values.as_ref() } | ||
143 | } | ||
144 | |||
145 | pub fn or(self, other: PerNs<T>) -> PerNs<T> { | ||
146 | PerNs { types: self.types.or(other.types), values: self.values.or(other.values) } | ||
147 | } | ||
148 | |||
149 | pub fn and_then<U>(self, f: impl Fn(T) -> Option<U>) -> PerNs<U> { | ||
150 | PerNs { types: self.types.and_then(&f), values: self.values.and_then(&f) } | ||
151 | } | ||
152 | |||
153 | pub fn map<U>(self, f: impl Fn(T) -> U) -> PerNs<U> { | ||
154 | PerNs { types: self.types.map(&f), values: self.values.map(&f) } | ||
155 | } | ||
156 | } | ||
157 | |||
158 | struct Resolver<'a, DB> { | ||
159 | db: &'a DB, | ||
160 | input: &'a FxHashMap<ModuleId, Arc<LoweredModule>>, | ||
161 | krate: Crate, | ||
162 | module_tree: Arc<ModuleTree>, | ||
163 | processed_imports: FxHashSet<(ModuleId, ImportId)>, | ||
164 | /// If module `a` has `use b::*`, then this contains the mapping b -> a (and the import) | ||
165 | glob_imports: FxHashMap<ModuleId, Vec<(ModuleId, ImportId)>>, | ||
166 | result: ItemMap, | ||
167 | } | ||
168 | |||
169 | impl<'a, DB> Resolver<'a, DB> | ||
170 | where | ||
171 | DB: PersistentHirDatabase, | ||
172 | { | ||
173 | fn new( | ||
174 | db: &'a DB, | ||
175 | input: &'a FxHashMap<ModuleId, Arc<LoweredModule>>, | ||
176 | krate: Crate, | ||
177 | ) -> Resolver<'a, DB> { | ||
178 | let module_tree = db.module_tree(krate); | ||
179 | Resolver { | ||
180 | db, | ||
181 | input, | ||
182 | krate, | ||
183 | module_tree, | ||
184 | processed_imports: FxHashSet::default(), | ||
185 | glob_imports: FxHashMap::default(), | ||
186 | result: ItemMap { | ||
187 | edition: krate.edition(db), | ||
188 | prelude: None, | ||
189 | extern_prelude: FxHashMap::default(), | ||
190 | per_module: ArenaMap::default(), | ||
191 | }, | ||
192 | } | ||
193 | } | ||
194 | |||
195 | pub(crate) fn resolve(mut self) -> ItemMap { | ||
196 | self.populate_extern_prelude(); | ||
197 | for (&module_id, items) in self.input.iter() { | ||
198 | self.populate_module(module_id, Arc::clone(items)); | ||
199 | } | ||
200 | |||
201 | let mut iter = 0; | ||
202 | loop { | ||
203 | iter += 1; | ||
204 | if iter > 1000 { | ||
205 | panic!("failed to reach fixedpoint after 1000 iters") | ||
206 | } | ||
207 | let processed_imports_count = self.processed_imports.len(); | ||
208 | for &module_id in self.input.keys() { | ||
209 | self.db.check_canceled(); | ||
210 | self.resolve_imports(module_id); | ||
211 | } | ||
212 | if processed_imports_count == self.processed_imports.len() { | ||
213 | // no new imports resolved | ||
214 | break; | ||
215 | } | ||
216 | } | ||
217 | self.result | ||
218 | } | ||
219 | |||
220 | fn populate_extern_prelude(&mut self) { | ||
221 | for dep in self.krate.dependencies(self.db) { | ||
222 | log::debug!("crate dep {:?} -> {:?}", dep.name, dep.krate); | ||
223 | if let Some(module) = dep.krate.root_module(self.db) { | ||
224 | self.result.extern_prelude.insert(dep.name.clone(), module.into()); | ||
225 | } | ||
226 | // look for the prelude | ||
227 | if self.result.prelude.is_none() { | ||
228 | let item_map = self.db.item_map(dep.krate); | ||
229 | if item_map.prelude.is_some() { | ||
230 | self.result.prelude = item_map.prelude; | ||
231 | } | ||
232 | } | ||
233 | } | ||
234 | } | ||
235 | |||
236 | fn populate_module(&mut self, module_id: ModuleId, input: Arc<LoweredModule>) { | ||
237 | let mut module_items = ModuleScope::default(); | ||
238 | for (import_id, import_data) in input.imports.iter() { | ||
239 | if let Some(last_segment) = import_data.path.segments.iter().last() { | ||
240 | if !import_data.is_glob { | ||
241 | let name = | ||
242 | import_data.alias.clone().unwrap_or_else(|| last_segment.name.clone()); | ||
243 | module_items | ||
244 | .items | ||
245 | .insert(name, Resolution { def: PerNs::none(), import: Some(import_id) }); | ||
246 | } | ||
247 | } | ||
248 | } | ||
249 | // Populate explicitly declared items, except modules | ||
250 | for (name, &def) in input.declarations.iter() { | ||
251 | let resolution = Resolution { def, import: None }; | ||
252 | module_items.items.insert(name.clone(), resolution); | ||
253 | } | ||
254 | |||
255 | // Populate modules | ||
256 | for (name, module_id) in module_id.children(&self.module_tree) { | ||
257 | let module = Module { module_id, krate: self.krate }; | ||
258 | self.add_module_item(&mut module_items, name, PerNs::types(module.into())); | ||
259 | } | ||
260 | |||
261 | self.result.per_module.insert(module_id, module_items); | ||
262 | } | ||
263 | |||
264 | fn add_module_item(&self, module_items: &mut ModuleScope, name: Name, def: PerNs<ModuleDef>) { | ||
265 | let resolution = Resolution { def, import: None }; | ||
266 | module_items.items.insert(name, resolution); | ||
267 | } | ||
268 | |||
269 | fn resolve_imports(&mut self, module_id: ModuleId) { | ||
270 | for (import_id, import_data) in self.input[&module_id].imports.iter() { | ||
271 | if self.processed_imports.contains(&(module_id, import_id)) { | ||
272 | // already done | ||
273 | continue; | ||
274 | } | ||
275 | if self.resolve_import(module_id, import_id, import_data) == ReachedFixedPoint::Yes { | ||
276 | log::debug!("import {:?} resolved (or definite error)", import_id); | ||
277 | self.processed_imports.insert((module_id, import_id)); | ||
278 | } | ||
279 | } | ||
280 | } | ||
281 | |||
282 | fn resolve_import( | ||
283 | &mut self, | ||
284 | module_id: ModuleId, | ||
285 | import_id: ImportId, | ||
286 | import: &ImportData, | ||
287 | ) -> ReachedFixedPoint { | ||
288 | log::debug!("resolving import: {:?} ({:?})", import, self.result.edition); | ||
289 | let original_module = Module { krate: self.krate, module_id }; | ||
290 | |||
291 | let (def, reached_fixedpoint) = if import.is_extern_crate { | ||
292 | let res = self.result.resolve_name_in_extern_prelude( | ||
293 | &import | ||
294 | .path | ||
295 | .as_ident() | ||
296 | .expect("extern crate should have been desugared to one-element path"), | ||
297 | ); | ||
298 | (res, if res.is_none() { ReachedFixedPoint::No } else { ReachedFixedPoint::Yes }) | ||
299 | } else { | ||
300 | let res = self.result.resolve_path_fp( | ||
301 | self.db, | ||
302 | ResolveMode::Import, | ||
303 | original_module, | ||
304 | &import.path, | ||
305 | ); | ||
306 | |||
307 | (res.resolved_def, res.reached_fixedpoint) | ||
308 | }; | ||
309 | |||
310 | if reached_fixedpoint != ReachedFixedPoint::Yes { | ||
311 | return reached_fixedpoint; | ||
312 | } | ||
313 | |||
314 | if import.is_glob { | ||
315 | log::debug!("glob import: {:?}", import); | ||
316 | match def.take_types() { | ||
317 | Some(ModuleDef::Module(m)) => { | ||
318 | if import.is_prelude { | ||
319 | tested_by!(std_prelude); | ||
320 | self.result.prelude = Some(m); | ||
321 | } else if m.krate != self.krate { | ||
322 | tested_by!(glob_across_crates); | ||
323 | // glob import from other crate => we can just import everything once | ||
324 | let item_map = self.db.item_map(m.krate); | ||
325 | let scope = &item_map[m.module_id]; | ||
326 | let items = scope | ||
327 | .items | ||
328 | .iter() | ||
329 | .map(|(name, res)| (name.clone(), res.clone())) | ||
330 | .collect::<Vec<_>>(); | ||
331 | self.update(module_id, Some(import_id), &items); | ||
332 | } else { | ||
333 | // glob import from same crate => we do an initial | ||
334 | // import, and then need to propagate any further | ||
335 | // additions | ||
336 | let scope = &self.result[m.module_id]; | ||
337 | let items = scope | ||
338 | .items | ||
339 | .iter() | ||
340 | .map(|(name, res)| (name.clone(), res.clone())) | ||
341 | .collect::<Vec<_>>(); | ||
342 | self.update(module_id, Some(import_id), &items); | ||
343 | // record the glob import in case we add further items | ||
344 | self.glob_imports | ||
345 | .entry(m.module_id) | ||
346 | .or_default() | ||
347 | .push((module_id, import_id)); | ||
348 | } | ||
349 | } | ||
350 | Some(ModuleDef::Enum(e)) => { | ||
351 | tested_by!(glob_enum); | ||
352 | // glob import from enum => just import all the variants | ||
353 | let variants = e.variants(self.db); | ||
354 | let resolutions = variants | ||
355 | .into_iter() | ||
356 | .filter_map(|variant| { | ||
357 | let res = Resolution { | ||
358 | def: PerNs::both(variant.into(), variant.into()), | ||
359 | import: Some(import_id), | ||
360 | }; | ||
361 | let name = variant.name(self.db)?; | ||
362 | Some((name, res)) | ||
363 | }) | ||
364 | .collect::<Vec<_>>(); | ||
365 | self.update(module_id, Some(import_id), &resolutions); | ||
366 | } | ||
367 | Some(d) => { | ||
368 | log::debug!("glob import {:?} from non-module/enum {:?}", import, d); | ||
369 | } | ||
370 | None => { | ||
371 | log::debug!("glob import {:?} didn't resolve as type", import); | ||
372 | } | ||
373 | } | ||
374 | } else { | ||
375 | let last_segment = import.path.segments.last().unwrap(); | ||
376 | let name = import.alias.clone().unwrap_or_else(|| last_segment.name.clone()); | ||
377 | log::debug!("resolved import {:?} ({:?}) to {:?}", name, import, def); | ||
378 | |||
379 | // extern crates in the crate root are special-cased to insert entries into the extern prelude: rust-lang/rust#54658 | ||
380 | if let Some(root_module) = self.krate.root_module(self.db) { | ||
381 | if import.is_extern_crate && module_id == root_module.module_id { | ||
382 | if let Some(def) = def.take_types() { | ||
383 | self.result.extern_prelude.insert(name.clone(), def); | ||
384 | } | ||
385 | } | ||
386 | } | ||
387 | let resolution = Resolution { def, import: Some(import_id) }; | ||
388 | self.update(module_id, None, &[(name, resolution)]); | ||
389 | } | ||
390 | reached_fixedpoint | ||
391 | } | ||
392 | |||
393 | fn update( | ||
394 | &mut self, | ||
395 | module_id: ModuleId, | ||
396 | import: Option<ImportId>, | ||
397 | resolutions: &[(Name, Resolution)], | ||
398 | ) { | ||
399 | self.update_recursive(module_id, import, resolutions, 0) | ||
400 | } | ||
401 | |||
402 | fn update_recursive( | ||
403 | &mut self, | ||
404 | module_id: ModuleId, | ||
405 | import: Option<ImportId>, | ||
406 | resolutions: &[(Name, Resolution)], | ||
407 | depth: usize, | ||
408 | ) { | ||
409 | if depth > 100 { | ||
410 | // prevent stack overflows (but this shouldn't be possible) | ||
411 | panic!("infinite recursion in glob imports!"); | ||
412 | } | ||
413 | let module_items = self.result.per_module.get_mut(module_id).unwrap(); | ||
414 | let mut changed = false; | ||
415 | for (name, res) in resolutions { | ||
416 | let existing = module_items.items.entry(name.clone()).or_default(); | ||
417 | if existing.def.types.is_none() && res.def.types.is_some() { | ||
418 | existing.def.types = res.def.types; | ||
419 | existing.import = import.or(res.import); | ||
420 | changed = true; | ||
421 | } | ||
422 | if existing.def.values.is_none() && res.def.values.is_some() { | ||
423 | existing.def.values = res.def.values; | ||
424 | existing.import = import.or(res.import); | ||
425 | changed = true; | ||
426 | } | ||
427 | } | ||
428 | if !changed { | ||
429 | return; | ||
430 | } | ||
431 | let glob_imports = self | ||
432 | .glob_imports | ||
433 | .get(&module_id) | ||
434 | .into_iter() | ||
435 | .flat_map(|v| v.iter()) | ||
436 | .cloned() | ||
437 | .collect::<Vec<_>>(); | ||
438 | for (glob_importing_module, glob_import) in glob_imports { | ||
439 | // We pass the glob import so that the tracked import in those modules is that glob import | ||
440 | self.update_recursive(glob_importing_module, Some(glob_import), resolutions, depth + 1); | ||
441 | } | ||
442 | } | ||
443 | } | ||
444 | |||
445 | #[derive(Debug, Clone)] | 165 | #[derive(Debug, Clone)] |
446 | struct ResolvePathResult { | 166 | struct ResolvePathResult { |
447 | resolved_def: PerNs<ModuleDef>, | 167 | resolved_def: PerNs<ModuleDef>, |
@@ -475,84 +195,85 @@ enum ReachedFixedPoint { | |||
475 | No, | 195 | No, |
476 | } | 196 | } |
477 | 197 | ||
478 | impl ItemMap { | 198 | impl CrateDefMap { |
479 | pub(crate) fn item_map_query(db: &impl PersistentHirDatabase, krate: Crate) -> Arc<ItemMap> { | 199 | pub(crate) fn crate_def_map_query( |
480 | let start = time::Instant::now(); | 200 | db: &impl PersistentHirDatabase, |
481 | let module_tree = db.module_tree(krate); | 201 | krate: Crate, |
482 | let input = module_tree | 202 | ) -> Arc<CrateDefMap> { |
483 | .modules() | 203 | let start = std::time::Instant::now(); |
484 | .map(|module_id| (module_id, db.lower_module(Module { krate, module_id }))) | 204 | let def_map = { |
485 | .collect::<FxHashMap<_, _>>(); | 205 | let edition = krate.edition(db); |
486 | 206 | let mut modules: Arena<CrateModuleId, ModuleData> = Arena::default(); | |
487 | let resolver = Resolver::new(db, &input, krate); | 207 | let root = modules.alloc(ModuleData::default()); |
488 | let res = resolver.resolve(); | 208 | CrateDefMap { |
489 | let elapsed = start.elapsed(); | 209 | krate, |
490 | log::info!("item_map: {:?}", elapsed); | 210 | edition, |
491 | Arc::new(res) | 211 | extern_prelude: FxHashMap::default(), |
212 | prelude: None, | ||
213 | root, | ||
214 | modules, | ||
215 | macros: Arena::default(), | ||
216 | public_macros: FxHashMap::default(), | ||
217 | macro_resolutions: FxHashMap::default(), | ||
218 | problems: CrateDefMapProblems::default(), | ||
219 | } | ||
220 | }; | ||
221 | let def_map = collector::collect_defs(db, def_map); | ||
222 | log::info!("crate_def_map_query: {:?}", start.elapsed()); | ||
223 | Arc::new(def_map) | ||
492 | } | 224 | } |
493 | 225 | ||
494 | pub(crate) fn resolve_path( | 226 | pub(crate) fn root(&self) -> CrateModuleId { |
495 | &self, | 227 | self.root |
496 | db: &impl PersistentHirDatabase, | ||
497 | original_module: Module, | ||
498 | path: &Path, | ||
499 | ) -> (PerNs<ModuleDef>, Option<usize>) { | ||
500 | let res = self.resolve_path_fp(db, ResolveMode::Other, original_module, path); | ||
501 | (res.resolved_def, res.segment_index) | ||
502 | } | 228 | } |
503 | 229 | ||
504 | fn resolve_in_prelude( | 230 | pub(crate) fn problems(&self) -> &CrateDefMapProblems { |
505 | &self, | 231 | &self.problems |
506 | db: &impl PersistentHirDatabase, | ||
507 | original_module: Module, | ||
508 | name: &Name, | ||
509 | ) -> PerNs<ModuleDef> { | ||
510 | if let Some(prelude) = self.prelude { | ||
511 | let resolution = if prelude.krate == original_module.krate { | ||
512 | self[prelude.module_id].items.get(name).cloned() | ||
513 | } else { | ||
514 | db.item_map(prelude.krate)[prelude.module_id].items.get(name).cloned() | ||
515 | }; | ||
516 | resolution.map(|r| r.def).unwrap_or_else(PerNs::none) | ||
517 | } else { | ||
518 | PerNs::none() | ||
519 | } | ||
520 | } | 232 | } |
521 | 233 | ||
522 | pub(crate) fn resolve_name_in_module( | 234 | pub(crate) fn mk_module(&self, module_id: CrateModuleId) -> Module { |
523 | &self, | 235 | Module { krate: self.krate, module_id } |
524 | db: &impl PersistentHirDatabase, | 236 | } |
525 | module: Module, | ||
526 | name: &Name, | ||
527 | ) -> PerNs<ModuleDef> { | ||
528 | // Resolve in: | ||
529 | // - current module / scope | ||
530 | // - extern prelude | ||
531 | // - std prelude | ||
532 | let from_scope = self[module.module_id].items.get(name).map_or(PerNs::none(), |it| it.def); | ||
533 | let from_extern_prelude = | ||
534 | self.extern_prelude.get(name).map_or(PerNs::none(), |&it| PerNs::types(it)); | ||
535 | let from_prelude = self.resolve_in_prelude(db, module, name); | ||
536 | 237 | ||
537 | from_scope.or(from_extern_prelude).or(from_prelude) | 238 | pub(crate) fn prelude(&self) -> Option<Module> { |
239 | self.prelude | ||
538 | } | 240 | } |
539 | 241 | ||
540 | fn resolve_name_in_extern_prelude(&self, name: &Name) -> PerNs<ModuleDef> { | 242 | pub(crate) fn extern_prelude(&self) -> &FxHashMap<Name, ModuleDef> { |
541 | self.extern_prelude.get(name).map_or(PerNs::none(), |&it| PerNs::types(it)) | 243 | &self.extern_prelude |
542 | } | 244 | } |
543 | 245 | ||
544 | fn resolve_name_in_crate_root_or_extern_prelude( | 246 | pub(crate) fn resolve_macro( |
545 | &self, | 247 | &self, |
546 | db: &impl PersistentHirDatabase, | 248 | macro_call_id: MacroCallId, |
547 | module: Module, | 249 | ) -> Option<(Crate, CrateMacroId)> { |
548 | name: &Name, | 250 | self.macro_resolutions.get(¯o_call_id).map(|&it| it) |
549 | ) -> PerNs<ModuleDef> { | 251 | } |
550 | let crate_root = module.crate_root(db); | ||
551 | let from_crate_root = | ||
552 | self[crate_root.module_id].items.get(name).map_or(PerNs::none(), |it| it.def); | ||
553 | let from_extern_prelude = self.resolve_name_in_extern_prelude(name); | ||
554 | 252 | ||
555 | from_crate_root.or(from_extern_prelude) | 253 | pub(crate) fn find_module_by_source( |
254 | &self, | ||
255 | file_id: HirFileId, | ||
256 | decl_id: Option<SourceFileItemId>, | ||
257 | ) -> Option<CrateModuleId> { | ||
258 | let decl_id = decl_id.map(|it| it.with_file_id(file_id)); | ||
259 | let (module_id, _module_data) = self.modules.iter().find(|(_module_id, module_data)| { | ||
260 | if decl_id.is_some() { | ||
261 | module_data.declaration == decl_id | ||
262 | } else { | ||
263 | module_data.definition.map(|it| it.into()) == Some(file_id) | ||
264 | } | ||
265 | })?; | ||
266 | Some(module_id) | ||
267 | } | ||
268 | |||
269 | pub(crate) fn resolve_path( | ||
270 | &self, | ||
271 | db: &impl PersistentHirDatabase, | ||
272 | original_module: CrateModuleId, | ||
273 | path: &Path, | ||
274 | ) -> (PerNs<ModuleDef>, Option<usize>) { | ||
275 | let res = self.resolve_path_fp(db, ResolveMode::Other, original_module, path); | ||
276 | (res.resolved_def, res.segment_index) | ||
556 | } | 277 | } |
557 | 278 | ||
558 | // Returns Yes if we are sure that additions to `ItemMap` wouldn't change | 279 | // Returns Yes if we are sure that additions to `ItemMap` wouldn't change |
@@ -561,13 +282,17 @@ impl ItemMap { | |||
561 | &self, | 282 | &self, |
562 | db: &impl PersistentHirDatabase, | 283 | db: &impl PersistentHirDatabase, |
563 | mode: ResolveMode, | 284 | mode: ResolveMode, |
564 | original_module: Module, | 285 | original_module: CrateModuleId, |
565 | path: &Path, | 286 | path: &Path, |
566 | ) -> ResolvePathResult { | 287 | ) -> ResolvePathResult { |
567 | let mut segments = path.segments.iter().enumerate(); | 288 | let mut segments = path.segments.iter().enumerate(); |
568 | let mut curr_per_ns: PerNs<ModuleDef> = match path.kind { | 289 | let mut curr_per_ns: PerNs<ModuleDef> = match path.kind { |
569 | PathKind::Crate => PerNs::types(original_module.crate_root(db).into()), | 290 | PathKind::Crate => { |
570 | PathKind::Self_ => PerNs::types(original_module.into()), | 291 | PerNs::types(Module { krate: self.krate, module_id: self.root }.into()) |
292 | } | ||
293 | PathKind::Self_ => { | ||
294 | PerNs::types(Module { krate: self.krate, module_id: original_module }.into()) | ||
295 | } | ||
571 | // plain import or absolute path in 2015: crate-relative with | 296 | // plain import or absolute path in 2015: crate-relative with |
572 | // fallback to extern prelude (with the simplification in | 297 | // fallback to extern prelude (with the simplification in |
573 | // rust-lang/rust#57745) | 298 | // rust-lang/rust#57745) |
@@ -581,11 +306,7 @@ impl ItemMap { | |||
581 | None => return ResolvePathResult::empty(ReachedFixedPoint::Yes), | 306 | None => return ResolvePathResult::empty(ReachedFixedPoint::Yes), |
582 | }; | 307 | }; |
583 | log::debug!("resolving {:?} in crate root (+ extern prelude)", segment); | 308 | log::debug!("resolving {:?} in crate root (+ extern prelude)", segment); |
584 | self.resolve_name_in_crate_root_or_extern_prelude( | 309 | self.resolve_name_in_crate_root_or_extern_prelude(&segment.name) |
585 | db, | ||
586 | original_module, | ||
587 | &segment.name, | ||
588 | ) | ||
589 | } | 310 | } |
590 | PathKind::Plain => { | 311 | PathKind::Plain => { |
591 | let segment = match segments.next() { | 312 | let segment = match segments.next() { |
@@ -596,8 +317,8 @@ impl ItemMap { | |||
596 | self.resolve_name_in_module(db, original_module, &segment.name) | 317 | self.resolve_name_in_module(db, original_module, &segment.name) |
597 | } | 318 | } |
598 | PathKind::Super => { | 319 | PathKind::Super => { |
599 | if let Some(p) = original_module.parent(db) { | 320 | if let Some(p) = self.modules[original_module].parent { |
600 | PerNs::types(p.into()) | 321 | PerNs::types(Module { krate: self.krate, module_id: p }.into()) |
601 | } else { | 322 | } else { |
602 | log::debug!("super path in root module"); | 323 | log::debug!("super path in root module"); |
603 | return ResolvePathResult::empty(ReachedFixedPoint::Yes); | 324 | return ResolvePathResult::empty(ReachedFixedPoint::Yes); |
@@ -634,14 +355,14 @@ impl ItemMap { | |||
634 | 355 | ||
635 | curr_per_ns = match curr { | 356 | curr_per_ns = match curr { |
636 | ModuleDef::Module(module) => { | 357 | ModuleDef::Module(module) => { |
637 | if module.krate != original_module.krate { | 358 | if module.krate != self.krate { |
638 | let path = Path { | 359 | let path = Path { |
639 | segments: path.segments[i..].iter().cloned().collect(), | 360 | segments: path.segments[i..].iter().cloned().collect(), |
640 | kind: PathKind::Self_, | 361 | kind: PathKind::Self_, |
641 | }; | 362 | }; |
642 | log::debug!("resolving {:?} in other crate", path); | 363 | log::debug!("resolving {:?} in other crate", path); |
643 | let item_map = db.item_map(module.krate); | 364 | let defp_map = db.crate_def_map(module.krate); |
644 | let (def, s) = item_map.resolve_path(db, *module, &path); | 365 | let (def, s) = defp_map.resolve_path(db, module.module_id, &path); |
645 | return ResolvePathResult::with( | 366 | return ResolvePathResult::with( |
646 | def, | 367 | def, |
647 | ReachedFixedPoint::Yes, | 368 | ReachedFixedPoint::Yes, |
@@ -649,7 +370,7 @@ impl ItemMap { | |||
649 | ); | 370 | ); |
650 | } | 371 | } |
651 | 372 | ||
652 | match self[module.module_id].items.get(&segment.name) { | 373 | match self[module.module_id].scope.items.get(&segment.name) { |
653 | Some(res) if !res.def.is_none() => res.def, | 374 | Some(res) if !res.def.is_none() => res.def, |
654 | _ => { | 375 | _ => { |
655 | log::debug!("path segment {:?} not found", segment.name); | 376 | log::debug!("path segment {:?} not found", segment.name); |
@@ -659,7 +380,7 @@ impl ItemMap { | |||
659 | } | 380 | } |
660 | ModuleDef::Enum(e) => { | 381 | ModuleDef::Enum(e) => { |
661 | // enum variant | 382 | // enum variant |
662 | tested_by!(item_map_enum_importing); | 383 | tested_by!(can_import_enum_variant); |
663 | match e.variant(db, &segment.name) { | 384 | match e.variant(db, &segment.name) { |
664 | Some(variant) => PerNs::both(variant.into(), variant.into()), | 385 | Some(variant) => PerNs::both(variant.into(), variant.into()), |
665 | None => { | 386 | None => { |
@@ -690,7 +411,47 @@ impl ItemMap { | |||
690 | } | 411 | } |
691 | ResolvePathResult::with(curr_per_ns, ReachedFixedPoint::Yes, None) | 412 | ResolvePathResult::with(curr_per_ns, ReachedFixedPoint::Yes, None) |
692 | } | 413 | } |
693 | } | ||
694 | 414 | ||
695 | #[cfg(test)] | 415 | fn resolve_name_in_crate_root_or_extern_prelude(&self, name: &Name) -> PerNs<ModuleDef> { |
696 | mod tests; | 416 | let from_crate_root = |
417 | self[self.root].scope.items.get(name).map_or(PerNs::none(), |it| it.def); | ||
418 | let from_extern_prelude = self.resolve_name_in_extern_prelude(name); | ||
419 | |||
420 | from_crate_root.or(from_extern_prelude) | ||
421 | } | ||
422 | |||
423 | pub(crate) fn resolve_name_in_module( | ||
424 | &self, | ||
425 | db: &impl PersistentHirDatabase, | ||
426 | module: CrateModuleId, | ||
427 | name: &Name, | ||
428 | ) -> PerNs<ModuleDef> { | ||
429 | // Resolve in: | ||
430 | // - current module / scope | ||
431 | // - extern prelude | ||
432 | // - std prelude | ||
433 | let from_scope = self[module].scope.items.get(name).map_or(PerNs::none(), |it| it.def); | ||
434 | let from_extern_prelude = | ||
435 | self.extern_prelude.get(name).map_or(PerNs::none(), |&it| PerNs::types(it)); | ||
436 | let from_prelude = self.resolve_in_prelude(db, name); | ||
437 | |||
438 | from_scope.or(from_extern_prelude).or(from_prelude) | ||
439 | } | ||
440 | |||
441 | fn resolve_name_in_extern_prelude(&self, name: &Name) -> PerNs<ModuleDef> { | ||
442 | self.extern_prelude.get(name).map_or(PerNs::none(), |&it| PerNs::types(it)) | ||
443 | } | ||
444 | |||
445 | fn resolve_in_prelude(&self, db: &impl PersistentHirDatabase, name: &Name) -> PerNs<ModuleDef> { | ||
446 | if let Some(prelude) = self.prelude { | ||
447 | let resolution = if prelude.krate == self.krate { | ||
448 | self[prelude.module_id].scope.items.get(name).cloned() | ||
449 | } else { | ||
450 | db.crate_def_map(prelude.krate)[prelude.module_id].scope.items.get(name).cloned() | ||
451 | }; | ||
452 | resolution.map(|r| r.def).unwrap_or_else(PerNs::none) | ||
453 | } else { | ||
454 | PerNs::none() | ||
455 | } | ||
456 | } | ||
457 | } | ||
diff --git a/crates/ra_hir/src/nameres/collector.rs b/crates/ra_hir/src/nameres/collector.rs new file mode 100644 index 000000000..12ed49a0a --- /dev/null +++ b/crates/ra_hir/src/nameres/collector.rs | |||
@@ -0,0 +1,564 @@ | |||
1 | use arrayvec::ArrayVec; | ||
2 | use rustc_hash::FxHashMap; | ||
3 | use relative_path::RelativePathBuf; | ||
4 | use test_utils::tested_by; | ||
5 | use ra_db::FileId; | ||
6 | |||
7 | use crate::{ | ||
8 | Function, Module, Struct, Enum, Const, Static, Trait, TypeAlias, | ||
9 | PersistentHirDatabase, HirFileId, Name, Path, Problem, Crate, | ||
10 | KnownName, | ||
11 | nameres::{Resolution, PerNs, ModuleDef, ReachedFixedPoint, ResolveMode, raw}, | ||
12 | ids::{AstItemDef, LocationCtx, MacroCallLoc, SourceItemId, MacroCallId}, | ||
13 | }; | ||
14 | |||
15 | use super::{CrateDefMap, CrateModuleId, ModuleData, CrateMacroId}; | ||
16 | |||
17 | pub(super) fn collect_defs( | ||
18 | db: &impl PersistentHirDatabase, | ||
19 | mut def_map: CrateDefMap, | ||
20 | ) -> CrateDefMap { | ||
21 | // populate external prelude | ||
22 | for dep in def_map.krate.dependencies(db) { | ||
23 | log::debug!("crate dep {:?} -> {:?}", dep.name, dep.krate); | ||
24 | if let Some(module) = dep.krate.root_module(db) { | ||
25 | def_map.extern_prelude.insert(dep.name.clone(), module.into()); | ||
26 | } | ||
27 | // look for the prelude | ||
28 | if def_map.prelude.is_none() { | ||
29 | let map = db.crate_def_map(dep.krate); | ||
30 | if map.prelude.is_some() { | ||
31 | def_map.prelude = map.prelude; | ||
32 | } | ||
33 | } | ||
34 | } | ||
35 | |||
36 | let mut collector = DefCollector { | ||
37 | db, | ||
38 | def_map, | ||
39 | glob_imports: FxHashMap::default(), | ||
40 | unresolved_imports: Vec::new(), | ||
41 | unexpanded_macros: Vec::new(), | ||
42 | global_macro_scope: FxHashMap::default(), | ||
43 | }; | ||
44 | collector.collect(); | ||
45 | collector.finish() | ||
46 | } | ||
47 | |||
48 | /// Walks the tree of module recursively | ||
49 | struct DefCollector<DB> { | ||
50 | db: DB, | ||
51 | def_map: CrateDefMap, | ||
52 | glob_imports: FxHashMap<CrateModuleId, Vec<(CrateModuleId, raw::ImportId)>>, | ||
53 | unresolved_imports: Vec<(CrateModuleId, raw::ImportId, raw::ImportData)>, | ||
54 | unexpanded_macros: Vec<(CrateModuleId, MacroCallId, Path, tt::Subtree)>, | ||
55 | global_macro_scope: FxHashMap<Name, CrateMacroId>, | ||
56 | } | ||
57 | |||
58 | impl<'a, DB> DefCollector<&'a DB> | ||
59 | where | ||
60 | DB: PersistentHirDatabase, | ||
61 | { | ||
62 | fn collect(&mut self) { | ||
63 | let crate_graph = self.db.crate_graph(); | ||
64 | let file_id = crate_graph.crate_root(self.def_map.krate.crate_id()); | ||
65 | let raw_items = self.db.raw_items(file_id); | ||
66 | let module_id = self.def_map.root; | ||
67 | self.def_map.modules[module_id].definition = Some(file_id); | ||
68 | ModCollector { | ||
69 | def_collector: &mut *self, | ||
70 | module_id, | ||
71 | file_id: file_id.into(), | ||
72 | raw_items: &raw_items, | ||
73 | } | ||
74 | .collect(raw_items.items()); | ||
75 | |||
76 | // main name resolution fixed-point loop. | ||
77 | let mut i = 0; | ||
78 | loop { | ||
79 | match (self.resolve_imports(), self.resolve_macros()) { | ||
80 | (ReachedFixedPoint::Yes, ReachedFixedPoint::Yes) => break, | ||
81 | _ => i += 1, | ||
82 | } | ||
83 | if i == 1000 { | ||
84 | log::error!("diverging name resolution"); | ||
85 | break; | ||
86 | } | ||
87 | } | ||
88 | |||
89 | let unresolved_imports = std::mem::replace(&mut self.unresolved_imports, Vec::new()); | ||
90 | // show unresolved imports in completion, etc | ||
91 | for (module_id, import, import_data) in unresolved_imports { | ||
92 | self.record_resolved_import(module_id, PerNs::none(), import, &import_data) | ||
93 | } | ||
94 | } | ||
95 | |||
96 | fn define_macro(&mut self, name: Name, tt: &tt::Subtree, export: bool) { | ||
97 | if let Ok(rules) = mbe::MacroRules::parse(tt) { | ||
98 | let macro_id = self.def_map.macros.alloc(rules); | ||
99 | if export { | ||
100 | self.def_map.public_macros.insert(name.clone(), macro_id); | ||
101 | } | ||
102 | self.global_macro_scope.insert(name, macro_id); | ||
103 | } | ||
104 | } | ||
105 | |||
106 | fn resolve_imports(&mut self) -> ReachedFixedPoint { | ||
107 | let mut imports = std::mem::replace(&mut self.unresolved_imports, Vec::new()); | ||
108 | let mut resolved = Vec::new(); | ||
109 | imports.retain(|(module_id, import, import_data)| { | ||
110 | let (def, fp) = self.resolve_import(*module_id, import_data); | ||
111 | if fp == ReachedFixedPoint::Yes { | ||
112 | resolved.push((*module_id, def, *import, import_data.clone())) | ||
113 | } | ||
114 | fp == ReachedFixedPoint::No | ||
115 | }); | ||
116 | self.unresolved_imports = imports; | ||
117 | // Resolves imports, filling-in module scopes | ||
118 | let result = | ||
119 | if resolved.is_empty() { ReachedFixedPoint::Yes } else { ReachedFixedPoint::No }; | ||
120 | for (module_id, def, import, import_data) in resolved { | ||
121 | self.record_resolved_import(module_id, def, import, &import_data) | ||
122 | } | ||
123 | result | ||
124 | } | ||
125 | |||
126 | fn resolve_import( | ||
127 | &mut self, | ||
128 | module_id: CrateModuleId, | ||
129 | import: &raw::ImportData, | ||
130 | ) -> (PerNs<ModuleDef>, ReachedFixedPoint) { | ||
131 | log::debug!("resolving import: {:?} ({:?})", import, self.def_map.edition); | ||
132 | if import.is_extern_crate { | ||
133 | let res = self.def_map.resolve_name_in_extern_prelude( | ||
134 | &import | ||
135 | .path | ||
136 | .as_ident() | ||
137 | .expect("extern crate should have been desugared to one-element path"), | ||
138 | ); | ||
139 | (res, ReachedFixedPoint::Yes) | ||
140 | } else { | ||
141 | let res = | ||
142 | self.def_map.resolve_path_fp(self.db, ResolveMode::Import, module_id, &import.path); | ||
143 | |||
144 | (res.resolved_def, res.reached_fixedpoint) | ||
145 | } | ||
146 | } | ||
147 | |||
148 | fn record_resolved_import( | ||
149 | &mut self, | ||
150 | module_id: CrateModuleId, | ||
151 | def: PerNs<ModuleDef>, | ||
152 | import_id: raw::ImportId, | ||
153 | import: &raw::ImportData, | ||
154 | ) { | ||
155 | if import.is_glob { | ||
156 | log::debug!("glob import: {:?}", import); | ||
157 | match def.take_types() { | ||
158 | Some(ModuleDef::Module(m)) => { | ||
159 | if import.is_prelude { | ||
160 | tested_by!(std_prelude); | ||
161 | self.def_map.prelude = Some(m); | ||
162 | } else if m.krate != self.def_map.krate { | ||
163 | tested_by!(glob_across_crates); | ||
164 | // glob import from other crate => we can just import everything once | ||
165 | let item_map = self.db.crate_def_map(m.krate); | ||
166 | let scope = &item_map[m.module_id].scope; | ||
167 | let items = scope | ||
168 | .items | ||
169 | .iter() | ||
170 | .map(|(name, res)| (name.clone(), res.clone())) | ||
171 | .collect::<Vec<_>>(); | ||
172 | self.update(module_id, Some(import_id), &items); | ||
173 | } else { | ||
174 | // glob import from same crate => we do an initial | ||
175 | // import, and then need to propagate any further | ||
176 | // additions | ||
177 | let scope = &self.def_map[m.module_id].scope; | ||
178 | let items = scope | ||
179 | .items | ||
180 | .iter() | ||
181 | .map(|(name, res)| (name.clone(), res.clone())) | ||
182 | .collect::<Vec<_>>(); | ||
183 | self.update(module_id, Some(import_id), &items); | ||
184 | // record the glob import in case we add further items | ||
185 | self.glob_imports | ||
186 | .entry(m.module_id) | ||
187 | .or_default() | ||
188 | .push((module_id, import_id)); | ||
189 | } | ||
190 | } | ||
191 | Some(ModuleDef::Enum(e)) => { | ||
192 | tested_by!(glob_enum); | ||
193 | // glob import from enum => just import all the variants | ||
194 | let variants = e.variants(self.db); | ||
195 | let resolutions = variants | ||
196 | .into_iter() | ||
197 | .filter_map(|variant| { | ||
198 | let res = Resolution { | ||
199 | def: PerNs::both(variant.into(), variant.into()), | ||
200 | import: Some(import_id), | ||
201 | }; | ||
202 | let name = variant.name(self.db)?; | ||
203 | Some((name, res)) | ||
204 | }) | ||
205 | .collect::<Vec<_>>(); | ||
206 | self.update(module_id, Some(import_id), &resolutions); | ||
207 | } | ||
208 | Some(d) => { | ||
209 | log::debug!("glob import {:?} from non-module/enum {:?}", import, d); | ||
210 | } | ||
211 | None => { | ||
212 | log::debug!("glob import {:?} didn't resolve as type", import); | ||
213 | } | ||
214 | } | ||
215 | } else { | ||
216 | match import.path.segments.last() { | ||
217 | Some(last_segment) => { | ||
218 | let name = import.alias.clone().unwrap_or_else(|| last_segment.name.clone()); | ||
219 | log::debug!("resolved import {:?} ({:?}) to {:?}", name, import, def); | ||
220 | |||
221 | // extern crates in the crate root are special-cased to insert entries into the extern prelude: rust-lang/rust#54658 | ||
222 | if import.is_extern_crate && module_id == self.def_map.root { | ||
223 | if let Some(def) = def.take_types() { | ||
224 | self.def_map.extern_prelude.insert(name.clone(), def); | ||
225 | } | ||
226 | } | ||
227 | let resolution = Resolution { def, import: Some(import_id) }; | ||
228 | self.update(module_id, Some(import_id), &[(name, resolution)]); | ||
229 | } | ||
230 | None => tested_by!(bogus_paths), | ||
231 | } | ||
232 | } | ||
233 | } | ||
234 | |||
235 | fn update( | ||
236 | &mut self, | ||
237 | module_id: CrateModuleId, | ||
238 | import: Option<raw::ImportId>, | ||
239 | resolutions: &[(Name, Resolution)], | ||
240 | ) { | ||
241 | self.update_recursive(module_id, import, resolutions, 0) | ||
242 | } | ||
243 | |||
244 | fn update_recursive( | ||
245 | &mut self, | ||
246 | module_id: CrateModuleId, | ||
247 | import: Option<raw::ImportId>, | ||
248 | resolutions: &[(Name, Resolution)], | ||
249 | depth: usize, | ||
250 | ) { | ||
251 | if depth > 100 { | ||
252 | // prevent stack overflows (but this shouldn't be possible) | ||
253 | panic!("infinite recursion in glob imports!"); | ||
254 | } | ||
255 | let module_items = &mut self.def_map.modules[module_id].scope; | ||
256 | let mut changed = false; | ||
257 | for (name, res) in resolutions { | ||
258 | let existing = module_items.items.entry(name.clone()).or_default(); | ||
259 | if existing.def.types.is_none() && res.def.types.is_some() { | ||
260 | existing.def.types = res.def.types; | ||
261 | existing.import = import.or(res.import); | ||
262 | changed = true; | ||
263 | } | ||
264 | if existing.def.values.is_none() && res.def.values.is_some() { | ||
265 | existing.def.values = res.def.values; | ||
266 | existing.import = import.or(res.import); | ||
267 | changed = true; | ||
268 | } | ||
269 | if existing.def.is_none() | ||
270 | && res.def.is_none() | ||
271 | && existing.import.is_none() | ||
272 | && res.import.is_some() | ||
273 | { | ||
274 | existing.import = res.import; | ||
275 | } | ||
276 | } | ||
277 | if !changed { | ||
278 | return; | ||
279 | } | ||
280 | let glob_imports = self | ||
281 | .glob_imports | ||
282 | .get(&module_id) | ||
283 | .into_iter() | ||
284 | .flat_map(|v| v.iter()) | ||
285 | .cloned() | ||
286 | .collect::<Vec<_>>(); | ||
287 | for (glob_importing_module, glob_import) in glob_imports { | ||
288 | // We pass the glob import so that the tracked import in those modules is that glob import | ||
289 | self.update_recursive(glob_importing_module, Some(glob_import), resolutions, depth + 1); | ||
290 | } | ||
291 | } | ||
292 | |||
293 | // XXX: this is just a pile of hacks now, because `PerNs` does not handle | ||
294 | // macro namespace. | ||
295 | fn resolve_macros(&mut self) -> ReachedFixedPoint { | ||
296 | let mut macros = std::mem::replace(&mut self.unexpanded_macros, Vec::new()); | ||
297 | let mut resolved = Vec::new(); | ||
298 | let mut res = ReachedFixedPoint::Yes; | ||
299 | macros.retain(|(module_id, call_id, path, tt)| { | ||
300 | if path.segments.len() != 2 { | ||
301 | return true; | ||
302 | } | ||
303 | let crate_name = &path.segments[0].name; | ||
304 | let krate = match self.def_map.resolve_name_in_extern_prelude(crate_name).take_types() { | ||
305 | Some(ModuleDef::Module(m)) => m.krate(self.db), | ||
306 | _ => return true, | ||
307 | }; | ||
308 | let krate = match krate { | ||
309 | Some(it) => it, | ||
310 | _ => return true, | ||
311 | }; | ||
312 | res = ReachedFixedPoint::No; | ||
313 | let def_map = self.db.crate_def_map(krate); | ||
314 | if let Some(macro_id) = def_map.public_macros.get(&path.segments[1].name).cloned() { | ||
315 | resolved.push((*module_id, *call_id, (krate, macro_id), tt.clone())); | ||
316 | } | ||
317 | false | ||
318 | }); | ||
319 | |||
320 | for (module_id, macro_call_id, macro_def_id, arg) in resolved { | ||
321 | self.collect_macro_expansion(module_id, macro_call_id, macro_def_id, arg); | ||
322 | } | ||
323 | res | ||
324 | } | ||
325 | |||
326 | fn collect_macro_expansion( | ||
327 | &mut self, | ||
328 | module_id: CrateModuleId, | ||
329 | macro_call_id: MacroCallId, | ||
330 | macro_def_id: (Crate, CrateMacroId), | ||
331 | macro_arg: tt::Subtree, | ||
332 | ) { | ||
333 | let (macro_krate, macro_id) = macro_def_id; | ||
334 | let dm; | ||
335 | let rules = if macro_krate == self.def_map.krate { | ||
336 | &self.def_map[macro_id] | ||
337 | } else { | ||
338 | dm = self.db.crate_def_map(macro_krate); | ||
339 | &dm[macro_id] | ||
340 | }; | ||
341 | if let Ok(expansion) = rules.expand(¯o_arg) { | ||
342 | self.def_map.macro_resolutions.insert(macro_call_id, macro_def_id); | ||
343 | // XXX: this **does not** go through a database, because we can't | ||
344 | // identify macro_call without adding the whole state of name resolution | ||
345 | // as a parameter to the query. | ||
346 | // | ||
347 | // So, we run the queries "manually" and we must ensure that | ||
348 | // `db.hir_parse(macro_call_id)` returns the same source_file. | ||
349 | let file_id: HirFileId = macro_call_id.into(); | ||
350 | let source_file = mbe::token_tree_to_ast_item_list(&expansion); | ||
351 | |||
352 | let raw_items = raw::RawItems::from_source_file(&source_file, file_id); | ||
353 | ModCollector { def_collector: &mut *self, file_id, module_id, raw_items: &raw_items } | ||
354 | .collect(raw_items.items()) | ||
355 | } | ||
356 | } | ||
357 | |||
358 | fn finish(self) -> CrateDefMap { | ||
359 | self.def_map | ||
360 | } | ||
361 | } | ||
362 | |||
363 | /// Walks a single module, populating defs, imports and macros | ||
364 | struct ModCollector<'a, D> { | ||
365 | def_collector: D, | ||
366 | module_id: CrateModuleId, | ||
367 | file_id: HirFileId, | ||
368 | raw_items: &'a raw::RawItems, | ||
369 | } | ||
370 | |||
371 | impl<DB> ModCollector<'_, &'_ mut DefCollector<&'_ DB>> | ||
372 | where | ||
373 | DB: PersistentHirDatabase, | ||
374 | { | ||
375 | fn collect(&mut self, items: &[raw::RawItem]) { | ||
376 | for item in items { | ||
377 | match *item { | ||
378 | raw::RawItem::Module(m) => self.collect_module(&self.raw_items[m]), | ||
379 | raw::RawItem::Import(import) => self.def_collector.unresolved_imports.push(( | ||
380 | self.module_id, | ||
381 | import, | ||
382 | self.raw_items[import].clone(), | ||
383 | )), | ||
384 | raw::RawItem::Def(def) => self.define_def(&self.raw_items[def]), | ||
385 | raw::RawItem::Macro(mac) => self.collect_macro(&self.raw_items[mac]), | ||
386 | } | ||
387 | } | ||
388 | } | ||
389 | |||
390 | fn collect_module(&mut self, module: &raw::ModuleData) { | ||
391 | match module { | ||
392 | // inline module, just recurse | ||
393 | raw::ModuleData::Definition { name, items, source_item_id } => { | ||
394 | let module_id = self.push_child_module( | ||
395 | name.clone(), | ||
396 | source_item_id.with_file_id(self.file_id), | ||
397 | None, | ||
398 | ); | ||
399 | ModCollector { | ||
400 | def_collector: &mut *self.def_collector, | ||
401 | module_id, | ||
402 | file_id: self.file_id, | ||
403 | raw_items: self.raw_items, | ||
404 | } | ||
405 | .collect(&*items); | ||
406 | } | ||
407 | // out of line module, resovle, parse and recurse | ||
408 | raw::ModuleData::Declaration { name, source_item_id } => { | ||
409 | let source_item_id = source_item_id.with_file_id(self.file_id); | ||
410 | let is_root = self.def_collector.def_map.modules[self.module_id].parent.is_none(); | ||
411 | let (file_ids, problem) = | ||
412 | resolve_submodule(self.def_collector.db, self.file_id, name, is_root); | ||
413 | |||
414 | if let Some(problem) = problem { | ||
415 | self.def_collector.def_map.problems.add(source_item_id, problem) | ||
416 | } | ||
417 | |||
418 | if let Some(&file_id) = file_ids.first() { | ||
419 | let module_id = | ||
420 | self.push_child_module(name.clone(), source_item_id, Some(file_id)); | ||
421 | let raw_items = self.def_collector.db.raw_items(file_id); | ||
422 | ModCollector { | ||
423 | def_collector: &mut *self.def_collector, | ||
424 | module_id, | ||
425 | file_id: file_id.into(), | ||
426 | raw_items: &raw_items, | ||
427 | } | ||
428 | .collect(raw_items.items()) | ||
429 | } | ||
430 | } | ||
431 | } | ||
432 | } | ||
433 | |||
434 | fn push_child_module( | ||
435 | &mut self, | ||
436 | name: Name, | ||
437 | declaration: SourceItemId, | ||
438 | definition: Option<FileId>, | ||
439 | ) -> CrateModuleId { | ||
440 | let modules = &mut self.def_collector.def_map.modules; | ||
441 | let res = modules.alloc(ModuleData::default()); | ||
442 | modules[res].parent = Some(self.module_id); | ||
443 | modules[res].declaration = Some(declaration); | ||
444 | modules[res].definition = definition; | ||
445 | modules[self.module_id].children.insert(name.clone(), res); | ||
446 | let resolution = Resolution { | ||
447 | def: PerNs::types( | ||
448 | Module { krate: self.def_collector.def_map.krate, module_id: res }.into(), | ||
449 | ), | ||
450 | import: None, | ||
451 | }; | ||
452 | self.def_collector.update(self.module_id, None, &[(name, resolution)]); | ||
453 | res | ||
454 | } | ||
455 | |||
456 | fn define_def(&mut self, def: &raw::DefData) { | ||
457 | let module = Module { krate: self.def_collector.def_map.krate, module_id: self.module_id }; | ||
458 | let ctx = LocationCtx::new(self.def_collector.db, module, self.file_id.into()); | ||
459 | macro_rules! id { | ||
460 | () => { | ||
461 | AstItemDef::from_source_item_id_unchecked(ctx, def.source_item_id) | ||
462 | }; | ||
463 | } | ||
464 | let name = def.name.clone(); | ||
465 | let def: PerNs<ModuleDef> = match def.kind { | ||
466 | raw::DefKind::Function => PerNs::values(Function { id: id!() }.into()), | ||
467 | raw::DefKind::Struct => { | ||
468 | let s = Struct { id: id!() }.into(); | ||
469 | PerNs::both(s, s) | ||
470 | } | ||
471 | raw::DefKind::Enum => PerNs::types(Enum { id: id!() }.into()), | ||
472 | raw::DefKind::Const => PerNs::values(Const { id: id!() }.into()), | ||
473 | raw::DefKind::Static => PerNs::values(Static { id: id!() }.into()), | ||
474 | raw::DefKind::Trait => PerNs::types(Trait { id: id!() }.into()), | ||
475 | raw::DefKind::TypeAlias => PerNs::types(TypeAlias { id: id!() }.into()), | ||
476 | }; | ||
477 | let resolution = Resolution { def, import: None }; | ||
478 | self.def_collector.update(self.module_id, None, &[(name, resolution)]) | ||
479 | } | ||
480 | |||
481 | fn collect_macro(&mut self, mac: &raw::MacroData) { | ||
482 | // Case 1: macro rules, define a macro in crate-global mutable scope | ||
483 | if is_macro_rules(&mac.path) { | ||
484 | if let Some(name) = &mac.name { | ||
485 | self.def_collector.define_macro(name.clone(), &mac.arg, mac.export) | ||
486 | } | ||
487 | return; | ||
488 | } | ||
489 | |||
490 | let source_item_id = SourceItemId { file_id: self.file_id, item_id: mac.source_item_id }; | ||
491 | let macro_call_id = MacroCallLoc { | ||
492 | module: Module { krate: self.def_collector.def_map.krate, module_id: self.module_id }, | ||
493 | source_item_id, | ||
494 | } | ||
495 | .id(self.def_collector.db); | ||
496 | |||
497 | // Case 2: try to expand macro_rules from this crate, triggering | ||
498 | // recursive item collection. | ||
499 | if let Some(¯o_id) = | ||
500 | mac.path.as_ident().and_then(|name| self.def_collector.global_macro_scope.get(name)) | ||
501 | { | ||
502 | self.def_collector.collect_macro_expansion( | ||
503 | self.module_id, | ||
504 | macro_call_id, | ||
505 | (self.def_collector.def_map.krate, macro_id), | ||
506 | mac.arg.clone(), | ||
507 | ); | ||
508 | return; | ||
509 | } | ||
510 | |||
511 | // Case 3: path to a macro from another crate, expand during name resolution | ||
512 | self.def_collector.unexpanded_macros.push(( | ||
513 | self.module_id, | ||
514 | macro_call_id, | ||
515 | mac.path.clone(), | ||
516 | mac.arg.clone(), | ||
517 | )) | ||
518 | } | ||
519 | } | ||
520 | |||
521 | fn is_macro_rules(path: &Path) -> bool { | ||
522 | path.as_ident().and_then(Name::as_known_name) == Some(KnownName::MacroRules) | ||
523 | } | ||
524 | |||
525 | fn resolve_submodule( | ||
526 | db: &impl PersistentHirDatabase, | ||
527 | file_id: HirFileId, | ||
528 | name: &Name, | ||
529 | is_root: bool, | ||
530 | ) -> (Vec<FileId>, Option<Problem>) { | ||
531 | // FIXME: handle submodules of inline modules properly | ||
532 | let file_id = file_id.original_file(db); | ||
533 | let source_root_id = db.file_source_root(file_id); | ||
534 | let path = db.file_relative_path(file_id); | ||
535 | let root = RelativePathBuf::default(); | ||
536 | let dir_path = path.parent().unwrap_or(&root); | ||
537 | let mod_name = path.file_stem().unwrap_or("unknown"); | ||
538 | let is_dir_owner = is_root || mod_name == "mod"; | ||
539 | |||
540 | let file_mod = dir_path.join(format!("{}.rs", name)); | ||
541 | let dir_mod = dir_path.join(format!("{}/mod.rs", name)); | ||
542 | let file_dir_mod = dir_path.join(format!("{}/{}.rs", mod_name, name)); | ||
543 | let mut candidates = ArrayVec::<[_; 2]>::new(); | ||
544 | if is_dir_owner { | ||
545 | candidates.push(file_mod.clone()); | ||
546 | candidates.push(dir_mod); | ||
547 | } else { | ||
548 | candidates.push(file_dir_mod.clone()); | ||
549 | }; | ||
550 | let sr = db.source_root(source_root_id); | ||
551 | let points_to = candidates | ||
552 | .into_iter() | ||
553 | .filter_map(|path| sr.files.get(&path)) | ||
554 | .map(|&it| it) | ||
555 | .collect::<Vec<_>>(); | ||
556 | let problem = if points_to.is_empty() { | ||
557 | Some(Problem::UnresolvedModule { | ||
558 | candidate: if is_dir_owner { file_mod } else { file_dir_mod }, | ||
559 | }) | ||
560 | } else { | ||
561 | None | ||
562 | }; | ||
563 | (points_to, problem) | ||
564 | } | ||
diff --git a/crates/ra_hir/src/nameres/lower.rs b/crates/ra_hir/src/nameres/lower.rs deleted file mode 100644 index 56262ad6d..000000000 --- a/crates/ra_hir/src/nameres/lower.rs +++ /dev/null | |||
@@ -1,222 +0,0 @@ | |||
1 | use std::sync::Arc; | ||
2 | |||
3 | use ra_syntax::{ | ||
4 | AstNode, SourceFile, TreeArc, AstPtr, | ||
5 | ast::{self, ModuleItemOwner, NameOwner, AttrsOwner}, | ||
6 | }; | ||
7 | use ra_arena::{Arena, RawId, impl_arena_id, map::ArenaMap}; | ||
8 | use rustc_hash::FxHashMap; | ||
9 | |||
10 | use crate::{ | ||
11 | SourceItemId, Path, ModuleSource, Name, | ||
12 | HirFileId, MacroCallLoc, AsName, PerNs, Function, | ||
13 | ModuleDef, Module, Struct, Enum, Const, Static, Trait, TypeAlias, | ||
14 | ids::LocationCtx, PersistentHirDatabase, | ||
15 | }; | ||
16 | |||
17 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] | ||
18 | pub struct ImportId(RawId); | ||
19 | impl_arena_id!(ImportId); | ||
20 | |||
21 | #[derive(Debug, PartialEq, Eq)] | ||
22 | pub(super) struct ImportData { | ||
23 | pub(super) path: Path, | ||
24 | pub(super) alias: Option<Name>, | ||
25 | pub(super) is_glob: bool, | ||
26 | pub(super) is_prelude: bool, | ||
27 | pub(super) is_extern_crate: bool, | ||
28 | } | ||
29 | |||
30 | /// A set of items and imports declared inside a module, without relation to | ||
31 | /// other modules. | ||
32 | /// | ||
33 | /// This sits in-between raw syntax and name resolution and allows us to avoid | ||
34 | /// recomputing name res: if two instance of `InputModuleItems` are the same, we | ||
35 | /// can avoid redoing name resolution. | ||
36 | #[derive(Debug, Default, PartialEq, Eq)] | ||
37 | pub struct LoweredModule { | ||
38 | pub(crate) declarations: FxHashMap<Name, PerNs<ModuleDef>>, | ||
39 | pub(super) imports: Arena<ImportId, ImportData>, | ||
40 | } | ||
41 | |||
42 | #[derive(Debug, Default, PartialEq, Eq)] | ||
43 | pub struct ImportSourceMap { | ||
44 | map: ArenaMap<ImportId, AstPtr<ast::PathSegment>>, | ||
45 | } | ||
46 | |||
47 | impl ImportSourceMap { | ||
48 | fn insert(&mut self, import: ImportId, segment: &ast::PathSegment) { | ||
49 | self.map.insert(import, AstPtr::new(segment)) | ||
50 | } | ||
51 | |||
52 | pub fn get(&self, source: &ModuleSource, import: ImportId) -> TreeArc<ast::PathSegment> { | ||
53 | let file = match source { | ||
54 | ModuleSource::SourceFile(file) => &*file, | ||
55 | ModuleSource::Module(m) => m.syntax().ancestors().find_map(SourceFile::cast).unwrap(), | ||
56 | }; | ||
57 | |||
58 | self.map[import].to_node(file).to_owned() | ||
59 | } | ||
60 | } | ||
61 | |||
62 | impl LoweredModule { | ||
63 | pub(crate) fn lower_module_query( | ||
64 | db: &impl PersistentHirDatabase, | ||
65 | module: Module, | ||
66 | ) -> Arc<LoweredModule> { | ||
67 | db.lower_module_with_source_map(module).0 | ||
68 | } | ||
69 | |||
70 | pub(crate) fn lower_module_with_source_map_query( | ||
71 | db: &impl PersistentHirDatabase, | ||
72 | module: Module, | ||
73 | ) -> (Arc<LoweredModule>, Arc<ImportSourceMap>) { | ||
74 | let (file_id, source) = module.definition_source(db); | ||
75 | let file_id: HirFileId = file_id.into(); | ||
76 | let mut source_map = ImportSourceMap::default(); | ||
77 | let mut res = LoweredModule::default(); | ||
78 | match source { | ||
79 | ModuleSource::SourceFile(it) => { | ||
80 | res.fill(&mut source_map, db, module, file_id, &mut it.items_with_macros()) | ||
81 | } | ||
82 | ModuleSource::Module(it) => { | ||
83 | if let Some(item_list) = it.item_list() { | ||
84 | res.fill( | ||
85 | &mut source_map, | ||
86 | db, | ||
87 | module, | ||
88 | file_id, | ||
89 | &mut item_list.items_with_macros(), | ||
90 | ) | ||
91 | } | ||
92 | } | ||
93 | }; | ||
94 | (Arc::new(res), Arc::new(source_map)) | ||
95 | } | ||
96 | |||
97 | fn fill( | ||
98 | &mut self, | ||
99 | source_map: &mut ImportSourceMap, | ||
100 | db: &impl PersistentHirDatabase, | ||
101 | module: Module, | ||
102 | file_id: HirFileId, | ||
103 | items: &mut Iterator<Item = ast::ItemOrMacro>, | ||
104 | ) { | ||
105 | let file_items = db.file_items(file_id); | ||
106 | |||
107 | for item in items { | ||
108 | match item { | ||
109 | ast::ItemOrMacro::Item(it) => { | ||
110 | self.add_def_id(source_map, db, module, file_id, it); | ||
111 | } | ||
112 | ast::ItemOrMacro::Macro(macro_call) => { | ||
113 | let item_id = file_items.id_of_unchecked(macro_call.syntax()); | ||
114 | let loc = | ||
115 | MacroCallLoc { module, source_item_id: SourceItemId { file_id, item_id } }; | ||
116 | let id = loc.id(db); | ||
117 | let file_id = HirFileId::from(id); | ||
118 | //FIXME: expand recursively | ||
119 | for item in db.hir_parse(file_id).items() { | ||
120 | self.add_def_id(source_map, db, module, file_id, item); | ||
121 | } | ||
122 | } | ||
123 | } | ||
124 | } | ||
125 | } | ||
126 | |||
127 | fn add_def_id( | ||
128 | &mut self, | ||
129 | source_map: &mut ImportSourceMap, | ||
130 | db: &impl PersistentHirDatabase, | ||
131 | module: Module, | ||
132 | file_id: HirFileId, | ||
133 | item: &ast::ModuleItem, | ||
134 | ) { | ||
135 | let ctx = LocationCtx::new(db, module, file_id); | ||
136 | match item.kind() { | ||
137 | ast::ModuleItemKind::StructDef(it) => { | ||
138 | if let Some(name) = it.name() { | ||
139 | let s = Struct { id: ctx.to_def(it) }; | ||
140 | let s: ModuleDef = s.into(); | ||
141 | self.declarations.insert(name.as_name(), PerNs::both(s, s)); | ||
142 | } | ||
143 | } | ||
144 | ast::ModuleItemKind::EnumDef(it) => { | ||
145 | if let Some(name) = it.name() { | ||
146 | let e = Enum { id: ctx.to_def(it) }; | ||
147 | let e: ModuleDef = e.into(); | ||
148 | self.declarations.insert(name.as_name(), PerNs::types(e)); | ||
149 | } | ||
150 | } | ||
151 | ast::ModuleItemKind::FnDef(it) => { | ||
152 | if let Some(name) = it.name() { | ||
153 | let func = Function { id: ctx.to_def(it) }; | ||
154 | self.declarations.insert(name.as_name(), PerNs::values(func.into())); | ||
155 | } | ||
156 | } | ||
157 | ast::ModuleItemKind::TraitDef(it) => { | ||
158 | if let Some(name) = it.name() { | ||
159 | let t = Trait { id: ctx.to_def(it) }; | ||
160 | self.declarations.insert(name.as_name(), PerNs::types(t.into())); | ||
161 | } | ||
162 | } | ||
163 | ast::ModuleItemKind::TypeAliasDef(it) => { | ||
164 | if let Some(name) = it.name() { | ||
165 | let t = TypeAlias { id: ctx.to_def(it) }; | ||
166 | self.declarations.insert(name.as_name(), PerNs::types(t.into())); | ||
167 | } | ||
168 | } | ||
169 | ast::ModuleItemKind::ImplBlock(_) => { | ||
170 | // impls don't define items | ||
171 | } | ||
172 | ast::ModuleItemKind::UseItem(it) => { | ||
173 | self.add_use_item(source_map, it); | ||
174 | } | ||
175 | ast::ModuleItemKind::ExternCrateItem(it) => { | ||
176 | if let Some(name_ref) = it.name_ref() { | ||
177 | let path = Path::from_name_ref(name_ref); | ||
178 | let alias = it.alias().and_then(|a| a.name()).map(AsName::as_name); | ||
179 | self.imports.alloc(ImportData { | ||
180 | path, | ||
181 | alias, | ||
182 | is_glob: false, | ||
183 | is_prelude: false, | ||
184 | is_extern_crate: true, | ||
185 | }); | ||
186 | } | ||
187 | } | ||
188 | ast::ModuleItemKind::ConstDef(it) => { | ||
189 | if let Some(name) = it.name() { | ||
190 | let c = Const { id: ctx.to_def(it) }; | ||
191 | self.declarations.insert(name.as_name(), PerNs::values(c.into())); | ||
192 | } | ||
193 | } | ||
194 | ast::ModuleItemKind::StaticDef(it) => { | ||
195 | if let Some(name) = it.name() { | ||
196 | let s = Static { id: ctx.to_def(it) }; | ||
197 | self.declarations.insert(name.as_name(), PerNs::values(s.into())); | ||
198 | } | ||
199 | } | ||
200 | ast::ModuleItemKind::Module(_) => { | ||
201 | // modules are handled separately directly by name res | ||
202 | } | ||
203 | }; | ||
204 | } | ||
205 | |||
206 | fn add_use_item(&mut self, source_map: &mut ImportSourceMap, item: &ast::UseItem) { | ||
207 | let is_prelude = | ||
208 | item.attrs().any(|attr| attr.as_atom().map(|s| s == "prelude_import").unwrap_or(false)); | ||
209 | Path::expand_use_item(item, |path, segment, alias| { | ||
210 | let import = self.imports.alloc(ImportData { | ||
211 | path, | ||
212 | alias, | ||
213 | is_glob: segment.is_none(), | ||
214 | is_prelude, | ||
215 | is_extern_crate: false, | ||
216 | }); | ||
217 | if let Some(segment) = segment { | ||
218 | source_map.insert(import, segment) | ||
219 | } | ||
220 | }) | ||
221 | } | ||
222 | } | ||
diff --git a/crates/ra_hir/src/nameres/per_ns.rs b/crates/ra_hir/src/nameres/per_ns.rs new file mode 100644 index 000000000..c40a3ff9d --- /dev/null +++ b/crates/ra_hir/src/nameres/per_ns.rs | |||
@@ -0,0 +1,78 @@ | |||
1 | #[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] | ||
2 | pub enum Namespace { | ||
3 | Types, | ||
4 | Values, | ||
5 | } | ||
6 | |||
7 | #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] | ||
8 | pub struct PerNs<T> { | ||
9 | pub types: Option<T>, | ||
10 | pub values: Option<T>, | ||
11 | } | ||
12 | |||
13 | impl<T> Default for PerNs<T> { | ||
14 | fn default() -> Self { | ||
15 | PerNs { types: None, values: None } | ||
16 | } | ||
17 | } | ||
18 | |||
19 | impl<T> PerNs<T> { | ||
20 | pub fn none() -> PerNs<T> { | ||
21 | PerNs { types: None, values: None } | ||
22 | } | ||
23 | |||
24 | pub fn values(t: T) -> PerNs<T> { | ||
25 | PerNs { types: None, values: Some(t) } | ||
26 | } | ||
27 | |||
28 | pub fn types(t: T) -> PerNs<T> { | ||
29 | PerNs { types: Some(t), values: None } | ||
30 | } | ||
31 | |||
32 | pub fn both(types: T, values: T) -> PerNs<T> { | ||
33 | PerNs { types: Some(types), values: Some(values) } | ||
34 | } | ||
35 | |||
36 | pub fn is_none(&self) -> bool { | ||
37 | self.types.is_none() && self.values.is_none() | ||
38 | } | ||
39 | |||
40 | pub fn is_both(&self) -> bool { | ||
41 | self.types.is_some() && self.values.is_some() | ||
42 | } | ||
43 | |||
44 | pub fn take(self, namespace: Namespace) -> Option<T> { | ||
45 | match namespace { | ||
46 | Namespace::Types => self.types, | ||
47 | Namespace::Values => self.values, | ||
48 | } | ||
49 | } | ||
50 | |||
51 | pub fn take_types(self) -> Option<T> { | ||
52 | self.take(Namespace::Types) | ||
53 | } | ||
54 | |||
55 | pub fn take_values(self) -> Option<T> { | ||
56 | self.take(Namespace::Values) | ||
57 | } | ||
58 | |||
59 | pub fn get(&self, namespace: Namespace) -> Option<&T> { | ||
60 | self.as_ref().take(namespace) | ||
61 | } | ||
62 | |||
63 | pub fn as_ref(&self) -> PerNs<&T> { | ||
64 | PerNs { types: self.types.as_ref(), values: self.values.as_ref() } | ||
65 | } | ||
66 | |||
67 | pub fn or(self, other: PerNs<T>) -> PerNs<T> { | ||
68 | PerNs { types: self.types.or(other.types), values: self.values.or(other.values) } | ||
69 | } | ||
70 | |||
71 | pub fn and_then<U>(self, f: impl Fn(T) -> Option<U>) -> PerNs<U> { | ||
72 | PerNs { types: self.types.and_then(&f), values: self.values.and_then(&f) } | ||
73 | } | ||
74 | |||
75 | pub fn map<U>(self, f: impl Fn(T) -> U) -> PerNs<U> { | ||
76 | PerNs { types: self.types.map(&f), values: self.values.map(&f) } | ||
77 | } | ||
78 | } | ||
diff --git a/crates/ra_hir/src/nameres/raw.rs b/crates/ra_hir/src/nameres/raw.rs new file mode 100644 index 000000000..3226bbf0d --- /dev/null +++ b/crates/ra_hir/src/nameres/raw.rs | |||
@@ -0,0 +1,322 @@ | |||
1 | use std::{ | ||
2 | sync::Arc, | ||
3 | ops::Index, | ||
4 | }; | ||
5 | |||
6 | use test_utils::tested_by; | ||
7 | use ra_db::FileId; | ||
8 | use ra_arena::{Arena, impl_arena_id, RawId, map::ArenaMap}; | ||
9 | use ra_syntax::{ | ||
10 | AstNode, SourceFile, AstPtr, TreeArc, | ||
11 | ast::{self, NameOwner, AttrsOwner}, | ||
12 | }; | ||
13 | |||
14 | use crate::{ | ||
15 | PersistentHirDatabase, Name, AsName, Path, HirFileId, ModuleSource, | ||
16 | ids::{SourceFileItemId, SourceFileItems}, | ||
17 | }; | ||
18 | |||
19 | #[derive(Debug, Default, PartialEq, Eq)] | ||
20 | pub struct RawItems { | ||
21 | modules: Arena<Module, ModuleData>, | ||
22 | imports: Arena<ImportId, ImportData>, | ||
23 | defs: Arena<Def, DefData>, | ||
24 | macros: Arena<Macro, MacroData>, | ||
25 | /// items for top-level module | ||
26 | items: Vec<RawItem>, | ||
27 | } | ||
28 | |||
29 | #[derive(Debug, Default, PartialEq, Eq)] | ||
30 | pub struct ImportSourceMap { | ||
31 | map: ArenaMap<ImportId, AstPtr<ast::PathSegment>>, | ||
32 | } | ||
33 | |||
34 | impl ImportSourceMap { | ||
35 | pub(crate) fn insert(&mut self, import: ImportId, segment: &ast::PathSegment) { | ||
36 | self.map.insert(import, AstPtr::new(segment)) | ||
37 | } | ||
38 | |||
39 | pub fn get(&self, source: &ModuleSource, import: ImportId) -> TreeArc<ast::PathSegment> { | ||
40 | let file = match source { | ||
41 | ModuleSource::SourceFile(file) => &*file, | ||
42 | ModuleSource::Module(m) => m.syntax().ancestors().find_map(SourceFile::cast).unwrap(), | ||
43 | }; | ||
44 | |||
45 | self.map[import].to_node(file).to_owned() | ||
46 | } | ||
47 | } | ||
48 | |||
49 | impl RawItems { | ||
50 | pub(crate) fn raw_items_query( | ||
51 | db: &impl PersistentHirDatabase, | ||
52 | file_id: FileId, | ||
53 | ) -> Arc<RawItems> { | ||
54 | db.raw_items_with_source_map(file_id).0 | ||
55 | } | ||
56 | |||
57 | pub(crate) fn raw_items_with_source_map_query( | ||
58 | db: &impl PersistentHirDatabase, | ||
59 | file_id: FileId, | ||
60 | ) -> (Arc<RawItems>, Arc<ImportSourceMap>) { | ||
61 | let mut collector = RawItemsCollector { | ||
62 | raw_items: RawItems::default(), | ||
63 | source_file_items: db.file_items(file_id.into()), | ||
64 | source_map: ImportSourceMap::default(), | ||
65 | }; | ||
66 | let source_file = db.parse(file_id); | ||
67 | collector.process_module(None, &*source_file); | ||
68 | (Arc::new(collector.raw_items), Arc::new(collector.source_map)) | ||
69 | } | ||
70 | |||
71 | pub(crate) fn items(&self) -> &[RawItem] { | ||
72 | &self.items | ||
73 | } | ||
74 | |||
75 | // We can't use queries during name resolution for fear of cycles, so this | ||
76 | // is a query-less variant of the above function. | ||
77 | pub(crate) fn from_source_file(source_file: &SourceFile, file_id: HirFileId) -> RawItems { | ||
78 | let source_file_items = SourceFileItems::from_source_file(source_file, file_id); | ||
79 | let mut collector = RawItemsCollector { | ||
80 | raw_items: RawItems::default(), | ||
81 | source_file_items: Arc::new(source_file_items), | ||
82 | source_map: ImportSourceMap::default(), | ||
83 | }; | ||
84 | collector.process_module(None, &*source_file); | ||
85 | collector.raw_items | ||
86 | } | ||
87 | } | ||
88 | |||
89 | impl Index<Module> for RawItems { | ||
90 | type Output = ModuleData; | ||
91 | fn index(&self, idx: Module) -> &ModuleData { | ||
92 | &self.modules[idx] | ||
93 | } | ||
94 | } | ||
95 | |||
96 | impl Index<ImportId> for RawItems { | ||
97 | type Output = ImportData; | ||
98 | fn index(&self, idx: ImportId) -> &ImportData { | ||
99 | &self.imports[idx] | ||
100 | } | ||
101 | } | ||
102 | |||
103 | impl Index<Def> for RawItems { | ||
104 | type Output = DefData; | ||
105 | fn index(&self, idx: Def) -> &DefData { | ||
106 | &self.defs[idx] | ||
107 | } | ||
108 | } | ||
109 | |||
110 | impl Index<Macro> for RawItems { | ||
111 | type Output = MacroData; | ||
112 | fn index(&self, idx: Macro) -> &MacroData { | ||
113 | &self.macros[idx] | ||
114 | } | ||
115 | } | ||
116 | |||
117 | #[derive(Debug, PartialEq, Eq, Clone, Copy)] | ||
118 | pub(crate) enum RawItem { | ||
119 | Module(Module), | ||
120 | Import(ImportId), | ||
121 | Def(Def), | ||
122 | Macro(Macro), | ||
123 | } | ||
124 | |||
125 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] | ||
126 | pub(crate) struct Module(RawId); | ||
127 | impl_arena_id!(Module); | ||
128 | |||
129 | #[derive(Debug, PartialEq, Eq)] | ||
130 | pub(crate) enum ModuleData { | ||
131 | Declaration { name: Name, source_item_id: SourceFileItemId }, | ||
132 | Definition { name: Name, source_item_id: SourceFileItemId, items: Vec<RawItem> }, | ||
133 | } | ||
134 | |||
135 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] | ||
136 | pub struct ImportId(RawId); | ||
137 | impl_arena_id!(ImportId); | ||
138 | |||
139 | #[derive(Debug, Clone, PartialEq, Eq)] | ||
140 | pub struct ImportData { | ||
141 | pub(crate) path: Path, | ||
142 | pub(crate) alias: Option<Name>, | ||
143 | pub(crate) is_glob: bool, | ||
144 | pub(crate) is_prelude: bool, | ||
145 | pub(crate) is_extern_crate: bool, | ||
146 | } | ||
147 | |||
148 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] | ||
149 | pub(crate) struct Def(RawId); | ||
150 | impl_arena_id!(Def); | ||
151 | |||
152 | #[derive(Debug, PartialEq, Eq)] | ||
153 | pub(crate) struct DefData { | ||
154 | pub(crate) source_item_id: SourceFileItemId, | ||
155 | pub(crate) name: Name, | ||
156 | pub(crate) kind: DefKind, | ||
157 | } | ||
158 | |||
159 | #[derive(Debug, PartialEq, Eq, Clone, Copy)] | ||
160 | pub(crate) enum DefKind { | ||
161 | Function, | ||
162 | Struct, | ||
163 | Enum, | ||
164 | Const, | ||
165 | Static, | ||
166 | Trait, | ||
167 | TypeAlias, | ||
168 | } | ||
169 | |||
170 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] | ||
171 | pub(crate) struct Macro(RawId); | ||
172 | impl_arena_id!(Macro); | ||
173 | |||
174 | #[derive(Debug, PartialEq, Eq)] | ||
175 | pub(crate) struct MacroData { | ||
176 | pub(crate) source_item_id: SourceFileItemId, | ||
177 | pub(crate) path: Path, | ||
178 | pub(crate) name: Option<Name>, | ||
179 | pub(crate) arg: tt::Subtree, | ||
180 | pub(crate) export: bool, | ||
181 | } | ||
182 | |||
183 | struct RawItemsCollector { | ||
184 | raw_items: RawItems, | ||
185 | source_file_items: Arc<SourceFileItems>, | ||
186 | source_map: ImportSourceMap, | ||
187 | } | ||
188 | |||
189 | impl RawItemsCollector { | ||
190 | fn process_module(&mut self, current_module: Option<Module>, body: &impl ast::ModuleItemOwner) { | ||
191 | for item_or_macro in body.items_with_macros() { | ||
192 | match item_or_macro { | ||
193 | ast::ItemOrMacro::Macro(m) => self.add_macro(current_module, m), | ||
194 | ast::ItemOrMacro::Item(item) => self.add_item(current_module, item), | ||
195 | } | ||
196 | } | ||
197 | } | ||
198 | |||
199 | fn add_item(&mut self, current_module: Option<Module>, item: &ast::ModuleItem) { | ||
200 | let (kind, name) = match item.kind() { | ||
201 | ast::ModuleItemKind::Module(module) => { | ||
202 | self.add_module(current_module, module); | ||
203 | return; | ||
204 | } | ||
205 | ast::ModuleItemKind::UseItem(use_item) => { | ||
206 | self.add_use_item(current_module, use_item); | ||
207 | return; | ||
208 | } | ||
209 | ast::ModuleItemKind::ExternCrateItem(extern_crate) => { | ||
210 | self.add_extern_crate_item(current_module, extern_crate); | ||
211 | return; | ||
212 | } | ||
213 | ast::ModuleItemKind::ImplBlock(_) => { | ||
214 | // impls don't participate in name resolution | ||
215 | return; | ||
216 | } | ||
217 | ast::ModuleItemKind::StructDef(it) => (DefKind::Struct, it.name()), | ||
218 | ast::ModuleItemKind::EnumDef(it) => (DefKind::Enum, it.name()), | ||
219 | ast::ModuleItemKind::FnDef(it) => (DefKind::Function, it.name()), | ||
220 | ast::ModuleItemKind::TraitDef(it) => (DefKind::Trait, it.name()), | ||
221 | ast::ModuleItemKind::TypeAliasDef(it) => (DefKind::TypeAlias, it.name()), | ||
222 | ast::ModuleItemKind::ConstDef(it) => (DefKind::Const, it.name()), | ||
223 | ast::ModuleItemKind::StaticDef(it) => (DefKind::Static, it.name()), | ||
224 | }; | ||
225 | if let Some(name) = name { | ||
226 | let name = name.as_name(); | ||
227 | let source_item_id = self.source_file_items.id_of_unchecked(item.syntax()); | ||
228 | let def = self.raw_items.defs.alloc(DefData { name, kind, source_item_id }); | ||
229 | self.push_item(current_module, RawItem::Def(def)) | ||
230 | } | ||
231 | } | ||
232 | |||
233 | fn add_module(&mut self, current_module: Option<Module>, module: &ast::Module) { | ||
234 | let name = match module.name() { | ||
235 | Some(it) => it.as_name(), | ||
236 | None => return, | ||
237 | }; | ||
238 | let source_item_id = self.source_file_items.id_of_unchecked(module.syntax()); | ||
239 | if module.has_semi() { | ||
240 | let item = | ||
241 | self.raw_items.modules.alloc(ModuleData::Declaration { name, source_item_id }); | ||
242 | self.push_item(current_module, RawItem::Module(item)); | ||
243 | return; | ||
244 | } | ||
245 | |||
246 | if let Some(item_list) = module.item_list() { | ||
247 | let item = self.raw_items.modules.alloc(ModuleData::Definition { | ||
248 | name, | ||
249 | source_item_id, | ||
250 | items: Vec::new(), | ||
251 | }); | ||
252 | self.process_module(Some(item), item_list); | ||
253 | self.push_item(current_module, RawItem::Module(item)); | ||
254 | return; | ||
255 | } | ||
256 | tested_by!(name_res_works_for_broken_modules); | ||
257 | } | ||
258 | |||
259 | fn add_use_item(&mut self, current_module: Option<Module>, use_item: &ast::UseItem) { | ||
260 | let is_prelude = use_item.has_atom_attr("prelude_import"); | ||
261 | |||
262 | Path::expand_use_item(use_item, |path, segment, alias| { | ||
263 | let import = self.raw_items.imports.alloc(ImportData { | ||
264 | path, | ||
265 | alias, | ||
266 | is_glob: segment.is_none(), | ||
267 | is_prelude, | ||
268 | is_extern_crate: false, | ||
269 | }); | ||
270 | if let Some(segment) = segment { | ||
271 | self.source_map.insert(import, segment) | ||
272 | } | ||
273 | self.push_item(current_module, RawItem::Import(import)) | ||
274 | }) | ||
275 | } | ||
276 | |||
277 | fn add_extern_crate_item( | ||
278 | &mut self, | ||
279 | current_module: Option<Module>, | ||
280 | extern_crate: &ast::ExternCrateItem, | ||
281 | ) { | ||
282 | if let Some(name_ref) = extern_crate.name_ref() { | ||
283 | let path = Path::from_name_ref(name_ref); | ||
284 | let alias = extern_crate.alias().and_then(|a| a.name()).map(AsName::as_name); | ||
285 | let import = self.raw_items.imports.alloc(ImportData { | ||
286 | path, | ||
287 | alias, | ||
288 | is_glob: false, | ||
289 | is_prelude: false, | ||
290 | is_extern_crate: true, | ||
291 | }); | ||
292 | self.push_item(current_module, RawItem::Import(import)) | ||
293 | } | ||
294 | } | ||
295 | |||
296 | fn add_macro(&mut self, current_module: Option<Module>, m: &ast::MacroCall) { | ||
297 | let (path, arg) = match ( | ||
298 | m.path().and_then(Path::from_ast), | ||
299 | m.token_tree().and_then(mbe::ast_to_token_tree), | ||
300 | ) { | ||
301 | (Some(path), Some((token_tree, _token_map))) => (path, token_tree), | ||
302 | _ => return, | ||
303 | }; | ||
304 | |||
305 | let name = m.name().map(|it| it.as_name()); | ||
306 | let source_item_id = self.source_file_items.id_of_unchecked(m.syntax()); | ||
307 | let export = m.has_atom_attr("macro_export"); | ||
308 | let m = self.raw_items.macros.alloc(MacroData { source_item_id, path, arg, name, export }); | ||
309 | self.push_item(current_module, RawItem::Macro(m)); | ||
310 | } | ||
311 | |||
312 | fn push_item(&mut self, current_module: Option<Module>, item: RawItem) { | ||
313 | match current_module { | ||
314 | Some(module) => match &mut self.raw_items.modules[module] { | ||
315 | ModuleData::Definition { items, .. } => items, | ||
316 | ModuleData::Declaration { .. } => unreachable!(), | ||
317 | }, | ||
318 | None => &mut self.raw_items.items, | ||
319 | } | ||
320 | .push(item) | ||
321 | } | ||
322 | } | ||
diff --git a/crates/ra_hir/src/nameres/tests.rs b/crates/ra_hir/src/nameres/tests.rs index 9b151bb0c..ac9b88520 100644 --- a/crates/ra_hir/src/nameres/tests.rs +++ b/crates/ra_hir/src/nameres/tests.rs | |||
@@ -1,34 +1,43 @@ | |||
1 | mod macros; | ||
2 | mod globs; | ||
3 | mod incremental; | ||
4 | |||
1 | use std::sync::Arc; | 5 | use std::sync::Arc; |
2 | 6 | ||
3 | use ra_db::SourceDatabase; | 7 | use ra_db::SourceDatabase; |
4 | use test_utils::{assert_eq_text, covers}; | 8 | use test_utils::covers; |
5 | 9 | use insta::assert_snapshot_matches; | |
6 | use crate::{ | 10 | |
7 | ItemMap, | 11 | use crate::{Crate, mock::{MockDatabase, CrateGraphFixture}, nameres::Resolution}; |
8 | PersistentHirDatabase, | 12 | |
9 | mock::MockDatabase, | 13 | use super::*; |
10 | module_tree::ModuleId, | 14 | |
11 | }; | 15 | fn compute_crate_def_map(fixture: &str, graph: Option<CrateGraphFixture>) -> Arc<CrateDefMap> { |
12 | use super::Resolution; | 16 | let mut db = MockDatabase::with_files(fixture); |
13 | 17 | if let Some(graph) = graph { | |
14 | fn item_map(fixture: &str) -> (Arc<ItemMap>, ModuleId) { | 18 | db.set_crate_graph_from_fixture(graph); |
15 | let (db, pos) = MockDatabase::with_position(fixture); | 19 | } |
16 | let module = crate::source_binder::module_from_position(&db, pos).unwrap(); | 20 | let crate_id = db.crate_graph().iter().next().unwrap(); |
17 | let krate = module.krate(&db).unwrap(); | 21 | let krate = Crate { crate_id }; |
18 | let module_id = module.module_id; | 22 | db.crate_def_map(krate) |
19 | (db.item_map(krate), module_id) | ||
20 | } | 23 | } |
21 | 24 | ||
22 | fn check_module_item_map(map: &ItemMap, module_id: ModuleId, expected: &str) { | 25 | fn render_crate_def_map(map: &CrateDefMap) -> String { |
23 | let mut lines = map[module_id] | 26 | let mut buf = String::new(); |
24 | .items | 27 | go(&mut buf, map, "\ncrate", map.root); |
25 | .iter() | 28 | return buf; |
26 | .map(|(name, res)| format!("{}: {}", name, dump_resolution(res))) | 29 | |
27 | .collect::<Vec<_>>(); | 30 | fn go(buf: &mut String, map: &CrateDefMap, path: &str, module: CrateModuleId) { |
28 | lines.sort(); | 31 | *buf += path; |
29 | let actual = lines.join("\n"); | 32 | *buf += "\n"; |
30 | let expected = expected.trim().lines().map(|it| it.trim()).collect::<Vec<_>>().join("\n"); | 33 | for (name, res) in map.modules[module].scope.items.iter() { |
31 | assert_eq_text!(&expected, &actual); | 34 | *buf += &format!("{}: {}\n", name, dump_resolution(res)) |
35 | } | ||
36 | for (name, child) in map.modules[module].children.iter() { | ||
37 | let path = path.to_string() + &format!("::{}", name); | ||
38 | go(buf, map, &path, *child); | ||
39 | } | ||
40 | } | ||
32 | 41 | ||
33 | fn dump_resolution(resolution: &Resolution) -> &'static str { | 42 | fn dump_resolution(resolution: &Resolution) -> &'static str { |
34 | match (resolution.def.types.is_some(), resolution.def.values.is_some()) { | 43 | match (resolution.def.types.is_some(), resolution.def.values.is_some()) { |
@@ -40,66 +49,112 @@ fn check_module_item_map(map: &ItemMap, module_id: ModuleId, expected: &str) { | |||
40 | } | 49 | } |
41 | } | 50 | } |
42 | 51 | ||
52 | fn def_map(fixtute: &str) -> String { | ||
53 | let dm = compute_crate_def_map(fixtute, None); | ||
54 | render_crate_def_map(&dm) | ||
55 | } | ||
56 | |||
57 | fn def_map_with_crate_graph(fixtute: &str, graph: CrateGraphFixture) -> String { | ||
58 | let dm = compute_crate_def_map(fixtute, Some(graph)); | ||
59 | render_crate_def_map(&dm) | ||
60 | } | ||
61 | |||
43 | #[test] | 62 | #[test] |
44 | fn item_map_smoke_test() { | 63 | fn crate_def_map_smoke_test() { |
45 | let (item_map, module_id) = item_map( | 64 | let map = def_map( |
46 | " | 65 | " |
47 | //- /lib.rs | 66 | //- /lib.rs |
48 | mod foo; | 67 | mod foo; |
49 | 68 | struct S; | |
50 | use crate::foo::bar::Baz; | 69 | use crate::foo::bar::E; |
51 | <|> | 70 | use self::E::V; |
52 | 71 | ||
53 | //- /foo/mod.rs | 72 | //- /foo/mod.rs |
54 | pub mod bar; | 73 | pub mod bar; |
74 | fn f() {} | ||
55 | 75 | ||
56 | //- /foo/bar.rs | 76 | //- /foo/bar.rs |
57 | pub struct Baz; | 77 | pub struct Baz; |
58 | ", | 78 | enum E { V } |
79 | ", | ||
59 | ); | 80 | ); |
60 | check_module_item_map( | 81 | assert_snapshot_matches!(map, @r###" |
61 | &item_map, | 82 | crate |
62 | module_id, | 83 | V: t v |
84 | E: t | ||
85 | foo: t | ||
86 | S: t v | ||
87 | |||
88 | crate::foo | ||
89 | bar: t | ||
90 | f: v | ||
91 | |||
92 | crate::foo::bar | ||
93 | Baz: t v | ||
94 | E: t | ||
95 | "### | ||
96 | ) | ||
97 | } | ||
98 | |||
99 | #[test] | ||
100 | fn bogus_paths() { | ||
101 | covers!(bogus_paths); | ||
102 | let map = def_map( | ||
63 | " | 103 | " |
64 | Baz: t v | 104 | //- /lib.rs |
65 | foo: t | 105 | mod foo; |
106 | struct S; | ||
107 | use self; | ||
108 | |||
109 | //- /foo/mod.rs | ||
110 | use super; | ||
111 | use crate; | ||
112 | |||
66 | ", | 113 | ", |
67 | ); | 114 | ); |
115 | assert_snapshot_matches!(map, @r###" | ||
116 | crate | ||
117 | foo: t | ||
118 | S: t v | ||
119 | |||
120 | crate::foo | ||
121 | "### | ||
122 | ) | ||
68 | } | 123 | } |
69 | 124 | ||
70 | #[test] | 125 | #[test] |
71 | fn use_as() { | 126 | fn use_as() { |
72 | let (item_map, module_id) = item_map( | 127 | let map = def_map( |
73 | " | 128 | " |
74 | //- /lib.rs | 129 | //- /lib.rs |
75 | mod foo; | 130 | mod foo; |
76 | 131 | ||
77 | use crate::foo::Baz as Foo; | 132 | use crate::foo::Baz as Foo; |
78 | <|> | ||
79 | 133 | ||
80 | //- /foo/mod.rs | 134 | //- /foo/mod.rs |
81 | pub struct Baz; | 135 | pub struct Baz; |
82 | ", | ||
83 | ); | ||
84 | check_module_item_map( | ||
85 | &item_map, | ||
86 | module_id, | ||
87 | " | ||
88 | Foo: t v | ||
89 | foo: t | ||
90 | ", | 136 | ", |
91 | ); | 137 | ); |
138 | assert_snapshot_matches!(map, | ||
139 | @r###" | ||
140 | crate | ||
141 | Foo: t v | ||
142 | foo: t | ||
143 | |||
144 | crate::foo | ||
145 | Baz: t v | ||
146 | "### | ||
147 | ); | ||
92 | } | 148 | } |
93 | 149 | ||
94 | #[test] | 150 | #[test] |
95 | fn use_trees() { | 151 | fn use_trees() { |
96 | let (item_map, module_id) = item_map( | 152 | let map = def_map( |
97 | " | 153 | " |
98 | //- /lib.rs | 154 | //- /lib.rs |
99 | mod foo; | 155 | mod foo; |
100 | 156 | ||
101 | use crate::foo::bar::{Baz, Quux}; | 157 | use crate::foo::bar::{Baz, Quux}; |
102 | <|> | ||
103 | 158 | ||
104 | //- /foo/mod.rs | 159 | //- /foo/mod.rs |
105 | pub mod bar; | 160 | pub mod bar; |
@@ -107,28 +162,33 @@ fn use_trees() { | |||
107 | //- /foo/bar.rs | 162 | //- /foo/bar.rs |
108 | pub struct Baz; | 163 | pub struct Baz; |
109 | pub enum Quux {}; | 164 | pub enum Quux {}; |
110 | ", | ||
111 | ); | ||
112 | check_module_item_map( | ||
113 | &item_map, | ||
114 | module_id, | ||
115 | " | ||
116 | Baz: t v | ||
117 | Quux: t | ||
118 | foo: t | ||
119 | ", | 165 | ", |
120 | ); | 166 | ); |
167 | assert_snapshot_matches!(map, | ||
168 | @r###" | ||
169 | crate | ||
170 | Quux: t | ||
171 | Baz: t v | ||
172 | foo: t | ||
173 | |||
174 | crate::foo | ||
175 | bar: t | ||
176 | |||
177 | crate::foo::bar | ||
178 | Quux: t | ||
179 | Baz: t v | ||
180 | "### | ||
181 | ); | ||
121 | } | 182 | } |
122 | 183 | ||
123 | #[test] | 184 | #[test] |
124 | fn re_exports() { | 185 | fn re_exports() { |
125 | let (item_map, module_id) = item_map( | 186 | let map = def_map( |
126 | " | 187 | " |
127 | //- /lib.rs | 188 | //- /lib.rs |
128 | mod foo; | 189 | mod foo; |
129 | 190 | ||
130 | use self::foo::Baz; | 191 | use self::foo::Baz; |
131 | <|> | ||
132 | 192 | ||
133 | //- /foo/mod.rs | 193 | //- /foo/mod.rs |
134 | pub mod bar; | 194 | pub mod bar; |
@@ -137,137 +197,73 @@ fn re_exports() { | |||
137 | 197 | ||
138 | //- /foo/bar.rs | 198 | //- /foo/bar.rs |
139 | pub struct Baz; | 199 | pub struct Baz; |
140 | ", | ||
141 | ); | ||
142 | check_module_item_map( | ||
143 | &item_map, | ||
144 | module_id, | ||
145 | " | ||
146 | Baz: t v | ||
147 | foo: t | ||
148 | ", | 200 | ", |
149 | ); | 201 | ); |
150 | } | 202 | assert_snapshot_matches!(map, |
203 | @r###" | ||
204 | crate | ||
205 | Baz: t v | ||
206 | foo: t | ||
151 | 207 | ||
152 | #[test] | 208 | crate::foo |
153 | fn glob_1() { | 209 | bar: t |
154 | let (item_map, module_id) = item_map( | 210 | Baz: t v |
155 | " | ||
156 | //- /lib.rs | ||
157 | mod foo; | ||
158 | use foo::*; | ||
159 | <|> | ||
160 | |||
161 | //- /foo/mod.rs | ||
162 | pub mod bar; | ||
163 | pub use self::bar::Baz; | ||
164 | pub struct Foo; | ||
165 | 211 | ||
166 | //- /foo/bar.rs | 212 | crate::foo::bar |
167 | pub struct Baz; | 213 | Baz: t v |
168 | ", | 214 | "### |
169 | ); | ||
170 | check_module_item_map( | ||
171 | &item_map, | ||
172 | module_id, | ||
173 | " | ||
174 | Baz: t v | ||
175 | Foo: t v | ||
176 | bar: t | ||
177 | foo: t | ||
178 | ", | ||
179 | ); | 215 | ); |
180 | } | 216 | } |
181 | 217 | ||
182 | #[test] | 218 | #[test] |
183 | fn glob_2() { | 219 | fn std_prelude() { |
184 | let (item_map, module_id) = item_map( | 220 | covers!(std_prelude); |
185 | " | 221 | let map = def_map_with_crate_graph( |
186 | //- /lib.rs | ||
187 | mod foo; | ||
188 | use foo::*; | ||
189 | <|> | ||
190 | |||
191 | //- /foo/mod.rs | ||
192 | pub mod bar; | ||
193 | pub use self::bar::*; | ||
194 | pub struct Foo; | ||
195 | |||
196 | //- /foo/bar.rs | ||
197 | pub struct Baz; | ||
198 | pub use super::*; | ||
199 | ", | ||
200 | ); | ||
201 | check_module_item_map( | ||
202 | &item_map, | ||
203 | module_id, | ||
204 | " | 222 | " |
205 | Baz: t v | 223 | //- /main.rs |
206 | Foo: t v | 224 | use Foo::*; |
207 | bar: t | ||
208 | foo: t | ||
209 | ", | ||
210 | ); | ||
211 | } | ||
212 | 225 | ||
213 | #[test] | ||
214 | fn glob_enum() { | ||
215 | covers!(glob_enum); | ||
216 | let (item_map, module_id) = item_map( | ||
217 | " | ||
218 | //- /lib.rs | 226 | //- /lib.rs |
219 | enum Foo { | 227 | mod prelude; |
220 | Bar, Baz | 228 | #[prelude_import] |
221 | } | 229 | use prelude::*; |
222 | use self::Foo::*; | 230 | |
223 | <|> | 231 | //- /prelude.rs |
224 | ", | 232 | pub enum Foo { Bar, Baz }; |
225 | ); | ||
226 | check_module_item_map( | ||
227 | &item_map, | ||
228 | module_id, | ||
229 | " | ||
230 | Bar: t v | ||
231 | Baz: t v | ||
232 | Foo: t | ||
233 | ", | 233 | ", |
234 | crate_graph! { | ||
235 | "main": ("/main.rs", ["test_crate"]), | ||
236 | "test_crate": ("/lib.rs", []), | ||
237 | }, | ||
234 | ); | 238 | ); |
239 | assert_snapshot_matches!(map, @r###" | ||
240 | crate | ||
241 | Bar: t v | ||
242 | Baz: t v | ||
243 | "###); | ||
235 | } | 244 | } |
236 | 245 | ||
237 | #[test] | 246 | #[test] |
238 | fn glob_across_crates() { | 247 | fn can_import_enum_variant() { |
239 | covers!(glob_across_crates); | 248 | covers!(can_import_enum_variant); |
240 | let mut db = MockDatabase::with_files( | 249 | let map = def_map( |
241 | " | 250 | " |
242 | //- /main.rs | ||
243 | use test_crate::*; | ||
244 | |||
245 | //- /lib.rs | 251 | //- /lib.rs |
246 | pub struct Baz; | 252 | enum E { V } |
253 | use self::E::V; | ||
247 | ", | 254 | ", |
248 | ); | 255 | ); |
249 | db.set_crate_graph_from_fixture(crate_graph! { | 256 | assert_snapshot_matches!(map, @r###" |
250 | "main": ("/main.rs", ["test_crate"]), | 257 | crate |
251 | "test_crate": ("/lib.rs", []), | 258 | V: t v |
252 | }); | 259 | E: t |
253 | let main_id = db.file_id_of("/main.rs"); | 260 | "### |
254 | |||
255 | let module = crate::source_binder::module_from_file_id(&db, main_id).unwrap(); | ||
256 | let krate = module.krate(&db).unwrap(); | ||
257 | let item_map = db.item_map(krate); | ||
258 | |||
259 | check_module_item_map( | ||
260 | &item_map, | ||
261 | module.module_id, | ||
262 | " | ||
263 | Baz: t v | ||
264 | ", | ||
265 | ); | 261 | ); |
266 | } | 262 | } |
267 | 263 | ||
268 | #[test] | 264 | #[test] |
269 | fn edition_2015_imports() { | 265 | fn edition_2015_imports() { |
270 | let mut db = MockDatabase::with_files( | 266 | let map = def_map_with_crate_graph( |
271 | " | 267 | " |
272 | //- /main.rs | 268 | //- /main.rs |
273 | mod foo; | 269 | mod foo; |
@@ -282,31 +278,32 @@ fn edition_2015_imports() { | |||
282 | 278 | ||
283 | //- /lib.rs | 279 | //- /lib.rs |
284 | struct FromLib; | 280 | struct FromLib; |
285 | ", | ||
286 | ); | ||
287 | db.set_crate_graph_from_fixture(crate_graph! { | ||
288 | "main": ("/main.rs", "2015", ["other_crate"]), | ||
289 | "other_crate": ("/lib.rs", "2018", []), | ||
290 | }); | ||
291 | let foo_id = db.file_id_of("/foo.rs"); | ||
292 | |||
293 | let module = crate::source_binder::module_from_file_id(&db, foo_id).unwrap(); | ||
294 | let krate = module.krate(&db).unwrap(); | ||
295 | let item_map = db.item_map(krate); | ||
296 | |||
297 | check_module_item_map( | ||
298 | &item_map, | ||
299 | module.module_id, | ||
300 | " | ||
301 | Bar: t v | ||
302 | FromLib: t v | ||
303 | ", | 281 | ", |
282 | crate_graph! { | ||
283 | "main": ("/main.rs", "2015", ["other_crate"]), | ||
284 | "other_crate": ("/lib.rs", "2018", []), | ||
285 | }, | ||
286 | ); | ||
287 | |||
288 | assert_snapshot_matches!(map, | ||
289 | @r###" | ||
290 | crate | ||
291 | bar: t | ||
292 | foo: t | ||
293 | |||
294 | crate::bar | ||
295 | Bar: t v | ||
296 | |||
297 | crate::foo | ||
298 | FromLib: t v | ||
299 | Bar: t v | ||
300 | "### | ||
304 | ); | 301 | ); |
305 | } | 302 | } |
306 | 303 | ||
307 | #[test] | 304 | #[test] |
308 | fn module_resolution_works_for_non_standard_filenames() { | 305 | fn module_resolution_works_for_non_standard_filenames() { |
309 | let mut db = MockDatabase::with_files( | 306 | let map = def_map_with_crate_graph( |
310 | " | 307 | " |
311 | //- /my_library.rs | 308 | //- /my_library.rs |
312 | mod foo; | 309 | mod foo; |
@@ -315,73 +312,32 @@ fn module_resolution_works_for_non_standard_filenames() { | |||
315 | //- /foo/mod.rs | 312 | //- /foo/mod.rs |
316 | pub struct Bar; | 313 | pub struct Bar; |
317 | ", | 314 | ", |
315 | crate_graph! { | ||
316 | "my_library": ("/my_library.rs", []), | ||
317 | }, | ||
318 | ); | 318 | ); |
319 | db.set_crate_graph_from_fixture(crate_graph! { | ||
320 | "my_library": ("/my_library.rs", []), | ||
321 | }); | ||
322 | let file_id = db.file_id_of("/my_library.rs"); | ||
323 | |||
324 | let module = crate::source_binder::module_from_file_id(&db, file_id).unwrap(); | ||
325 | let krate = module.krate(&db).unwrap(); | ||
326 | let module_id = module.module_id; | ||
327 | let item_map = db.item_map(krate); | ||
328 | check_module_item_map( | ||
329 | &item_map, | ||
330 | module_id, | ||
331 | " | ||
332 | Bar: t v | ||
333 | foo: t | ||
334 | ", | ||
335 | ); | ||
336 | } | ||
337 | 319 | ||
338 | #[test] | 320 | assert_snapshot_matches!(map, |
339 | fn std_prelude() { | 321 | @r###" |
340 | covers!(std_prelude); | 322 | crate |
341 | let mut db = MockDatabase::with_files( | 323 | Bar: t v |
342 | " | 324 | foo: t |
343 | //- /main.rs | ||
344 | use Foo::*; | ||
345 | |||
346 | //- /lib.rs | ||
347 | mod prelude; | ||
348 | #[prelude_import] | ||
349 | use prelude::*; | ||
350 | 325 | ||
351 | //- /prelude.rs | 326 | crate::foo |
352 | pub enum Foo { Bar, Baz }; | 327 | Bar: t v |
353 | ", | 328 | "### |
354 | ); | ||
355 | db.set_crate_graph_from_fixture(crate_graph! { | ||
356 | "main": ("/main.rs", ["test_crate"]), | ||
357 | "test_crate": ("/lib.rs", []), | ||
358 | }); | ||
359 | let main_id = db.file_id_of("/main.rs"); | ||
360 | |||
361 | let module = crate::source_binder::module_from_file_id(&db, main_id).unwrap(); | ||
362 | let krate = module.krate(&db).unwrap(); | ||
363 | let item_map = db.item_map(krate); | ||
364 | |||
365 | check_module_item_map( | ||
366 | &item_map, | ||
367 | module.module_id, | ||
368 | " | ||
369 | Bar: t v | ||
370 | Baz: t v | ||
371 | ", | ||
372 | ); | 329 | ); |
373 | } | 330 | } |
374 | 331 | ||
375 | #[test] | 332 | #[test] |
376 | fn name_res_works_for_broken_modules() { | 333 | fn name_res_works_for_broken_modules() { |
377 | covers!(name_res_works_for_broken_modules); | 334 | covers!(name_res_works_for_broken_modules); |
378 | let (item_map, module_id) = item_map( | 335 | let map = def_map( |
379 | " | 336 | " |
380 | //- /lib.rs | 337 | //- /lib.rs |
381 | mod foo // no `;`, no body | 338 | mod foo // no `;`, no body |
382 | 339 | ||
383 | use self::foo::Baz; | 340 | use self::foo::Baz; |
384 | <|> | ||
385 | 341 | ||
386 | //- /foo/mod.rs | 342 | //- /foo/mod.rs |
387 | pub mod bar; | 343 | pub mod bar; |
@@ -390,65 +346,47 @@ fn name_res_works_for_broken_modules() { | |||
390 | 346 | ||
391 | //- /foo/bar.rs | 347 | //- /foo/bar.rs |
392 | pub struct Baz; | 348 | pub struct Baz; |
393 | ", | ||
394 | ); | ||
395 | check_module_item_map( | ||
396 | &item_map, | ||
397 | module_id, | ||
398 | " | ||
399 | Baz: _ | ||
400 | ", | 349 | ", |
401 | ); | 350 | ); |
402 | } | 351 | assert_snapshot_matches!(map, |
403 | 352 | @r###" | |
404 | #[test] | 353 | crate |
405 | fn item_map_using_self() { | 354 | Baz: _ |
406 | let (item_map, module_id) = item_map( | 355 | "### |
407 | " | ||
408 | //- /lib.rs | ||
409 | mod foo; | ||
410 | use crate::foo::bar::Baz::{self}; | ||
411 | <|> | ||
412 | //- /foo/mod.rs | ||
413 | pub mod bar; | ||
414 | //- /foo/bar.rs | ||
415 | pub struct Baz; | ||
416 | ", | ||
417 | ); | ||
418 | check_module_item_map( | ||
419 | &item_map, | ||
420 | module_id, | ||
421 | " | ||
422 | Baz: t v | ||
423 | foo: t | ||
424 | ", | ||
425 | ); | 356 | ); |
426 | } | 357 | } |
427 | 358 | ||
428 | #[test] | 359 | #[test] |
429 | fn item_map_enum_importing() { | 360 | fn item_map_using_self() { |
430 | covers!(item_map_enum_importing); | 361 | let map = def_map( |
431 | let (item_map, module_id) = item_map( | ||
432 | " | 362 | " |
433 | //- /lib.rs | 363 | //- /lib.rs |
434 | enum E { V } | 364 | mod foo; |
435 | use self::E::V; | 365 | use crate::foo::bar::Baz::{self}; |
436 | <|> | 366 | //- /foo/mod.rs |
367 | pub mod bar; | ||
368 | //- /foo/bar.rs | ||
369 | pub struct Baz; | ||
437 | ", | 370 | ", |
438 | ); | 371 | ); |
439 | check_module_item_map( | 372 | assert_snapshot_matches!(map, |
440 | &item_map, | 373 | @r###" |
441 | module_id, | 374 | crate |
442 | " | 375 | Baz: t v |
443 | E: t | 376 | foo: t |
444 | V: t v | 377 | |
445 | ", | 378 | crate::foo |
379 | bar: t | ||
380 | |||
381 | crate::foo::bar | ||
382 | Baz: t v | ||
383 | "### | ||
446 | ); | 384 | ); |
447 | } | 385 | } |
448 | 386 | ||
449 | #[test] | 387 | #[test] |
450 | fn item_map_across_crates() { | 388 | fn item_map_across_crates() { |
451 | let mut db = MockDatabase::with_files( | 389 | let map = def_map_with_crate_graph( |
452 | " | 390 | " |
453 | //- /main.rs | 391 | //- /main.rs |
454 | use test_crate::Baz; | 392 | use test_crate::Baz; |
@@ -456,29 +394,23 @@ fn item_map_across_crates() { | |||
456 | //- /lib.rs | 394 | //- /lib.rs |
457 | pub struct Baz; | 395 | pub struct Baz; |
458 | ", | 396 | ", |
397 | crate_graph! { | ||
398 | "main": ("/main.rs", ["test_crate"]), | ||
399 | "test_crate": ("/lib.rs", []), | ||
400 | }, | ||
459 | ); | 401 | ); |
460 | db.set_crate_graph_from_fixture(crate_graph! { | 402 | |
461 | "main": ("/main.rs", ["test_crate"]), | 403 | assert_snapshot_matches!(map, |
462 | "test_crate": ("/lib.rs", []), | 404 | @r###" |
463 | }); | 405 | crate |
464 | let main_id = db.file_id_of("/main.rs"); | 406 | Baz: t v |
465 | 407 | "### | |
466 | let module = crate::source_binder::module_from_file_id(&db, main_id).unwrap(); | ||
467 | let krate = module.krate(&db).unwrap(); | ||
468 | let item_map = db.item_map(krate); | ||
469 | |||
470 | check_module_item_map( | ||
471 | &item_map, | ||
472 | module.module_id, | ||
473 | " | ||
474 | Baz: t v | ||
475 | ", | ||
476 | ); | 408 | ); |
477 | } | 409 | } |
478 | 410 | ||
479 | #[test] | 411 | #[test] |
480 | fn extern_crate_rename() { | 412 | fn extern_crate_rename() { |
481 | let mut db = MockDatabase::with_files( | 413 | let map = def_map_with_crate_graph( |
482 | " | 414 | " |
483 | //- /main.rs | 415 | //- /main.rs |
484 | extern crate alloc as alloc_crate; | 416 | extern crate alloc as alloc_crate; |
@@ -492,29 +424,23 @@ fn extern_crate_rename() { | |||
492 | //- /lib.rs | 424 | //- /lib.rs |
493 | struct Arc; | 425 | struct Arc; |
494 | ", | 426 | ", |
427 | crate_graph! { | ||
428 | "main": ("/main.rs", ["alloc"]), | ||
429 | "alloc": ("/lib.rs", []), | ||
430 | }, | ||
495 | ); | 431 | ); |
496 | db.set_crate_graph_from_fixture(crate_graph! { | 432 | |
497 | "main": ("/main.rs", ["alloc"]), | 433 | assert_snapshot_matches!(map, |
498 | "alloc": ("/lib.rs", []), | 434 | @r###" |
499 | }); | 435 | crate |
500 | let sync_id = db.file_id_of("/sync.rs"); | 436 | Arc: t v |
501 | 437 | "### | |
502 | let module = crate::source_binder::module_from_file_id(&db, sync_id).unwrap(); | ||
503 | let krate = module.krate(&db).unwrap(); | ||
504 | let item_map = db.item_map(krate); | ||
505 | |||
506 | check_module_item_map( | ||
507 | &item_map, | ||
508 | module.module_id, | ||
509 | " | ||
510 | Arc: t v | ||
511 | ", | ||
512 | ); | 438 | ); |
513 | } | 439 | } |
514 | 440 | ||
515 | #[test] | 441 | #[test] |
516 | fn extern_crate_rename_2015_edition() { | 442 | fn extern_crate_rename_2015_edition() { |
517 | let mut db = MockDatabase::with_files( | 443 | let map = def_map_with_crate_graph( |
518 | " | 444 | " |
519 | //- /main.rs | 445 | //- /main.rs |
520 | extern crate alloc as alloc_crate; | 446 | extern crate alloc as alloc_crate; |
@@ -528,29 +454,23 @@ fn extern_crate_rename_2015_edition() { | |||
528 | //- /lib.rs | 454 | //- /lib.rs |
529 | struct Arc; | 455 | struct Arc; |
530 | ", | 456 | ", |
457 | crate_graph! { | ||
458 | "main": ("/main.rs", "2015", ["alloc"]), | ||
459 | "alloc": ("/lib.rs", []), | ||
460 | }, | ||
531 | ); | 461 | ); |
532 | db.set_crate_graph_from_fixture(crate_graph! { | 462 | |
533 | "main": ("/main.rs", "2015", ["alloc"]), | 463 | assert_snapshot_matches!(map, |
534 | "alloc": ("/lib.rs", []), | 464 | @r###" |
535 | }); | 465 | crate |
536 | let sync_id = db.file_id_of("/sync.rs"); | 466 | Arc: t v |
537 | 467 | "### | |
538 | let module = crate::source_binder::module_from_file_id(&db, sync_id).unwrap(); | ||
539 | let krate = module.krate(&db).unwrap(); | ||
540 | let item_map = db.item_map(krate); | ||
541 | |||
542 | check_module_item_map( | ||
543 | &item_map, | ||
544 | module.module_id, | ||
545 | " | ||
546 | Arc: t v | ||
547 | ", | ||
548 | ); | 468 | ); |
549 | } | 469 | } |
550 | 470 | ||
551 | #[test] | 471 | #[test] |
552 | fn import_across_source_roots() { | 472 | fn import_across_source_roots() { |
553 | let mut db = MockDatabase::with_files( | 473 | let map = def_map_with_crate_graph( |
554 | " | 474 | " |
555 | //- /lib.rs | 475 | //- /lib.rs |
556 | pub mod a { | 476 | pub mod a { |
@@ -564,29 +484,23 @@ fn import_across_source_roots() { | |||
564 | //- /main/main.rs | 484 | //- /main/main.rs |
565 | use test_crate::a::b::C; | 485 | use test_crate::a::b::C; |
566 | ", | 486 | ", |
487 | crate_graph! { | ||
488 | "main": ("/main/main.rs", ["test_crate"]), | ||
489 | "test_crate": ("/lib.rs", []), | ||
490 | }, | ||
567 | ); | 491 | ); |
568 | db.set_crate_graph_from_fixture(crate_graph! { | 492 | |
569 | "main": ("/main/main.rs", ["test_crate"]), | 493 | assert_snapshot_matches!(map, |
570 | "test_crate": ("/lib.rs", []), | 494 | @r###" |
571 | }); | 495 | crate |
572 | let main_id = db.file_id_of("/main/main.rs"); | 496 | C: t v |
573 | 497 | "### | |
574 | let module = crate::source_binder::module_from_file_id(&db, main_id).unwrap(); | ||
575 | let krate = module.krate(&db).unwrap(); | ||
576 | let item_map = db.item_map(krate); | ||
577 | |||
578 | check_module_item_map( | ||
579 | &item_map, | ||
580 | module.module_id, | ||
581 | " | ||
582 | C: t v | ||
583 | ", | ||
584 | ); | 498 | ); |
585 | } | 499 | } |
586 | 500 | ||
587 | #[test] | 501 | #[test] |
588 | fn reexport_across_crates() { | 502 | fn reexport_across_crates() { |
589 | let mut db = MockDatabase::with_files( | 503 | let map = def_map_with_crate_graph( |
590 | " | 504 | " |
591 | //- /main.rs | 505 | //- /main.rs |
592 | use test_crate::Baz; | 506 | use test_crate::Baz; |
@@ -599,29 +513,23 @@ fn reexport_across_crates() { | |||
599 | //- /foo.rs | 513 | //- /foo.rs |
600 | pub struct Baz; | 514 | pub struct Baz; |
601 | ", | 515 | ", |
516 | crate_graph! { | ||
517 | "main": ("/main.rs", ["test_crate"]), | ||
518 | "test_crate": ("/lib.rs", []), | ||
519 | }, | ||
602 | ); | 520 | ); |
603 | db.set_crate_graph_from_fixture(crate_graph! { | 521 | |
604 | "main": ("/main.rs", ["test_crate"]), | 522 | assert_snapshot_matches!(map, |
605 | "test_crate": ("/lib.rs", []), | 523 | @r###" |
606 | }); | 524 | crate |
607 | let main_id = db.file_id_of("/main.rs"); | 525 | Baz: t v |
608 | 526 | "### | |
609 | let module = crate::source_binder::module_from_file_id(&db, main_id).unwrap(); | ||
610 | let krate = module.krate(&db).unwrap(); | ||
611 | let item_map = db.item_map(krate); | ||
612 | |||
613 | check_module_item_map( | ||
614 | &item_map, | ||
615 | module.module_id, | ||
616 | " | ||
617 | Baz: t v | ||
618 | ", | ||
619 | ); | 527 | ); |
620 | } | 528 | } |
621 | 529 | ||
622 | #[test] | 530 | #[test] |
623 | fn values_dont_shadow_extern_crates() { | 531 | fn values_dont_shadow_extern_crates() { |
624 | let mut db = MockDatabase::with_files( | 532 | let map = def_map_with_crate_graph( |
625 | " | 533 | " |
626 | //- /main.rs | 534 | //- /main.rs |
627 | fn foo() {} | 535 | fn foo() {} |
@@ -630,139 +538,17 @@ fn values_dont_shadow_extern_crates() { | |||
630 | //- /foo/lib.rs | 538 | //- /foo/lib.rs |
631 | pub struct Bar; | 539 | pub struct Bar; |
632 | ", | 540 | ", |
541 | crate_graph! { | ||
542 | "main": ("/main.rs", ["foo"]), | ||
543 | "foo": ("/foo/lib.rs", []), | ||
544 | }, | ||
633 | ); | 545 | ); |
634 | db.set_crate_graph_from_fixture(crate_graph! { | ||
635 | "main": ("/main.rs", ["foo"]), | ||
636 | "foo": ("/foo/lib.rs", []), | ||
637 | }); | ||
638 | let main_id = db.file_id_of("/main.rs"); | ||
639 | |||
640 | let module = crate::source_binder::module_from_file_id(&db, main_id).unwrap(); | ||
641 | let krate = module.krate(&db).unwrap(); | ||
642 | let item_map = db.item_map(krate); | ||
643 | |||
644 | check_module_item_map( | ||
645 | &item_map, | ||
646 | module.module_id, | ||
647 | " | ||
648 | Bar: t v | ||
649 | foo: v | ||
650 | ", | ||
651 | ); | ||
652 | } | ||
653 | |||
654 | fn check_item_map_is_not_recomputed(initial: &str, file_change: &str) { | ||
655 | let (mut db, pos) = MockDatabase::with_position(initial); | ||
656 | let module = crate::source_binder::module_from_file_id(&db, pos.file_id).unwrap(); | ||
657 | let krate = module.krate(&db).unwrap(); | ||
658 | { | ||
659 | let events = db.log_executed(|| { | ||
660 | db.item_map(krate); | ||
661 | }); | ||
662 | assert!(format!("{:?}", events).contains("item_map")) | ||
663 | } | ||
664 | db.set_file_text(pos.file_id, Arc::new(file_change.to_string())); | ||
665 | |||
666 | { | ||
667 | let events = db.log_executed(|| { | ||
668 | db.item_map(krate); | ||
669 | }); | ||
670 | assert!(!format!("{:?}", events).contains("item_map"), "{:#?}", events) | ||
671 | } | ||
672 | } | ||
673 | |||
674 | #[test] | ||
675 | fn typing_inside_a_function_should_not_invalidate_item_map() { | ||
676 | check_item_map_is_not_recomputed( | ||
677 | " | ||
678 | //- /lib.rs | ||
679 | mod foo;<|> | ||
680 | |||
681 | use crate::foo::bar::Baz; | ||
682 | |||
683 | fn foo() -> i32 { | ||
684 | 1 + 1 | ||
685 | } | ||
686 | //- /foo/mod.rs | ||
687 | pub mod bar; | ||
688 | |||
689 | //- /foo/bar.rs | ||
690 | pub struct Baz; | ||
691 | ", | ||
692 | " | ||
693 | mod foo; | ||
694 | |||
695 | use crate::foo::bar::Baz; | ||
696 | |||
697 | fn foo() -> i32 { 92 } | ||
698 | ", | ||
699 | ); | ||
700 | } | ||
701 | |||
702 | #[test] | ||
703 | fn adding_inner_items_should_not_invalidate_item_map() { | ||
704 | check_item_map_is_not_recomputed( | ||
705 | " | ||
706 | //- /lib.rs | ||
707 | struct S { a: i32} | ||
708 | enum E { A } | ||
709 | trait T { | ||
710 | fn a() {} | ||
711 | } | ||
712 | mod foo;<|> | ||
713 | impl S { | ||
714 | fn a() {} | ||
715 | } | ||
716 | use crate::foo::bar::Baz; | ||
717 | //- /foo/mod.rs | ||
718 | pub mod bar; | ||
719 | |||
720 | //- /foo/bar.rs | ||
721 | pub struct Baz; | ||
722 | ", | ||
723 | " | ||
724 | struct S { a: i32, b: () } | ||
725 | enum E { A, B } | ||
726 | trait T { | ||
727 | fn a() {} | ||
728 | fn b() {} | ||
729 | } | ||
730 | mod foo;<|> | ||
731 | impl S { | ||
732 | fn a() {} | ||
733 | fn b() {} | ||
734 | } | ||
735 | use crate::foo::bar::Baz; | ||
736 | ", | ||
737 | ); | ||
738 | } | ||
739 | 546 | ||
740 | #[test] | 547 | assert_snapshot_matches!(map, |
741 | fn typing_inside_a_function_inside_a_macro_should_not_invalidate_item_map() { | 548 | @r###" |
742 | check_item_map_is_not_recomputed( | 549 | crate |
743 | " | 550 | Bar: t v |
744 | //- /lib.rs | 551 | foo: v |
745 | mod foo; | 552 | "### |
746 | |||
747 | use crate::foo::bar::Baz; | ||
748 | |||
749 | //- /foo/mod.rs | ||
750 | pub mod bar; | ||
751 | |||
752 | //- /foo/bar.rs | ||
753 | <|> | ||
754 | salsa::query_group! { | ||
755 | trait Baz { | ||
756 | fn foo() -> i32 { 1 + 1 } | ||
757 | } | ||
758 | } | ||
759 | ", | ||
760 | " | ||
761 | salsa::query_group! { | ||
762 | trait Baz { | ||
763 | fn foo() -> i32 { 92 } | ||
764 | } | ||
765 | } | ||
766 | ", | ||
767 | ); | 553 | ); |
768 | } | 554 | } |
diff --git a/crates/ra_hir/src/nameres/tests/globs.rs b/crates/ra_hir/src/nameres/tests/globs.rs new file mode 100644 index 000000000..6e50c7ff6 --- /dev/null +++ b/crates/ra_hir/src/nameres/tests/globs.rs | |||
@@ -0,0 +1,118 @@ | |||
1 | use super::*; | ||
2 | |||
3 | #[test] | ||
4 | fn glob_1() { | ||
5 | let map = def_map( | ||
6 | " | ||
7 | //- /lib.rs | ||
8 | mod foo; | ||
9 | use foo::*; | ||
10 | |||
11 | //- /foo/mod.rs | ||
12 | pub mod bar; | ||
13 | pub use self::bar::Baz; | ||
14 | pub struct Foo; | ||
15 | |||
16 | //- /foo/bar.rs | ||
17 | pub struct Baz; | ||
18 | ", | ||
19 | ); | ||
20 | assert_snapshot_matches!(map, @r###" | ||
21 | crate | ||
22 | bar: t | ||
23 | Foo: t v | ||
24 | Baz: t v | ||
25 | foo: t | ||
26 | |||
27 | crate::foo | ||
28 | bar: t | ||
29 | Foo: t v | ||
30 | Baz: t v | ||
31 | |||
32 | crate::foo::bar | ||
33 | Baz: t v | ||
34 | "### | ||
35 | ); | ||
36 | } | ||
37 | |||
38 | #[test] | ||
39 | fn glob_2() { | ||
40 | let map = def_map( | ||
41 | " | ||
42 | //- /lib.rs | ||
43 | mod foo; | ||
44 | use foo::*; | ||
45 | |||
46 | //- /foo/mod.rs | ||
47 | pub mod bar; | ||
48 | pub use self::bar::*; | ||
49 | pub struct Foo; | ||
50 | |||
51 | //- /foo/bar.rs | ||
52 | pub struct Baz; | ||
53 | pub use super::*; | ||
54 | ", | ||
55 | ); | ||
56 | assert_snapshot_matches!(map, @r###" | ||
57 | crate | ||
58 | bar: t | ||
59 | Foo: t v | ||
60 | Baz: t v | ||
61 | foo: t | ||
62 | |||
63 | crate::foo | ||
64 | bar: t | ||
65 | Foo: t v | ||
66 | Baz: t v | ||
67 | |||
68 | crate::foo::bar | ||
69 | bar: t | ||
70 | Foo: t v | ||
71 | Baz: t v | ||
72 | "### | ||
73 | ); | ||
74 | } | ||
75 | |||
76 | #[test] | ||
77 | fn glob_across_crates() { | ||
78 | covers!(glob_across_crates); | ||
79 | let map = def_map_with_crate_graph( | ||
80 | " | ||
81 | //- /main.rs | ||
82 | use test_crate::*; | ||
83 | |||
84 | //- /lib.rs | ||
85 | pub struct Baz; | ||
86 | ", | ||
87 | crate_graph! { | ||
88 | "main": ("/main.rs", ["test_crate"]), | ||
89 | "test_crate": ("/lib.rs", []), | ||
90 | }, | ||
91 | ); | ||
92 | assert_snapshot_matches!(map, @r###" | ||
93 | crate | ||
94 | Baz: t v | ||
95 | "### | ||
96 | ); | ||
97 | } | ||
98 | |||
99 | #[test] | ||
100 | fn glob_enum() { | ||
101 | covers!(glob_enum); | ||
102 | let map = def_map( | ||
103 | " | ||
104 | //- /lib.rs | ||
105 | enum Foo { | ||
106 | Bar, Baz | ||
107 | } | ||
108 | use self::Foo::*; | ||
109 | ", | ||
110 | ); | ||
111 | assert_snapshot_matches!(map, @r###" | ||
112 | crate | ||
113 | Foo: t | ||
114 | Bar: t v | ||
115 | Baz: t v | ||
116 | "### | ||
117 | ); | ||
118 | } | ||
diff --git a/crates/ra_hir/src/nameres/tests/incremental.rs b/crates/ra_hir/src/nameres/tests/incremental.rs new file mode 100644 index 000000000..698781923 --- /dev/null +++ b/crates/ra_hir/src/nameres/tests/incremental.rs | |||
@@ -0,0 +1,123 @@ | |||
1 | use super::*; | ||
2 | |||
3 | use std::sync::Arc; | ||
4 | |||
5 | use ra_db::SourceDatabase; | ||
6 | |||
7 | fn check_def_map_is_not_recomputed(initial: &str, file_change: &str) { | ||
8 | let (mut db, pos) = MockDatabase::with_position(initial); | ||
9 | let crate_id = db.crate_graph().iter().next().unwrap(); | ||
10 | let krate = Crate { crate_id }; | ||
11 | { | ||
12 | let events = db.log_executed(|| { | ||
13 | db.crate_def_map(krate); | ||
14 | }); | ||
15 | assert!(format!("{:?}", events).contains("crate_def_map"), "{:#?}", events) | ||
16 | } | ||
17 | db.set_file_text(pos.file_id, Arc::new(file_change.to_string())); | ||
18 | |||
19 | { | ||
20 | let events = db.log_executed(|| { | ||
21 | db.crate_def_map(krate); | ||
22 | }); | ||
23 | assert!(!format!("{:?}", events).contains("crate_def_map"), "{:#?}", events) | ||
24 | } | ||
25 | } | ||
26 | |||
27 | #[test] | ||
28 | fn typing_inside_a_function_should_not_invalidate_def_map() { | ||
29 | check_def_map_is_not_recomputed( | ||
30 | " | ||
31 | //- /lib.rs | ||
32 | mod foo;<|> | ||
33 | |||
34 | use crate::foo::bar::Baz; | ||
35 | |||
36 | fn foo() -> i32 { | ||
37 | 1 + 1 | ||
38 | } | ||
39 | //- /foo/mod.rs | ||
40 | pub mod bar; | ||
41 | |||
42 | //- /foo/bar.rs | ||
43 | pub struct Baz; | ||
44 | ", | ||
45 | " | ||
46 | mod foo; | ||
47 | |||
48 | use crate::foo::bar::Baz; | ||
49 | |||
50 | fn foo() -> i32 { 92 } | ||
51 | ", | ||
52 | ); | ||
53 | } | ||
54 | |||
55 | #[test] | ||
56 | fn adding_inner_items_should_not_invalidate_def_map() { | ||
57 | check_def_map_is_not_recomputed( | ||
58 | " | ||
59 | //- /lib.rs | ||
60 | struct S { a: i32} | ||
61 | enum E { A } | ||
62 | trait T { | ||
63 | fn a() {} | ||
64 | } | ||
65 | mod foo;<|> | ||
66 | impl S { | ||
67 | fn a() {} | ||
68 | } | ||
69 | use crate::foo::bar::Baz; | ||
70 | //- /foo/mod.rs | ||
71 | pub mod bar; | ||
72 | |||
73 | //- /foo/bar.rs | ||
74 | pub struct Baz; | ||
75 | ", | ||
76 | " | ||
77 | struct S { a: i32, b: () } | ||
78 | enum E { A, B } | ||
79 | trait T { | ||
80 | fn a() {} | ||
81 | fn b() {} | ||
82 | } | ||
83 | mod foo;<|> | ||
84 | impl S { | ||
85 | fn a() {} | ||
86 | fn b() {} | ||
87 | } | ||
88 | use crate::foo::bar::Baz; | ||
89 | ", | ||
90 | ); | ||
91 | } | ||
92 | |||
93 | // It would be awesome to make this work, but it's unclear how | ||
94 | #[test] | ||
95 | #[ignore] | ||
96 | fn typing_inside_a_function_inside_a_macro_should_not_invalidate_def_map() { | ||
97 | check_def_map_is_not_recomputed( | ||
98 | " | ||
99 | //- /lib.rs | ||
100 | mod foo; | ||
101 | |||
102 | use crate::foo::bar::Baz; | ||
103 | |||
104 | //- /foo/mod.rs | ||
105 | pub mod bar; | ||
106 | |||
107 | //- /foo/bar.rs | ||
108 | <|> | ||
109 | salsa::query_group! { | ||
110 | trait Baz { | ||
111 | fn foo() -> i32 { 1 + 1 } | ||
112 | } | ||
113 | } | ||
114 | ", | ||
115 | " | ||
116 | salsa::query_group! { | ||
117 | trait Baz { | ||
118 | fn foo() -> i32 { 92 } | ||
119 | } | ||
120 | } | ||
121 | ", | ||
122 | ); | ||
123 | } | ||
diff --git a/crates/ra_hir/src/nameres/tests/macros.rs b/crates/ra_hir/src/nameres/tests/macros.rs new file mode 100644 index 000000000..8781b026b --- /dev/null +++ b/crates/ra_hir/src/nameres/tests/macros.rs | |||
@@ -0,0 +1,94 @@ | |||
1 | use super::*; | ||
2 | |||
3 | #[test] | ||
4 | fn macro_rules_are_globally_visible() { | ||
5 | let map = def_map( | ||
6 | " | ||
7 | //- /lib.rs | ||
8 | macro_rules! structs { | ||
9 | ($($i:ident),*) => { | ||
10 | $(struct $i { field: u32 } )* | ||
11 | } | ||
12 | } | ||
13 | structs!(Foo); | ||
14 | mod nested; | ||
15 | |||
16 | //- /nested.rs | ||
17 | structs!(Bar, Baz); | ||
18 | ", | ||
19 | ); | ||
20 | assert_snapshot_matches!(map, @r###" | ||
21 | crate | ||
22 | nested: t | ||
23 | Foo: t v | ||
24 | |||
25 | crate::nested | ||
26 | Bar: t v | ||
27 | Baz: t v | ||
28 | "###); | ||
29 | } | ||
30 | |||
31 | #[test] | ||
32 | fn macro_rules_can_define_modules() { | ||
33 | let map = def_map( | ||
34 | " | ||
35 | //- /lib.rs | ||
36 | macro_rules! m { | ||
37 | ($name:ident) => { mod $name; } | ||
38 | } | ||
39 | m!(n1); | ||
40 | |||
41 | //- /n1.rs | ||
42 | m!(n2) | ||
43 | //- /n1/n2.rs | ||
44 | struct X; | ||
45 | ", | ||
46 | ); | ||
47 | assert_snapshot_matches!(map, @r###" | ||
48 | crate | ||
49 | n1: t | ||
50 | |||
51 | crate::n1 | ||
52 | n2: t | ||
53 | |||
54 | crate::n1::n2 | ||
55 | X: t v | ||
56 | "###); | ||
57 | } | ||
58 | |||
59 | #[test] | ||
60 | fn macro_rules_from_other_crates_are_visible() { | ||
61 | let map = def_map_with_crate_graph( | ||
62 | " | ||
63 | //- /main.rs | ||
64 | foo::structs!(Foo, Bar) | ||
65 | mod bar; | ||
66 | |||
67 | //- /bar.rs | ||
68 | use crate::*; | ||
69 | |||
70 | //- /lib.rs | ||
71 | #[macro_export] | ||
72 | macro_rules! structs { | ||
73 | ($($i:ident),*) => { | ||
74 | $(struct $i { field: u32 } )* | ||
75 | } | ||
76 | } | ||
77 | ", | ||
78 | crate_graph! { | ||
79 | "main": ("/main.rs", ["foo"]), | ||
80 | "foo": ("/lib.rs", []), | ||
81 | }, | ||
82 | ); | ||
83 | assert_snapshot_matches!(map, @r###" | ||
84 | crate | ||
85 | bar: t | ||
86 | Foo: t v | ||
87 | Bar: t v | ||
88 | |||
89 | crate::bar | ||
90 | bar: t | ||
91 | Foo: t v | ||
92 | Bar: t v | ||
93 | "###); | ||
94 | } | ||
diff --git a/crates/ra_hir/src/resolve.rs b/crates/ra_hir/src/resolve.rs index 57e7d0b9a..59af4ec60 100644 --- a/crates/ra_hir/src/resolve.rs +++ b/crates/ra_hir/src/resolve.rs | |||
@@ -4,10 +4,10 @@ use std::sync::Arc; | |||
4 | use rustc_hash::FxHashMap; | 4 | use rustc_hash::FxHashMap; |
5 | 5 | ||
6 | use crate::{ | 6 | use crate::{ |
7 | ModuleDef, Module, | 7 | ModuleDef, |
8 | db::HirDatabase, | 8 | db::HirDatabase, |
9 | name::{Name, KnownName}, | 9 | name::{Name, KnownName}, |
10 | nameres::{PerNs, ItemMap}, | 10 | nameres::{PerNs, CrateDefMap, CrateModuleId}, |
11 | generics::GenericParams, | 11 | generics::GenericParams, |
12 | expr::{scope::{ExprScopes, ScopeId}, PatId, Body}, | 12 | expr::{scope::{ExprScopes, ScopeId}, PatId, Body}, |
13 | impl_block::ImplBlock, | 13 | impl_block::ImplBlock, |
@@ -22,8 +22,8 @@ pub struct Resolver { | |||
22 | // TODO how to store these best | 22 | // TODO how to store these best |
23 | #[derive(Debug, Clone)] | 23 | #[derive(Debug, Clone)] |
24 | pub(crate) struct ModuleItemMap { | 24 | pub(crate) struct ModuleItemMap { |
25 | item_map: Arc<ItemMap>, | 25 | crate_def_map: Arc<CrateDefMap>, |
26 | module: Module, | 26 | module_id: CrateModuleId, |
27 | } | 27 | } |
28 | 28 | ||
29 | #[derive(Debug, Clone)] | 29 | #[derive(Debug, Clone)] |
@@ -175,9 +175,9 @@ impl Resolver { | |||
175 | names | 175 | names |
176 | } | 176 | } |
177 | 177 | ||
178 | fn module(&self) -> Option<(&ItemMap, Module)> { | 178 | fn module(&self) -> Option<(&CrateDefMap, CrateModuleId)> { |
179 | self.scopes.iter().rev().find_map(|scope| match scope { | 179 | self.scopes.iter().rev().find_map(|scope| match scope { |
180 | Scope::ModuleScope(m) => Some((&*m.item_map, m.module.clone())), | 180 | Scope::ModuleScope(m) => Some((&*m.crate_def_map, m.module_id)), |
181 | 181 | ||
182 | _ => None, | 182 | _ => None, |
183 | }) | 183 | }) |
@@ -206,8 +206,12 @@ impl Resolver { | |||
206 | self.push_scope(Scope::ImplBlockScope(impl_block)) | 206 | self.push_scope(Scope::ImplBlockScope(impl_block)) |
207 | } | 207 | } |
208 | 208 | ||
209 | pub(crate) fn push_module_scope(self, item_map: Arc<ItemMap>, module: Module) -> Resolver { | 209 | pub(crate) fn push_module_scope( |
210 | self.push_scope(Scope::ModuleScope(ModuleItemMap { item_map, module })) | 210 | self, |
211 | crate_def_map: Arc<CrateDefMap>, | ||
212 | module_id: CrateModuleId, | ||
213 | ) -> Resolver { | ||
214 | self.push_scope(Scope::ModuleScope(ModuleItemMap { crate_def_map, module_id })) | ||
211 | } | 215 | } |
212 | 216 | ||
213 | pub(crate) fn push_expr_scope( | 217 | pub(crate) fn push_expr_scope( |
@@ -224,9 +228,11 @@ impl Scope { | |||
224 | match self { | 228 | match self { |
225 | Scope::ModuleScope(m) => { | 229 | Scope::ModuleScope(m) => { |
226 | if let Some(KnownName::SelfParam) = name.as_known_name() { | 230 | if let Some(KnownName::SelfParam) = name.as_known_name() { |
227 | PerNs::types(Resolution::Def(m.module.into())) | 231 | PerNs::types(Resolution::Def(m.crate_def_map.mk_module(m.module_id).into())) |
228 | } else { | 232 | } else { |
229 | m.item_map.resolve_name_in_module(db, m.module, name).map(Resolution::Def) | 233 | m.crate_def_map |
234 | .resolve_name_in_module(db, m.module_id, name) | ||
235 | .map(Resolution::Def) | ||
230 | } | 236 | } |
231 | } | 237 | } |
232 | Scope::GenericParams(gp) => match gp.find_by_name(name) { | 238 | Scope::GenericParams(gp) => match gp.find_by_name(name) { |
@@ -261,15 +267,15 @@ impl Scope { | |||
261 | // def: m.module.into(), | 267 | // def: m.module.into(), |
262 | // }), | 268 | // }), |
263 | // ); | 269 | // ); |
264 | m.item_map[m.module.module_id].entries().for_each(|(name, res)| { | 270 | m.crate_def_map[m.module_id].scope.entries().for_each(|(name, res)| { |
265 | f(name.clone(), res.def.map(Resolution::Def)); | 271 | f(name.clone(), res.def.map(Resolution::Def)); |
266 | }); | 272 | }); |
267 | m.item_map.extern_prelude.iter().for_each(|(name, def)| { | 273 | m.crate_def_map.extern_prelude().iter().for_each(|(name, def)| { |
268 | f(name.clone(), PerNs::types(Resolution::Def(*def))); | 274 | f(name.clone(), PerNs::types(Resolution::Def(*def))); |
269 | }); | 275 | }); |
270 | if let Some(prelude) = m.item_map.prelude { | 276 | if let Some(prelude) = m.crate_def_map.prelude() { |
271 | let prelude_item_map = db.item_map(prelude.krate); | 277 | let prelude_def_map = db.crate_def_map(prelude.krate); |
272 | prelude_item_map[prelude.module_id].entries().for_each(|(name, res)| { | 278 | prelude_def_map[prelude.module_id].scope.entries().for_each(|(name, res)| { |
273 | f(name.clone(), res.def.map(Resolution::Def)); | 279 | f(name.clone(), res.def.map(Resolution::Def)); |
274 | }); | 280 | }); |
275 | } | 281 | } |
diff --git a/crates/ra_hir/src/source_binder.rs b/crates/ra_hir/src/source_binder.rs index 4a9921a85..902110913 100644 --- a/crates/ra_hir/src/source_binder.rs +++ b/crates/ra_hir/src/source_binder.rs | |||
@@ -7,13 +7,13 @@ | |||
7 | /// purely for "IDE needs". | 7 | /// purely for "IDE needs". |
8 | use ra_db::{FileId, FilePosition}; | 8 | use ra_db::{FileId, FilePosition}; |
9 | use ra_syntax::{ | 9 | use ra_syntax::{ |
10 | SmolStr, TextRange, SyntaxNode, | 10 | SyntaxNode, |
11 | ast::{self, AstNode, NameOwner}, | 11 | ast::{self, AstNode, NameOwner}, |
12 | algo::{find_node_at_offset, find_leaf_at_offset}, | 12 | algo::{find_node_at_offset, find_leaf_at_offset}, |
13 | }; | 13 | }; |
14 | 14 | ||
15 | use crate::{ | 15 | use crate::{ |
16 | HirDatabase, Function, ModuleDef, Struct, Enum, | 16 | HirDatabase, Function, Struct, Enum, |
17 | AsName, Module, HirFileId, Crate, Trait, Resolver, | 17 | AsName, Module, HirFileId, Crate, Trait, Resolver, |
18 | ids::{LocationCtx, SourceFileItemId}, | 18 | ids::{LocationCtx, SourceFileItemId}, |
19 | expr | 19 | expr |
@@ -80,8 +80,8 @@ fn module_from_source( | |||
80 | let source_root_id = db.file_source_root(file_id.as_original_file()); | 80 | let source_root_id = db.file_source_root(file_id.as_original_file()); |
81 | db.source_root_crates(source_root_id).iter().map(|&crate_id| Crate { crate_id }).find_map( | 81 | db.source_root_crates(source_root_id).iter().map(|&crate_id| Crate { crate_id }).find_map( |
82 | |krate| { | 82 | |krate| { |
83 | let module_tree = db.module_tree(krate); | 83 | let def_map = db.crate_def_map(krate); |
84 | let module_id = module_tree.find_module_by_source(file_id, decl_id)?; | 84 | let module_id = def_map.find_module_by_source(file_id, decl_id)?; |
85 | Some(Module { krate, module_id }) | 85 | Some(Module { krate, module_id }) |
86 | }, | 86 | }, |
87 | ) | 87 | ) |
@@ -152,44 +152,6 @@ pub fn trait_from_module( | |||
152 | Trait { id: ctx.to_def(trait_def) } | 152 | Trait { id: ctx.to_def(trait_def) } |
153 | } | 153 | } |
154 | 154 | ||
155 | pub fn macro_symbols(db: &impl HirDatabase, file_id: FileId) -> Vec<(SmolStr, TextRange)> { | ||
156 | let module = match module_from_file_id(db, file_id) { | ||
157 | Some(it) => it, | ||
158 | None => return Vec::new(), | ||
159 | }; | ||
160 | let items = db.lower_module(module); | ||
161 | let mut res = Vec::new(); | ||
162 | |||
163 | for macro_call_id in items | ||
164 | .declarations | ||
165 | .iter() | ||
166 | .filter_map(|(_, it)| it.clone().take_types()) | ||
167 | .filter_map(|it| match it { | ||
168 | ModuleDef::Trait(it) => Some(it), | ||
169 | _ => None, | ||
170 | }) | ||
171 | .filter_map(|it| it.source(db).0.as_macro_call_id()) | ||
172 | { | ||
173 | if let Some(exp) = db.expand_macro_invocation(macro_call_id) { | ||
174 | let loc = macro_call_id.loc(db); | ||
175 | let syntax = db.file_item(loc.source_item_id); | ||
176 | let macro_call = ast::MacroCall::cast(&syntax).unwrap(); | ||
177 | let off = macro_call.token_tree().unwrap().syntax().range().start(); | ||
178 | let file = exp.file(); | ||
179 | for trait_def in file.syntax().descendants().filter_map(ast::TraitDef::cast) { | ||
180 | if let Some(name) = trait_def.name() { | ||
181 | let dst_range = name.syntax().range(); | ||
182 | if let Some(src_range) = exp.map_range_back(dst_range) { | ||
183 | res.push((name.text().clone(), src_range + off)) | ||
184 | } | ||
185 | } | ||
186 | } | ||
187 | } | ||
188 | } | ||
189 | |||
190 | res | ||
191 | } | ||
192 | |||
193 | pub fn resolver_for_position(db: &impl HirDatabase, position: FilePosition) -> Resolver { | 155 | pub fn resolver_for_position(db: &impl HirDatabase, position: FilePosition) -> Resolver { |
194 | let file_id = position.file_id; | 156 | let file_id = position.file_id; |
195 | let file = db.parse(file_id); | 157 | let file = db.parse(file_id); |
diff --git a/crates/ra_hir/src/ty/method_resolution.rs b/crates/ra_hir/src/ty/method_resolution.rs index 94b757af2..804824868 100644 --- a/crates/ra_hir/src/ty/method_resolution.rs +++ b/crates/ra_hir/src/ty/method_resolution.rs | |||
@@ -7,10 +7,12 @@ use std::sync::Arc; | |||
7 | use rustc_hash::FxHashMap; | 7 | use rustc_hash::FxHashMap; |
8 | 8 | ||
9 | use crate::{ | 9 | use crate::{ |
10 | HirDatabase, module_tree::ModuleId, Module, Crate, Name, Function, Trait, | 10 | HirDatabase, Module, Crate, Name, Function, Trait, |
11 | ids::TraitId, | 11 | ids::TraitId, |
12 | impl_block::{ImplId, ImplBlock, ImplItem}, | 12 | impl_block::{ImplId, ImplBlock, ImplItem}, |
13 | ty::{AdtDef, Ty}, | 13 | ty::{AdtDef, Ty}, |
14 | nameres::CrateModuleId, | ||
15 | |||
14 | }; | 16 | }; |
15 | 17 | ||
16 | /// This is used as a key for indexing impls. | 18 | /// This is used as a key for indexing impls. |
@@ -33,10 +35,10 @@ impl TyFingerprint { | |||
33 | 35 | ||
34 | #[derive(Debug, PartialEq, Eq)] | 36 | #[derive(Debug, PartialEq, Eq)] |
35 | pub struct CrateImplBlocks { | 37 | pub struct CrateImplBlocks { |
36 | /// To make sense of the ModuleIds, we need the source root. | 38 | /// To make sense of the CrateModuleIds, we need the source root. |
37 | krate: Crate, | 39 | krate: Crate, |
38 | impls: FxHashMap<TyFingerprint, Vec<(ModuleId, ImplId)>>, | 40 | impls: FxHashMap<TyFingerprint, Vec<(CrateModuleId, ImplId)>>, |
39 | impls_by_trait: FxHashMap<TraitId, Vec<(ModuleId, ImplId)>>, | 41 | impls_by_trait: FxHashMap<TraitId, Vec<(CrateModuleId, ImplId)>>, |
40 | } | 42 | } |
41 | 43 | ||
42 | impl CrateImplBlocks { | 44 | impl CrateImplBlocks { |