aboutsummaryrefslogtreecommitdiff
path: root/crates/ra_hir/src/nameres
diff options
context:
space:
mode:
Diffstat (limited to 'crates/ra_hir/src/nameres')
-rw-r--r--crates/ra_hir/src/nameres/collector.rs564
-rw-r--r--crates/ra_hir/src/nameres/lower.rs222
-rw-r--r--crates/ra_hir/src/nameres/per_ns.rs78
-rw-r--r--crates/ra_hir/src/nameres/raw.rs322
-rw-r--r--crates/ra_hir/src/nameres/tests.rs784
-rw-r--r--crates/ra_hir/src/nameres/tests/globs.rs118
-rw-r--r--crates/ra_hir/src/nameres/tests/incremental.rs123
-rw-r--r--crates/ra_hir/src/nameres/tests/macros.rs94
8 files changed, 1584 insertions, 721 deletions
diff --git a/crates/ra_hir/src/nameres/collector.rs b/crates/ra_hir/src/nameres/collector.rs
new file mode 100644
index 000000000..12ed49a0a
--- /dev/null
+++ b/crates/ra_hir/src/nameres/collector.rs
@@ -0,0 +1,564 @@
1use arrayvec::ArrayVec;
2use rustc_hash::FxHashMap;
3use relative_path::RelativePathBuf;
4use test_utils::tested_by;
5use ra_db::FileId;
6
7use crate::{
8 Function, Module, Struct, Enum, Const, Static, Trait, TypeAlias,
9 PersistentHirDatabase, HirFileId, Name, Path, Problem, Crate,
10 KnownName,
11 nameres::{Resolution, PerNs, ModuleDef, ReachedFixedPoint, ResolveMode, raw},
12 ids::{AstItemDef, LocationCtx, MacroCallLoc, SourceItemId, MacroCallId},
13};
14
15use super::{CrateDefMap, CrateModuleId, ModuleData, CrateMacroId};
16
17pub(super) fn collect_defs(
18 db: &impl PersistentHirDatabase,
19 mut def_map: CrateDefMap,
20) -> CrateDefMap {
21 // populate external prelude
22 for dep in def_map.krate.dependencies(db) {
23 log::debug!("crate dep {:?} -> {:?}", dep.name, dep.krate);
24 if let Some(module) = dep.krate.root_module(db) {
25 def_map.extern_prelude.insert(dep.name.clone(), module.into());
26 }
27 // look for the prelude
28 if def_map.prelude.is_none() {
29 let map = db.crate_def_map(dep.krate);
30 if map.prelude.is_some() {
31 def_map.prelude = map.prelude;
32 }
33 }
34 }
35
36 let mut collector = DefCollector {
37 db,
38 def_map,
39 glob_imports: FxHashMap::default(),
40 unresolved_imports: Vec::new(),
41 unexpanded_macros: Vec::new(),
42 global_macro_scope: FxHashMap::default(),
43 };
44 collector.collect();
45 collector.finish()
46}
47
48/// Walks the tree of module recursively
49struct DefCollector<DB> {
50 db: DB,
51 def_map: CrateDefMap,
52 glob_imports: FxHashMap<CrateModuleId, Vec<(CrateModuleId, raw::ImportId)>>,
53 unresolved_imports: Vec<(CrateModuleId, raw::ImportId, raw::ImportData)>,
54 unexpanded_macros: Vec<(CrateModuleId, MacroCallId, Path, tt::Subtree)>,
55 global_macro_scope: FxHashMap<Name, CrateMacroId>,
56}
57
58impl<'a, DB> DefCollector<&'a DB>
59where
60 DB: PersistentHirDatabase,
61{
62 fn collect(&mut self) {
63 let crate_graph = self.db.crate_graph();
64 let file_id = crate_graph.crate_root(self.def_map.krate.crate_id());
65 let raw_items = self.db.raw_items(file_id);
66 let module_id = self.def_map.root;
67 self.def_map.modules[module_id].definition = Some(file_id);
68 ModCollector {
69 def_collector: &mut *self,
70 module_id,
71 file_id: file_id.into(),
72 raw_items: &raw_items,
73 }
74 .collect(raw_items.items());
75
76 // main name resolution fixed-point loop.
77 let mut i = 0;
78 loop {
79 match (self.resolve_imports(), self.resolve_macros()) {
80 (ReachedFixedPoint::Yes, ReachedFixedPoint::Yes) => break,
81 _ => i += 1,
82 }
83 if i == 1000 {
84 log::error!("diverging name resolution");
85 break;
86 }
87 }
88
89 let unresolved_imports = std::mem::replace(&mut self.unresolved_imports, Vec::new());
90 // show unresolved imports in completion, etc
91 for (module_id, import, import_data) in unresolved_imports {
92 self.record_resolved_import(module_id, PerNs::none(), import, &import_data)
93 }
94 }
95
96 fn define_macro(&mut self, name: Name, tt: &tt::Subtree, export: bool) {
97 if let Ok(rules) = mbe::MacroRules::parse(tt) {
98 let macro_id = self.def_map.macros.alloc(rules);
99 if export {
100 self.def_map.public_macros.insert(name.clone(), macro_id);
101 }
102 self.global_macro_scope.insert(name, macro_id);
103 }
104 }
105
106 fn resolve_imports(&mut self) -> ReachedFixedPoint {
107 let mut imports = std::mem::replace(&mut self.unresolved_imports, Vec::new());
108 let mut resolved = Vec::new();
109 imports.retain(|(module_id, import, import_data)| {
110 let (def, fp) = self.resolve_import(*module_id, import_data);
111 if fp == ReachedFixedPoint::Yes {
112 resolved.push((*module_id, def, *import, import_data.clone()))
113 }
114 fp == ReachedFixedPoint::No
115 });
116 self.unresolved_imports = imports;
117 // Resolves imports, filling-in module scopes
118 let result =
119 if resolved.is_empty() { ReachedFixedPoint::Yes } else { ReachedFixedPoint::No };
120 for (module_id, def, import, import_data) in resolved {
121 self.record_resolved_import(module_id, def, import, &import_data)
122 }
123 result
124 }
125
126 fn resolve_import(
127 &mut self,
128 module_id: CrateModuleId,
129 import: &raw::ImportData,
130 ) -> (PerNs<ModuleDef>, ReachedFixedPoint) {
131 log::debug!("resolving import: {:?} ({:?})", import, self.def_map.edition);
132 if import.is_extern_crate {
133 let res = self.def_map.resolve_name_in_extern_prelude(
134 &import
135 .path
136 .as_ident()
137 .expect("extern crate should have been desugared to one-element path"),
138 );
139 (res, ReachedFixedPoint::Yes)
140 } else {
141 let res =
142 self.def_map.resolve_path_fp(self.db, ResolveMode::Import, module_id, &import.path);
143
144 (res.resolved_def, res.reached_fixedpoint)
145 }
146 }
147
148 fn record_resolved_import(
149 &mut self,
150 module_id: CrateModuleId,
151 def: PerNs<ModuleDef>,
152 import_id: raw::ImportId,
153 import: &raw::ImportData,
154 ) {
155 if import.is_glob {
156 log::debug!("glob import: {:?}", import);
157 match def.take_types() {
158 Some(ModuleDef::Module(m)) => {
159 if import.is_prelude {
160 tested_by!(std_prelude);
161 self.def_map.prelude = Some(m);
162 } else if m.krate != self.def_map.krate {
163 tested_by!(glob_across_crates);
164 // glob import from other crate => we can just import everything once
165 let item_map = self.db.crate_def_map(m.krate);
166 let scope = &item_map[m.module_id].scope;
167 let items = scope
168 .items
169 .iter()
170 .map(|(name, res)| (name.clone(), res.clone()))
171 .collect::<Vec<_>>();
172 self.update(module_id, Some(import_id), &items);
173 } else {
174 // glob import from same crate => we do an initial
175 // import, and then need to propagate any further
176 // additions
177 let scope = &self.def_map[m.module_id].scope;
178 let items = scope
179 .items
180 .iter()
181 .map(|(name, res)| (name.clone(), res.clone()))
182 .collect::<Vec<_>>();
183 self.update(module_id, Some(import_id), &items);
184 // record the glob import in case we add further items
185 self.glob_imports
186 .entry(m.module_id)
187 .or_default()
188 .push((module_id, import_id));
189 }
190 }
191 Some(ModuleDef::Enum(e)) => {
192 tested_by!(glob_enum);
193 // glob import from enum => just import all the variants
194 let variants = e.variants(self.db);
195 let resolutions = variants
196 .into_iter()
197 .filter_map(|variant| {
198 let res = Resolution {
199 def: PerNs::both(variant.into(), variant.into()),
200 import: Some(import_id),
201 };
202 let name = variant.name(self.db)?;
203 Some((name, res))
204 })
205 .collect::<Vec<_>>();
206 self.update(module_id, Some(import_id), &resolutions);
207 }
208 Some(d) => {
209 log::debug!("glob import {:?} from non-module/enum {:?}", import, d);
210 }
211 None => {
212 log::debug!("glob import {:?} didn't resolve as type", import);
213 }
214 }
215 } else {
216 match import.path.segments.last() {
217 Some(last_segment) => {
218 let name = import.alias.clone().unwrap_or_else(|| last_segment.name.clone());
219 log::debug!("resolved import {:?} ({:?}) to {:?}", name, import, def);
220
221 // extern crates in the crate root are special-cased to insert entries into the extern prelude: rust-lang/rust#54658
222 if import.is_extern_crate && module_id == self.def_map.root {
223 if let Some(def) = def.take_types() {
224 self.def_map.extern_prelude.insert(name.clone(), def);
225 }
226 }
227 let resolution = Resolution { def, import: Some(import_id) };
228 self.update(module_id, Some(import_id), &[(name, resolution)]);
229 }
230 None => tested_by!(bogus_paths),
231 }
232 }
233 }
234
235 fn update(
236 &mut self,
237 module_id: CrateModuleId,
238 import: Option<raw::ImportId>,
239 resolutions: &[(Name, Resolution)],
240 ) {
241 self.update_recursive(module_id, import, resolutions, 0)
242 }
243
244 fn update_recursive(
245 &mut self,
246 module_id: CrateModuleId,
247 import: Option<raw::ImportId>,
248 resolutions: &[(Name, Resolution)],
249 depth: usize,
250 ) {
251 if depth > 100 {
252 // prevent stack overflows (but this shouldn't be possible)
253 panic!("infinite recursion in glob imports!");
254 }
255 let module_items = &mut self.def_map.modules[module_id].scope;
256 let mut changed = false;
257 for (name, res) in resolutions {
258 let existing = module_items.items.entry(name.clone()).or_default();
259 if existing.def.types.is_none() && res.def.types.is_some() {
260 existing.def.types = res.def.types;
261 existing.import = import.or(res.import);
262 changed = true;
263 }
264 if existing.def.values.is_none() && res.def.values.is_some() {
265 existing.def.values = res.def.values;
266 existing.import = import.or(res.import);
267 changed = true;
268 }
269 if existing.def.is_none()
270 && res.def.is_none()
271 && existing.import.is_none()
272 && res.import.is_some()
273 {
274 existing.import = res.import;
275 }
276 }
277 if !changed {
278 return;
279 }
280 let glob_imports = self
281 .glob_imports
282 .get(&module_id)
283 .into_iter()
284 .flat_map(|v| v.iter())
285 .cloned()
286 .collect::<Vec<_>>();
287 for (glob_importing_module, glob_import) in glob_imports {
288 // We pass the glob import so that the tracked import in those modules is that glob import
289 self.update_recursive(glob_importing_module, Some(glob_import), resolutions, depth + 1);
290 }
291 }
292
293 // XXX: this is just a pile of hacks now, because `PerNs` does not handle
294 // macro namespace.
295 fn resolve_macros(&mut self) -> ReachedFixedPoint {
296 let mut macros = std::mem::replace(&mut self.unexpanded_macros, Vec::new());
297 let mut resolved = Vec::new();
298 let mut res = ReachedFixedPoint::Yes;
299 macros.retain(|(module_id, call_id, path, tt)| {
300 if path.segments.len() != 2 {
301 return true;
302 }
303 let crate_name = &path.segments[0].name;
304 let krate = match self.def_map.resolve_name_in_extern_prelude(crate_name).take_types() {
305 Some(ModuleDef::Module(m)) => m.krate(self.db),
306 _ => return true,
307 };
308 let krate = match krate {
309 Some(it) => it,
310 _ => return true,
311 };
312 res = ReachedFixedPoint::No;
313 let def_map = self.db.crate_def_map(krate);
314 if let Some(macro_id) = def_map.public_macros.get(&path.segments[1].name).cloned() {
315 resolved.push((*module_id, *call_id, (krate, macro_id), tt.clone()));
316 }
317 false
318 });
319
320 for (module_id, macro_call_id, macro_def_id, arg) in resolved {
321 self.collect_macro_expansion(module_id, macro_call_id, macro_def_id, arg);
322 }
323 res
324 }
325
326 fn collect_macro_expansion(
327 &mut self,
328 module_id: CrateModuleId,
329 macro_call_id: MacroCallId,
330 macro_def_id: (Crate, CrateMacroId),
331 macro_arg: tt::Subtree,
332 ) {
333 let (macro_krate, macro_id) = macro_def_id;
334 let dm;
335 let rules = if macro_krate == self.def_map.krate {
336 &self.def_map[macro_id]
337 } else {
338 dm = self.db.crate_def_map(macro_krate);
339 &dm[macro_id]
340 };
341 if let Ok(expansion) = rules.expand(&macro_arg) {
342 self.def_map.macro_resolutions.insert(macro_call_id, macro_def_id);
343 // XXX: this **does not** go through a database, because we can't
344 // identify macro_call without adding the whole state of name resolution
345 // as a parameter to the query.
346 //
347 // So, we run the queries "manually" and we must ensure that
348 // `db.hir_parse(macro_call_id)` returns the same source_file.
349 let file_id: HirFileId = macro_call_id.into();
350 let source_file = mbe::token_tree_to_ast_item_list(&expansion);
351
352 let raw_items = raw::RawItems::from_source_file(&source_file, file_id);
353 ModCollector { def_collector: &mut *self, file_id, module_id, raw_items: &raw_items }
354 .collect(raw_items.items())
355 }
356 }
357
358 fn finish(self) -> CrateDefMap {
359 self.def_map
360 }
361}
362
363/// Walks a single module, populating defs, imports and macros
364struct ModCollector<'a, D> {
365 def_collector: D,
366 module_id: CrateModuleId,
367 file_id: HirFileId,
368 raw_items: &'a raw::RawItems,
369}
370
371impl<DB> ModCollector<'_, &'_ mut DefCollector<&'_ DB>>
372where
373 DB: PersistentHirDatabase,
374{
375 fn collect(&mut self, items: &[raw::RawItem]) {
376 for item in items {
377 match *item {
378 raw::RawItem::Module(m) => self.collect_module(&self.raw_items[m]),
379 raw::RawItem::Import(import) => self.def_collector.unresolved_imports.push((
380 self.module_id,
381 import,
382 self.raw_items[import].clone(),
383 )),
384 raw::RawItem::Def(def) => self.define_def(&self.raw_items[def]),
385 raw::RawItem::Macro(mac) => self.collect_macro(&self.raw_items[mac]),
386 }
387 }
388 }
389
390 fn collect_module(&mut self, module: &raw::ModuleData) {
391 match module {
392 // inline module, just recurse
393 raw::ModuleData::Definition { name, items, source_item_id } => {
394 let module_id = self.push_child_module(
395 name.clone(),
396 source_item_id.with_file_id(self.file_id),
397 None,
398 );
399 ModCollector {
400 def_collector: &mut *self.def_collector,
401 module_id,
402 file_id: self.file_id,
403 raw_items: self.raw_items,
404 }
405 .collect(&*items);
406 }
407 // out of line module, resovle, parse and recurse
408 raw::ModuleData::Declaration { name, source_item_id } => {
409 let source_item_id = source_item_id.with_file_id(self.file_id);
410 let is_root = self.def_collector.def_map.modules[self.module_id].parent.is_none();
411 let (file_ids, problem) =
412 resolve_submodule(self.def_collector.db, self.file_id, name, is_root);
413
414 if let Some(problem) = problem {
415 self.def_collector.def_map.problems.add(source_item_id, problem)
416 }
417
418 if let Some(&file_id) = file_ids.first() {
419 let module_id =
420 self.push_child_module(name.clone(), source_item_id, Some(file_id));
421 let raw_items = self.def_collector.db.raw_items(file_id);
422 ModCollector {
423 def_collector: &mut *self.def_collector,
424 module_id,
425 file_id: file_id.into(),
426 raw_items: &raw_items,
427 }
428 .collect(raw_items.items())
429 }
430 }
431 }
432 }
433
434 fn push_child_module(
435 &mut self,
436 name: Name,
437 declaration: SourceItemId,
438 definition: Option<FileId>,
439 ) -> CrateModuleId {
440 let modules = &mut self.def_collector.def_map.modules;
441 let res = modules.alloc(ModuleData::default());
442 modules[res].parent = Some(self.module_id);
443 modules[res].declaration = Some(declaration);
444 modules[res].definition = definition;
445 modules[self.module_id].children.insert(name.clone(), res);
446 let resolution = Resolution {
447 def: PerNs::types(
448 Module { krate: self.def_collector.def_map.krate, module_id: res }.into(),
449 ),
450 import: None,
451 };
452 self.def_collector.update(self.module_id, None, &[(name, resolution)]);
453 res
454 }
455
456 fn define_def(&mut self, def: &raw::DefData) {
457 let module = Module { krate: self.def_collector.def_map.krate, module_id: self.module_id };
458 let ctx = LocationCtx::new(self.def_collector.db, module, self.file_id.into());
459 macro_rules! id {
460 () => {
461 AstItemDef::from_source_item_id_unchecked(ctx, def.source_item_id)
462 };
463 }
464 let name = def.name.clone();
465 let def: PerNs<ModuleDef> = match def.kind {
466 raw::DefKind::Function => PerNs::values(Function { id: id!() }.into()),
467 raw::DefKind::Struct => {
468 let s = Struct { id: id!() }.into();
469 PerNs::both(s, s)
470 }
471 raw::DefKind::Enum => PerNs::types(Enum { id: id!() }.into()),
472 raw::DefKind::Const => PerNs::values(Const { id: id!() }.into()),
473 raw::DefKind::Static => PerNs::values(Static { id: id!() }.into()),
474 raw::DefKind::Trait => PerNs::types(Trait { id: id!() }.into()),
475 raw::DefKind::TypeAlias => PerNs::types(TypeAlias { id: id!() }.into()),
476 };
477 let resolution = Resolution { def, import: None };
478 self.def_collector.update(self.module_id, None, &[(name, resolution)])
479 }
480
481 fn collect_macro(&mut self, mac: &raw::MacroData) {
482 // Case 1: macro rules, define a macro in crate-global mutable scope
483 if is_macro_rules(&mac.path) {
484 if let Some(name) = &mac.name {
485 self.def_collector.define_macro(name.clone(), &mac.arg, mac.export)
486 }
487 return;
488 }
489
490 let source_item_id = SourceItemId { file_id: self.file_id, item_id: mac.source_item_id };
491 let macro_call_id = MacroCallLoc {
492 module: Module { krate: self.def_collector.def_map.krate, module_id: self.module_id },
493 source_item_id,
494 }
495 .id(self.def_collector.db);
496
497 // Case 2: try to expand macro_rules from this crate, triggering
498 // recursive item collection.
499 if let Some(&macro_id) =
500 mac.path.as_ident().and_then(|name| self.def_collector.global_macro_scope.get(name))
501 {
502 self.def_collector.collect_macro_expansion(
503 self.module_id,
504 macro_call_id,
505 (self.def_collector.def_map.krate, macro_id),
506 mac.arg.clone(),
507 );
508 return;
509 }
510
511 // Case 3: path to a macro from another crate, expand during name resolution
512 self.def_collector.unexpanded_macros.push((
513 self.module_id,
514 macro_call_id,
515 mac.path.clone(),
516 mac.arg.clone(),
517 ))
518 }
519}
520
521fn is_macro_rules(path: &Path) -> bool {
522 path.as_ident().and_then(Name::as_known_name) == Some(KnownName::MacroRules)
523}
524
525fn resolve_submodule(
526 db: &impl PersistentHirDatabase,
527 file_id: HirFileId,
528 name: &Name,
529 is_root: bool,
530) -> (Vec<FileId>, Option<Problem>) {
531 // FIXME: handle submodules of inline modules properly
532 let file_id = file_id.original_file(db);
533 let source_root_id = db.file_source_root(file_id);
534 let path = db.file_relative_path(file_id);
535 let root = RelativePathBuf::default();
536 let dir_path = path.parent().unwrap_or(&root);
537 let mod_name = path.file_stem().unwrap_or("unknown");
538 let is_dir_owner = is_root || mod_name == "mod";
539
540 let file_mod = dir_path.join(format!("{}.rs", name));
541 let dir_mod = dir_path.join(format!("{}/mod.rs", name));
542 let file_dir_mod = dir_path.join(format!("{}/{}.rs", mod_name, name));
543 let mut candidates = ArrayVec::<[_; 2]>::new();
544 if is_dir_owner {
545 candidates.push(file_mod.clone());
546 candidates.push(dir_mod);
547 } else {
548 candidates.push(file_dir_mod.clone());
549 };
550 let sr = db.source_root(source_root_id);
551 let points_to = candidates
552 .into_iter()
553 .filter_map(|path| sr.files.get(&path))
554 .map(|&it| it)
555 .collect::<Vec<_>>();
556 let problem = if points_to.is_empty() {
557 Some(Problem::UnresolvedModule {
558 candidate: if is_dir_owner { file_mod } else { file_dir_mod },
559 })
560 } else {
561 None
562 };
563 (points_to, problem)
564}
diff --git a/crates/ra_hir/src/nameres/lower.rs b/crates/ra_hir/src/nameres/lower.rs
deleted file mode 100644
index 56262ad6d..000000000
--- a/crates/ra_hir/src/nameres/lower.rs
+++ /dev/null
@@ -1,222 +0,0 @@
1use std::sync::Arc;
2
3use ra_syntax::{
4 AstNode, SourceFile, TreeArc, AstPtr,
5 ast::{self, ModuleItemOwner, NameOwner, AttrsOwner},
6};
7use ra_arena::{Arena, RawId, impl_arena_id, map::ArenaMap};
8use rustc_hash::FxHashMap;
9
10use crate::{
11 SourceItemId, Path, ModuleSource, Name,
12 HirFileId, MacroCallLoc, AsName, PerNs, Function,
13 ModuleDef, Module, Struct, Enum, Const, Static, Trait, TypeAlias,
14 ids::LocationCtx, PersistentHirDatabase,
15};
16
17#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
18pub struct ImportId(RawId);
19impl_arena_id!(ImportId);
20
21#[derive(Debug, PartialEq, Eq)]
22pub(super) struct ImportData {
23 pub(super) path: Path,
24 pub(super) alias: Option<Name>,
25 pub(super) is_glob: bool,
26 pub(super) is_prelude: bool,
27 pub(super) is_extern_crate: bool,
28}
29
30/// A set of items and imports declared inside a module, without relation to
31/// other modules.
32///
33/// This sits in-between raw syntax and name resolution and allows us to avoid
34/// recomputing name res: if two instance of `InputModuleItems` are the same, we
35/// can avoid redoing name resolution.
36#[derive(Debug, Default, PartialEq, Eq)]
37pub struct LoweredModule {
38 pub(crate) declarations: FxHashMap<Name, PerNs<ModuleDef>>,
39 pub(super) imports: Arena<ImportId, ImportData>,
40}
41
42#[derive(Debug, Default, PartialEq, Eq)]
43pub struct ImportSourceMap {
44 map: ArenaMap<ImportId, AstPtr<ast::PathSegment>>,
45}
46
47impl ImportSourceMap {
48 fn insert(&mut self, import: ImportId, segment: &ast::PathSegment) {
49 self.map.insert(import, AstPtr::new(segment))
50 }
51
52 pub fn get(&self, source: &ModuleSource, import: ImportId) -> TreeArc<ast::PathSegment> {
53 let file = match source {
54 ModuleSource::SourceFile(file) => &*file,
55 ModuleSource::Module(m) => m.syntax().ancestors().find_map(SourceFile::cast).unwrap(),
56 };
57
58 self.map[import].to_node(file).to_owned()
59 }
60}
61
62impl LoweredModule {
63 pub(crate) fn lower_module_query(
64 db: &impl PersistentHirDatabase,
65 module: Module,
66 ) -> Arc<LoweredModule> {
67 db.lower_module_with_source_map(module).0
68 }
69
70 pub(crate) fn lower_module_with_source_map_query(
71 db: &impl PersistentHirDatabase,
72 module: Module,
73 ) -> (Arc<LoweredModule>, Arc<ImportSourceMap>) {
74 let (file_id, source) = module.definition_source(db);
75 let file_id: HirFileId = file_id.into();
76 let mut source_map = ImportSourceMap::default();
77 let mut res = LoweredModule::default();
78 match source {
79 ModuleSource::SourceFile(it) => {
80 res.fill(&mut source_map, db, module, file_id, &mut it.items_with_macros())
81 }
82 ModuleSource::Module(it) => {
83 if let Some(item_list) = it.item_list() {
84 res.fill(
85 &mut source_map,
86 db,
87 module,
88 file_id,
89 &mut item_list.items_with_macros(),
90 )
91 }
92 }
93 };
94 (Arc::new(res), Arc::new(source_map))
95 }
96
97 fn fill(
98 &mut self,
99 source_map: &mut ImportSourceMap,
100 db: &impl PersistentHirDatabase,
101 module: Module,
102 file_id: HirFileId,
103 items: &mut Iterator<Item = ast::ItemOrMacro>,
104 ) {
105 let file_items = db.file_items(file_id);
106
107 for item in items {
108 match item {
109 ast::ItemOrMacro::Item(it) => {
110 self.add_def_id(source_map, db, module, file_id, it);
111 }
112 ast::ItemOrMacro::Macro(macro_call) => {
113 let item_id = file_items.id_of_unchecked(macro_call.syntax());
114 let loc =
115 MacroCallLoc { module, source_item_id: SourceItemId { file_id, item_id } };
116 let id = loc.id(db);
117 let file_id = HirFileId::from(id);
118 //FIXME: expand recursively
119 for item in db.hir_parse(file_id).items() {
120 self.add_def_id(source_map, db, module, file_id, item);
121 }
122 }
123 }
124 }
125 }
126
127 fn add_def_id(
128 &mut self,
129 source_map: &mut ImportSourceMap,
130 db: &impl PersistentHirDatabase,
131 module: Module,
132 file_id: HirFileId,
133 item: &ast::ModuleItem,
134 ) {
135 let ctx = LocationCtx::new(db, module, file_id);
136 match item.kind() {
137 ast::ModuleItemKind::StructDef(it) => {
138 if let Some(name) = it.name() {
139 let s = Struct { id: ctx.to_def(it) };
140 let s: ModuleDef = s.into();
141 self.declarations.insert(name.as_name(), PerNs::both(s, s));
142 }
143 }
144 ast::ModuleItemKind::EnumDef(it) => {
145 if let Some(name) = it.name() {
146 let e = Enum { id: ctx.to_def(it) };
147 let e: ModuleDef = e.into();
148 self.declarations.insert(name.as_name(), PerNs::types(e));
149 }
150 }
151 ast::ModuleItemKind::FnDef(it) => {
152 if let Some(name) = it.name() {
153 let func = Function { id: ctx.to_def(it) };
154 self.declarations.insert(name.as_name(), PerNs::values(func.into()));
155 }
156 }
157 ast::ModuleItemKind::TraitDef(it) => {
158 if let Some(name) = it.name() {
159 let t = Trait { id: ctx.to_def(it) };
160 self.declarations.insert(name.as_name(), PerNs::types(t.into()));
161 }
162 }
163 ast::ModuleItemKind::TypeAliasDef(it) => {
164 if let Some(name) = it.name() {
165 let t = TypeAlias { id: ctx.to_def(it) };
166 self.declarations.insert(name.as_name(), PerNs::types(t.into()));
167 }
168 }
169 ast::ModuleItemKind::ImplBlock(_) => {
170 // impls don't define items
171 }
172 ast::ModuleItemKind::UseItem(it) => {
173 self.add_use_item(source_map, it);
174 }
175 ast::ModuleItemKind::ExternCrateItem(it) => {
176 if let Some(name_ref) = it.name_ref() {
177 let path = Path::from_name_ref(name_ref);
178 let alias = it.alias().and_then(|a| a.name()).map(AsName::as_name);
179 self.imports.alloc(ImportData {
180 path,
181 alias,
182 is_glob: false,
183 is_prelude: false,
184 is_extern_crate: true,
185 });
186 }
187 }
188 ast::ModuleItemKind::ConstDef(it) => {
189 if let Some(name) = it.name() {
190 let c = Const { id: ctx.to_def(it) };
191 self.declarations.insert(name.as_name(), PerNs::values(c.into()));
192 }
193 }
194 ast::ModuleItemKind::StaticDef(it) => {
195 if let Some(name) = it.name() {
196 let s = Static { id: ctx.to_def(it) };
197 self.declarations.insert(name.as_name(), PerNs::values(s.into()));
198 }
199 }
200 ast::ModuleItemKind::Module(_) => {
201 // modules are handled separately directly by name res
202 }
203 };
204 }
205
206 fn add_use_item(&mut self, source_map: &mut ImportSourceMap, item: &ast::UseItem) {
207 let is_prelude =
208 item.attrs().any(|attr| attr.as_atom().map(|s| s == "prelude_import").unwrap_or(false));
209 Path::expand_use_item(item, |path, segment, alias| {
210 let import = self.imports.alloc(ImportData {
211 path,
212 alias,
213 is_glob: segment.is_none(),
214 is_prelude,
215 is_extern_crate: false,
216 });
217 if let Some(segment) = segment {
218 source_map.insert(import, segment)
219 }
220 })
221 }
222}
diff --git a/crates/ra_hir/src/nameres/per_ns.rs b/crates/ra_hir/src/nameres/per_ns.rs
new file mode 100644
index 000000000..c40a3ff9d
--- /dev/null
+++ b/crates/ra_hir/src/nameres/per_ns.rs
@@ -0,0 +1,78 @@
1#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
2pub enum Namespace {
3 Types,
4 Values,
5}
6
7#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
8pub struct PerNs<T> {
9 pub types: Option<T>,
10 pub values: Option<T>,
11}
12
13impl<T> Default for PerNs<T> {
14 fn default() -> Self {
15 PerNs { types: None, values: None }
16 }
17}
18
19impl<T> PerNs<T> {
20 pub fn none() -> PerNs<T> {
21 PerNs { types: None, values: None }
22 }
23
24 pub fn values(t: T) -> PerNs<T> {
25 PerNs { types: None, values: Some(t) }
26 }
27
28 pub fn types(t: T) -> PerNs<T> {
29 PerNs { types: Some(t), values: None }
30 }
31
32 pub fn both(types: T, values: T) -> PerNs<T> {
33 PerNs { types: Some(types), values: Some(values) }
34 }
35
36 pub fn is_none(&self) -> bool {
37 self.types.is_none() && self.values.is_none()
38 }
39
40 pub fn is_both(&self) -> bool {
41 self.types.is_some() && self.values.is_some()
42 }
43
44 pub fn take(self, namespace: Namespace) -> Option<T> {
45 match namespace {
46 Namespace::Types => self.types,
47 Namespace::Values => self.values,
48 }
49 }
50
51 pub fn take_types(self) -> Option<T> {
52 self.take(Namespace::Types)
53 }
54
55 pub fn take_values(self) -> Option<T> {
56 self.take(Namespace::Values)
57 }
58
59 pub fn get(&self, namespace: Namespace) -> Option<&T> {
60 self.as_ref().take(namespace)
61 }
62
63 pub fn as_ref(&self) -> PerNs<&T> {
64 PerNs { types: self.types.as_ref(), values: self.values.as_ref() }
65 }
66
67 pub fn or(self, other: PerNs<T>) -> PerNs<T> {
68 PerNs { types: self.types.or(other.types), values: self.values.or(other.values) }
69 }
70
71 pub fn and_then<U>(self, f: impl Fn(T) -> Option<U>) -> PerNs<U> {
72 PerNs { types: self.types.and_then(&f), values: self.values.and_then(&f) }
73 }
74
75 pub fn map<U>(self, f: impl Fn(T) -> U) -> PerNs<U> {
76 PerNs { types: self.types.map(&f), values: self.values.map(&f) }
77 }
78}
diff --git a/crates/ra_hir/src/nameres/raw.rs b/crates/ra_hir/src/nameres/raw.rs
new file mode 100644
index 000000000..3226bbf0d
--- /dev/null
+++ b/crates/ra_hir/src/nameres/raw.rs
@@ -0,0 +1,322 @@
1use std::{
2 sync::Arc,
3 ops::Index,
4};
5
6use test_utils::tested_by;
7use ra_db::FileId;
8use ra_arena::{Arena, impl_arena_id, RawId, map::ArenaMap};
9use ra_syntax::{
10 AstNode, SourceFile, AstPtr, TreeArc,
11 ast::{self, NameOwner, AttrsOwner},
12};
13
14use crate::{
15 PersistentHirDatabase, Name, AsName, Path, HirFileId, ModuleSource,
16 ids::{SourceFileItemId, SourceFileItems},
17};
18
19#[derive(Debug, Default, PartialEq, Eq)]
20pub struct RawItems {
21 modules: Arena<Module, ModuleData>,
22 imports: Arena<ImportId, ImportData>,
23 defs: Arena<Def, DefData>,
24 macros: Arena<Macro, MacroData>,
25 /// items for top-level module
26 items: Vec<RawItem>,
27}
28
29#[derive(Debug, Default, PartialEq, Eq)]
30pub struct ImportSourceMap {
31 map: ArenaMap<ImportId, AstPtr<ast::PathSegment>>,
32}
33
34impl ImportSourceMap {
35 pub(crate) fn insert(&mut self, import: ImportId, segment: &ast::PathSegment) {
36 self.map.insert(import, AstPtr::new(segment))
37 }
38
39 pub fn get(&self, source: &ModuleSource, import: ImportId) -> TreeArc<ast::PathSegment> {
40 let file = match source {
41 ModuleSource::SourceFile(file) => &*file,
42 ModuleSource::Module(m) => m.syntax().ancestors().find_map(SourceFile::cast).unwrap(),
43 };
44
45 self.map[import].to_node(file).to_owned()
46 }
47}
48
49impl RawItems {
50 pub(crate) fn raw_items_query(
51 db: &impl PersistentHirDatabase,
52 file_id: FileId,
53 ) -> Arc<RawItems> {
54 db.raw_items_with_source_map(file_id).0
55 }
56
57 pub(crate) fn raw_items_with_source_map_query(
58 db: &impl PersistentHirDatabase,
59 file_id: FileId,
60 ) -> (Arc<RawItems>, Arc<ImportSourceMap>) {
61 let mut collector = RawItemsCollector {
62 raw_items: RawItems::default(),
63 source_file_items: db.file_items(file_id.into()),
64 source_map: ImportSourceMap::default(),
65 };
66 let source_file = db.parse(file_id);
67 collector.process_module(None, &*source_file);
68 (Arc::new(collector.raw_items), Arc::new(collector.source_map))
69 }
70
71 pub(crate) fn items(&self) -> &[RawItem] {
72 &self.items
73 }
74
75 // We can't use queries during name resolution for fear of cycles, so this
76 // is a query-less variant of the above function.
77 pub(crate) fn from_source_file(source_file: &SourceFile, file_id: HirFileId) -> RawItems {
78 let source_file_items = SourceFileItems::from_source_file(source_file, file_id);
79 let mut collector = RawItemsCollector {
80 raw_items: RawItems::default(),
81 source_file_items: Arc::new(source_file_items),
82 source_map: ImportSourceMap::default(),
83 };
84 collector.process_module(None, &*source_file);
85 collector.raw_items
86 }
87}
88
89impl Index<Module> for RawItems {
90 type Output = ModuleData;
91 fn index(&self, idx: Module) -> &ModuleData {
92 &self.modules[idx]
93 }
94}
95
96impl Index<ImportId> for RawItems {
97 type Output = ImportData;
98 fn index(&self, idx: ImportId) -> &ImportData {
99 &self.imports[idx]
100 }
101}
102
103impl Index<Def> for RawItems {
104 type Output = DefData;
105 fn index(&self, idx: Def) -> &DefData {
106 &self.defs[idx]
107 }
108}
109
110impl Index<Macro> for RawItems {
111 type Output = MacroData;
112 fn index(&self, idx: Macro) -> &MacroData {
113 &self.macros[idx]
114 }
115}
116
117#[derive(Debug, PartialEq, Eq, Clone, Copy)]
118pub(crate) enum RawItem {
119 Module(Module),
120 Import(ImportId),
121 Def(Def),
122 Macro(Macro),
123}
124
125#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
126pub(crate) struct Module(RawId);
127impl_arena_id!(Module);
128
129#[derive(Debug, PartialEq, Eq)]
130pub(crate) enum ModuleData {
131 Declaration { name: Name, source_item_id: SourceFileItemId },
132 Definition { name: Name, source_item_id: SourceFileItemId, items: Vec<RawItem> },
133}
134
135#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
136pub struct ImportId(RawId);
137impl_arena_id!(ImportId);
138
139#[derive(Debug, Clone, PartialEq, Eq)]
140pub struct ImportData {
141 pub(crate) path: Path,
142 pub(crate) alias: Option<Name>,
143 pub(crate) is_glob: bool,
144 pub(crate) is_prelude: bool,
145 pub(crate) is_extern_crate: bool,
146}
147
148#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
149pub(crate) struct Def(RawId);
150impl_arena_id!(Def);
151
152#[derive(Debug, PartialEq, Eq)]
153pub(crate) struct DefData {
154 pub(crate) source_item_id: SourceFileItemId,
155 pub(crate) name: Name,
156 pub(crate) kind: DefKind,
157}
158
159#[derive(Debug, PartialEq, Eq, Clone, Copy)]
160pub(crate) enum DefKind {
161 Function,
162 Struct,
163 Enum,
164 Const,
165 Static,
166 Trait,
167 TypeAlias,
168}
169
170#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
171pub(crate) struct Macro(RawId);
172impl_arena_id!(Macro);
173
174#[derive(Debug, PartialEq, Eq)]
175pub(crate) struct MacroData {
176 pub(crate) source_item_id: SourceFileItemId,
177 pub(crate) path: Path,
178 pub(crate) name: Option<Name>,
179 pub(crate) arg: tt::Subtree,
180 pub(crate) export: bool,
181}
182
183struct RawItemsCollector {
184 raw_items: RawItems,
185 source_file_items: Arc<SourceFileItems>,
186 source_map: ImportSourceMap,
187}
188
189impl RawItemsCollector {
190 fn process_module(&mut self, current_module: Option<Module>, body: &impl ast::ModuleItemOwner) {
191 for item_or_macro in body.items_with_macros() {
192 match item_or_macro {
193 ast::ItemOrMacro::Macro(m) => self.add_macro(current_module, m),
194 ast::ItemOrMacro::Item(item) => self.add_item(current_module, item),
195 }
196 }
197 }
198
199 fn add_item(&mut self, current_module: Option<Module>, item: &ast::ModuleItem) {
200 let (kind, name) = match item.kind() {
201 ast::ModuleItemKind::Module(module) => {
202 self.add_module(current_module, module);
203 return;
204 }
205 ast::ModuleItemKind::UseItem(use_item) => {
206 self.add_use_item(current_module, use_item);
207 return;
208 }
209 ast::ModuleItemKind::ExternCrateItem(extern_crate) => {
210 self.add_extern_crate_item(current_module, extern_crate);
211 return;
212 }
213 ast::ModuleItemKind::ImplBlock(_) => {
214 // impls don't participate in name resolution
215 return;
216 }
217 ast::ModuleItemKind::StructDef(it) => (DefKind::Struct, it.name()),
218 ast::ModuleItemKind::EnumDef(it) => (DefKind::Enum, it.name()),
219 ast::ModuleItemKind::FnDef(it) => (DefKind::Function, it.name()),
220 ast::ModuleItemKind::TraitDef(it) => (DefKind::Trait, it.name()),
221 ast::ModuleItemKind::TypeAliasDef(it) => (DefKind::TypeAlias, it.name()),
222 ast::ModuleItemKind::ConstDef(it) => (DefKind::Const, it.name()),
223 ast::ModuleItemKind::StaticDef(it) => (DefKind::Static, it.name()),
224 };
225 if let Some(name) = name {
226 let name = name.as_name();
227 let source_item_id = self.source_file_items.id_of_unchecked(item.syntax());
228 let def = self.raw_items.defs.alloc(DefData { name, kind, source_item_id });
229 self.push_item(current_module, RawItem::Def(def))
230 }
231 }
232
233 fn add_module(&mut self, current_module: Option<Module>, module: &ast::Module) {
234 let name = match module.name() {
235 Some(it) => it.as_name(),
236 None => return,
237 };
238 let source_item_id = self.source_file_items.id_of_unchecked(module.syntax());
239 if module.has_semi() {
240 let item =
241 self.raw_items.modules.alloc(ModuleData::Declaration { name, source_item_id });
242 self.push_item(current_module, RawItem::Module(item));
243 return;
244 }
245
246 if let Some(item_list) = module.item_list() {
247 let item = self.raw_items.modules.alloc(ModuleData::Definition {
248 name,
249 source_item_id,
250 items: Vec::new(),
251 });
252 self.process_module(Some(item), item_list);
253 self.push_item(current_module, RawItem::Module(item));
254 return;
255 }
256 tested_by!(name_res_works_for_broken_modules);
257 }
258
259 fn add_use_item(&mut self, current_module: Option<Module>, use_item: &ast::UseItem) {
260 let is_prelude = use_item.has_atom_attr("prelude_import");
261
262 Path::expand_use_item(use_item, |path, segment, alias| {
263 let import = self.raw_items.imports.alloc(ImportData {
264 path,
265 alias,
266 is_glob: segment.is_none(),
267 is_prelude,
268 is_extern_crate: false,
269 });
270 if let Some(segment) = segment {
271 self.source_map.insert(import, segment)
272 }
273 self.push_item(current_module, RawItem::Import(import))
274 })
275 }
276
277 fn add_extern_crate_item(
278 &mut self,
279 current_module: Option<Module>,
280 extern_crate: &ast::ExternCrateItem,
281 ) {
282 if let Some(name_ref) = extern_crate.name_ref() {
283 let path = Path::from_name_ref(name_ref);
284 let alias = extern_crate.alias().and_then(|a| a.name()).map(AsName::as_name);
285 let import = self.raw_items.imports.alloc(ImportData {
286 path,
287 alias,
288 is_glob: false,
289 is_prelude: false,
290 is_extern_crate: true,
291 });
292 self.push_item(current_module, RawItem::Import(import))
293 }
294 }
295
296 fn add_macro(&mut self, current_module: Option<Module>, m: &ast::MacroCall) {
297 let (path, arg) = match (
298 m.path().and_then(Path::from_ast),
299 m.token_tree().and_then(mbe::ast_to_token_tree),
300 ) {
301 (Some(path), Some((token_tree, _token_map))) => (path, token_tree),
302 _ => return,
303 };
304
305 let name = m.name().map(|it| it.as_name());
306 let source_item_id = self.source_file_items.id_of_unchecked(m.syntax());
307 let export = m.has_atom_attr("macro_export");
308 let m = self.raw_items.macros.alloc(MacroData { source_item_id, path, arg, name, export });
309 self.push_item(current_module, RawItem::Macro(m));
310 }
311
312 fn push_item(&mut self, current_module: Option<Module>, item: RawItem) {
313 match current_module {
314 Some(module) => match &mut self.raw_items.modules[module] {
315 ModuleData::Definition { items, .. } => items,
316 ModuleData::Declaration { .. } => unreachable!(),
317 },
318 None => &mut self.raw_items.items,
319 }
320 .push(item)
321 }
322}
diff --git a/crates/ra_hir/src/nameres/tests.rs b/crates/ra_hir/src/nameres/tests.rs
index 9b151bb0c..ac9b88520 100644
--- a/crates/ra_hir/src/nameres/tests.rs
+++ b/crates/ra_hir/src/nameres/tests.rs
@@ -1,34 +1,43 @@
1mod macros;
2mod globs;
3mod incremental;
4
1use std::sync::Arc; 5use std::sync::Arc;
2 6
3use ra_db::SourceDatabase; 7use ra_db::SourceDatabase;
4use test_utils::{assert_eq_text, covers}; 8use test_utils::covers;
5 9use insta::assert_snapshot_matches;
6use crate::{ 10
7 ItemMap, 11use crate::{Crate, mock::{MockDatabase, CrateGraphFixture}, nameres::Resolution};
8 PersistentHirDatabase, 12
9 mock::MockDatabase, 13use super::*;
10 module_tree::ModuleId, 14
11}; 15fn compute_crate_def_map(fixture: &str, graph: Option<CrateGraphFixture>) -> Arc<CrateDefMap> {
12use super::Resolution; 16 let mut db = MockDatabase::with_files(fixture);
13 17 if let Some(graph) = graph {
14fn item_map(fixture: &str) -> (Arc<ItemMap>, ModuleId) { 18 db.set_crate_graph_from_fixture(graph);
15 let (db, pos) = MockDatabase::with_position(fixture); 19 }
16 let module = crate::source_binder::module_from_position(&db, pos).unwrap(); 20 let crate_id = db.crate_graph().iter().next().unwrap();
17 let krate = module.krate(&db).unwrap(); 21 let krate = Crate { crate_id };
18 let module_id = module.module_id; 22 db.crate_def_map(krate)
19 (db.item_map(krate), module_id)
20} 23}
21 24
22fn check_module_item_map(map: &ItemMap, module_id: ModuleId, expected: &str) { 25fn render_crate_def_map(map: &CrateDefMap) -> String {
23 let mut lines = map[module_id] 26 let mut buf = String::new();
24 .items 27 go(&mut buf, map, "\ncrate", map.root);
25 .iter() 28 return buf;
26 .map(|(name, res)| format!("{}: {}", name, dump_resolution(res))) 29
27 .collect::<Vec<_>>(); 30 fn go(buf: &mut String, map: &CrateDefMap, path: &str, module: CrateModuleId) {
28 lines.sort(); 31 *buf += path;
29 let actual = lines.join("\n"); 32 *buf += "\n";
30 let expected = expected.trim().lines().map(|it| it.trim()).collect::<Vec<_>>().join("\n"); 33 for (name, res) in map.modules[module].scope.items.iter() {
31 assert_eq_text!(&expected, &actual); 34 *buf += &format!("{}: {}\n", name, dump_resolution(res))
35 }
36 for (name, child) in map.modules[module].children.iter() {
37 let path = path.to_string() + &format!("::{}", name);
38 go(buf, map, &path, *child);
39 }
40 }
32 41
33 fn dump_resolution(resolution: &Resolution) -> &'static str { 42 fn dump_resolution(resolution: &Resolution) -> &'static str {
34 match (resolution.def.types.is_some(), resolution.def.values.is_some()) { 43 match (resolution.def.types.is_some(), resolution.def.values.is_some()) {
@@ -40,66 +49,112 @@ fn check_module_item_map(map: &ItemMap, module_id: ModuleId, expected: &str) {
40 } 49 }
41} 50}
42 51
52fn def_map(fixtute: &str) -> String {
53 let dm = compute_crate_def_map(fixtute, None);
54 render_crate_def_map(&dm)
55}
56
57fn def_map_with_crate_graph(fixtute: &str, graph: CrateGraphFixture) -> String {
58 let dm = compute_crate_def_map(fixtute, Some(graph));
59 render_crate_def_map(&dm)
60}
61
43#[test] 62#[test]
44fn item_map_smoke_test() { 63fn crate_def_map_smoke_test() {
45 let (item_map, module_id) = item_map( 64 let map = def_map(
46 " 65 "
47 //- /lib.rs 66 //- /lib.rs
48 mod foo; 67 mod foo;
49 68 struct S;
50 use crate::foo::bar::Baz; 69 use crate::foo::bar::E;
51 <|> 70 use self::E::V;
52 71
53 //- /foo/mod.rs 72 //- /foo/mod.rs
54 pub mod bar; 73 pub mod bar;
74 fn f() {}
55 75
56 //- /foo/bar.rs 76 //- /foo/bar.rs
57 pub struct Baz; 77 pub struct Baz;
58 ", 78 enum E { V }
79 ",
59 ); 80 );
60 check_module_item_map( 81 assert_snapshot_matches!(map, @r###"
61 &item_map, 82crate
62 module_id, 83V: t v
84E: t
85foo: t
86S: t v
87
88crate::foo
89bar: t
90f: v
91
92crate::foo::bar
93Baz: t v
94E: t
95"###
96 )
97}
98
99#[test]
100fn bogus_paths() {
101 covers!(bogus_paths);
102 let map = def_map(
63 " 103 "
64 Baz: t v 104 //- /lib.rs
65 foo: t 105 mod foo;
106 struct S;
107 use self;
108
109 //- /foo/mod.rs
110 use super;
111 use crate;
112
66 ", 113 ",
67 ); 114 );
115 assert_snapshot_matches!(map, @r###"
116crate
117foo: t
118S: t v
119
120crate::foo
121"###
122 )
68} 123}
69 124
70#[test] 125#[test]
71fn use_as() { 126fn use_as() {
72 let (item_map, module_id) = item_map( 127 let map = def_map(
73 " 128 "
74 //- /lib.rs 129 //- /lib.rs
75 mod foo; 130 mod foo;
76 131
77 use crate::foo::Baz as Foo; 132 use crate::foo::Baz as Foo;
78 <|>
79 133
80 //- /foo/mod.rs 134 //- /foo/mod.rs
81 pub struct Baz; 135 pub struct Baz;
82 ",
83 );
84 check_module_item_map(
85 &item_map,
86 module_id,
87 "
88 Foo: t v
89 foo: t
90 ", 136 ",
91 ); 137 );
138 assert_snapshot_matches!(map,
139 @r###"
140crate
141Foo: t v
142foo: t
143
144crate::foo
145Baz: t v
146"###
147 );
92} 148}
93 149
94#[test] 150#[test]
95fn use_trees() { 151fn use_trees() {
96 let (item_map, module_id) = item_map( 152 let map = def_map(
97 " 153 "
98 //- /lib.rs 154 //- /lib.rs
99 mod foo; 155 mod foo;
100 156
101 use crate::foo::bar::{Baz, Quux}; 157 use crate::foo::bar::{Baz, Quux};
102 <|>
103 158
104 //- /foo/mod.rs 159 //- /foo/mod.rs
105 pub mod bar; 160 pub mod bar;
@@ -107,28 +162,33 @@ fn use_trees() {
107 //- /foo/bar.rs 162 //- /foo/bar.rs
108 pub struct Baz; 163 pub struct Baz;
109 pub enum Quux {}; 164 pub enum Quux {};
110 ",
111 );
112 check_module_item_map(
113 &item_map,
114 module_id,
115 "
116 Baz: t v
117 Quux: t
118 foo: t
119 ", 165 ",
120 ); 166 );
167 assert_snapshot_matches!(map,
168 @r###"
169crate
170Quux: t
171Baz: t v
172foo: t
173
174crate::foo
175bar: t
176
177crate::foo::bar
178Quux: t
179Baz: t v
180"###
181 );
121} 182}
122 183
123#[test] 184#[test]
124fn re_exports() { 185fn re_exports() {
125 let (item_map, module_id) = item_map( 186 let map = def_map(
126 " 187 "
127 //- /lib.rs 188 //- /lib.rs
128 mod foo; 189 mod foo;
129 190
130 use self::foo::Baz; 191 use self::foo::Baz;
131 <|>
132 192
133 //- /foo/mod.rs 193 //- /foo/mod.rs
134 pub mod bar; 194 pub mod bar;
@@ -137,137 +197,73 @@ fn re_exports() {
137 197
138 //- /foo/bar.rs 198 //- /foo/bar.rs
139 pub struct Baz; 199 pub struct Baz;
140 ",
141 );
142 check_module_item_map(
143 &item_map,
144 module_id,
145 "
146 Baz: t v
147 foo: t
148 ", 200 ",
149 ); 201 );
150} 202 assert_snapshot_matches!(map,
203 @r###"
204crate
205Baz: t v
206foo: t
151 207
152#[test] 208crate::foo
153fn glob_1() { 209bar: t
154 let (item_map, module_id) = item_map( 210Baz: t v
155 "
156 //- /lib.rs
157 mod foo;
158 use foo::*;
159 <|>
160
161 //- /foo/mod.rs
162 pub mod bar;
163 pub use self::bar::Baz;
164 pub struct Foo;
165 211
166 //- /foo/bar.rs 212crate::foo::bar
167 pub struct Baz; 213Baz: t v
168 ", 214"###
169 );
170 check_module_item_map(
171 &item_map,
172 module_id,
173 "
174 Baz: t v
175 Foo: t v
176 bar: t
177 foo: t
178 ",
179 ); 215 );
180} 216}
181 217
182#[test] 218#[test]
183fn glob_2() { 219fn std_prelude() {
184 let (item_map, module_id) = item_map( 220 covers!(std_prelude);
185 " 221 let map = def_map_with_crate_graph(
186 //- /lib.rs
187 mod foo;
188 use foo::*;
189 <|>
190
191 //- /foo/mod.rs
192 pub mod bar;
193 pub use self::bar::*;
194 pub struct Foo;
195
196 //- /foo/bar.rs
197 pub struct Baz;
198 pub use super::*;
199 ",
200 );
201 check_module_item_map(
202 &item_map,
203 module_id,
204 " 222 "
205 Baz: t v 223 //- /main.rs
206 Foo: t v 224 use Foo::*;
207 bar: t
208 foo: t
209 ",
210 );
211}
212 225
213#[test]
214fn glob_enum() {
215 covers!(glob_enum);
216 let (item_map, module_id) = item_map(
217 "
218 //- /lib.rs 226 //- /lib.rs
219 enum Foo { 227 mod prelude;
220 Bar, Baz 228 #[prelude_import]
221 } 229 use prelude::*;
222 use self::Foo::*; 230
223 <|> 231 //- /prelude.rs
224 ", 232 pub enum Foo { Bar, Baz };
225 );
226 check_module_item_map(
227 &item_map,
228 module_id,
229 "
230 Bar: t v
231 Baz: t v
232 Foo: t
233 ", 233 ",
234 crate_graph! {
235 "main": ("/main.rs", ["test_crate"]),
236 "test_crate": ("/lib.rs", []),
237 },
234 ); 238 );
239 assert_snapshot_matches!(map, @r###"
240crate
241Bar: t v
242Baz: t v
243"###);
235} 244}
236 245
237#[test] 246#[test]
238fn glob_across_crates() { 247fn can_import_enum_variant() {
239 covers!(glob_across_crates); 248 covers!(can_import_enum_variant);
240 let mut db = MockDatabase::with_files( 249 let map = def_map(
241 " 250 "
242 //- /main.rs
243 use test_crate::*;
244
245 //- /lib.rs 251 //- /lib.rs
246 pub struct Baz; 252 enum E { V }
253 use self::E::V;
247 ", 254 ",
248 ); 255 );
249 db.set_crate_graph_from_fixture(crate_graph! { 256 assert_snapshot_matches!(map, @r###"
250 "main": ("/main.rs", ["test_crate"]), 257crate
251 "test_crate": ("/lib.rs", []), 258V: t v
252 }); 259E: t
253 let main_id = db.file_id_of("/main.rs"); 260"###
254
255 let module = crate::source_binder::module_from_file_id(&db, main_id).unwrap();
256 let krate = module.krate(&db).unwrap();
257 let item_map = db.item_map(krate);
258
259 check_module_item_map(
260 &item_map,
261 module.module_id,
262 "
263 Baz: t v
264 ",
265 ); 261 );
266} 262}
267 263
268#[test] 264#[test]
269fn edition_2015_imports() { 265fn edition_2015_imports() {
270 let mut db = MockDatabase::with_files( 266 let map = def_map_with_crate_graph(
271 " 267 "
272 //- /main.rs 268 //- /main.rs
273 mod foo; 269 mod foo;
@@ -282,31 +278,32 @@ fn edition_2015_imports() {
282 278
283 //- /lib.rs 279 //- /lib.rs
284 struct FromLib; 280 struct FromLib;
285 ",
286 );
287 db.set_crate_graph_from_fixture(crate_graph! {
288 "main": ("/main.rs", "2015", ["other_crate"]),
289 "other_crate": ("/lib.rs", "2018", []),
290 });
291 let foo_id = db.file_id_of("/foo.rs");
292
293 let module = crate::source_binder::module_from_file_id(&db, foo_id).unwrap();
294 let krate = module.krate(&db).unwrap();
295 let item_map = db.item_map(krate);
296
297 check_module_item_map(
298 &item_map,
299 module.module_id,
300 "
301 Bar: t v
302 FromLib: t v
303 ", 281 ",
282 crate_graph! {
283 "main": ("/main.rs", "2015", ["other_crate"]),
284 "other_crate": ("/lib.rs", "2018", []),
285 },
286 );
287
288 assert_snapshot_matches!(map,
289 @r###"
290crate
291bar: t
292foo: t
293
294crate::bar
295Bar: t v
296
297crate::foo
298FromLib: t v
299Bar: t v
300"###
304 ); 301 );
305} 302}
306 303
307#[test] 304#[test]
308fn module_resolution_works_for_non_standard_filenames() { 305fn module_resolution_works_for_non_standard_filenames() {
309 let mut db = MockDatabase::with_files( 306 let map = def_map_with_crate_graph(
310 " 307 "
311 //- /my_library.rs 308 //- /my_library.rs
312 mod foo; 309 mod foo;
@@ -315,73 +312,32 @@ fn module_resolution_works_for_non_standard_filenames() {
315 //- /foo/mod.rs 312 //- /foo/mod.rs
316 pub struct Bar; 313 pub struct Bar;
317 ", 314 ",
315 crate_graph! {
316 "my_library": ("/my_library.rs", []),
317 },
318 ); 318 );
319 db.set_crate_graph_from_fixture(crate_graph! {
320 "my_library": ("/my_library.rs", []),
321 });
322 let file_id = db.file_id_of("/my_library.rs");
323
324 let module = crate::source_binder::module_from_file_id(&db, file_id).unwrap();
325 let krate = module.krate(&db).unwrap();
326 let module_id = module.module_id;
327 let item_map = db.item_map(krate);
328 check_module_item_map(
329 &item_map,
330 module_id,
331 "
332 Bar: t v
333 foo: t
334 ",
335 );
336}
337 319
338#[test] 320 assert_snapshot_matches!(map,
339fn std_prelude() { 321 @r###"
340 covers!(std_prelude); 322crate
341 let mut db = MockDatabase::with_files( 323Bar: t v
342 " 324foo: t
343 //- /main.rs
344 use Foo::*;
345
346 //- /lib.rs
347 mod prelude;
348 #[prelude_import]
349 use prelude::*;
350 325
351 //- /prelude.rs 326crate::foo
352 pub enum Foo { Bar, Baz }; 327Bar: t v
353 ", 328"###
354 );
355 db.set_crate_graph_from_fixture(crate_graph! {
356 "main": ("/main.rs", ["test_crate"]),
357 "test_crate": ("/lib.rs", []),
358 });
359 let main_id = db.file_id_of("/main.rs");
360
361 let module = crate::source_binder::module_from_file_id(&db, main_id).unwrap();
362 let krate = module.krate(&db).unwrap();
363 let item_map = db.item_map(krate);
364
365 check_module_item_map(
366 &item_map,
367 module.module_id,
368 "
369 Bar: t v
370 Baz: t v
371 ",
372 ); 329 );
373} 330}
374 331
375#[test] 332#[test]
376fn name_res_works_for_broken_modules() { 333fn name_res_works_for_broken_modules() {
377 covers!(name_res_works_for_broken_modules); 334 covers!(name_res_works_for_broken_modules);
378 let (item_map, module_id) = item_map( 335 let map = def_map(
379 " 336 "
380 //- /lib.rs 337 //- /lib.rs
381 mod foo // no `;`, no body 338 mod foo // no `;`, no body
382 339
383 use self::foo::Baz; 340 use self::foo::Baz;
384 <|>
385 341
386 //- /foo/mod.rs 342 //- /foo/mod.rs
387 pub mod bar; 343 pub mod bar;
@@ -390,65 +346,47 @@ fn name_res_works_for_broken_modules() {
390 346
391 //- /foo/bar.rs 347 //- /foo/bar.rs
392 pub struct Baz; 348 pub struct Baz;
393 ",
394 );
395 check_module_item_map(
396 &item_map,
397 module_id,
398 "
399 Baz: _
400 ", 349 ",
401 ); 350 );
402} 351 assert_snapshot_matches!(map,
403 352 @r###"
404#[test] 353crate
405fn item_map_using_self() { 354Baz: _
406 let (item_map, module_id) = item_map( 355"###
407 "
408 //- /lib.rs
409 mod foo;
410 use crate::foo::bar::Baz::{self};
411 <|>
412 //- /foo/mod.rs
413 pub mod bar;
414 //- /foo/bar.rs
415 pub struct Baz;
416 ",
417 );
418 check_module_item_map(
419 &item_map,
420 module_id,
421 "
422 Baz: t v
423 foo: t
424 ",
425 ); 356 );
426} 357}
427 358
428#[test] 359#[test]
429fn item_map_enum_importing() { 360fn item_map_using_self() {
430 covers!(item_map_enum_importing); 361 let map = def_map(
431 let (item_map, module_id) = item_map(
432 " 362 "
433 //- /lib.rs 363 //- /lib.rs
434 enum E { V } 364 mod foo;
435 use self::E::V; 365 use crate::foo::bar::Baz::{self};
436 <|> 366 //- /foo/mod.rs
367 pub mod bar;
368 //- /foo/bar.rs
369 pub struct Baz;
437 ", 370 ",
438 ); 371 );
439 check_module_item_map( 372 assert_snapshot_matches!(map,
440 &item_map, 373 @r###"
441 module_id, 374crate
442 " 375Baz: t v
443 E: t 376foo: t
444 V: t v 377
445 ", 378crate::foo
379bar: t
380
381crate::foo::bar
382Baz: t v
383"###
446 ); 384 );
447} 385}
448 386
449#[test] 387#[test]
450fn item_map_across_crates() { 388fn item_map_across_crates() {
451 let mut db = MockDatabase::with_files( 389 let map = def_map_with_crate_graph(
452 " 390 "
453 //- /main.rs 391 //- /main.rs
454 use test_crate::Baz; 392 use test_crate::Baz;
@@ -456,29 +394,23 @@ fn item_map_across_crates() {
456 //- /lib.rs 394 //- /lib.rs
457 pub struct Baz; 395 pub struct Baz;
458 ", 396 ",
397 crate_graph! {
398 "main": ("/main.rs", ["test_crate"]),
399 "test_crate": ("/lib.rs", []),
400 },
459 ); 401 );
460 db.set_crate_graph_from_fixture(crate_graph! { 402
461 "main": ("/main.rs", ["test_crate"]), 403 assert_snapshot_matches!(map,
462 "test_crate": ("/lib.rs", []), 404 @r###"
463 }); 405crate
464 let main_id = db.file_id_of("/main.rs"); 406Baz: t v
465 407"###
466 let module = crate::source_binder::module_from_file_id(&db, main_id).unwrap();
467 let krate = module.krate(&db).unwrap();
468 let item_map = db.item_map(krate);
469
470 check_module_item_map(
471 &item_map,
472 module.module_id,
473 "
474 Baz: t v
475 ",
476 ); 408 );
477} 409}
478 410
479#[test] 411#[test]
480fn extern_crate_rename() { 412fn extern_crate_rename() {
481 let mut db = MockDatabase::with_files( 413 let map = def_map_with_crate_graph(
482 " 414 "
483 //- /main.rs 415 //- /main.rs
484 extern crate alloc as alloc_crate; 416 extern crate alloc as alloc_crate;
@@ -492,29 +424,23 @@ fn extern_crate_rename() {
492 //- /lib.rs 424 //- /lib.rs
493 struct Arc; 425 struct Arc;
494 ", 426 ",
427 crate_graph! {
428 "main": ("/main.rs", ["alloc"]),
429 "alloc": ("/lib.rs", []),
430 },
495 ); 431 );
496 db.set_crate_graph_from_fixture(crate_graph! { 432
497 "main": ("/main.rs", ["alloc"]), 433 assert_snapshot_matches!(map,
498 "alloc": ("/lib.rs", []), 434 @r###"
499 }); 435crate
500 let sync_id = db.file_id_of("/sync.rs"); 436Arc: t v
501 437"###
502 let module = crate::source_binder::module_from_file_id(&db, sync_id).unwrap();
503 let krate = module.krate(&db).unwrap();
504 let item_map = db.item_map(krate);
505
506 check_module_item_map(
507 &item_map,
508 module.module_id,
509 "
510 Arc: t v
511 ",
512 ); 438 );
513} 439}
514 440
515#[test] 441#[test]
516fn extern_crate_rename_2015_edition() { 442fn extern_crate_rename_2015_edition() {
517 let mut db = MockDatabase::with_files( 443 let map = def_map_with_crate_graph(
518 " 444 "
519 //- /main.rs 445 //- /main.rs
520 extern crate alloc as alloc_crate; 446 extern crate alloc as alloc_crate;
@@ -528,29 +454,23 @@ fn extern_crate_rename_2015_edition() {
528 //- /lib.rs 454 //- /lib.rs
529 struct Arc; 455 struct Arc;
530 ", 456 ",
457 crate_graph! {
458 "main": ("/main.rs", "2015", ["alloc"]),
459 "alloc": ("/lib.rs", []),
460 },
531 ); 461 );
532 db.set_crate_graph_from_fixture(crate_graph! { 462
533 "main": ("/main.rs", "2015", ["alloc"]), 463 assert_snapshot_matches!(map,
534 "alloc": ("/lib.rs", []), 464 @r###"
535 }); 465crate
536 let sync_id = db.file_id_of("/sync.rs"); 466Arc: t v
537 467"###
538 let module = crate::source_binder::module_from_file_id(&db, sync_id).unwrap();
539 let krate = module.krate(&db).unwrap();
540 let item_map = db.item_map(krate);
541
542 check_module_item_map(
543 &item_map,
544 module.module_id,
545 "
546 Arc: t v
547 ",
548 ); 468 );
549} 469}
550 470
551#[test] 471#[test]
552fn import_across_source_roots() { 472fn import_across_source_roots() {
553 let mut db = MockDatabase::with_files( 473 let map = def_map_with_crate_graph(
554 " 474 "
555 //- /lib.rs 475 //- /lib.rs
556 pub mod a { 476 pub mod a {
@@ -564,29 +484,23 @@ fn import_across_source_roots() {
564 //- /main/main.rs 484 //- /main/main.rs
565 use test_crate::a::b::C; 485 use test_crate::a::b::C;
566 ", 486 ",
487 crate_graph! {
488 "main": ("/main/main.rs", ["test_crate"]),
489 "test_crate": ("/lib.rs", []),
490 },
567 ); 491 );
568 db.set_crate_graph_from_fixture(crate_graph! { 492
569 "main": ("/main/main.rs", ["test_crate"]), 493 assert_snapshot_matches!(map,
570 "test_crate": ("/lib.rs", []), 494 @r###"
571 }); 495crate
572 let main_id = db.file_id_of("/main/main.rs"); 496C: t v
573 497"###
574 let module = crate::source_binder::module_from_file_id(&db, main_id).unwrap();
575 let krate = module.krate(&db).unwrap();
576 let item_map = db.item_map(krate);
577
578 check_module_item_map(
579 &item_map,
580 module.module_id,
581 "
582 C: t v
583 ",
584 ); 498 );
585} 499}
586 500
587#[test] 501#[test]
588fn reexport_across_crates() { 502fn reexport_across_crates() {
589 let mut db = MockDatabase::with_files( 503 let map = def_map_with_crate_graph(
590 " 504 "
591 //- /main.rs 505 //- /main.rs
592 use test_crate::Baz; 506 use test_crate::Baz;
@@ -599,29 +513,23 @@ fn reexport_across_crates() {
599 //- /foo.rs 513 //- /foo.rs
600 pub struct Baz; 514 pub struct Baz;
601 ", 515 ",
516 crate_graph! {
517 "main": ("/main.rs", ["test_crate"]),
518 "test_crate": ("/lib.rs", []),
519 },
602 ); 520 );
603 db.set_crate_graph_from_fixture(crate_graph! { 521
604 "main": ("/main.rs", ["test_crate"]), 522 assert_snapshot_matches!(map,
605 "test_crate": ("/lib.rs", []), 523 @r###"
606 }); 524crate
607 let main_id = db.file_id_of("/main.rs"); 525Baz: t v
608 526"###
609 let module = crate::source_binder::module_from_file_id(&db, main_id).unwrap();
610 let krate = module.krate(&db).unwrap();
611 let item_map = db.item_map(krate);
612
613 check_module_item_map(
614 &item_map,
615 module.module_id,
616 "
617 Baz: t v
618 ",
619 ); 527 );
620} 528}
621 529
622#[test] 530#[test]
623fn values_dont_shadow_extern_crates() { 531fn values_dont_shadow_extern_crates() {
624 let mut db = MockDatabase::with_files( 532 let map = def_map_with_crate_graph(
625 " 533 "
626 //- /main.rs 534 //- /main.rs
627 fn foo() {} 535 fn foo() {}
@@ -630,139 +538,17 @@ fn values_dont_shadow_extern_crates() {
630 //- /foo/lib.rs 538 //- /foo/lib.rs
631 pub struct Bar; 539 pub struct Bar;
632 ", 540 ",
541 crate_graph! {
542 "main": ("/main.rs", ["foo"]),
543 "foo": ("/foo/lib.rs", []),
544 },
633 ); 545 );
634 db.set_crate_graph_from_fixture(crate_graph! {
635 "main": ("/main.rs", ["foo"]),
636 "foo": ("/foo/lib.rs", []),
637 });
638 let main_id = db.file_id_of("/main.rs");
639
640 let module = crate::source_binder::module_from_file_id(&db, main_id).unwrap();
641 let krate = module.krate(&db).unwrap();
642 let item_map = db.item_map(krate);
643
644 check_module_item_map(
645 &item_map,
646 module.module_id,
647 "
648 Bar: t v
649 foo: v
650 ",
651 );
652}
653
654fn check_item_map_is_not_recomputed(initial: &str, file_change: &str) {
655 let (mut db, pos) = MockDatabase::with_position(initial);
656 let module = crate::source_binder::module_from_file_id(&db, pos.file_id).unwrap();
657 let krate = module.krate(&db).unwrap();
658 {
659 let events = db.log_executed(|| {
660 db.item_map(krate);
661 });
662 assert!(format!("{:?}", events).contains("item_map"))
663 }
664 db.set_file_text(pos.file_id, Arc::new(file_change.to_string()));
665
666 {
667 let events = db.log_executed(|| {
668 db.item_map(krate);
669 });
670 assert!(!format!("{:?}", events).contains("item_map"), "{:#?}", events)
671 }
672}
673
674#[test]
675fn typing_inside_a_function_should_not_invalidate_item_map() {
676 check_item_map_is_not_recomputed(
677 "
678 //- /lib.rs
679 mod foo;<|>
680
681 use crate::foo::bar::Baz;
682
683 fn foo() -> i32 {
684 1 + 1
685 }
686 //- /foo/mod.rs
687 pub mod bar;
688
689 //- /foo/bar.rs
690 pub struct Baz;
691 ",
692 "
693 mod foo;
694
695 use crate::foo::bar::Baz;
696
697 fn foo() -> i32 { 92 }
698 ",
699 );
700}
701
702#[test]
703fn adding_inner_items_should_not_invalidate_item_map() {
704 check_item_map_is_not_recomputed(
705 "
706 //- /lib.rs
707 struct S { a: i32}
708 enum E { A }
709 trait T {
710 fn a() {}
711 }
712 mod foo;<|>
713 impl S {
714 fn a() {}
715 }
716 use crate::foo::bar::Baz;
717 //- /foo/mod.rs
718 pub mod bar;
719
720 //- /foo/bar.rs
721 pub struct Baz;
722 ",
723 "
724 struct S { a: i32, b: () }
725 enum E { A, B }
726 trait T {
727 fn a() {}
728 fn b() {}
729 }
730 mod foo;<|>
731 impl S {
732 fn a() {}
733 fn b() {}
734 }
735 use crate::foo::bar::Baz;
736 ",
737 );
738}
739 546
740#[test] 547 assert_snapshot_matches!(map,
741fn typing_inside_a_function_inside_a_macro_should_not_invalidate_item_map() { 548 @r###"
742 check_item_map_is_not_recomputed( 549crate
743 " 550Bar: t v
744 //- /lib.rs 551foo: v
745 mod foo; 552"###
746
747 use crate::foo::bar::Baz;
748
749 //- /foo/mod.rs
750 pub mod bar;
751
752 //- /foo/bar.rs
753 <|>
754 salsa::query_group! {
755 trait Baz {
756 fn foo() -> i32 { 1 + 1 }
757 }
758 }
759 ",
760 "
761 salsa::query_group! {
762 trait Baz {
763 fn foo() -> i32 { 92 }
764 }
765 }
766 ",
767 ); 553 );
768} 554}
diff --git a/crates/ra_hir/src/nameres/tests/globs.rs b/crates/ra_hir/src/nameres/tests/globs.rs
new file mode 100644
index 000000000..6e50c7ff6
--- /dev/null
+++ b/crates/ra_hir/src/nameres/tests/globs.rs
@@ -0,0 +1,118 @@
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_matches!(map, @r###"
21crate
22bar: t
23Foo: t v
24Baz: t v
25foo: t
26
27crate::foo
28bar: t
29Foo: t v
30Baz: t v
31
32crate::foo::bar
33Baz: 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_matches!(map, @r###"
57crate
58bar: t
59Foo: t v
60Baz: t v
61foo: t
62
63crate::foo
64bar: t
65Foo: t v
66Baz: t v
67
68crate::foo::bar
69bar: t
70Foo: t v
71Baz: t v
72"###
73 );
74}
75
76#[test]
77fn glob_across_crates() {
78 covers!(glob_across_crates);
79 let map = def_map_with_crate_graph(
80 "
81 //- /main.rs
82 use test_crate::*;
83
84 //- /lib.rs
85 pub struct Baz;
86 ",
87 crate_graph! {
88 "main": ("/main.rs", ["test_crate"]),
89 "test_crate": ("/lib.rs", []),
90 },
91 );
92 assert_snapshot_matches!(map, @r###"
93crate
94Baz: t v
95"###
96 );
97}
98
99#[test]
100fn glob_enum() {
101 covers!(glob_enum);
102 let map = def_map(
103 "
104 //- /lib.rs
105 enum Foo {
106 Bar, Baz
107 }
108 use self::Foo::*;
109 ",
110 );
111 assert_snapshot_matches!(map, @r###"
112crate
113Foo: t
114Bar: t v
115Baz: t v
116"###
117 );
118}
diff --git a/crates/ra_hir/src/nameres/tests/incremental.rs b/crates/ra_hir/src/nameres/tests/incremental.rs
new file mode 100644
index 000000000..698781923
--- /dev/null
+++ b/crates/ra_hir/src/nameres/tests/incremental.rs
@@ -0,0 +1,123 @@
1use super::*;
2
3use std::sync::Arc;
4
5use ra_db::SourceDatabase;
6
7fn check_def_map_is_not_recomputed(initial: &str, file_change: &str) {
8 let (mut db, pos) = MockDatabase::with_position(initial);
9 let crate_id = db.crate_graph().iter().next().unwrap();
10 let krate = Crate { crate_id };
11 {
12 let events = db.log_executed(|| {
13 db.crate_def_map(krate);
14 });
15 assert!(format!("{:?}", events).contains("crate_def_map"), "{:#?}", events)
16 }
17 db.set_file_text(pos.file_id, Arc::new(file_change.to_string()));
18
19 {
20 let events = db.log_executed(|| {
21 db.crate_def_map(krate);
22 });
23 assert!(!format!("{:?}", events).contains("crate_def_map"), "{:#?}", events)
24 }
25}
26
27#[test]
28fn typing_inside_a_function_should_not_invalidate_def_map() {
29 check_def_map_is_not_recomputed(
30 "
31 //- /lib.rs
32 mod foo;<|>
33
34 use crate::foo::bar::Baz;
35
36 fn foo() -> i32 {
37 1 + 1
38 }
39 //- /foo/mod.rs
40 pub mod bar;
41
42 //- /foo/bar.rs
43 pub struct Baz;
44 ",
45 "
46 mod foo;
47
48 use crate::foo::bar::Baz;
49
50 fn foo() -> i32 { 92 }
51 ",
52 );
53}
54
55#[test]
56fn adding_inner_items_should_not_invalidate_def_map() {
57 check_def_map_is_not_recomputed(
58 "
59 //- /lib.rs
60 struct S { a: i32}
61 enum E { A }
62 trait T {
63 fn a() {}
64 }
65 mod foo;<|>
66 impl S {
67 fn a() {}
68 }
69 use crate::foo::bar::Baz;
70 //- /foo/mod.rs
71 pub mod bar;
72
73 //- /foo/bar.rs
74 pub struct Baz;
75 ",
76 "
77 struct S { a: i32, b: () }
78 enum E { A, B }
79 trait T {
80 fn a() {}
81 fn b() {}
82 }
83 mod foo;<|>
84 impl S {
85 fn a() {}
86 fn b() {}
87 }
88 use crate::foo::bar::Baz;
89 ",
90 );
91}
92
93// It would be awesome to make this work, but it's unclear how
94#[test]
95#[ignore]
96fn typing_inside_a_function_inside_a_macro_should_not_invalidate_def_map() {
97 check_def_map_is_not_recomputed(
98 "
99 //- /lib.rs
100 mod foo;
101
102 use crate::foo::bar::Baz;
103
104 //- /foo/mod.rs
105 pub mod bar;
106
107 //- /foo/bar.rs
108 <|>
109 salsa::query_group! {
110 trait Baz {
111 fn foo() -> i32 { 1 + 1 }
112 }
113 }
114 ",
115 "
116 salsa::query_group! {
117 trait Baz {
118 fn foo() -> i32 { 92 }
119 }
120 }
121 ",
122 );
123}
diff --git a/crates/ra_hir/src/nameres/tests/macros.rs b/crates/ra_hir/src/nameres/tests/macros.rs
new file mode 100644
index 000000000..8781b026b
--- /dev/null
+++ b/crates/ra_hir/src/nameres/tests/macros.rs
@@ -0,0 +1,94 @@
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_matches!(map, @r###"
21crate
22nested: t
23Foo: t v
24
25crate::nested
26Bar: t v
27Baz: 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 //- /n1.rs
42 m!(n2)
43 //- /n1/n2.rs
44 struct X;
45 ",
46 );
47 assert_snapshot_matches!(map, @r###"
48crate
49n1: t
50
51crate::n1
52n2: t
53
54crate::n1::n2
55X: t v
56"###);
57}
58
59#[test]
60fn macro_rules_from_other_crates_are_visible() {
61 let map = def_map_with_crate_graph(
62 "
63 //- /main.rs
64 foo::structs!(Foo, Bar)
65 mod bar;
66
67 //- /bar.rs
68 use crate::*;
69
70 //- /lib.rs
71 #[macro_export]
72 macro_rules! structs {
73 ($($i:ident),*) => {
74 $(struct $i { field: u32 } )*
75 }
76 }
77 ",
78 crate_graph! {
79 "main": ("/main.rs", ["foo"]),
80 "foo": ("/lib.rs", []),
81 },
82 );
83 assert_snapshot_matches!(map, @r###"
84crate
85bar: t
86Foo: t v
87Bar: t v
88
89crate::bar
90bar: t
91Foo: t v
92Bar: t v
93"###);
94}