diff options
author | Aleksey Kladov <[email protected]> | 2019-03-02 20:59:04 +0000 |
---|---|---|
committer | Aleksey Kladov <[email protected]> | 2019-03-17 09:46:13 +0000 |
commit | 0d8d9186563637f493ac7691268319373251b18a (patch) | |
tree | ed5c887e53ee7896c719c58cb1463ce2d57f7432 /crates/ra_hir/src/nameres/crate_def_map/collector.rs | |
parent | 65e763fa84ae70ec9cee13f434acaae5371ad8e5 (diff) |
add skeleton for macro-aware name resolutions
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.rs | 256 |
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 @@ | |||
1 | use std::sync::Arc; | ||
2 | |||
3 | use rustc_hash::FxHashMap; | ||
4 | use ra_arena::Arena; | ||
5 | |||
6 | use 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 | |||
15 | use super::{CrateDefMap, ModuleId, ModuleData, raw}; | ||
16 | |||
17 | #[allow(unused)] | ||
18 | pub(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 | ||
38 | struct 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 | ||
48 | struct ModCollector<'a, D> { | ||
49 | def_collector: D, | ||
50 | module_id: ModuleId, | ||
51 | file_id: HirFileId, | ||
52 | raw_items: &'a raw::RawItems, | ||
53 | } | ||
54 | |||
55 | impl<'a, DB> DefCollector<&'a DB> | ||
56 | where | ||
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 | |||
140 | impl<DB> ModCollector<'_, &'_ mut DefCollector<&'_ DB>> | ||
141 | where | ||
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 | |||
254 | fn is_macro_rules(path: &Path) -> bool { | ||
255 | path.as_ident().and_then(Name::as_known_name) == Some(KnownName::MacroRules) | ||
256 | } | ||