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