aboutsummaryrefslogtreecommitdiff
path: root/crates/ra_hir/src/module
diff options
context:
space:
mode:
authorAleksey Kladov <[email protected]>2019-01-06 14:33:27 +0000
committerAleksey Kladov <[email protected]>2019-01-06 14:36:10 +0000
commitfd4456d0ec88e3433a7a8be6f27d8af9afedefe5 (patch)
tree37f1225b085e22756cbfd65875595c60205cc93a /crates/ra_hir/src/module
parent5a505189a8f7ed274893a45aed0d0249083d1277 (diff)
flatten module structure
Diffstat (limited to 'crates/ra_hir/src/module')
-rw-r--r--crates/ra_hir/src/module/imp.rs190
-rw-r--r--crates/ra_hir/src/module/nameres.rs509
-rw-r--r--crates/ra_hir/src/module/nameres/tests.rs273
3 files changed, 0 insertions, 972 deletions
diff --git a/crates/ra_hir/src/module/imp.rs b/crates/ra_hir/src/module/imp.rs
deleted file mode 100644
index 3849026db..000000000
--- a/crates/ra_hir/src/module/imp.rs
+++ /dev/null
@@ -1,190 +0,0 @@
1use std::sync::Arc;
2
3use ra_syntax::ast::{self, NameOwner};
4use relative_path::RelativePathBuf;
5use rustc_hash::{FxHashMap, FxHashSet};
6use arrayvec::ArrayVec;
7use ra_db::{SourceRoot, SourceRootId, Cancelable, FileId};
8
9use crate::{
10 HirDatabase, Name, AsName,
11};
12
13use super::{
14 LinkData, LinkId, ModuleData, ModuleId, ModuleSource,
15 ModuleTree, Problem,
16};
17
18#[derive(Clone, Hash, PartialEq, Eq, Debug)]
19pub enum Submodule {
20 Declaration(Name),
21 Definition(Name, ModuleSource),
22}
23
24impl Submodule {
25 fn name(&self) -> &Name {
26 match self {
27 Submodule::Declaration(name) => name,
28 Submodule::Definition(name, _) => name,
29 }
30 }
31}
32
33pub(crate) fn modules<'a>(
34 root: impl ast::ModuleItemOwner<'a>,
35) -> impl Iterator<Item = (Name, ast::Module<'a>)> {
36 root.items()
37 .filter_map(|item| match item {
38 ast::ModuleItem::Module(m) => Some(m),
39 _ => None,
40 })
41 .filter_map(|module| {
42 let name = module.name()?.as_name();
43 Some((name, module))
44 })
45}
46
47pub(crate) fn module_tree(
48 db: &impl HirDatabase,
49 source_root: SourceRootId,
50) -> Cancelable<Arc<ModuleTree>> {
51 db.check_canceled()?;
52 let res = create_module_tree(db, source_root)?;
53 Ok(Arc::new(res))
54}
55
56fn create_module_tree<'a>(
57 db: &impl HirDatabase,
58 source_root: SourceRootId,
59) -> Cancelable<ModuleTree> {
60 let mut tree = ModuleTree::default();
61
62 let mut roots = FxHashMap::default();
63 let mut visited = FxHashSet::default();
64
65 let source_root = db.source_root(source_root);
66 for &file_id in source_root.files.values() {
67 let source = ModuleSource::new_file(file_id.into());
68 if visited.contains(&source) {
69 continue; // TODO: use explicit crate_roots here
70 }
71 assert!(!roots.contains_key(&file_id));
72 let module_id = build_subtree(
73 db,
74 &source_root,
75 &mut tree,
76 &mut visited,
77 &mut roots,
78 None,
79 source,
80 )?;
81 roots.insert(file_id, module_id);
82 }
83 Ok(tree)
84}
85
86fn build_subtree(
87 db: &impl HirDatabase,
88 source_root: &SourceRoot,
89 tree: &mut ModuleTree,
90 visited: &mut FxHashSet<ModuleSource>,
91 roots: &mut FxHashMap<FileId, ModuleId>,
92 parent: Option<LinkId>,
93 source: ModuleSource,
94) -> Cancelable<ModuleId> {
95 visited.insert(source);
96 let id = tree.push_mod(ModuleData {
97 source,
98 parent,
99 children: Vec::new(),
100 });
101 for sub in db.submodules(source)?.iter() {
102 let link = tree.push_link(LinkData {
103 name: sub.name().clone(),
104 owner: id,
105 points_to: Vec::new(),
106 problem: None,
107 });
108
109 let (points_to, problem) = match sub {
110 Submodule::Declaration(name) => {
111 let (points_to, problem) = resolve_submodule(db, source, &name);
112 let points_to = points_to
113 .into_iter()
114 .map(|file_id| match roots.remove(&file_id) {
115 Some(module_id) => {
116 tree.mods[module_id].parent = Some(link);
117 Ok(module_id)
118 }
119 None => build_subtree(
120 db,
121 source_root,
122 tree,
123 visited,
124 roots,
125 Some(link),
126 ModuleSource::new_file(file_id.into()),
127 ),
128 })
129 .collect::<Cancelable<Vec<_>>>()?;
130 (points_to, problem)
131 }
132 Submodule::Definition(_name, submodule_source) => {
133 let points_to = build_subtree(
134 db,
135 source_root,
136 tree,
137 visited,
138 roots,
139 Some(link),
140 *submodule_source,
141 )?;
142 (vec![points_to], None)
143 }
144 };
145
146 tree.links[link].points_to = points_to;
147 tree.links[link].problem = problem;
148 }
149 Ok(id)
150}
151
152fn resolve_submodule(
153 db: &impl HirDatabase,
154 source: ModuleSource,
155 name: &Name,
156) -> (Vec<FileId>, Option<Problem>) {
157 // FIXME: handle submodules of inline modules properly
158 let file_id = source.file_id().original_file(db);
159 let source_root_id = db.file_source_root(file_id);
160 let path = db.file_relative_path(file_id);
161 let root = RelativePathBuf::default();
162 let dir_path = path.parent().unwrap_or(&root);
163 let mod_name = path.file_stem().unwrap_or("unknown");
164 let is_dir_owner = mod_name == "mod" || mod_name == "lib" || mod_name == "main";
165
166 let file_mod = dir_path.join(format!("{}.rs", name));
167 let dir_mod = dir_path.join(format!("{}/mod.rs", name));
168 let file_dir_mod = dir_path.join(format!("{}/{}.rs", mod_name, name));
169 let mut candidates = ArrayVec::<[_; 2]>::new();
170 if is_dir_owner {
171 candidates.push(file_mod.clone());
172 candidates.push(dir_mod);
173 } else {
174 candidates.push(file_dir_mod.clone());
175 };
176 let sr = db.source_root(source_root_id);
177 let points_to = candidates
178 .into_iter()
179 .filter_map(|path| sr.files.get(&path))
180 .map(|&it| it)
181 .collect::<Vec<_>>();
182 let problem = if points_to.is_empty() {
183 Some(Problem::UnresolvedModule {
184 candidate: if is_dir_owner { file_mod } else { file_dir_mod },
185 })
186 } else {
187 None
188 };
189 (points_to, problem)
190}
diff --git a/crates/ra_hir/src/module/nameres.rs b/crates/ra_hir/src/module/nameres.rs
deleted file mode 100644
index 7d5e86c89..000000000
--- a/crates/ra_hir/src/module/nameres.rs
+++ /dev/null
@@ -1,509 +0,0 @@
1//! Name resolution algorithm. The end result of the algorithm is `ItemMap`: a
2//! map with maps each module to it's scope: the set of items, visible in the
3//! module. That is, we only resolve imports here, name resolution of item
4//! bodies will be done in a separate step.
5//!
6//! Like Rustc, we use an interative per-crate algorithm: we start with scopes
7//! containing only directly defined items, and then iteratively resolve
8//! imports.
9//!
10//! To make this work nicely in the IDE scenarios, we place `InputModuleItems`
11//! in between raw syntax and name resolution. `InputModuleItems` are computed
12//! using only the module's syntax, and it is all directly defined items plus
13//! imports. The plain is to make `InputModuleItems` independent of local
14//! modifications (that is, typing inside a function shold not change IMIs),
15//! such that the results of name resolution can be preserved unless the module
16//! structure itself is modified.
17use std::sync::Arc;
18
19use rustc_hash::FxHashMap;
20use ra_syntax::{
21 TextRange,
22 SyntaxKind::{self, *},
23 ast::{self, AstNode}
24};
25use ra_db::{SourceRootId, Cancelable, FileId};
26
27use crate::{
28 HirFileId,
29 DefId, DefLoc, DefKind,
30 SourceItemId, SourceFileItemId, SourceFileItems,
31 Path, PathKind,
32 HirDatabase, Crate,
33 Name, AsName,
34 module::{ModuleId, ModuleTree},
35};
36
37/// Item map is the result of the name resolution. Item map contains, for each
38/// module, the set of visible items.
39// FIXME: currenty we compute item map per source-root. We should do it per crate instead.
40#[derive(Default, Debug, PartialEq, Eq)]
41pub struct ItemMap {
42 pub per_module: FxHashMap<ModuleId, ModuleScope>,
43}
44
45#[derive(Debug, Default, PartialEq, Eq, Clone)]
46pub struct ModuleScope {
47 items: FxHashMap<Name, Resolution>,
48}
49
50impl ModuleScope {
51 pub fn entries<'a>(&'a self) -> impl Iterator<Item = (&'a Name, &'a Resolution)> + 'a {
52 self.items.iter()
53 }
54 pub fn get(&self, name: &Name) -> Option<&Resolution> {
55 self.items.get(name)
56 }
57}
58
59/// A set of items and imports declared inside a module, without relation to
60/// other modules.
61///
62/// This stands in-between raw syntax and name resolution and alow us to avoid
63/// recomputing name res: if `InputModuleItems` are the same, we can avoid
64/// running name resolution.
65#[derive(Debug, Default, PartialEq, Eq)]
66pub struct InputModuleItems {
67 pub(crate) items: Vec<ModuleItem>,
68 imports: Vec<Import>,
69}
70
71#[derive(Debug, PartialEq, Eq)]
72pub(crate) struct ModuleItem {
73 pub(crate) id: SourceItemId,
74 pub(crate) name: Name,
75 kind: SyntaxKind,
76 vis: Vis,
77}
78
79#[derive(Debug, PartialEq, Eq)]
80enum Vis {
81 // Priv,
82 Other,
83}
84
85#[derive(Debug, Clone, PartialEq, Eq)]
86struct Import {
87 path: Path,
88 kind: ImportKind,
89}
90
91#[derive(Debug, Clone, Copy, PartialEq, Eq)]
92pub struct NamedImport {
93 pub file_item_id: SourceFileItemId,
94 pub relative_range: TextRange,
95}
96
97impl NamedImport {
98 // FIXME: this is only here for one use-case in completion. Seems like a
99 // pretty gross special case.
100 pub fn range(&self, db: &impl HirDatabase, file_id: FileId) -> TextRange {
101 let source_item_id = SourceItemId {
102 file_id: file_id.into(),
103 item_id: Some(self.file_item_id),
104 };
105 let syntax = db.file_item(source_item_id);
106 let offset = syntax.borrowed().range().start();
107 self.relative_range + offset
108 }
109}
110
111#[derive(Debug, Clone, PartialEq, Eq)]
112enum ImportKind {
113 Glob,
114 Named(NamedImport),
115}
116
117/// Resolution is basically `DefId` atm, but it should account for stuff like
118/// multiple namespaces, ambiguity and errors.
119#[derive(Debug, Clone, PartialEq, Eq)]
120pub struct Resolution {
121 /// None for unresolved
122 pub def_id: PerNs<DefId>,
123 /// ident by whitch this is imported into local scope.
124 pub import: Option<NamedImport>,
125}
126
127#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
128pub enum Namespace {
129 Types,
130 Values,
131}
132
133#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
134pub struct PerNs<T> {
135 pub types: Option<T>,
136 pub values: Option<T>,
137}
138
139impl<T> PerNs<T> {
140 pub fn none() -> PerNs<T> {
141 PerNs {
142 types: None,
143 values: None,
144 }
145 }
146
147 pub fn values(t: T) -> PerNs<T> {
148 PerNs {
149 types: None,
150 values: Some(t),
151 }
152 }
153
154 pub fn types(t: T) -> PerNs<T> {
155 PerNs {
156 types: Some(t),
157 values: None,
158 }
159 }
160
161 pub fn both(types: T, values: T) -> PerNs<T> {
162 PerNs {
163 types: Some(types),
164 values: Some(values),
165 }
166 }
167
168 pub fn is_none(&self) -> bool {
169 self.types.is_none() && self.values.is_none()
170 }
171
172 pub fn take(self, namespace: Namespace) -> Option<T> {
173 match namespace {
174 Namespace::Types => self.types,
175 Namespace::Values => self.values,
176 }
177 }
178
179 pub fn take_types(self) -> Option<T> {
180 self.take(Namespace::Types)
181 }
182
183 pub fn take_values(self) -> Option<T> {
184 self.take(Namespace::Values)
185 }
186
187 pub fn get(&self, namespace: Namespace) -> Option<&T> {
188 self.as_ref().take(namespace)
189 }
190
191 pub fn as_ref(&self) -> PerNs<&T> {
192 PerNs {
193 types: self.types.as_ref(),
194 values: self.values.as_ref(),
195 }
196 }
197
198 pub fn and_then<U>(self, f: impl Fn(T) -> Option<U>) -> PerNs<U> {
199 PerNs {
200 types: self.types.and_then(&f),
201 values: self.values.and_then(&f),
202 }
203 }
204
205 pub fn map<U>(self, f: impl Fn(T) -> U) -> PerNs<U> {
206 PerNs {
207 types: self.types.map(&f),
208 values: self.values.map(&f),
209 }
210 }
211}
212
213impl InputModuleItems {
214 pub(crate) fn add_item(
215 &mut self,
216 file_id: HirFileId,
217 file_items: &SourceFileItems,
218 item: ast::ModuleItem,
219 ) -> Option<()> {
220 match item {
221 ast::ModuleItem::StructDef(it) => {
222 self.items.push(ModuleItem::new(file_id, file_items, it)?)
223 }
224 ast::ModuleItem::EnumDef(it) => {
225 self.items.push(ModuleItem::new(file_id, file_items, it)?)
226 }
227 ast::ModuleItem::FnDef(it) => {
228 self.items.push(ModuleItem::new(file_id, file_items, it)?)
229 }
230 ast::ModuleItem::TraitDef(it) => {
231 self.items.push(ModuleItem::new(file_id, file_items, it)?)
232 }
233 ast::ModuleItem::TypeDef(it) => {
234 self.items.push(ModuleItem::new(file_id, file_items, it)?)
235 }
236 ast::ModuleItem::ImplBlock(_) => {
237 // impls don't define items
238 }
239 ast::ModuleItem::UseItem(it) => self.add_use_item(file_items, it),
240 ast::ModuleItem::ExternCrateItem(_) => {
241 // TODO
242 }
243 ast::ModuleItem::ConstDef(it) => {
244 self.items.push(ModuleItem::new(file_id, file_items, it)?)
245 }
246 ast::ModuleItem::StaticDef(it) => {
247 self.items.push(ModuleItem::new(file_id, file_items, it)?)
248 }
249 ast::ModuleItem::Module(it) => {
250 self.items.push(ModuleItem::new(file_id, file_items, it)?)
251 }
252 }
253 Some(())
254 }
255
256 fn add_use_item(&mut self, file_items: &SourceFileItems, item: ast::UseItem) {
257 let file_item_id = file_items.id_of_unchecked(item.syntax());
258 let start_offset = item.syntax().range().start();
259 Path::expand_use_item(item, |path, range| {
260 let kind = match range {
261 None => ImportKind::Glob,
262 Some(range) => ImportKind::Named(NamedImport {
263 file_item_id,
264 relative_range: range - start_offset,
265 }),
266 };
267 self.imports.push(Import { kind, path })
268 })
269 }
270}
271
272impl ModuleItem {
273 fn new<'a>(
274 file_id: HirFileId,
275 file_items: &SourceFileItems,
276 item: impl ast::NameOwner<'a>,
277 ) -> Option<ModuleItem> {
278 let name = item.name()?.as_name();
279 let kind = item.syntax().kind();
280 let vis = Vis::Other;
281 let item_id = Some(file_items.id_of_unchecked(item.syntax()));
282 let id = SourceItemId { file_id, item_id };
283 let res = ModuleItem {
284 id,
285 name,
286 kind,
287 vis,
288 };
289 Some(res)
290 }
291}
292
293pub(crate) struct Resolver<'a, DB> {
294 db: &'a DB,
295 input: &'a FxHashMap<ModuleId, Arc<InputModuleItems>>,
296 source_root: SourceRootId,
297 module_tree: Arc<ModuleTree>,
298 result: ItemMap,
299}
300
301impl<'a, DB> Resolver<'a, DB>
302where
303 DB: HirDatabase,
304{
305 pub(crate) fn new(
306 db: &'a DB,
307 input: &'a FxHashMap<ModuleId, Arc<InputModuleItems>>,
308 source_root: SourceRootId,
309 module_tree: Arc<ModuleTree>,
310 ) -> Resolver<'a, DB> {
311 Resolver {
312 db,
313 input,
314 source_root,
315 module_tree,
316 result: ItemMap::default(),
317 }
318 }
319
320 pub(crate) fn resolve(mut self) -> Cancelable<ItemMap> {
321 for (&module_id, items) in self.input.iter() {
322 self.populate_module(module_id, Arc::clone(items))?;
323 }
324
325 for &module_id in self.input.keys() {
326 self.db.check_canceled()?;
327 self.resolve_imports(module_id)?;
328 }
329 Ok(self.result)
330 }
331
332 fn populate_module(
333 &mut self,
334 module_id: ModuleId,
335 input: Arc<InputModuleItems>,
336 ) -> Cancelable<()> {
337 let mut module_items = ModuleScope::default();
338
339 // Populate extern crates prelude
340 {
341 let root_id = module_id.crate_root(&self.module_tree);
342 let file_id = root_id.source(&self.module_tree).file_id();
343 let crate_graph = self.db.crate_graph();
344 if let Some(crate_id) = crate_graph.crate_id_for_crate_root(file_id.as_original_file())
345 {
346 let krate = Crate::new(crate_id);
347 for dep in krate.dependencies(self.db)? {
348 if let Some(module) = dep.krate.root_module(self.db)? {
349 let def_id = module.def_id;
350 self.add_module_item(
351 &mut module_items,
352 dep.name.clone(),
353 PerNs::types(def_id),
354 );
355 }
356 }
357 };
358 }
359 for import in input.imports.iter() {
360 if let Some(name) = import.path.segments.iter().last() {
361 if let ImportKind::Named(import) = import.kind {
362 module_items.items.insert(
363 name.clone(),
364 Resolution {
365 def_id: PerNs::none(),
366 import: Some(import),
367 },
368 );
369 }
370 }
371 }
372 // Populate explicitly declared items, except modules
373 for item in input.items.iter() {
374 if item.kind == MODULE {
375 continue;
376 }
377 // depending on the item kind, the location can define something in
378 // the values namespace, the types namespace, or both
379 let kind = DefKind::for_syntax_kind(item.kind);
380 let def_id = kind.map(|k| {
381 let def_loc = DefLoc {
382 kind: k,
383 source_root_id: self.source_root,
384 module_id,
385 source_item_id: item.id,
386 };
387 def_loc.id(self.db)
388 });
389 let resolution = Resolution {
390 def_id,
391 import: None,
392 };
393 module_items.items.insert(item.name.clone(), resolution);
394 }
395
396 // Populate modules
397 for (name, module_id) in module_id.children(&self.module_tree) {
398 let def_loc = DefLoc {
399 kind: DefKind::Module,
400 source_root_id: self.source_root,
401 module_id,
402 source_item_id: module_id.source(&self.module_tree).0,
403 };
404 let def_id = def_loc.id(self.db);
405 self.add_module_item(&mut module_items, name, PerNs::types(def_id));
406 }
407
408 self.result.per_module.insert(module_id, module_items);
409 Ok(())
410 }
411
412 fn add_module_item(&self, module_items: &mut ModuleScope, name: Name, def_id: PerNs<DefId>) {
413 let resolution = Resolution {
414 def_id,
415 import: None,
416 };
417 module_items.items.insert(name, resolution);
418 }
419
420 fn resolve_imports(&mut self, module_id: ModuleId) -> Cancelable<()> {
421 for import in self.input[&module_id].imports.iter() {
422 self.resolve_import(module_id, import)?;
423 }
424 Ok(())
425 }
426
427 fn resolve_import(&mut self, module_id: ModuleId, import: &Import) -> Cancelable<()> {
428 let ptr = match import.kind {
429 ImportKind::Glob => return Ok(()),
430 ImportKind::Named(ptr) => ptr,
431 };
432
433 let mut curr: ModuleId = match import.path.kind {
434 PathKind::Plain | PathKind::Self_ => module_id,
435 PathKind::Super => {
436 match module_id.parent(&self.module_tree) {
437 Some(it) => it,
438 // TODO: error
439 None => return Ok(()),
440 }
441 }
442 PathKind::Crate => module_id.crate_root(&self.module_tree),
443 };
444
445 for (i, name) in import.path.segments.iter().enumerate() {
446 let is_last = i == import.path.segments.len() - 1;
447
448 let def_id = match self.result.per_module[&curr].items.get(name) {
449 Some(res) if !res.def_id.is_none() => res.def_id,
450 _ => return Ok(()),
451 };
452
453 if !is_last {
454 let type_def_id = if let Some(d) = def_id.take(Namespace::Types) {
455 d
456 } else {
457 return Ok(());
458 };
459 curr = match type_def_id.loc(self.db) {
460 DefLoc {
461 kind: DefKind::Module,
462 module_id: target_module_id,
463 source_root_id,
464 ..
465 } => {
466 if source_root_id == self.source_root {
467 target_module_id
468 } else {
469 let module = crate::code_model_api::Module::new(type_def_id);
470 let path = Path {
471 segments: import.path.segments[i + 1..].iter().cloned().collect(),
472 kind: PathKind::Crate,
473 };
474 let def_id = module.resolve_path(self.db, &path)?;
475 if !def_id.is_none() {
476 self.update(module_id, |items| {
477 let res = Resolution {
478 def_id: def_id,
479 import: Some(ptr),
480 };
481 items.items.insert(name.clone(), res);
482 })
483 }
484 return Ok(());
485 }
486 }
487 _ => return Ok(()),
488 }
489 } else {
490 self.update(module_id, |items| {
491 let res = Resolution {
492 def_id: def_id,
493 import: Some(ptr),
494 };
495 items.items.insert(name.clone(), res);
496 })
497 }
498 }
499 Ok(())
500 }
501
502 fn update(&mut self, module_id: ModuleId, f: impl FnOnce(&mut ModuleScope)) {
503 let module_items = self.result.per_module.get_mut(&module_id).unwrap();
504 f(module_items)
505 }
506}
507
508#[cfg(test)]
509mod tests;
diff --git a/crates/ra_hir/src/module/nameres/tests.rs b/crates/ra_hir/src/module/nameres/tests.rs
deleted file mode 100644
index dcbe65aec..000000000
--- a/crates/ra_hir/src/module/nameres/tests.rs
+++ /dev/null
@@ -1,273 +0,0 @@
1use std::sync::Arc;
2
3use salsa::Database;
4use ra_db::{FilesDatabase, CrateGraph};
5use relative_path::RelativePath;
6use test_utils::assert_eq_text;
7
8use crate::{
9 self as hir,
10 db::HirDatabase,
11 mock::MockDatabase,
12};
13
14fn item_map(fixture: &str) -> (Arc<hir::ItemMap>, hir::ModuleId) {
15 let (db, pos) = MockDatabase::with_position(fixture);
16 let source_root = db.file_source_root(pos.file_id);
17 let module = hir::source_binder::module_from_position(&db, pos)
18 .unwrap()
19 .unwrap();
20 let module_id = module.def_id.loc(&db).module_id;
21 (db.item_map(source_root).unwrap(), module_id)
22}
23
24fn check_module_item_map(map: &hir::ItemMap, module_id: hir::ModuleId, expected: &str) {
25 let mut lines = map.per_module[&module_id]
26 .items
27 .iter()
28 .map(|(name, res)| format!("{}: {}", name, dump_resolution(res)))
29 .collect::<Vec<_>>();
30 lines.sort();
31 let actual = lines.join("\n");
32 let expected = expected
33 .trim()
34 .lines()
35 .map(|it| it.trim())
36 .collect::<Vec<_>>()
37 .join("\n");
38 assert_eq_text!(&actual, &expected);
39
40 fn dump_resolution(resolution: &hir::Resolution) -> &'static str {
41 match (
42 resolution.def_id.types.is_some(),
43 resolution.def_id.values.is_some(),
44 ) {
45 (true, true) => "t v",
46 (true, false) => "t",
47 (false, true) => "v",
48 (false, false) => "_",
49 }
50 }
51}
52
53#[test]
54fn item_map_smoke_test() {
55 let (item_map, module_id) = item_map(
56 "
57 //- /lib.rs
58 mod foo;
59
60 use crate::foo::bar::Baz;
61 <|>
62
63 //- /foo/mod.rs
64 pub mod bar;
65
66 //- /foo/bar.rs
67 pub struct Baz;
68 ",
69 );
70 check_module_item_map(
71 &item_map,
72 module_id,
73 "
74 Baz: t v
75 foo: t
76 ",
77 );
78}
79
80#[test]
81fn item_map_contains_items_from_expansions() {
82 let (item_map, module_id) = item_map(
83 "
84 //- /lib.rs
85 mod foo;
86
87 use crate::foo::bar::Baz;
88 <|>
89
90 //- /foo/mod.rs
91 pub mod bar;
92
93 //- /foo/bar.rs
94 salsa::query_group! {
95 trait Baz {}
96 }
97 ",
98 );
99 check_module_item_map(
100 &item_map,
101 module_id,
102 "
103 Baz: t
104 foo: t
105 ",
106 );
107}
108
109#[test]
110fn item_map_using_self() {
111 let (item_map, module_id) = item_map(
112 "
113 //- /lib.rs
114 mod foo;
115 use crate::foo::bar::Baz::{self};
116 <|>
117 //- /foo/mod.rs
118 pub mod bar;
119 //- /foo/bar.rs
120 pub struct Baz;
121 ",
122 );
123 check_module_item_map(
124 &item_map,
125 module_id,
126 "
127 Baz: t v
128 foo: t
129 ",
130 );
131}
132
133#[test]
134fn item_map_across_crates() {
135 let (mut db, sr) = MockDatabase::with_files(
136 "
137 //- /main.rs
138 use test_crate::Baz;
139
140 //- /lib.rs
141 pub struct Baz;
142 ",
143 );
144 let main_id = sr.files[RelativePath::new("/main.rs")];
145 let lib_id = sr.files[RelativePath::new("/lib.rs")];
146
147 let mut crate_graph = CrateGraph::default();
148 let main_crate = crate_graph.add_crate_root(main_id);
149 let lib_crate = crate_graph.add_crate_root(lib_id);
150 crate_graph.add_dep(main_crate, "test_crate".into(), lib_crate);
151
152 db.set_crate_graph(crate_graph);
153
154 let source_root = db.file_source_root(main_id);
155 let module = hir::source_binder::module_from_file_id(&db, main_id)
156 .unwrap()
157 .unwrap();
158 let module_id = module.def_id.loc(&db).module_id;
159 let item_map = db.item_map(source_root).unwrap();
160
161 check_module_item_map(
162 &item_map,
163 module_id,
164 "
165 Baz: t v
166 test_crate: t
167 ",
168 );
169}
170
171#[test]
172fn typing_inside_a_function_should_not_invalidate_item_map() {
173 let (mut db, pos) = MockDatabase::with_position(
174 "
175 //- /lib.rs
176 mod foo;
177
178 use crate::foo::bar::Baz;
179
180 //- /foo/mod.rs
181 pub mod bar;
182
183 //- /foo/bar.rs
184 <|>
185 salsa::query_group! {
186 trait Baz {
187 fn foo() -> i32 { 1 + 1 }
188 }
189 }
190 ",
191 );
192 let source_root = db.file_source_root(pos.file_id);
193 {
194 let events = db.log_executed(|| {
195 db.item_map(source_root).unwrap();
196 });
197 assert!(format!("{:?}", events).contains("item_map"))
198 }
199
200 let new_text = "
201 salsa::query_group! {
202 trait Baz {
203 fn foo() -> i32 { 92 }
204 }
205 }
206 "
207 .to_string();
208
209 db.query_mut(ra_db::FileTextQuery)
210 .set(pos.file_id, Arc::new(new_text));
211
212 {
213 let events = db.log_executed(|| {
214 db.item_map(source_root).unwrap();
215 });
216 assert!(
217 !format!("{:?}", events).contains("item_map"),
218 "{:#?}",
219 events
220 )
221 }
222}
223
224#[test]
225fn typing_inside_a_function_inside_a_macro_should_not_invalidate_item_map() {
226 let (mut db, pos) = MockDatabase::with_position(
227 "
228 //- /lib.rs
229 mod foo;<|>
230
231 use crate::foo::bar::Baz;
232
233 fn foo() -> i32 {
234 1 + 1
235 }
236 //- /foo/mod.rs
237 pub mod bar;
238
239 //- /foo/bar.rs
240 pub struct Baz;
241 ",
242 );
243 let source_root = db.file_source_root(pos.file_id);
244 {
245 let events = db.log_executed(|| {
246 db.item_map(source_root).unwrap();
247 });
248 assert!(format!("{:?}", events).contains("item_map"))
249 }
250
251 let new_text = "
252 mod foo;
253
254 use crate::foo::bar::Baz;
255
256 fn foo() -> i32 { 92 }
257 "
258 .to_string();
259
260 db.query_mut(ra_db::FileTextQuery)
261 .set(pos.file_id, Arc::new(new_text));
262
263 {
264 let events = db.log_executed(|| {
265 db.item_map(source_root).unwrap();
266 });
267 assert!(
268 !format!("{:?}", events).contains("item_map"),
269 "{:#?}",
270 events
271 )
272 }
273}