aboutsummaryrefslogtreecommitdiff
path: root/crates/ra_hir_def/src/nameres/collector.rs
diff options
context:
space:
mode:
authorAleksey Kladov <[email protected]>2019-10-31 15:45:10 +0000
committerAleksey Kladov <[email protected]>2019-11-03 15:04:06 +0000
commitba2efca2bbe5f4434f9a2522b2b94df873f3563b (patch)
tree786ede7a7c94793becb90a4ca735ecbc7d798d2f /crates/ra_hir_def/src/nameres/collector.rs
parentf9f1effd011b906903891c09f1cb6b2a42f73e95 (diff)
Move CrateDefMap to hir_def
Diffstat (limited to 'crates/ra_hir_def/src/nameres/collector.rs')
-rw-r--r--crates/ra_hir_def/src/nameres/collector.rs845
1 files changed, 845 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..8a96d3d31
--- /dev/null
+++ b/crates/ra_hir_def/src/nameres/collector.rs
@@ -0,0 +1,845 @@
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;
11// use 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 // tested_by!(bogus_paths),
377 None => (),
378 }
379 }
380 }
381
382 fn update(
383 &mut self,
384 module_id: CrateModuleId,
385 import: Option<raw::ImportId>,
386 resolutions: &[(Name, Resolution)],
387 ) {
388 self.update_recursive(module_id, import, resolutions, 0)
389 }
390
391 fn update_recursive(
392 &mut self,
393 module_id: CrateModuleId,
394 import: Option<raw::ImportId>,
395 resolutions: &[(Name, Resolution)],
396 depth: usize,
397 ) {
398 if depth > 100 {
399 // prevent stack overflows (but this shouldn't be possible)
400 panic!("infinite recursion in glob imports!");
401 }
402 let module_items = &mut self.def_map.modules[module_id].scope;
403 let mut changed = false;
404 for (name, res) in resolutions {
405 let existing = module_items.items.entry(name.clone()).or_default();
406
407 if existing.def.types.is_none() && res.def.types.is_some() {
408 existing.def.types = res.def.types;
409 existing.import = import.or(res.import);
410 changed = true;
411 }
412 if existing.def.values.is_none() && res.def.values.is_some() {
413 existing.def.values = res.def.values;
414 existing.import = import.or(res.import);
415 changed = true;
416 }
417 if existing.def.macros.is_none() && res.def.macros.is_some() {
418 existing.def.macros = res.def.macros;
419 existing.import = import.or(res.import);
420 changed = true;
421 }
422
423 if existing.def.is_none()
424 && res.def.is_none()
425 && existing.import.is_none()
426 && res.import.is_some()
427 {
428 existing.import = res.import;
429 }
430 }
431
432 if !changed {
433 return;
434 }
435 let glob_imports = self
436 .glob_imports
437 .get(&module_id)
438 .into_iter()
439 .flat_map(|v| v.iter())
440 .cloned()
441 .collect::<Vec<_>>();
442 for (glob_importing_module, glob_import) in glob_imports {
443 // We pass the glob import so that the tracked import in those modules is that glob import
444 self.update_recursive(glob_importing_module, Some(glob_import), resolutions, depth + 1);
445 }
446 }
447
448 fn resolve_macros(&mut self) -> ReachedFixedPoint {
449 let mut macros = std::mem::replace(&mut self.unexpanded_macros, Vec::new());
450 let mut resolved = Vec::new();
451 let mut res = ReachedFixedPoint::Yes;
452 macros.retain(|(module_id, ast_id, path)| {
453 let resolved_res = self.def_map.resolve_path_fp_with_macro(
454 self.db,
455 ResolveMode::Other,
456 *module_id,
457 path,
458 );
459
460 if let Some(def) = resolved_res.resolved_def.get_macros() {
461 let call_id = self.db.intern_macro(MacroCallLoc { def, ast_id: *ast_id });
462 resolved.push((*module_id, call_id, def));
463 res = ReachedFixedPoint::No;
464 return false;
465 }
466
467 true
468 });
469
470 self.unexpanded_macros = macros;
471
472 for (module_id, macro_call_id, macro_def_id) in resolved {
473 self.collect_macro_expansion(module_id, macro_call_id, macro_def_id);
474 }
475
476 res
477 }
478
479 fn collect_macro_expansion(
480 &mut self,
481 module_id: CrateModuleId,
482 macro_call_id: MacroCallId,
483 macro_def_id: MacroDefId,
484 ) {
485 if self.def_map.poison_macros.contains(&macro_def_id) {
486 return;
487 }
488
489 self.macro_stack_monitor.increase(macro_def_id);
490
491 if !self.macro_stack_monitor.is_poison(macro_def_id) {
492 let file_id: HirFileId = macro_call_id.as_file(MacroFileKind::Items);
493 let raw_items = self.db.raw_items(file_id);
494 let mod_dir = self.mod_dirs[&module_id].clone();
495 ModCollector {
496 def_collector: &mut *self,
497 file_id,
498 module_id,
499 raw_items: &raw_items,
500 mod_dir,
501 }
502 .collect(raw_items.items());
503 } else {
504 log::error!("Too deep macro expansion: {:?}", macro_call_id);
505 self.def_map.poison_macros.insert(macro_def_id);
506 }
507
508 self.macro_stack_monitor.decrease(macro_def_id);
509 }
510
511 fn finish(self) -> CrateDefMap {
512 self.def_map
513 }
514}
515
516/// Walks a single module, populating defs, imports and macros
517struct ModCollector<'a, D> {
518 def_collector: D,
519 module_id: CrateModuleId,
520 file_id: HirFileId,
521 raw_items: &'a raw::RawItems,
522 mod_dir: ModDir,
523}
524
525impl<DB> ModCollector<'_, &'_ mut DefCollector<'_, DB>>
526where
527 DB: DefDatabase2,
528{
529 fn collect(&mut self, items: &[raw::RawItem]) {
530 // Note: don't assert that inserted value is fresh: it's simply not true
531 // for macros.
532 self.def_collector.mod_dirs.insert(self.module_id, self.mod_dir.clone());
533
534 // Prelude module is always considered to be `#[macro_use]`.
535 if let Some(prelude_module) = self.def_collector.def_map.prelude {
536 if prelude_module.krate != self.def_collector.def_map.krate {
537 // tested_by!(prelude_is_macro_use);
538 self.def_collector.import_all_macros_exported(self.module_id, prelude_module.krate);
539 }
540 }
541
542 // This should be processed eagerly instead of deferred to resolving.
543 // `#[macro_use] extern crate` is hoisted to imports macros before collecting
544 // any other items.
545 for item in items {
546 if self.is_cfg_enabled(item.attrs()) {
547 if let raw::RawItemKind::Import(import_id) = item.kind {
548 let import = self.raw_items[import_id].clone();
549 if import.is_extern_crate && import.is_macro_use {
550 self.def_collector.import_macros_from_extern_crate(self.module_id, &import);
551 }
552 }
553 }
554 }
555
556 for item in items {
557 if self.is_cfg_enabled(item.attrs()) {
558 match item.kind {
559 raw::RawItemKind::Module(m) => {
560 self.collect_module(&self.raw_items[m], item.attrs())
561 }
562 raw::RawItemKind::Import(import_id) => self
563 .def_collector
564 .unresolved_imports
565 .push((self.module_id, import_id, self.raw_items[import_id].clone())),
566 raw::RawItemKind::Def(def) => self.define_def(&self.raw_items[def]),
567 raw::RawItemKind::Macro(mac) => self.collect_macro(&self.raw_items[mac]),
568 }
569 }
570 }
571 }
572
573 fn collect_module(&mut self, module: &raw::ModuleData, attrs: &[Attr]) {
574 let path_attr = self.path_attr(attrs);
575 let is_macro_use = self.is_macro_use(attrs);
576 match module {
577 // inline module, just recurse
578 raw::ModuleData::Definition { name, items, ast_id } => {
579 let module_id =
580 self.push_child_module(name.clone(), AstId::new(self.file_id, *ast_id), None);
581
582 ModCollector {
583 def_collector: &mut *self.def_collector,
584 module_id,
585 file_id: self.file_id,
586 raw_items: self.raw_items,
587 mod_dir: self.mod_dir.descend_into_definition(name, path_attr),
588 }
589 .collect(&*items);
590 if is_macro_use {
591 self.import_all_legacy_macros(module_id);
592 }
593 }
594 // out of line module, resolve, parse and recurse
595 raw::ModuleData::Declaration { name, ast_id } => {
596 let ast_id = AstId::new(self.file_id, *ast_id);
597 match self.mod_dir.resolve_declaration(
598 self.def_collector.db,
599 self.file_id,
600 name,
601 path_attr,
602 ) {
603 Ok((file_id, mod_dir)) => {
604 let module_id = self.push_child_module(name.clone(), ast_id, Some(file_id));
605 let raw_items = self.def_collector.db.raw_items(file_id.into());
606 ModCollector {
607 def_collector: &mut *self.def_collector,
608 module_id,
609 file_id: file_id.into(),
610 raw_items: &raw_items,
611 mod_dir,
612 }
613 .collect(raw_items.items());
614 if is_macro_use {
615 self.import_all_legacy_macros(module_id);
616 }
617 }
618 Err(candidate) => self.def_collector.def_map.diagnostics.push(
619 DefDiagnostic::UnresolvedModule {
620 module: self.module_id,
621 declaration: ast_id,
622 candidate,
623 },
624 ),
625 };
626 }
627 }
628 }
629
630 fn push_child_module(
631 &mut self,
632 name: Name,
633 declaration: AstId<ast::Module>,
634 definition: Option<FileId>,
635 ) -> CrateModuleId {
636 let modules = &mut self.def_collector.def_map.modules;
637 let res = modules.alloc(ModuleData::default());
638 modules[res].parent = Some(self.module_id);
639 modules[res].declaration = Some(declaration);
640 modules[res].definition = definition;
641 modules[res].scope.legacy_macros = modules[self.module_id].scope.legacy_macros.clone();
642 modules[self.module_id].children.insert(name.clone(), res);
643 let resolution = Resolution {
644 def: PerNs::types(
645 ModuleId { krate: self.def_collector.def_map.krate, module_id: res }.into(),
646 ),
647 import: None,
648 };
649 self.def_collector.update(self.module_id, None, &[(name, resolution)]);
650 res
651 }
652
653 fn define_def(&mut self, def: &raw::DefData) {
654 let module =
655 ModuleId { krate: self.def_collector.def_map.krate, module_id: self.module_id };
656 let ctx = LocationCtx::new(self.def_collector.db, module, self.file_id);
657
658 let name = def.name.clone();
659 let def: PerNs = match def.kind {
660 raw::DefKind::Function(ast_id) => {
661 PerNs::values(FunctionId::from_ast_id(ctx, ast_id).into())
662 }
663 raw::DefKind::Struct(ast_id) => {
664 let s = StructId::from_ast_id(ctx, ast_id).into();
665 PerNs::both(s, s)
666 }
667 raw::DefKind::Union(ast_id) => {
668 let s = UnionId::from_ast_id(ctx, ast_id).into();
669 PerNs::both(s, s)
670 }
671 raw::DefKind::Enum(ast_id) => PerNs::types(EnumId::from_ast_id(ctx, ast_id).into()),
672 raw::DefKind::Const(ast_id) => PerNs::values(ConstId::from_ast_id(ctx, ast_id).into()),
673 raw::DefKind::Static(ast_id) => {
674 PerNs::values(StaticId::from_ast_id(ctx, ast_id).into())
675 }
676 raw::DefKind::Trait(ast_id) => PerNs::types(TraitId::from_ast_id(ctx, ast_id).into()),
677 raw::DefKind::TypeAlias(ast_id) => {
678 PerNs::types(TypeAliasId::from_ast_id(ctx, ast_id).into())
679 }
680 };
681 let resolution = Resolution { def, import: None };
682 self.def_collector.update(self.module_id, None, &[(name, resolution)])
683 }
684
685 fn collect_macro(&mut self, mac: &raw::MacroData) {
686 let ast_id = AstId::new(self.file_id, mac.ast_id);
687
688 // Case 1: macro rules, define a macro in crate-global mutable scope
689 if is_macro_rules(&mac.path) {
690 if let Some(name) = &mac.name {
691 let macro_id = MacroDefId { ast_id, krate: self.def_collector.def_map.krate };
692 self.def_collector.define_macro(self.module_id, name.clone(), macro_id, mac.export);
693 }
694 return;
695 }
696
697 // Case 2: try to resolve in legacy scope and expand macro_rules, triggering
698 // recursive item collection.
699 if let Some(macro_def) = mac.path.as_ident().and_then(|name| {
700 self.def_collector.def_map[self.module_id].scope.get_legacy_macro(&name)
701 }) {
702 let macro_call_id =
703 self.def_collector.db.intern_macro(MacroCallLoc { def: macro_def, ast_id });
704
705 self.def_collector.collect_macro_expansion(self.module_id, macro_call_id, macro_def);
706 return;
707 }
708
709 // Case 3: resolve in module scope, expand during name resolution.
710 // We rewrite simple path `macro_name` to `self::macro_name` to force resolve in module scope only.
711 let mut path = mac.path.clone();
712 if path.is_ident() {
713 path.kind = PathKind::Self_;
714 }
715 self.def_collector.unexpanded_macros.push((self.module_id, ast_id, path));
716 }
717
718 fn import_all_legacy_macros(&mut self, module_id: CrateModuleId) {
719 let macros = self.def_collector.def_map[module_id].scope.legacy_macros.clone();
720 for (name, macro_) in macros {
721 self.def_collector.define_legacy_macro(self.module_id, name.clone(), macro_);
722 }
723 }
724
725 fn is_cfg_enabled(&self, attrs: &[Attr]) -> bool {
726 attrs.iter().all(|attr| attr.is_cfg_enabled(&self.def_collector.cfg_options) != Some(false))
727 }
728
729 fn path_attr<'a>(&self, attrs: &'a [Attr]) -> Option<&'a SmolStr> {
730 attrs.iter().find_map(|attr| attr.as_path())
731 }
732
733 fn is_macro_use<'a>(&self, attrs: &'a [Attr]) -> bool {
734 attrs.iter().any(|attr| attr.is_simple_atom("macro_use"))
735 }
736}
737
738fn is_macro_rules(path: &Path) -> bool {
739 path.as_ident() == Some(&name::MACRO_RULES)
740}
741
742#[cfg(never)]
743mod tests {
744 use ra_db::SourceDatabase;
745
746 use super::*;
747 use crate::{db::DefDatabase, mock::MockDatabase, Crate};
748 use ra_arena::Arena;
749 use rustc_hash::FxHashSet;
750
751 fn do_collect_defs(
752 db: &impl DefDatabase,
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, _source_root, _) = MockDatabase::with_single_file(&code);
772 let crate_id = db.crate_graph().iter().next().unwrap();
773 let krate = Crate { crate_id };
774
775 let def_map = {
776 let edition = krate.edition(&db);
777 let mut modules: Arena<CrateModuleId, ModuleData> = Arena::default();
778 let root = modules.alloc(ModuleData::default());
779 CrateDefMap {
780 krate,
781 edition,
782 extern_prelude: FxHashMap::default(),
783 prelude: None,
784 root,
785 modules,
786 poison_macros: FxHashSet::default(),
787 diagnostics: Vec::new(),
788 }
789 };
790
791 let mut monitor = MacroStackMonitor::default();
792 monitor.validator = Some(Box::new(move |count| {
793 assert!(count < limit);
794 count >= poison_limit
795 }));
796
797 do_collect_defs(&db, def_map, monitor)
798 }
799
800 #[test]
801 fn test_macro_expand_limit_width() {
802 do_limited_resolve(
803 r#"
804 macro_rules! foo {
805 ($($ty:ty)*) => { foo!($($ty)*, $($ty)*); }
806 }
807foo!(KABOOM);
808 "#,
809 16,
810 1000,
811 );
812 }
813
814 #[test]
815 fn test_macro_expand_poisoned() {
816 let def = do_limited_resolve(
817 r#"
818 macro_rules! foo {
819 ($ty:ty) => { foo!($ty); }
820 }
821foo!(KABOOM);
822 "#,
823 100,
824 16,
825 );
826
827 assert_eq!(def.poison_macros.len(), 1);
828 }
829
830 #[test]
831 fn test_macro_expand_normal() {
832 let def = do_limited_resolve(
833 r#"
834 macro_rules! foo {
835 ($ident:ident) => { struct $ident {} }
836 }
837foo!(Bar);
838 "#,
839 16,
840 16,
841 );
842
843 assert_eq!(def.poison_macros.len(), 0);
844 }
845}