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