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 | |
parent | 65e763fa84ae70ec9cee13f434acaae5371ad8e5 (diff) |
add skeleton for macro-aware name resolutions
Diffstat (limited to 'crates/ra_hir/src')
-rw-r--r-- | crates/ra_hir/src/ids.rs | 27 | ||||
-rw-r--r-- | crates/ra_hir/src/module_tree.rs | 9 | ||||
-rw-r--r-- | crates/ra_hir/src/name.rs | 3 | ||||
-rw-r--r-- | crates/ra_hir/src/nameres.rs | 1 | ||||
-rw-r--r-- | crates/ra_hir/src/nameres/crate_def_map.rs | 204 | ||||
-rw-r--r-- | crates/ra_hir/src/nameres/crate_def_map/collector.rs | 256 | ||||
-rw-r--r-- | crates/ra_hir/src/nameres/crate_def_map/raw.rs | 278 |
7 files changed, 769 insertions, 9 deletions
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 | ||
292 | pub(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 | |||
292 | fn resolve_submodule( | 301 | fn 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. |
17 | pub(crate) mod lower; | 17 | pub(crate) mod lower; |
18 | mod crate_def_map; | ||
18 | 19 | ||
19 | use std::{time, sync::Arc}; | 20 | use 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; | ||
43 | mod raw; | ||
44 | mod collector; | ||
45 | |||
46 | use rustc_hash::FxHashMap; | ||
47 | use ra_arena::{Arena}; | ||
48 | |||
49 | use crate::{ | ||
50 | Name, | ||
51 | module_tree::ModuleId, | ||
52 | nameres::ModuleScope, | ||
53 | }; | ||
54 | |||
55 | #[derive(Default, Debug)] | ||
56 | struct 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)] | ||
64 | pub(crate) struct CrateDefMap { | ||
65 | root: ModuleId, | ||
66 | modules: Arena<ModuleId, ModuleData>, | ||
67 | } | ||
68 | |||
69 | #[cfg(test)] | ||
70 | mod 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###" | ||
139 | crate | ||
140 | S: t v | ||
141 | |||
142 | crate::foo | ||
143 | f: v | ||
144 | |||
145 | crate::foo::bar | ||
146 | Baz: t v | ||
147 | E: 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###" | ||
170 | crate | ||
171 | Foo: t v | ||
172 | |||
173 | crate::nested | ||
174 | Bar: t v | ||
175 | Baz: 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###" | ||
196 | crate | ||
197 | |||
198 | crate::n1 | ||
199 | |||
200 | crate::n1::n2 | ||
201 | X: 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 @@ | |||
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 | } | ||
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 @@ | |||
1 | use std::{ | ||
2 | sync::Arc, | ||
3 | ops::Index, | ||
4 | }; | ||
5 | |||
6 | use ra_db::FileId; | ||
7 | use ra_arena::{Arena, impl_arena_id, RawId}; | ||
8 | use ra_syntax::{ | ||
9 | AstNode, SourceFile, | ||
10 | ast::{self, NameOwner, AttrsOwner}, | ||
11 | }; | ||
12 | |||
13 | use crate::{ | ||
14 | PersistentHirDatabase, Name, AsName, Path, HirFileId, | ||
15 | ids::{SourceFileItemId, SourceFileItems}, | ||
16 | }; | ||
17 | |||
18 | #[derive(Default, PartialEq, Eq)] | ||
19 | pub(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 | |||
28 | impl 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 | |||
56 | impl Index<Module> for RawItems { | ||
57 | type Output = ModuleData; | ||
58 | fn index(&self, idx: Module) -> &ModuleData { | ||
59 | &self.modules[idx] | ||
60 | } | ||
61 | } | ||
62 | |||
63 | impl Index<Import> for RawItems { | ||
64 | type Output = ImportData; | ||
65 | fn index(&self, idx: Import) -> &ImportData { | ||
66 | &self.imports[idx] | ||
67 | } | ||
68 | } | ||
69 | |||
70 | impl Index<Def> for RawItems { | ||
71 | type Output = DefData; | ||
72 | fn index(&self, idx: Def) -> &DefData { | ||
73 | &self.defs[idx] | ||
74 | } | ||
75 | } | ||
76 | |||
77 | impl 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)] | ||
85 | pub(crate) enum RawItem { | ||
86 | Module(Module), | ||
87 | Import(Import), | ||
88 | Def(Def), | ||
89 | Macro(Macro), | ||
90 | } | ||
91 | |||
92 | #[derive(Clone, Copy, PartialEq, Eq, Hash)] | ||
93 | pub(crate) struct Module(RawId); | ||
94 | impl_arena_id!(Module); | ||
95 | |||
96 | #[derive(PartialEq, Eq)] | ||
97 | pub(crate) enum ModuleData { | ||
98 | Declaration { name: Name }, | ||
99 | Definition { name: Name, items: Vec<RawItem> }, | ||
100 | } | ||
101 | |||
102 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] | ||
103 | pub(crate) struct Import(RawId); | ||
104 | impl_arena_id!(Import); | ||
105 | |||
106 | #[derive(PartialEq, Eq)] | ||
107 | pub(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)] | ||
116 | pub(crate) struct Def(RawId); | ||
117 | impl_arena_id!(Def); | ||
118 | |||
119 | #[derive(PartialEq, Eq)] | ||
120 | pub(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)] | ||
127 | pub(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)] | ||
138 | pub(crate) struct Macro(RawId); | ||
139 | impl_arena_id!(Macro); | ||
140 | |||
141 | #[derive(PartialEq, Eq)] | ||
142 | pub(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 | |||
149 | struct RawItemsCollector { | ||
150 | raw_items: RawItems, | ||
151 | source_file_items: Arc<SourceFileItems>, | ||
152 | } | ||
153 | |||
154 | impl 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 | } | ||