diff options
Diffstat (limited to 'crates/ra_hir_def/src/nameres/raw.rs')
-rw-r--r-- | crates/ra_hir_def/src/nameres/raw.rs | 407 |
1 files changed, 407 insertions, 0 deletions
diff --git a/crates/ra_hir_def/src/nameres/raw.rs b/crates/ra_hir_def/src/nameres/raw.rs new file mode 100644 index 000000000..13b9fbf48 --- /dev/null +++ b/crates/ra_hir_def/src/nameres/raw.rs | |||
@@ -0,0 +1,407 @@ | |||
1 | //! FIXME: write short doc here | ||
2 | |||
3 | use std::{ops::Index, sync::Arc}; | ||
4 | |||
5 | use hir_expand::{ast_id_map::AstIdMap, db::AstDatabase}; | ||
6 | use ra_arena::{impl_arena_id, map::ArenaMap, Arena, RawId}; | ||
7 | use ra_syntax::{ | ||
8 | ast::{self, AttrsOwner, NameOwner}, | ||
9 | AstNode, AstPtr, SourceFile, | ||
10 | }; | ||
11 | use test_utils::tested_by; | ||
12 | |||
13 | use crate::{ | ||
14 | attr::Attr, | ||
15 | db::DefDatabase2, | ||
16 | either::Either, | ||
17 | name::{AsName, Name}, | ||
18 | path::Path, | ||
19 | FileAstId, HirFileId, ModuleSource, Source, | ||
20 | }; | ||
21 | |||
22 | /// `RawItems` is a set of top-level items in a file (except for impls). | ||
23 | /// | ||
24 | /// It is the input to name resolution algorithm. `RawItems` are not invalidated | ||
25 | /// on most edits. | ||
26 | #[derive(Debug, Default, PartialEq, Eq)] | ||
27 | pub struct RawItems { | ||
28 | modules: Arena<Module, ModuleData>, | ||
29 | imports: Arena<ImportId, ImportData>, | ||
30 | defs: Arena<Def, DefData>, | ||
31 | macros: Arena<Macro, MacroData>, | ||
32 | /// items for top-level module | ||
33 | items: Vec<RawItem>, | ||
34 | } | ||
35 | |||
36 | #[derive(Debug, Default, PartialEq, Eq)] | ||
37 | pub struct ImportSourceMap { | ||
38 | map: ArenaMap<ImportId, ImportSourcePtr>, | ||
39 | } | ||
40 | |||
41 | type ImportSourcePtr = Either<AstPtr<ast::UseTree>, AstPtr<ast::ExternCrateItem>>; | ||
42 | type ImportSource = Either<ast::UseTree, ast::ExternCrateItem>; | ||
43 | |||
44 | impl ImportSourcePtr { | ||
45 | fn to_node(self, file: &SourceFile) -> ImportSource { | ||
46 | self.map(|ptr| ptr.to_node(file.syntax()), |ptr| ptr.to_node(file.syntax())) | ||
47 | } | ||
48 | } | ||
49 | |||
50 | impl ImportSourceMap { | ||
51 | fn insert(&mut self, import: ImportId, ptr: ImportSourcePtr) { | ||
52 | self.map.insert(import, ptr) | ||
53 | } | ||
54 | |||
55 | pub fn get(&self, source: &ModuleSource, import: ImportId) -> ImportSource { | ||
56 | let file = match source { | ||
57 | ModuleSource::SourceFile(file) => file.clone(), | ||
58 | ModuleSource::Module(m) => m.syntax().ancestors().find_map(SourceFile::cast).unwrap(), | ||
59 | }; | ||
60 | |||
61 | self.map[import].to_node(&file) | ||
62 | } | ||
63 | } | ||
64 | |||
65 | impl RawItems { | ||
66 | pub(crate) fn raw_items_query( | ||
67 | db: &(impl DefDatabase2 + AstDatabase), | ||
68 | file_id: HirFileId, | ||
69 | ) -> Arc<RawItems> { | ||
70 | db.raw_items_with_source_map(file_id).0 | ||
71 | } | ||
72 | |||
73 | pub(crate) fn raw_items_with_source_map_query( | ||
74 | db: &(impl DefDatabase2 + AstDatabase), | ||
75 | file_id: HirFileId, | ||
76 | ) -> (Arc<RawItems>, Arc<ImportSourceMap>) { | ||
77 | let mut collector = RawItemsCollector { | ||
78 | raw_items: RawItems::default(), | ||
79 | source_ast_id_map: db.ast_id_map(file_id), | ||
80 | source_map: ImportSourceMap::default(), | ||
81 | file_id, | ||
82 | db, | ||
83 | }; | ||
84 | if let Some(node) = db.parse_or_expand(file_id) { | ||
85 | if let Some(source_file) = ast::SourceFile::cast(node.clone()) { | ||
86 | collector.process_module(None, source_file); | ||
87 | } else if let Some(item_list) = ast::MacroItems::cast(node) { | ||
88 | collector.process_module(None, item_list); | ||
89 | } | ||
90 | } | ||
91 | (Arc::new(collector.raw_items), Arc::new(collector.source_map)) | ||
92 | } | ||
93 | |||
94 | pub fn items(&self) -> &[RawItem] { | ||
95 | &self.items | ||
96 | } | ||
97 | } | ||
98 | |||
99 | impl Index<Module> for RawItems { | ||
100 | type Output = ModuleData; | ||
101 | fn index(&self, idx: Module) -> &ModuleData { | ||
102 | &self.modules[idx] | ||
103 | } | ||
104 | } | ||
105 | |||
106 | impl Index<ImportId> for RawItems { | ||
107 | type Output = ImportData; | ||
108 | fn index(&self, idx: ImportId) -> &ImportData { | ||
109 | &self.imports[idx] | ||
110 | } | ||
111 | } | ||
112 | |||
113 | impl Index<Def> for RawItems { | ||
114 | type Output = DefData; | ||
115 | fn index(&self, idx: Def) -> &DefData { | ||
116 | &self.defs[idx] | ||
117 | } | ||
118 | } | ||
119 | |||
120 | impl Index<Macro> for RawItems { | ||
121 | type Output = MacroData; | ||
122 | fn index(&self, idx: Macro) -> &MacroData { | ||
123 | &self.macros[idx] | ||
124 | } | ||
125 | } | ||
126 | |||
127 | // Avoid heap allocation on items without attributes. | ||
128 | type Attrs = Option<Arc<[Attr]>>; | ||
129 | |||
130 | #[derive(Debug, PartialEq, Eq, Clone)] | ||
131 | pub struct RawItem { | ||
132 | attrs: Attrs, | ||
133 | pub kind: RawItemKind, | ||
134 | } | ||
135 | |||
136 | impl RawItem { | ||
137 | pub fn attrs(&self) -> &[Attr] { | ||
138 | self.attrs.as_ref().map_or(&[], |it| &*it) | ||
139 | } | ||
140 | } | ||
141 | |||
142 | #[derive(Debug, PartialEq, Eq, Clone, Copy)] | ||
143 | pub enum RawItemKind { | ||
144 | Module(Module), | ||
145 | Import(ImportId), | ||
146 | Def(Def), | ||
147 | Macro(Macro), | ||
148 | } | ||
149 | |||
150 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] | ||
151 | pub struct Module(RawId); | ||
152 | impl_arena_id!(Module); | ||
153 | |||
154 | #[derive(Debug, PartialEq, Eq)] | ||
155 | pub enum ModuleData { | ||
156 | Declaration { name: Name, ast_id: FileAstId<ast::Module> }, | ||
157 | Definition { name: Name, ast_id: FileAstId<ast::Module>, items: Vec<RawItem> }, | ||
158 | } | ||
159 | |||
160 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] | ||
161 | pub struct ImportId(RawId); | ||
162 | impl_arena_id!(ImportId); | ||
163 | |||
164 | #[derive(Debug, Clone, PartialEq, Eq)] | ||
165 | pub struct ImportData { | ||
166 | pub path: Path, | ||
167 | pub alias: Option<Name>, | ||
168 | pub is_glob: bool, | ||
169 | pub is_prelude: bool, | ||
170 | pub is_extern_crate: bool, | ||
171 | pub is_macro_use: bool, | ||
172 | } | ||
173 | |||
174 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] | ||
175 | pub struct Def(RawId); | ||
176 | impl_arena_id!(Def); | ||
177 | |||
178 | #[derive(Debug, PartialEq, Eq)] | ||
179 | pub struct DefData { | ||
180 | pub name: Name, | ||
181 | pub kind: DefKind, | ||
182 | } | ||
183 | |||
184 | #[derive(Debug, PartialEq, Eq, Clone, Copy)] | ||
185 | pub enum DefKind { | ||
186 | Function(FileAstId<ast::FnDef>), | ||
187 | Struct(FileAstId<ast::StructDef>), | ||
188 | Union(FileAstId<ast::StructDef>), | ||
189 | Enum(FileAstId<ast::EnumDef>), | ||
190 | Const(FileAstId<ast::ConstDef>), | ||
191 | Static(FileAstId<ast::StaticDef>), | ||
192 | Trait(FileAstId<ast::TraitDef>), | ||
193 | TypeAlias(FileAstId<ast::TypeAliasDef>), | ||
194 | } | ||
195 | |||
196 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] | ||
197 | pub struct Macro(RawId); | ||
198 | impl_arena_id!(Macro); | ||
199 | |||
200 | #[derive(Debug, PartialEq, Eq)] | ||
201 | pub struct MacroData { | ||
202 | pub ast_id: FileAstId<ast::MacroCall>, | ||
203 | pub path: Path, | ||
204 | pub name: Option<Name>, | ||
205 | pub export: bool, | ||
206 | } | ||
207 | |||
208 | struct RawItemsCollector<DB> { | ||
209 | raw_items: RawItems, | ||
210 | source_ast_id_map: Arc<AstIdMap>, | ||
211 | source_map: ImportSourceMap, | ||
212 | file_id: HirFileId, | ||
213 | db: DB, | ||
214 | } | ||
215 | |||
216 | impl<DB: AstDatabase> RawItemsCollector<&DB> { | ||
217 | fn process_module(&mut self, current_module: Option<Module>, body: impl ast::ModuleItemOwner) { | ||
218 | for item_or_macro in body.items_with_macros() { | ||
219 | match item_or_macro { | ||
220 | ast::ItemOrMacro::Macro(m) => self.add_macro(current_module, m), | ||
221 | ast::ItemOrMacro::Item(item) => self.add_item(current_module, item), | ||
222 | } | ||
223 | } | ||
224 | } | ||
225 | |||
226 | fn add_item(&mut self, current_module: Option<Module>, item: ast::ModuleItem) { | ||
227 | let attrs = self.parse_attrs(&item); | ||
228 | let (kind, name) = match item { | ||
229 | ast::ModuleItem::Module(module) => { | ||
230 | self.add_module(current_module, module); | ||
231 | return; | ||
232 | } | ||
233 | ast::ModuleItem::UseItem(use_item) => { | ||
234 | self.add_use_item(current_module, use_item); | ||
235 | return; | ||
236 | } | ||
237 | ast::ModuleItem::ExternCrateItem(extern_crate) => { | ||
238 | self.add_extern_crate_item(current_module, extern_crate); | ||
239 | return; | ||
240 | } | ||
241 | ast::ModuleItem::ImplBlock(_) => { | ||
242 | // impls don't participate in name resolution | ||
243 | return; | ||
244 | } | ||
245 | ast::ModuleItem::StructDef(it) => { | ||
246 | let id = self.source_ast_id_map.ast_id(&it); | ||
247 | let name = it.name(); | ||
248 | if it.is_union() { | ||
249 | (DefKind::Union(id), name) | ||
250 | } else { | ||
251 | (DefKind::Struct(id), name) | ||
252 | } | ||
253 | } | ||
254 | ast::ModuleItem::EnumDef(it) => { | ||
255 | (DefKind::Enum(self.source_ast_id_map.ast_id(&it)), it.name()) | ||
256 | } | ||
257 | ast::ModuleItem::FnDef(it) => { | ||
258 | (DefKind::Function(self.source_ast_id_map.ast_id(&it)), it.name()) | ||
259 | } | ||
260 | ast::ModuleItem::TraitDef(it) => { | ||
261 | (DefKind::Trait(self.source_ast_id_map.ast_id(&it)), it.name()) | ||
262 | } | ||
263 | ast::ModuleItem::TypeAliasDef(it) => { | ||
264 | (DefKind::TypeAlias(self.source_ast_id_map.ast_id(&it)), it.name()) | ||
265 | } | ||
266 | ast::ModuleItem::ConstDef(it) => { | ||
267 | (DefKind::Const(self.source_ast_id_map.ast_id(&it)), it.name()) | ||
268 | } | ||
269 | ast::ModuleItem::StaticDef(it) => { | ||
270 | (DefKind::Static(self.source_ast_id_map.ast_id(&it)), it.name()) | ||
271 | } | ||
272 | }; | ||
273 | if let Some(name) = name { | ||
274 | let name = name.as_name(); | ||
275 | let def = self.raw_items.defs.alloc(DefData { name, kind }); | ||
276 | self.push_item(current_module, attrs, RawItemKind::Def(def)); | ||
277 | } | ||
278 | } | ||
279 | |||
280 | fn add_module(&mut self, current_module: Option<Module>, module: ast::Module) { | ||
281 | let name = match module.name() { | ||
282 | Some(it) => it.as_name(), | ||
283 | None => return, | ||
284 | }; | ||
285 | let attrs = self.parse_attrs(&module); | ||
286 | |||
287 | let ast_id = self.source_ast_id_map.ast_id(&module); | ||
288 | if module.has_semi() { | ||
289 | let item = self.raw_items.modules.alloc(ModuleData::Declaration { name, ast_id }); | ||
290 | self.push_item(current_module, attrs, RawItemKind::Module(item)); | ||
291 | return; | ||
292 | } | ||
293 | |||
294 | if let Some(item_list) = module.item_list() { | ||
295 | let item = self.raw_items.modules.alloc(ModuleData::Definition { | ||
296 | name, | ||
297 | ast_id, | ||
298 | items: Vec::new(), | ||
299 | }); | ||
300 | self.process_module(Some(item), item_list); | ||
301 | self.push_item(current_module, attrs, RawItemKind::Module(item)); | ||
302 | return; | ||
303 | } | ||
304 | tested_by!(name_res_works_for_broken_modules); | ||
305 | } | ||
306 | |||
307 | fn add_use_item(&mut self, current_module: Option<Module>, use_item: ast::UseItem) { | ||
308 | // FIXME: cfg_attr | ||
309 | let is_prelude = use_item.has_atom_attr("prelude_import"); | ||
310 | let attrs = self.parse_attrs(&use_item); | ||
311 | |||
312 | Path::expand_use_item( | ||
313 | Source { ast: use_item, file_id: self.file_id }, | ||
314 | self.db, | ||
315 | |path, use_tree, is_glob, alias| { | ||
316 | let import_data = ImportData { | ||
317 | path, | ||
318 | alias, | ||
319 | is_glob, | ||
320 | is_prelude, | ||
321 | is_extern_crate: false, | ||
322 | is_macro_use: false, | ||
323 | }; | ||
324 | self.push_import( | ||
325 | current_module, | ||
326 | attrs.clone(), | ||
327 | import_data, | ||
328 | Either::A(AstPtr::new(use_tree)), | ||
329 | ); | ||
330 | }, | ||
331 | ) | ||
332 | } | ||
333 | |||
334 | fn add_extern_crate_item( | ||
335 | &mut self, | ||
336 | current_module: Option<Module>, | ||
337 | extern_crate: ast::ExternCrateItem, | ||
338 | ) { | ||
339 | if let Some(name_ref) = extern_crate.name_ref() { | ||
340 | let path = Path::from_name_ref(&name_ref); | ||
341 | let alias = extern_crate.alias().and_then(|a| a.name()).map(|it| it.as_name()); | ||
342 | let attrs = self.parse_attrs(&extern_crate); | ||
343 | // FIXME: cfg_attr | ||
344 | let is_macro_use = extern_crate.has_atom_attr("macro_use"); | ||
345 | let import_data = ImportData { | ||
346 | path, | ||
347 | alias, | ||
348 | is_glob: false, | ||
349 | is_prelude: false, | ||
350 | is_extern_crate: true, | ||
351 | is_macro_use, | ||
352 | }; | ||
353 | self.push_import( | ||
354 | current_module, | ||
355 | attrs, | ||
356 | import_data, | ||
357 | Either::B(AstPtr::new(&extern_crate)), | ||
358 | ); | ||
359 | } | ||
360 | } | ||
361 | |||
362 | fn add_macro(&mut self, current_module: Option<Module>, m: ast::MacroCall) { | ||
363 | let attrs = self.parse_attrs(&m); | ||
364 | let path = match m | ||
365 | .path() | ||
366 | .and_then(|path| Path::from_src(Source { ast: path, file_id: self.file_id }, self.db)) | ||
367 | { | ||
368 | Some(it) => it, | ||
369 | _ => return, | ||
370 | }; | ||
371 | |||
372 | let name = m.name().map(|it| it.as_name()); | ||
373 | let ast_id = self.source_ast_id_map.ast_id(&m); | ||
374 | // FIXME: cfg_attr | ||
375 | let export = m.attrs().filter_map(|x| x.simple_name()).any(|name| name == "macro_export"); | ||
376 | |||
377 | let m = self.raw_items.macros.alloc(MacroData { ast_id, path, name, export }); | ||
378 | self.push_item(current_module, attrs, RawItemKind::Macro(m)); | ||
379 | } | ||
380 | |||
381 | fn push_import( | ||
382 | &mut self, | ||
383 | current_module: Option<Module>, | ||
384 | attrs: Attrs, | ||
385 | data: ImportData, | ||
386 | source: ImportSourcePtr, | ||
387 | ) { | ||
388 | let import = self.raw_items.imports.alloc(data); | ||
389 | self.source_map.insert(import, source); | ||
390 | self.push_item(current_module, attrs, RawItemKind::Import(import)) | ||
391 | } | ||
392 | |||
393 | fn push_item(&mut self, current_module: Option<Module>, attrs: Attrs, kind: RawItemKind) { | ||
394 | match current_module { | ||
395 | Some(module) => match &mut self.raw_items.modules[module] { | ||
396 | ModuleData::Definition { items, .. } => items, | ||
397 | ModuleData::Declaration { .. } => unreachable!(), | ||
398 | }, | ||
399 | None => &mut self.raw_items.items, | ||
400 | } | ||
401 | .push(RawItem { attrs, kind }) | ||
402 | } | ||
403 | |||
404 | fn parse_attrs(&self, item: &impl ast::AttrsOwner) -> Attrs { | ||
405 | Attr::from_attrs_owner(self.file_id, item, self.db) | ||
406 | } | ||
407 | } | ||