diff options
Diffstat (limited to 'crates/hir_def/src/nameres/collector.rs')
-rw-r--r-- | crates/hir_def/src/nameres/collector.rs | 1279 |
1 files changed, 1279 insertions, 0 deletions
diff --git a/crates/hir_def/src/nameres/collector.rs b/crates/hir_def/src/nameres/collector.rs new file mode 100644 index 000000000..3e99c8773 --- /dev/null +++ b/crates/hir_def/src/nameres/collector.rs | |||
@@ -0,0 +1,1279 @@ | |||
1 | //! The core of the module-level name resolution algorithm. | ||
2 | //! | ||
3 | //! `DefCollector::collect` contains the fixed-point iteration loop which | ||
4 | //! resolves imports and expands macros. | ||
5 | |||
6 | use base_db::{CrateId, FileId, ProcMacroId}; | ||
7 | use cfg::CfgOptions; | ||
8 | use hir_expand::{ | ||
9 | ast_id_map::FileAstId, | ||
10 | builtin_derive::find_builtin_derive, | ||
11 | builtin_macro::find_builtin_macro, | ||
12 | name::{name, AsName, Name}, | ||
13 | proc_macro::ProcMacroExpander, | ||
14 | HirFileId, MacroCallId, MacroDefId, MacroDefKind, | ||
15 | }; | ||
16 | use rustc_hash::FxHashMap; | ||
17 | use syntax::ast; | ||
18 | use test_utils::mark; | ||
19 | |||
20 | use crate::{ | ||
21 | attr::Attrs, | ||
22 | db::DefDatabase, | ||
23 | item_scope::{ImportType, PerNsGlobImports}, | ||
24 | item_tree::{ | ||
25 | self, FileItemTreeId, ItemTree, ItemTreeId, MacroCall, Mod, ModItem, ModKind, StructDefKind, | ||
26 | }, | ||
27 | nameres::{ | ||
28 | diagnostics::DefDiagnostic, mod_resolution::ModDir, path_resolution::ReachedFixedPoint, | ||
29 | BuiltinShadowMode, CrateDefMap, ModuleData, ModuleOrigin, ResolveMode, | ||
30 | }, | ||
31 | path::{ImportAlias, ModPath, PathKind}, | ||
32 | per_ns::PerNs, | ||
33 | visibility::{RawVisibility, Visibility}, | ||
34 | AdtId, AsMacroCall, AstId, AstIdWithPath, ConstLoc, ContainerId, EnumLoc, EnumVariantId, | ||
35 | FunctionLoc, ImplLoc, Intern, LocalModuleId, ModuleDefId, ModuleId, StaticLoc, StructLoc, | ||
36 | TraitLoc, TypeAliasLoc, UnionLoc, | ||
37 | }; | ||
38 | |||
39 | const GLOB_RECURSION_LIMIT: usize = 100; | ||
40 | const EXPANSION_DEPTH_LIMIT: usize = 128; | ||
41 | const FIXED_POINT_LIMIT: usize = 8192; | ||
42 | |||
43 | pub(super) fn collect_defs(db: &dyn DefDatabase, mut def_map: CrateDefMap) -> CrateDefMap { | ||
44 | let crate_graph = db.crate_graph(); | ||
45 | |||
46 | // populate external prelude | ||
47 | for dep in &crate_graph[def_map.krate].dependencies { | ||
48 | log::debug!("crate dep {:?} -> {:?}", dep.name, dep.crate_id); | ||
49 | let dep_def_map = db.crate_def_map(dep.crate_id); | ||
50 | def_map.extern_prelude.insert( | ||
51 | dep.as_name(), | ||
52 | ModuleId { krate: dep.crate_id, local_id: dep_def_map.root }.into(), | ||
53 | ); | ||
54 | |||
55 | // look for the prelude | ||
56 | // If the dependency defines a prelude, we overwrite an already defined | ||
57 | // prelude. This is necessary to import the "std" prelude if a crate | ||
58 | // depends on both "core" and "std". | ||
59 | if dep_def_map.prelude.is_some() { | ||
60 | def_map.prelude = dep_def_map.prelude; | ||
61 | } | ||
62 | } | ||
63 | |||
64 | let cfg_options = &crate_graph[def_map.krate].cfg_options; | ||
65 | let proc_macros = &crate_graph[def_map.krate].proc_macro; | ||
66 | let proc_macros = proc_macros | ||
67 | .iter() | ||
68 | .enumerate() | ||
69 | .map(|(idx, it)| { | ||
70 | // FIXME: a hacky way to create a Name from string. | ||
71 | let name = tt::Ident { text: it.name.clone(), id: tt::TokenId::unspecified() }; | ||
72 | (name.as_name(), ProcMacroExpander::new(def_map.krate, ProcMacroId(idx as u32))) | ||
73 | }) | ||
74 | .collect(); | ||
75 | |||
76 | let mut collector = DefCollector { | ||
77 | db, | ||
78 | def_map, | ||
79 | glob_imports: FxHashMap::default(), | ||
80 | unresolved_imports: Vec::new(), | ||
81 | resolved_imports: Vec::new(), | ||
82 | |||
83 | unexpanded_macros: Vec::new(), | ||
84 | unexpanded_attribute_macros: Vec::new(), | ||
85 | mod_dirs: FxHashMap::default(), | ||
86 | cfg_options, | ||
87 | proc_macros, | ||
88 | from_glob_import: Default::default(), | ||
89 | }; | ||
90 | collector.collect(); | ||
91 | collector.finish() | ||
92 | } | ||
93 | |||
94 | #[derive(Copy, Clone, Debug, Eq, PartialEq)] | ||
95 | enum PartialResolvedImport { | ||
96 | /// None of any namespaces is resolved | ||
97 | Unresolved, | ||
98 | /// One of namespaces is resolved | ||
99 | Indeterminate(PerNs), | ||
100 | /// All namespaces are resolved, OR it is came from other crate | ||
101 | Resolved(PerNs), | ||
102 | } | ||
103 | |||
104 | impl PartialResolvedImport { | ||
105 | fn namespaces(&self) -> PerNs { | ||
106 | match self { | ||
107 | PartialResolvedImport::Unresolved => PerNs::none(), | ||
108 | PartialResolvedImport::Indeterminate(ns) => *ns, | ||
109 | PartialResolvedImport::Resolved(ns) => *ns, | ||
110 | } | ||
111 | } | ||
112 | } | ||
113 | |||
114 | #[derive(Clone, Debug, Eq, PartialEq)] | ||
115 | struct Import { | ||
116 | pub path: ModPath, | ||
117 | pub alias: Option<ImportAlias>, | ||
118 | pub visibility: RawVisibility, | ||
119 | pub is_glob: bool, | ||
120 | pub is_prelude: bool, | ||
121 | pub is_extern_crate: bool, | ||
122 | pub is_macro_use: bool, | ||
123 | } | ||
124 | |||
125 | impl Import { | ||
126 | fn from_use(tree: &ItemTree, id: FileItemTreeId<item_tree::Import>) -> Self { | ||
127 | let it = &tree[id]; | ||
128 | let visibility = &tree[it.visibility]; | ||
129 | Self { | ||
130 | path: it.path.clone(), | ||
131 | alias: it.alias.clone(), | ||
132 | visibility: visibility.clone(), | ||
133 | is_glob: it.is_glob, | ||
134 | is_prelude: it.is_prelude, | ||
135 | is_extern_crate: false, | ||
136 | is_macro_use: false, | ||
137 | } | ||
138 | } | ||
139 | |||
140 | fn from_extern_crate(tree: &ItemTree, id: FileItemTreeId<item_tree::ExternCrate>) -> Self { | ||
141 | let it = &tree[id]; | ||
142 | let visibility = &tree[it.visibility]; | ||
143 | Self { | ||
144 | path: it.path.clone(), | ||
145 | alias: it.alias.clone(), | ||
146 | visibility: visibility.clone(), | ||
147 | is_glob: false, | ||
148 | is_prelude: false, | ||
149 | is_extern_crate: true, | ||
150 | is_macro_use: it.is_macro_use, | ||
151 | } | ||
152 | } | ||
153 | } | ||
154 | |||
155 | #[derive(Clone, Debug, Eq, PartialEq)] | ||
156 | struct ImportDirective { | ||
157 | module_id: LocalModuleId, | ||
158 | import: Import, | ||
159 | status: PartialResolvedImport, | ||
160 | } | ||
161 | |||
162 | #[derive(Clone, Debug, Eq, PartialEq)] | ||
163 | struct MacroDirective { | ||
164 | module_id: LocalModuleId, | ||
165 | ast_id: AstIdWithPath<ast::MacroCall>, | ||
166 | legacy: Option<MacroCallId>, | ||
167 | depth: usize, | ||
168 | } | ||
169 | |||
170 | #[derive(Clone, Debug, Eq, PartialEq)] | ||
171 | struct DeriveDirective { | ||
172 | module_id: LocalModuleId, | ||
173 | ast_id: AstIdWithPath<ast::Item>, | ||
174 | } | ||
175 | |||
176 | struct DefData<'a> { | ||
177 | id: ModuleDefId, | ||
178 | name: &'a Name, | ||
179 | visibility: &'a RawVisibility, | ||
180 | has_constructor: bool, | ||
181 | } | ||
182 | |||
183 | /// Walks the tree of module recursively | ||
184 | struct DefCollector<'a> { | ||
185 | db: &'a dyn DefDatabase, | ||
186 | def_map: CrateDefMap, | ||
187 | glob_imports: FxHashMap<LocalModuleId, Vec<(LocalModuleId, Visibility)>>, | ||
188 | unresolved_imports: Vec<ImportDirective>, | ||
189 | resolved_imports: Vec<ImportDirective>, | ||
190 | unexpanded_macros: Vec<MacroDirective>, | ||
191 | unexpanded_attribute_macros: Vec<DeriveDirective>, | ||
192 | mod_dirs: FxHashMap<LocalModuleId, ModDir>, | ||
193 | cfg_options: &'a CfgOptions, | ||
194 | proc_macros: Vec<(Name, ProcMacroExpander)>, | ||
195 | from_glob_import: PerNsGlobImports, | ||
196 | } | ||
197 | |||
198 | impl DefCollector<'_> { | ||
199 | fn collect(&mut self) { | ||
200 | let file_id = self.db.crate_graph()[self.def_map.krate].root_file_id; | ||
201 | let item_tree = self.db.item_tree(file_id.into()); | ||
202 | let module_id = self.def_map.root; | ||
203 | self.def_map.modules[module_id].origin = ModuleOrigin::CrateRoot { definition: file_id }; | ||
204 | ModCollector { | ||
205 | def_collector: &mut *self, | ||
206 | macro_depth: 0, | ||
207 | module_id, | ||
208 | file_id: file_id.into(), | ||
209 | item_tree: &item_tree, | ||
210 | mod_dir: ModDir::root(), | ||
211 | } | ||
212 | .collect(item_tree.top_level_items()); | ||
213 | |||
214 | // main name resolution fixed-point loop. | ||
215 | let mut i = 0; | ||
216 | loop { | ||
217 | self.db.check_canceled(); | ||
218 | self.resolve_imports(); | ||
219 | |||
220 | match self.resolve_macros() { | ||
221 | ReachedFixedPoint::Yes => break, | ||
222 | ReachedFixedPoint::No => i += 1, | ||
223 | } | ||
224 | if i == FIXED_POINT_LIMIT { | ||
225 | log::error!("name resolution is stuck"); | ||
226 | break; | ||
227 | } | ||
228 | } | ||
229 | |||
230 | // Resolve all indeterminate resolved imports again | ||
231 | // As some of the macros will expand newly import shadowing partial resolved imports | ||
232 | // FIXME: We maybe could skip this, if we handle the Indetermine imports in `resolve_imports` | ||
233 | // correctly | ||
234 | let partial_resolved = self.resolved_imports.iter().filter_map(|directive| { | ||
235 | if let PartialResolvedImport::Indeterminate(_) = directive.status { | ||
236 | let mut directive = directive.clone(); | ||
237 | directive.status = PartialResolvedImport::Unresolved; | ||
238 | Some(directive) | ||
239 | } else { | ||
240 | None | ||
241 | } | ||
242 | }); | ||
243 | self.unresolved_imports.extend(partial_resolved); | ||
244 | self.resolve_imports(); | ||
245 | |||
246 | let unresolved_imports = std::mem::replace(&mut self.unresolved_imports, Vec::new()); | ||
247 | // show unresolved imports in completion, etc | ||
248 | for directive in unresolved_imports { | ||
249 | self.record_resolved_import(&directive) | ||
250 | } | ||
251 | |||
252 | // Record proc-macros | ||
253 | self.collect_proc_macro(); | ||
254 | } | ||
255 | |||
256 | fn collect_proc_macro(&mut self) { | ||
257 | let proc_macros = std::mem::take(&mut self.proc_macros); | ||
258 | for (name, expander) in proc_macros { | ||
259 | let krate = self.def_map.krate; | ||
260 | |||
261 | let macro_id = MacroDefId { | ||
262 | ast_id: None, | ||
263 | krate: Some(krate), | ||
264 | kind: MacroDefKind::CustomDerive(expander), | ||
265 | local_inner: false, | ||
266 | }; | ||
267 | |||
268 | self.define_proc_macro(name.clone(), macro_id); | ||
269 | } | ||
270 | } | ||
271 | |||
272 | /// Define a macro with `macro_rules`. | ||
273 | /// | ||
274 | /// It will define the macro in legacy textual scope, and if it has `#[macro_export]`, | ||
275 | /// then it is also defined in the root module scope. | ||
276 | /// You can `use` or invoke it by `crate::macro_name` anywhere, before or after the definition. | ||
277 | /// | ||
278 | /// It is surprising that the macro will never be in the current module scope. | ||
279 | /// These code fails with "unresolved import/macro", | ||
280 | /// ```rust,compile_fail | ||
281 | /// mod m { macro_rules! foo { () => {} } } | ||
282 | /// use m::foo as bar; | ||
283 | /// ``` | ||
284 | /// | ||
285 | /// ```rust,compile_fail | ||
286 | /// macro_rules! foo { () => {} } | ||
287 | /// self::foo!(); | ||
288 | /// crate::foo!(); | ||
289 | /// ``` | ||
290 | /// | ||
291 | /// Well, this code compiles, because the plain path `foo` in `use` is searched | ||
292 | /// in the legacy textual scope only. | ||
293 | /// ```rust | ||
294 | /// macro_rules! foo { () => {} } | ||
295 | /// use foo as bar; | ||
296 | /// ``` | ||
297 | fn define_macro( | ||
298 | &mut self, | ||
299 | module_id: LocalModuleId, | ||
300 | name: Name, | ||
301 | macro_: MacroDefId, | ||
302 | export: bool, | ||
303 | ) { | ||
304 | // Textual scoping | ||
305 | self.define_legacy_macro(module_id, name.clone(), macro_); | ||
306 | |||
307 | // Module scoping | ||
308 | // In Rust, `#[macro_export]` macros are unconditionally visible at the | ||
309 | // crate root, even if the parent modules is **not** visible. | ||
310 | if export { | ||
311 | self.update( | ||
312 | self.def_map.root, | ||
313 | &[(Some(name), PerNs::macros(macro_, Visibility::Public))], | ||
314 | Visibility::Public, | ||
315 | ImportType::Named, | ||
316 | ); | ||
317 | } | ||
318 | } | ||
319 | |||
320 | /// Define a legacy textual scoped macro in module | ||
321 | /// | ||
322 | /// We use a map `legacy_macros` to store all legacy textual scoped macros visible per module. | ||
323 | /// It will clone all macros from parent legacy scope, whose definition is prior to | ||
324 | /// the definition of current module. | ||
325 | /// And also, `macro_use` on a module will import all legacy macros visible inside to | ||
326 | /// current legacy scope, with possible shadowing. | ||
327 | fn define_legacy_macro(&mut self, module_id: LocalModuleId, name: Name, mac: MacroDefId) { | ||
328 | // Always shadowing | ||
329 | self.def_map.modules[module_id].scope.define_legacy_macro(name, mac); | ||
330 | } | ||
331 | |||
332 | /// Define a proc macro | ||
333 | /// | ||
334 | /// A proc macro is similar to normal macro scope, but it would not visiable in legacy textual scoped. | ||
335 | /// And unconditionally exported. | ||
336 | fn define_proc_macro(&mut self, name: Name, macro_: MacroDefId) { | ||
337 | self.update( | ||
338 | self.def_map.root, | ||
339 | &[(Some(name), PerNs::macros(macro_, Visibility::Public))], | ||
340 | Visibility::Public, | ||
341 | ImportType::Named, | ||
342 | ); | ||
343 | } | ||
344 | |||
345 | /// Import macros from `#[macro_use] extern crate`. | ||
346 | fn import_macros_from_extern_crate( | ||
347 | &mut self, | ||
348 | current_module_id: LocalModuleId, | ||
349 | import: &item_tree::ExternCrate, | ||
350 | ) { | ||
351 | log::debug!( | ||
352 | "importing macros from extern crate: {:?} ({:?})", | ||
353 | import, | ||
354 | self.def_map.edition, | ||
355 | ); | ||
356 | |||
357 | let res = self.def_map.resolve_name_in_extern_prelude( | ||
358 | &import | ||
359 | .path | ||
360 | .as_ident() | ||
361 | .expect("extern crate should have been desugared to one-element path"), | ||
362 | ); | ||
363 | |||
364 | if let Some(ModuleDefId::ModuleId(m)) = res.take_types() { | ||
365 | mark::hit!(macro_rules_from_other_crates_are_visible_with_macro_use); | ||
366 | self.import_all_macros_exported(current_module_id, m.krate); | ||
367 | } | ||
368 | } | ||
369 | |||
370 | /// Import all exported macros from another crate | ||
371 | /// | ||
372 | /// Exported macros are just all macros in the root module scope. | ||
373 | /// Note that it contains not only all `#[macro_export]` macros, but also all aliases | ||
374 | /// created by `use` in the root module, ignoring the visibility of `use`. | ||
375 | fn import_all_macros_exported(&mut self, current_module_id: LocalModuleId, krate: CrateId) { | ||
376 | let def_map = self.db.crate_def_map(krate); | ||
377 | for (name, def) in def_map[def_map.root].scope.macros() { | ||
378 | // `macro_use` only bring things into legacy scope. | ||
379 | self.define_legacy_macro(current_module_id, name.clone(), def); | ||
380 | } | ||
381 | } | ||
382 | |||
383 | /// Import resolution | ||
384 | /// | ||
385 | /// This is a fix point algorithm. We resolve imports until no forward | ||
386 | /// progress in resolving imports is made | ||
387 | fn resolve_imports(&mut self) { | ||
388 | let mut n_previous_unresolved = self.unresolved_imports.len() + 1; | ||
389 | |||
390 | while self.unresolved_imports.len() < n_previous_unresolved { | ||
391 | n_previous_unresolved = self.unresolved_imports.len(); | ||
392 | let imports = std::mem::replace(&mut self.unresolved_imports, Vec::new()); | ||
393 | for mut directive in imports { | ||
394 | directive.status = self.resolve_import(directive.module_id, &directive.import); | ||
395 | match directive.status { | ||
396 | PartialResolvedImport::Indeterminate(_) => { | ||
397 | self.record_resolved_import(&directive); | ||
398 | // FIXME: For avoid performance regression, | ||
399 | // we consider an imported resolved if it is indeterminate (i.e not all namespace resolved) | ||
400 | self.resolved_imports.push(directive) | ||
401 | } | ||
402 | PartialResolvedImport::Resolved(_) => { | ||
403 | self.record_resolved_import(&directive); | ||
404 | self.resolved_imports.push(directive) | ||
405 | } | ||
406 | PartialResolvedImport::Unresolved => { | ||
407 | self.unresolved_imports.push(directive); | ||
408 | } | ||
409 | } | ||
410 | } | ||
411 | } | ||
412 | } | ||
413 | |||
414 | fn resolve_import(&self, module_id: LocalModuleId, import: &Import) -> PartialResolvedImport { | ||
415 | log::debug!("resolving import: {:?} ({:?})", import, self.def_map.edition); | ||
416 | if import.is_extern_crate { | ||
417 | let res = self.def_map.resolve_name_in_extern_prelude( | ||
418 | &import | ||
419 | .path | ||
420 | .as_ident() | ||
421 | .expect("extern crate should have been desugared to one-element path"), | ||
422 | ); | ||
423 | PartialResolvedImport::Resolved(res) | ||
424 | } else { | ||
425 | let res = self.def_map.resolve_path_fp_with_macro( | ||
426 | self.db, | ||
427 | ResolveMode::Import, | ||
428 | module_id, | ||
429 | &import.path, | ||
430 | BuiltinShadowMode::Module, | ||
431 | ); | ||
432 | |||
433 | let def = res.resolved_def; | ||
434 | if res.reached_fixedpoint == ReachedFixedPoint::No || def.is_none() { | ||
435 | return PartialResolvedImport::Unresolved; | ||
436 | } | ||
437 | |||
438 | if let Some(krate) = res.krate { | ||
439 | if krate != self.def_map.krate { | ||
440 | return PartialResolvedImport::Resolved(def); | ||
441 | } | ||
442 | } | ||
443 | |||
444 | // Check whether all namespace is resolved | ||
445 | if def.take_types().is_some() | ||
446 | && def.take_values().is_some() | ||
447 | && def.take_macros().is_some() | ||
448 | { | ||
449 | PartialResolvedImport::Resolved(def) | ||
450 | } else { | ||
451 | PartialResolvedImport::Indeterminate(def) | ||
452 | } | ||
453 | } | ||
454 | } | ||
455 | |||
456 | fn record_resolved_import(&mut self, directive: &ImportDirective) { | ||
457 | let module_id = directive.module_id; | ||
458 | let import = &directive.import; | ||
459 | let def = directive.status.namespaces(); | ||
460 | let vis = self | ||
461 | .def_map | ||
462 | .resolve_visibility(self.db, module_id, &directive.import.visibility) | ||
463 | .unwrap_or(Visibility::Public); | ||
464 | |||
465 | if import.is_glob { | ||
466 | log::debug!("glob import: {:?}", import); | ||
467 | match def.take_types() { | ||
468 | Some(ModuleDefId::ModuleId(m)) => { | ||
469 | if import.is_prelude { | ||
470 | mark::hit!(std_prelude); | ||
471 | self.def_map.prelude = Some(m); | ||
472 | } else if m.krate != self.def_map.krate { | ||
473 | mark::hit!(glob_across_crates); | ||
474 | // glob import from other crate => we can just import everything once | ||
475 | let item_map = self.db.crate_def_map(m.krate); | ||
476 | let scope = &item_map[m.local_id].scope; | ||
477 | |||
478 | // Module scoped macros is included | ||
479 | let items = scope | ||
480 | .resolutions() | ||
481 | // only keep visible names... | ||
482 | .map(|(n, res)| { | ||
483 | (n, res.filter_visibility(|v| v.is_visible_from_other_crate())) | ||
484 | }) | ||
485 | .filter(|(_, res)| !res.is_none()) | ||
486 | .collect::<Vec<_>>(); | ||
487 | |||
488 | self.update(module_id, &items, vis, ImportType::Glob); | ||
489 | } else { | ||
490 | // glob import from same crate => we do an initial | ||
491 | // import, and then need to propagate any further | ||
492 | // additions | ||
493 | let scope = &self.def_map[m.local_id].scope; | ||
494 | |||
495 | // Module scoped macros is included | ||
496 | let items = scope | ||
497 | .resolutions() | ||
498 | // only keep visible names... | ||
499 | .map(|(n, res)| { | ||
500 | ( | ||
501 | n, | ||
502 | res.filter_visibility(|v| { | ||
503 | v.is_visible_from_def_map(&self.def_map, module_id) | ||
504 | }), | ||
505 | ) | ||
506 | }) | ||
507 | .filter(|(_, res)| !res.is_none()) | ||
508 | .collect::<Vec<_>>(); | ||
509 | |||
510 | self.update(module_id, &items, vis, ImportType::Glob); | ||
511 | // record the glob import in case we add further items | ||
512 | let glob = self.glob_imports.entry(m.local_id).or_default(); | ||
513 | if !glob.iter().any(|(mid, _)| *mid == module_id) { | ||
514 | glob.push((module_id, vis)); | ||
515 | } | ||
516 | } | ||
517 | } | ||
518 | Some(ModuleDefId::AdtId(AdtId::EnumId(e))) => { | ||
519 | mark::hit!(glob_enum); | ||
520 | // glob import from enum => just import all the variants | ||
521 | |||
522 | // XXX: urgh, so this works by accident! Here, we look at | ||
523 | // the enum data, and, in theory, this might require us to | ||
524 | // look back at the crate_def_map, creating a cycle. For | ||
525 | // example, `enum E { crate::some_macro!(); }`. Luckely, the | ||
526 | // only kind of macro that is allowed inside enum is a | ||
527 | // `cfg_macro`, and we don't need to run name resolution for | ||
528 | // it, but this is sheer luck! | ||
529 | let enum_data = self.db.enum_data(e); | ||
530 | let resolutions = enum_data | ||
531 | .variants | ||
532 | .iter() | ||
533 | .map(|(local_id, variant_data)| { | ||
534 | let name = variant_data.name.clone(); | ||
535 | let variant = EnumVariantId { parent: e, local_id }; | ||
536 | let res = PerNs::both(variant.into(), variant.into(), vis); | ||
537 | (Some(name), res) | ||
538 | }) | ||
539 | .collect::<Vec<_>>(); | ||
540 | self.update(module_id, &resolutions, vis, ImportType::Glob); | ||
541 | } | ||
542 | Some(d) => { | ||
543 | log::debug!("glob import {:?} from non-module/enum {:?}", import, d); | ||
544 | } | ||
545 | None => { | ||
546 | log::debug!("glob import {:?} didn't resolve as type", import); | ||
547 | } | ||
548 | } | ||
549 | } else { | ||
550 | match import.path.segments.last() { | ||
551 | Some(last_segment) => { | ||
552 | let name = match &import.alias { | ||
553 | Some(ImportAlias::Alias(name)) => Some(name.clone()), | ||
554 | Some(ImportAlias::Underscore) => None, | ||
555 | None => Some(last_segment.clone()), | ||
556 | }; | ||
557 | log::debug!("resolved import {:?} ({:?}) to {:?}", name, import, def); | ||
558 | |||
559 | // extern crates in the crate root are special-cased to insert entries into the extern prelude: rust-lang/rust#54658 | ||
560 | if import.is_extern_crate && module_id == self.def_map.root { | ||
561 | if let (Some(def), Some(name)) = (def.take_types(), name.as_ref()) { | ||
562 | self.def_map.extern_prelude.insert(name.clone(), def); | ||
563 | } | ||
564 | } | ||
565 | |||
566 | self.update(module_id, &[(name, def)], vis, ImportType::Named); | ||
567 | } | ||
568 | None => mark::hit!(bogus_paths), | ||
569 | } | ||
570 | } | ||
571 | } | ||
572 | |||
573 | fn update( | ||
574 | &mut self, | ||
575 | module_id: LocalModuleId, | ||
576 | resolutions: &[(Option<Name>, PerNs)], | ||
577 | vis: Visibility, | ||
578 | import_type: ImportType, | ||
579 | ) { | ||
580 | self.db.check_canceled(); | ||
581 | self.update_recursive(module_id, resolutions, vis, import_type, 0) | ||
582 | } | ||
583 | |||
584 | fn update_recursive( | ||
585 | &mut self, | ||
586 | module_id: LocalModuleId, | ||
587 | resolutions: &[(Option<Name>, PerNs)], | ||
588 | // All resolutions are imported with this visibility; the visibilies in | ||
589 | // the `PerNs` values are ignored and overwritten | ||
590 | vis: Visibility, | ||
591 | import_type: ImportType, | ||
592 | depth: usize, | ||
593 | ) { | ||
594 | if depth > GLOB_RECURSION_LIMIT { | ||
595 | // prevent stack overflows (but this shouldn't be possible) | ||
596 | panic!("infinite recursion in glob imports!"); | ||
597 | } | ||
598 | let mut changed = false; | ||
599 | |||
600 | for (name, res) in resolutions { | ||
601 | match name { | ||
602 | Some(name) => { | ||
603 | let scope = &mut self.def_map.modules[module_id].scope; | ||
604 | changed |= scope.push_res_with_import( | ||
605 | &mut self.from_glob_import, | ||
606 | (module_id, name.clone()), | ||
607 | res.with_visibility(vis), | ||
608 | import_type, | ||
609 | ); | ||
610 | } | ||
611 | None => { | ||
612 | let tr = match res.take_types() { | ||
613 | Some(ModuleDefId::TraitId(tr)) => tr, | ||
614 | Some(other) => { | ||
615 | log::debug!("non-trait `_` import of {:?}", other); | ||
616 | continue; | ||
617 | } | ||
618 | None => continue, | ||
619 | }; | ||
620 | let old_vis = self.def_map.modules[module_id].scope.unnamed_trait_vis(tr); | ||
621 | let should_update = match old_vis { | ||
622 | None => true, | ||
623 | Some(old_vis) => { | ||
624 | let max_vis = old_vis.max(vis, &self.def_map).unwrap_or_else(|| { | ||
625 | panic!("`Tr as _` imports with unrelated visibilities {:?} and {:?} (trait {:?})", old_vis, vis, tr); | ||
626 | }); | ||
627 | |||
628 | if max_vis == old_vis { | ||
629 | false | ||
630 | } else { | ||
631 | mark::hit!(upgrade_underscore_visibility); | ||
632 | true | ||
633 | } | ||
634 | } | ||
635 | }; | ||
636 | |||
637 | if should_update { | ||
638 | changed = true; | ||
639 | self.def_map.modules[module_id].scope.push_unnamed_trait(tr, vis); | ||
640 | } | ||
641 | } | ||
642 | } | ||
643 | } | ||
644 | |||
645 | if !changed { | ||
646 | return; | ||
647 | } | ||
648 | let glob_imports = self | ||
649 | .glob_imports | ||
650 | .get(&module_id) | ||
651 | .into_iter() | ||
652 | .flat_map(|v| v.iter()) | ||
653 | .filter(|(glob_importing_module, _)| { | ||
654 | // we know all resolutions have the same visibility (`vis`), so we | ||
655 | // just need to check that once | ||
656 | vis.is_visible_from_def_map(&self.def_map, *glob_importing_module) | ||
657 | }) | ||
658 | .cloned() | ||
659 | .collect::<Vec<_>>(); | ||
660 | |||
661 | for (glob_importing_module, glob_import_vis) in glob_imports { | ||
662 | self.update_recursive( | ||
663 | glob_importing_module, | ||
664 | resolutions, | ||
665 | glob_import_vis, | ||
666 | ImportType::Glob, | ||
667 | depth + 1, | ||
668 | ); | ||
669 | } | ||
670 | } | ||
671 | |||
672 | fn resolve_macros(&mut self) -> ReachedFixedPoint { | ||
673 | let mut macros = std::mem::replace(&mut self.unexpanded_macros, Vec::new()); | ||
674 | let mut attribute_macros = | ||
675 | std::mem::replace(&mut self.unexpanded_attribute_macros, Vec::new()); | ||
676 | let mut resolved = Vec::new(); | ||
677 | let mut res = ReachedFixedPoint::Yes; | ||
678 | macros.retain(|directive| { | ||
679 | if let Some(call_id) = directive.legacy { | ||
680 | res = ReachedFixedPoint::No; | ||
681 | resolved.push((directive.module_id, call_id, directive.depth)); | ||
682 | return false; | ||
683 | } | ||
684 | |||
685 | if let Some(call_id) = | ||
686 | directive.ast_id.as_call_id(self.db, self.def_map.krate, |path| { | ||
687 | let resolved_res = self.def_map.resolve_path_fp_with_macro( | ||
688 | self.db, | ||
689 | ResolveMode::Other, | ||
690 | directive.module_id, | ||
691 | &path, | ||
692 | BuiltinShadowMode::Module, | ||
693 | ); | ||
694 | resolved_res.resolved_def.take_macros() | ||
695 | }) | ||
696 | { | ||
697 | resolved.push((directive.module_id, call_id, directive.depth)); | ||
698 | res = ReachedFixedPoint::No; | ||
699 | return false; | ||
700 | } | ||
701 | |||
702 | true | ||
703 | }); | ||
704 | attribute_macros.retain(|directive| { | ||
705 | if let Some(call_id) = | ||
706 | directive.ast_id.as_call_id(self.db, self.def_map.krate, |path| { | ||
707 | self.resolve_attribute_macro(&directive, &path) | ||
708 | }) | ||
709 | { | ||
710 | resolved.push((directive.module_id, call_id, 0)); | ||
711 | res = ReachedFixedPoint::No; | ||
712 | return false; | ||
713 | } | ||
714 | |||
715 | true | ||
716 | }); | ||
717 | |||
718 | self.unexpanded_macros = macros; | ||
719 | self.unexpanded_attribute_macros = attribute_macros; | ||
720 | |||
721 | for (module_id, macro_call_id, depth) in resolved { | ||
722 | self.collect_macro_expansion(module_id, macro_call_id, depth); | ||
723 | } | ||
724 | |||
725 | res | ||
726 | } | ||
727 | |||
728 | fn resolve_attribute_macro( | ||
729 | &self, | ||
730 | directive: &DeriveDirective, | ||
731 | path: &ModPath, | ||
732 | ) -> Option<MacroDefId> { | ||
733 | if let Some(name) = path.as_ident() { | ||
734 | // FIXME this should actually be handled with the normal name | ||
735 | // resolution; the std lib defines built-in stubs for the derives, | ||
736 | // but these are new-style `macro`s, which we don't support yet | ||
737 | if let Some(def_id) = find_builtin_derive(name) { | ||
738 | return Some(def_id); | ||
739 | } | ||
740 | } | ||
741 | let resolved_res = self.def_map.resolve_path_fp_with_macro( | ||
742 | self.db, | ||
743 | ResolveMode::Other, | ||
744 | directive.module_id, | ||
745 | &path, | ||
746 | BuiltinShadowMode::Module, | ||
747 | ); | ||
748 | |||
749 | resolved_res.resolved_def.take_macros() | ||
750 | } | ||
751 | |||
752 | fn collect_macro_expansion( | ||
753 | &mut self, | ||
754 | module_id: LocalModuleId, | ||
755 | macro_call_id: MacroCallId, | ||
756 | depth: usize, | ||
757 | ) { | ||
758 | if depth > EXPANSION_DEPTH_LIMIT { | ||
759 | mark::hit!(macro_expansion_overflow); | ||
760 | log::warn!("macro expansion is too deep"); | ||
761 | return; | ||
762 | } | ||
763 | let file_id: HirFileId = macro_call_id.as_file(); | ||
764 | let item_tree = self.db.item_tree(file_id); | ||
765 | let mod_dir = self.mod_dirs[&module_id].clone(); | ||
766 | ModCollector { | ||
767 | def_collector: &mut *self, | ||
768 | macro_depth: depth, | ||
769 | file_id, | ||
770 | module_id, | ||
771 | item_tree: &item_tree, | ||
772 | mod_dir, | ||
773 | } | ||
774 | .collect(item_tree.top_level_items()); | ||
775 | } | ||
776 | |||
777 | fn finish(self) -> CrateDefMap { | ||
778 | self.def_map | ||
779 | } | ||
780 | } | ||
781 | |||
782 | /// Walks a single module, populating defs, imports and macros | ||
783 | struct ModCollector<'a, 'b> { | ||
784 | def_collector: &'a mut DefCollector<'b>, | ||
785 | macro_depth: usize, | ||
786 | module_id: LocalModuleId, | ||
787 | file_id: HirFileId, | ||
788 | item_tree: &'a ItemTree, | ||
789 | mod_dir: ModDir, | ||
790 | } | ||
791 | |||
792 | impl ModCollector<'_, '_> { | ||
793 | fn collect(&mut self, items: &[ModItem]) { | ||
794 | // Note: don't assert that inserted value is fresh: it's simply not true | ||
795 | // for macros. | ||
796 | self.def_collector.mod_dirs.insert(self.module_id, self.mod_dir.clone()); | ||
797 | |||
798 | // Prelude module is always considered to be `#[macro_use]`. | ||
799 | if let Some(prelude_module) = self.def_collector.def_map.prelude { | ||
800 | if prelude_module.krate != self.def_collector.def_map.krate { | ||
801 | mark::hit!(prelude_is_macro_use); | ||
802 | self.def_collector.import_all_macros_exported(self.module_id, prelude_module.krate); | ||
803 | } | ||
804 | } | ||
805 | |||
806 | // This should be processed eagerly instead of deferred to resolving. | ||
807 | // `#[macro_use] extern crate` is hoisted to imports macros before collecting | ||
808 | // any other items. | ||
809 | for item in items { | ||
810 | if self.is_cfg_enabled(self.item_tree.attrs((*item).into())) { | ||
811 | if let ModItem::ExternCrate(id) = item { | ||
812 | let import = self.item_tree[*id].clone(); | ||
813 | if import.is_macro_use { | ||
814 | self.def_collector.import_macros_from_extern_crate(self.module_id, &import); | ||
815 | } | ||
816 | } | ||
817 | } | ||
818 | } | ||
819 | |||
820 | for &item in items { | ||
821 | let attrs = self.item_tree.attrs(item.into()); | ||
822 | if self.is_cfg_enabled(attrs) { | ||
823 | let module = | ||
824 | ModuleId { krate: self.def_collector.def_map.krate, local_id: self.module_id }; | ||
825 | let container = ContainerId::ModuleId(module); | ||
826 | |||
827 | let mut def = None; | ||
828 | match item { | ||
829 | ModItem::Mod(m) => self.collect_module(&self.item_tree[m], attrs), | ||
830 | ModItem::Import(import_id) => { | ||
831 | self.def_collector.unresolved_imports.push(ImportDirective { | ||
832 | module_id: self.module_id, | ||
833 | import: Import::from_use(&self.item_tree, import_id), | ||
834 | status: PartialResolvedImport::Unresolved, | ||
835 | }) | ||
836 | } | ||
837 | ModItem::ExternCrate(import_id) => { | ||
838 | self.def_collector.unresolved_imports.push(ImportDirective { | ||
839 | module_id: self.module_id, | ||
840 | import: Import::from_extern_crate(&self.item_tree, import_id), | ||
841 | status: PartialResolvedImport::Unresolved, | ||
842 | }) | ||
843 | } | ||
844 | ModItem::MacroCall(mac) => self.collect_macro(&self.item_tree[mac]), | ||
845 | ModItem::Impl(imp) => { | ||
846 | let module = ModuleId { | ||
847 | krate: self.def_collector.def_map.krate, | ||
848 | local_id: self.module_id, | ||
849 | }; | ||
850 | let container = ContainerId::ModuleId(module); | ||
851 | let impl_id = ImplLoc { container, id: ItemTreeId::new(self.file_id, imp) } | ||
852 | .intern(self.def_collector.db); | ||
853 | self.def_collector.def_map.modules[self.module_id] | ||
854 | .scope | ||
855 | .define_impl(impl_id) | ||
856 | } | ||
857 | ModItem::Function(id) => { | ||
858 | let func = &self.item_tree[id]; | ||
859 | def = Some(DefData { | ||
860 | id: FunctionLoc { | ||
861 | container: container.into(), | ||
862 | id: ItemTreeId::new(self.file_id, id), | ||
863 | } | ||
864 | .intern(self.def_collector.db) | ||
865 | .into(), | ||
866 | name: &func.name, | ||
867 | visibility: &self.item_tree[func.visibility], | ||
868 | has_constructor: false, | ||
869 | }); | ||
870 | } | ||
871 | ModItem::Struct(id) => { | ||
872 | let it = &self.item_tree[id]; | ||
873 | |||
874 | // FIXME: check attrs to see if this is an attribute macro invocation; | ||
875 | // in which case we don't add the invocation, just a single attribute | ||
876 | // macro invocation | ||
877 | self.collect_derives(attrs, it.ast_id.upcast()); | ||
878 | |||
879 | def = Some(DefData { | ||
880 | id: StructLoc { container, id: ItemTreeId::new(self.file_id, id) } | ||
881 | .intern(self.def_collector.db) | ||
882 | .into(), | ||
883 | name: &it.name, | ||
884 | visibility: &self.item_tree[it.visibility], | ||
885 | has_constructor: it.kind != StructDefKind::Record, | ||
886 | }); | ||
887 | } | ||
888 | ModItem::Union(id) => { | ||
889 | let it = &self.item_tree[id]; | ||
890 | |||
891 | // FIXME: check attrs to see if this is an attribute macro invocation; | ||
892 | // in which case we don't add the invocation, just a single attribute | ||
893 | // macro invocation | ||
894 | self.collect_derives(attrs, it.ast_id.upcast()); | ||
895 | |||
896 | def = Some(DefData { | ||
897 | id: UnionLoc { container, id: ItemTreeId::new(self.file_id, id) } | ||
898 | .intern(self.def_collector.db) | ||
899 | .into(), | ||
900 | name: &it.name, | ||
901 | visibility: &self.item_tree[it.visibility], | ||
902 | has_constructor: false, | ||
903 | }); | ||
904 | } | ||
905 | ModItem::Enum(id) => { | ||
906 | let it = &self.item_tree[id]; | ||
907 | |||
908 | // FIXME: check attrs to see if this is an attribute macro invocation; | ||
909 | // in which case we don't add the invocation, just a single attribute | ||
910 | // macro invocation | ||
911 | self.collect_derives(attrs, it.ast_id.upcast()); | ||
912 | |||
913 | def = Some(DefData { | ||
914 | id: EnumLoc { container, id: ItemTreeId::new(self.file_id, id) } | ||
915 | .intern(self.def_collector.db) | ||
916 | .into(), | ||
917 | name: &it.name, | ||
918 | visibility: &self.item_tree[it.visibility], | ||
919 | has_constructor: false, | ||
920 | }); | ||
921 | } | ||
922 | ModItem::Const(id) => { | ||
923 | let it = &self.item_tree[id]; | ||
924 | |||
925 | if let Some(name) = &it.name { | ||
926 | def = Some(DefData { | ||
927 | id: ConstLoc { | ||
928 | container: container.into(), | ||
929 | id: ItemTreeId::new(self.file_id, id), | ||
930 | } | ||
931 | .intern(self.def_collector.db) | ||
932 | .into(), | ||
933 | name, | ||
934 | visibility: &self.item_tree[it.visibility], | ||
935 | has_constructor: false, | ||
936 | }); | ||
937 | } | ||
938 | } | ||
939 | ModItem::Static(id) => { | ||
940 | let it = &self.item_tree[id]; | ||
941 | |||
942 | def = Some(DefData { | ||
943 | id: StaticLoc { container, id: ItemTreeId::new(self.file_id, id) } | ||
944 | .intern(self.def_collector.db) | ||
945 | .into(), | ||
946 | name: &it.name, | ||
947 | visibility: &self.item_tree[it.visibility], | ||
948 | has_constructor: false, | ||
949 | }); | ||
950 | } | ||
951 | ModItem::Trait(id) => { | ||
952 | let it = &self.item_tree[id]; | ||
953 | |||
954 | def = Some(DefData { | ||
955 | id: TraitLoc { container, id: ItemTreeId::new(self.file_id, id) } | ||
956 | .intern(self.def_collector.db) | ||
957 | .into(), | ||
958 | name: &it.name, | ||
959 | visibility: &self.item_tree[it.visibility], | ||
960 | has_constructor: false, | ||
961 | }); | ||
962 | } | ||
963 | ModItem::TypeAlias(id) => { | ||
964 | let it = &self.item_tree[id]; | ||
965 | |||
966 | def = Some(DefData { | ||
967 | id: TypeAliasLoc { | ||
968 | container: container.into(), | ||
969 | id: ItemTreeId::new(self.file_id, id), | ||
970 | } | ||
971 | .intern(self.def_collector.db) | ||
972 | .into(), | ||
973 | name: &it.name, | ||
974 | visibility: &self.item_tree[it.visibility], | ||
975 | has_constructor: false, | ||
976 | }); | ||
977 | } | ||
978 | } | ||
979 | |||
980 | if let Some(DefData { id, name, visibility, has_constructor }) = def { | ||
981 | self.def_collector.def_map.modules[self.module_id].scope.define_def(id); | ||
982 | let vis = self | ||
983 | .def_collector | ||
984 | .def_map | ||
985 | .resolve_visibility(self.def_collector.db, self.module_id, visibility) | ||
986 | .unwrap_or(Visibility::Public); | ||
987 | self.def_collector.update( | ||
988 | self.module_id, | ||
989 | &[(Some(name.clone()), PerNs::from_def(id, vis, has_constructor))], | ||
990 | vis, | ||
991 | ImportType::Named, | ||
992 | ) | ||
993 | } | ||
994 | } | ||
995 | } | ||
996 | } | ||
997 | |||
998 | fn collect_module(&mut self, module: &Mod, attrs: &Attrs) { | ||
999 | let path_attr = attrs.by_key("path").string_value(); | ||
1000 | let is_macro_use = attrs.by_key("macro_use").exists(); | ||
1001 | match &module.kind { | ||
1002 | // inline module, just recurse | ||
1003 | ModKind::Inline { items } => { | ||
1004 | let module_id = self.push_child_module( | ||
1005 | module.name.clone(), | ||
1006 | AstId::new(self.file_id, module.ast_id), | ||
1007 | None, | ||
1008 | &self.item_tree[module.visibility], | ||
1009 | ); | ||
1010 | |||
1011 | ModCollector { | ||
1012 | def_collector: &mut *self.def_collector, | ||
1013 | macro_depth: self.macro_depth, | ||
1014 | module_id, | ||
1015 | file_id: self.file_id, | ||
1016 | item_tree: self.item_tree, | ||
1017 | mod_dir: self.mod_dir.descend_into_definition(&module.name, path_attr), | ||
1018 | } | ||
1019 | .collect(&*items); | ||
1020 | if is_macro_use { | ||
1021 | self.import_all_legacy_macros(module_id); | ||
1022 | } | ||
1023 | } | ||
1024 | // out of line module, resolve, parse and recurse | ||
1025 | ModKind::Outline {} => { | ||
1026 | let ast_id = AstId::new(self.file_id, module.ast_id); | ||
1027 | match self.mod_dir.resolve_declaration( | ||
1028 | self.def_collector.db, | ||
1029 | self.file_id, | ||
1030 | &module.name, | ||
1031 | path_attr, | ||
1032 | ) { | ||
1033 | Ok((file_id, is_mod_rs, mod_dir)) => { | ||
1034 | let module_id = self.push_child_module( | ||
1035 | module.name.clone(), | ||
1036 | ast_id, | ||
1037 | Some((file_id, is_mod_rs)), | ||
1038 | &self.item_tree[module.visibility], | ||
1039 | ); | ||
1040 | let item_tree = self.def_collector.db.item_tree(file_id.into()); | ||
1041 | ModCollector { | ||
1042 | def_collector: &mut *self.def_collector, | ||
1043 | macro_depth: self.macro_depth, | ||
1044 | module_id, | ||
1045 | file_id: file_id.into(), | ||
1046 | item_tree: &item_tree, | ||
1047 | mod_dir, | ||
1048 | } | ||
1049 | .collect(item_tree.top_level_items()); | ||
1050 | if is_macro_use { | ||
1051 | self.import_all_legacy_macros(module_id); | ||
1052 | } | ||
1053 | } | ||
1054 | Err(candidate) => self.def_collector.def_map.diagnostics.push( | ||
1055 | DefDiagnostic::UnresolvedModule { | ||
1056 | module: self.module_id, | ||
1057 | declaration: ast_id, | ||
1058 | candidate, | ||
1059 | }, | ||
1060 | ), | ||
1061 | }; | ||
1062 | } | ||
1063 | } | ||
1064 | } | ||
1065 | |||
1066 | fn push_child_module( | ||
1067 | &mut self, | ||
1068 | name: Name, | ||
1069 | declaration: AstId<ast::Module>, | ||
1070 | definition: Option<(FileId, bool)>, | ||
1071 | visibility: &crate::visibility::RawVisibility, | ||
1072 | ) -> LocalModuleId { | ||
1073 | let vis = self | ||
1074 | .def_collector | ||
1075 | .def_map | ||
1076 | .resolve_visibility(self.def_collector.db, self.module_id, visibility) | ||
1077 | .unwrap_or(Visibility::Public); | ||
1078 | let modules = &mut self.def_collector.def_map.modules; | ||
1079 | let res = modules.alloc(ModuleData::default()); | ||
1080 | modules[res].parent = Some(self.module_id); | ||
1081 | modules[res].origin = match definition { | ||
1082 | None => ModuleOrigin::Inline { definition: declaration }, | ||
1083 | Some((definition, is_mod_rs)) => { | ||
1084 | ModuleOrigin::File { declaration, definition, is_mod_rs } | ||
1085 | } | ||
1086 | }; | ||
1087 | for (name, mac) in modules[self.module_id].scope.collect_legacy_macros() { | ||
1088 | modules[res].scope.define_legacy_macro(name, mac) | ||
1089 | } | ||
1090 | modules[self.module_id].children.insert(name.clone(), res); | ||
1091 | let module = ModuleId { krate: self.def_collector.def_map.krate, local_id: res }; | ||
1092 | let def: ModuleDefId = module.into(); | ||
1093 | self.def_collector.def_map.modules[self.module_id].scope.define_def(def); | ||
1094 | self.def_collector.update( | ||
1095 | self.module_id, | ||
1096 | &[(Some(name), PerNs::from_def(def, vis, false))], | ||
1097 | vis, | ||
1098 | ImportType::Named, | ||
1099 | ); | ||
1100 | res | ||
1101 | } | ||
1102 | |||
1103 | fn collect_derives(&mut self, attrs: &Attrs, ast_id: FileAstId<ast::Item>) { | ||
1104 | for derive_subtree in attrs.by_key("derive").tt_values() { | ||
1105 | // for #[derive(Copy, Clone)], `derive_subtree` is the `(Copy, Clone)` subtree | ||
1106 | for tt in &derive_subtree.token_trees { | ||
1107 | let ident = match &tt { | ||
1108 | tt::TokenTree::Leaf(tt::Leaf::Ident(ident)) => ident, | ||
1109 | tt::TokenTree::Leaf(tt::Leaf::Punct(_)) => continue, // , is ok | ||
1110 | _ => continue, // anything else would be an error (which we currently ignore) | ||
1111 | }; | ||
1112 | let path = ModPath::from_tt_ident(ident); | ||
1113 | |||
1114 | let ast_id = AstIdWithPath::new(self.file_id, ast_id, path); | ||
1115 | self.def_collector | ||
1116 | .unexpanded_attribute_macros | ||
1117 | .push(DeriveDirective { module_id: self.module_id, ast_id }); | ||
1118 | } | ||
1119 | } | ||
1120 | } | ||
1121 | |||
1122 | fn collect_macro(&mut self, mac: &MacroCall) { | ||
1123 | let mut ast_id = AstIdWithPath::new(self.file_id, mac.ast_id, mac.path.clone()); | ||
1124 | |||
1125 | // Case 0: builtin macros | ||
1126 | if mac.is_builtin { | ||
1127 | if let Some(name) = &mac.name { | ||
1128 | let krate = self.def_collector.def_map.krate; | ||
1129 | if let Some(macro_id) = find_builtin_macro(name, krate, ast_id.ast_id) { | ||
1130 | self.def_collector.define_macro( | ||
1131 | self.module_id, | ||
1132 | name.clone(), | ||
1133 | macro_id, | ||
1134 | mac.is_export, | ||
1135 | ); | ||
1136 | return; | ||
1137 | } | ||
1138 | } | ||
1139 | } | ||
1140 | |||
1141 | // Case 1: macro rules, define a macro in crate-global mutable scope | ||
1142 | if is_macro_rules(&mac.path) { | ||
1143 | if let Some(name) = &mac.name { | ||
1144 | let macro_id = MacroDefId { | ||
1145 | ast_id: Some(ast_id.ast_id), | ||
1146 | krate: Some(self.def_collector.def_map.krate), | ||
1147 | kind: MacroDefKind::Declarative, | ||
1148 | local_inner: mac.is_local_inner, | ||
1149 | }; | ||
1150 | self.def_collector.define_macro( | ||
1151 | self.module_id, | ||
1152 | name.clone(), | ||
1153 | macro_id, | ||
1154 | mac.is_export, | ||
1155 | ); | ||
1156 | } | ||
1157 | return; | ||
1158 | } | ||
1159 | |||
1160 | // Case 2: try to resolve in legacy scope and expand macro_rules | ||
1161 | if let Some(macro_call_id) = | ||
1162 | ast_id.as_call_id(self.def_collector.db, self.def_collector.def_map.krate, |path| { | ||
1163 | path.as_ident().and_then(|name| { | ||
1164 | self.def_collector.def_map[self.module_id].scope.get_legacy_macro(&name) | ||
1165 | }) | ||
1166 | }) | ||
1167 | { | ||
1168 | self.def_collector.unexpanded_macros.push(MacroDirective { | ||
1169 | module_id: self.module_id, | ||
1170 | ast_id, | ||
1171 | legacy: Some(macro_call_id), | ||
1172 | depth: self.macro_depth + 1, | ||
1173 | }); | ||
1174 | |||
1175 | return; | ||
1176 | } | ||
1177 | |||
1178 | // Case 3: resolve in module scope, expand during name resolution. | ||
1179 | // We rewrite simple path `macro_name` to `self::macro_name` to force resolve in module scope only. | ||
1180 | if ast_id.path.is_ident() { | ||
1181 | ast_id.path.kind = PathKind::Super(0); | ||
1182 | } | ||
1183 | |||
1184 | self.def_collector.unexpanded_macros.push(MacroDirective { | ||
1185 | module_id: self.module_id, | ||
1186 | ast_id, | ||
1187 | legacy: None, | ||
1188 | depth: self.macro_depth + 1, | ||
1189 | }); | ||
1190 | } | ||
1191 | |||
1192 | fn import_all_legacy_macros(&mut self, module_id: LocalModuleId) { | ||
1193 | let macros = self.def_collector.def_map[module_id].scope.collect_legacy_macros(); | ||
1194 | for (name, macro_) in macros { | ||
1195 | self.def_collector.define_legacy_macro(self.module_id, name.clone(), macro_); | ||
1196 | } | ||
1197 | } | ||
1198 | |||
1199 | fn is_cfg_enabled(&self, attrs: &Attrs) -> bool { | ||
1200 | attrs.is_cfg_enabled(self.def_collector.cfg_options) | ||
1201 | } | ||
1202 | } | ||
1203 | |||
1204 | fn is_macro_rules(path: &ModPath) -> bool { | ||
1205 | path.as_ident() == Some(&name![macro_rules]) | ||
1206 | } | ||
1207 | |||
1208 | #[cfg(test)] | ||
1209 | mod tests { | ||
1210 | use crate::{db::DefDatabase, test_db::TestDB}; | ||
1211 | use arena::Arena; | ||
1212 | use base_db::{fixture::WithFixture, SourceDatabase}; | ||
1213 | |||
1214 | use super::*; | ||
1215 | |||
1216 | fn do_collect_defs(db: &dyn DefDatabase, def_map: CrateDefMap) -> CrateDefMap { | ||
1217 | let mut collector = DefCollector { | ||
1218 | db, | ||
1219 | def_map, | ||
1220 | glob_imports: FxHashMap::default(), | ||
1221 | unresolved_imports: Vec::new(), | ||
1222 | resolved_imports: Vec::new(), | ||
1223 | unexpanded_macros: Vec::new(), | ||
1224 | unexpanded_attribute_macros: Vec::new(), | ||
1225 | mod_dirs: FxHashMap::default(), | ||
1226 | cfg_options: &CfgOptions::default(), | ||
1227 | proc_macros: Default::default(), | ||
1228 | from_glob_import: Default::default(), | ||
1229 | }; | ||
1230 | collector.collect(); | ||
1231 | collector.def_map | ||
1232 | } | ||
1233 | |||
1234 | fn do_resolve(code: &str) -> CrateDefMap { | ||
1235 | let (db, _file_id) = TestDB::with_single_file(&code); | ||
1236 | let krate = db.test_crate(); | ||
1237 | |||
1238 | let def_map = { | ||
1239 | let edition = db.crate_graph()[krate].edition; | ||
1240 | let mut modules: Arena<ModuleData> = Arena::default(); | ||
1241 | let root = modules.alloc(ModuleData::default()); | ||
1242 | CrateDefMap { | ||
1243 | krate, | ||
1244 | edition, | ||
1245 | extern_prelude: FxHashMap::default(), | ||
1246 | prelude: None, | ||
1247 | root, | ||
1248 | modules, | ||
1249 | diagnostics: Vec::new(), | ||
1250 | } | ||
1251 | }; | ||
1252 | do_collect_defs(&db, def_map) | ||
1253 | } | ||
1254 | |||
1255 | #[test] | ||
1256 | fn test_macro_expand_will_stop_1() { | ||
1257 | do_resolve( | ||
1258 | r#" | ||
1259 | macro_rules! foo { | ||
1260 | ($($ty:ty)*) => { foo!($($ty)*); } | ||
1261 | } | ||
1262 | foo!(KABOOM); | ||
1263 | "#, | ||
1264 | ); | ||
1265 | } | ||
1266 | |||
1267 | #[ignore] // this test does succeed, but takes quite a while :/ | ||
1268 | #[test] | ||
1269 | fn test_macro_expand_will_stop_2() { | ||
1270 | do_resolve( | ||
1271 | r#" | ||
1272 | macro_rules! foo { | ||
1273 | ($($ty:ty)*) => { foo!($($ty)* $($ty)*); } | ||
1274 | } | ||
1275 | foo!(KABOOM); | ||
1276 | "#, | ||
1277 | ); | ||
1278 | } | ||
1279 | } | ||