aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAleksey Kladov <[email protected]>2019-03-02 20:59:04 +0000
committerAleksey Kladov <[email protected]>2019-03-17 09:46:13 +0000
commit0d8d9186563637f493ac7691268319373251b18a (patch)
treeed5c887e53ee7896c719c58cb1463ce2d57f7432
parent65e763fa84ae70ec9cee13f434acaae5371ad8e5 (diff)
add skeleton for macro-aware name resolutions
-rw-r--r--crates/ra_db/src/input.rs4
-rw-r--r--crates/ra_hir/src/ids.rs27
-rw-r--r--crates/ra_hir/src/module_tree.rs9
-rw-r--r--crates/ra_hir/src/name.rs3
-rw-r--r--crates/ra_hir/src/nameres.rs1
-rw-r--r--crates/ra_hir/src/nameres/crate_def_map.rs204
-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
8 files changed, 773 insertions, 9 deletions
diff --git a/crates/ra_db/src/input.rs b/crates/ra_db/src/input.rs
index e45a510b3..2b1001d48 100644
--- a/crates/ra_db/src/input.rs
+++ b/crates/ra_db/src/input.rs
@@ -124,6 +124,10 @@ impl CrateGraph {
124 self.arena.is_empty() 124 self.arena.is_empty()
125 } 125 }
126 126
127 pub fn iter<'a>(&'a self) -> impl Iterator<Item = CrateId> + 'a {
128 self.arena.keys().map(|it| *it)
129 }
130
127 pub fn crate_root(&self, crate_id: CrateId) -> FileId { 131 pub fn crate_root(&self, crate_id: CrateId) -> FileId {
128 self.arena[&crate_id].file_id 132 self.arena[&crate_id].file_id
129 } 133 }
diff --git a/crates/ra_hir/src/ids.rs b/crates/ra_hir/src/ids.rs
index 5b00330c6..e805ddcba 100644
--- a/crates/ra_hir/src/ids.rs
+++ b/crates/ra_hir/src/ids.rs
@@ -200,8 +200,14 @@ pub(crate) trait AstItemDef<N: AstNode>: ArenaId + Clone {
200 fn interner(interner: &HirInterner) -> &LocationIntener<ItemLoc<N>, Self>; 200 fn interner(interner: &HirInterner) -> &LocationIntener<ItemLoc<N>, Self>;
201 fn from_ast(ctx: LocationCtx<&impl PersistentHirDatabase>, ast: &N) -> Self { 201 fn from_ast(ctx: LocationCtx<&impl PersistentHirDatabase>, ast: &N) -> Self {
202 let items = ctx.db.file_items(ctx.file_id); 202 let items = ctx.db.file_items(ctx.file_id);
203 let raw = 203 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()) }; 204 Self::from_source_item_id_unchecked(ctx, item_id)
205 }
206 fn from_source_item_id_unchecked(
207 ctx: LocationCtx<&impl PersistentHirDatabase>,
208 item_id: SourceFileItemId,
209 ) -> Self {
210 let raw = SourceItemId { file_id: ctx.file_id, item_id };
205 let loc = ItemLoc { module: ctx.module, raw, _ty: PhantomData }; 211 let loc = ItemLoc { module: ctx.module, raw, _ty: PhantomData };
206 212
207 Self::interner(ctx.db.as_ref()).loc2id(&loc) 213 Self::interner(ctx.db.as_ref()).loc2id(&loc)
@@ -309,9 +315,7 @@ impl SourceFileItems {
309 file_id: HirFileId, 315 file_id: HirFileId,
310 ) -> Arc<SourceFileItems> { 316 ) -> Arc<SourceFileItems> {
311 let source_file = db.hir_parse(file_id); 317 let source_file = db.hir_parse(file_id);
312 let mut res = SourceFileItems { file_id, arena: Arena::default() }; 318 Arc::new(SourceFileItems::from_source_file(&source_file, file_id))
313 res.init(&source_file);
314 Arc::new(res)
315 } 319 }
316 320
317 pub(crate) fn file_item_query( 321 pub(crate) fn file_item_query(
@@ -324,18 +328,23 @@ impl SourceFileItems {
324 .to_owned() 328 .to_owned()
325 } 329 }
326 330
327 fn init(&mut self, source_file: &SourceFile) { 331 pub(crate) fn from_source_file(
332 source_file: &SourceFile,
333 file_id: HirFileId,
334 ) -> SourceFileItems {
335 let mut res = SourceFileItems { file_id, arena: Arena::default() };
328 // By walking the tree in bread-first order we make sure that parents 336 // 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 337 // 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 338 // 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. 339 // trait does not change ids of top-level items, which helps caching.
332 bfs(source_file.syntax(), |it| { 340 bfs(source_file.syntax(), |it| {
333 if let Some(module_item) = ast::ModuleItem::cast(it) { 341 if let Some(module_item) = ast::ModuleItem::cast(it) {
334 self.alloc(module_item.syntax()); 342 res.alloc(module_item.syntax());
335 } else if let Some(macro_call) = ast::MacroCall::cast(it) { 343 } else if let Some(macro_call) = ast::MacroCall::cast(it) {
336 self.alloc(macro_call.syntax()); 344 res.alloc(macro_call.syntax());
337 } 345 }
338 }) 346 });
347 res
339 } 348 }
340 349
341 fn alloc(&mut self, item: &SyntaxNode) -> SourceFileItemId { 350 fn alloc(&mut self, item: &SyntaxNode) -> SourceFileItemId {
diff --git a/crates/ra_hir/src/module_tree.rs b/crates/ra_hir/src/module_tree.rs
index 99c2115e1..4d0f40e85 100644
--- a/crates/ra_hir/src/module_tree.rs
+++ b/crates/ra_hir/src/module_tree.rs
@@ -289,6 +289,15 @@ impl LinkId {
289 } 289 }
290} 290}
291 291
292pub(crate) fn resolve_module_declaration(
293 db: &impl PersistentHirDatabase,
294 file_id: HirFileId,
295 name: &Name,
296 is_root: bool,
297) -> Option<FileId> {
298 resolve_submodule(db, file_id, name, is_root).0.first().map(|it| *it)
299}
300
292fn resolve_submodule( 301fn resolve_submodule(
293 db: &impl PersistentHirDatabase, 302 db: &impl PersistentHirDatabase,
294 file_id: HirFileId, 303 file_id: HirFileId,
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..6f98ac071 100644
--- a/crates/ra_hir/src/nameres.rs
+++ b/crates/ra_hir/src/nameres.rs
@@ -15,6 +15,7 @@
15//! so that the results of name resolution can be preserved unless the module 15//! so that the results of name resolution can be preserved unless the module
16//! structure itself is modified. 16//! structure itself is modified.
17pub(crate) mod lower; 17pub(crate) mod lower;
18mod crate_def_map;
18 19
19use std::{time, sync::Arc}; 20use std::{time, sync::Arc};
20 21
diff --git a/crates/ra_hir/src/nameres/crate_def_map.rs b/crates/ra_hir/src/nameres/crate_def_map.rs
new file mode 100644
index 000000000..ea9b4fb50
--- /dev/null
+++ b/crates/ra_hir/src/nameres/crate_def_map.rs
@@ -0,0 +1,204 @@
1/// This module implements new import-resolution/macro expansion algorithm.
2///
3/// The result of this module is `CrateDefMap`: a datastructure which contains:
4///
5/// * a tree of modules for the crate
6/// * for each module, a set of items visible in the module (directly declared
7/// or imported)
8///
9/// Note that `CrateDefMap` contains fully macro expanded code.
10///
11/// Computing `CrateDefMap` can be partitioned into several logically
12/// independent "phases". The phases are mutually recursive though, there's no
13/// stric ordering.
14///
15/// ## Collecting RawItems
16///
17/// This happens in the `raw` module, which parses a single source file into a
18/// set of top-level items. Nested importa are desugared to flat imports in
19/// this phase. Macro calls are represented as a triple of (Path, Option<Name>,
20/// TokenTree).
21///
22/// ## Collecting Modules
23///
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 unresovled imports and macros.
28///
29/// While we walk tree of modules, we also record macro_rules defenitions and
30/// expand calls to macro_rules defined macros.
31///
32/// ## Resolving Imports
33///
34/// TBD
35///
36/// ## Resolving Macros
37///
38/// While macro_rules from the same crate use a global mutable namespace, macros
39/// from other crates (including proc-macros) can be used with `foo::bar!`
40/// syntax.
41///
42/// TBD;
43mod raw;
44mod collector;
45
46use rustc_hash::FxHashMap;
47use ra_arena::{Arena};
48
49use crate::{
50 Name,
51 module_tree::ModuleId,
52 nameres::ModuleScope,
53};
54
55#[derive(Default, Debug)]
56struct ModuleData {
57 parent: Option<ModuleId>,
58 children: FxHashMap<Name, ModuleId>,
59 scope: ModuleScope,
60}
61
62/// Contans all top-level defs from a macro-expanded crate
63#[derive(Debug)]
64pub(crate) struct CrateDefMap {
65 root: ModuleId,
66 modules: Arena<ModuleId, ModuleData>,
67}
68
69#[cfg(test)]
70mod tests {
71 use std::sync::Arc;
72
73 use ra_db::SourceDatabase;
74 use insta::assert_snapshot_matches;
75
76 use crate::{Crate, mock::MockDatabase, nameres::Resolution};
77
78 use super::*;
79
80 fn compute_crate_def_map(fixture: &str) -> Arc<CrateDefMap> {
81 let db = MockDatabase::with_files(fixture);
82 let crate_id = db.crate_graph().iter().next().unwrap();
83 let krate = Crate { crate_id };
84 collector::crate_def_map_query(&db, krate)
85 }
86
87 fn render_crate_def_map(map: &CrateDefMap) -> String {
88 let mut buf = String::new();
89 go(&mut buf, map, "\ncrate", map.root);
90 return buf;
91
92 fn go(buf: &mut String, map: &CrateDefMap, path: &str, module: ModuleId) {
93 *buf += path;
94 *buf += "\n";
95 for (name, res) in map.modules[module].scope.items.iter() {
96 *buf += &format!("{}: {}\n", name, dump_resolution(res))
97 }
98 for (name, child) in map.modules[module].children.iter() {
99 let path = path.to_string() + &format!("::{}", name);
100 go(buf, map, &path, *child);
101 }
102 }
103
104 fn dump_resolution(resolution: &Resolution) -> &'static str {
105 match (resolution.def.types.is_some(), resolution.def.values.is_some()) {
106 (true, true) => "t v",
107 (true, false) => "t",
108 (false, true) => "v",
109 (false, false) => "_",
110 }
111 }
112 }
113
114 fn def_map(fixtute: &str) -> String {
115 let dm = compute_crate_def_map(fixtute);
116 render_crate_def_map(&dm)
117 }
118
119 #[test]
120 fn crate_def_map_smoke_test() {
121 let map = def_map(
122 "
123 //- /lib.rs
124 mod foo;
125 struct S;
126
127 //- /foo/mod.rs
128 pub mod bar;
129 fn f() {}
130
131 //- /foo/bar.rs
132 pub struct Baz;
133 enum E { V }
134 ",
135 );
136 assert_snapshot_matches!(
137 map,
138 @r###"
139crate
140S: t v
141
142crate::foo
143f: v
144
145crate::foo::bar
146Baz: t v
147E: t
148"###
149 )
150 }
151
152 #[test]
153 fn macro_rules_are_globally_visible() {
154 let map = def_map(
155 "
156 //- /lib.rs
157 macro_rules! structs {
158 ($($i:ident),*) => {
159 $(struct $i { field: u32 } )*
160 }
161 }
162 structs!(Foo);
163 mod nested;
164
165 //- /nested.rs
166 structs!(Bar, Baz);
167 ",
168 );
169 assert_snapshot_matches!(map, @r###"
170crate
171Foo: t v
172
173crate::nested
174Bar: t v
175Baz: t v
176"###);
177 }
178
179 #[test]
180 fn macro_rules_can_define_modules() {
181 let map = def_map(
182 "
183 //- /lib.rs
184 macro_rules! m {
185 ($name:ident) => { mod $name; }
186 }
187 m!(n1);
188
189 //- /n1.rs
190 m!(n2)
191 //- /n1/n2.rs
192 struct X;
193 ",
194 );
195 assert_snapshot_matches!(map, @r###"
196crate
197
198crate::n1
199
200crate::n1::n2
201X: t v
202"###);
203 }
204}
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}