aboutsummaryrefslogtreecommitdiff
path: root/crates/ra_hir_def
diff options
context:
space:
mode:
Diffstat (limited to 'crates/ra_hir_def')
-rw-r--r--crates/ra_hir_def/src/db.rs18
-rw-r--r--crates/ra_hir_def/src/find_path.rs202
-rw-r--r--crates/ra_hir_def/src/import_map.rs331
-rw-r--r--crates/ra_hir_def/src/item_scope.rs23
-rw-r--r--crates/ra_hir_def/src/lib.rs1
-rw-r--r--crates/ra_hir_def/src/nameres/mod_resolution.rs2
-rw-r--r--crates/ra_hir_def/src/path.rs15
-rw-r--r--crates/ra_hir_def/src/per_ns.rs10
-rw-r--r--crates/ra_hir_def/src/test_db.rs20
9 files changed, 503 insertions, 119 deletions
diff --git a/crates/ra_hir_def/src/db.rs b/crates/ra_hir_def/src/db.rs
index 945a0025e..10cc26480 100644
--- a/crates/ra_hir_def/src/db.rs
+++ b/crates/ra_hir_def/src/db.rs
@@ -1,7 +1,7 @@
1//! Defines database & queries for name resolution. 1//! Defines database & queries for name resolution.
2use std::sync::Arc; 2use std::sync::Arc;
3 3
4use hir_expand::{db::AstDatabase, name::Name, HirFileId}; 4use hir_expand::{db::AstDatabase, HirFileId};
5use ra_db::{salsa, CrateId, SourceDatabase, Upcast}; 5use ra_db::{salsa, CrateId, SourceDatabase, Upcast};
6use ra_prof::profile; 6use ra_prof::profile;
7use ra_syntax::SmolStr; 7use ra_syntax::SmolStr;
@@ -12,13 +12,10 @@ use crate::{
12 body::{scope::ExprScopes, Body, BodySourceMap}, 12 body::{scope::ExprScopes, Body, BodySourceMap},
13 data::{ConstData, FunctionData, ImplData, StaticData, TraitData, TypeAliasData}, 13 data::{ConstData, FunctionData, ImplData, StaticData, TraitData, TypeAliasData},
14 docs::Documentation, 14 docs::Documentation,
15 find_path,
16 generics::GenericParams, 15 generics::GenericParams,
17 item_scope::ItemInNs, 16 import_map::ImportMap,
18 lang_item::{LangItemTarget, LangItems}, 17 lang_item::{LangItemTarget, LangItems},
19 nameres::{raw::RawItems, CrateDefMap}, 18 nameres::{raw::RawItems, CrateDefMap},
20 path::ModPath,
21 visibility::Visibility,
22 AttrDefId, ConstId, ConstLoc, DefWithBodyId, EnumId, EnumLoc, FunctionId, FunctionLoc, 19 AttrDefId, ConstId, ConstLoc, DefWithBodyId, EnumId, EnumLoc, FunctionId, FunctionLoc,
23 GenericDefId, ImplId, ImplLoc, ModuleId, StaticId, StaticLoc, StructId, StructLoc, TraitId, 20 GenericDefId, ImplId, ImplLoc, ModuleId, StaticId, StaticLoc, StructId, StructLoc, TraitId,
24 TraitLoc, TypeAliasId, TypeAliasLoc, UnionId, UnionLoc, 21 TraitLoc, TypeAliasId, TypeAliasLoc, UnionId, UnionLoc,
@@ -113,15 +110,8 @@ pub trait DefDatabase: InternDatabase + AstDatabase + Upcast<dyn AstDatabase> {
113 #[salsa::invoke(Documentation::documentation_query)] 110 #[salsa::invoke(Documentation::documentation_query)]
114 fn documentation(&self, def: AttrDefId) -> Option<Documentation>; 111 fn documentation(&self, def: AttrDefId) -> Option<Documentation>;
115 112
116 #[salsa::invoke(find_path::importable_locations_of_query)] 113 #[salsa::invoke(ImportMap::import_map_query)]
117 fn importable_locations_of( 114 fn import_map(&self, krate: CrateId) -> Arc<ImportMap>;
118 &self,
119 item: ItemInNs,
120 krate: CrateId,
121 ) -> Arc<[(ModuleId, Name, Visibility)]>;
122
123 #[salsa::invoke(find_path::find_path_inner_query)]
124 fn find_path_inner(&self, item: ItemInNs, from: ModuleId, max_len: usize) -> Option<ModPath>;
125} 115}
126 116
127fn crate_def_map_wait(db: &impl DefDatabase, krate: CrateId) -> Arc<CrateDefMap> { 117fn crate_def_map_wait(db: &impl DefDatabase, krate: CrateId) -> Arc<CrateDefMap> {
diff --git a/crates/ra_hir_def/src/find_path.rs b/crates/ra_hir_def/src/find_path.rs
index 4db798473..a7f59e028 100644
--- a/crates/ra_hir_def/src/find_path.rs
+++ b/crates/ra_hir_def/src/find_path.rs
@@ -1,9 +1,8 @@
1//! An algorithm to find a path to refer to a certain item. 1//! An algorithm to find a path to refer to a certain item.
2 2
3use std::sync::Arc;
4
5use hir_expand::name::{known, AsName, Name}; 3use hir_expand::name::{known, AsName, Name};
6use ra_prof::profile; 4use ra_prof::profile;
5use rustc_hash::FxHashSet;
7use test_utils::mark; 6use test_utils::mark;
8 7
9use crate::{ 8use crate::{
@@ -11,7 +10,7 @@ use crate::{
11 item_scope::ItemInNs, 10 item_scope::ItemInNs,
12 path::{ModPath, PathKind}, 11 path::{ModPath, PathKind},
13 visibility::Visibility, 12 visibility::Visibility,
14 CrateId, ModuleDefId, ModuleId, 13 ModuleDefId, ModuleId,
15}; 14};
16 15
17// FIXME: handle local items 16// FIXME: handle local items
@@ -20,7 +19,7 @@ use crate::{
20/// *from where* you're referring to the item, hence the `from` parameter. 19/// *from where* you're referring to the item, hence the `from` parameter.
21pub fn find_path(db: &dyn DefDatabase, item: ItemInNs, from: ModuleId) -> Option<ModPath> { 20pub fn find_path(db: &dyn DefDatabase, item: ItemInNs, from: ModuleId) -> Option<ModPath> {
22 let _p = profile("find_path"); 21 let _p = profile("find_path");
23 db.find_path_inner(item, from, MAX_PATH_LEN) 22 find_path_inner(db, item, from, MAX_PATH_LEN)
24} 23}
25 24
26const MAX_PATH_LEN: usize = 15; 25const MAX_PATH_LEN: usize = 15;
@@ -36,20 +35,9 @@ impl ModPath {
36 let first_segment = self.segments.first(); 35 let first_segment = self.segments.first();
37 first_segment == Some(&known::alloc) || first_segment == Some(&known::core) 36 first_segment == Some(&known::alloc) || first_segment == Some(&known::core)
38 } 37 }
39
40 fn len(&self) -> usize {
41 self.segments.len()
42 + match self.kind {
43 PathKind::Plain => 0,
44 PathKind::Super(i) => i as usize,
45 PathKind::Crate => 1,
46 PathKind::Abs => 0,
47 PathKind::DollarCrate(_) => 1,
48 }
49 }
50} 38}
51 39
52pub(crate) fn find_path_inner_query( 40fn find_path_inner(
53 db: &dyn DefDatabase, 41 db: &dyn DefDatabase,
54 item: ItemInNs, 42 item: ItemInNs,
55 from: ModuleId, 43 from: ModuleId,
@@ -133,31 +121,61 @@ pub(crate) fn find_path_inner_query(
133 } 121 }
134 122
135 // - otherwise, look for modules containing (reexporting) it and import it from one of those 123 // - otherwise, look for modules containing (reexporting) it and import it from one of those
124
136 let crate_root = ModuleId { local_id: def_map.root, krate: from.krate }; 125 let crate_root = ModuleId { local_id: def_map.root, krate: from.krate };
137 let crate_attrs = db.attrs(crate_root.into()); 126 let crate_attrs = db.attrs(crate_root.into());
138 let prefer_no_std = crate_attrs.by_key("no_std").exists(); 127 let prefer_no_std = crate_attrs.by_key("no_std").exists();
139 let importable_locations = find_importable_locations(db, item, from);
140 let mut best_path = None; 128 let mut best_path = None;
141 let mut best_path_len = max_len; 129 let mut best_path_len = max_len;
142 for (module_id, name) in importable_locations {
143 let mut path = match db.find_path_inner(
144 ItemInNs::Types(ModuleDefId::ModuleId(module_id)),
145 from,
146 best_path_len - 1,
147 ) {
148 None => continue,
149 Some(path) => path,
150 };
151 path.segments.push(name);
152 130
153 let new_path = if let Some(best_path) = best_path { 131 if item.krate(db) == Some(from.krate) {
154 select_best_path(best_path, path, prefer_no_std) 132 // Item was defined in the same crate that wants to import it. It cannot be found in any
155 } else { 133 // dependency in this case.
156 path 134
157 }; 135 let local_imports = find_local_import_locations(db, item, from);
158 best_path_len = new_path.len(); 136 for (module_id, name) in local_imports {
159 best_path = Some(new_path); 137 if let Some(mut path) = find_path_inner(
138 db,
139 ItemInNs::Types(ModuleDefId::ModuleId(module_id)),
140 from,
141 best_path_len - 1,
142 ) {
143 path.segments.push(name);
144
145 let new_path = if let Some(best_path) = best_path {
146 select_best_path(best_path, path, prefer_no_std)
147 } else {
148 path
149 };
150 best_path_len = new_path.len();
151 best_path = Some(new_path);
152 }
153 }
154 } else {
155 // Item was defined in some upstream crate. This means that it must be exported from one,
156 // too (unless we can't name it at all). It could *also* be (re)exported by the same crate
157 // that wants to import it here, but we always prefer to use the external path here.
158
159 let crate_graph = db.crate_graph();
160 let extern_paths = crate_graph[from.krate].dependencies.iter().filter_map(|dep| {
161 let import_map = db.import_map(dep.crate_id);
162 import_map.path_of(item).map(|modpath| {
163 let mut modpath = modpath.clone();
164 modpath.segments.insert(0, dep.as_name());
165 modpath
166 })
167 });
168
169 for path in extern_paths {
170 let new_path = if let Some(best_path) = best_path {
171 select_best_path(best_path, path, prefer_no_std)
172 } else {
173 path
174 };
175 best_path = Some(new_path);
176 }
160 } 177 }
178
161 best_path 179 best_path
162} 180}
163 181
@@ -185,69 +203,86 @@ fn select_best_path(old_path: ModPath, new_path: ModPath, prefer_no_std: bool) -
185 } 203 }
186} 204}
187 205
188fn find_importable_locations( 206/// Finds locations in `from.krate` from which `item` can be imported by `from`.
207fn find_local_import_locations(
189 db: &dyn DefDatabase, 208 db: &dyn DefDatabase,
190 item: ItemInNs, 209 item: ItemInNs,
191 from: ModuleId, 210 from: ModuleId,
192) -> Vec<(ModuleId, Name)> { 211) -> Vec<(ModuleId, Name)> {
193 let crate_graph = db.crate_graph(); 212 let _p = profile("find_local_import_locations");
194 let mut result = Vec::new(); 213
195 // We only look in the crate from which we are importing, and the direct 214 // `from` can import anything below `from` with visibility of at least `from`, and anything
196 // dependencies. We cannot refer to names from transitive dependencies 215 // above `from` with any visibility. That means we do not need to descend into private siblings
197 // directly (only through reexports in direct dependencies). 216 // of `from` (and similar).
198 for krate in Some(from.krate) 217
199 .into_iter() 218 let def_map = db.crate_def_map(from.krate);
200 .chain(crate_graph[from.krate].dependencies.iter().map(|dep| dep.crate_id)) 219
201 { 220 // Compute the initial worklist. We start with all direct child modules of `from` as well as all
202 result.extend( 221 // of its (recursive) parent modules.
203 db.importable_locations_of(item, krate) 222 let data = &def_map.modules[from.local_id];
204 .iter() 223 let mut worklist = data
205 .filter(|(_, _, vis)| vis.is_visible_from(db, from)) 224 .children
206 .map(|(m, n, _)| (*m, n.clone())), 225 .values()
207 ); 226 .map(|child| ModuleId { krate: from.krate, local_id: *child })
208 } 227 .collect::<Vec<_>>();
209 result 228 let mut parent = data.parent;
210} 229 while let Some(p) = parent {
230 worklist.push(ModuleId { krate: from.krate, local_id: p });
231 parent = def_map.modules[p].parent;
232 }
233
234 let mut seen: FxHashSet<_> = FxHashSet::default();
235
236 let mut locations = Vec::new();
237 while let Some(module) = worklist.pop() {
238 if !seen.insert(module) {
239 continue; // already processed this module
240 }
241
242 let ext_def_map;
243 let data = if module.krate == from.krate {
244 &def_map[module.local_id]
245 } else {
246 // The crate might reexport a module defined in another crate.
247 ext_def_map = db.crate_def_map(module.krate);
248 &ext_def_map[module.local_id]
249 };
211 250
212/// Collects all locations from which we might import the item in a particular
213/// crate. These include the original definition of the item, and any
214/// non-private `use`s.
215///
216/// Note that the crate doesn't need to be the one in which the item is defined;
217/// it might be re-exported in other crates.
218pub(crate) fn importable_locations_of_query(
219 db: &dyn DefDatabase,
220 item: ItemInNs,
221 krate: CrateId,
222) -> Arc<[(ModuleId, Name, Visibility)]> {
223 let _p = profile("importable_locations_of_query");
224 let def_map = db.crate_def_map(krate);
225 let mut result = Vec::new();
226 for (local_id, data) in def_map.modules.iter() {
227 if let Some((name, vis)) = data.scope.name_of(item) { 251 if let Some((name, vis)) = data.scope.name_of(item) {
228 let is_private = if let Visibility::Module(private_to) = vis { 252 if vis.is_visible_from(db, from) {
229 private_to.local_id == local_id 253 let is_private = if let Visibility::Module(private_to) = vis {
230 } else { 254 private_to.local_id == module.local_id
231 false 255 } else {
232 }; 256 false
233 let is_original_def = if let Some(module_def_id) = item.as_module_def_id() { 257 };
234 data.scope.declarations().any(|it| it == module_def_id) 258 let is_original_def = if let Some(module_def_id) = item.as_module_def_id() {
235 } else { 259 data.scope.declarations().any(|it| it == module_def_id)
236 false 260 } else {
237 }; 261 false
238 if is_private && !is_original_def { 262 };
263
239 // Ignore private imports. these could be used if we are 264 // Ignore private imports. these could be used if we are
240 // in a submodule of this module, but that's usually not 265 // in a submodule of this module, but that's usually not
241 // what the user wants; and if this module can import 266 // what the user wants; and if this module can import
242 // the item and we're a submodule of it, so can we. 267 // the item and we're a submodule of it, so can we.
243 // Also this keeps the cached data smaller. 268 // Also this keeps the cached data smaller.
244 continue; 269 if !is_private || is_original_def {
270 locations.push((module, name.clone()));
271 }
272 }
273 }
274
275 // Descend into all modules visible from `from`.
276 for (_, per_ns) in data.scope.entries() {
277 if let Some((ModuleDefId::ModuleId(module), vis)) = per_ns.take_types_vis() {
278 if vis.is_visible_from(db, from) {
279 worklist.push(module);
280 }
245 } 281 }
246 result.push((ModuleId { krate, local_id }, name.clone(), vis));
247 } 282 }
248 } 283 }
249 284
250 Arc::from(result) 285 locations
251} 286}
252 287
253#[cfg(test)] 288#[cfg(test)]
@@ -385,6 +420,7 @@ mod tests {
385 420
386 #[test] 421 #[test]
387 fn different_crate_renamed() { 422 fn different_crate_renamed() {
423 // Even if a local path exists, if the item is defined externally, prefer an external path.
388 let code = r#" 424 let code = r#"
389 //- /main.rs crate:main deps:std 425 //- /main.rs crate:main deps:std
390 extern crate std as std_renamed; 426 extern crate std as std_renamed;
@@ -392,7 +428,7 @@ mod tests {
392 //- /std.rs crate:std 428 //- /std.rs crate:std
393 pub struct S; 429 pub struct S;
394 "#; 430 "#;
395 check_found_path(code, "std_renamed::S"); 431 check_found_path(code, "std::S");
396 } 432 }
397 433
398 #[test] 434 #[test]
diff --git a/crates/ra_hir_def/src/import_map.rs b/crates/ra_hir_def/src/import_map.rs
new file mode 100644
index 000000000..4284a0a91
--- /dev/null
+++ b/crates/ra_hir_def/src/import_map.rs
@@ -0,0 +1,331 @@
1//! A map of all publicly exported items in a crate.
2
3use std::{collections::hash_map::Entry, fmt, sync::Arc};
4
5use ra_db::CrateId;
6use rustc_hash::FxHashMap;
7
8use crate::{
9 db::DefDatabase,
10 item_scope::ItemInNs,
11 path::{ModPath, PathKind},
12 visibility::Visibility,
13 ModuleDefId, ModuleId,
14};
15
16/// A map from publicly exported items to the path needed to import/name them from a downstream
17/// crate.
18///
19/// Reexports of items are taken into account, ie. if something is exported under multiple
20/// names, the one with the shortest import path will be used.
21///
22/// Note that all paths are relative to the containing crate's root, so the crate name still needs
23/// to be prepended to the `ModPath` before the path is valid.
24#[derive(Eq, PartialEq)]
25pub struct ImportMap {
26 map: FxHashMap<ItemInNs, ModPath>,
27}
28
29impl ImportMap {
30 pub fn import_map_query(db: &dyn DefDatabase, krate: CrateId) -> Arc<Self> {
31 let _p = ra_prof::profile("import_map_query");
32 let def_map = db.crate_def_map(krate);
33 let mut import_map = FxHashMap::with_capacity_and_hasher(64, Default::default());
34
35 // We look only into modules that are public(ly reexported), starting with the crate root.
36 let empty = ModPath { kind: PathKind::Plain, segments: vec![] };
37 let root = ModuleId { krate, local_id: def_map.root };
38 let mut worklist = vec![(root, empty)];
39 while let Some((module, mod_path)) = worklist.pop() {
40 let ext_def_map;
41 let mod_data = if module.krate == krate {
42 &def_map[module.local_id]
43 } else {
44 // The crate might reexport a module defined in another crate.
45 ext_def_map = db.crate_def_map(module.krate);
46 &ext_def_map[module.local_id]
47 };
48
49 let visible_items = mod_data.scope.entries().filter_map(|(name, per_ns)| {
50 let per_ns = per_ns.filter_visibility(|vis| vis == Visibility::Public);
51 if per_ns.is_none() {
52 None
53 } else {
54 Some((name, per_ns))
55 }
56 });
57
58 for (name, per_ns) in visible_items {
59 let mk_path = || {
60 let mut path = mod_path.clone();
61 path.segments.push(name.clone());
62 path
63 };
64
65 for item in per_ns.iter_items() {
66 let path = mk_path();
67 match import_map.entry(item) {
68 Entry::Vacant(entry) => {
69 entry.insert(path);
70 }
71 Entry::Occupied(mut entry) => {
72 // If the new path is shorter, prefer that one.
73 if path.len() < entry.get().len() {
74 *entry.get_mut() = path;
75 } else {
76 continue;
77 }
78 }
79 }
80
81 // If we've just added a path to a module, descend into it. We might traverse
82 // modules multiple times, but only if the new path to it is shorter than the
83 // first (else we `continue` above).
84 if let Some(ModuleDefId::ModuleId(mod_id)) = item.as_module_def_id() {
85 worklist.push((mod_id, mk_path()));
86 }
87 }
88 }
89 }
90
91 Arc::new(Self { map: import_map })
92 }
93
94 /// Returns the `ModPath` needed to import/mention `item`, relative to this crate's root.
95 pub fn path_of(&self, item: ItemInNs) -> Option<&ModPath> {
96 self.map.get(&item)
97 }
98}
99
100impl fmt::Debug for ImportMap {
101 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
102 let mut importable_paths: Vec<_> = self
103 .map
104 .iter()
105 .map(|(item, modpath)| {
106 let ns = match item {
107 ItemInNs::Types(_) => "t",
108 ItemInNs::Values(_) => "v",
109 ItemInNs::Macros(_) => "m",
110 };
111 format!("- {} ({})", modpath, ns)
112 })
113 .collect();
114
115 importable_paths.sort();
116 f.write_str(&importable_paths.join("\n"))
117 }
118}
119
120#[cfg(test)]
121mod tests {
122 use super::*;
123 use crate::test_db::TestDB;
124 use insta::assert_snapshot;
125 use ra_db::fixture::WithFixture;
126 use ra_db::SourceDatabase;
127
128 fn import_map(ra_fixture: &str) -> String {
129 let db = TestDB::with_files(ra_fixture);
130 let crate_graph = db.crate_graph();
131
132 let import_maps: Vec<_> = crate_graph
133 .iter()
134 .filter_map(|krate| {
135 let cdata = &crate_graph[krate];
136 let name = cdata.display_name.as_ref()?;
137
138 let map = db.import_map(krate);
139
140 Some(format!("{}:\n{:?}", name, map))
141 })
142 .collect();
143
144 import_maps.join("\n")
145 }
146
147 #[test]
148 fn smoke() {
149 let map = import_map(
150 r"
151 //- /main.rs crate:main deps:lib
152
153 mod private {
154 pub use lib::Pub;
155 pub struct InPrivateModule;
156 }
157
158 pub mod publ1 {
159 use lib::Pub;
160 }
161
162 pub mod real_pub {
163 pub use lib::Pub;
164 }
165 pub mod real_pu2 { // same path length as above
166 pub use lib::Pub;
167 }
168
169 //- /lib.rs crate:lib
170 pub struct Pub {}
171 pub struct Pub2; // t + v
172 struct Priv;
173 ",
174 );
175
176 assert_snapshot!(map, @r###"
177 main:
178 - publ1 (t)
179 - real_pu2 (t)
180 - real_pub (t)
181 - real_pub::Pub (t)
182 lib:
183 - Pub (t)
184 - Pub2 (t)
185 - Pub2 (v)
186 "###);
187 }
188
189 #[test]
190 fn prefers_shortest_path() {
191 let map = import_map(
192 r"
193 //- /main.rs crate:main
194
195 pub mod sub {
196 pub mod subsub {
197 pub struct Def {}
198 }
199
200 pub use super::sub::subsub::Def;
201 }
202 ",
203 );
204
205 assert_snapshot!(map, @r###"
206 main:
207 - sub (t)
208 - sub::Def (t)
209 - sub::subsub (t)
210 "###);
211 }
212
213 #[test]
214 fn type_reexport_cross_crate() {
215 // Reexports need to be visible from a crate, even if the original crate exports the item
216 // at a shorter path.
217 let map = import_map(
218 r"
219 //- /main.rs crate:main deps:lib
220 pub mod m {
221 pub use lib::S;
222 }
223 //- /lib.rs crate:lib
224 pub struct S;
225 ",
226 );
227
228 assert_snapshot!(map, @r###"
229 main:
230 - m (t)
231 - m::S (t)
232 - m::S (v)
233 lib:
234 - S (t)
235 - S (v)
236 "###);
237 }
238
239 #[test]
240 fn macro_reexport() {
241 let map = import_map(
242 r"
243 //- /main.rs crate:main deps:lib
244 pub mod m {
245 pub use lib::pub_macro;
246 }
247 //- /lib.rs crate:lib
248 #[macro_export]
249 macro_rules! pub_macro {
250 () => {};
251 }
252 ",
253 );
254
255 assert_snapshot!(map, @r###"
256 main:
257 - m (t)
258 - m::pub_macro (m)
259 lib:
260 - pub_macro (m)
261 "###);
262 }
263
264 #[test]
265 fn module_reexport() {
266 // Reexporting modules from a dependency adds all contents to the import map.
267 let map = import_map(
268 r"
269 //- /main.rs crate:main deps:lib
270 pub use lib::module as reexported_module;
271 //- /lib.rs crate:lib
272 pub mod module {
273 pub struct S;
274 }
275 ",
276 );
277
278 assert_snapshot!(map, @r###"
279 main:
280 - reexported_module (t)
281 - reexported_module::S (t)
282 - reexported_module::S (v)
283 lib:
284 - module (t)
285 - module::S (t)
286 - module::S (v)
287 "###);
288 }
289
290 #[test]
291 fn cyclic_module_reexport() {
292 // A cyclic reexport does not hang.
293 let map = import_map(
294 r"
295 //- /lib.rs crate:lib
296 pub mod module {
297 pub struct S;
298 pub use super::sub::*;
299 }
300
301 pub mod sub {
302 pub use super::module;
303 }
304 ",
305 );
306
307 assert_snapshot!(map, @r###"
308 lib:
309 - module (t)
310 - module::S (t)
311 - module::S (v)
312 - sub (t)
313 "###);
314 }
315
316 #[test]
317 fn private_macro() {
318 let map = import_map(
319 r"
320 //- /lib.rs crate:lib
321 macro_rules! private_macro {
322 () => {};
323 }
324 ",
325 );
326
327 assert_snapshot!(map, @r###"
328 lib:
329 "###);
330 }
331}
diff --git a/crates/ra_hir_def/src/item_scope.rs b/crates/ra_hir_def/src/item_scope.rs
index fc15948ad..b03ba939a 100644
--- a/crates/ra_hir_def/src/item_scope.rs
+++ b/crates/ra_hir_def/src/item_scope.rs
@@ -3,11 +3,12 @@
3 3
4use hir_expand::name::Name; 4use hir_expand::name::Name;
5use once_cell::sync::Lazy; 5use once_cell::sync::Lazy;
6use ra_db::CrateId;
6use rustc_hash::FxHashMap; 7use rustc_hash::FxHashMap;
7 8
8use crate::{ 9use crate::{
9 per_ns::PerNs, visibility::Visibility, AdtId, BuiltinType, ImplId, MacroDefId, ModuleDefId, 10 db::DefDatabase, per_ns::PerNs, visibility::Visibility, AdtId, BuiltinType, HasModule, ImplId,
10 TraitId, 11 Lookup, MacroDefId, ModuleDefId, TraitId,
11}; 12};
12 13
13#[derive(Debug, Default, PartialEq, Eq)] 14#[derive(Debug, Default, PartialEq, Eq)]
@@ -203,4 +204,22 @@ impl ItemInNs {
203 ItemInNs::Macros(_) => None, 204 ItemInNs::Macros(_) => None,
204 } 205 }
205 } 206 }
207
208 /// Returns the crate defining this item (or `None` if `self` is built-in).
209 pub fn krate(&self, db: &dyn DefDatabase) -> Option<CrateId> {
210 Some(match self {
211 ItemInNs::Types(did) | ItemInNs::Values(did) => match did {
212 ModuleDefId::ModuleId(id) => id.krate,
213 ModuleDefId::FunctionId(id) => id.lookup(db).module(db).krate,
214 ModuleDefId::AdtId(id) => id.module(db).krate,
215 ModuleDefId::EnumVariantId(id) => id.parent.lookup(db).container.module(db).krate,
216 ModuleDefId::ConstId(id) => id.lookup(db).container.module(db).krate,
217 ModuleDefId::StaticId(id) => id.lookup(db).container.module(db).krate,
218 ModuleDefId::TraitId(id) => id.lookup(db).container.module(db).krate,
219 ModuleDefId::TypeAliasId(id) => id.lookup(db).module(db).krate,
220 ModuleDefId::BuiltinType(_) => return None,
221 },
222 ItemInNs::Macros(id) => return id.krate,
223 })
224 }
206} 225}
diff --git a/crates/ra_hir_def/src/lib.rs b/crates/ra_hir_def/src/lib.rs
index 5325a2760..de490fcc5 100644
--- a/crates/ra_hir_def/src/lib.rs
+++ b/crates/ra_hir_def/src/lib.rs
@@ -43,6 +43,7 @@ pub mod child_by_source;
43 43
44pub mod visibility; 44pub mod visibility;
45pub mod find_path; 45pub mod find_path;
46pub mod import_map;
46 47
47#[cfg(test)] 48#[cfg(test)]
48mod test_db; 49mod test_db;
diff --git a/crates/ra_hir_def/src/nameres/mod_resolution.rs b/crates/ra_hir_def/src/nameres/mod_resolution.rs
index 386c5cade..cede4a6fc 100644
--- a/crates/ra_hir_def/src/nameres/mod_resolution.rs
+++ b/crates/ra_hir_def/src/nameres/mod_resolution.rs
@@ -61,7 +61,7 @@ impl ModDir {
61 }; 61 };
62 62
63 for candidate in candidate_files.iter() { 63 for candidate in candidate_files.iter() {
64 if let Some(file_id) = db.resolve_relative_path(file_id, candidate) { 64 if let Some(file_id) = db.resolve_path(file_id, candidate.as_str()) {
65 let mut root_non_dir_owner = false; 65 let mut root_non_dir_owner = false;
66 let mut mod_path = RelativePathBuf::new(); 66 let mut mod_path = RelativePathBuf::new();
67 if !(candidate.ends_with("mod.rs") || attr_path.is_some()) { 67 if !(candidate.ends_with("mod.rs") || attr_path.is_some()) {
diff --git a/crates/ra_hir_def/src/path.rs b/crates/ra_hir_def/src/path.rs
index e84efe2ab..bfa921de2 100644
--- a/crates/ra_hir_def/src/path.rs
+++ b/crates/ra_hir_def/src/path.rs
@@ -76,6 +76,19 @@ impl ModPath {
76 } 76 }
77 } 77 }
78 78
79 /// Returns the number of segments in the path (counting special segments like `$crate` and
80 /// `super`).
81 pub fn len(&self) -> usize {
82 self.segments.len()
83 + match self.kind {
84 PathKind::Plain => 0,
85 PathKind::Super(i) => i as usize,
86 PathKind::Crate => 1,
87 PathKind::Abs => 0,
88 PathKind::DollarCrate(_) => 1,
89 }
90 }
91
79 pub fn is_ident(&self) -> bool { 92 pub fn is_ident(&self) -> bool {
80 self.kind == PathKind::Plain && self.segments.len() == 1 93 self.kind == PathKind::Plain && self.segments.len() == 1
81 } 94 }
@@ -273,7 +286,7 @@ impl From<Name> for ModPath {
273impl Display for ModPath { 286impl Display for ModPath {
274 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 287 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
275 let mut first_segment = true; 288 let mut first_segment = true;
276 let mut add_segment = |s| { 289 let mut add_segment = |s| -> fmt::Result {
277 if !first_segment { 290 if !first_segment {
278 f.write_str("::")?; 291 f.write_str("::")?;
279 } 292 }
diff --git a/crates/ra_hir_def/src/per_ns.rs b/crates/ra_hir_def/src/per_ns.rs
index 6e435c8c1..74665c588 100644
--- a/crates/ra_hir_def/src/per_ns.rs
+++ b/crates/ra_hir_def/src/per_ns.rs
@@ -5,7 +5,7 @@
5 5
6use hir_expand::MacroDefId; 6use hir_expand::MacroDefId;
7 7
8use crate::{visibility::Visibility, ModuleDefId}; 8use crate::{item_scope::ItemInNs, visibility::Visibility, ModuleDefId};
9 9
10#[derive(Debug, Copy, Clone, PartialEq, Eq)] 10#[derive(Debug, Copy, Clone, PartialEq, Eq)]
11pub struct PerNs { 11pub struct PerNs {
@@ -84,4 +84,12 @@ impl PerNs {
84 macros: self.macros.or(other.macros), 84 macros: self.macros.or(other.macros),
85 } 85 }
86 } 86 }
87
88 pub fn iter_items(self) -> impl Iterator<Item = ItemInNs> {
89 self.types
90 .map(|it| ItemInNs::Types(it.0))
91 .into_iter()
92 .chain(self.values.map(|it| ItemInNs::Values(it.0)).into_iter())
93 .chain(self.macros.map(|it| ItemInNs::Macros(it.0)).into_iter())
94 }
87} 95}
diff --git a/crates/ra_hir_def/src/test_db.rs b/crates/ra_hir_def/src/test_db.rs
index eb83dee79..bcfa66ac9 100644
--- a/crates/ra_hir_def/src/test_db.rs
+++ b/crates/ra_hir_def/src/test_db.rs
@@ -6,9 +6,7 @@ use std::{
6}; 6};
7 7
8use hir_expand::db::AstDatabase; 8use hir_expand::db::AstDatabase;
9use ra_db::{ 9use ra_db::{salsa, CrateId, FileId, FileLoader, FileLoaderDelegate, Upcast};
10 salsa, CrateId, ExternSourceId, FileId, FileLoader, FileLoaderDelegate, RelativePath, Upcast,
11};
12 10
13use crate::db::DefDatabase; 11use crate::db::DefDatabase;
14 12
@@ -58,24 +56,12 @@ impl FileLoader for TestDB {
58 fn file_text(&self, file_id: FileId) -> Arc<String> { 56 fn file_text(&self, file_id: FileId) -> Arc<String> {
59 FileLoaderDelegate(self).file_text(file_id) 57 FileLoaderDelegate(self).file_text(file_id)
60 } 58 }
61 fn resolve_relative_path( 59 fn resolve_path(&self, anchor: FileId, path: &str) -> Option<FileId> {
62 &self, 60 FileLoaderDelegate(self).resolve_path(anchor, path)
63 anchor: FileId,
64 relative_path: &RelativePath,
65 ) -> Option<FileId> {
66 FileLoaderDelegate(self).resolve_relative_path(anchor, relative_path)
67 } 61 }
68 fn relevant_crates(&self, file_id: FileId) -> Arc<Vec<CrateId>> { 62 fn relevant_crates(&self, file_id: FileId) -> Arc<Vec<CrateId>> {
69 FileLoaderDelegate(self).relevant_crates(file_id) 63 FileLoaderDelegate(self).relevant_crates(file_id)
70 } 64 }
71
72 fn resolve_extern_path(
73 &self,
74 extern_id: ExternSourceId,
75 relative_path: &RelativePath,
76 ) -> Option<FileId> {
77 FileLoaderDelegate(self).resolve_extern_path(extern_id, relative_path)
78 }
79} 65}
80 66
81impl TestDB { 67impl TestDB {