aboutsummaryrefslogtreecommitdiff
path: root/crates/ra_hir/src/nameres/crate_def_map
diff options
context:
space:
mode:
Diffstat (limited to 'crates/ra_hir/src/nameres/crate_def_map')
-rw-r--r--crates/ra_hir/src/nameres/crate_def_map/collector.rs256
-rw-r--r--crates/ra_hir/src/nameres/crate_def_map/raw.rs278
2 files changed, 534 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}
diff --git a/crates/ra_hir/src/nameres/crate_def_map/raw.rs b/crates/ra_hir/src/nameres/crate_def_map/raw.rs
new file mode 100644
index 000000000..cec2484eb
--- /dev/null
+++ b/crates/ra_hir/src/nameres/crate_def_map/raw.rs
@@ -0,0 +1,278 @@
1use std::{
2 sync::Arc,
3 ops::Index,
4};
5
6use ra_db::FileId;
7use ra_arena::{Arena, impl_arena_id, RawId};
8use ra_syntax::{
9 AstNode, SourceFile,
10 ast::{self, NameOwner, AttrsOwner},
11};
12
13use crate::{
14 PersistentHirDatabase, Name, AsName, Path, HirFileId,
15 ids::{SourceFileItemId, SourceFileItems},
16};
17
18#[derive(Default, PartialEq, Eq)]
19pub(crate) struct RawItems {
20 modules: Arena<Module, ModuleData>,
21 imports: Arena<Import, ImportData>,
22 defs: Arena<Def, DefData>,
23 macros: Arena<Macro, MacroData>,
24 /// items for top-level module
25 items: Vec<RawItem>,
26}
27
28impl RawItems {
29 pub(crate) fn items(&self) -> &[RawItem] {
30 &self.items
31 }
32
33 pub(crate) fn raw_items_query(db: &impl PersistentHirDatabase, file_id: FileId) -> RawItems {
34 let mut collector = RawItemsCollector {
35 raw_items: RawItems::default(),
36 source_file_items: db.file_items(file_id.into()),
37 };
38 let source_file = db.parse(file_id);
39 collector.process_module(None, &*source_file);
40 collector.raw_items
41 }
42
43 // We can't use queries during name resolution for fear of cycles, so this
44 // is a query-less variant of the above function.
45 pub(crate) fn from_source_file(source_file: &SourceFile, file_id: HirFileId) -> RawItems {
46 let source_file_items = SourceFileItems::from_source_file(source_file, file_id);
47 let mut collector = RawItemsCollector {
48 raw_items: RawItems::default(),
49 source_file_items: Arc::new(source_file_items),
50 };
51 collector.process_module(None, &*source_file);
52 collector.raw_items
53 }
54}
55
56impl Index<Module> for RawItems {
57 type Output = ModuleData;
58 fn index(&self, idx: Module) -> &ModuleData {
59 &self.modules[idx]
60 }
61}
62
63impl Index<Import> for RawItems {
64 type Output = ImportData;
65 fn index(&self, idx: Import) -> &ImportData {
66 &self.imports[idx]
67 }
68}
69
70impl Index<Def> for RawItems {
71 type Output = DefData;
72 fn index(&self, idx: Def) -> &DefData {
73 &self.defs[idx]
74 }
75}
76
77impl Index<Macro> for RawItems {
78 type Output = MacroData;
79 fn index(&self, idx: Macro) -> &MacroData {
80 &self.macros[idx]
81 }
82}
83
84#[derive(PartialEq, Eq, Clone, Copy)]
85pub(crate) enum RawItem {
86 Module(Module),
87 Import(Import),
88 Def(Def),
89 Macro(Macro),
90}
91
92#[derive(Clone, Copy, PartialEq, Eq, Hash)]
93pub(crate) struct Module(RawId);
94impl_arena_id!(Module);
95
96#[derive(PartialEq, Eq)]
97pub(crate) enum ModuleData {
98 Declaration { name: Name },
99 Definition { name: Name, items: Vec<RawItem> },
100}
101
102#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
103pub(crate) struct Import(RawId);
104impl_arena_id!(Import);
105
106#[derive(PartialEq, Eq)]
107pub(crate) struct ImportData {
108 path: Path,
109 alias: Option<Name>,
110 is_glob: bool,
111 is_prelude: bool,
112 is_extern_crate: bool,
113}
114
115#[derive(Clone, Copy, PartialEq, Eq, Hash)]
116pub(crate) struct Def(RawId);
117impl_arena_id!(Def);
118
119#[derive(PartialEq, Eq)]
120pub(crate) struct DefData {
121 pub(crate) source_item_id: SourceFileItemId,
122 pub(crate) name: Name,
123 pub(crate) kind: DefKind,
124}
125
126#[derive(Debug, PartialEq, Eq, Clone, Copy)]
127pub(crate) enum DefKind {
128 Function,
129 Struct,
130 Enum,
131 Const,
132 Static,
133 Trait,
134 TypeAlias,
135}
136
137#[derive(Clone, Copy, PartialEq, Eq, Hash)]
138pub(crate) struct Macro(RawId);
139impl_arena_id!(Macro);
140
141#[derive(PartialEq, Eq)]
142pub(crate) struct MacroData {
143 pub(crate) source_item_id: SourceFileItemId,
144 pub(crate) path: Path,
145 pub(crate) name: Option<Name>,
146 pub(crate) arg: tt::Subtree,
147}
148
149struct RawItemsCollector {
150 raw_items: RawItems,
151 source_file_items: Arc<SourceFileItems>,
152}
153
154impl RawItemsCollector {
155 fn process_module(&mut self, current_module: Option<Module>, body: &impl ast::ModuleItemOwner) {
156 for item_or_macro in body.items_with_macros() {
157 match item_or_macro {
158 ast::ItemOrMacro::Macro(m) => self.add_macro(current_module, m),
159 ast::ItemOrMacro::Item(item) => self.add_item(current_module, item),
160 }
161 }
162 }
163
164 fn add_item(&mut self, current_module: Option<Module>, item: &ast::ModuleItem) {
165 let (kind, name) = match item.kind() {
166 ast::ModuleItemKind::Module(module) => {
167 self.add_module(current_module, module);
168 return;
169 }
170 ast::ModuleItemKind::UseItem(use_item) => {
171 self.add_use_item(current_module, use_item);
172 return;
173 }
174 ast::ModuleItemKind::ExternCrateItem(extern_crate) => {
175 self.add_extern_crate_item(current_module, extern_crate);
176 return;
177 }
178 ast::ModuleItemKind::ImplBlock(_) => {
179 // impls don't participate in name resolution
180 return;
181 }
182 ast::ModuleItemKind::StructDef(it) => (DefKind::Struct, it.name()),
183 ast::ModuleItemKind::EnumDef(it) => (DefKind::Enum, it.name()),
184 ast::ModuleItemKind::FnDef(it) => (DefKind::Function, it.name()),
185 ast::ModuleItemKind::TraitDef(it) => (DefKind::Trait, it.name()),
186 ast::ModuleItemKind::TypeAliasDef(it) => (DefKind::TypeAlias, it.name()),
187 ast::ModuleItemKind::ConstDef(it) => (DefKind::Const, it.name()),
188 ast::ModuleItemKind::StaticDef(it) => (DefKind::Static, it.name()),
189 };
190 if let Some(name) = name {
191 let name = name.as_name();
192 let source_item_id = self.source_file_items.id_of_unchecked(item.syntax());
193 let def = self.raw_items.defs.alloc(DefData { name, kind, source_item_id });
194 self.push_item(current_module, RawItem::Def(def))
195 }
196 }
197
198 fn add_module(&mut self, current_module: Option<Module>, module: &ast::Module) {
199 let name = match module.name() {
200 Some(it) => it.as_name(),
201 None => return,
202 };
203 if module.has_semi() {
204 let item = self.raw_items.modules.alloc(ModuleData::Declaration { name });
205 self.push_item(current_module, RawItem::Module(item));
206 return;
207 }
208
209 if let Some(item_list) = module.item_list() {
210 let item =
211 self.raw_items.modules.alloc(ModuleData::Definition { name, items: Vec::new() });
212 self.process_module(Some(item), item_list);
213 self.push_item(current_module, RawItem::Module(item));
214 }
215 }
216
217 fn add_use_item(&mut self, current_module: Option<Module>, use_item: &ast::UseItem) {
218 let is_prelude = use_item
219 .attrs()
220 .any(|attr| attr.as_atom().map(|s| s == "prelude_import").unwrap_or(false));
221
222 Path::expand_use_item(use_item, |path, segment, alias| {
223 let import = self.raw_items.imports.alloc(ImportData {
224 path,
225 alias,
226 is_glob: segment.is_none(),
227 is_prelude,
228 is_extern_crate: false,
229 });
230 self.push_item(current_module, RawItem::Import(import))
231 })
232 }
233
234 fn add_extern_crate_item(
235 &mut self,
236 current_module: Option<Module>,
237 extern_crate: &ast::ExternCrateItem,
238 ) {
239 if let Some(name_ref) = extern_crate.name_ref() {
240 let path = Path::from_name_ref(name_ref);
241 let alias = extern_crate.alias().and_then(|a| a.name()).map(AsName::as_name);
242 let import = self.raw_items.imports.alloc(ImportData {
243 path,
244 alias,
245 is_glob: false,
246 is_prelude: false,
247 is_extern_crate: true,
248 });
249 self.push_item(current_module, RawItem::Import(import))
250 }
251 }
252
253 fn add_macro(&mut self, current_module: Option<Module>, m: &ast::MacroCall) {
254 let (path, arg) = match (
255 m.path().and_then(Path::from_ast),
256 m.token_tree().and_then(mbe::ast_to_token_tree),
257 ) {
258 (Some(path), Some((token_tree, _token_map))) => (path, token_tree),
259 _ => return,
260 };
261
262 let name = m.name().map(|it| it.as_name());
263 let source_item_id = self.source_file_items.id_of_unchecked(m.syntax());
264 let m = self.raw_items.macros.alloc(MacroData { source_item_id, path, arg, name });
265 self.push_item(current_module, RawItem::Macro(m));
266 }
267
268 fn push_item(&mut self, current_module: Option<Module>, item: RawItem) {
269 match current_module {
270 Some(module) => match &mut self.raw_items.modules[module] {
271 ModuleData::Definition { items, .. } => items,
272 ModuleData::Declaration { .. } => unreachable!(),
273 },
274 None => &mut self.raw_items.items,
275 }
276 .push(item)
277 }
278}