diff options
author | bors[bot] <bors[bot]@users.noreply.github.com> | 2019-03-17 09:59:04 +0000 |
---|---|---|
committer | bors[bot] <bors[bot]@users.noreply.github.com> | 2019-03-17 09:59:04 +0000 |
commit | 7d3f48cdaf20d718e711f999573adf3303e9396a (patch) | |
tree | df584fbb044cad23e196da5ae0b3636b06bfeeff /crates/ra_hir/src/nameres/raw.rs | |
parent | 65e763fa84ae70ec9cee13f434acaae5371ad8e5 (diff) | |
parent | 3a770233652cbf3e48688dd5f1d9f3c363eda5a8 (diff) |
Merge #968
968: Macro aware name resoltion r=matklad a=matklad
The first commit lays the ground work for new name resolution, including
* extracting position-indendent items from parse trees
* walking the tree of modules
* old-style macro_rules resolve
cc @pnkfelix: this looks like an API name resolution should interact with.
Co-authored-by: Aleksey Kladov <[email protected]>
Diffstat (limited to 'crates/ra_hir/src/nameres/raw.rs')
-rw-r--r-- | crates/ra_hir/src/nameres/raw.rs | 322 |
1 files changed, 322 insertions, 0 deletions
diff --git a/crates/ra_hir/src/nameres/raw.rs b/crates/ra_hir/src/nameres/raw.rs new file mode 100644 index 000000000..3226bbf0d --- /dev/null +++ b/crates/ra_hir/src/nameres/raw.rs | |||
@@ -0,0 +1,322 @@ | |||
1 | use std::{ | ||
2 | sync::Arc, | ||
3 | ops::Index, | ||
4 | }; | ||
5 | |||
6 | use test_utils::tested_by; | ||
7 | use ra_db::FileId; | ||
8 | use ra_arena::{Arena, impl_arena_id, RawId, map::ArenaMap}; | ||
9 | use ra_syntax::{ | ||
10 | AstNode, SourceFile, AstPtr, TreeArc, | ||
11 | ast::{self, NameOwner, AttrsOwner}, | ||
12 | }; | ||
13 | |||
14 | use crate::{ | ||
15 | PersistentHirDatabase, Name, AsName, Path, HirFileId, ModuleSource, | ||
16 | ids::{SourceFileItemId, SourceFileItems}, | ||
17 | }; | ||
18 | |||
19 | #[derive(Debug, Default, PartialEq, Eq)] | ||
20 | pub struct RawItems { | ||
21 | modules: Arena<Module, ModuleData>, | ||
22 | imports: Arena<ImportId, ImportData>, | ||
23 | defs: Arena<Def, DefData>, | ||
24 | macros: Arena<Macro, MacroData>, | ||
25 | /// items for top-level module | ||
26 | items: Vec<RawItem>, | ||
27 | } | ||
28 | |||
29 | #[derive(Debug, Default, PartialEq, Eq)] | ||
30 | pub struct ImportSourceMap { | ||
31 | map: ArenaMap<ImportId, AstPtr<ast::PathSegment>>, | ||
32 | } | ||
33 | |||
34 | impl ImportSourceMap { | ||
35 | pub(crate) fn insert(&mut self, import: ImportId, segment: &ast::PathSegment) { | ||
36 | self.map.insert(import, AstPtr::new(segment)) | ||
37 | } | ||
38 | |||
39 | pub fn get(&self, source: &ModuleSource, import: ImportId) -> TreeArc<ast::PathSegment> { | ||
40 | let file = match source { | ||
41 | ModuleSource::SourceFile(file) => &*file, | ||
42 | ModuleSource::Module(m) => m.syntax().ancestors().find_map(SourceFile::cast).unwrap(), | ||
43 | }; | ||
44 | |||
45 | self.map[import].to_node(file).to_owned() | ||
46 | } | ||
47 | } | ||
48 | |||
49 | impl RawItems { | ||
50 | pub(crate) fn raw_items_query( | ||
51 | db: &impl PersistentHirDatabase, | ||
52 | file_id: FileId, | ||
53 | ) -> Arc<RawItems> { | ||
54 | db.raw_items_with_source_map(file_id).0 | ||
55 | } | ||
56 | |||
57 | pub(crate) fn raw_items_with_source_map_query( | ||
58 | db: &impl PersistentHirDatabase, | ||
59 | file_id: FileId, | ||
60 | ) -> (Arc<RawItems>, Arc<ImportSourceMap>) { | ||
61 | let mut collector = RawItemsCollector { | ||
62 | raw_items: RawItems::default(), | ||
63 | source_file_items: db.file_items(file_id.into()), | ||
64 | source_map: ImportSourceMap::default(), | ||
65 | }; | ||
66 | let source_file = db.parse(file_id); | ||
67 | collector.process_module(None, &*source_file); | ||
68 | (Arc::new(collector.raw_items), Arc::new(collector.source_map)) | ||
69 | } | ||
70 | |||
71 | pub(crate) fn items(&self) -> &[RawItem] { | ||
72 | &self.items | ||
73 | } | ||
74 | |||
75 | // We can't use queries during name resolution for fear of cycles, so this | ||
76 | // is a query-less variant of the above function. | ||
77 | pub(crate) fn from_source_file(source_file: &SourceFile, file_id: HirFileId) -> RawItems { | ||
78 | let source_file_items = SourceFileItems::from_source_file(source_file, file_id); | ||
79 | let mut collector = RawItemsCollector { | ||
80 | raw_items: RawItems::default(), | ||
81 | source_file_items: Arc::new(source_file_items), | ||
82 | source_map: ImportSourceMap::default(), | ||
83 | }; | ||
84 | collector.process_module(None, &*source_file); | ||
85 | collector.raw_items | ||
86 | } | ||
87 | } | ||
88 | |||
89 | impl Index<Module> for RawItems { | ||
90 | type Output = ModuleData; | ||
91 | fn index(&self, idx: Module) -> &ModuleData { | ||
92 | &self.modules[idx] | ||
93 | } | ||
94 | } | ||
95 | |||
96 | impl Index<ImportId> for RawItems { | ||
97 | type Output = ImportData; | ||
98 | fn index(&self, idx: ImportId) -> &ImportData { | ||
99 | &self.imports[idx] | ||
100 | } | ||
101 | } | ||
102 | |||
103 | impl Index<Def> for RawItems { | ||
104 | type Output = DefData; | ||
105 | fn index(&self, idx: Def) -> &DefData { | ||
106 | &self.defs[idx] | ||
107 | } | ||
108 | } | ||
109 | |||
110 | impl Index<Macro> for RawItems { | ||
111 | type Output = MacroData; | ||
112 | fn index(&self, idx: Macro) -> &MacroData { | ||
113 | &self.macros[idx] | ||
114 | } | ||
115 | } | ||
116 | |||
117 | #[derive(Debug, PartialEq, Eq, Clone, Copy)] | ||
118 | pub(crate) enum RawItem { | ||
119 | Module(Module), | ||
120 | Import(ImportId), | ||
121 | Def(Def), | ||
122 | Macro(Macro), | ||
123 | } | ||
124 | |||
125 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] | ||
126 | pub(crate) struct Module(RawId); | ||
127 | impl_arena_id!(Module); | ||
128 | |||
129 | #[derive(Debug, PartialEq, Eq)] | ||
130 | pub(crate) enum ModuleData { | ||
131 | Declaration { name: Name, source_item_id: SourceFileItemId }, | ||
132 | Definition { name: Name, source_item_id: SourceFileItemId, items: Vec<RawItem> }, | ||
133 | } | ||
134 | |||
135 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] | ||
136 | pub struct ImportId(RawId); | ||
137 | impl_arena_id!(ImportId); | ||
138 | |||
139 | #[derive(Debug, Clone, PartialEq, Eq)] | ||
140 | pub struct ImportData { | ||
141 | pub(crate) path: Path, | ||
142 | pub(crate) alias: Option<Name>, | ||
143 | pub(crate) is_glob: bool, | ||
144 | pub(crate) is_prelude: bool, | ||
145 | pub(crate) is_extern_crate: bool, | ||
146 | } | ||
147 | |||
148 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] | ||
149 | pub(crate) struct Def(RawId); | ||
150 | impl_arena_id!(Def); | ||
151 | |||
152 | #[derive(Debug, PartialEq, Eq)] | ||
153 | pub(crate) struct DefData { | ||
154 | pub(crate) source_item_id: SourceFileItemId, | ||
155 | pub(crate) name: Name, | ||
156 | pub(crate) kind: DefKind, | ||
157 | } | ||
158 | |||
159 | #[derive(Debug, PartialEq, Eq, Clone, Copy)] | ||
160 | pub(crate) enum DefKind { | ||
161 | Function, | ||
162 | Struct, | ||
163 | Enum, | ||
164 | Const, | ||
165 | Static, | ||
166 | Trait, | ||
167 | TypeAlias, | ||
168 | } | ||
169 | |||
170 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] | ||
171 | pub(crate) struct Macro(RawId); | ||
172 | impl_arena_id!(Macro); | ||
173 | |||
174 | #[derive(Debug, PartialEq, Eq)] | ||
175 | pub(crate) struct MacroData { | ||
176 | pub(crate) source_item_id: SourceFileItemId, | ||
177 | pub(crate) path: Path, | ||
178 | pub(crate) name: Option<Name>, | ||
179 | pub(crate) arg: tt::Subtree, | ||
180 | pub(crate) export: bool, | ||
181 | } | ||
182 | |||
183 | struct RawItemsCollector { | ||
184 | raw_items: RawItems, | ||
185 | source_file_items: Arc<SourceFileItems>, | ||
186 | source_map: ImportSourceMap, | ||
187 | } | ||
188 | |||
189 | impl RawItemsCollector { | ||
190 | fn process_module(&mut self, current_module: Option<Module>, body: &impl ast::ModuleItemOwner) { | ||
191 | for item_or_macro in body.items_with_macros() { | ||
192 | match item_or_macro { | ||
193 | ast::ItemOrMacro::Macro(m) => self.add_macro(current_module, m), | ||
194 | ast::ItemOrMacro::Item(item) => self.add_item(current_module, item), | ||
195 | } | ||
196 | } | ||
197 | } | ||
198 | |||
199 | fn add_item(&mut self, current_module: Option<Module>, item: &ast::ModuleItem) { | ||
200 | let (kind, name) = match item.kind() { | ||
201 | ast::ModuleItemKind::Module(module) => { | ||
202 | self.add_module(current_module, module); | ||
203 | return; | ||
204 | } | ||
205 | ast::ModuleItemKind::UseItem(use_item) => { | ||
206 | self.add_use_item(current_module, use_item); | ||
207 | return; | ||
208 | } | ||
209 | ast::ModuleItemKind::ExternCrateItem(extern_crate) => { | ||
210 | self.add_extern_crate_item(current_module, extern_crate); | ||
211 | return; | ||
212 | } | ||
213 | ast::ModuleItemKind::ImplBlock(_) => { | ||
214 | // impls don't participate in name resolution | ||
215 | return; | ||
216 | } | ||
217 | ast::ModuleItemKind::StructDef(it) => (DefKind::Struct, it.name()), | ||
218 | ast::ModuleItemKind::EnumDef(it) => (DefKind::Enum, it.name()), | ||
219 | ast::ModuleItemKind::FnDef(it) => (DefKind::Function, it.name()), | ||
220 | ast::ModuleItemKind::TraitDef(it) => (DefKind::Trait, it.name()), | ||
221 | ast::ModuleItemKind::TypeAliasDef(it) => (DefKind::TypeAlias, it.name()), | ||
222 | ast::ModuleItemKind::ConstDef(it) => (DefKind::Const, it.name()), | ||
223 | ast::ModuleItemKind::StaticDef(it) => (DefKind::Static, it.name()), | ||
224 | }; | ||
225 | if let Some(name) = name { | ||
226 | let name = name.as_name(); | ||
227 | let source_item_id = self.source_file_items.id_of_unchecked(item.syntax()); | ||
228 | let def = self.raw_items.defs.alloc(DefData { name, kind, source_item_id }); | ||
229 | self.push_item(current_module, RawItem::Def(def)) | ||
230 | } | ||
231 | } | ||
232 | |||
233 | fn add_module(&mut self, current_module: Option<Module>, module: &ast::Module) { | ||
234 | let name = match module.name() { | ||
235 | Some(it) => it.as_name(), | ||
236 | None => return, | ||
237 | }; | ||
238 | let source_item_id = self.source_file_items.id_of_unchecked(module.syntax()); | ||
239 | if module.has_semi() { | ||
240 | let item = | ||
241 | self.raw_items.modules.alloc(ModuleData::Declaration { name, source_item_id }); | ||
242 | self.push_item(current_module, RawItem::Module(item)); | ||
243 | return; | ||
244 | } | ||
245 | |||
246 | if let Some(item_list) = module.item_list() { | ||
247 | let item = self.raw_items.modules.alloc(ModuleData::Definition { | ||
248 | name, | ||
249 | source_item_id, | ||
250 | items: Vec::new(), | ||
251 | }); | ||
252 | self.process_module(Some(item), item_list); | ||
253 | self.push_item(current_module, RawItem::Module(item)); | ||
254 | return; | ||
255 | } | ||
256 | tested_by!(name_res_works_for_broken_modules); | ||
257 | } | ||
258 | |||
259 | fn add_use_item(&mut self, current_module: Option<Module>, use_item: &ast::UseItem) { | ||
260 | let is_prelude = use_item.has_atom_attr("prelude_import"); | ||
261 | |||
262 | Path::expand_use_item(use_item, |path, segment, alias| { | ||
263 | let import = self.raw_items.imports.alloc(ImportData { | ||
264 | path, | ||
265 | alias, | ||
266 | is_glob: segment.is_none(), | ||
267 | is_prelude, | ||
268 | is_extern_crate: false, | ||
269 | }); | ||
270 | if let Some(segment) = segment { | ||
271 | self.source_map.insert(import, segment) | ||
272 | } | ||
273 | self.push_item(current_module, RawItem::Import(import)) | ||
274 | }) | ||
275 | } | ||
276 | |||
277 | fn add_extern_crate_item( | ||
278 | &mut self, | ||
279 | current_module: Option<Module>, | ||
280 | extern_crate: &ast::ExternCrateItem, | ||
281 | ) { | ||
282 | if let Some(name_ref) = extern_crate.name_ref() { | ||
283 | let path = Path::from_name_ref(name_ref); | ||
284 | let alias = extern_crate.alias().and_then(|a| a.name()).map(AsName::as_name); | ||
285 | let import = self.raw_items.imports.alloc(ImportData { | ||
286 | path, | ||
287 | alias, | ||
288 | is_glob: false, | ||
289 | is_prelude: false, | ||
290 | is_extern_crate: true, | ||
291 | }); | ||
292 | self.push_item(current_module, RawItem::Import(import)) | ||
293 | } | ||
294 | } | ||
295 | |||
296 | fn add_macro(&mut self, current_module: Option<Module>, m: &ast::MacroCall) { | ||
297 | let (path, arg) = match ( | ||
298 | m.path().and_then(Path::from_ast), | ||
299 | m.token_tree().and_then(mbe::ast_to_token_tree), | ||
300 | ) { | ||
301 | (Some(path), Some((token_tree, _token_map))) => (path, token_tree), | ||
302 | _ => return, | ||
303 | }; | ||
304 | |||
305 | let name = m.name().map(|it| it.as_name()); | ||
306 | let source_item_id = self.source_file_items.id_of_unchecked(m.syntax()); | ||
307 | let export = m.has_atom_attr("macro_export"); | ||
308 | let m = self.raw_items.macros.alloc(MacroData { source_item_id, path, arg, name, export }); | ||
309 | self.push_item(current_module, RawItem::Macro(m)); | ||
310 | } | ||
311 | |||
312 | fn push_item(&mut self, current_module: Option<Module>, item: RawItem) { | ||
313 | match current_module { | ||
314 | Some(module) => match &mut self.raw_items.modules[module] { | ||
315 | ModuleData::Definition { items, .. } => items, | ||
316 | ModuleData::Declaration { .. } => unreachable!(), | ||
317 | }, | ||
318 | None => &mut self.raw_items.items, | ||
319 | } | ||
320 | .push(item) | ||
321 | } | ||
322 | } | ||