diff options
Diffstat (limited to 'crates/ra_hir/src/nameres/crate_def_map.rs')
-rw-r--r-- | crates/ra_hir/src/nameres/crate_def_map.rs | 204 |
1 files changed, 204 insertions, 0 deletions
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 | } | ||