aboutsummaryrefslogtreecommitdiff
path: root/crates/ra_hir/src/nameres/crate_def_map/collector.rs
diff options
context:
space:
mode:
Diffstat (limited to 'crates/ra_hir/src/nameres/crate_def_map/collector.rs')
-rw-r--r--crates/ra_hir/src/nameres/crate_def_map/collector.rs256
1 files changed, 256 insertions, 0 deletions
diff --git a/crates/ra_hir/src/nameres/crate_def_map/collector.rs b/crates/ra_hir/src/nameres/crate_def_map/collector.rs
new file mode 100644
index 000000000..46bef3dbe
--- /dev/null
+++ b/crates/ra_hir/src/nameres/crate_def_map/collector.rs
@@ -0,0 +1,256 @@
1use std::sync::Arc;
2
3use rustc_hash::FxHashMap;
4use ra_arena::Arena;
5
6use crate::{
7 Function, Module, Struct, Enum, Const, Static, Trait, TypeAlias,
8 Crate, PersistentHirDatabase, HirFileId, Name, Path,
9 KnownName,
10 nameres::{Resolution, PerNs, ModuleDef, ReachedFixedPoint},
11 ids::{AstItemDef, LocationCtx, MacroCallLoc, SourceItemId, MacroCallId},
12 module_tree::resolve_module_declaration,
13};
14
15use super::{CrateDefMap, ModuleId, ModuleData, raw};
16
17#[allow(unused)]
18pub(crate) fn crate_def_map_query(
19 db: &impl PersistentHirDatabase,
20 krate: Crate,
21) -> Arc<CrateDefMap> {
22 let mut modules: Arena<ModuleId, ModuleData> = Arena::default();
23 let root = modules.alloc(ModuleData::default());
24 let mut collector = DefCollector {
25 db,
26 krate,
27 def_map: CrateDefMap { modules, root },
28 unresolved_imports: Vec::new(),
29 unexpanded_macros: Vec::new(),
30 global_macro_scope: FxHashMap::default(),
31 };
32 collector.collect();
33 let def_map = collector.finish();
34 Arc::new(def_map)
35}
36
37/// Walks the tree of module recursively
38struct DefCollector<DB> {
39 db: DB,
40 krate: Crate,
41 def_map: CrateDefMap,
42 unresolved_imports: Vec<(ModuleId, raw::Import)>,
43 unexpanded_macros: Vec<(ModuleId, MacroCallId, tt::Subtree)>,
44 global_macro_scope: FxHashMap<Name, mbe::MacroRules>,
45}
46
47/// Walks a single module, populating defs, imports and macros
48struct ModCollector<'a, D> {
49 def_collector: D,
50 module_id: ModuleId,
51 file_id: HirFileId,
52 raw_items: &'a raw::RawItems,
53}
54
55impl<'a, DB> DefCollector<&'a DB>
56where
57 DB: PersistentHirDatabase,
58{
59 fn collect(&mut self) {
60 let crate_graph = self.db.crate_graph();
61 let file_id = crate_graph.crate_root(self.krate.crate_id());
62 let raw_items = raw::RawItems::raw_items_query(self.db, file_id);
63 let module_id = self.def_map.root;
64 ModCollector {
65 def_collector: &mut *self,
66 module_id,
67 file_id: file_id.into(),
68 raw_items: &raw_items,
69 }
70 .collect(raw_items.items());
71
72 // main name resolution fixed-point loop.
73 let mut i = 0;
74 loop {
75 match (self.resolve_imports(), self.resolve_macros()) {
76 (ReachedFixedPoint::Yes, ReachedFixedPoint::Yes) => break,
77 _ => i += 1,
78 }
79 if i == 1000 {
80 log::error!("diverging name resolution");
81 break;
82 }
83 }
84 }
85
86 fn define_macro(&mut self, name: Name, tt: &tt::Subtree) {
87 if let Ok(rules) = mbe::MacroRules::parse(tt) {
88 self.global_macro_scope.insert(name, rules);
89 }
90 }
91
92 fn alloc_module(&mut self) -> ModuleId {
93 self.def_map.modules.alloc(ModuleData::default())
94 }
95
96 fn resolve_imports(&mut self) -> ReachedFixedPoint {
97 // Resolves imports, filling-in module scopes
98 ReachedFixedPoint::Yes
99 }
100
101 fn resolve_macros(&mut self) -> ReachedFixedPoint {
102 // Resolve macros, calling into `expand_macro` to actually do the
103 // expansion.
104 ReachedFixedPoint::Yes
105 }
106
107 #[allow(unused)]
108 fn expand_macro(&mut self, idx: usize, rules: &mbe::MacroRules) {
109 let (module_id, call_id, arg) = self.unexpanded_macros.swap_remove(idx);
110 if let Ok(tt) = rules.expand(&arg) {
111 self.collect_macro_expansion(module_id, call_id, tt);
112 }
113 }
114
115 fn collect_macro_expansion(
116 &mut self,
117 module_id: ModuleId,
118 macro_call_id: MacroCallId,
119 expansion: tt::Subtree,
120 ) {
121 // XXX: this **does not** go through a database, because we can't
122 // identify macro_call without adding the whole state of name resolution
123 // as a parameter to the query.
124 //
125 // So, we run the queries "manually" and we must ensure that
126 // `db.hir_parse(macro_call_id)` returns the same source_file.
127 let file_id: HirFileId = macro_call_id.into();
128 let source_file = mbe::token_tree_to_ast_item_list(&expansion);
129
130 let raw_items = raw::RawItems::from_source_file(&source_file, file_id);
131 ModCollector { def_collector: &mut *self, file_id, module_id, raw_items: &raw_items }
132 .collect(raw_items.items())
133 }
134
135 fn finish(self) -> CrateDefMap {
136 self.def_map
137 }
138}
139
140impl<DB> ModCollector<'_, &'_ mut DefCollector<&'_ DB>>
141where
142 DB: PersistentHirDatabase,
143{
144 fn collect(&mut self, items: &[raw::RawItem]) {
145 for item in items {
146 match *item {
147 raw::RawItem::Module(m) => self.collect_module(&self.raw_items[m]),
148 raw::RawItem::Import(import) => {
149 self.def_collector.unresolved_imports.push((self.module_id, import))
150 }
151 raw::RawItem::Def(def) => self.define_def(&self.raw_items[def]),
152 raw::RawItem::Macro(mac) => self.collect_macro(&self.raw_items[mac]),
153 }
154 }
155 }
156
157 fn collect_module(&mut self, module: &raw::ModuleData) {
158 match module {
159 // inline module, just recurse
160 raw::ModuleData::Definition { name, items } => {
161 let module_id = self.push_child_module(name.clone());
162 ModCollector {
163 def_collector: &mut *self.def_collector,
164 module_id,
165 file_id: self.file_id,
166 raw_items: self.raw_items,
167 }
168 .collect(&*items);
169 }
170 // out of line module, resovle, parse and recurse
171 raw::ModuleData::Declaration { name } => {
172 let module_id = self.push_child_module(name.clone());
173 let is_root = self.def_collector.def_map.modules[self.module_id].parent.is_none();
174 if let Some(file_id) =
175 resolve_module_declaration(self.def_collector.db, self.file_id, name, is_root)
176 {
177 let raw_items = raw::RawItems::raw_items_query(self.def_collector.db, file_id);
178 ModCollector {
179 def_collector: &mut *self.def_collector,
180 module_id,
181 file_id: file_id.into(),
182 raw_items: &raw_items,
183 }
184 .collect(raw_items.items())
185 }
186 }
187 }
188 }
189
190 fn push_child_module(&mut self, name: Name) -> ModuleId {
191 let res = self.def_collector.alloc_module();
192 self.def_collector.def_map.modules[res].parent = Some(self.module_id);
193 self.def_collector.def_map.modules[self.module_id].children.insert(name, res);
194 res
195 }
196
197 fn define_def(&mut self, def: &raw::DefData) {
198 let module = Module { krate: self.def_collector.krate, module_id: self.module_id };
199 let ctx = LocationCtx::new(self.def_collector.db, module, self.file_id.into());
200 macro_rules! id {
201 () => {
202 AstItemDef::from_source_item_id_unchecked(ctx, def.source_item_id)
203 };
204 }
205 let name = def.name.clone();
206 let def: PerNs<ModuleDef> = match def.kind {
207 raw::DefKind::Function => PerNs::values(Function { id: id!() }.into()),
208 raw::DefKind::Struct => {
209 let s = Struct { id: id!() }.into();
210 PerNs::both(s, s)
211 }
212 raw::DefKind::Enum => PerNs::types(Enum { id: id!() }.into()),
213 raw::DefKind::Const => PerNs::values(Const { id: id!() }.into()),
214 raw::DefKind::Static => PerNs::values(Static { id: id!() }.into()),
215 raw::DefKind::Trait => PerNs::types(Trait { id: id!() }.into()),
216 raw::DefKind::TypeAlias => PerNs::types(TypeAlias { id: id!() }.into()),
217 };
218 let resolution = Resolution { def, import: None };
219 self.def_collector.def_map.modules[self.module_id].scope.items.insert(name, resolution);
220 }
221
222 fn collect_macro(&mut self, mac: &raw::MacroData) {
223 // Case 1: macro rules, define a macro in crate-global mutable scope
224 if is_macro_rules(&mac.path) {
225 if let Some(name) = &mac.name {
226 self.def_collector.define_macro(name.clone(), &mac.arg)
227 }
228 return;
229 }
230
231 let source_item_id = SourceItemId { file_id: self.file_id, item_id: mac.source_item_id };
232 let macro_call_id = MacroCallLoc {
233 module: Module { krate: self.def_collector.krate, module_id: self.module_id },
234 source_item_id,
235 }
236 .id(self.def_collector.db);
237
238 // Case 2: try to expand macro_rules from this crate, triggering
239 // recursive item collection.
240 if let Some(rules) =
241 mac.path.as_ident().and_then(|name| self.def_collector.global_macro_scope.get(name))
242 {
243 if let Ok(tt) = rules.expand(&mac.arg) {
244 self.def_collector.collect_macro_expansion(self.module_id, macro_call_id, tt);
245 }
246 return;
247 }
248
249 // Case 3: path to a macro from another crate, expand during name resolution
250 self.def_collector.unexpanded_macros.push((self.module_id, macro_call_id, mac.arg.clone()))
251 }
252}
253
254fn is_macro_rules(path: &Path) -> bool {
255 path.as_ident().and_then(Name::as_known_name) == Some(KnownName::MacroRules)
256}