aboutsummaryrefslogtreecommitdiff
path: root/crates/ra_hir/src/nameres/crate_def_map.rs
diff options
context:
space:
mode:
Diffstat (limited to 'crates/ra_hir/src/nameres/crate_def_map.rs')
-rw-r--r--crates/ra_hir/src/nameres/crate_def_map.rs204
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;
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}