aboutsummaryrefslogtreecommitdiff
path: root/crates/hir_def/src/nameres
diff options
context:
space:
mode:
Diffstat (limited to 'crates/hir_def/src/nameres')
-rw-r--r--crates/hir_def/src/nameres/collector.rs1279
-rw-r--r--crates/hir_def/src/nameres/mod_resolution.rs139
-rw-r--r--crates/hir_def/src/nameres/path_resolution.rs330
-rw-r--r--crates/hir_def/src/nameres/tests.rs690
-rw-r--r--crates/hir_def/src/nameres/tests/globs.rs338
-rw-r--r--crates/hir_def/src/nameres/tests/incremental.rs101
-rw-r--r--crates/hir_def/src/nameres/tests/macros.rs669
-rw-r--r--crates/hir_def/src/nameres/tests/mod_resolution.rs796
-rw-r--r--crates/hir_def/src/nameres/tests/primitives.rs23
9 files changed, 4365 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
6use base_db::{CrateId, FileId, ProcMacroId};
7use cfg::CfgOptions;
8use 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};
16use rustc_hash::FxHashMap;
17use syntax::ast;
18use test_utils::mark;
19
20use 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
39const GLOB_RECURSION_LIMIT: usize = 100;
40const EXPANSION_DEPTH_LIMIT: usize = 128;
41const FIXED_POINT_LIMIT: usize = 8192;
42
43pub(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)]
95enum 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
104impl 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)]
115struct 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
125impl 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)]
156struct ImportDirective {
157 module_id: LocalModuleId,
158 import: Import,
159 status: PartialResolvedImport,
160}
161
162#[derive(Clone, Debug, Eq, PartialEq)]
163struct 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)]
171struct DeriveDirective {
172 module_id: LocalModuleId,
173 ast_id: AstIdWithPath<ast::Item>,
174}
175
176struct 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
184struct 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
198impl 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
783struct 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
792impl 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
1204fn is_macro_rules(path: &ModPath) -> bool {
1205 path.as_ident() == Some(&name![macro_rules])
1206}
1207
1208#[cfg(test)]
1209mod 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}
diff --git a/crates/hir_def/src/nameres/mod_resolution.rs b/crates/hir_def/src/nameres/mod_resolution.rs
new file mode 100644
index 000000000..e8389b484
--- /dev/null
+++ b/crates/hir_def/src/nameres/mod_resolution.rs
@@ -0,0 +1,139 @@
1//! This module resolves `mod foo;` declaration to file.
2use base_db::FileId;
3use hir_expand::name::Name;
4use syntax::SmolStr;
5
6use crate::{db::DefDatabase, HirFileId};
7
8#[derive(Clone, Debug)]
9pub(super) struct ModDir {
10 /// `` for `mod.rs`, `lib.rs`
11 /// `foo/` for `foo.rs`
12 /// `foo/bar/` for `mod bar { mod x; }` nested in `foo.rs`
13 /// Invariant: path.is_empty() || path.ends_with('/')
14 dir_path: DirPath,
15 /// inside `./foo.rs`, mods with `#[path]` should *not* be relative to `./foo/`
16 root_non_dir_owner: bool,
17}
18
19impl ModDir {
20 pub(super) fn root() -> ModDir {
21 ModDir { dir_path: DirPath::empty(), root_non_dir_owner: false }
22 }
23
24 pub(super) fn descend_into_definition(
25 &self,
26 name: &Name,
27 attr_path: Option<&SmolStr>,
28 ) -> ModDir {
29 let path = match attr_path.map(|it| it.as_str()) {
30 None => {
31 let mut path = self.dir_path.clone();
32 path.push(&name.to_string());
33 path
34 }
35 Some(attr_path) => {
36 let mut path = self.dir_path.join_attr(attr_path, self.root_non_dir_owner);
37 if !(path.is_empty() || path.ends_with('/')) {
38 path.push('/')
39 }
40 DirPath::new(path)
41 }
42 };
43 ModDir { dir_path: path, root_non_dir_owner: false }
44 }
45
46 pub(super) fn resolve_declaration(
47 &self,
48 db: &dyn DefDatabase,
49 file_id: HirFileId,
50 name: &Name,
51 attr_path: Option<&SmolStr>,
52 ) -> Result<(FileId, bool, ModDir), String> {
53 let file_id = file_id.original_file(db.upcast());
54
55 let mut candidate_files = Vec::new();
56 match attr_path {
57 Some(attr_path) => {
58 candidate_files.push(self.dir_path.join_attr(attr_path, self.root_non_dir_owner))
59 }
60 None => {
61 candidate_files.push(format!("{}{}.rs", self.dir_path.0, name));
62 candidate_files.push(format!("{}{}/mod.rs", self.dir_path.0, name));
63 }
64 };
65
66 for candidate in candidate_files.iter() {
67 if let Some(file_id) = db.resolve_path(file_id, candidate.as_str()) {
68 let is_mod_rs = candidate.ends_with("mod.rs");
69
70 let (dir_path, root_non_dir_owner) = if is_mod_rs || attr_path.is_some() {
71 (DirPath::empty(), false)
72 } else {
73 (DirPath::new(format!("{}/", name)), true)
74 };
75 return Ok((file_id, is_mod_rs, ModDir { dir_path, root_non_dir_owner }));
76 }
77 }
78 Err(candidate_files.remove(0))
79 }
80}
81
82#[derive(Clone, Debug)]
83struct DirPath(String);
84
85impl DirPath {
86 fn assert_invariant(&self) {
87 assert!(self.0.is_empty() || self.0.ends_with('/'));
88 }
89 fn new(repr: String) -> DirPath {
90 let res = DirPath(repr);
91 res.assert_invariant();
92 res
93 }
94 fn empty() -> DirPath {
95 DirPath::new(String::new())
96 }
97 fn push(&mut self, name: &str) {
98 self.0.push_str(name);
99 self.0.push('/');
100 self.assert_invariant();
101 }
102 fn parent(&self) -> Option<&str> {
103 if self.0.is_empty() {
104 return None;
105 };
106 let idx =
107 self.0[..self.0.len() - '/'.len_utf8()].rfind('/').map_or(0, |it| it + '/'.len_utf8());
108 Some(&self.0[..idx])
109 }
110 /// So this is the case which doesn't really work I think if we try to be
111 /// 100% platform agnostic:
112 ///
113 /// ```
114 /// mod a {
115 /// #[path="C://sad/face"]
116 /// mod b { mod c; }
117 /// }
118 /// ```
119 ///
120 /// Here, we need to join logical dir path to a string path from an
121 /// attribute. Ideally, we should somehow losslessly communicate the whole
122 /// construction to `FileLoader`.
123 fn join_attr(&self, mut attr: &str, relative_to_parent: bool) -> String {
124 let base = if relative_to_parent { self.parent().unwrap() } else { &self.0 };
125
126 if attr.starts_with("./") {
127 attr = &attr["./".len()..];
128 }
129 let tmp;
130 let attr = if attr.contains('\\') {
131 tmp = attr.replace('\\', "/");
132 &tmp
133 } else {
134 attr
135 };
136 let res = format!("{}{}", base, attr);
137 res
138 }
139}
diff --git a/crates/hir_def/src/nameres/path_resolution.rs b/crates/hir_def/src/nameres/path_resolution.rs
new file mode 100644
index 000000000..88e10574e
--- /dev/null
+++ b/crates/hir_def/src/nameres/path_resolution.rs
@@ -0,0 +1,330 @@
1//! This modules implements a function to resolve a path `foo::bar::baz` to a
2//! def, which is used within the name resolution.
3//!
4//! When name resolution is finished, the result of resolving a path is either
5//! `Some(def)` or `None`. However, when we are in process of resolving imports
6//! or macros, there's a third possibility:
7//!
8//! I can't resolve this path right now, but I might be resolve this path
9//! later, when more macros are expanded.
10//!
11//! `ReachedFixedPoint` signals about this.
12
13use std::iter::successors;
14
15use base_db::Edition;
16use hir_expand::name::Name;
17use test_utils::mark;
18
19use crate::{
20 db::DefDatabase,
21 item_scope::BUILTIN_SCOPE,
22 nameres::{BuiltinShadowMode, CrateDefMap},
23 path::{ModPath, PathKind},
24 per_ns::PerNs,
25 visibility::{RawVisibility, Visibility},
26 AdtId, CrateId, EnumVariantId, LocalModuleId, ModuleDefId, ModuleId,
27};
28
29#[derive(Debug, Clone, Copy, PartialEq, Eq)]
30pub(super) enum ResolveMode {
31 Import,
32 Other,
33}
34
35#[derive(Debug, Clone, Copy, PartialEq, Eq)]
36pub(super) enum ReachedFixedPoint {
37 Yes,
38 No,
39}
40
41#[derive(Debug, Clone)]
42pub(super) struct ResolvePathResult {
43 pub(super) resolved_def: PerNs,
44 pub(super) segment_index: Option<usize>,
45 pub(super) reached_fixedpoint: ReachedFixedPoint,
46 pub(super) krate: Option<CrateId>,
47}
48
49impl ResolvePathResult {
50 fn empty(reached_fixedpoint: ReachedFixedPoint) -> ResolvePathResult {
51 ResolvePathResult::with(PerNs::none(), reached_fixedpoint, None, None)
52 }
53
54 fn with(
55 resolved_def: PerNs,
56 reached_fixedpoint: ReachedFixedPoint,
57 segment_index: Option<usize>,
58 krate: Option<CrateId>,
59 ) -> ResolvePathResult {
60 ResolvePathResult { resolved_def, reached_fixedpoint, segment_index, krate }
61 }
62}
63
64impl CrateDefMap {
65 pub(super) fn resolve_name_in_extern_prelude(&self, name: &Name) -> PerNs {
66 self.extern_prelude
67 .get(name)
68 .map_or(PerNs::none(), |&it| PerNs::types(it, Visibility::Public))
69 }
70
71 pub(crate) fn resolve_visibility(
72 &self,
73 db: &dyn DefDatabase,
74 original_module: LocalModuleId,
75 visibility: &RawVisibility,
76 ) -> Option<Visibility> {
77 match visibility {
78 RawVisibility::Module(path) => {
79 let (result, remaining) =
80 self.resolve_path(db, original_module, &path, BuiltinShadowMode::Module);
81 if remaining.is_some() {
82 return None;
83 }
84 let types = result.take_types()?;
85 match types {
86 ModuleDefId::ModuleId(m) => Some(Visibility::Module(m)),
87 _ => {
88 // error: visibility needs to refer to module
89 None
90 }
91 }
92 }
93 RawVisibility::Public => Some(Visibility::Public),
94 }
95 }
96
97 // Returns Yes if we are sure that additions to `ItemMap` wouldn't change
98 // the result.
99 pub(super) fn resolve_path_fp_with_macro(
100 &self,
101 db: &dyn DefDatabase,
102 mode: ResolveMode,
103 original_module: LocalModuleId,
104 path: &ModPath,
105 shadow: BuiltinShadowMode,
106 ) -> ResolvePathResult {
107 let mut segments = path.segments.iter().enumerate();
108 let mut curr_per_ns: PerNs = match path.kind {
109 PathKind::DollarCrate(krate) => {
110 if krate == self.krate {
111 mark::hit!(macro_dollar_crate_self);
112 PerNs::types(
113 ModuleId { krate: self.krate, local_id: self.root }.into(),
114 Visibility::Public,
115 )
116 } else {
117 let def_map = db.crate_def_map(krate);
118 let module = ModuleId { krate, local_id: def_map.root };
119 mark::hit!(macro_dollar_crate_other);
120 PerNs::types(module.into(), Visibility::Public)
121 }
122 }
123 PathKind::Crate => PerNs::types(
124 ModuleId { krate: self.krate, local_id: self.root }.into(),
125 Visibility::Public,
126 ),
127 // plain import or absolute path in 2015: crate-relative with
128 // fallback to extern prelude (with the simplification in
129 // rust-lang/rust#57745)
130 // FIXME there must be a nicer way to write this condition
131 PathKind::Plain | PathKind::Abs
132 if self.edition == Edition::Edition2015
133 && (path.kind == PathKind::Abs || mode == ResolveMode::Import) =>
134 {
135 let (_, segment) = match segments.next() {
136 Some((idx, segment)) => (idx, segment),
137 None => return ResolvePathResult::empty(ReachedFixedPoint::Yes),
138 };
139 log::debug!("resolving {:?} in crate root (+ extern prelude)", segment);
140 self.resolve_name_in_crate_root_or_extern_prelude(&segment)
141 }
142 PathKind::Plain => {
143 let (_, segment) = match segments.next() {
144 Some((idx, segment)) => (idx, segment),
145 None => return ResolvePathResult::empty(ReachedFixedPoint::Yes),
146 };
147 // The first segment may be a builtin type. If the path has more
148 // than one segment, we first try resolving it as a module
149 // anyway.
150 // FIXME: If the next segment doesn't resolve in the module and
151 // BuiltinShadowMode wasn't Module, then we need to try
152 // resolving it as a builtin.
153 let prefer_module =
154 if path.segments.len() == 1 { shadow } else { BuiltinShadowMode::Module };
155
156 log::debug!("resolving {:?} in module", segment);
157 self.resolve_name_in_module(db, original_module, &segment, prefer_module)
158 }
159 PathKind::Super(lvl) => {
160 let m = successors(Some(original_module), |m| self.modules[*m].parent)
161 .nth(lvl as usize);
162 if let Some(local_id) = m {
163 PerNs::types(
164 ModuleId { krate: self.krate, local_id }.into(),
165 Visibility::Public,
166 )
167 } else {
168 log::debug!("super path in root module");
169 return ResolvePathResult::empty(ReachedFixedPoint::Yes);
170 }
171 }
172 PathKind::Abs => {
173 // 2018-style absolute path -- only extern prelude
174 let segment = match segments.next() {
175 Some((_, segment)) => segment,
176 None => return ResolvePathResult::empty(ReachedFixedPoint::Yes),
177 };
178 if let Some(def) = self.extern_prelude.get(&segment) {
179 log::debug!("absolute path {:?} resolved to crate {:?}", path, def);
180 PerNs::types(*def, Visibility::Public)
181 } else {
182 return ResolvePathResult::empty(ReachedFixedPoint::No); // extern crate declarations can add to the extern prelude
183 }
184 }
185 };
186
187 for (i, segment) in segments {
188 let (curr, vis) = match curr_per_ns.take_types_vis() {
189 Some(r) => r,
190 None => {
191 // we still have path segments left, but the path so far
192 // didn't resolve in the types namespace => no resolution
193 // (don't break here because `curr_per_ns` might contain
194 // something in the value namespace, and it would be wrong
195 // to return that)
196 return ResolvePathResult::empty(ReachedFixedPoint::No);
197 }
198 };
199 // resolve segment in curr
200
201 curr_per_ns = match curr {
202 ModuleDefId::ModuleId(module) => {
203 if module.krate != self.krate {
204 let path = ModPath {
205 segments: path.segments[i..].to_vec(),
206 kind: PathKind::Super(0),
207 };
208 log::debug!("resolving {:?} in other crate", path);
209 let defp_map = db.crate_def_map(module.krate);
210 let (def, s) = defp_map.resolve_path(db, module.local_id, &path, shadow);
211 return ResolvePathResult::with(
212 def,
213 ReachedFixedPoint::Yes,
214 s.map(|s| s + i),
215 Some(module.krate),
216 );
217 }
218
219 // Since it is a qualified path here, it should not contains legacy macros
220 self[module.local_id].scope.get(&segment)
221 }
222 ModuleDefId::AdtId(AdtId::EnumId(e)) => {
223 // enum variant
224 mark::hit!(can_import_enum_variant);
225 let enum_data = db.enum_data(e);
226 match enum_data.variant(&segment) {
227 Some(local_id) => {
228 let variant = EnumVariantId { parent: e, local_id };
229 match &*enum_data.variants[local_id].variant_data {
230 crate::adt::VariantData::Record(_) => {
231 PerNs::types(variant.into(), Visibility::Public)
232 }
233 crate::adt::VariantData::Tuple(_)
234 | crate::adt::VariantData::Unit => {
235 PerNs::both(variant.into(), variant.into(), Visibility::Public)
236 }
237 }
238 }
239 None => {
240 return ResolvePathResult::with(
241 PerNs::types(e.into(), vis),
242 ReachedFixedPoint::Yes,
243 Some(i),
244 Some(self.krate),
245 );
246 }
247 }
248 }
249 s => {
250 // could be an inherent method call in UFCS form
251 // (`Struct::method`), or some other kind of associated item
252 log::debug!(
253 "path segment {:?} resolved to non-module {:?}, but is not last",
254 segment,
255 curr,
256 );
257
258 return ResolvePathResult::with(
259 PerNs::types(s, vis),
260 ReachedFixedPoint::Yes,
261 Some(i),
262 Some(self.krate),
263 );
264 }
265 };
266 }
267
268 ResolvePathResult::with(curr_per_ns, ReachedFixedPoint::Yes, None, Some(self.krate))
269 }
270
271 fn resolve_name_in_module(
272 &self,
273 db: &dyn DefDatabase,
274 module: LocalModuleId,
275 name: &Name,
276 shadow: BuiltinShadowMode,
277 ) -> PerNs {
278 // Resolve in:
279 // - legacy scope of macro
280 // - current module / scope
281 // - extern prelude
282 // - std prelude
283 let from_legacy_macro = self[module]
284 .scope
285 .get_legacy_macro(name)
286 .map_or_else(PerNs::none, |m| PerNs::macros(m, Visibility::Public));
287 let from_scope = self[module].scope.get(name);
288 let from_builtin = BUILTIN_SCOPE.get(name).copied().unwrap_or_else(PerNs::none);
289 let from_scope_or_builtin = match shadow {
290 BuiltinShadowMode::Module => from_scope.or(from_builtin),
291 BuiltinShadowMode::Other => {
292 if let Some(ModuleDefId::ModuleId(_)) = from_scope.take_types() {
293 from_builtin.or(from_scope)
294 } else {
295 from_scope.or(from_builtin)
296 }
297 }
298 };
299 let from_extern_prelude = self
300 .extern_prelude
301 .get(name)
302 .map_or(PerNs::none(), |&it| PerNs::types(it, Visibility::Public));
303 let from_prelude = self.resolve_in_prelude(db, name);
304
305 from_legacy_macro.or(from_scope_or_builtin).or(from_extern_prelude).or(from_prelude)
306 }
307
308 fn resolve_name_in_crate_root_or_extern_prelude(&self, name: &Name) -> PerNs {
309 let from_crate_root = self[self.root].scope.get(name);
310 let from_extern_prelude = self.resolve_name_in_extern_prelude(name);
311
312 from_crate_root.or(from_extern_prelude)
313 }
314
315 fn resolve_in_prelude(&self, db: &dyn DefDatabase, name: &Name) -> PerNs {
316 if let Some(prelude) = self.prelude {
317 let keep;
318 let def_map = if prelude.krate == self.krate {
319 self
320 } else {
321 // Extend lifetime
322 keep = db.crate_def_map(prelude.krate);
323 &keep
324 };
325 def_map[prelude.local_id].scope.get(name)
326 } else {
327 PerNs::none()
328 }
329 }
330}
diff --git a/crates/hir_def/src/nameres/tests.rs b/crates/hir_def/src/nameres/tests.rs
new file mode 100644
index 000000000..8aaf7a158
--- /dev/null
+++ b/crates/hir_def/src/nameres/tests.rs
@@ -0,0 +1,690 @@
1mod globs;
2mod incremental;
3mod macros;
4mod mod_resolution;
5mod primitives;
6
7use std::sync::Arc;
8
9use base_db::{fixture::WithFixture, SourceDatabase};
10use expect_test::{expect, Expect};
11use test_utils::mark;
12
13use crate::{db::DefDatabase, nameres::*, test_db::TestDB};
14
15fn compute_crate_def_map(fixture: &str) -> Arc<CrateDefMap> {
16 let db = TestDB::with_files(fixture);
17 let krate = db.crate_graph().iter().next().unwrap();
18 db.crate_def_map(krate)
19}
20
21fn check(ra_fixture: &str, expect: Expect) {
22 let db = TestDB::with_files(ra_fixture);
23 let krate = db.crate_graph().iter().next().unwrap();
24 let actual = db.crate_def_map(krate).dump();
25 expect.assert_eq(&actual);
26}
27
28#[test]
29fn crate_def_map_smoke_test() {
30 check(
31 r#"
32//- /lib.rs
33mod foo;
34struct S;
35use crate::foo::bar::E;
36use self::E::V;
37
38//- /foo/mod.rs
39pub mod bar;
40fn f() {}
41
42//- /foo/bar.rs
43pub struct Baz;
44
45union U { to_be: bool, not_to_be: u8 }
46enum E { V }
47
48extern {
49 static EXT: u8;
50 fn ext();
51}
52"#,
53 expect![[r#"
54 crate
55 E: t
56 S: t v
57 V: t v
58 foo: t
59
60 crate::foo
61 bar: t
62 f: v
63
64 crate::foo::bar
65 Baz: t v
66 E: t
67 EXT: v
68 U: t
69 ext: v
70 "#]],
71 );
72}
73
74#[test]
75fn crate_def_map_super_super() {
76 check(
77 r#"
78mod a {
79 const A: usize = 0;
80 mod b {
81 const B: usize = 0;
82 mod c {
83 use super::super::*;
84 }
85 }
86}
87"#,
88 expect![[r#"
89 crate
90 a: t
91
92 crate::a
93 A: v
94 b: t
95
96 crate::a::b
97 B: v
98 c: t
99
100 crate::a::b::c
101 A: v
102 b: t
103 "#]],
104 );
105}
106
107#[test]
108fn crate_def_map_fn_mod_same_name() {
109 check(
110 r#"
111mod m {
112 pub mod z {}
113 pub fn z() {}
114}
115"#,
116 expect![[r#"
117 crate
118 m: t
119
120 crate::m
121 z: t v
122
123 crate::m::z
124 "#]],
125 );
126}
127
128#[test]
129fn bogus_paths() {
130 mark::check!(bogus_paths);
131 check(
132 r#"
133//- /lib.rs
134mod foo;
135struct S;
136use self;
137
138//- /foo/mod.rs
139use super;
140use crate;
141"#,
142 expect![[r#"
143 crate
144 S: t v
145 foo: t
146
147 crate::foo
148 "#]],
149 );
150}
151
152#[test]
153fn use_as() {
154 check(
155 r#"
156//- /lib.rs
157mod foo;
158use crate::foo::Baz as Foo;
159
160//- /foo/mod.rs
161pub struct Baz;
162"#,
163 expect![[r#"
164 crate
165 Foo: t v
166 foo: t
167
168 crate::foo
169 Baz: t v
170 "#]],
171 );
172}
173
174#[test]
175fn use_trees() {
176 check(
177 r#"
178//- /lib.rs
179mod foo;
180use crate::foo::bar::{Baz, Quux};
181
182//- /foo/mod.rs
183pub mod bar;
184
185//- /foo/bar.rs
186pub struct Baz;
187pub enum Quux {};
188"#,
189 expect![[r#"
190 crate
191 Baz: t v
192 Quux: t
193 foo: t
194
195 crate::foo
196 bar: t
197
198 crate::foo::bar
199 Baz: t v
200 Quux: t
201 "#]],
202 );
203}
204
205#[test]
206fn re_exports() {
207 check(
208 r#"
209//- /lib.rs
210mod foo;
211use self::foo::Baz;
212
213//- /foo/mod.rs
214pub mod bar;
215pub use self::bar::Baz;
216
217//- /foo/bar.rs
218pub struct Baz;
219"#,
220 expect![[r#"
221 crate
222 Baz: t v
223 foo: t
224
225 crate::foo
226 Baz: t v
227 bar: t
228
229 crate::foo::bar
230 Baz: t v
231 "#]],
232 );
233}
234
235#[test]
236fn std_prelude() {
237 mark::check!(std_prelude);
238 check(
239 r#"
240//- /main.rs crate:main deps:test_crate
241use Foo::*;
242
243//- /lib.rs crate:test_crate
244mod prelude;
245#[prelude_import]
246use prelude::*;
247
248//- /prelude.rs
249pub enum Foo { Bar, Baz };
250"#,
251 expect![[r#"
252 crate
253 Bar: t v
254 Baz: t v
255 "#]],
256 );
257}
258
259#[test]
260fn can_import_enum_variant() {
261 mark::check!(can_import_enum_variant);
262 check(
263 r#"
264enum E { V }
265use self::E::V;
266"#,
267 expect![[r#"
268 crate
269 E: t
270 V: t v
271 "#]],
272 );
273}
274
275#[test]
276fn edition_2015_imports() {
277 check(
278 r#"
279//- /main.rs crate:main deps:other_crate edition:2015
280mod foo;
281mod bar;
282
283//- /bar.rs
284struct Bar;
285
286//- /foo.rs
287use bar::Bar;
288use other_crate::FromLib;
289
290//- /lib.rs crate:other_crate edition:2018
291struct FromLib;
292"#,
293 expect![[r#"
294 crate
295 bar: t
296 foo: t
297
298 crate::bar
299 Bar: t v
300
301 crate::foo
302 Bar: t v
303 FromLib: t v
304 "#]],
305 );
306}
307
308#[test]
309fn item_map_using_self() {
310 check(
311 r#"
312//- /lib.rs
313mod foo;
314use crate::foo::bar::Baz::{self};
315
316//- /foo/mod.rs
317pub mod bar;
318
319//- /foo/bar.rs
320pub struct Baz;
321"#,
322 expect![[r#"
323 crate
324 Baz: t v
325 foo: t
326
327 crate::foo
328 bar: t
329
330 crate::foo::bar
331 Baz: t v
332 "#]],
333 );
334}
335
336#[test]
337fn item_map_across_crates() {
338 check(
339 r#"
340//- /main.rs crate:main deps:test_crate
341use test_crate::Baz;
342
343//- /lib.rs crate:test_crate
344pub struct Baz;
345"#,
346 expect![[r#"
347 crate
348 Baz: t v
349 "#]],
350 );
351}
352
353#[test]
354fn extern_crate_rename() {
355 check(
356 r#"
357//- /main.rs crate:main deps:alloc
358extern crate alloc as alloc_crate;
359mod alloc;
360mod sync;
361
362//- /sync.rs
363use alloc_crate::Arc;
364
365//- /lib.rs crate:alloc
366struct Arc;
367"#,
368 expect![[r#"
369 crate
370 alloc_crate: t
371 sync: t
372
373 crate::sync
374 Arc: t v
375 "#]],
376 );
377}
378
379#[test]
380fn extern_crate_rename_2015_edition() {
381 check(
382 r#"
383//- /main.rs crate:main deps:alloc edition:2015
384extern crate alloc as alloc_crate;
385mod alloc;
386mod sync;
387
388//- /sync.rs
389use alloc_crate::Arc;
390
391//- /lib.rs crate:alloc
392struct Arc;
393"#,
394 expect![[r#"
395 crate
396 alloc_crate: t
397 sync: t
398
399 crate::sync
400 Arc: t v
401 "#]],
402 );
403}
404
405#[test]
406fn reexport_across_crates() {
407 check(
408 r#"
409//- /main.rs crate:main deps:test_crate
410use test_crate::Baz;
411
412//- /lib.rs crate:test_crate
413pub use foo::Baz;
414mod foo;
415
416//- /foo.rs
417pub struct Baz;
418"#,
419 expect![[r#"
420 crate
421 Baz: t v
422 "#]],
423 );
424}
425
426#[test]
427fn values_dont_shadow_extern_crates() {
428 check(
429 r#"
430//- /main.rs crate:main deps:foo
431fn foo() {}
432use foo::Bar;
433
434//- /foo/lib.rs crate:foo
435pub struct Bar;
436"#,
437 expect![[r#"
438 crate
439 Bar: t v
440 foo: v
441 "#]],
442 );
443}
444
445#[test]
446fn std_prelude_takes_precedence_above_core_prelude() {
447 check(
448 r#"
449//- /main.rs crate:main deps:core,std
450use {Foo, Bar};
451
452//- /std.rs crate:std deps:core
453#[prelude_import]
454pub use self::prelude::*;
455mod prelude {
456 pub struct Foo;
457 pub use core::prelude::Bar;
458}
459
460//- /core.rs crate:core
461#[prelude_import]
462pub use self::prelude::*;
463mod prelude {
464 pub struct Bar;
465}
466"#,
467 expect![[r#"
468 crate
469 Bar: t v
470 Foo: t v
471 "#]],
472 );
473}
474
475#[test]
476fn cfg_not_test() {
477 check(
478 r#"
479//- /main.rs crate:main deps:std
480use {Foo, Bar, Baz};
481
482//- /lib.rs crate:std
483#[prelude_import]
484pub use self::prelude::*;
485mod prelude {
486 #[cfg(test)]
487 pub struct Foo;
488 #[cfg(not(test))]
489 pub struct Bar;
490 #[cfg(all(not(any()), feature = "foo", feature = "bar", opt = "42"))]
491 pub struct Baz;
492}
493"#,
494 expect![[r#"
495 crate
496 Bar: t v
497 Baz: _
498 Foo: _
499 "#]],
500 );
501}
502
503#[test]
504fn cfg_test() {
505 check(
506 r#"
507//- /main.rs crate:main deps:std
508use {Foo, Bar, Baz};
509
510//- /lib.rs crate:std cfg:test,feature=foo,feature=bar,opt=42
511#[prelude_import]
512pub use self::prelude::*;
513mod prelude {
514 #[cfg(test)]
515 pub struct Foo;
516 #[cfg(not(test))]
517 pub struct Bar;
518 #[cfg(all(not(any()), feature = "foo", feature = "bar", opt = "42"))]
519 pub struct Baz;
520}
521"#,
522 expect![[r#"
523 crate
524 Bar: _
525 Baz: t v
526 Foo: t v
527 "#]],
528 );
529}
530
531#[test]
532fn infer_multiple_namespace() {
533 check(
534 r#"
535//- /main.rs
536mod a {
537 pub type T = ();
538 pub use crate::b::*;
539}
540
541use crate::a::T;
542
543mod b {
544 pub const T: () = ();
545}
546"#,
547 expect![[r#"
548 crate
549 T: t v
550 a: t
551 b: t
552
553 crate::b
554 T: v
555
556 crate::a
557 T: t v
558 "#]],
559 );
560}
561
562#[test]
563fn underscore_import() {
564 check(
565 r#"
566//- /main.rs
567use tr::Tr as _;
568use tr::Tr2 as _;
569
570mod tr {
571 pub trait Tr {}
572 pub trait Tr2 {}
573}
574 "#,
575 expect![[r#"
576 crate
577 _: t
578 _: t
579 tr: t
580
581 crate::tr
582 Tr: t
583 Tr2: t
584 "#]],
585 );
586}
587
588#[test]
589fn underscore_reexport() {
590 check(
591 r#"
592//- /main.rs
593mod tr {
594 pub trait PubTr {}
595 pub trait PrivTr {}
596}
597mod reex {
598 use crate::tr::PrivTr as _;
599 pub use crate::tr::PubTr as _;
600}
601use crate::reex::*;
602 "#,
603 expect![[r#"
604 crate
605 _: t
606 reex: t
607 tr: t
608
609 crate::tr
610 PrivTr: t
611 PubTr: t
612
613 crate::reex
614 _: t
615 _: t
616 "#]],
617 );
618}
619
620#[test]
621fn underscore_pub_crate_reexport() {
622 mark::check!(upgrade_underscore_visibility);
623 check(
624 r#"
625//- /main.rs crate:main deps:lib
626use lib::*;
627
628//- /lib.rs crate:lib
629use tr::Tr as _;
630pub use tr::Tr as _;
631
632mod tr {
633 pub trait Tr {}
634}
635 "#,
636 expect![[r#"
637 crate
638 _: t
639 "#]],
640 );
641}
642
643#[test]
644fn underscore_nontrait() {
645 check(
646 r#"
647//- /main.rs
648mod m {
649 pub struct Struct;
650 pub enum Enum {}
651 pub const CONST: () = ();
652}
653use crate::m::{Struct as _, Enum as _, CONST as _};
654 "#,
655 expect![[r#"
656 crate
657 m: t
658
659 crate::m
660 CONST: v
661 Enum: t
662 Struct: t v
663 "#]],
664 );
665}
666
667#[test]
668fn underscore_name_conflict() {
669 check(
670 r#"
671//- /main.rs
672struct Tr;
673
674use tr::Tr as _;
675
676mod tr {
677 pub trait Tr {}
678}
679 "#,
680 expect![[r#"
681 crate
682 _: t
683 Tr: t v
684 tr: t
685
686 crate::tr
687 Tr: t
688 "#]],
689 );
690}
diff --git a/crates/hir_def/src/nameres/tests/globs.rs b/crates/hir_def/src/nameres/tests/globs.rs
new file mode 100644
index 000000000..2ae836e3c
--- /dev/null
+++ b/crates/hir_def/src/nameres/tests/globs.rs
@@ -0,0 +1,338 @@
1use super::*;
2
3#[test]
4fn glob_1() {
5 check(
6 r#"
7//- /lib.rs
8mod foo;
9use foo::*;
10
11//- /foo/mod.rs
12pub mod bar;
13pub use self::bar::Baz;
14pub struct Foo;
15
16//- /foo/bar.rs
17pub struct Baz;
18"#,
19 expect![[r#"
20 crate
21 Baz: t v
22 Foo: t v
23 bar: t
24 foo: t
25
26 crate::foo
27 Baz: t v
28 Foo: t v
29 bar: t
30
31 crate::foo::bar
32 Baz: t v
33 "#]],
34 );
35}
36
37#[test]
38fn glob_2() {
39 check(
40 r#"
41//- /lib.rs
42mod foo;
43use foo::*;
44
45//- /foo/mod.rs
46pub mod bar;
47pub use self::bar::*;
48pub struct Foo;
49
50//- /foo/bar.rs
51pub struct Baz;
52pub use super::*;
53"#,
54 expect![[r#"
55 crate
56 Baz: t v
57 Foo: t v
58 bar: t
59 foo: t
60
61 crate::foo
62 Baz: t v
63 Foo: t v
64 bar: t
65
66 crate::foo::bar
67 Baz: t v
68 Foo: t v
69 bar: t
70 "#]],
71 );
72}
73
74#[test]
75fn glob_privacy_1() {
76 check(
77 r"
78//- /lib.rs
79mod foo;
80use foo::*;
81
82//- /foo/mod.rs
83pub mod bar;
84pub use self::bar::*;
85struct PrivateStructFoo;
86
87//- /foo/bar.rs
88pub struct Baz;
89struct PrivateStructBar;
90pub use super::*;
91",
92 expect![[r#"
93 crate
94 Baz: t v
95 bar: t
96 foo: t
97
98 crate::foo
99 Baz: t v
100 PrivateStructFoo: t v
101 bar: t
102
103 crate::foo::bar
104 Baz: t v
105 PrivateStructBar: t v
106 PrivateStructFoo: t v
107 bar: t
108 "#]],
109 );
110}
111
112#[test]
113fn glob_privacy_2() {
114 check(
115 r"
116//- /lib.rs
117mod foo;
118use foo::*;
119use foo::bar::*;
120
121//- /foo/mod.rs
122mod bar;
123fn Foo() {};
124pub struct Foo {};
125
126//- /foo/bar.rs
127pub(super) struct PrivateBaz;
128struct PrivateBar;
129pub(crate) struct PubCrateStruct;
130",
131 expect![[r#"
132 crate
133 Foo: t
134 PubCrateStruct: t v
135 foo: t
136
137 crate::foo
138 Foo: t v
139 bar: t
140
141 crate::foo::bar
142 PrivateBar: t v
143 PrivateBaz: t v
144 PubCrateStruct: t v
145 "#]],
146 );
147}
148
149#[test]
150fn glob_across_crates() {
151 mark::check!(glob_across_crates);
152 check(
153 r#"
154//- /main.rs crate:main deps:test_crate
155use test_crate::*;
156
157//- /lib.rs crate:test_crate
158pub struct Baz;
159"#,
160 expect![[r#"
161 crate
162 Baz: t v
163 "#]],
164 );
165}
166
167#[test]
168fn glob_privacy_across_crates() {
169 check(
170 r#"
171//- /main.rs crate:main deps:test_crate
172use test_crate::*;
173
174//- /lib.rs crate:test_crate
175pub struct Baz;
176struct Foo;
177"#,
178 expect![[r#"
179 crate
180 Baz: t v
181 "#]],
182 );
183}
184
185#[test]
186fn glob_enum() {
187 mark::check!(glob_enum);
188 check(
189 r#"
190enum Foo { Bar, Baz }
191use self::Foo::*;
192"#,
193 expect![[r#"
194 crate
195 Bar: t v
196 Baz: t v
197 Foo: t
198 "#]],
199 );
200}
201
202#[test]
203fn glob_enum_group() {
204 mark::check!(glob_enum_group);
205 check(
206 r#"
207enum Foo { Bar, Baz }
208use self::Foo::{*};
209"#,
210 expect![[r#"
211 crate
212 Bar: t v
213 Baz: t v
214 Foo: t
215 "#]],
216 );
217}
218
219#[test]
220fn glob_shadowed_def() {
221 mark::check!(import_shadowed);
222 check(
223 r#"
224//- /lib.rs
225mod foo;
226mod bar;
227use foo::*;
228use bar::baz;
229use baz::Bar;
230
231//- /foo.rs
232pub mod baz { pub struct Foo; }
233
234//- /bar.rs
235pub mod baz { pub struct Bar; }
236"#,
237 expect![[r#"
238 crate
239 Bar: t v
240 bar: t
241 baz: t
242 foo: t
243
244 crate::bar
245 baz: t
246
247 crate::bar::baz
248 Bar: t v
249
250 crate::foo
251 baz: t
252
253 crate::foo::baz
254 Foo: t v
255 "#]],
256 );
257}
258
259#[test]
260fn glob_shadowed_def_reversed() {
261 check(
262 r#"
263//- /lib.rs
264mod foo;
265mod bar;
266use bar::baz;
267use foo::*;
268use baz::Bar;
269
270//- /foo.rs
271pub mod baz { pub struct Foo; }
272
273//- /bar.rs
274pub mod baz { pub struct Bar; }
275"#,
276 expect![[r#"
277 crate
278 Bar: t v
279 bar: t
280 baz: t
281 foo: t
282
283 crate::bar
284 baz: t
285
286 crate::bar::baz
287 Bar: t v
288
289 crate::foo
290 baz: t
291
292 crate::foo::baz
293 Foo: t v
294 "#]],
295 );
296}
297
298#[test]
299fn glob_shadowed_def_dependencies() {
300 check(
301 r#"
302mod a { pub mod foo { pub struct X; } }
303mod b { pub use super::a::foo; }
304mod c { pub mod foo { pub struct Y; } }
305mod d {
306 use super::c::foo;
307 use super::b::*;
308 use foo::Y;
309}
310"#,
311 expect![[r#"
312 crate
313 a: t
314 b: t
315 c: t
316 d: t
317
318 crate::d
319 Y: t v
320 foo: t
321
322 crate::c
323 foo: t
324
325 crate::c::foo
326 Y: t v
327
328 crate::b
329 foo: t
330
331 crate::a
332 foo: t
333
334 crate::a::foo
335 X: t v
336 "#]],
337 );
338}
diff --git a/crates/hir_def/src/nameres/tests/incremental.rs b/crates/hir_def/src/nameres/tests/incremental.rs
new file mode 100644
index 000000000..cfbc62cc4
--- /dev/null
+++ b/crates/hir_def/src/nameres/tests/incremental.rs
@@ -0,0 +1,101 @@
1use std::sync::Arc;
2
3use base_db::SourceDatabaseExt;
4
5use super::*;
6
7fn check_def_map_is_not_recomputed(ra_fixture_initial: &str, ra_fixture_change: &str) {
8 let (mut db, pos) = TestDB::with_position(ra_fixture_initial);
9 let krate = db.test_crate();
10 {
11 let events = db.log_executed(|| {
12 db.crate_def_map(krate);
13 });
14 assert!(format!("{:?}", events).contains("crate_def_map"), "{:#?}", events)
15 }
16 db.set_file_text(pos.file_id, Arc::new(ra_fixture_change.to_string()));
17
18 {
19 let events = db.log_executed(|| {
20 db.crate_def_map(krate);
21 });
22 assert!(!format!("{:?}", events).contains("crate_def_map"), "{:#?}", events)
23 }
24}
25
26#[test]
27fn typing_inside_a_function_should_not_invalidate_def_map() {
28 check_def_map_is_not_recomputed(
29 r"
30 //- /lib.rs
31 mod foo;<|>
32
33 use crate::foo::bar::Baz;
34
35 enum E { A, B }
36 use E::*;
37
38 fn foo() -> i32 {
39 1 + 1
40 }
41 //- /foo/mod.rs
42 pub mod bar;
43
44 //- /foo/bar.rs
45 pub struct Baz;
46 ",
47 r"
48 mod foo;
49
50 use crate::foo::bar::Baz;
51
52 enum E { A, B }
53 use E::*;
54
55 fn foo() -> i32 { 92 }
56 ",
57 );
58}
59
60#[test]
61fn typing_inside_a_macro_should_not_invalidate_def_map() {
62 let (mut db, pos) = TestDB::with_position(
63 r"
64 //- /lib.rs
65 macro_rules! m {
66 ($ident:ident) => {
67 fn f() {
68 $ident + $ident;
69 };
70 }
71 }
72 mod foo;
73
74 //- /foo/mod.rs
75 pub mod bar;
76
77 //- /foo/bar.rs
78 <|>
79 m!(X);
80 ",
81 );
82 let krate = db.test_crate();
83 {
84 let events = db.log_executed(|| {
85 let crate_def_map = db.crate_def_map(krate);
86 let (_, module_data) = crate_def_map.modules.iter().last().unwrap();
87 assert_eq!(module_data.scope.resolutions().count(), 1);
88 });
89 assert!(format!("{:?}", events).contains("crate_def_map"), "{:#?}", events)
90 }
91 db.set_file_text(pos.file_id, Arc::new("m!(Y);".to_string()));
92
93 {
94 let events = db.log_executed(|| {
95 let crate_def_map = db.crate_def_map(krate);
96 let (_, module_data) = crate_def_map.modules.iter().last().unwrap();
97 assert_eq!(module_data.scope.resolutions().count(), 1);
98 });
99 assert!(!format!("{:?}", events).contains("crate_def_map"), "{:#?}", events)
100 }
101}
diff --git a/crates/hir_def/src/nameres/tests/macros.rs b/crates/hir_def/src/nameres/tests/macros.rs
new file mode 100644
index 000000000..e0fb8bdef
--- /dev/null
+++ b/crates/hir_def/src/nameres/tests/macros.rs
@@ -0,0 +1,669 @@
1use super::*;
2
3#[test]
4fn macro_rules_are_globally_visible() {
5 check(
6 r#"
7//- /lib.rs
8macro_rules! structs {
9 ($($i:ident),*) => {
10 $(struct $i { field: u32 } )*
11 }
12}
13structs!(Foo);
14mod nested;
15
16//- /nested.rs
17structs!(Bar, Baz);
18"#,
19 expect![[r#"
20 crate
21 Foo: t
22 nested: t
23
24 crate::nested
25 Bar: t
26 Baz: t
27 "#]],
28 );
29}
30
31#[test]
32fn macro_rules_can_define_modules() {
33 check(
34 r#"
35//- /lib.rs
36macro_rules! m {
37 ($name:ident) => { mod $name; }
38}
39m!(n1);
40mod m { m!(n3) }
41
42//- /n1.rs
43m!(n2)
44//- /n1/n2.rs
45struct X;
46//- /m/n3.rs
47struct Y;
48"#,
49 expect![[r#"
50 crate
51 m: t
52 n1: t
53
54 crate::m
55 n3: t
56
57 crate::m::n3
58 Y: t v
59
60 crate::n1
61 n2: t
62
63 crate::n1::n2
64 X: t v
65 "#]],
66 );
67}
68
69#[test]
70fn macro_rules_from_other_crates_are_visible() {
71 check(
72 r#"
73//- /main.rs crate:main deps:foo
74foo::structs!(Foo, Bar)
75mod bar;
76
77//- /bar.rs
78use crate::*;
79
80//- /lib.rs crate:foo
81#[macro_export]
82macro_rules! structs {
83 ($($i:ident),*) => {
84 $(struct $i { field: u32 } )*
85 }
86}
87"#,
88 expect![[r#"
89 crate
90 Bar: t
91 Foo: t
92 bar: t
93
94 crate::bar
95 Bar: t
96 Foo: t
97 bar: t
98 "#]],
99 );
100}
101
102#[test]
103fn macro_rules_export_with_local_inner_macros_are_visible() {
104 check(
105 r#"
106//- /main.rs crate:main deps:foo
107foo::structs!(Foo, Bar)
108mod bar;
109
110//- /bar.rs
111use crate::*;
112
113//- /lib.rs crate:foo
114#[macro_export(local_inner_macros)]
115macro_rules! structs {
116 ($($i:ident),*) => {
117 $(struct $i { field: u32 } )*
118 }
119}
120"#,
121 expect![[r#"
122 crate
123 Bar: t
124 Foo: t
125 bar: t
126
127 crate::bar
128 Bar: t
129 Foo: t
130 bar: t
131 "#]],
132 );
133}
134
135#[test]
136fn local_inner_macros_makes_local_macros_usable() {
137 check(
138 r#"
139//- /main.rs crate:main deps:foo
140foo::structs!(Foo, Bar);
141mod bar;
142
143//- /bar.rs
144use crate::*;
145
146//- /lib.rs crate:foo
147#[macro_export(local_inner_macros)]
148macro_rules! structs {
149 ($($i:ident),*) => {
150 inner!($($i),*);
151 }
152}
153#[macro_export]
154macro_rules! inner {
155 ($($i:ident),*) => {
156 $(struct $i { field: u32 } )*
157 }
158}
159"#,
160 expect![[r#"
161 crate
162 Bar: t
163 Foo: t
164 bar: t
165
166 crate::bar
167 Bar: t
168 Foo: t
169 bar: t
170 "#]],
171 );
172}
173
174#[test]
175fn unexpanded_macro_should_expand_by_fixedpoint_loop() {
176 check(
177 r#"
178//- /main.rs crate:main deps:foo
179macro_rules! baz {
180 () => {
181 use foo::bar;
182 }
183}
184foo!();
185bar!();
186baz!();
187
188//- /lib.rs crate:foo
189#[macro_export]
190macro_rules! foo {
191 () => {
192 struct Foo { field: u32 }
193 }
194}
195#[macro_export]
196macro_rules! bar {
197 () => {
198 use foo::foo;
199 }
200}
201"#,
202 expect![[r#"
203 crate
204 Foo: t
205 bar: m
206 foo: m
207 "#]],
208 );
209}
210
211#[test]
212fn macro_rules_from_other_crates_are_visible_with_macro_use() {
213 mark::check!(macro_rules_from_other_crates_are_visible_with_macro_use);
214 check(
215 r#"
216//- /main.rs crate:main deps:foo
217structs!(Foo);
218structs_priv!(Bar);
219structs_not_exported!(MacroNotResolved1);
220crate::structs!(MacroNotResolved2);
221
222mod bar;
223
224#[macro_use]
225extern crate foo;
226
227//- /bar.rs
228structs!(Baz);
229crate::structs!(MacroNotResolved3);
230
231//- /lib.rs crate:foo
232#[macro_export]
233macro_rules! structs {
234 ($i:ident) => { struct $i; }
235}
236
237macro_rules! structs_not_exported {
238 ($i:ident) => { struct $i; }
239}
240
241mod priv_mod {
242 #[macro_export]
243 macro_rules! structs_priv {
244 ($i:ident) => { struct $i; }
245 }
246}
247"#,
248 expect![[r#"
249 crate
250 Bar: t v
251 Foo: t v
252 bar: t
253 foo: t
254
255 crate::bar
256 Baz: t v
257 "#]],
258 );
259}
260
261#[test]
262fn prelude_is_macro_use() {
263 mark::check!(prelude_is_macro_use);
264 check(
265 r#"
266//- /main.rs crate:main deps:foo
267structs!(Foo);
268structs_priv!(Bar);
269structs_outside!(Out);
270crate::structs!(MacroNotResolved2);
271
272mod bar;
273
274//- /bar.rs
275structs!(Baz);
276crate::structs!(MacroNotResolved3);
277
278//- /lib.rs crate:foo
279#[prelude_import]
280use self::prelude::*;
281
282mod prelude {
283 #[macro_export]
284 macro_rules! structs {
285 ($i:ident) => { struct $i; }
286 }
287
288 mod priv_mod {
289 #[macro_export]
290 macro_rules! structs_priv {
291 ($i:ident) => { struct $i; }
292 }
293 }
294}
295
296#[macro_export]
297macro_rules! structs_outside {
298 ($i:ident) => { struct $i; }
299}
300"#,
301 expect![[r#"
302 crate
303 Bar: t v
304 Foo: t v
305 Out: t v
306 bar: t
307
308 crate::bar
309 Baz: t v
310 "#]],
311 );
312}
313
314#[test]
315fn prelude_cycle() {
316 check(
317 r#"
318#[prelude_import]
319use self::prelude::*;
320
321declare_mod!();
322
323mod prelude {
324 macro_rules! declare_mod {
325 () => (mod foo {})
326 }
327}
328"#,
329 expect![[r#"
330 crate
331 prelude: t
332
333 crate::prelude
334 "#]],
335 );
336}
337
338#[test]
339fn plain_macros_are_legacy_textual_scoped() {
340 check(
341 r#"
342//- /main.rs
343mod m1;
344bar!(NotFoundNotMacroUse);
345
346mod m2 { foo!(NotFoundBeforeInside2); }
347
348macro_rules! foo {
349 ($x:ident) => { struct $x; }
350}
351foo!(Ok);
352
353mod m3;
354foo!(OkShadowStop);
355bar!(NotFoundMacroUseStop);
356
357#[macro_use]
358mod m5 {
359 #[macro_use]
360 mod m6 {
361 macro_rules! foo {
362 ($x:ident) => { fn $x() {} }
363 }
364 }
365}
366foo!(ok_double_macro_use_shadow);
367
368baz!(NotFoundBefore);
369#[macro_use]
370mod m7 {
371 macro_rules! baz {
372 ($x:ident) => { struct $x; }
373 }
374}
375baz!(OkAfter);
376
377//- /m1.rs
378foo!(NotFoundBeforeInside1);
379macro_rules! bar {
380 ($x:ident) => { struct $x; }
381}
382
383//- /m3/mod.rs
384foo!(OkAfterInside);
385macro_rules! foo {
386 ($x:ident) => { fn $x() {} }
387}
388foo!(ok_shadow);
389
390#[macro_use]
391mod m4;
392bar!(OkMacroUse);
393
394//- /m3/m4.rs
395foo!(ok_shadow_deep);
396macro_rules! bar {
397 ($x:ident) => { struct $x; }
398}
399"#,
400 expect![[r#"
401 crate
402 Ok: t v
403 OkAfter: t v
404 OkShadowStop: t v
405 m1: t
406 m2: t
407 m3: t
408 m5: t
409 m7: t
410 ok_double_macro_use_shadow: v
411
412 crate::m7
413
414 crate::m1
415
416 crate::m5
417 m6: t
418
419 crate::m5::m6
420
421 crate::m2
422
423 crate::m3
424 OkAfterInside: t v
425 OkMacroUse: t v
426 m4: t
427 ok_shadow: v
428
429 crate::m3::m4
430 ok_shadow_deep: v
431 "#]],
432 );
433}
434
435#[test]
436fn type_value_macro_live_in_different_scopes() {
437 check(
438 r#"
439#[macro_export]
440macro_rules! foo {
441 ($x:ident) => { type $x = (); }
442}
443
444foo!(foo);
445use foo as bar;
446
447use self::foo as baz;
448fn baz() {}
449"#,
450 expect![[r#"
451 crate
452 bar: t m
453 baz: t v m
454 foo: t m
455 "#]],
456 );
457}
458
459#[test]
460fn macro_use_can_be_aliased() {
461 check(
462 r#"
463//- /main.rs crate:main deps:foo
464#[macro_use]
465extern crate foo;
466
467foo!(Direct);
468bar!(Alias);
469
470//- /lib.rs crate:foo
471use crate::foo as bar;
472
473mod m {
474 #[macro_export]
475 macro_rules! foo {
476 ($x:ident) => { struct $x; }
477 }
478}
479"#,
480 expect![[r#"
481 crate
482 Alias: t v
483 Direct: t v
484 foo: t
485 "#]],
486 );
487}
488
489#[test]
490fn path_qualified_macros() {
491 check(
492 r#"
493macro_rules! foo {
494 ($x:ident) => { struct $x; }
495}
496
497crate::foo!(NotResolved);
498
499crate::bar!(OkCrate);
500bar!(OkPlain);
501alias1!(NotHere);
502m::alias1!(OkAliasPlain);
503m::alias2!(OkAliasSuper);
504m::alias3!(OkAliasCrate);
505not_found!(NotFound);
506
507mod m {
508 #[macro_export]
509 macro_rules! bar {
510 ($x:ident) => { struct $x; }
511 }
512 pub use bar as alias1;
513 pub use super::bar as alias2;
514 pub use crate::bar as alias3;
515 pub use self::bar as not_found;
516}
517"#,
518 expect![[r#"
519 crate
520 OkAliasCrate: t v
521 OkAliasPlain: t v
522 OkAliasSuper: t v
523 OkCrate: t v
524 OkPlain: t v
525 bar: m
526 m: t
527
528 crate::m
529 alias1: m
530 alias2: m
531 alias3: m
532 not_found: _
533 "#]],
534 );
535}
536
537#[test]
538fn macro_dollar_crate_is_correct_in_item() {
539 mark::check!(macro_dollar_crate_self);
540 check(
541 r#"
542//- /main.rs crate:main deps:foo
543#[macro_use]
544extern crate foo;
545
546#[macro_use]
547mod m {
548 macro_rules! current {
549 () => {
550 use $crate::Foo as FooSelf;
551 }
552 }
553}
554
555struct Foo;
556
557current!();
558not_current1!();
559foo::not_current2!();
560
561//- /lib.rs crate:foo
562mod m {
563 #[macro_export]
564 macro_rules! not_current1 {
565 () => {
566 use $crate::Bar;
567 }
568 }
569}
570
571#[macro_export]
572macro_rules! not_current2 {
573 () => {
574 use $crate::Baz;
575 }
576}
577
578struct Bar;
579struct Baz;
580"#,
581 expect![[r#"
582 crate
583 Bar: t v
584 Baz: t v
585 Foo: t v
586 FooSelf: t v
587 foo: t
588 m: t
589
590 crate::m
591 "#]],
592 );
593}
594
595#[test]
596fn macro_dollar_crate_is_correct_in_indirect_deps() {
597 mark::check!(macro_dollar_crate_other);
598 // From std
599 check(
600 r#"
601//- /main.rs crate:main deps:std
602foo!();
603
604//- /std.rs crate:std deps:core
605#[prelude_import]
606use self::prelude::*;
607
608pub use core::foo;
609
610mod prelude {}
611
612#[macro_use]
613mod std_macros;
614
615//- /core.rs crate:core
616#[macro_export]
617macro_rules! foo {
618 () => {
619 use $crate::bar;
620 }
621}
622
623pub struct bar;
624"#,
625 expect![[r#"
626 crate
627 bar: t v
628 "#]],
629 );
630}
631
632#[test]
633fn expand_derive() {
634 let map = compute_crate_def_map(
635 "
636 //- /main.rs
637 #[derive(Copy, Clone)]
638 struct Foo;
639 ",
640 );
641 assert_eq!(map.modules[map.root].scope.impls().len(), 2);
642}
643
644#[test]
645fn macro_expansion_overflow() {
646 mark::check!(macro_expansion_overflow);
647 check(
648 r#"
649macro_rules! a {
650 ($e:expr; $($t:tt)*) => {
651 b!($($t)*);
652 };
653 () => {};
654}
655
656macro_rules! b {
657 (static = $e:expr; $($t:tt)*) => {
658 a!($e; $($t)*);
659 };
660 () => {};
661}
662
663b! { static = #[] (); }
664"#,
665 expect![[r#"
666 crate
667 "#]],
668 );
669}
diff --git a/crates/hir_def/src/nameres/tests/mod_resolution.rs b/crates/hir_def/src/nameres/tests/mod_resolution.rs
new file mode 100644
index 000000000..1f619787e
--- /dev/null
+++ b/crates/hir_def/src/nameres/tests/mod_resolution.rs
@@ -0,0 +1,796 @@
1use super::*;
2
3#[test]
4fn name_res_works_for_broken_modules() {
5 mark::check!(name_res_works_for_broken_modules);
6 check(
7 r"
8//- /lib.rs
9mod foo // no `;`, no body
10use self::foo::Baz;
11
12//- /foo/mod.rs
13pub mod bar;
14pub use self::bar::Baz;
15
16//- /foo/bar.rs
17pub struct Baz;
18",
19 expect![[r#"
20 crate
21 Baz: _
22 foo: t
23
24 crate::foo
25 "#]],
26 );
27}
28
29#[test]
30fn nested_module_resolution() {
31 check(
32 r#"
33//- /lib.rs
34mod n1;
35
36//- /n1.rs
37mod n2;
38
39//- /n1/n2.rs
40struct X;
41"#,
42 expect![[r#"
43 crate
44 n1: t
45
46 crate::n1
47 n2: t
48
49 crate::n1::n2
50 X: t v
51 "#]],
52 );
53}
54
55#[test]
56fn nested_module_resolution_2() {
57 check(
58 r#"
59//- /lib.rs
60mod prelude;
61mod iter;
62
63//- /prelude.rs
64pub use crate::iter::Iterator;
65
66//- /iter.rs
67pub use self::traits::Iterator;
68mod traits;
69
70//- /iter/traits.rs
71pub use self::iterator::Iterator;
72mod iterator;
73
74//- /iter/traits/iterator.rs
75pub trait Iterator;
76"#,
77 expect![[r#"
78 crate
79 iter: t
80 prelude: t
81
82 crate::iter
83 Iterator: t
84 traits: t
85
86 crate::iter::traits
87 Iterator: t
88 iterator: t
89
90 crate::iter::traits::iterator
91 Iterator: t
92
93 crate::prelude
94 Iterator: t
95 "#]],
96 );
97}
98
99#[test]
100fn module_resolution_works_for_non_standard_filenames() {
101 check(
102 r#"
103//- /my_library.rs crate:my_library
104mod foo;
105use self::foo::Bar;
106
107//- /foo/mod.rs
108pub struct Bar;
109"#,
110 expect![[r#"
111 crate
112 Bar: t v
113 foo: t
114
115 crate::foo
116 Bar: t v
117 "#]],
118 );
119}
120
121#[test]
122fn module_resolution_works_for_raw_modules() {
123 check(
124 r#"
125//- /lib.rs
126mod r#async;
127use self::r#async::Bar;
128
129//- /async.rs
130pub struct Bar;
131"#,
132 expect![[r#"
133 crate
134 Bar: t v
135 async: t
136
137 crate::async
138 Bar: t v
139 "#]],
140 );
141}
142
143#[test]
144fn module_resolution_decl_path() {
145 check(
146 r#"
147//- /lib.rs
148#[path = "bar/baz/foo.rs"]
149mod foo;
150use self::foo::Bar;
151
152//- /bar/baz/foo.rs
153pub struct Bar;
154"#,
155 expect![[r#"
156 crate
157 Bar: t v
158 foo: t
159
160 crate::foo
161 Bar: t v
162 "#]],
163 );
164}
165
166#[test]
167fn module_resolution_module_with_path_in_mod_rs() {
168 check(
169 r#"
170//- /main.rs
171mod foo;
172
173//- /foo/mod.rs
174#[path = "baz.rs"]
175pub mod bar;
176use self::bar::Baz;
177
178//- /foo/baz.rs
179pub struct Baz;
180"#,
181 expect![[r#"
182 crate
183 foo: t
184
185 crate::foo
186 Baz: t v
187 bar: t
188
189 crate::foo::bar
190 Baz: t v
191 "#]],
192 );
193}
194
195#[test]
196fn module_resolution_module_with_path_non_crate_root() {
197 check(
198 r#"
199//- /main.rs
200mod foo;
201
202//- /foo.rs
203#[path = "baz.rs"]
204pub mod bar;
205use self::bar::Baz;
206
207//- /baz.rs
208pub struct Baz;
209"#,
210 expect![[r#"
211 crate
212 foo: t
213
214 crate::foo
215 Baz: t v
216 bar: t
217
218 crate::foo::bar
219 Baz: t v
220 "#]],
221 );
222}
223
224#[test]
225fn module_resolution_module_decl_path_super() {
226 check(
227 r#"
228//- /main.rs
229#[path = "bar/baz/module.rs"]
230mod foo;
231pub struct Baz;
232
233//- /bar/baz/module.rs
234use super::Baz;
235"#,
236 expect![[r#"
237 crate
238 Baz: t v
239 foo: t
240
241 crate::foo
242 Baz: t v
243 "#]],
244 );
245}
246
247#[test]
248fn module_resolution_explicit_path_mod_rs() {
249 check(
250 r#"
251//- /main.rs
252#[path = "module/mod.rs"]
253mod foo;
254
255//- /module/mod.rs
256pub struct Baz;
257"#,
258 expect![[r#"
259 crate
260 foo: t
261
262 crate::foo
263 Baz: t v
264 "#]],
265 );
266}
267
268#[test]
269fn module_resolution_relative_path() {
270 check(
271 r#"
272//- /main.rs
273mod foo;
274
275//- /foo.rs
276#[path = "./sub.rs"]
277pub mod foo_bar;
278
279//- /sub.rs
280pub struct Baz;
281"#,
282 expect![[r#"
283 crate
284 foo: t
285
286 crate::foo
287 foo_bar: t
288
289 crate::foo::foo_bar
290 Baz: t v
291 "#]],
292 );
293}
294
295#[test]
296fn module_resolution_relative_path_2() {
297 check(
298 r#"
299//- /main.rs
300mod foo;
301
302//- /foo/mod.rs
303#[path="../sub.rs"]
304pub mod foo_bar;
305
306//- /sub.rs
307pub struct Baz;
308"#,
309 expect![[r#"
310 crate
311 foo: t
312
313 crate::foo
314 foo_bar: t
315
316 crate::foo::foo_bar
317 Baz: t v
318 "#]],
319 );
320}
321
322#[test]
323fn module_resolution_relative_path_outside_root() {
324 check(
325 r#"
326//- /main.rs
327#[path="../../../../../outside.rs"]
328mod foo;
329"#,
330 expect![[r#"
331 crate
332 "#]],
333 );
334}
335
336#[test]
337fn module_resolution_explicit_path_mod_rs_2() {
338 check(
339 r#"
340//- /main.rs
341#[path = "module/bar/mod.rs"]
342mod foo;
343
344//- /module/bar/mod.rs
345pub struct Baz;
346"#,
347 expect![[r#"
348 crate
349 foo: t
350
351 crate::foo
352 Baz: t v
353 "#]],
354 );
355}
356
357#[test]
358fn module_resolution_explicit_path_mod_rs_with_win_separator() {
359 check(
360 r#"
361//- /main.rs
362#[path = "module\bar\mod.rs"]
363mod foo;
364
365//- /module/bar/mod.rs
366pub struct Baz;
367"#,
368 expect![[r#"
369 crate
370 foo: t
371
372 crate::foo
373 Baz: t v
374 "#]],
375 );
376}
377
378#[test]
379fn module_resolution_decl_inside_inline_module_with_path_attribute() {
380 check(
381 r#"
382//- /main.rs
383#[path = "models"]
384mod foo { mod bar; }
385
386//- /models/bar.rs
387pub struct Baz;
388"#,
389 expect![[r#"
390 crate
391 foo: t
392
393 crate::foo
394 bar: t
395
396 crate::foo::bar
397 Baz: t v
398 "#]],
399 );
400}
401
402#[test]
403fn module_resolution_decl_inside_inline_module() {
404 check(
405 r#"
406//- /main.rs
407mod foo { mod bar; }
408
409//- /foo/bar.rs
410pub struct Baz;
411"#,
412 expect![[r#"
413 crate
414 foo: t
415
416 crate::foo
417 bar: t
418
419 crate::foo::bar
420 Baz: t v
421 "#]],
422 );
423}
424
425#[test]
426fn module_resolution_decl_inside_inline_module_2_with_path_attribute() {
427 check(
428 r#"
429//- /main.rs
430#[path = "models/db"]
431mod foo { mod bar; }
432
433//- /models/db/bar.rs
434pub struct Baz;
435"#,
436 expect![[r#"
437 crate
438 foo: t
439
440 crate::foo
441 bar: t
442
443 crate::foo::bar
444 Baz: t v
445 "#]],
446 );
447}
448
449#[test]
450fn module_resolution_decl_inside_inline_module_3() {
451 check(
452 r#"
453//- /main.rs
454#[path = "models/db"]
455mod foo {
456 #[path = "users.rs"]
457 mod bar;
458}
459
460//- /models/db/users.rs
461pub struct Baz;
462"#,
463 expect![[r#"
464 crate
465 foo: t
466
467 crate::foo
468 bar: t
469
470 crate::foo::bar
471 Baz: t v
472 "#]],
473 );
474}
475
476#[test]
477fn module_resolution_decl_inside_inline_module_empty_path() {
478 check(
479 r#"
480//- /main.rs
481#[path = ""]
482mod foo {
483 #[path = "users.rs"]
484 mod bar;
485}
486
487//- /users.rs
488pub struct Baz;
489"#,
490 expect![[r#"
491 crate
492 foo: t
493
494 crate::foo
495 bar: t
496
497 crate::foo::bar
498 Baz: t v
499 "#]],
500 );
501}
502
503#[test]
504fn module_resolution_decl_empty_path() {
505 check(
506 r#"
507//- /main.rs
508#[path = ""] // Should try to read `/` (a directory)
509mod foo;
510
511//- /foo.rs
512pub struct Baz;
513"#,
514 expect![[r#"
515 crate
516 "#]],
517 );
518}
519
520#[test]
521fn module_resolution_decl_inside_inline_module_relative_path() {
522 check(
523 r#"
524//- /main.rs
525#[path = "./models"]
526mod foo { mod bar; }
527
528//- /models/bar.rs
529pub struct Baz;
530"#,
531 expect![[r#"
532 crate
533 foo: t
534
535 crate::foo
536 bar: t
537
538 crate::foo::bar
539 Baz: t v
540 "#]],
541 );
542}
543
544#[test]
545fn module_resolution_decl_inside_inline_module_in_crate_root() {
546 check(
547 r#"
548//- /main.rs
549mod foo {
550 #[path = "baz.rs"]
551 mod bar;
552}
553use self::foo::bar::Baz;
554
555//- /foo/baz.rs
556pub struct Baz;
557"#,
558 expect![[r#"
559 crate
560 Baz: t v
561 foo: t
562
563 crate::foo
564 bar: t
565
566 crate::foo::bar
567 Baz: t v
568 "#]],
569 );
570}
571
572#[test]
573fn module_resolution_decl_inside_inline_module_in_mod_rs() {
574 check(
575 r#"
576//- /main.rs
577mod foo;
578
579//- /foo/mod.rs
580mod bar {
581 #[path = "qwe.rs"]
582 pub mod baz;
583}
584use self::bar::baz::Baz;
585
586//- /foo/bar/qwe.rs
587pub struct Baz;
588"#,
589 expect![[r#"
590 crate
591 foo: t
592
593 crate::foo
594 Baz: t v
595 bar: t
596
597 crate::foo::bar
598 baz: t
599
600 crate::foo::bar::baz
601 Baz: t v
602 "#]],
603 );
604}
605
606#[test]
607fn module_resolution_decl_inside_inline_module_in_non_crate_root() {
608 check(
609 r#"
610//- /main.rs
611mod foo;
612
613//- /foo.rs
614mod bar {
615 #[path = "qwe.rs"]
616 pub mod baz;
617}
618use self::bar::baz::Baz;
619
620//- /foo/bar/qwe.rs
621pub struct Baz;
622"#,
623 expect![[r#"
624 crate
625 foo: t
626
627 crate::foo
628 Baz: t v
629 bar: t
630
631 crate::foo::bar
632 baz: t
633
634 crate::foo::bar::baz
635 Baz: t v
636 "#]],
637 );
638}
639
640#[test]
641fn module_resolution_decl_inside_inline_module_in_non_crate_root_2() {
642 check(
643 r#"
644//- /main.rs
645mod foo;
646
647//- /foo.rs
648#[path = "bar"]
649mod bar {
650 pub mod baz;
651}
652use self::bar::baz::Baz;
653
654//- /bar/baz.rs
655pub struct Baz;
656"#,
657 expect![[r#"
658 crate
659 foo: t
660
661 crate::foo
662 Baz: t v
663 bar: t
664
665 crate::foo::bar
666 baz: t
667
668 crate::foo::bar::baz
669 Baz: t v
670 "#]],
671 );
672}
673
674#[test]
675fn unresolved_module_diagnostics() {
676 let db = TestDB::with_files(
677 r"
678 //- /lib.rs
679 mod foo;
680 mod bar;
681 mod baz {}
682 //- /foo.rs
683 ",
684 );
685 let krate = db.test_crate();
686
687 let crate_def_map = db.crate_def_map(krate);
688
689 expect![[r#"
690 [
691 UnresolvedModule {
692 module: Idx::<ModuleData>(0),
693 declaration: InFile {
694 file_id: HirFileId(
695 FileId(
696 FileId(
697 0,
698 ),
699 ),
700 ),
701 value: FileAstId::<syntax::ast::generated::nodes::Module>(1),
702 },
703 candidate: "bar.rs",
704 },
705 ]
706 "#]]
707 .assert_debug_eq(&crate_def_map.diagnostics);
708}
709
710#[test]
711fn module_resolution_decl_inside_module_in_non_crate_root_2() {
712 check(
713 r#"
714//- /main.rs
715#[path="module/m2.rs"]
716mod module;
717
718//- /module/m2.rs
719pub mod submod;
720
721//- /module/submod.rs
722pub struct Baz;
723"#,
724 expect![[r#"
725 crate
726 module: t
727
728 crate::module
729 submod: t
730
731 crate::module::submod
732 Baz: t v
733 "#]],
734 );
735}
736
737#[test]
738fn nested_out_of_line_module() {
739 check(
740 r#"
741//- /lib.rs
742mod a {
743 mod b {
744 mod c;
745 }
746}
747
748//- /a/b/c.rs
749struct X;
750"#,
751 expect![[r#"
752 crate
753 a: t
754
755 crate::a
756 b: t
757
758 crate::a::b
759 c: t
760
761 crate::a::b::c
762 X: t v
763 "#]],
764 );
765}
766
767#[test]
768fn nested_out_of_line_module_with_path() {
769 check(
770 r#"
771//- /lib.rs
772mod a {
773 #[path = "d/e"]
774 mod b {
775 mod c;
776 }
777}
778
779//- /a/d/e/c.rs
780struct X;
781"#,
782 expect![[r#"
783 crate
784 a: t
785
786 crate::a
787 b: t
788
789 crate::a::b
790 c: t
791
792 crate::a::b::c
793 X: t v
794 "#]],
795 );
796}
diff --git a/crates/hir_def/src/nameres/tests/primitives.rs b/crates/hir_def/src/nameres/tests/primitives.rs
new file mode 100644
index 000000000..215e8952d
--- /dev/null
+++ b/crates/hir_def/src/nameres/tests/primitives.rs
@@ -0,0 +1,23 @@
1use super::*;
2
3#[test]
4fn primitive_reexport() {
5 check(
6 r#"
7//- /lib.rs
8mod foo;
9use foo::int;
10
11//- /foo.rs
12pub use i32 as int;
13"#,
14 expect![[r#"
15 crate
16 foo: t
17 int: t
18
19 crate::foo
20 int: t
21 "#]],
22 );
23}