aboutsummaryrefslogtreecommitdiff
path: root/crates/ra_hir_def/src/nameres/collector.rs
diff options
context:
space:
mode:
Diffstat (limited to 'crates/ra_hir_def/src/nameres/collector.rs')
-rw-r--r--crates/ra_hir_def/src/nameres/collector.rs844
1 files changed, 844 insertions, 0 deletions
diff --git a/crates/ra_hir_def/src/nameres/collector.rs b/crates/ra_hir_def/src/nameres/collector.rs
new file mode 100644
index 000000000..3b61d9895
--- /dev/null
+++ b/crates/ra_hir_def/src/nameres/collector.rs
@@ -0,0 +1,844 @@
1//! FIXME: write short doc here
2
3use hir_expand::{
4 name::{self, AsName, Name},
5 HirFileId, MacroCallId, MacroCallLoc, MacroDefId, MacroFileKind,
6};
7use ra_cfg::CfgOptions;
8use ra_db::{CrateId, FileId};
9use ra_syntax::{ast, SmolStr};
10use rustc_hash::FxHashMap;
11use test_utils::tested_by;
12
13use crate::{
14 attr::Attr,
15 db::DefDatabase2,
16 nameres::{
17 diagnostics::DefDiagnostic, mod_resolution::ModDir, per_ns::PerNs, raw, CrateDefMap,
18 ModuleData, ReachedFixedPoint, Resolution, ResolveMode,
19 },
20 path::{Path, PathKind},
21 AdtId, AstId, AstItemDef, ConstId, CrateModuleId, EnumId, EnumVariantId, FunctionId,
22 LocationCtx, ModuleDefId, ModuleId, StaticId, StructId, TraitId, TypeAliasId, UnionId,
23};
24
25pub(super) fn collect_defs(db: &impl DefDatabase2, mut def_map: CrateDefMap) -> CrateDefMap {
26 let crate_graph = db.crate_graph();
27
28 // populate external prelude
29 for dep in crate_graph.dependencies(def_map.krate) {
30 let dep_def_map = db.crate_def_map(dep.crate_id);
31 log::debug!("crate dep {:?} -> {:?}", dep.name, dep.crate_id);
32 def_map.extern_prelude.insert(
33 dep.as_name(),
34 ModuleId { krate: dep.crate_id, module_id: dep_def_map.root }.into(),
35 );
36
37 // look for the prelude
38 if def_map.prelude.is_none() {
39 let map = db.crate_def_map(dep.crate_id);
40 if map.prelude.is_some() {
41 def_map.prelude = map.prelude;
42 }
43 }
44 }
45
46 let cfg_options = crate_graph.cfg_options(def_map.krate);
47
48 let mut collector = DefCollector {
49 db,
50 def_map,
51 glob_imports: FxHashMap::default(),
52 unresolved_imports: Vec::new(),
53 unexpanded_macros: Vec::new(),
54 mod_dirs: FxHashMap::default(),
55 macro_stack_monitor: MacroStackMonitor::default(),
56 cfg_options,
57 };
58 collector.collect();
59 collector.finish()
60}
61
62#[derive(Default)]
63struct MacroStackMonitor {
64 counts: FxHashMap<MacroDefId, u32>,
65
66 /// Mainly use for test
67 validator: Option<Box<dyn Fn(u32) -> bool>>,
68}
69
70impl MacroStackMonitor {
71 fn increase(&mut self, macro_def_id: MacroDefId) {
72 *self.counts.entry(macro_def_id).or_default() += 1;
73 }
74
75 fn decrease(&mut self, macro_def_id: MacroDefId) {
76 *self.counts.entry(macro_def_id).or_default() -= 1;
77 }
78
79 fn is_poison(&self, macro_def_id: MacroDefId) -> bool {
80 let cur = *self.counts.get(&macro_def_id).unwrap_or(&0);
81
82 if let Some(validator) = &self.validator {
83 validator(cur)
84 } else {
85 cur > 100
86 }
87 }
88}
89
90/// Walks the tree of module recursively
91struct DefCollector<'a, DB> {
92 db: &'a DB,
93 def_map: CrateDefMap,
94 glob_imports: FxHashMap<CrateModuleId, Vec<(CrateModuleId, raw::ImportId)>>,
95 unresolved_imports: Vec<(CrateModuleId, raw::ImportId, raw::ImportData)>,
96 unexpanded_macros: Vec<(CrateModuleId, AstId<ast::MacroCall>, Path)>,
97 mod_dirs: FxHashMap<CrateModuleId, ModDir>,
98
99 /// Some macro use `$tt:tt which mean we have to handle the macro perfectly
100 /// To prevent stack overflow, we add a deep counter here for prevent that.
101 macro_stack_monitor: MacroStackMonitor,
102
103 cfg_options: &'a CfgOptions,
104}
105
106impl<DB> DefCollector<'_, DB>
107where
108 DB: DefDatabase2,
109{
110 fn collect(&mut self) {
111 let crate_graph = self.db.crate_graph();
112 let file_id = crate_graph.crate_root(self.def_map.krate);
113 let raw_items = self.db.raw_items(file_id.into());
114 let module_id = self.def_map.root;
115 self.def_map.modules[module_id].definition = Some(file_id);
116 ModCollector {
117 def_collector: &mut *self,
118 module_id,
119 file_id: file_id.into(),
120 raw_items: &raw_items,
121 mod_dir: ModDir::root(),
122 }
123 .collect(raw_items.items());
124
125 // main name resolution fixed-point loop.
126 let mut i = 0;
127 loop {
128 self.db.check_canceled();
129 match (self.resolve_imports(), self.resolve_macros()) {
130 (ReachedFixedPoint::Yes, ReachedFixedPoint::Yes) => break,
131 _ => i += 1,
132 }
133 if i == 1000 {
134 log::error!("name resolution is stuck");
135 break;
136 }
137 }
138
139 let unresolved_imports = std::mem::replace(&mut self.unresolved_imports, Vec::new());
140 // show unresolved imports in completion, etc
141 for (module_id, import, import_data) in unresolved_imports {
142 self.record_resolved_import(module_id, PerNs::none(), import, &import_data)
143 }
144 }
145
146 /// Define a macro with `macro_rules`.
147 ///
148 /// It will define the macro in legacy textual scope, and if it has `#[macro_export]`,
149 /// then it is also defined in the root module scope.
150 /// You can `use` or invoke it by `crate::macro_name` anywhere, before or after the definition.
151 ///
152 /// It is surprising that the macro will never be in the current module scope.
153 /// These code fails with "unresolved import/macro",
154 /// ```rust,compile_fail
155 /// mod m { macro_rules! foo { () => {} } }
156 /// use m::foo as bar;
157 /// ```
158 ///
159 /// ```rust,compile_fail
160 /// macro_rules! foo { () => {} }
161 /// self::foo!();
162 /// crate::foo!();
163 /// ```
164 ///
165 /// Well, this code compiles, bacause the plain path `foo` in `use` is searched
166 /// in the legacy textual scope only.
167 /// ```rust
168 /// macro_rules! foo { () => {} }
169 /// use foo as bar;
170 /// ```
171 fn define_macro(
172 &mut self,
173 module_id: CrateModuleId,
174 name: Name,
175 macro_: MacroDefId,
176 export: bool,
177 ) {
178 // Textual scoping
179 self.define_legacy_macro(module_id, name.clone(), macro_);
180
181 // Module scoping
182 // In Rust, `#[macro_export]` macros are unconditionally visible at the
183 // crate root, even if the parent modules is **not** visible.
184 if export {
185 self.update(self.def_map.root, None, &[(name, Resolution::from_macro(macro_))]);
186 }
187 }
188
189 /// Define a legacy textual scoped macro in module
190 ///
191 /// We use a map `legacy_macros` to store all legacy textual scoped macros visable per module.
192 /// It will clone all macros from parent legacy scope, whose definition is prior to
193 /// the definition of current module.
194 /// And also, `macro_use` on a module will import all legacy macros visable inside to
195 /// current legacy scope, with possible shadowing.
196 fn define_legacy_macro(&mut self, module_id: CrateModuleId, name: Name, macro_: MacroDefId) {
197 // Always shadowing
198 self.def_map.modules[module_id].scope.legacy_macros.insert(name, macro_);
199 }
200
201 /// Import macros from `#[macro_use] extern crate`.
202 fn import_macros_from_extern_crate(
203 &mut self,
204 current_module_id: CrateModuleId,
205 import: &raw::ImportData,
206 ) {
207 log::debug!(
208 "importing macros from extern crate: {:?} ({:?})",
209 import,
210 self.def_map.edition,
211 );
212
213 let res = self.def_map.resolve_name_in_extern_prelude(
214 &import
215 .path
216 .as_ident()
217 .expect("extern crate should have been desugared to one-element path"),
218 );
219
220 if let Some(ModuleDefId::ModuleId(m)) = res.take_types() {
221 tested_by!(macro_rules_from_other_crates_are_visible_with_macro_use);
222 self.import_all_macros_exported(current_module_id, m.krate);
223 }
224 }
225
226 /// Import all exported macros from another crate
227 ///
228 /// Exported macros are just all macros in the root module scope.
229 /// Note that it contains not only all `#[macro_export]` macros, but also all aliases
230 /// created by `use` in the root module, ignoring the visibility of `use`.
231 fn import_all_macros_exported(&mut self, current_module_id: CrateModuleId, krate: CrateId) {
232 let def_map = self.db.crate_def_map(krate);
233 for (name, def) in def_map[def_map.root].scope.macros() {
234 // `macro_use` only bring things into legacy scope.
235 self.define_legacy_macro(current_module_id, name.clone(), def);
236 }
237 }
238
239 fn resolve_imports(&mut self) -> ReachedFixedPoint {
240 let mut imports = std::mem::replace(&mut self.unresolved_imports, Vec::new());
241 let mut resolved = Vec::new();
242 imports.retain(|(module_id, import, import_data)| {
243 let (def, fp) = self.resolve_import(*module_id, import_data);
244 if fp == ReachedFixedPoint::Yes {
245 resolved.push((*module_id, def, *import, import_data.clone()))
246 }
247 fp == ReachedFixedPoint::No
248 });
249 self.unresolved_imports = imports;
250 // Resolves imports, filling-in module scopes
251 let result =
252 if resolved.is_empty() { ReachedFixedPoint::Yes } else { ReachedFixedPoint::No };
253 for (module_id, def, import, import_data) in resolved {
254 self.record_resolved_import(module_id, def, import, &import_data)
255 }
256 result
257 }
258
259 fn resolve_import(
260 &self,
261 module_id: CrateModuleId,
262 import: &raw::ImportData,
263 ) -> (PerNs, ReachedFixedPoint) {
264 log::debug!("resolving import: {:?} ({:?})", import, self.def_map.edition);
265 if import.is_extern_crate {
266 let res = self.def_map.resolve_name_in_extern_prelude(
267 &import
268 .path
269 .as_ident()
270 .expect("extern crate should have been desugared to one-element path"),
271 );
272 (res, ReachedFixedPoint::Yes)
273 } else {
274 let res = self.def_map.resolve_path_fp_with_macro(
275 self.db,
276 ResolveMode::Import,
277 module_id,
278 &import.path,
279 );
280
281 (res.resolved_def, res.reached_fixedpoint)
282 }
283 }
284
285 fn record_resolved_import(
286 &mut self,
287 module_id: CrateModuleId,
288 def: PerNs,
289 import_id: raw::ImportId,
290 import: &raw::ImportData,
291 ) {
292 if import.is_glob {
293 log::debug!("glob import: {:?}", import);
294 match def.take_types() {
295 Some(ModuleDefId::ModuleId(m)) => {
296 if import.is_prelude {
297 tested_by!(std_prelude);
298 self.def_map.prelude = Some(m);
299 } else if m.krate != self.def_map.krate {
300 tested_by!(glob_across_crates);
301 // glob import from other crate => we can just import everything once
302 let item_map = self.db.crate_def_map(m.krate);
303 let scope = &item_map[m.module_id].scope;
304
305 // Module scoped macros is included
306 let items = scope
307 .items
308 .iter()
309 .map(|(name, res)| (name.clone(), res.clone()))
310 .collect::<Vec<_>>();
311
312 self.update(module_id, Some(import_id), &items);
313 } else {
314 // glob import from same crate => we do an initial
315 // import, and then need to propagate any further
316 // additions
317 let scope = &self.def_map[m.module_id].scope;
318
319 // Module scoped macros is included
320 let items = scope
321 .items
322 .iter()
323 .map(|(name, res)| (name.clone(), res.clone()))
324 .collect::<Vec<_>>();
325
326 self.update(module_id, Some(import_id), &items);
327 // record the glob import in case we add further items
328 self.glob_imports
329 .entry(m.module_id)
330 .or_default()
331 .push((module_id, import_id));
332 }
333 }
334 Some(ModuleDefId::AdtId(AdtId::EnumId(e))) => {
335 tested_by!(glob_enum);
336 // glob import from enum => just import all the variants
337 let enum_data = self.db.enum_data(e);
338 let resolutions = enum_data
339 .variants
340 .iter()
341 .filter_map(|(local_id, variant_data)| {
342 let name = variant_data.name.clone()?;
343 let variant = EnumVariantId { parent: e, local_id };
344 let res = Resolution {
345 def: PerNs::both(variant.into(), variant.into()),
346 import: Some(import_id),
347 };
348 Some((name, res))
349 })
350 .collect::<Vec<_>>();
351 self.update(module_id, Some(import_id), &resolutions);
352 }
353 Some(d) => {
354 log::debug!("glob import {:?} from non-module/enum {:?}", import, d);
355 }
356 None => {
357 log::debug!("glob import {:?} didn't resolve as type", import);
358 }
359 }
360 } else {
361 match import.path.segments.last() {
362 Some(last_segment) => {
363 let name = import.alias.clone().unwrap_or_else(|| last_segment.name.clone());
364 log::debug!("resolved import {:?} ({:?}) to {:?}", name, import, def);
365
366 // extern crates in the crate root are special-cased to insert entries into the extern prelude: rust-lang/rust#54658
367 if import.is_extern_crate && module_id == self.def_map.root {
368 if let Some(def) = def.take_types() {
369 self.def_map.extern_prelude.insert(name.clone(), def);
370 }
371 }
372
373 let resolution = Resolution { def, import: Some(import_id) };
374 self.update(module_id, Some(import_id), &[(name, resolution)]);
375 }
376 None => tested_by!(bogus_paths),
377 }
378 }
379 }
380
381 fn update(
382 &mut self,
383 module_id: CrateModuleId,
384 import: Option<raw::ImportId>,
385 resolutions: &[(Name, Resolution)],
386 ) {
387 self.update_recursive(module_id, import, resolutions, 0)
388 }
389
390 fn update_recursive(
391 &mut self,
392 module_id: CrateModuleId,
393 import: Option<raw::ImportId>,
394 resolutions: &[(Name, Resolution)],
395 depth: usize,
396 ) {
397 if depth > 100 {
398 // prevent stack overflows (but this shouldn't be possible)
399 panic!("infinite recursion in glob imports!");
400 }
401 let module_items = &mut self.def_map.modules[module_id].scope;
402 let mut changed = false;
403 for (name, res) in resolutions {
404 let existing = module_items.items.entry(name.clone()).or_default();
405
406 if existing.def.types.is_none() && res.def.types.is_some() {
407 existing.def.types = res.def.types;
408 existing.import = import.or(res.import);
409 changed = true;
410 }
411 if existing.def.values.is_none() && res.def.values.is_some() {
412 existing.def.values = res.def.values;
413 existing.import = import.or(res.import);
414 changed = true;
415 }
416 if existing.def.macros.is_none() && res.def.macros.is_some() {
417 existing.def.macros = res.def.macros;
418 existing.import = import.or(res.import);
419 changed = true;
420 }
421
422 if existing.def.is_none()
423 && res.def.is_none()
424 && existing.import.is_none()
425 && res.import.is_some()
426 {
427 existing.import = res.import;
428 }
429 }
430
431 if !changed {
432 return;
433 }
434 let glob_imports = self
435 .glob_imports
436 .get(&module_id)
437 .into_iter()
438 .flat_map(|v| v.iter())
439 .cloned()
440 .collect::<Vec<_>>();
441 for (glob_importing_module, glob_import) in glob_imports {
442 // We pass the glob import so that the tracked import in those modules is that glob import
443 self.update_recursive(glob_importing_module, Some(glob_import), resolutions, depth + 1);
444 }
445 }
446
447 fn resolve_macros(&mut self) -> ReachedFixedPoint {
448 let mut macros = std::mem::replace(&mut self.unexpanded_macros, Vec::new());
449 let mut resolved = Vec::new();
450 let mut res = ReachedFixedPoint::Yes;
451 macros.retain(|(module_id, ast_id, path)| {
452 let resolved_res = self.def_map.resolve_path_fp_with_macro(
453 self.db,
454 ResolveMode::Other,
455 *module_id,
456 path,
457 );
458
459 if let Some(def) = resolved_res.resolved_def.get_macros() {
460 let call_id = self.db.intern_macro(MacroCallLoc { def, ast_id: *ast_id });
461 resolved.push((*module_id, call_id, def));
462 res = ReachedFixedPoint::No;
463 return false;
464 }
465
466 true
467 });
468
469 self.unexpanded_macros = macros;
470
471 for (module_id, macro_call_id, macro_def_id) in resolved {
472 self.collect_macro_expansion(module_id, macro_call_id, macro_def_id);
473 }
474
475 res
476 }
477
478 fn collect_macro_expansion(
479 &mut self,
480 module_id: CrateModuleId,
481 macro_call_id: MacroCallId,
482 macro_def_id: MacroDefId,
483 ) {
484 if self.def_map.poison_macros.contains(&macro_def_id) {
485 return;
486 }
487
488 self.macro_stack_monitor.increase(macro_def_id);
489
490 if !self.macro_stack_monitor.is_poison(macro_def_id) {
491 let file_id: HirFileId = macro_call_id.as_file(MacroFileKind::Items);
492 let raw_items = self.db.raw_items(file_id);
493 let mod_dir = self.mod_dirs[&module_id].clone();
494 ModCollector {
495 def_collector: &mut *self,
496 file_id,
497 module_id,
498 raw_items: &raw_items,
499 mod_dir,
500 }
501 .collect(raw_items.items());
502 } else {
503 log::error!("Too deep macro expansion: {:?}", macro_call_id);
504 self.def_map.poison_macros.insert(macro_def_id);
505 }
506
507 self.macro_stack_monitor.decrease(macro_def_id);
508 }
509
510 fn finish(self) -> CrateDefMap {
511 self.def_map
512 }
513}
514
515/// Walks a single module, populating defs, imports and macros
516struct ModCollector<'a, D> {
517 def_collector: D,
518 module_id: CrateModuleId,
519 file_id: HirFileId,
520 raw_items: &'a raw::RawItems,
521 mod_dir: ModDir,
522}
523
524impl<DB> ModCollector<'_, &'_ mut DefCollector<'_, DB>>
525where
526 DB: DefDatabase2,
527{
528 fn collect(&mut self, items: &[raw::RawItem]) {
529 // Note: don't assert that inserted value is fresh: it's simply not true
530 // for macros.
531 self.def_collector.mod_dirs.insert(self.module_id, self.mod_dir.clone());
532
533 // Prelude module is always considered to be `#[macro_use]`.
534 if let Some(prelude_module) = self.def_collector.def_map.prelude {
535 if prelude_module.krate != self.def_collector.def_map.krate {
536 tested_by!(prelude_is_macro_use);
537 self.def_collector.import_all_macros_exported(self.module_id, prelude_module.krate);
538 }
539 }
540
541 // This should be processed eagerly instead of deferred to resolving.
542 // `#[macro_use] extern crate` is hoisted to imports macros before collecting
543 // any other items.
544 for item in items {
545 if self.is_cfg_enabled(item.attrs()) {
546 if let raw::RawItemKind::Import(import_id) = item.kind {
547 let import = self.raw_items[import_id].clone();
548 if import.is_extern_crate && import.is_macro_use {
549 self.def_collector.import_macros_from_extern_crate(self.module_id, &import);
550 }
551 }
552 }
553 }
554
555 for item in items {
556 if self.is_cfg_enabled(item.attrs()) {
557 match item.kind {
558 raw::RawItemKind::Module(m) => {
559 self.collect_module(&self.raw_items[m], item.attrs())
560 }
561 raw::RawItemKind::Import(import_id) => self
562 .def_collector
563 .unresolved_imports
564 .push((self.module_id, import_id, self.raw_items[import_id].clone())),
565 raw::RawItemKind::Def(def) => self.define_def(&self.raw_items[def]),
566 raw::RawItemKind::Macro(mac) => self.collect_macro(&self.raw_items[mac]),
567 }
568 }
569 }
570 }
571
572 fn collect_module(&mut self, module: &raw::ModuleData, attrs: &[Attr]) {
573 let path_attr = self.path_attr(attrs);
574 let is_macro_use = self.is_macro_use(attrs);
575 match module {
576 // inline module, just recurse
577 raw::ModuleData::Definition { name, items, ast_id } => {
578 let module_id =
579 self.push_child_module(name.clone(), AstId::new(self.file_id, *ast_id), None);
580
581 ModCollector {
582 def_collector: &mut *self.def_collector,
583 module_id,
584 file_id: self.file_id,
585 raw_items: self.raw_items,
586 mod_dir: self.mod_dir.descend_into_definition(name, path_attr),
587 }
588 .collect(&*items);
589 if is_macro_use {
590 self.import_all_legacy_macros(module_id);
591 }
592 }
593 // out of line module, resolve, parse and recurse
594 raw::ModuleData::Declaration { name, ast_id } => {
595 let ast_id = AstId::new(self.file_id, *ast_id);
596 match self.mod_dir.resolve_declaration(
597 self.def_collector.db,
598 self.file_id,
599 name,
600 path_attr,
601 ) {
602 Ok((file_id, mod_dir)) => {
603 let module_id = self.push_child_module(name.clone(), ast_id, Some(file_id));
604 let raw_items = self.def_collector.db.raw_items(file_id.into());
605 ModCollector {
606 def_collector: &mut *self.def_collector,
607 module_id,
608 file_id: file_id.into(),
609 raw_items: &raw_items,
610 mod_dir,
611 }
612 .collect(raw_items.items());
613 if is_macro_use {
614 self.import_all_legacy_macros(module_id);
615 }
616 }
617 Err(candidate) => self.def_collector.def_map.diagnostics.push(
618 DefDiagnostic::UnresolvedModule {
619 module: self.module_id,
620 declaration: ast_id,
621 candidate,
622 },
623 ),
624 };
625 }
626 }
627 }
628
629 fn push_child_module(
630 &mut self,
631 name: Name,
632 declaration: AstId<ast::Module>,
633 definition: Option<FileId>,
634 ) -> CrateModuleId {
635 let modules = &mut self.def_collector.def_map.modules;
636 let res = modules.alloc(ModuleData::default());
637 modules[res].parent = Some(self.module_id);
638 modules[res].declaration = Some(declaration);
639 modules[res].definition = definition;
640 modules[res].scope.legacy_macros = modules[self.module_id].scope.legacy_macros.clone();
641 modules[self.module_id].children.insert(name.clone(), res);
642 let resolution = Resolution {
643 def: PerNs::types(
644 ModuleId { krate: self.def_collector.def_map.krate, module_id: res }.into(),
645 ),
646 import: None,
647 };
648 self.def_collector.update(self.module_id, None, &[(name, resolution)]);
649 res
650 }
651
652 fn define_def(&mut self, def: &raw::DefData) {
653 let module =
654 ModuleId { krate: self.def_collector.def_map.krate, module_id: self.module_id };
655 let ctx = LocationCtx::new(self.def_collector.db, module, self.file_id);
656
657 let name = def.name.clone();
658 let def: PerNs = match def.kind {
659 raw::DefKind::Function(ast_id) => {
660 PerNs::values(FunctionId::from_ast_id(ctx, ast_id).into())
661 }
662 raw::DefKind::Struct(ast_id) => {
663 let s = StructId::from_ast_id(ctx, ast_id).into();
664 PerNs::both(s, s)
665 }
666 raw::DefKind::Union(ast_id) => {
667 let s = UnionId::from_ast_id(ctx, ast_id).into();
668 PerNs::both(s, s)
669 }
670 raw::DefKind::Enum(ast_id) => PerNs::types(EnumId::from_ast_id(ctx, ast_id).into()),
671 raw::DefKind::Const(ast_id) => PerNs::values(ConstId::from_ast_id(ctx, ast_id).into()),
672 raw::DefKind::Static(ast_id) => {
673 PerNs::values(StaticId::from_ast_id(ctx, ast_id).into())
674 }
675 raw::DefKind::Trait(ast_id) => PerNs::types(TraitId::from_ast_id(ctx, ast_id).into()),
676 raw::DefKind::TypeAlias(ast_id) => {
677 PerNs::types(TypeAliasId::from_ast_id(ctx, ast_id).into())
678 }
679 };
680 let resolution = Resolution { def, import: None };
681 self.def_collector.update(self.module_id, None, &[(name, resolution)])
682 }
683
684 fn collect_macro(&mut self, mac: &raw::MacroData) {
685 let ast_id = AstId::new(self.file_id, mac.ast_id);
686
687 // Case 1: macro rules, define a macro in crate-global mutable scope
688 if is_macro_rules(&mac.path) {
689 if let Some(name) = &mac.name {
690 let macro_id = MacroDefId { ast_id, krate: self.def_collector.def_map.krate };
691 self.def_collector.define_macro(self.module_id, name.clone(), macro_id, mac.export);
692 }
693 return;
694 }
695
696 // Case 2: try to resolve in legacy scope and expand macro_rules, triggering
697 // recursive item collection.
698 if let Some(macro_def) = mac.path.as_ident().and_then(|name| {
699 self.def_collector.def_map[self.module_id].scope.get_legacy_macro(&name)
700 }) {
701 let macro_call_id =
702 self.def_collector.db.intern_macro(MacroCallLoc { def: macro_def, ast_id });
703
704 self.def_collector.collect_macro_expansion(self.module_id, macro_call_id, macro_def);
705 return;
706 }
707
708 // Case 3: resolve in module scope, expand during name resolution.
709 // We rewrite simple path `macro_name` to `self::macro_name` to force resolve in module scope only.
710 let mut path = mac.path.clone();
711 if path.is_ident() {
712 path.kind = PathKind::Self_;
713 }
714 self.def_collector.unexpanded_macros.push((self.module_id, ast_id, path));
715 }
716
717 fn import_all_legacy_macros(&mut self, module_id: CrateModuleId) {
718 let macros = self.def_collector.def_map[module_id].scope.legacy_macros.clone();
719 for (name, macro_) in macros {
720 self.def_collector.define_legacy_macro(self.module_id, name.clone(), macro_);
721 }
722 }
723
724 fn is_cfg_enabled(&self, attrs: &[Attr]) -> bool {
725 attrs.iter().all(|attr| attr.is_cfg_enabled(&self.def_collector.cfg_options) != Some(false))
726 }
727
728 fn path_attr<'a>(&self, attrs: &'a [Attr]) -> Option<&'a SmolStr> {
729 attrs.iter().find_map(|attr| attr.as_path())
730 }
731
732 fn is_macro_use<'a>(&self, attrs: &'a [Attr]) -> bool {
733 attrs.iter().any(|attr| attr.is_simple_atom("macro_use"))
734 }
735}
736
737fn is_macro_rules(path: &Path) -> bool {
738 path.as_ident() == Some(&name::MACRO_RULES)
739}
740
741#[cfg(test)]
742mod tests {
743 use ra_arena::Arena;
744 use ra_db::{fixture::WithFixture, SourceDatabase};
745 use rustc_hash::FxHashSet;
746
747 use crate::{db::DefDatabase2, test_db::TestDB};
748
749 use super::*;
750
751 fn do_collect_defs(
752 db: &impl DefDatabase2,
753 def_map: CrateDefMap,
754 monitor: MacroStackMonitor,
755 ) -> CrateDefMap {
756 let mut collector = DefCollector {
757 db,
758 def_map,
759 glob_imports: FxHashMap::default(),
760 unresolved_imports: Vec::new(),
761 unexpanded_macros: Vec::new(),
762 mod_dirs: FxHashMap::default(),
763 macro_stack_monitor: monitor,
764 cfg_options: &CfgOptions::default(),
765 };
766 collector.collect();
767 collector.finish()
768 }
769
770 fn do_limited_resolve(code: &str, limit: u32, poison_limit: u32) -> CrateDefMap {
771 let (db, _file_id) = TestDB::with_single_file(&code);
772 let krate = db.crate_graph().iter().next().unwrap();
773
774 let def_map = {
775 let edition = db.crate_graph().edition(krate);
776 let mut modules: Arena<CrateModuleId, ModuleData> = Arena::default();
777 let root = modules.alloc(ModuleData::default());
778 CrateDefMap {
779 krate,
780 edition,
781 extern_prelude: FxHashMap::default(),
782 prelude: None,
783 root,
784 modules,
785 poison_macros: FxHashSet::default(),
786 diagnostics: Vec::new(),
787 }
788 };
789
790 let mut monitor = MacroStackMonitor::default();
791 monitor.validator = Some(Box::new(move |count| {
792 assert!(count < limit);
793 count >= poison_limit
794 }));
795
796 do_collect_defs(&db, def_map, monitor)
797 }
798
799 #[test]
800 fn test_macro_expand_limit_width() {
801 do_limited_resolve(
802 r#"
803 macro_rules! foo {
804 ($($ty:ty)*) => { foo!($($ty)*, $($ty)*); }
805 }
806foo!(KABOOM);
807 "#,
808 16,
809 1000,
810 );
811 }
812
813 #[test]
814 fn test_macro_expand_poisoned() {
815 let def = do_limited_resolve(
816 r#"
817 macro_rules! foo {
818 ($ty:ty) => { foo!($ty); }
819 }
820foo!(KABOOM);
821 "#,
822 100,
823 16,
824 );
825
826 assert_eq!(def.poison_macros.len(), 1);
827 }
828
829 #[test]
830 fn test_macro_expand_normal() {
831 let def = do_limited_resolve(
832 r#"
833 macro_rules! foo {
834 ($ident:ident) => { struct $ident {} }
835 }
836foo!(Bar);
837 "#,
838 16,
839 16,
840 );
841
842 assert_eq!(def.poison_macros.len(), 0);
843 }
844}