aboutsummaryrefslogtreecommitdiff
path: root/crates/ra_hir_def
diff options
context:
space:
mode:
Diffstat (limited to 'crates/ra_hir_def')
-rw-r--r--crates/ra_hir_def/Cargo.toml4
-rw-r--r--crates/ra_hir_def/src/adt.rs14
-rw-r--r--crates/ra_hir_def/src/db.rs15
-rw-r--r--crates/ra_hir_def/src/diagnostics.rs28
-rw-r--r--crates/ra_hir_def/src/lib.rs10
-rw-r--r--crates/ra_hir_def/src/marks.rs14
-rw-r--r--crates/ra_hir_def/src/nameres.rs540
-rw-r--r--crates/ra_hir_def/src/nameres/collector.rs844
-rw-r--r--crates/ra_hir_def/src/nameres/per_ns.rs82
-rw-r--r--crates/ra_hir_def/src/nameres/raw.rs4
-rw-r--r--crates/ra_hir_def/src/nameres/tests.rs522
-rw-r--r--crates/ra_hir_def/src/nameres/tests/globs.rs114
-rw-r--r--crates/ra_hir_def/src/nameres/tests/incremental.rs133
-rw-r--r--crates/ra_hir_def/src/nameres/tests/macros.rs602
-rw-r--r--crates/ra_hir_def/src/nameres/tests/mod_resolution.rs782
-rw-r--r--crates/ra_hir_def/src/nameres/tests/primitives.rs24
-rw-r--r--crates/ra_hir_def/src/test_db.rs76
17 files changed, 3799 insertions, 9 deletions
diff --git a/crates/ra_hir_def/Cargo.toml b/crates/ra_hir_def/Cargo.toml
index 746c907e8..15055db64 100644
--- a/crates/ra_hir_def/Cargo.toml
+++ b/crates/ra_hir_def/Cargo.toml
@@ -19,3 +19,7 @@ test_utils = { path = "../test_utils" }
19mbe = { path = "../ra_mbe", package = "ra_mbe" } 19mbe = { path = "../ra_mbe", package = "ra_mbe" }
20ra_cfg = { path = "../ra_cfg" } 20ra_cfg = { path = "../ra_cfg" }
21tt = { path = "../ra_tt", package = "ra_tt" } 21tt = { path = "../ra_tt", package = "ra_tt" }
22
23[dev-dependencies]
24insta = "0.12.0"
25
diff --git a/crates/ra_hir_def/src/adt.rs b/crates/ra_hir_def/src/adt.rs
index 22bd469f0..8f41e55d2 100644
--- a/crates/ra_hir_def/src/adt.rs
+++ b/crates/ra_hir_def/src/adt.rs
@@ -8,7 +8,7 @@ use ra_syntax::ast::{self, NameOwner, TypeAscriptionOwner};
8 8
9use crate::{ 9use crate::{
10 db::DefDatabase2, type_ref::TypeRef, AstItemDef, EnumId, LocalEnumVariantId, 10 db::DefDatabase2, type_ref::TypeRef, AstItemDef, EnumId, LocalEnumVariantId,
11 LocalStructFieldId, StructId, 11 LocalStructFieldId, StructId, UnionId,
12}; 12};
13 13
14/// Note that we use `StructData` for unions as well! 14/// Note that we use `StructData` for unions as well!
@@ -56,6 +56,13 @@ impl StructData {
56 let variant_data = Arc::new(variant_data); 56 let variant_data = Arc::new(variant_data);
57 Arc::new(StructData { name, variant_data }) 57 Arc::new(StructData { name, variant_data })
58 } 58 }
59 pub(crate) fn union_data_query(db: &impl DefDatabase2, struct_: UnionId) -> Arc<StructData> {
60 let src = struct_.source(db);
61 let name = src.ast.name().map(|n| n.as_name());
62 let variant_data = VariantData::new(src.ast.kind());
63 let variant_data = Arc::new(variant_data);
64 Arc::new(StructData { name, variant_data })
65 }
59} 66}
60 67
61impl EnumData { 68impl EnumData {
@@ -74,6 +81,11 @@ impl EnumData {
74 .collect(); 81 .collect();
75 Arc::new(EnumData { name, variants }) 82 Arc::new(EnumData { name, variants })
76 } 83 }
84
85 pub(crate) fn variant(&self, name: &Name) -> Option<LocalEnumVariantId> {
86 let (id, _) = self.variants.iter().find(|(_id, data)| data.name.as_ref() == Some(name))?;
87 Some(id)
88 }
77} 89}
78 90
79impl VariantData { 91impl VariantData {
diff --git a/crates/ra_hir_def/src/db.rs b/crates/ra_hir_def/src/db.rs
index f6027013f..a42348101 100644
--- a/crates/ra_hir_def/src/db.rs
+++ b/crates/ra_hir_def/src/db.rs
@@ -2,13 +2,16 @@
2use std::sync::Arc; 2use std::sync::Arc;
3 3
4use hir_expand::{db::AstDatabase, HirFileId}; 4use hir_expand::{db::AstDatabase, HirFileId};
5use ra_db::{salsa, SourceDatabase}; 5use ra_db::{salsa, CrateId, SourceDatabase};
6use ra_syntax::ast; 6use ra_syntax::ast;
7 7
8use crate::{ 8use crate::{
9 adt::{EnumData, StructData}, 9 adt::{EnumData, StructData},
10 nameres::raw::{ImportSourceMap, RawItems}, 10 nameres::{
11 EnumId, StructId, 11 raw::{ImportSourceMap, RawItems},
12 CrateDefMap,
13 },
14 EnumId, StructId, UnionId,
12}; 15};
13 16
14#[salsa::query_group(InternDatabaseStorage)] 17#[salsa::query_group(InternDatabaseStorage)]
@@ -42,9 +45,15 @@ pub trait DefDatabase2: InternDatabase + AstDatabase {
42 #[salsa::invoke(RawItems::raw_items_query)] 45 #[salsa::invoke(RawItems::raw_items_query)]
43 fn raw_items(&self, file_id: HirFileId) -> Arc<RawItems>; 46 fn raw_items(&self, file_id: HirFileId) -> Arc<RawItems>;
44 47
48 #[salsa::invoke(CrateDefMap::crate_def_map_query)]
49 fn crate_def_map(&self, krate: CrateId) -> Arc<CrateDefMap>;
50
45 #[salsa::invoke(StructData::struct_data_query)] 51 #[salsa::invoke(StructData::struct_data_query)]
46 fn struct_data(&self, s: StructId) -> Arc<StructData>; 52 fn struct_data(&self, s: StructId) -> Arc<StructData>;
47 53
54 #[salsa::invoke(StructData::union_data_query)]
55 fn union_data(&self, s: UnionId) -> Arc<StructData>;
56
48 #[salsa::invoke(EnumData::enum_data_query)] 57 #[salsa::invoke(EnumData::enum_data_query)]
49 fn enum_data(&self, e: EnumId) -> Arc<EnumData>; 58 fn enum_data(&self, e: EnumId) -> Arc<EnumData>;
50} 59}
diff --git a/crates/ra_hir_def/src/diagnostics.rs b/crates/ra_hir_def/src/diagnostics.rs
new file mode 100644
index 000000000..1c1ccdecb
--- /dev/null
+++ b/crates/ra_hir_def/src/diagnostics.rs
@@ -0,0 +1,28 @@
1//! Diagnostics produced by `hir_def`.
2
3use std::any::Any;
4
5use hir_expand::diagnostics::Diagnostic;
6use ra_syntax::{ast, AstPtr, SyntaxNodePtr};
7use relative_path::RelativePathBuf;
8
9use hir_expand::{HirFileId, Source};
10
11#[derive(Debug)]
12pub struct UnresolvedModule {
13 pub file: HirFileId,
14 pub decl: AstPtr<ast::Module>,
15 pub candidate: RelativePathBuf,
16}
17
18impl Diagnostic for UnresolvedModule {
19 fn message(&self) -> String {
20 "unresolved module".to_string()
21 }
22 fn source(&self) -> Source<SyntaxNodePtr> {
23 Source { file_id: self.file, ast: self.decl.into() }
24 }
25 fn as_any(&self) -> &(dyn Any + Send + 'static) {
26 self
27 }
28}
diff --git a/crates/ra_hir_def/src/lib.rs b/crates/ra_hir_def/src/lib.rs
index 6d66f481d..63ed2a098 100644
--- a/crates/ra_hir_def/src/lib.rs
+++ b/crates/ra_hir_def/src/lib.rs
@@ -13,6 +13,12 @@ pub mod path;
13pub mod type_ref; 13pub mod type_ref;
14pub mod builtin_type; 14pub mod builtin_type;
15pub mod adt; 15pub mod adt;
16pub mod diagnostics;
17
18#[cfg(test)]
19mod test_db;
20#[cfg(test)]
21mod marks;
16 22
17// FIXME: this should be private 23// FIXME: this should be private
18pub mod nameres; 24pub mod nameres;
@@ -237,8 +243,8 @@ impl AstItemDef<ast::EnumDef> for EnumId {
237// FIXME: rename to `VariantId`, only enums can ave variants 243// FIXME: rename to `VariantId`, only enums can ave variants
238#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] 244#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
239pub struct EnumVariantId { 245pub struct EnumVariantId {
240 parent: EnumId, 246 pub parent: EnumId,
241 local_id: LocalEnumVariantId, 247 pub local_id: LocalEnumVariantId,
242} 248}
243 249
244#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] 250#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
diff --git a/crates/ra_hir_def/src/marks.rs b/crates/ra_hir_def/src/marks.rs
new file mode 100644
index 000000000..0b99eac71
--- /dev/null
+++ b/crates/ra_hir_def/src/marks.rs
@@ -0,0 +1,14 @@
1//! See test_utils/src/marks.rs
2
3test_utils::marks!(
4 bogus_paths
5 name_res_works_for_broken_modules
6 can_import_enum_variant
7 glob_enum
8 glob_across_crates
9 std_prelude
10 macro_rules_from_other_crates_are_visible_with_macro_use
11 prelude_is_macro_use
12 macro_dollar_crate_self
13 macro_dollar_crate_other
14);
diff --git a/crates/ra_hir_def/src/nameres.rs b/crates/ra_hir_def/src/nameres.rs
index 11ba8a777..fbd4248e6 100644
--- a/crates/ra_hir_def/src/nameres.rs
+++ b/crates/ra_hir_def/src/nameres.rs
@@ -1,5 +1,543 @@
1//! FIXME: write short doc here 1//! This module implements import-resolution/macro expansion algorithm.
2//!
3//! The result of this module is `CrateDefMap`: a data structure which contains:
4//!
5//! * a tree of modules for the crate
6//! * for each module, a set of items visible in the module (directly declared
7//! or imported)
8//!
9//! Note that `CrateDefMap` contains fully macro expanded code.
10//!
11//! Computing `CrateDefMap` can be partitioned into several logically
12//! independent "phases". The phases are mutually recursive though, there's no
13//! strict ordering.
14//!
15//! ## Collecting RawItems
16//!
17//! This happens in the `raw` module, which parses a single source file into a
18//! set of top-level items. Nested imports are desugared to flat imports in
19//! this phase. Macro calls are represented as a triple of (Path, Option<Name>,
20//! TokenTree).
21//!
22//! ## Collecting Modules
23//!
24//! This happens in the `collector` module. In this phase, we recursively walk
25//! tree of modules, collect raw items from submodules, populate module scopes
26//! with defined items (so, we assign item ids in this phase) and record the set
27//! of unresolved imports and macros.
28//!
29//! While we walk tree of modules, we also record macro_rules definitions and
30//! expand calls to macro_rules defined macros.
31//!
32//! ## Resolving Imports
33//!
34//! We maintain a list of currently unresolved imports. On every iteration, we
35//! try to resolve some imports from this list. If the import is resolved, we
36//! record it, by adding an item to current module scope and, if necessary, by
37//! recursively populating glob imports.
38//!
39//! ## Resolving Macros
40//!
41//! macro_rules from the same crate use a global mutable namespace. We expand
42//! them immediately, when we collect modules.
43//!
44//! Macros from other crates (including proc-macros) can be used with
45//! `foo::bar!` syntax. We handle them similarly to imports. There's a list of
46//! unexpanded macros. On every iteration, we try to resolve each macro call
47//! path and, upon success, we run macro expansion and "collect module" phase
48//! on the result
2 49
3// FIXME: review privacy of submodules 50// FIXME: review privacy of submodules
4pub mod raw; 51pub mod raw;
52pub mod per_ns;
53pub mod collector;
5pub mod mod_resolution; 54pub mod mod_resolution;
55
56#[cfg(test)]
57mod tests;
58
59use std::sync::Arc;
60
61use hir_expand::{diagnostics::DiagnosticSink, name::Name, MacroDefId};
62use once_cell::sync::Lazy;
63use ra_arena::Arena;
64use ra_db::{CrateId, Edition, FileId};
65use ra_prof::profile;
66use ra_syntax::ast;
67use rustc_hash::{FxHashMap, FxHashSet};
68use test_utils::tested_by;
69
70use crate::{
71 builtin_type::BuiltinType,
72 db::DefDatabase2,
73 nameres::{diagnostics::DefDiagnostic, per_ns::PerNs, raw::ImportId},
74 path::{Path, PathKind},
75 AdtId, AstId, CrateModuleId, EnumVariantId, ModuleDefId, ModuleId, TraitId,
76};
77
78/// Contains all top-level defs from a macro-expanded crate
79#[derive(Debug, PartialEq, Eq)]
80pub struct CrateDefMap {
81 krate: CrateId,
82 edition: Edition,
83 /// The prelude module for this crate. This either comes from an import
84 /// marked with the `prelude_import` attribute, or (in the normal case) from
85 /// a dependency (`std` or `core`).
86 prelude: Option<ModuleId>,
87 extern_prelude: FxHashMap<Name, ModuleDefId>,
88 root: CrateModuleId,
89 pub modules: Arena<CrateModuleId, ModuleData>,
90
91 /// Some macros are not well-behavior, which leads to infinite loop
92 /// e.g. macro_rules! foo { ($ty:ty) => { foo!($ty); } }
93 /// We mark it down and skip it in collector
94 ///
95 /// FIXME:
96 /// Right now it only handle a poison macro in a single crate,
97 /// such that if other crate try to call that macro,
98 /// the whole process will do again until it became poisoned in that crate.
99 /// We should handle this macro set globally
100 /// However, do we want to put it as a global variable?
101 poison_macros: FxHashSet<MacroDefId>,
102
103 diagnostics: Vec<DefDiagnostic>,
104}
105
106impl std::ops::Index<CrateModuleId> for CrateDefMap {
107 type Output = ModuleData;
108 fn index(&self, id: CrateModuleId) -> &ModuleData {
109 &self.modules[id]
110 }
111}
112
113#[derive(Default, Debug, PartialEq, Eq)]
114pub struct ModuleData {
115 pub parent: Option<CrateModuleId>,
116 pub children: FxHashMap<Name, CrateModuleId>,
117 pub scope: ModuleScope,
118 /// None for root
119 pub declaration: Option<AstId<ast::Module>>,
120 /// None for inline modules.
121 ///
122 /// Note that non-inline modules, by definition, live inside non-macro file.
123 pub definition: Option<FileId>,
124}
125
126#[derive(Debug, Default, PartialEq, Eq, Clone)]
127pub struct ModuleScope {
128 pub items: FxHashMap<Name, Resolution>,
129 /// Macros visable in current module in legacy textual scope
130 ///
131 /// For macros invoked by an unquatified identifier like `bar!()`, `legacy_macros` will be searched in first.
132 /// If it yields no result, then it turns to module scoped `macros`.
133 /// It macros with name quatified with a path like `crate::foo::bar!()`, `legacy_macros` will be skipped,
134 /// and only normal scoped `macros` will be searched in.
135 ///
136 /// Note that this automatically inherit macros defined textually before the definition of module itself.
137 ///
138 /// Module scoped macros will be inserted into `items` instead of here.
139 // FIXME: Macro shadowing in one module is not properly handled. Non-item place macros will
140 // be all resolved to the last one defined if shadowing happens.
141 legacy_macros: FxHashMap<Name, MacroDefId>,
142}
143
144static BUILTIN_SCOPE: Lazy<FxHashMap<Name, Resolution>> = Lazy::new(|| {
145 BuiltinType::ALL
146 .iter()
147 .map(|(name, ty)| {
148 (name.clone(), Resolution { def: PerNs::types(ty.clone().into()), import: None })
149 })
150 .collect()
151});
152
153/// Legacy macros can only be accessed through special methods like `get_legacy_macros`.
154/// Other methods will only resolve values, types and module scoped macros only.
155impl ModuleScope {
156 pub fn entries<'a>(&'a self) -> impl Iterator<Item = (&'a Name, &'a Resolution)> + 'a {
157 //FIXME: shadowing
158 self.items.iter().chain(BUILTIN_SCOPE.iter())
159 }
160
161 /// Iterate over all module scoped macros
162 pub fn macros<'a>(&'a self) -> impl Iterator<Item = (&'a Name, MacroDefId)> + 'a {
163 self.items
164 .iter()
165 .filter_map(|(name, res)| res.def.get_macros().map(|macro_| (name, macro_)))
166 }
167
168 /// Iterate over all legacy textual scoped macros visable at the end of the module
169 pub fn legacy_macros<'a>(&'a self) -> impl Iterator<Item = (&'a Name, MacroDefId)> + 'a {
170 self.legacy_macros.iter().map(|(name, def)| (name, *def))
171 }
172
173 /// Get a name from current module scope, legacy macros are not included
174 pub fn get(&self, name: &Name) -> Option<&Resolution> {
175 self.items.get(name).or_else(|| BUILTIN_SCOPE.get(name))
176 }
177
178 pub fn traits<'a>(&'a self) -> impl Iterator<Item = TraitId> + 'a {
179 self.items.values().filter_map(|r| match r.def.take_types() {
180 Some(ModuleDefId::TraitId(t)) => Some(t),
181 _ => None,
182 })
183 }
184
185 fn get_legacy_macro(&self, name: &Name) -> Option<MacroDefId> {
186 self.legacy_macros.get(name).copied()
187 }
188}
189
190#[derive(Debug, Clone, PartialEq, Eq, Default)]
191pub struct Resolution {
192 /// None for unresolved
193 pub def: PerNs,
194 /// ident by which this is imported into local scope.
195 pub import: Option<ImportId>,
196}
197
198impl Resolution {
199 pub(crate) fn from_macro(macro_: MacroDefId) -> Self {
200 Resolution { def: PerNs::macros(macro_), import: None }
201 }
202}
203
204#[derive(Debug, Clone)]
205struct ResolvePathResult {
206 resolved_def: PerNs,
207 segment_index: Option<usize>,
208 reached_fixedpoint: ReachedFixedPoint,
209}
210
211impl ResolvePathResult {
212 fn empty(reached_fixedpoint: ReachedFixedPoint) -> ResolvePathResult {
213 ResolvePathResult::with(PerNs::none(), reached_fixedpoint, None)
214 }
215
216 fn with(
217 resolved_def: PerNs,
218 reached_fixedpoint: ReachedFixedPoint,
219 segment_index: Option<usize>,
220 ) -> ResolvePathResult {
221 ResolvePathResult { resolved_def, reached_fixedpoint, segment_index }
222 }
223}
224
225#[derive(Debug, Clone, Copy, PartialEq, Eq)]
226enum ResolveMode {
227 Import,
228 Other,
229}
230
231#[derive(Debug, Clone, Copy, PartialEq, Eq)]
232enum ReachedFixedPoint {
233 Yes,
234 No,
235}
236
237impl CrateDefMap {
238 pub(crate) fn crate_def_map_query(
239 // Note that this doesn't have `+ AstDatabase`!
240 // This gurantess that `CrateDefMap` is stable across reparses.
241 db: &impl DefDatabase2,
242 krate: CrateId,
243 ) -> Arc<CrateDefMap> {
244 let _p = profile("crate_def_map_query");
245 let def_map = {
246 let crate_graph = db.crate_graph();
247 let edition = crate_graph.edition(krate);
248 let mut modules: Arena<CrateModuleId, ModuleData> = Arena::default();
249 let root = modules.alloc(ModuleData::default());
250 CrateDefMap {
251 krate,
252 edition,
253 extern_prelude: FxHashMap::default(),
254 prelude: None,
255 root,
256 modules,
257 poison_macros: FxHashSet::default(),
258 diagnostics: Vec::new(),
259 }
260 };
261 let def_map = collector::collect_defs(db, def_map);
262 Arc::new(def_map)
263 }
264
265 pub fn krate(&self) -> CrateId {
266 self.krate
267 }
268
269 pub fn root(&self) -> CrateModuleId {
270 self.root
271 }
272
273 pub fn prelude(&self) -> Option<ModuleId> {
274 self.prelude
275 }
276
277 pub fn extern_prelude(&self) -> &FxHashMap<Name, ModuleDefId> {
278 &self.extern_prelude
279 }
280
281 pub fn add_diagnostics(
282 &self,
283 db: &impl DefDatabase2,
284 module: CrateModuleId,
285 sink: &mut DiagnosticSink,
286 ) {
287 self.diagnostics.iter().for_each(|it| it.add_to(db, module, sink))
288 }
289
290 pub fn resolve_path(
291 &self,
292 db: &impl DefDatabase2,
293 original_module: CrateModuleId,
294 path: &Path,
295 ) -> (PerNs, Option<usize>) {
296 let res = self.resolve_path_fp_with_macro(db, ResolveMode::Other, original_module, path);
297 (res.resolved_def, res.segment_index)
298 }
299
300 // Returns Yes if we are sure that additions to `ItemMap` wouldn't change
301 // the result.
302 fn resolve_path_fp_with_macro(
303 &self,
304 db: &impl DefDatabase2,
305 mode: ResolveMode,
306 original_module: CrateModuleId,
307 path: &Path,
308 ) -> ResolvePathResult {
309 let mut segments = path.segments.iter().enumerate();
310 let mut curr_per_ns: PerNs = match path.kind {
311 PathKind::DollarCrate(krate) => {
312 if krate == self.krate {
313 tested_by!(macro_dollar_crate_self);
314 PerNs::types(ModuleId { krate: self.krate, module_id: self.root }.into())
315 } else {
316 let def_map = db.crate_def_map(krate);
317 let module = ModuleId { krate, module_id: def_map.root };
318 tested_by!(macro_dollar_crate_other);
319 PerNs::types(module.into())
320 }
321 }
322 PathKind::Crate => {
323 PerNs::types(ModuleId { krate: self.krate, module_id: self.root }.into())
324 }
325 PathKind::Self_ => {
326 PerNs::types(ModuleId { krate: self.krate, module_id: original_module }.into())
327 }
328 // plain import or absolute path in 2015: crate-relative with
329 // fallback to extern prelude (with the simplification in
330 // rust-lang/rust#57745)
331 // FIXME there must be a nicer way to write this condition
332 PathKind::Plain | PathKind::Abs
333 if self.edition == Edition::Edition2015
334 && (path.kind == PathKind::Abs || mode == ResolveMode::Import) =>
335 {
336 let segment = match segments.next() {
337 Some((_, segment)) => segment,
338 None => return ResolvePathResult::empty(ReachedFixedPoint::Yes),
339 };
340 log::debug!("resolving {:?} in crate root (+ extern prelude)", segment);
341 self.resolve_name_in_crate_root_or_extern_prelude(&segment.name)
342 }
343 PathKind::Plain => {
344 let segment = match segments.next() {
345 Some((_, segment)) => segment,
346 None => return ResolvePathResult::empty(ReachedFixedPoint::Yes),
347 };
348 log::debug!("resolving {:?} in module", segment);
349 self.resolve_name_in_module(db, original_module, &segment.name)
350 }
351 PathKind::Super => {
352 if let Some(p) = self.modules[original_module].parent {
353 PerNs::types(ModuleId { krate: self.krate, module_id: p }.into())
354 } else {
355 log::debug!("super path in root module");
356 return ResolvePathResult::empty(ReachedFixedPoint::Yes);
357 }
358 }
359 PathKind::Abs => {
360 // 2018-style absolute path -- only extern prelude
361 let segment = match segments.next() {
362 Some((_, segment)) => segment,
363 None => return ResolvePathResult::empty(ReachedFixedPoint::Yes),
364 };
365 if let Some(def) = self.extern_prelude.get(&segment.name) {
366 log::debug!("absolute path {:?} resolved to crate {:?}", path, def);
367 PerNs::types(*def)
368 } else {
369 return ResolvePathResult::empty(ReachedFixedPoint::No); // extern crate declarations can add to the extern prelude
370 }
371 }
372 PathKind::Type(_) => {
373 // This is handled in `infer::infer_path_expr`
374 // The result returned here does not matter
375 return ResolvePathResult::empty(ReachedFixedPoint::Yes);
376 }
377 };
378
379 for (i, segment) in segments {
380 let curr = match curr_per_ns.take_types() {
381 Some(r) => r,
382 None => {
383 // we still have path segments left, but the path so far
384 // didn't resolve in the types namespace => no resolution
385 // (don't break here because `curr_per_ns` might contain
386 // something in the value namespace, and it would be wrong
387 // to return that)
388 return ResolvePathResult::empty(ReachedFixedPoint::No);
389 }
390 };
391 // resolve segment in curr
392
393 curr_per_ns = match curr {
394 ModuleDefId::ModuleId(module) => {
395 if module.krate != self.krate {
396 let path =
397 Path { segments: path.segments[i..].to_vec(), kind: PathKind::Self_ };
398 log::debug!("resolving {:?} in other crate", path);
399 let defp_map = db.crate_def_map(module.krate);
400 let (def, s) = defp_map.resolve_path(db, module.module_id, &path);
401 return ResolvePathResult::with(
402 def,
403 ReachedFixedPoint::Yes,
404 s.map(|s| s + i),
405 );
406 }
407
408 // Since it is a qualified path here, it should not contains legacy macros
409 match self[module.module_id].scope.get(&segment.name) {
410 Some(res) => res.def,
411 _ => {
412 log::debug!("path segment {:?} not found", segment.name);
413 return ResolvePathResult::empty(ReachedFixedPoint::No);
414 }
415 }
416 }
417 ModuleDefId::AdtId(AdtId::EnumId(e)) => {
418 // enum variant
419 tested_by!(can_import_enum_variant);
420 let enum_data = db.enum_data(e);
421 match enum_data.variant(&segment.name) {
422 Some(local_id) => {
423 let variant = EnumVariantId { parent: e, local_id };
424 PerNs::both(variant.into(), variant.into())
425 }
426 None => {
427 return ResolvePathResult::with(
428 PerNs::types(e.into()),
429 ReachedFixedPoint::Yes,
430 Some(i),
431 );
432 }
433 }
434 }
435 s => {
436 // could be an inherent method call in UFCS form
437 // (`Struct::method`), or some other kind of associated item
438 log::debug!(
439 "path segment {:?} resolved to non-module {:?}, but is not last",
440 segment.name,
441 curr,
442 );
443
444 return ResolvePathResult::with(
445 PerNs::types(s),
446 ReachedFixedPoint::Yes,
447 Some(i),
448 );
449 }
450 };
451 }
452 ResolvePathResult::with(curr_per_ns, ReachedFixedPoint::Yes, None)
453 }
454
455 fn resolve_name_in_crate_root_or_extern_prelude(&self, name: &Name) -> PerNs {
456 let from_crate_root =
457 self[self.root].scope.get(name).map_or_else(PerNs::none, |res| res.def);
458 let from_extern_prelude = self.resolve_name_in_extern_prelude(name);
459
460 from_crate_root.or(from_extern_prelude)
461 }
462
463 pub(crate) fn resolve_name_in_module(
464 &self,
465 db: &impl DefDatabase2,
466 module: CrateModuleId,
467 name: &Name,
468 ) -> PerNs {
469 // Resolve in:
470 // - legacy scope of macro
471 // - current module / scope
472 // - extern prelude
473 // - std prelude
474 let from_legacy_macro =
475 self[module].scope.get_legacy_macro(name).map_or_else(PerNs::none, PerNs::macros);
476 let from_scope = self[module].scope.get(name).map_or_else(PerNs::none, |res| res.def);
477 let from_extern_prelude =
478 self.extern_prelude.get(name).map_or(PerNs::none(), |&it| PerNs::types(it));
479 let from_prelude = self.resolve_in_prelude(db, name);
480
481 from_legacy_macro.or(from_scope).or(from_extern_prelude).or(from_prelude)
482 }
483
484 fn resolve_name_in_extern_prelude(&self, name: &Name) -> PerNs {
485 self.extern_prelude.get(name).map_or(PerNs::none(), |&it| PerNs::types(it))
486 }
487
488 fn resolve_in_prelude(&self, db: &impl DefDatabase2, name: &Name) -> PerNs {
489 if let Some(prelude) = self.prelude {
490 let keep;
491 let def_map = if prelude.krate == self.krate {
492 self
493 } else {
494 // Extend lifetime
495 keep = db.crate_def_map(prelude.krate);
496 &keep
497 };
498 def_map[prelude.module_id].scope.get(name).map_or_else(PerNs::none, |res| res.def)
499 } else {
500 PerNs::none()
501 }
502 }
503}
504
505mod diagnostics {
506 use hir_expand::diagnostics::DiagnosticSink;
507 use ra_syntax::{ast, AstPtr};
508 use relative_path::RelativePathBuf;
509
510 use crate::{db::DefDatabase2, diagnostics::UnresolvedModule, nameres::CrateModuleId, AstId};
511
512 #[derive(Debug, PartialEq, Eq)]
513 pub(super) enum DefDiagnostic {
514 UnresolvedModule {
515 module: CrateModuleId,
516 declaration: AstId<ast::Module>,
517 candidate: RelativePathBuf,
518 },
519 }
520
521 impl DefDiagnostic {
522 pub(super) fn add_to(
523 &self,
524 db: &impl DefDatabase2,
525 target_module: CrateModuleId,
526 sink: &mut DiagnosticSink,
527 ) {
528 match self {
529 DefDiagnostic::UnresolvedModule { module, declaration, candidate } => {
530 if *module != target_module {
531 return;
532 }
533 let decl = declaration.to_node(db);
534 sink.push(UnresolvedModule {
535 file: declaration.file_id(),
536 decl: AstPtr::new(&decl),
537 candidate: candidate.clone(),
538 })
539 }
540 }
541 }
542 }
543}
diff --git a/crates/ra_hir_def/src/nameres/collector.rs b/crates/ra_hir_def/src/nameres/collector.rs
new file mode 100644
index 000000000..3b61d9895
--- /dev/null
+++ b/crates/ra_hir_def/src/nameres/collector.rs
@@ -0,0 +1,844 @@
1//! FIXME: write short doc here
2
3use hir_expand::{
4 name::{self, AsName, Name},
5 HirFileId, MacroCallId, MacroCallLoc, MacroDefId, MacroFileKind,
6};
7use ra_cfg::CfgOptions;
8use ra_db::{CrateId, FileId};
9use ra_syntax::{ast, SmolStr};
10use rustc_hash::FxHashMap;
11use test_utils::tested_by;
12
13use crate::{
14 attr::Attr,
15 db::DefDatabase2,
16 nameres::{
17 diagnostics::DefDiagnostic, mod_resolution::ModDir, per_ns::PerNs, raw, CrateDefMap,
18 ModuleData, ReachedFixedPoint, Resolution, ResolveMode,
19 },
20 path::{Path, PathKind},
21 AdtId, AstId, AstItemDef, ConstId, CrateModuleId, EnumId, EnumVariantId, FunctionId,
22 LocationCtx, ModuleDefId, ModuleId, StaticId, StructId, TraitId, TypeAliasId, UnionId,
23};
24
25pub(super) fn collect_defs(db: &impl DefDatabase2, mut def_map: CrateDefMap) -> CrateDefMap {
26 let crate_graph = db.crate_graph();
27
28 // populate external prelude
29 for dep in crate_graph.dependencies(def_map.krate) {
30 let dep_def_map = db.crate_def_map(dep.crate_id);
31 log::debug!("crate dep {:?} -> {:?}", dep.name, dep.crate_id);
32 def_map.extern_prelude.insert(
33 dep.as_name(),
34 ModuleId { krate: dep.crate_id, module_id: dep_def_map.root }.into(),
35 );
36
37 // look for the prelude
38 if def_map.prelude.is_none() {
39 let map = db.crate_def_map(dep.crate_id);
40 if map.prelude.is_some() {
41 def_map.prelude = map.prelude;
42 }
43 }
44 }
45
46 let cfg_options = crate_graph.cfg_options(def_map.krate);
47
48 let mut collector = DefCollector {
49 db,
50 def_map,
51 glob_imports: FxHashMap::default(),
52 unresolved_imports: Vec::new(),
53 unexpanded_macros: Vec::new(),
54 mod_dirs: FxHashMap::default(),
55 macro_stack_monitor: MacroStackMonitor::default(),
56 cfg_options,
57 };
58 collector.collect();
59 collector.finish()
60}
61
62#[derive(Default)]
63struct MacroStackMonitor {
64 counts: FxHashMap<MacroDefId, u32>,
65
66 /// Mainly use for test
67 validator: Option<Box<dyn Fn(u32) -> bool>>,
68}
69
70impl MacroStackMonitor {
71 fn increase(&mut self, macro_def_id: MacroDefId) {
72 *self.counts.entry(macro_def_id).or_default() += 1;
73 }
74
75 fn decrease(&mut self, macro_def_id: MacroDefId) {
76 *self.counts.entry(macro_def_id).or_default() -= 1;
77 }
78
79 fn is_poison(&self, macro_def_id: MacroDefId) -> bool {
80 let cur = *self.counts.get(&macro_def_id).unwrap_or(&0);
81
82 if let Some(validator) = &self.validator {
83 validator(cur)
84 } else {
85 cur > 100
86 }
87 }
88}
89
90/// Walks the tree of module recursively
91struct DefCollector<'a, DB> {
92 db: &'a DB,
93 def_map: CrateDefMap,
94 glob_imports: FxHashMap<CrateModuleId, Vec<(CrateModuleId, raw::ImportId)>>,
95 unresolved_imports: Vec<(CrateModuleId, raw::ImportId, raw::ImportData)>,
96 unexpanded_macros: Vec<(CrateModuleId, AstId<ast::MacroCall>, Path)>,
97 mod_dirs: FxHashMap<CrateModuleId, ModDir>,
98
99 /// Some macro use `$tt:tt which mean we have to handle the macro perfectly
100 /// To prevent stack overflow, we add a deep counter here for prevent that.
101 macro_stack_monitor: MacroStackMonitor,
102
103 cfg_options: &'a CfgOptions,
104}
105
106impl<DB> DefCollector<'_, DB>
107where
108 DB: DefDatabase2,
109{
110 fn collect(&mut self) {
111 let crate_graph = self.db.crate_graph();
112 let file_id = crate_graph.crate_root(self.def_map.krate);
113 let raw_items = self.db.raw_items(file_id.into());
114 let module_id = self.def_map.root;
115 self.def_map.modules[module_id].definition = Some(file_id);
116 ModCollector {
117 def_collector: &mut *self,
118 module_id,
119 file_id: file_id.into(),
120 raw_items: &raw_items,
121 mod_dir: ModDir::root(),
122 }
123 .collect(raw_items.items());
124
125 // main name resolution fixed-point loop.
126 let mut i = 0;
127 loop {
128 self.db.check_canceled();
129 match (self.resolve_imports(), self.resolve_macros()) {
130 (ReachedFixedPoint::Yes, ReachedFixedPoint::Yes) => break,
131 _ => i += 1,
132 }
133 if i == 1000 {
134 log::error!("name resolution is stuck");
135 break;
136 }
137 }
138
139 let unresolved_imports = std::mem::replace(&mut self.unresolved_imports, Vec::new());
140 // show unresolved imports in completion, etc
141 for (module_id, import, import_data) in unresolved_imports {
142 self.record_resolved_import(module_id, PerNs::none(), import, &import_data)
143 }
144 }
145
146 /// Define a macro with `macro_rules`.
147 ///
148 /// It will define the macro in legacy textual scope, and if it has `#[macro_export]`,
149 /// then it is also defined in the root module scope.
150 /// You can `use` or invoke it by `crate::macro_name` anywhere, before or after the definition.
151 ///
152 /// It is surprising that the macro will never be in the current module scope.
153 /// These code fails with "unresolved import/macro",
154 /// ```rust,compile_fail
155 /// mod m { macro_rules! foo { () => {} } }
156 /// use m::foo as bar;
157 /// ```
158 ///
159 /// ```rust,compile_fail
160 /// macro_rules! foo { () => {} }
161 /// self::foo!();
162 /// crate::foo!();
163 /// ```
164 ///
165 /// Well, this code compiles, bacause the plain path `foo` in `use` is searched
166 /// in the legacy textual scope only.
167 /// ```rust
168 /// macro_rules! foo { () => {} }
169 /// use foo as bar;
170 /// ```
171 fn define_macro(
172 &mut self,
173 module_id: CrateModuleId,
174 name: Name,
175 macro_: MacroDefId,
176 export: bool,
177 ) {
178 // Textual scoping
179 self.define_legacy_macro(module_id, name.clone(), macro_);
180
181 // Module scoping
182 // In Rust, `#[macro_export]` macros are unconditionally visible at the
183 // crate root, even if the parent modules is **not** visible.
184 if export {
185 self.update(self.def_map.root, None, &[(name, Resolution::from_macro(macro_))]);
186 }
187 }
188
189 /// Define a legacy textual scoped macro in module
190 ///
191 /// We use a map `legacy_macros` to store all legacy textual scoped macros visable per module.
192 /// It will clone all macros from parent legacy scope, whose definition is prior to
193 /// the definition of current module.
194 /// And also, `macro_use` on a module will import all legacy macros visable inside to
195 /// current legacy scope, with possible shadowing.
196 fn define_legacy_macro(&mut self, module_id: CrateModuleId, name: Name, macro_: MacroDefId) {
197 // Always shadowing
198 self.def_map.modules[module_id].scope.legacy_macros.insert(name, macro_);
199 }
200
201 /// Import macros from `#[macro_use] extern crate`.
202 fn import_macros_from_extern_crate(
203 &mut self,
204 current_module_id: CrateModuleId,
205 import: &raw::ImportData,
206 ) {
207 log::debug!(
208 "importing macros from extern crate: {:?} ({:?})",
209 import,
210 self.def_map.edition,
211 );
212
213 let res = self.def_map.resolve_name_in_extern_prelude(
214 &import
215 .path
216 .as_ident()
217 .expect("extern crate should have been desugared to one-element path"),
218 );
219
220 if let Some(ModuleDefId::ModuleId(m)) = res.take_types() {
221 tested_by!(macro_rules_from_other_crates_are_visible_with_macro_use);
222 self.import_all_macros_exported(current_module_id, m.krate);
223 }
224 }
225
226 /// Import all exported macros from another crate
227 ///
228 /// Exported macros are just all macros in the root module scope.
229 /// Note that it contains not only all `#[macro_export]` macros, but also all aliases
230 /// created by `use` in the root module, ignoring the visibility of `use`.
231 fn import_all_macros_exported(&mut self, current_module_id: CrateModuleId, krate: CrateId) {
232 let def_map = self.db.crate_def_map(krate);
233 for (name, def) in def_map[def_map.root].scope.macros() {
234 // `macro_use` only bring things into legacy scope.
235 self.define_legacy_macro(current_module_id, name.clone(), def);
236 }
237 }
238
239 fn resolve_imports(&mut self) -> ReachedFixedPoint {
240 let mut imports = std::mem::replace(&mut self.unresolved_imports, Vec::new());
241 let mut resolved = Vec::new();
242 imports.retain(|(module_id, import, import_data)| {
243 let (def, fp) = self.resolve_import(*module_id, import_data);
244 if fp == ReachedFixedPoint::Yes {
245 resolved.push((*module_id, def, *import, import_data.clone()))
246 }
247 fp == ReachedFixedPoint::No
248 });
249 self.unresolved_imports = imports;
250 // Resolves imports, filling-in module scopes
251 let result =
252 if resolved.is_empty() { ReachedFixedPoint::Yes } else { ReachedFixedPoint::No };
253 for (module_id, def, import, import_data) in resolved {
254 self.record_resolved_import(module_id, def, import, &import_data)
255 }
256 result
257 }
258
259 fn resolve_import(
260 &self,
261 module_id: CrateModuleId,
262 import: &raw::ImportData,
263 ) -> (PerNs, ReachedFixedPoint) {
264 log::debug!("resolving import: {:?} ({:?})", import, self.def_map.edition);
265 if import.is_extern_crate {
266 let res = self.def_map.resolve_name_in_extern_prelude(
267 &import
268 .path
269 .as_ident()
270 .expect("extern crate should have been desugared to one-element path"),
271 );
272 (res, ReachedFixedPoint::Yes)
273 } else {
274 let res = self.def_map.resolve_path_fp_with_macro(
275 self.db,
276 ResolveMode::Import,
277 module_id,
278 &import.path,
279 );
280
281 (res.resolved_def, res.reached_fixedpoint)
282 }
283 }
284
285 fn record_resolved_import(
286 &mut self,
287 module_id: CrateModuleId,
288 def: PerNs,
289 import_id: raw::ImportId,
290 import: &raw::ImportData,
291 ) {
292 if import.is_glob {
293 log::debug!("glob import: {:?}", import);
294 match def.take_types() {
295 Some(ModuleDefId::ModuleId(m)) => {
296 if import.is_prelude {
297 tested_by!(std_prelude);
298 self.def_map.prelude = Some(m);
299 } else if m.krate != self.def_map.krate {
300 tested_by!(glob_across_crates);
301 // glob import from other crate => we can just import everything once
302 let item_map = self.db.crate_def_map(m.krate);
303 let scope = &item_map[m.module_id].scope;
304
305 // Module scoped macros is included
306 let items = scope
307 .items
308 .iter()
309 .map(|(name, res)| (name.clone(), res.clone()))
310 .collect::<Vec<_>>();
311
312 self.update(module_id, Some(import_id), &items);
313 } else {
314 // glob import from same crate => we do an initial
315 // import, and then need to propagate any further
316 // additions
317 let scope = &self.def_map[m.module_id].scope;
318
319 // Module scoped macros is included
320 let items = scope
321 .items
322 .iter()
323 .map(|(name, res)| (name.clone(), res.clone()))
324 .collect::<Vec<_>>();
325
326 self.update(module_id, Some(import_id), &items);
327 // record the glob import in case we add further items
328 self.glob_imports
329 .entry(m.module_id)
330 .or_default()
331 .push((module_id, import_id));
332 }
333 }
334 Some(ModuleDefId::AdtId(AdtId::EnumId(e))) => {
335 tested_by!(glob_enum);
336 // glob import from enum => just import all the variants
337 let enum_data = self.db.enum_data(e);
338 let resolutions = enum_data
339 .variants
340 .iter()
341 .filter_map(|(local_id, variant_data)| {
342 let name = variant_data.name.clone()?;
343 let variant = EnumVariantId { parent: e, local_id };
344 let res = Resolution {
345 def: PerNs::both(variant.into(), variant.into()),
346 import: Some(import_id),
347 };
348 Some((name, res))
349 })
350 .collect::<Vec<_>>();
351 self.update(module_id, Some(import_id), &resolutions);
352 }
353 Some(d) => {
354 log::debug!("glob import {:?} from non-module/enum {:?}", import, d);
355 }
356 None => {
357 log::debug!("glob import {:?} didn't resolve as type", import);
358 }
359 }
360 } else {
361 match import.path.segments.last() {
362 Some(last_segment) => {
363 let name = import.alias.clone().unwrap_or_else(|| last_segment.name.clone());
364 log::debug!("resolved import {:?} ({:?}) to {:?}", name, import, def);
365
366 // extern crates in the crate root are special-cased to insert entries into the extern prelude: rust-lang/rust#54658
367 if import.is_extern_crate && module_id == self.def_map.root {
368 if let Some(def) = def.take_types() {
369 self.def_map.extern_prelude.insert(name.clone(), def);
370 }
371 }
372
373 let resolution = Resolution { def, import: Some(import_id) };
374 self.update(module_id, Some(import_id), &[(name, resolution)]);
375 }
376 None => tested_by!(bogus_paths),
377 }
378 }
379 }
380
381 fn update(
382 &mut self,
383 module_id: CrateModuleId,
384 import: Option<raw::ImportId>,
385 resolutions: &[(Name, Resolution)],
386 ) {
387 self.update_recursive(module_id, import, resolutions, 0)
388 }
389
390 fn update_recursive(
391 &mut self,
392 module_id: CrateModuleId,
393 import: Option<raw::ImportId>,
394 resolutions: &[(Name, Resolution)],
395 depth: usize,
396 ) {
397 if depth > 100 {
398 // prevent stack overflows (but this shouldn't be possible)
399 panic!("infinite recursion in glob imports!");
400 }
401 let module_items = &mut self.def_map.modules[module_id].scope;
402 let mut changed = false;
403 for (name, res) in resolutions {
404 let existing = module_items.items.entry(name.clone()).or_default();
405
406 if existing.def.types.is_none() && res.def.types.is_some() {
407 existing.def.types = res.def.types;
408 existing.import = import.or(res.import);
409 changed = true;
410 }
411 if existing.def.values.is_none() && res.def.values.is_some() {
412 existing.def.values = res.def.values;
413 existing.import = import.or(res.import);
414 changed = true;
415 }
416 if existing.def.macros.is_none() && res.def.macros.is_some() {
417 existing.def.macros = res.def.macros;
418 existing.import = import.or(res.import);
419 changed = true;
420 }
421
422 if existing.def.is_none()
423 && res.def.is_none()
424 && existing.import.is_none()
425 && res.import.is_some()
426 {
427 existing.import = res.import;
428 }
429 }
430
431 if !changed {
432 return;
433 }
434 let glob_imports = self
435 .glob_imports
436 .get(&module_id)
437 .into_iter()
438 .flat_map(|v| v.iter())
439 .cloned()
440 .collect::<Vec<_>>();
441 for (glob_importing_module, glob_import) in glob_imports {
442 // We pass the glob import so that the tracked import in those modules is that glob import
443 self.update_recursive(glob_importing_module, Some(glob_import), resolutions, depth + 1);
444 }
445 }
446
447 fn resolve_macros(&mut self) -> ReachedFixedPoint {
448 let mut macros = std::mem::replace(&mut self.unexpanded_macros, Vec::new());
449 let mut resolved = Vec::new();
450 let mut res = ReachedFixedPoint::Yes;
451 macros.retain(|(module_id, ast_id, path)| {
452 let resolved_res = self.def_map.resolve_path_fp_with_macro(
453 self.db,
454 ResolveMode::Other,
455 *module_id,
456 path,
457 );
458
459 if let Some(def) = resolved_res.resolved_def.get_macros() {
460 let call_id = self.db.intern_macro(MacroCallLoc { def, ast_id: *ast_id });
461 resolved.push((*module_id, call_id, def));
462 res = ReachedFixedPoint::No;
463 return false;
464 }
465
466 true
467 });
468
469 self.unexpanded_macros = macros;
470
471 for (module_id, macro_call_id, macro_def_id) in resolved {
472 self.collect_macro_expansion(module_id, macro_call_id, macro_def_id);
473 }
474
475 res
476 }
477
478 fn collect_macro_expansion(
479 &mut self,
480 module_id: CrateModuleId,
481 macro_call_id: MacroCallId,
482 macro_def_id: MacroDefId,
483 ) {
484 if self.def_map.poison_macros.contains(&macro_def_id) {
485 return;
486 }
487
488 self.macro_stack_monitor.increase(macro_def_id);
489
490 if !self.macro_stack_monitor.is_poison(macro_def_id) {
491 let file_id: HirFileId = macro_call_id.as_file(MacroFileKind::Items);
492 let raw_items = self.db.raw_items(file_id);
493 let mod_dir = self.mod_dirs[&module_id].clone();
494 ModCollector {
495 def_collector: &mut *self,
496 file_id,
497 module_id,
498 raw_items: &raw_items,
499 mod_dir,
500 }
501 .collect(raw_items.items());
502 } else {
503 log::error!("Too deep macro expansion: {:?}", macro_call_id);
504 self.def_map.poison_macros.insert(macro_def_id);
505 }
506
507 self.macro_stack_monitor.decrease(macro_def_id);
508 }
509
510 fn finish(self) -> CrateDefMap {
511 self.def_map
512 }
513}
514
515/// Walks a single module, populating defs, imports and macros
516struct ModCollector<'a, D> {
517 def_collector: D,
518 module_id: CrateModuleId,
519 file_id: HirFileId,
520 raw_items: &'a raw::RawItems,
521 mod_dir: ModDir,
522}
523
524impl<DB> ModCollector<'_, &'_ mut DefCollector<'_, DB>>
525where
526 DB: DefDatabase2,
527{
528 fn collect(&mut self, items: &[raw::RawItem]) {
529 // Note: don't assert that inserted value is fresh: it's simply not true
530 // for macros.
531 self.def_collector.mod_dirs.insert(self.module_id, self.mod_dir.clone());
532
533 // Prelude module is always considered to be `#[macro_use]`.
534 if let Some(prelude_module) = self.def_collector.def_map.prelude {
535 if prelude_module.krate != self.def_collector.def_map.krate {
536 tested_by!(prelude_is_macro_use);
537 self.def_collector.import_all_macros_exported(self.module_id, prelude_module.krate);
538 }
539 }
540
541 // This should be processed eagerly instead of deferred to resolving.
542 // `#[macro_use] extern crate` is hoisted to imports macros before collecting
543 // any other items.
544 for item in items {
545 if self.is_cfg_enabled(item.attrs()) {
546 if let raw::RawItemKind::Import(import_id) = item.kind {
547 let import = self.raw_items[import_id].clone();
548 if import.is_extern_crate && import.is_macro_use {
549 self.def_collector.import_macros_from_extern_crate(self.module_id, &import);
550 }
551 }
552 }
553 }
554
555 for item in items {
556 if self.is_cfg_enabled(item.attrs()) {
557 match item.kind {
558 raw::RawItemKind::Module(m) => {
559 self.collect_module(&self.raw_items[m], item.attrs())
560 }
561 raw::RawItemKind::Import(import_id) => self
562 .def_collector
563 .unresolved_imports
564 .push((self.module_id, import_id, self.raw_items[import_id].clone())),
565 raw::RawItemKind::Def(def) => self.define_def(&self.raw_items[def]),
566 raw::RawItemKind::Macro(mac) => self.collect_macro(&self.raw_items[mac]),
567 }
568 }
569 }
570 }
571
572 fn collect_module(&mut self, module: &raw::ModuleData, attrs: &[Attr]) {
573 let path_attr = self.path_attr(attrs);
574 let is_macro_use = self.is_macro_use(attrs);
575 match module {
576 // inline module, just recurse
577 raw::ModuleData::Definition { name, items, ast_id } => {
578 let module_id =
579 self.push_child_module(name.clone(), AstId::new(self.file_id, *ast_id), None);
580
581 ModCollector {
582 def_collector: &mut *self.def_collector,
583 module_id,
584 file_id: self.file_id,
585 raw_items: self.raw_items,
586 mod_dir: self.mod_dir.descend_into_definition(name, path_attr),
587 }
588 .collect(&*items);
589 if is_macro_use {
590 self.import_all_legacy_macros(module_id);
591 }
592 }
593 // out of line module, resolve, parse and recurse
594 raw::ModuleData::Declaration { name, ast_id } => {
595 let ast_id = AstId::new(self.file_id, *ast_id);
596 match self.mod_dir.resolve_declaration(
597 self.def_collector.db,
598 self.file_id,
599 name,
600 path_attr,
601 ) {
602 Ok((file_id, mod_dir)) => {
603 let module_id = self.push_child_module(name.clone(), ast_id, Some(file_id));
604 let raw_items = self.def_collector.db.raw_items(file_id.into());
605 ModCollector {
606 def_collector: &mut *self.def_collector,
607 module_id,
608 file_id: file_id.into(),
609 raw_items: &raw_items,
610 mod_dir,
611 }
612 .collect(raw_items.items());
613 if is_macro_use {
614 self.import_all_legacy_macros(module_id);
615 }
616 }
617 Err(candidate) => self.def_collector.def_map.diagnostics.push(
618 DefDiagnostic::UnresolvedModule {
619 module: self.module_id,
620 declaration: ast_id,
621 candidate,
622 },
623 ),
624 };
625 }
626 }
627 }
628
629 fn push_child_module(
630 &mut self,
631 name: Name,
632 declaration: AstId<ast::Module>,
633 definition: Option<FileId>,
634 ) -> CrateModuleId {
635 let modules = &mut self.def_collector.def_map.modules;
636 let res = modules.alloc(ModuleData::default());
637 modules[res].parent = Some(self.module_id);
638 modules[res].declaration = Some(declaration);
639 modules[res].definition = definition;
640 modules[res].scope.legacy_macros = modules[self.module_id].scope.legacy_macros.clone();
641 modules[self.module_id].children.insert(name.clone(), res);
642 let resolution = Resolution {
643 def: PerNs::types(
644 ModuleId { krate: self.def_collector.def_map.krate, module_id: res }.into(),
645 ),
646 import: None,
647 };
648 self.def_collector.update(self.module_id, None, &[(name, resolution)]);
649 res
650 }
651
652 fn define_def(&mut self, def: &raw::DefData) {
653 let module =
654 ModuleId { krate: self.def_collector.def_map.krate, module_id: self.module_id };
655 let ctx = LocationCtx::new(self.def_collector.db, module, self.file_id);
656
657 let name = def.name.clone();
658 let def: PerNs = match def.kind {
659 raw::DefKind::Function(ast_id) => {
660 PerNs::values(FunctionId::from_ast_id(ctx, ast_id).into())
661 }
662 raw::DefKind::Struct(ast_id) => {
663 let s = StructId::from_ast_id(ctx, ast_id).into();
664 PerNs::both(s, s)
665 }
666 raw::DefKind::Union(ast_id) => {
667 let s = UnionId::from_ast_id(ctx, ast_id).into();
668 PerNs::both(s, s)
669 }
670 raw::DefKind::Enum(ast_id) => PerNs::types(EnumId::from_ast_id(ctx, ast_id).into()),
671 raw::DefKind::Const(ast_id) => PerNs::values(ConstId::from_ast_id(ctx, ast_id).into()),
672 raw::DefKind::Static(ast_id) => {
673 PerNs::values(StaticId::from_ast_id(ctx, ast_id).into())
674 }
675 raw::DefKind::Trait(ast_id) => PerNs::types(TraitId::from_ast_id(ctx, ast_id).into()),
676 raw::DefKind::TypeAlias(ast_id) => {
677 PerNs::types(TypeAliasId::from_ast_id(ctx, ast_id).into())
678 }
679 };
680 let resolution = Resolution { def, import: None };
681 self.def_collector.update(self.module_id, None, &[(name, resolution)])
682 }
683
684 fn collect_macro(&mut self, mac: &raw::MacroData) {
685 let ast_id = AstId::new(self.file_id, mac.ast_id);
686
687 // Case 1: macro rules, define a macro in crate-global mutable scope
688 if is_macro_rules(&mac.path) {
689 if let Some(name) = &mac.name {
690 let macro_id = MacroDefId { ast_id, krate: self.def_collector.def_map.krate };
691 self.def_collector.define_macro(self.module_id, name.clone(), macro_id, mac.export);
692 }
693 return;
694 }
695
696 // Case 2: try to resolve in legacy scope and expand macro_rules, triggering
697 // recursive item collection.
698 if let Some(macro_def) = mac.path.as_ident().and_then(|name| {
699 self.def_collector.def_map[self.module_id].scope.get_legacy_macro(&name)
700 }) {
701 let macro_call_id =
702 self.def_collector.db.intern_macro(MacroCallLoc { def: macro_def, ast_id });
703
704 self.def_collector.collect_macro_expansion(self.module_id, macro_call_id, macro_def);
705 return;
706 }
707
708 // Case 3: resolve in module scope, expand during name resolution.
709 // We rewrite simple path `macro_name` to `self::macro_name` to force resolve in module scope only.
710 let mut path = mac.path.clone();
711 if path.is_ident() {
712 path.kind = PathKind::Self_;
713 }
714 self.def_collector.unexpanded_macros.push((self.module_id, ast_id, path));
715 }
716
717 fn import_all_legacy_macros(&mut self, module_id: CrateModuleId) {
718 let macros = self.def_collector.def_map[module_id].scope.legacy_macros.clone();
719 for (name, macro_) in macros {
720 self.def_collector.define_legacy_macro(self.module_id, name.clone(), macro_);
721 }
722 }
723
724 fn is_cfg_enabled(&self, attrs: &[Attr]) -> bool {
725 attrs.iter().all(|attr| attr.is_cfg_enabled(&self.def_collector.cfg_options) != Some(false))
726 }
727
728 fn path_attr<'a>(&self, attrs: &'a [Attr]) -> Option<&'a SmolStr> {
729 attrs.iter().find_map(|attr| attr.as_path())
730 }
731
732 fn is_macro_use<'a>(&self, attrs: &'a [Attr]) -> bool {
733 attrs.iter().any(|attr| attr.is_simple_atom("macro_use"))
734 }
735}
736
737fn is_macro_rules(path: &Path) -> bool {
738 path.as_ident() == Some(&name::MACRO_RULES)
739}
740
741#[cfg(test)]
742mod tests {
743 use ra_arena::Arena;
744 use ra_db::{fixture::WithFixture, SourceDatabase};
745 use rustc_hash::FxHashSet;
746
747 use crate::{db::DefDatabase2, test_db::TestDB};
748
749 use super::*;
750
751 fn do_collect_defs(
752 db: &impl DefDatabase2,
753 def_map: CrateDefMap,
754 monitor: MacroStackMonitor,
755 ) -> CrateDefMap {
756 let mut collector = DefCollector {
757 db,
758 def_map,
759 glob_imports: FxHashMap::default(),
760 unresolved_imports: Vec::new(),
761 unexpanded_macros: Vec::new(),
762 mod_dirs: FxHashMap::default(),
763 macro_stack_monitor: monitor,
764 cfg_options: &CfgOptions::default(),
765 };
766 collector.collect();
767 collector.finish()
768 }
769
770 fn do_limited_resolve(code: &str, limit: u32, poison_limit: u32) -> CrateDefMap {
771 let (db, _file_id) = TestDB::with_single_file(&code);
772 let krate = db.crate_graph().iter().next().unwrap();
773
774 let def_map = {
775 let edition = db.crate_graph().edition(krate);
776 let mut modules: Arena<CrateModuleId, ModuleData> = Arena::default();
777 let root = modules.alloc(ModuleData::default());
778 CrateDefMap {
779 krate,
780 edition,
781 extern_prelude: FxHashMap::default(),
782 prelude: None,
783 root,
784 modules,
785 poison_macros: FxHashSet::default(),
786 diagnostics: Vec::new(),
787 }
788 };
789
790 let mut monitor = MacroStackMonitor::default();
791 monitor.validator = Some(Box::new(move |count| {
792 assert!(count < limit);
793 count >= poison_limit
794 }));
795
796 do_collect_defs(&db, def_map, monitor)
797 }
798
799 #[test]
800 fn test_macro_expand_limit_width() {
801 do_limited_resolve(
802 r#"
803 macro_rules! foo {
804 ($($ty:ty)*) => { foo!($($ty)*, $($ty)*); }
805 }
806foo!(KABOOM);
807 "#,
808 16,
809 1000,
810 );
811 }
812
813 #[test]
814 fn test_macro_expand_poisoned() {
815 let def = do_limited_resolve(
816 r#"
817 macro_rules! foo {
818 ($ty:ty) => { foo!($ty); }
819 }
820foo!(KABOOM);
821 "#,
822 100,
823 16,
824 );
825
826 assert_eq!(def.poison_macros.len(), 1);
827 }
828
829 #[test]
830 fn test_macro_expand_normal() {
831 let def = do_limited_resolve(
832 r#"
833 macro_rules! foo {
834 ($ident:ident) => { struct $ident {} }
835 }
836foo!(Bar);
837 "#,
838 16,
839 16,
840 );
841
842 assert_eq!(def.poison_macros.len(), 0);
843 }
844}
diff --git a/crates/ra_hir_def/src/nameres/per_ns.rs b/crates/ra_hir_def/src/nameres/per_ns.rs
new file mode 100644
index 000000000..298b0b0c7
--- /dev/null
+++ b/crates/ra_hir_def/src/nameres/per_ns.rs
@@ -0,0 +1,82 @@
1//! FIXME: write short doc here
2
3use hir_expand::MacroDefId;
4
5use crate::ModuleDefId;
6
7#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
8pub enum Namespace {
9 Types,
10 Values,
11 // Note that only type inference uses this enum, and it doesn't care about macros.
12 // Macro,
13}
14
15#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
16pub struct PerNs {
17 pub types: Option<ModuleDefId>,
18 pub values: Option<ModuleDefId>,
19 /// Since macros has different type, many methods simply ignore it.
20 /// We can only use special method like `get_macros` to access it.
21 pub macros: Option<MacroDefId>,
22}
23
24impl Default for PerNs {
25 fn default() -> Self {
26 PerNs { types: None, values: None, macros: None }
27 }
28}
29
30impl PerNs {
31 pub fn none() -> PerNs {
32 PerNs { types: None, values: None, macros: None }
33 }
34
35 pub fn values(t: ModuleDefId) -> PerNs {
36 PerNs { types: None, values: Some(t), macros: None }
37 }
38
39 pub fn types(t: ModuleDefId) -> PerNs {
40 PerNs { types: Some(t), values: None, macros: None }
41 }
42
43 pub fn both(types: ModuleDefId, values: ModuleDefId) -> PerNs {
44 PerNs { types: Some(types), values: Some(values), macros: None }
45 }
46
47 pub fn macros(macro_: MacroDefId) -> PerNs {
48 PerNs { types: None, values: None, macros: Some(macro_) }
49 }
50
51 pub fn is_none(&self) -> bool {
52 self.types.is_none() && self.values.is_none() && self.macros.is_none()
53 }
54
55 pub fn is_all(&self) -> bool {
56 self.types.is_some() && self.values.is_some() && self.macros.is_some()
57 }
58
59 pub fn take_types(self) -> Option<ModuleDefId> {
60 self.types
61 }
62
63 pub fn take_values(self) -> Option<ModuleDefId> {
64 self.values
65 }
66
67 pub fn get_macros(&self) -> Option<MacroDefId> {
68 self.macros
69 }
70
71 pub fn only_macros(&self) -> PerNs {
72 PerNs { types: None, values: None, macros: self.macros }
73 }
74
75 pub fn or(self, other: PerNs) -> PerNs {
76 PerNs {
77 types: self.types.or(other.types),
78 values: self.values.or(other.values),
79 macros: self.macros.or(other.macros),
80 }
81 }
82}
diff --git a/crates/ra_hir_def/src/nameres/raw.rs b/crates/ra_hir_def/src/nameres/raw.rs
index 86c05d602..cb47fa317 100644
--- a/crates/ra_hir_def/src/nameres/raw.rs
+++ b/crates/ra_hir_def/src/nameres/raw.rs
@@ -14,6 +14,7 @@ use ra_syntax::{
14 ast::{self, AttrsOwner, NameOwner}, 14 ast::{self, AttrsOwner, NameOwner},
15 AstNode, AstPtr, SourceFile, 15 AstNode, AstPtr, SourceFile,
16}; 16};
17use test_utils::tested_by;
17 18
18use crate::{attr::Attr, db::DefDatabase2, path::Path, FileAstId, HirFileId, ModuleSource, Source}; 19use crate::{attr::Attr, db::DefDatabase2, path::Path, FileAstId, HirFileId, ModuleSource, Source};
19 20
@@ -297,8 +298,7 @@ impl RawItemsCollector {
297 self.push_item(current_module, attrs, RawItemKind::Module(item)); 298 self.push_item(current_module, attrs, RawItemKind::Module(item));
298 return; 299 return;
299 } 300 }
300 // FIXME: restore this mark once we complete hir splitting 301 tested_by!(name_res_works_for_broken_modules);
301 // tested_by!(name_res_works_for_broken_modules);
302 } 302 }
303 303
304 fn add_use_item(&mut self, current_module: Option<Module>, use_item: ast::UseItem) { 304 fn add_use_item(&mut self, current_module: Option<Module>, use_item: ast::UseItem) {
diff --git a/crates/ra_hir_def/src/nameres/tests.rs b/crates/ra_hir_def/src/nameres/tests.rs
new file mode 100644
index 000000000..52bd0aa91
--- /dev/null
+++ b/crates/ra_hir_def/src/nameres/tests.rs
@@ -0,0 +1,522 @@
1mod globs;
2mod incremental;
3mod macros;
4mod mod_resolution;
5mod primitives;
6
7use std::sync::Arc;
8
9use insta::assert_snapshot;
10use ra_db::{fixture::WithFixture, SourceDatabase};
11use test_utils::covers;
12
13use crate::{db::DefDatabase2, nameres::*, test_db::TestDB, CrateModuleId};
14
15fn def_map(fixtute: &str) -> String {
16 let dm = compute_crate_def_map(fixtute);
17 render_crate_def_map(&dm)
18}
19
20fn compute_crate_def_map(fixture: &str) -> Arc<CrateDefMap> {
21 let db = TestDB::with_files(fixture);
22 let krate = db.crate_graph().iter().next().unwrap();
23 db.crate_def_map(krate)
24}
25
26fn render_crate_def_map(map: &CrateDefMap) -> String {
27 let mut buf = String::new();
28 go(&mut buf, map, "\ncrate", map.root());
29 return buf.trim().to_string();
30
31 fn go(buf: &mut String, map: &CrateDefMap, path: &str, module: CrateModuleId) {
32 *buf += path;
33 *buf += "\n";
34
35 let mut entries = map.modules[module]
36 .scope
37 .items
38 .iter()
39 .map(|(name, res)| (name, res.def))
40 .collect::<Vec<_>>();
41 entries.sort_by_key(|(name, _)| *name);
42
43 for (name, res) in entries {
44 *buf += &format!("{}:", name);
45
46 if res.types.is_some() {
47 *buf += " t";
48 }
49 if res.values.is_some() {
50 *buf += " v";
51 }
52 if res.macros.is_some() {
53 *buf += " m";
54 }
55 if res.is_none() {
56 *buf += " _";
57 }
58
59 *buf += "\n";
60 }
61
62 for (name, child) in map.modules[module].children.iter() {
63 let path = path.to_string() + &format!("::{}", name);
64 go(buf, map, &path, *child);
65 }
66 }
67}
68
69#[test]
70fn crate_def_map_smoke_test() {
71 let map = def_map(
72 "
73 //- /lib.rs
74 mod foo;
75 struct S;
76 use crate::foo::bar::E;
77 use self::E::V;
78
79 //- /foo/mod.rs
80 pub mod bar;
81 fn f() {}
82
83 //- /foo/bar.rs
84 pub struct Baz;
85 enum E { V }
86 ",
87 );
88 assert_snapshot!(map, @r###"
89 ⋮crate
90 ⋮E: t
91 ⋮S: t v
92 ⋮V: t v
93 ⋮foo: t
94
95 ⋮crate::foo
96 ⋮bar: t
97 ⋮f: v
98
99 ⋮crate::foo::bar
100 ⋮Baz: t v
101 ⋮E: t
102 "###)
103}
104
105#[test]
106fn bogus_paths() {
107 covers!(bogus_paths);
108 let map = def_map(
109 "
110 //- /lib.rs
111 mod foo;
112 struct S;
113 use self;
114
115 //- /foo/mod.rs
116 use super;
117 use crate;
118
119 ",
120 );
121 assert_snapshot!(map, @r###"
122 ⋮crate
123 ⋮S: t v
124 ⋮foo: t
125
126 ⋮crate::foo
127 "###
128 )
129}
130
131#[test]
132fn use_as() {
133 let map = def_map(
134 "
135 //- /lib.rs
136 mod foo;
137
138 use crate::foo::Baz as Foo;
139
140 //- /foo/mod.rs
141 pub struct Baz;
142 ",
143 );
144 assert_snapshot!(map,
145 @r###"
146 ⋮crate
147 ⋮Foo: t v
148 ⋮foo: t
149
150 ⋮crate::foo
151 ⋮Baz: t v
152 "###
153 );
154}
155
156#[test]
157fn use_trees() {
158 let map = def_map(
159 "
160 //- /lib.rs
161 mod foo;
162
163 use crate::foo::bar::{Baz, Quux};
164
165 //- /foo/mod.rs
166 pub mod bar;
167
168 //- /foo/bar.rs
169 pub struct Baz;
170 pub enum Quux {};
171 ",
172 );
173 assert_snapshot!(map, @r###"
174 ⋮crate
175 ⋮Baz: t v
176 ⋮Quux: t
177 ⋮foo: t
178
179 ⋮crate::foo
180 ⋮bar: t
181
182 ⋮crate::foo::bar
183 ⋮Baz: t v
184 ⋮Quux: t
185 "###);
186}
187
188#[test]
189fn re_exports() {
190 let map = def_map(
191 "
192 //- /lib.rs
193 mod foo;
194
195 use self::foo::Baz;
196
197 //- /foo/mod.rs
198 pub mod bar;
199
200 pub use self::bar::Baz;
201
202 //- /foo/bar.rs
203 pub struct Baz;
204 ",
205 );
206 assert_snapshot!(map, @r###"
207 ⋮crate
208 ⋮Baz: t v
209 ⋮foo: t
210
211 ⋮crate::foo
212 ⋮Baz: t v
213 ⋮bar: t
214
215 ⋮crate::foo::bar
216 ⋮Baz: t v
217 "###);
218}
219
220#[test]
221fn std_prelude() {
222 covers!(std_prelude);
223 let map = def_map(
224 "
225 //- /main.rs crate:main deps:test_crate
226 use Foo::*;
227
228 //- /lib.rs crate:test_crate
229 mod prelude;
230 #[prelude_import]
231 use prelude::*;
232
233 //- /prelude.rs
234 pub enum Foo { Bar, Baz };
235 ",
236 );
237 assert_snapshot!(map, @r###"
238 ⋮crate
239 ⋮Bar: t v
240 ⋮Baz: t v
241 "###);
242}
243
244#[test]
245fn can_import_enum_variant() {
246 covers!(can_import_enum_variant);
247 let map = def_map(
248 "
249 //- /lib.rs
250 enum E { V }
251 use self::E::V;
252 ",
253 );
254 assert_snapshot!(map, @r###"
255 ⋮crate
256 ⋮E: t
257 ⋮V: t v
258 "###
259 );
260}
261
262#[test]
263fn edition_2015_imports() {
264 let map = def_map(
265 "
266 //- /main.rs crate:main deps:other_crate edition:2015
267 mod foo;
268 mod bar;
269
270 //- /bar.rs
271 struct Bar;
272
273 //- /foo.rs
274 use bar::Bar;
275 use other_crate::FromLib;
276
277 //- /lib.rs crate:other_crate edition:2018
278 struct FromLib;
279 ",
280 );
281
282 assert_snapshot!(map, @r###"
283 ⋮crate
284 ⋮bar: t
285 ⋮foo: t
286
287 ⋮crate::bar
288 ⋮Bar: t v
289
290 ⋮crate::foo
291 ⋮Bar: t v
292 ⋮FromLib: t v
293 "###);
294}
295
296#[test]
297fn item_map_using_self() {
298 let map = def_map(
299 "
300 //- /lib.rs
301 mod foo;
302 use crate::foo::bar::Baz::{self};
303 //- /foo/mod.rs
304 pub mod bar;
305 //- /foo/bar.rs
306 pub struct Baz;
307 ",
308 );
309 assert_snapshot!(map, @r###"
310 ⋮crate
311 ⋮Baz: t v
312 ⋮foo: t
313
314 ⋮crate::foo
315 ⋮bar: t
316
317 ⋮crate::foo::bar
318 ⋮Baz: t v
319 "###);
320}
321
322#[test]
323fn item_map_across_crates() {
324 let map = def_map(
325 "
326 //- /main.rs crate:main deps:test_crate
327 use test_crate::Baz;
328
329 //- /lib.rs crate:test_crate
330 pub struct Baz;
331 ",
332 );
333
334 assert_snapshot!(map, @r###"
335 ⋮crate
336 ⋮Baz: t v
337 "###);
338}
339
340#[test]
341fn extern_crate_rename() {
342 let map = def_map(
343 "
344 //- /main.rs crate:main deps:alloc
345 extern crate alloc as alloc_crate;
346
347 mod alloc;
348 mod sync;
349
350 //- /sync.rs
351 use alloc_crate::Arc;
352
353 //- /lib.rs crate:alloc
354 struct Arc;
355 ",
356 );
357
358 assert_snapshot!(map, @r###"
359 ⋮crate
360 ⋮alloc_crate: t
361 ⋮sync: t
362
363 ⋮crate::sync
364 ⋮Arc: t v
365 "###);
366}
367
368#[test]
369fn extern_crate_rename_2015_edition() {
370 let map = def_map(
371 "
372 //- /main.rs crate:main deps:alloc edition:2015
373 extern crate alloc as alloc_crate;
374
375 mod alloc;
376 mod sync;
377
378 //- /sync.rs
379 use alloc_crate::Arc;
380
381 //- /lib.rs crate:alloc
382 struct Arc;
383 ",
384 );
385
386 assert_snapshot!(map,
387 @r###"
388 ⋮crate
389 ⋮alloc_crate: t
390 ⋮sync: t
391
392 ⋮crate::sync
393 ⋮Arc: t v
394 "###
395 );
396}
397
398#[test]
399fn import_across_source_roots() {
400 let map = def_map(
401 "
402 //- /main.rs crate:main deps:test_crate
403 use test_crate::a::b::C;
404
405 //- root /test_crate/
406
407 //- /test_crate/lib.rs crate:test_crate
408 pub mod a {
409 pub mod b {
410 pub struct C;
411 }
412 }
413
414 ",
415 );
416
417 assert_snapshot!(map, @r###"
418 ⋮crate
419 ⋮C: t v
420 "###);
421}
422
423#[test]
424fn reexport_across_crates() {
425 let map = def_map(
426 "
427 //- /main.rs crate:main deps:test_crate
428 use test_crate::Baz;
429
430 //- /lib.rs crate:test_crate
431 pub use foo::Baz;
432
433 mod foo;
434
435 //- /foo.rs
436 pub struct Baz;
437 ",
438 );
439
440 assert_snapshot!(map, @r###"
441 ⋮crate
442 ⋮Baz: t v
443 "###);
444}
445
446#[test]
447fn values_dont_shadow_extern_crates() {
448 let map = def_map(
449 "
450 //- /main.rs crate:main deps:foo
451 fn foo() {}
452 use foo::Bar;
453
454 //- /foo/lib.rs crate:foo
455 pub struct Bar;
456 ",
457 );
458
459 assert_snapshot!(map, @r###"
460 ⋮crate
461 ⋮Bar: t v
462 ⋮foo: v
463 "###);
464}
465
466#[test]
467fn cfg_not_test() {
468 let map = def_map(
469 r#"
470 //- /main.rs crate:main deps:std
471 use {Foo, Bar, Baz};
472
473 //- /lib.rs crate:std
474 #[prelude_import]
475 pub use self::prelude::*;
476 mod prelude {
477 #[cfg(test)]
478 pub struct Foo;
479 #[cfg(not(test))]
480 pub struct Bar;
481 #[cfg(all(not(any()), feature = "foo", feature = "bar", opt = "42"))]
482 pub struct Baz;
483 }
484 "#,
485 );
486
487 assert_snapshot!(map, @r###"
488 ⋮crate
489 ⋮Bar: t v
490 ⋮Baz: _
491 ⋮Foo: _
492 "###);
493}
494
495#[test]
496fn cfg_test() {
497 let map = def_map(
498 r#"
499 //- /main.rs crate:main deps:std
500 use {Foo, Bar, Baz};
501
502 //- /lib.rs crate:std cfg:test,feature=foo,feature=bar,opt=42
503 #[prelude_import]
504 pub use self::prelude::*;
505 mod prelude {
506 #[cfg(test)]
507 pub struct Foo;
508 #[cfg(not(test))]
509 pub struct Bar;
510 #[cfg(all(not(any()), feature = "foo", feature = "bar", opt = "42"))]
511 pub struct Baz;
512 }
513 "#,
514 );
515
516 assert_snapshot!(map, @r###"
517 ⋮crate
518 ⋮Bar: _
519 ⋮Baz: t v
520 ⋮Foo: t v
521 "###);
522}
diff --git a/crates/ra_hir_def/src/nameres/tests/globs.rs b/crates/ra_hir_def/src/nameres/tests/globs.rs
new file mode 100644
index 000000000..5b03fe365
--- /dev/null
+++ b/crates/ra_hir_def/src/nameres/tests/globs.rs
@@ -0,0 +1,114 @@
1use super::*;
2
3#[test]
4fn glob_1() {
5 let map = def_map(
6 "
7 //- /lib.rs
8 mod foo;
9 use foo::*;
10
11 //- /foo/mod.rs
12 pub mod bar;
13 pub use self::bar::Baz;
14 pub struct Foo;
15
16 //- /foo/bar.rs
17 pub struct Baz;
18 ",
19 );
20 assert_snapshot!(map, @r###"
21 ⋮crate
22 ⋮Baz: t v
23 ⋮Foo: t v
24 ⋮bar: t
25 ⋮foo: t
26
27 ⋮crate::foo
28 ⋮Baz: t v
29 ⋮Foo: t v
30 ⋮bar: t
31
32 ⋮crate::foo::bar
33 ⋮Baz: t v
34 "###
35 );
36}
37
38#[test]
39fn glob_2() {
40 let map = def_map(
41 "
42 //- /lib.rs
43 mod foo;
44 use foo::*;
45
46 //- /foo/mod.rs
47 pub mod bar;
48 pub use self::bar::*;
49 pub struct Foo;
50
51 //- /foo/bar.rs
52 pub struct Baz;
53 pub use super::*;
54 ",
55 );
56 assert_snapshot!(map, @r###"
57 ⋮crate
58 ⋮Baz: t v
59 ⋮Foo: t v
60 ⋮bar: t
61 ⋮foo: t
62
63 ⋮crate::foo
64 ⋮Baz: t v
65 ⋮Foo: t v
66 ⋮bar: t
67
68 ⋮crate::foo::bar
69 ⋮Baz: t v
70 ⋮Foo: t v
71 ⋮bar: t
72 "###
73 );
74}
75
76#[test]
77fn glob_across_crates() {
78 covers!(glob_across_crates);
79 let map = def_map(
80 "
81 //- /main.rs crate:main deps:test_crate
82 use test_crate::*;
83
84 //- /lib.rs crate:test_crate
85 pub struct Baz;
86 ",
87 );
88 assert_snapshot!(map, @r###"
89 ⋮crate
90 ⋮Baz: t v
91 "###
92 );
93}
94
95#[test]
96fn glob_enum() {
97 covers!(glob_enum);
98 let map = def_map(
99 "
100 //- /lib.rs
101 enum Foo {
102 Bar, Baz
103 }
104 use self::Foo::*;
105 ",
106 );
107 assert_snapshot!(map, @r###"
108 ⋮crate
109 ⋮Bar: t v
110 ⋮Baz: t v
111 ⋮Foo: t
112 "###
113 );
114}
diff --git a/crates/ra_hir_def/src/nameres/tests/incremental.rs b/crates/ra_hir_def/src/nameres/tests/incremental.rs
new file mode 100644
index 000000000..80dcec62f
--- /dev/null
+++ b/crates/ra_hir_def/src/nameres/tests/incremental.rs
@@ -0,0 +1,133 @@
1use std::sync::Arc;
2
3use ra_db::{SourceDatabase, SourceDatabaseExt};
4
5use super::*;
6
7fn check_def_map_is_not_recomputed(initial: &str, file_change: &str) {
8 let (mut db, pos) = TestDB::with_position(initial);
9 let krate = db.crate_graph().iter().next().unwrap();
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(file_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 "
30 //- /lib.rs
31 mod foo;<|>
32
33 use crate::foo::bar::Baz;
34
35 fn foo() -> i32 {
36 1 + 1
37 }
38 //- /foo/mod.rs
39 pub mod bar;
40
41 //- /foo/bar.rs
42 pub struct Baz;
43 ",
44 "
45 mod foo;
46
47 use crate::foo::bar::Baz;
48
49 fn foo() -> i32 { 92 }
50 ",
51 );
52}
53
54#[test]
55fn adding_inner_items_should_not_invalidate_def_map() {
56 check_def_map_is_not_recomputed(
57 "
58 //- /lib.rs
59 struct S { a: i32}
60 enum E { A }
61 trait T {
62 fn a() {}
63 }
64 mod foo;<|>
65 impl S {
66 fn a() {}
67 }
68 use crate::foo::bar::Baz;
69 //- /foo/mod.rs
70 pub mod bar;
71
72 //- /foo/bar.rs
73 pub struct Baz;
74 ",
75 "
76 struct S { a: i32, b: () }
77 enum E { A, B }
78 trait T {
79 fn a() {}
80 fn b() {}
81 }
82 mod foo;<|>
83 impl S {
84 fn a() {}
85 fn b() {}
86 }
87 use crate::foo::bar::Baz;
88 ",
89 );
90}
91
92#[test]
93fn typing_inside_a_macro_should_not_invalidate_def_map() {
94 let (mut db, pos) = TestDB::with_position(
95 "
96 //- /lib.rs
97 macro_rules! m {
98 ($ident:ident) => {
99 fn f() {
100 $ident + $ident;
101 };
102 }
103 }
104 mod foo;
105
106 //- /foo/mod.rs
107 pub mod bar;
108
109 //- /foo/bar.rs
110 <|>
111 m!(X);
112 ",
113 );
114 let krate = db.crate_graph().iter().next().unwrap();
115 {
116 let events = db.log_executed(|| {
117 let crate_def_map = db.crate_def_map(krate);
118 let (_, module_data) = crate_def_map.modules.iter().last().unwrap();
119 assert_eq!(module_data.scope.items.len(), 1);
120 });
121 assert!(format!("{:?}", events).contains("crate_def_map"), "{:#?}", events)
122 }
123 db.set_file_text(pos.file_id, Arc::new("m!(Y);".to_string()));
124
125 {
126 let events = db.log_executed(|| {
127 let crate_def_map = db.crate_def_map(krate);
128 let (_, module_data) = crate_def_map.modules.iter().last().unwrap();
129 assert_eq!(module_data.scope.items.len(), 1);
130 });
131 assert!(!format!("{:?}", events).contains("crate_def_map"), "{:#?}", events)
132 }
133}
diff --git a/crates/ra_hir_def/src/nameres/tests/macros.rs b/crates/ra_hir_def/src/nameres/tests/macros.rs
new file mode 100644
index 000000000..704065633
--- /dev/null
+++ b/crates/ra_hir_def/src/nameres/tests/macros.rs
@@ -0,0 +1,602 @@
1use super::*;
2
3#[test]
4fn macro_rules_are_globally_visible() {
5 let map = def_map(
6 "
7 //- /lib.rs
8 macro_rules! structs {
9 ($($i:ident),*) => {
10 $(struct $i { field: u32 } )*
11 }
12 }
13 structs!(Foo);
14 mod nested;
15
16 //- /nested.rs
17 structs!(Bar, Baz);
18 ",
19 );
20 assert_snapshot!(map, @r###"
21 ⋮crate
22 ⋮Foo: t v
23 ⋮nested: t
24
25 ⋮crate::nested
26 ⋮Bar: t v
27 ⋮Baz: t v
28 "###);
29}
30
31#[test]
32fn macro_rules_can_define_modules() {
33 let map = def_map(
34 "
35 //- /lib.rs
36 macro_rules! m {
37 ($name:ident) => { mod $name; }
38 }
39 m!(n1);
40
41 mod m {
42 m!(n3)
43 }
44
45 //- /n1.rs
46 m!(n2)
47 //- /n1/n2.rs
48 struct X;
49 //- /m/n3.rs
50 struct Y;
51 ",
52 );
53 assert_snapshot!(map, @r###"
54 crate
55 m: t
56 n1: t
57
58 crate::m
59 n3: t
60
61 crate::m::n3
62 Y: t v
63
64 crate::n1
65 n2: t
66
67 crate::n1::n2
68 X: t v
69 "###);
70}
71
72#[test]
73fn macro_rules_from_other_crates_are_visible() {
74 let map = def_map(
75 "
76 //- /main.rs crate:main deps:foo
77 foo::structs!(Foo, Bar)
78 mod bar;
79
80 //- /bar.rs
81 use crate::*;
82
83 //- /lib.rs crate:foo
84 #[macro_export]
85 macro_rules! structs {
86 ($($i:ident),*) => {
87 $(struct $i { field: u32 } )*
88 }
89 }
90 ",
91 );
92 assert_snapshot!(map, @r###"
93 ⋮crate
94 ⋮Bar: t v
95 ⋮Foo: t v
96 ⋮bar: t
97
98 ⋮crate::bar
99 ⋮Bar: t v
100 ⋮Foo: t v
101 ⋮bar: t
102 "###);
103}
104
105#[test]
106fn macro_rules_export_with_local_inner_macros_are_visible() {
107 let map = def_map(
108 "
109 //- /main.rs crate:main deps:foo
110 foo::structs!(Foo, Bar)
111 mod bar;
112
113 //- /bar.rs
114 use crate::*;
115
116 //- /lib.rs crate:foo
117 #[macro_export(local_inner_macros)]
118 macro_rules! structs {
119 ($($i:ident),*) => {
120 $(struct $i { field: u32 } )*
121 }
122 }
123 ",
124 );
125 assert_snapshot!(map, @r###"
126 ⋮crate
127 ⋮Bar: t v
128 ⋮Foo: t v
129 ⋮bar: t
130
131 ⋮crate::bar
132 ⋮Bar: t v
133 ⋮Foo: t v
134 ⋮bar: t
135 "###);
136}
137
138#[test]
139fn unexpanded_macro_should_expand_by_fixedpoint_loop() {
140 let map = def_map(
141 "
142 //- /main.rs crate:main deps:foo
143 macro_rules! baz {
144 () => {
145 use foo::bar;
146 }
147 }
148
149 foo!();
150 bar!();
151 baz!();
152
153 //- /lib.rs crate:foo
154 #[macro_export]
155 macro_rules! foo {
156 () => {
157 struct Foo { field: u32 }
158 }
159 }
160 #[macro_export]
161 macro_rules! bar {
162 () => {
163 use foo::foo;
164 }
165 }
166 ",
167 );
168 assert_snapshot!(map, @r###"
169 ⋮crate
170 ⋮Foo: t v
171 ⋮bar: m
172 ⋮foo: m
173 "###);
174}
175
176#[test]
177fn macro_rules_from_other_crates_are_visible_with_macro_use() {
178 covers!(macro_rules_from_other_crates_are_visible_with_macro_use);
179 let map = def_map(
180 "
181 //- /main.rs crate:main deps:foo
182 structs!(Foo);
183 structs_priv!(Bar);
184 structs_not_exported!(MacroNotResolved1);
185 crate::structs!(MacroNotResolved2);
186
187 mod bar;
188
189 #[macro_use]
190 extern crate foo;
191
192 //- /bar.rs
193 structs!(Baz);
194 crate::structs!(MacroNotResolved3);
195
196 //- /lib.rs crate:foo
197 #[macro_export]
198 macro_rules! structs {
199 ($i:ident) => { struct $i; }
200 }
201
202 macro_rules! structs_not_exported {
203 ($i:ident) => { struct $i; }
204 }
205
206 mod priv_mod {
207 #[macro_export]
208 macro_rules! structs_priv {
209 ($i:ident) => { struct $i; }
210 }
211 }
212 ",
213 );
214 assert_snapshot!(map, @r###"
215 ⋮crate
216 ⋮Bar: t v
217 ⋮Foo: t v
218 ⋮bar: t
219 ⋮foo: t
220
221 ⋮crate::bar
222 ⋮Baz: t v
223 "###);
224}
225
226#[test]
227fn prelude_is_macro_use() {
228 covers!(prelude_is_macro_use);
229 let map = def_map(
230 "
231 //- /main.rs crate:main deps:foo
232 structs!(Foo);
233 structs_priv!(Bar);
234 structs_outside!(Out);
235 crate::structs!(MacroNotResolved2);
236
237 mod bar;
238
239 //- /bar.rs
240 structs!(Baz);
241 crate::structs!(MacroNotResolved3);
242
243 //- /lib.rs crate:foo
244 #[prelude_import]
245 use self::prelude::*;
246
247 mod prelude {
248 #[macro_export]
249 macro_rules! structs {
250 ($i:ident) => { struct $i; }
251 }
252
253 mod priv_mod {
254 #[macro_export]
255 macro_rules! structs_priv {
256 ($i:ident) => { struct $i; }
257 }
258 }
259 }
260
261 #[macro_export]
262 macro_rules! structs_outside {
263 ($i:ident) => { struct $i; }
264 }
265 ",
266 );
267 assert_snapshot!(map, @r###"
268 ⋮crate
269 ⋮Bar: t v
270 ⋮Foo: t v
271 ⋮Out: t v
272 ⋮bar: t
273
274 ⋮crate::bar
275 ⋮Baz: t v
276 "###);
277}
278
279#[test]
280fn prelude_cycle() {
281 let map = def_map(
282 "
283 //- /lib.rs
284 #[prelude_import]
285 use self::prelude::*;
286
287 declare_mod!();
288
289 mod prelude {
290 macro_rules! declare_mod {
291 () => (mod foo {})
292 }
293 }
294 ",
295 );
296 assert_snapshot!(map, @r###"
297 ⋮crate
298 ⋮prelude: t
299
300 ⋮crate::prelude
301 "###);
302}
303
304#[test]
305fn plain_macros_are_legacy_textual_scoped() {
306 let map = def_map(
307 r#"
308 //- /main.rs
309 mod m1;
310 bar!(NotFoundNotMacroUse);
311
312 mod m2 {
313 foo!(NotFoundBeforeInside2);
314 }
315
316 macro_rules! foo {
317 ($x:ident) => { struct $x; }
318 }
319 foo!(Ok);
320
321 mod m3;
322 foo!(OkShadowStop);
323 bar!(NotFoundMacroUseStop);
324
325 #[macro_use]
326 mod m5 {
327 #[macro_use]
328 mod m6 {
329 macro_rules! foo {
330 ($x:ident) => { fn $x() {} }
331 }
332 }
333 }
334 foo!(ok_double_macro_use_shadow);
335
336 baz!(NotFoundBefore);
337 #[macro_use]
338 mod m7 {
339 macro_rules! baz {
340 ($x:ident) => { struct $x; }
341 }
342 }
343 baz!(OkAfter);
344
345 //- /m1.rs
346 foo!(NotFoundBeforeInside1);
347 macro_rules! bar {
348 ($x:ident) => { struct $x; }
349 }
350
351 //- /m3/mod.rs
352 foo!(OkAfterInside);
353 macro_rules! foo {
354 ($x:ident) => { fn $x() {} }
355 }
356 foo!(ok_shadow);
357
358 #[macro_use]
359 mod m4;
360 bar!(OkMacroUse);
361
362 //- /m3/m4.rs
363 foo!(ok_shadow_deep);
364 macro_rules! bar {
365 ($x:ident) => { struct $x; }
366 }
367 "#,
368 );
369 assert_snapshot!(map, @r###"
370 ⋮crate
371 ⋮Ok: t v
372 ⋮OkAfter: t v
373 ⋮OkShadowStop: t v
374 ⋮m1: t
375 ⋮m2: t
376 ⋮m3: t
377 ⋮m5: t
378 ⋮m7: t
379 ⋮ok_double_macro_use_shadow: v
380
381 ⋮crate::m7
382
383 ⋮crate::m1
384
385 ⋮crate::m5
386 ⋮m6: t
387
388 ⋮crate::m5::m6
389
390 ⋮crate::m2
391
392 ⋮crate::m3
393 ⋮OkAfterInside: t v
394 ⋮OkMacroUse: t v
395 ⋮m4: t
396 ⋮ok_shadow: v
397
398 ⋮crate::m3::m4
399 ⋮ok_shadow_deep: v
400 "###);
401}
402
403#[test]
404fn type_value_macro_live_in_different_scopes() {
405 let map = def_map(
406 "
407 //- /main.rs
408 #[macro_export]
409 macro_rules! foo {
410 ($x:ident) => { type $x = (); }
411 }
412
413 foo!(foo);
414 use foo as bar;
415
416 use self::foo as baz;
417 fn baz() {}
418 ",
419 );
420 assert_snapshot!(map, @r###"
421 ⋮crate
422 ⋮bar: t m
423 ⋮baz: t v m
424 ⋮foo: t m
425 "###);
426}
427
428#[test]
429fn macro_use_can_be_aliased() {
430 let map = def_map(
431 "
432 //- /main.rs crate:main deps:foo
433 #[macro_use]
434 extern crate foo;
435
436 foo!(Direct);
437 bar!(Alias);
438
439 //- /lib.rs crate:foo
440 use crate::foo as bar;
441
442 mod m {
443 #[macro_export]
444 macro_rules! foo {
445 ($x:ident) => { struct $x; }
446 }
447 }
448 ",
449 );
450 assert_snapshot!(map, @r###"
451 ⋮crate
452 ⋮Alias: t v
453 ⋮Direct: t v
454 ⋮foo: t
455 "###);
456}
457
458#[test]
459fn path_qualified_macros() {
460 let map = def_map(
461 "
462 //- /main.rs
463 macro_rules! foo {
464 ($x:ident) => { struct $x; }
465 }
466
467 crate::foo!(NotResolved);
468
469 crate::bar!(OkCrate);
470 bar!(OkPlain);
471 alias1!(NotHere);
472 m::alias1!(OkAliasPlain);
473 m::alias2!(OkAliasSuper);
474 m::alias3!(OkAliasCrate);
475 not_found!(NotFound);
476
477 mod m {
478 #[macro_export]
479 macro_rules! bar {
480 ($x:ident) => { struct $x; }
481 }
482
483 pub use bar as alias1;
484 pub use super::bar as alias2;
485 pub use crate::bar as alias3;
486 pub use self::bar as not_found;
487 }
488 ",
489 );
490 assert_snapshot!(map, @r###"
491 ⋮crate
492 ⋮OkAliasCrate: t v
493 ⋮OkAliasPlain: t v
494 ⋮OkAliasSuper: t v
495 ⋮OkCrate: t v
496 ⋮OkPlain: t v
497 ⋮bar: m
498 ⋮m: t
499
500 ⋮crate::m
501 ⋮alias1: m
502 ⋮alias2: m
503 ⋮alias3: m
504 ⋮not_found: _
505 "###);
506}
507
508#[test]
509fn macro_dollar_crate_is_correct_in_item() {
510 covers!(macro_dollar_crate_self);
511 covers!(macro_dollar_crate_other);
512 let map = def_map(
513 "
514 //- /main.rs crate:main deps:foo
515 #[macro_use]
516 extern crate foo;
517
518 #[macro_use]
519 mod m {
520 macro_rules! current {
521 () => {
522 use $crate::Foo as FooSelf;
523 }
524 }
525 }
526
527 struct Foo;
528
529 current!();
530 not_current1!();
531 foo::not_current2!();
532
533 //- /lib.rs crate:foo
534 mod m {
535 #[macro_export]
536 macro_rules! not_current1 {
537 () => {
538 use $crate::Bar;
539 }
540 }
541 }
542
543 #[macro_export]
544 macro_rules! not_current2 {
545 () => {
546 use $crate::Baz;
547 }
548 }
549
550 struct Bar;
551 struct Baz;
552 ",
553 );
554 assert_snapshot!(map, @r###"
555 ⋮crate
556 ⋮Bar: t v
557 ⋮Baz: t v
558 ⋮Foo: t v
559 ⋮FooSelf: t v
560 ⋮foo: t
561 ⋮m: t
562
563 ⋮crate::m
564 "###);
565}
566
567#[test]
568fn macro_dollar_crate_is_correct_in_indirect_deps() {
569 covers!(macro_dollar_crate_other);
570 // From std
571 let map = def_map(
572 r#"
573 //- /main.rs crate:main deps:std
574 foo!();
575
576 //- /std.rs crate:std deps:core
577 #[prelude_import]
578 use self::prelude::*;
579
580 pub use core::foo;
581
582 mod prelude {}
583
584 #[macro_use]
585 mod std_macros;
586
587 //- /core.rs crate:core
588 #[macro_export]
589 macro_rules! foo {
590 () => {
591 use $crate::bar;
592 }
593 }
594
595 pub struct bar;
596 "#,
597 );
598 assert_snapshot!(map, @r###"
599 ⋮crate
600 ⋮bar: t v
601 "###);
602}
diff --git a/crates/ra_hir_def/src/nameres/tests/mod_resolution.rs b/crates/ra_hir_def/src/nameres/tests/mod_resolution.rs
new file mode 100644
index 000000000..dee364a14
--- /dev/null
+++ b/crates/ra_hir_def/src/nameres/tests/mod_resolution.rs
@@ -0,0 +1,782 @@
1use super::*;
2
3#[test]
4fn name_res_works_for_broken_modules() {
5 covers!(name_res_works_for_broken_modules);
6 let map = def_map(
7 "
8 //- /lib.rs
9 mod foo // no `;`, no body
10
11 use self::foo::Baz;
12
13 //- /foo/mod.rs
14 pub mod bar;
15
16 pub use self::bar::Baz;
17
18 //- /foo/bar.rs
19 pub struct Baz;
20 ",
21 );
22 assert_snapshot!(map, @r###"
23 ⋮crate
24 ⋮Baz: _
25 "###);
26}
27
28#[test]
29fn nested_module_resolution() {
30 let map = def_map(
31 "
32 //- /lib.rs
33 mod n1;
34
35 //- /n1.rs
36 mod n2;
37
38 //- /n1/n2.rs
39 struct X;
40 ",
41 );
42
43 assert_snapshot!(map, @r###"
44 ⋮crate
45 ⋮n1: t
46
47 ⋮crate::n1
48 ⋮n2: t
49
50 ⋮crate::n1::n2
51 ⋮X: t v
52 "###);
53}
54
55#[test]
56fn module_resolution_works_for_non_standard_filenames() {
57 let map = def_map(
58 "
59 //- /my_library.rs crate:my_library
60 mod foo;
61 use self::foo::Bar;
62
63 //- /foo/mod.rs
64 pub struct Bar;
65 ",
66 );
67
68 assert_snapshot!(map, @r###"
69 ⋮crate
70 ⋮Bar: t v
71 ⋮foo: t
72
73 ⋮crate::foo
74 ⋮Bar: t v
75 "###);
76}
77
78#[test]
79fn module_resolution_works_for_raw_modules() {
80 let map = def_map(
81 "
82 //- /lib.rs
83 mod r#async;
84 use self::r#async::Bar;
85
86 //- /async.rs
87 pub struct Bar;
88 ",
89 );
90
91 assert_snapshot!(map, @r###"
92 ⋮crate
93 ⋮Bar: t v
94 ⋮async: t
95
96 ⋮crate::async
97 ⋮Bar: t v
98 "###);
99}
100
101#[test]
102fn module_resolution_decl_path() {
103 let map = def_map(
104 r###"
105 //- /lib.rs
106 #[path = "bar/baz/foo.rs"]
107 mod foo;
108 use self::foo::Bar;
109
110 //- /bar/baz/foo.rs
111 pub struct Bar;
112 "###,
113 );
114
115 assert_snapshot!(map, @r###"
116 ⋮crate
117 ⋮Bar: t v
118 ⋮foo: t
119
120 ⋮crate::foo
121 ⋮Bar: t v
122 "###);
123}
124
125#[test]
126fn module_resolution_module_with_path_in_mod_rs() {
127 let map = def_map(
128 r###"
129 //- /main.rs
130 mod foo;
131
132 //- /foo/mod.rs
133 #[path = "baz.rs"]
134 pub mod bar;
135
136 use self::bar::Baz;
137
138 //- /foo/baz.rs
139 pub struct Baz;
140 "###,
141 );
142
143 assert_snapshot!(map, @r###"
144 ⋮crate
145 ⋮foo: t
146
147 ⋮crate::foo
148 ⋮Baz: t v
149 ⋮bar: t
150
151 ⋮crate::foo::bar
152 ⋮Baz: t v
153 "###);
154}
155
156#[test]
157fn module_resolution_module_with_path_non_crate_root() {
158 let map = def_map(
159 r###"
160 //- /main.rs
161 mod foo;
162
163 //- /foo.rs
164 #[path = "baz.rs"]
165 pub mod bar;
166
167 use self::bar::Baz;
168
169 //- /baz.rs
170 pub struct Baz;
171 "###,
172 );
173
174 assert_snapshot!(map, @r###"
175 ⋮crate
176 ⋮foo: t
177
178 ⋮crate::foo
179 ⋮Baz: t v
180 ⋮bar: t
181
182 ⋮crate::foo::bar
183 ⋮Baz: t v
184 "###);
185}
186
187#[test]
188fn module_resolution_module_decl_path_super() {
189 let map = def_map(
190 r###"
191 //- /main.rs
192 #[path = "bar/baz/module.rs"]
193 mod foo;
194 pub struct Baz;
195
196 //- /bar/baz/module.rs
197 use super::Baz;
198 "###,
199 );
200
201 assert_snapshot!(map, @r###"
202 ⋮crate
203 ⋮Baz: t v
204 ⋮foo: t
205
206 ⋮crate::foo
207 ⋮Baz: t v
208 "###);
209}
210
211#[test]
212fn module_resolution_explicit_path_mod_rs() {
213 let map = def_map(
214 r###"
215 //- /main.rs
216 #[path = "module/mod.rs"]
217 mod foo;
218
219 //- /module/mod.rs
220 pub struct Baz;
221 "###,
222 );
223
224 assert_snapshot!(map, @r###"
225 ⋮crate
226 ⋮foo: t
227
228 ⋮crate::foo
229 ⋮Baz: t v
230 "###);
231}
232
233#[test]
234fn module_resolution_relative_path() {
235 let map = def_map(
236 r###"
237 //- /main.rs
238 mod foo;
239
240 //- /foo.rs
241 #[path = "./sub.rs"]
242 pub mod foo_bar;
243
244 //- /sub.rs
245 pub struct Baz;
246 "###,
247 );
248
249 assert_snapshot!(map, @r###"
250 ⋮crate
251 ⋮foo: t
252
253 ⋮crate::foo
254 ⋮foo_bar: t
255
256 ⋮crate::foo::foo_bar
257 ⋮Baz: t v
258 "###);
259}
260
261#[test]
262fn module_resolution_relative_path_2() {
263 let map = def_map(
264 r###"
265 //- /main.rs
266 mod foo;
267
268 //- /foo/mod.rs
269 #[path="../sub.rs"]
270 pub mod foo_bar;
271
272 //- /sub.rs
273 pub struct Baz;
274 "###,
275 );
276
277 assert_snapshot!(map, @r###"
278 ⋮crate
279 ⋮foo: t
280
281 ⋮crate::foo
282 ⋮foo_bar: t
283
284 ⋮crate::foo::foo_bar
285 ⋮Baz: t v
286 "###);
287}
288
289#[test]
290fn module_resolution_explicit_path_mod_rs_2() {
291 let map = def_map(
292 r###"
293 //- /main.rs
294 #[path = "module/bar/mod.rs"]
295 mod foo;
296
297 //- /module/bar/mod.rs
298 pub struct Baz;
299 "###,
300 );
301
302 assert_snapshot!(map, @r###"
303 ⋮crate
304 ⋮foo: t
305
306 ⋮crate::foo
307 ⋮Baz: t v
308 "###);
309}
310
311#[test]
312fn module_resolution_explicit_path_mod_rs_with_win_separator() {
313 let map = def_map(
314 r###"
315 //- /main.rs
316 #[path = "module\bar\mod.rs"]
317 mod foo;
318
319 //- /module/bar/mod.rs
320 pub struct Baz;
321 "###,
322 );
323
324 assert_snapshot!(map, @r###"
325 ⋮crate
326 ⋮foo: t
327
328 ⋮crate::foo
329 ⋮Baz: t v
330 "###);
331}
332
333#[test]
334fn module_resolution_decl_inside_inline_module_with_path_attribute() {
335 let map = def_map(
336 r###"
337 //- /main.rs
338 #[path = "models"]
339 mod foo {
340 mod bar;
341 }
342
343 //- /models/bar.rs
344 pub struct Baz;
345 "###,
346 );
347
348 assert_snapshot!(map, @r###"
349 ⋮crate
350 ⋮foo: t
351
352 ⋮crate::foo
353 ⋮bar: t
354
355 ⋮crate::foo::bar
356 ⋮Baz: t v
357 "###);
358}
359
360#[test]
361fn module_resolution_decl_inside_inline_module() {
362 let map = def_map(
363 r###"
364 //- /main.rs
365 mod foo {
366 mod bar;
367 }
368
369 //- /foo/bar.rs
370 pub struct Baz;
371 "###,
372 );
373
374 assert_snapshot!(map, @r###"
375 ⋮crate
376 ⋮foo: t
377
378 ⋮crate::foo
379 ⋮bar: t
380
381 ⋮crate::foo::bar
382 ⋮Baz: t v
383 "###);
384}
385
386#[test]
387fn module_resolution_decl_inside_inline_module_2_with_path_attribute() {
388 let map = def_map(
389 r###"
390 //- /main.rs
391 #[path = "models/db"]
392 mod foo {
393 mod bar;
394 }
395
396 //- /models/db/bar.rs
397 pub struct Baz;
398 "###,
399 );
400
401 assert_snapshot!(map, @r###"
402 ⋮crate
403 ⋮foo: t
404
405 ⋮crate::foo
406 ⋮bar: t
407
408 ⋮crate::foo::bar
409 ⋮Baz: t v
410 "###);
411}
412
413#[test]
414fn module_resolution_decl_inside_inline_module_3() {
415 let map = def_map(
416 r###"
417 //- /main.rs
418 #[path = "models/db"]
419 mod foo {
420 #[path = "users.rs"]
421 mod bar;
422 }
423
424 //- /models/db/users.rs
425 pub struct Baz;
426 "###,
427 );
428
429 assert_snapshot!(map, @r###"
430 ⋮crate
431 ⋮foo: t
432
433 ⋮crate::foo
434 ⋮bar: t
435
436 ⋮crate::foo::bar
437 ⋮Baz: t v
438 "###);
439}
440
441#[test]
442fn module_resolution_decl_inside_inline_module_empty_path() {
443 let map = def_map(
444 r###"
445 //- /main.rs
446 #[path = ""]
447 mod foo {
448 #[path = "users.rs"]
449 mod bar;
450 }
451
452 //- /users.rs
453 pub struct Baz;
454 "###,
455 );
456
457 assert_snapshot!(map, @r###"
458 ⋮crate
459 ⋮foo: t
460
461 ⋮crate::foo
462 ⋮bar: t
463
464 ⋮crate::foo::bar
465 ⋮Baz: t v
466 "###);
467}
468
469#[test]
470fn module_resolution_decl_empty_path() {
471 let map = def_map(
472 r###"
473 //- /main.rs
474 #[path = ""] // Should try to read `/` (a directory)
475 mod foo;
476
477 //- /foo.rs
478 pub struct Baz;
479 "###,
480 );
481
482 assert_snapshot!(map, @r###"
483 ⋮crate
484 "###);
485}
486
487#[test]
488fn module_resolution_decl_inside_inline_module_relative_path() {
489 let map = def_map(
490 r###"
491 //- /main.rs
492 #[path = "./models"]
493 mod foo {
494 mod bar;
495 }
496
497 //- /models/bar.rs
498 pub struct Baz;
499 "###,
500 );
501
502 assert_snapshot!(map, @r###"
503 ⋮crate
504 ⋮foo: t
505
506 ⋮crate::foo
507 ⋮bar: t
508
509 ⋮crate::foo::bar
510 ⋮Baz: t v
511 "###);
512}
513
514#[test]
515fn module_resolution_decl_inside_inline_module_in_crate_root() {
516 let map = def_map(
517 r###"
518 //- /main.rs
519 mod foo {
520 #[path = "baz.rs"]
521 mod bar;
522 }
523 use self::foo::bar::Baz;
524
525 //- /foo/baz.rs
526 pub struct Baz;
527 "###,
528 );
529
530 assert_snapshot!(map, @r###"
531 ⋮crate
532 ⋮Baz: t v
533 ⋮foo: t
534
535 ⋮crate::foo
536 ⋮bar: t
537
538 ⋮crate::foo::bar
539 ⋮Baz: t v
540 "###);
541}
542
543#[test]
544fn module_resolution_decl_inside_inline_module_in_mod_rs() {
545 let map = def_map(
546 r###"
547 //- /main.rs
548 mod foo;
549
550 //- /foo/mod.rs
551 mod bar {
552 #[path = "qwe.rs"]
553 pub mod baz;
554 }
555 use self::bar::baz::Baz;
556
557 //- /foo/bar/qwe.rs
558 pub struct Baz;
559 "###,
560 );
561
562 assert_snapshot!(map, @r###"
563 ⋮crate
564 ⋮foo: t
565
566 ⋮crate::foo
567 ⋮Baz: t v
568 ⋮bar: t
569
570 ⋮crate::foo::bar
571 ⋮baz: t
572
573 ⋮crate::foo::bar::baz
574 ⋮Baz: t v
575 "###);
576}
577
578#[test]
579fn module_resolution_decl_inside_inline_module_in_non_crate_root() {
580 let map = def_map(
581 r###"
582 //- /main.rs
583 mod foo;
584
585 //- /foo.rs
586 mod bar {
587 #[path = "qwe.rs"]
588 pub mod baz;
589 }
590 use self::bar::baz::Baz;
591
592 //- /foo/bar/qwe.rs
593 pub struct Baz;
594 "###,
595 );
596
597 assert_snapshot!(map, @r###"
598 ⋮crate
599 ⋮foo: t
600
601 ⋮crate::foo
602 ⋮Baz: t v
603 ⋮bar: t
604
605 ⋮crate::foo::bar
606 ⋮baz: t
607
608 ⋮crate::foo::bar::baz
609 ⋮Baz: t v
610 "###);
611}
612
613#[test]
614fn module_resolution_decl_inside_inline_module_in_non_crate_root_2() {
615 let map = def_map(
616 r###"
617 //- /main.rs
618 mod foo;
619
620 //- /foo.rs
621 #[path = "bar"]
622 mod bar {
623 pub mod baz;
624 }
625 use self::bar::baz::Baz;
626
627 //- /bar/baz.rs
628 pub struct Baz;
629 "###,
630 );
631
632 assert_snapshot!(map, @r###"
633 ⋮crate
634 ⋮foo: t
635
636 ⋮crate::foo
637 ⋮Baz: t v
638 ⋮bar: t
639
640 ⋮crate::foo::bar
641 ⋮baz: t
642
643 ⋮crate::foo::bar::baz
644 ⋮Baz: t v
645 "###);
646}
647
648#[test]
649fn unresolved_module_diagnostics() {
650 let db = TestDB::with_files(
651 r"
652 //- /lib.rs
653 mod foo;
654 mod bar;
655 mod baz {}
656 //- /foo.rs
657 ",
658 );
659 let krate = db.crate_graph().iter().next().unwrap();
660
661 let crate_def_map = db.crate_def_map(krate);
662
663 insta::assert_debug_snapshot!(
664 crate_def_map.diagnostics,
665 @r###"
666 [
667 UnresolvedModule {
668 module: CrateModuleId(
669 0,
670 ),
671 declaration: AstId {
672 file_id: HirFileId(
673 FileId(
674 FileId(
675 0,
676 ),
677 ),
678 ),
679 file_ast_id: FileAstId {
680 raw: ErasedFileAstId(
681 1,
682 ),
683 _ty: PhantomData,
684 },
685 },
686 candidate: "bar.rs",
687 },
688 ]
689 "###
690 );
691}
692
693#[test]
694fn module_resolution_decl_inside_module_in_non_crate_root_2() {
695 let map = def_map(
696 r###"
697 //- /main.rs
698 #[path="module/m2.rs"]
699 mod module;
700
701 //- /module/m2.rs
702 pub mod submod;
703
704 //- /module/submod.rs
705 pub struct Baz;
706 "###,
707 );
708
709 assert_snapshot!(map, @r###"
710 ⋮crate
711 ⋮module: t
712
713 ⋮crate::module
714 ⋮submod: t
715
716 ⋮crate::module::submod
717 ⋮Baz: t v
718 "###);
719}
720
721#[test]
722fn nested_out_of_line_module() {
723 let map = def_map(
724 r###"
725 //- /lib.rs
726 mod a {
727 mod b {
728 mod c;
729 }
730 }
731
732 //- /a/b/c.rs
733 struct X;
734 "###,
735 );
736
737 assert_snapshot!(map, @r###"
738 crate
739 a: t
740
741 crate::a
742 b: t
743
744 crate::a::b
745 c: t
746
747 crate::a::b::c
748 X: t v
749 "###);
750}
751
752#[test]
753fn nested_out_of_line_module_with_path() {
754 let map = def_map(
755 r###"
756 //- /lib.rs
757 mod a {
758 #[path = "d/e"]
759 mod b {
760 mod c;
761 }
762 }
763
764 //- /a/d/e/c.rs
765 struct X;
766 "###,
767 );
768
769 assert_snapshot!(map, @r###"
770 crate
771 a: t
772
773 crate::a
774 b: t
775
776 crate::a::b
777 c: t
778
779 crate::a::b::c
780 X: t v
781 "###);
782}
diff --git a/crates/ra_hir_def/src/nameres/tests/primitives.rs b/crates/ra_hir_def/src/nameres/tests/primitives.rs
new file mode 100644
index 000000000..0e2708658
--- /dev/null
+++ b/crates/ra_hir_def/src/nameres/tests/primitives.rs
@@ -0,0 +1,24 @@
1use super::*;
2
3#[test]
4fn primitive_reexport() {
5 let map = def_map(
6 "
7 //- /lib.rs
8 mod foo;
9 use foo::int;
10
11 //- /foo.rs
12 pub use i32 as int;
13 ",
14 );
15 assert_snapshot!(map, @r###"
16 ⋮crate
17 ⋮foo: t
18 ⋮int: t
19
20 ⋮crate::foo
21 ⋮int: t
22 "###
23 );
24}
diff --git a/crates/ra_hir_def/src/test_db.rs b/crates/ra_hir_def/src/test_db.rs
new file mode 100644
index 000000000..f91a5b41d
--- /dev/null
+++ b/crates/ra_hir_def/src/test_db.rs
@@ -0,0 +1,76 @@
1//! Database used for testing `hir_def`.
2
3use std::{
4 panic,
5 sync::{Arc, Mutex},
6};
7
8use ra_db::{salsa, CrateId, FileId, FileLoader, FileLoaderDelegate};
9use relative_path::RelativePath;
10
11#[salsa::database(
12 ra_db::SourceDatabaseExtStorage,
13 ra_db::SourceDatabaseStorage,
14 hir_expand::db::AstDatabaseStorage,
15 crate::db::InternDatabaseStorage,
16 crate::db::DefDatabase2Storage
17)]
18#[derive(Debug, Default)]
19pub struct TestDB {
20 runtime: salsa::Runtime<TestDB>,
21 events: Mutex<Option<Vec<salsa::Event<TestDB>>>>,
22}
23
24impl salsa::Database for TestDB {
25 fn salsa_runtime(&self) -> &salsa::Runtime<Self> {
26 &self.runtime
27 }
28
29 fn salsa_event(&self, event: impl Fn() -> salsa::Event<TestDB>) {
30 let mut events = self.events.lock().unwrap();
31 if let Some(events) = &mut *events {
32 events.push(event());
33 }
34 }
35}
36
37impl panic::RefUnwindSafe for TestDB {}
38
39impl FileLoader for TestDB {
40 fn file_text(&self, file_id: FileId) -> Arc<String> {
41 FileLoaderDelegate(self).file_text(file_id)
42 }
43 fn resolve_relative_path(
44 &self,
45 anchor: FileId,
46 relative_path: &RelativePath,
47 ) -> Option<FileId> {
48 FileLoaderDelegate(self).resolve_relative_path(anchor, relative_path)
49 }
50 fn relevant_crates(&self, file_id: FileId) -> Arc<Vec<CrateId>> {
51 FileLoaderDelegate(self).relevant_crates(file_id)
52 }
53}
54
55impl TestDB {
56 pub fn log(&self, f: impl FnOnce()) -> Vec<salsa::Event<TestDB>> {
57 *self.events.lock().unwrap() = Some(Vec::new());
58 f();
59 self.events.lock().unwrap().take().unwrap()
60 }
61
62 pub fn log_executed(&self, f: impl FnOnce()) -> Vec<String> {
63 let events = self.log(f);
64 events
65 .into_iter()
66 .filter_map(|e| match e.kind {
67 // This pretty horrible, but `Debug` is the only way to inspect
68 // QueryDescriptor at the moment.
69 salsa::EventKind::WillExecute { database_key } => {
70 Some(format!("{:?}", database_key))
71 }
72 _ => None,
73 })
74 .collect()
75 }
76}