diff options
Diffstat (limited to 'crates/ra_hir_def/src')
-rw-r--r-- | crates/ra_hir_def/src/body.rs | 2 | ||||
-rw-r--r-- | crates/ra_hir_def/src/data.rs | 2 | ||||
-rw-r--r-- | crates/ra_hir_def/src/diagnostics.rs | 3 | ||||
-rw-r--r-- | crates/ra_hir_def/src/find_path.rs | 59 | ||||
-rw-r--r-- | crates/ra_hir_def/src/import_map.rs | 378 | ||||
-rw-r--r-- | crates/ra_hir_def/src/lib.rs | 11 | ||||
-rw-r--r-- | crates/ra_hir_def/src/nameres.rs | 3 | ||||
-rw-r--r-- | crates/ra_hir_def/src/nameres/collector.rs | 39 | ||||
-rw-r--r-- | crates/ra_hir_def/src/nameres/mod_resolution.rs | 8 | ||||
-rw-r--r-- | crates/ra_hir_def/src/path.rs | 22 | ||||
-rw-r--r-- | crates/ra_hir_def/src/test_db.rs | 3 |
11 files changed, 465 insertions, 65 deletions
diff --git a/crates/ra_hir_def/src/body.rs b/crates/ra_hir_def/src/body.rs index 273036cee..4f2350915 100644 --- a/crates/ra_hir_def/src/body.rs +++ b/crates/ra_hir_def/src/body.rs | |||
@@ -97,7 +97,7 @@ impl Expander { | |||
97 | 97 | ||
98 | let macro_call = InFile::new(self.current_file_id, ¯o_call); | 98 | let macro_call = InFile::new(self.current_file_id, ¯o_call); |
99 | 99 | ||
100 | if let Some(call_id) = macro_call.as_call_id(db, |path| { | 100 | if let Some(call_id) = macro_call.as_call_id(db, self.crate_def_map.krate, |path| { |
101 | if let Some(local_scope) = local_scope { | 101 | if let Some(local_scope) = local_scope { |
102 | if let Some(def) = path.as_ident().and_then(|n| local_scope.get_legacy_macro(n)) { | 102 | if let Some(def) = path.as_ident().and_then(|n| local_scope.get_legacy_macro(n)) { |
103 | return Some(def); | 103 | return Some(def); |
diff --git a/crates/ra_hir_def/src/data.rs b/crates/ra_hir_def/src/data.rs index 807195d25..53599e74a 100644 --- a/crates/ra_hir_def/src/data.rs +++ b/crates/ra_hir_def/src/data.rs | |||
@@ -99,7 +99,7 @@ impl FunctionData { | |||
99 | } | 99 | } |
100 | 100 | ||
101 | fn desugar_future_path(orig: TypeRef) -> Path { | 101 | fn desugar_future_path(orig: TypeRef) -> Path { |
102 | let path = path![std::future::Future]; | 102 | let path = path![core::future::Future]; |
103 | let mut generic_args: Vec<_> = std::iter::repeat(None).take(path.segments.len() - 1).collect(); | 103 | let mut generic_args: Vec<_> = std::iter::repeat(None).take(path.segments.len() - 1).collect(); |
104 | let mut last = GenericArgs::empty(); | 104 | let mut last = GenericArgs::empty(); |
105 | last.bindings.push(AssociatedTypeBinding { | 105 | last.bindings.push(AssociatedTypeBinding { |
diff --git a/crates/ra_hir_def/src/diagnostics.rs b/crates/ra_hir_def/src/diagnostics.rs index 510c5e064..30db48f86 100644 --- a/crates/ra_hir_def/src/diagnostics.rs +++ b/crates/ra_hir_def/src/diagnostics.rs | |||
@@ -3,7 +3,6 @@ | |||
3 | use std::any::Any; | 3 | use std::any::Any; |
4 | 4 | ||
5 | use hir_expand::diagnostics::Diagnostic; | 5 | use hir_expand::diagnostics::Diagnostic; |
6 | use ra_db::RelativePathBuf; | ||
7 | use ra_syntax::{ast, AstPtr, SyntaxNodePtr}; | 6 | use ra_syntax::{ast, AstPtr, SyntaxNodePtr}; |
8 | 7 | ||
9 | use hir_expand::{HirFileId, InFile}; | 8 | use hir_expand::{HirFileId, InFile}; |
@@ -12,7 +11,7 @@ use hir_expand::{HirFileId, InFile}; | |||
12 | pub struct UnresolvedModule { | 11 | pub struct UnresolvedModule { |
13 | pub file: HirFileId, | 12 | pub file: HirFileId, |
14 | pub decl: AstPtr<ast::Module>, | 13 | pub decl: AstPtr<ast::Module>, |
15 | pub candidate: RelativePathBuf, | 14 | pub candidate: String, |
16 | } | 15 | } |
17 | 16 | ||
18 | impl Diagnostic for UnresolvedModule { | 17 | impl Diagnostic for UnresolvedModule { |
diff --git a/crates/ra_hir_def/src/find_path.rs b/crates/ra_hir_def/src/find_path.rs index a7f59e028..06701a830 100644 --- a/crates/ra_hir_def/src/find_path.rs +++ b/crates/ra_hir_def/src/find_path.rs | |||
@@ -159,10 +159,16 @@ fn find_path_inner( | |||
159 | let crate_graph = db.crate_graph(); | 159 | let crate_graph = db.crate_graph(); |
160 | let extern_paths = crate_graph[from.krate].dependencies.iter().filter_map(|dep| { | 160 | let extern_paths = crate_graph[from.krate].dependencies.iter().filter_map(|dep| { |
161 | let import_map = db.import_map(dep.crate_id); | 161 | let import_map = db.import_map(dep.crate_id); |
162 | import_map.path_of(item).map(|modpath| { | 162 | import_map.import_info_for(item).and_then(|info| { |
163 | let mut modpath = modpath.clone(); | 163 | // Determine best path for containing module and append last segment from `info`. |
164 | modpath.segments.insert(0, dep.as_name()); | 164 | let mut path = find_path_inner( |
165 | modpath | 165 | db, |
166 | ItemInNs::Types(ModuleDefId::ModuleId(info.container)), | ||
167 | from, | ||
168 | best_path_len - 1, | ||
169 | )?; | ||
170 | path.segments.push(info.path.segments.last().unwrap().clone()); | ||
171 | Some(path) | ||
166 | }) | 172 | }) |
167 | }); | 173 | }); |
168 | 174 | ||
@@ -299,8 +305,8 @@ mod tests { | |||
299 | /// `code` needs to contain a cursor marker; checks that `find_path` for the | 305 | /// `code` needs to contain a cursor marker; checks that `find_path` for the |
300 | /// item the `path` refers to returns that same path when called from the | 306 | /// item the `path` refers to returns that same path when called from the |
301 | /// module the cursor is in. | 307 | /// module the cursor is in. |
302 | fn check_found_path(code: &str, path: &str) { | 308 | fn check_found_path(ra_fixture: &str, path: &str) { |
303 | let (db, pos) = TestDB::with_position(code); | 309 | let (db, pos) = TestDB::with_position(ra_fixture); |
304 | let module = db.module_for_file(pos.file_id); | 310 | let module = db.module_for_file(pos.file_id); |
305 | let parsed_path_file = ra_syntax::SourceFile::parse(&format!("use {};", path)); | 311 | let parsed_path_file = ra_syntax::SourceFile::parse(&format!("use {};", path)); |
306 | let ast_path = parsed_path_file | 312 | let ast_path = parsed_path_file |
@@ -420,7 +426,6 @@ mod tests { | |||
420 | 426 | ||
421 | #[test] | 427 | #[test] |
422 | fn different_crate_renamed() { | 428 | fn different_crate_renamed() { |
423 | // Even if a local path exists, if the item is defined externally, prefer an external path. | ||
424 | let code = r#" | 429 | let code = r#" |
425 | //- /main.rs crate:main deps:std | 430 | //- /main.rs crate:main deps:std |
426 | extern crate std as std_renamed; | 431 | extern crate std as std_renamed; |
@@ -428,7 +433,45 @@ mod tests { | |||
428 | //- /std.rs crate:std | 433 | //- /std.rs crate:std |
429 | pub struct S; | 434 | pub struct S; |
430 | "#; | 435 | "#; |
431 | check_found_path(code, "std::S"); | 436 | check_found_path(code, "std_renamed::S"); |
437 | } | ||
438 | |||
439 | #[test] | ||
440 | fn partially_imported() { | ||
441 | // Tests that short paths are used even for external items, when parts of the path are | ||
442 | // already in scope. | ||
443 | check_found_path( | ||
444 | r#" | ||
445 | //- /main.rs crate:main deps:ra_syntax | ||
446 | |||
447 | use ra_syntax::ast; | ||
448 | <|> | ||
449 | |||
450 | //- /lib.rs crate:ra_syntax | ||
451 | pub mod ast { | ||
452 | pub enum ModuleItem { | ||
453 | A, B, C, | ||
454 | } | ||
455 | } | ||
456 | "#, | ||
457 | "ast::ModuleItem", | ||
458 | ); | ||
459 | |||
460 | check_found_path( | ||
461 | r#" | ||
462 | //- /main.rs crate:main deps:ra_syntax | ||
463 | |||
464 | <|> | ||
465 | |||
466 | //- /lib.rs crate:ra_syntax | ||
467 | pub mod ast { | ||
468 | pub enum ModuleItem { | ||
469 | A, B, C, | ||
470 | } | ||
471 | } | ||
472 | "#, | ||
473 | "ra_syntax::ast::ModuleItem", | ||
474 | ); | ||
432 | } | 475 | } |
433 | 476 | ||
434 | #[test] | 477 | #[test] |
diff --git a/crates/ra_hir_def/src/import_map.rs b/crates/ra_hir_def/src/import_map.rs index 4284a0a91..68e20d06b 100644 --- a/crates/ra_hir_def/src/import_map.rs +++ b/crates/ra_hir_def/src/import_map.rs | |||
@@ -1,9 +1,11 @@ | |||
1 | //! A map of all publicly exported items in a crate. | 1 | //! A map of all publicly exported items in a crate. |
2 | 2 | ||
3 | use std::{collections::hash_map::Entry, fmt, sync::Arc}; | 3 | use std::{cmp::Ordering, fmt, hash::BuildHasherDefault, sync::Arc}; |
4 | 4 | ||
5 | use fst::{self, Streamer}; | ||
6 | use indexmap::{map::Entry, IndexMap}; | ||
5 | use ra_db::CrateId; | 7 | use ra_db::CrateId; |
6 | use rustc_hash::FxHashMap; | 8 | use rustc_hash::FxHasher; |
7 | 9 | ||
8 | use crate::{ | 10 | use crate::{ |
9 | db::DefDatabase, | 11 | db::DefDatabase, |
@@ -13,6 +15,17 @@ use crate::{ | |||
13 | ModuleDefId, ModuleId, | 15 | ModuleDefId, ModuleId, |
14 | }; | 16 | }; |
15 | 17 | ||
18 | type FxIndexMap<K, V> = IndexMap<K, V, BuildHasherDefault<FxHasher>>; | ||
19 | |||
20 | /// Item import details stored in the `ImportMap`. | ||
21 | #[derive(Debug, Clone, Eq, PartialEq)] | ||
22 | pub struct ImportInfo { | ||
23 | /// A path that can be used to import the item, relative to the crate's root. | ||
24 | pub path: ModPath, | ||
25 | /// The module containing this item. | ||
26 | pub container: ModuleId, | ||
27 | } | ||
28 | |||
16 | /// A map from publicly exported items to the path needed to import/name them from a downstream | 29 | /// A map from publicly exported items to the path needed to import/name them from a downstream |
17 | /// crate. | 30 | /// crate. |
18 | /// | 31 | /// |
@@ -21,16 +34,24 @@ use crate::{ | |||
21 | /// | 34 | /// |
22 | /// Note that all paths are relative to the containing crate's root, so the crate name still needs | 35 | /// 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. | 36 | /// to be prepended to the `ModPath` before the path is valid. |
24 | #[derive(Eq, PartialEq)] | ||
25 | pub struct ImportMap { | 37 | pub struct ImportMap { |
26 | map: FxHashMap<ItemInNs, ModPath>, | 38 | map: FxIndexMap<ItemInNs, ImportInfo>, |
39 | |||
40 | /// List of keys stored in `map`, sorted lexicographically by their `ModPath`. Indexed by the | ||
41 | /// values returned by running `fst`. | ||
42 | /// | ||
43 | /// Since a path can refer to multiple items due to namespacing, we store all items with the | ||
44 | /// same path right after each other. This allows us to find all items after the FST gives us | ||
45 | /// the index of the first one. | ||
46 | importables: Vec<ItemInNs>, | ||
47 | fst: fst::Map<Vec<u8>>, | ||
27 | } | 48 | } |
28 | 49 | ||
29 | impl ImportMap { | 50 | impl ImportMap { |
30 | pub fn import_map_query(db: &dyn DefDatabase, krate: CrateId) -> Arc<Self> { | 51 | pub fn import_map_query(db: &dyn DefDatabase, krate: CrateId) -> Arc<Self> { |
31 | let _p = ra_prof::profile("import_map_query"); | 52 | let _p = ra_prof::profile("import_map_query"); |
32 | let def_map = db.crate_def_map(krate); | 53 | let def_map = db.crate_def_map(krate); |
33 | let mut import_map = FxHashMap::with_capacity_and_hasher(64, Default::default()); | 54 | let mut import_map = FxIndexMap::with_capacity_and_hasher(64, Default::default()); |
34 | 55 | ||
35 | // We look only into modules that are public(ly reexported), starting with the crate root. | 56 | // We look only into modules that are public(ly reexported), starting with the crate root. |
36 | let empty = ModPath { kind: PathKind::Plain, segments: vec![] }; | 57 | let empty = ModPath { kind: PathKind::Plain, segments: vec![] }; |
@@ -66,12 +87,12 @@ impl ImportMap { | |||
66 | let path = mk_path(); | 87 | let path = mk_path(); |
67 | match import_map.entry(item) { | 88 | match import_map.entry(item) { |
68 | Entry::Vacant(entry) => { | 89 | Entry::Vacant(entry) => { |
69 | entry.insert(path); | 90 | entry.insert(ImportInfo { path, container: module }); |
70 | } | 91 | } |
71 | Entry::Occupied(mut entry) => { | 92 | Entry::Occupied(mut entry) => { |
72 | // If the new path is shorter, prefer that one. | 93 | // If the new path is shorter, prefer that one. |
73 | if path.len() < entry.get().len() { | 94 | if path.len() < entry.get().path.len() { |
74 | *entry.get_mut() = path; | 95 | *entry.get_mut() = ImportInfo { path, container: module }; |
75 | } else { | 96 | } else { |
76 | continue; | 97 | continue; |
77 | } | 98 | } |
@@ -88,27 +109,67 @@ impl ImportMap { | |||
88 | } | 109 | } |
89 | } | 110 | } |
90 | 111 | ||
91 | Arc::new(Self { map: import_map }) | 112 | let mut importables = import_map.iter().collect::<Vec<_>>(); |
113 | |||
114 | importables.sort_by(cmp); | ||
115 | |||
116 | // Build the FST, taking care not to insert duplicate values. | ||
117 | |||
118 | let mut builder = fst::MapBuilder::memory(); | ||
119 | let mut last_batch_start = 0; | ||
120 | |||
121 | for idx in 0..importables.len() { | ||
122 | if let Some(next_item) = importables.get(idx + 1) { | ||
123 | if cmp(&importables[last_batch_start], next_item) == Ordering::Equal { | ||
124 | continue; | ||
125 | } | ||
126 | } | ||
127 | |||
128 | let start = last_batch_start; | ||
129 | last_batch_start = idx + 1; | ||
130 | |||
131 | let key = fst_path(&importables[start].1.path); | ||
132 | |||
133 | builder.insert(key, start as u64).unwrap(); | ||
134 | } | ||
135 | |||
136 | let fst = fst::Map::new(builder.into_inner().unwrap()).unwrap(); | ||
137 | let importables = importables.iter().map(|(item, _)| **item).collect(); | ||
138 | |||
139 | Arc::new(Self { map: import_map, fst, importables }) | ||
92 | } | 140 | } |
93 | 141 | ||
94 | /// Returns the `ModPath` needed to import/mention `item`, relative to this crate's root. | 142 | /// Returns the `ModPath` needed to import/mention `item`, relative to this crate's root. |
95 | pub fn path_of(&self, item: ItemInNs) -> Option<&ModPath> { | 143 | pub fn path_of(&self, item: ItemInNs) -> Option<&ModPath> { |
144 | Some(&self.map.get(&item)?.path) | ||
145 | } | ||
146 | |||
147 | pub fn import_info_for(&self, item: ItemInNs) -> Option<&ImportInfo> { | ||
96 | self.map.get(&item) | 148 | self.map.get(&item) |
97 | } | 149 | } |
98 | } | 150 | } |
99 | 151 | ||
152 | impl PartialEq for ImportMap { | ||
153 | fn eq(&self, other: &Self) -> bool { | ||
154 | // `fst` and `importables` are built from `map`, so we don't need to compare them. | ||
155 | self.map == other.map | ||
156 | } | ||
157 | } | ||
158 | |||
159 | impl Eq for ImportMap {} | ||
160 | |||
100 | impl fmt::Debug for ImportMap { | 161 | impl fmt::Debug for ImportMap { |
101 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { | 162 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
102 | let mut importable_paths: Vec<_> = self | 163 | let mut importable_paths: Vec<_> = self |
103 | .map | 164 | .map |
104 | .iter() | 165 | .iter() |
105 | .map(|(item, modpath)| { | 166 | .map(|(item, info)| { |
106 | let ns = match item { | 167 | let ns = match item { |
107 | ItemInNs::Types(_) => "t", | 168 | ItemInNs::Types(_) => "t", |
108 | ItemInNs::Values(_) => "v", | 169 | ItemInNs::Values(_) => "v", |
109 | ItemInNs::Macros(_) => "m", | 170 | ItemInNs::Macros(_) => "m", |
110 | }; | 171 | }; |
111 | format!("- {} ({})", modpath, ns) | 172 | format!("- {} ({})", info.path, ns) |
112 | }) | 173 | }) |
113 | .collect(); | 174 | .collect(); |
114 | 175 | ||
@@ -117,19 +178,135 @@ impl fmt::Debug for ImportMap { | |||
117 | } | 178 | } |
118 | } | 179 | } |
119 | 180 | ||
181 | fn fst_path(path: &ModPath) -> String { | ||
182 | let mut s = path.to_string(); | ||
183 | s.make_ascii_lowercase(); | ||
184 | s | ||
185 | } | ||
186 | |||
187 | fn cmp((_, lhs): &(&ItemInNs, &ImportInfo), (_, rhs): &(&ItemInNs, &ImportInfo)) -> Ordering { | ||
188 | let lhs_str = fst_path(&lhs.path); | ||
189 | let rhs_str = fst_path(&rhs.path); | ||
190 | lhs_str.cmp(&rhs_str) | ||
191 | } | ||
192 | |||
193 | #[derive(Debug)] | ||
194 | pub struct Query { | ||
195 | query: String, | ||
196 | lowercased: String, | ||
197 | anchor_end: bool, | ||
198 | case_sensitive: bool, | ||
199 | limit: usize, | ||
200 | } | ||
201 | |||
202 | impl Query { | ||
203 | pub fn new(query: &str) -> Self { | ||
204 | Self { | ||
205 | lowercased: query.to_lowercase(), | ||
206 | query: query.to_string(), | ||
207 | anchor_end: false, | ||
208 | case_sensitive: false, | ||
209 | limit: usize::max_value(), | ||
210 | } | ||
211 | } | ||
212 | |||
213 | /// Only returns items whose paths end with the (case-insensitive) query string as their last | ||
214 | /// segment. | ||
215 | pub fn anchor_end(self) -> Self { | ||
216 | Self { anchor_end: true, ..self } | ||
217 | } | ||
218 | |||
219 | /// Limits the returned number of items to `limit`. | ||
220 | pub fn limit(self, limit: usize) -> Self { | ||
221 | Self { limit, ..self } | ||
222 | } | ||
223 | |||
224 | /// Respect casing of the query string when matching. | ||
225 | pub fn case_sensitive(self) -> Self { | ||
226 | Self { case_sensitive: true, ..self } | ||
227 | } | ||
228 | } | ||
229 | |||
230 | /// Searches dependencies of `krate` for an importable path matching `query`. | ||
231 | /// | ||
232 | /// This returns a list of items that could be imported from dependencies of `krate`. | ||
233 | pub fn search_dependencies<'a>( | ||
234 | db: &'a dyn DefDatabase, | ||
235 | krate: CrateId, | ||
236 | query: Query, | ||
237 | ) -> Vec<ItemInNs> { | ||
238 | let _p = ra_prof::profile("search_dependencies").detail(|| format!("{:?}", query)); | ||
239 | |||
240 | let graph = db.crate_graph(); | ||
241 | let import_maps: Vec<_> = | ||
242 | graph[krate].dependencies.iter().map(|dep| db.import_map(dep.crate_id)).collect(); | ||
243 | |||
244 | let automaton = fst::automaton::Subsequence::new(&query.lowercased); | ||
245 | |||
246 | let mut op = fst::map::OpBuilder::new(); | ||
247 | for map in &import_maps { | ||
248 | op = op.add(map.fst.search(&automaton)); | ||
249 | } | ||
250 | |||
251 | let mut stream = op.union(); | ||
252 | let mut res = Vec::new(); | ||
253 | while let Some((_, indexed_values)) = stream.next() { | ||
254 | for indexed_value in indexed_values { | ||
255 | let import_map = &import_maps[indexed_value.index]; | ||
256 | let importables = &import_map.importables[indexed_value.value as usize..]; | ||
257 | |||
258 | // Path shared by the importable items in this group. | ||
259 | let path = &import_map.map[&importables[0]].path; | ||
260 | |||
261 | if query.anchor_end { | ||
262 | // Last segment must match query. | ||
263 | let last = path.segments.last().unwrap().to_string(); | ||
264 | if last.to_lowercase() != query.lowercased { | ||
265 | continue; | ||
266 | } | ||
267 | } | ||
268 | |||
269 | // Add the items from this `ModPath` group. Those are all subsequent items in | ||
270 | // `importables` whose paths match `path`. | ||
271 | let iter = importables.iter().copied().take_while(|item| { | ||
272 | let item_path = &import_map.map[item].path; | ||
273 | fst_path(item_path) == fst_path(path) | ||
274 | }); | ||
275 | |||
276 | if query.case_sensitive { | ||
277 | // FIXME: This does not do a subsequence match. | ||
278 | res.extend(iter.filter(|item| { | ||
279 | let item_path = &import_map.map[item].path; | ||
280 | item_path.to_string().contains(&query.query) | ||
281 | })); | ||
282 | } else { | ||
283 | res.extend(iter); | ||
284 | } | ||
285 | |||
286 | if res.len() >= query.limit { | ||
287 | res.truncate(query.limit); | ||
288 | return res; | ||
289 | } | ||
290 | } | ||
291 | } | ||
292 | |||
293 | res | ||
294 | } | ||
295 | |||
120 | #[cfg(test)] | 296 | #[cfg(test)] |
121 | mod tests { | 297 | mod tests { |
122 | use super::*; | 298 | use super::*; |
123 | use crate::test_db::TestDB; | 299 | use crate::test_db::TestDB; |
124 | use insta::assert_snapshot; | 300 | use insta::assert_snapshot; |
301 | use itertools::Itertools; | ||
125 | use ra_db::fixture::WithFixture; | 302 | use ra_db::fixture::WithFixture; |
126 | use ra_db::SourceDatabase; | 303 | use ra_db::{SourceDatabase, Upcast}; |
127 | 304 | ||
128 | fn import_map(ra_fixture: &str) -> String { | 305 | fn import_map(ra_fixture: &str) -> String { |
129 | let db = TestDB::with_files(ra_fixture); | 306 | let db = TestDB::with_files(ra_fixture); |
130 | let crate_graph = db.crate_graph(); | 307 | let crate_graph = db.crate_graph(); |
131 | 308 | ||
132 | let import_maps: Vec<_> = crate_graph | 309 | let s = crate_graph |
133 | .iter() | 310 | .iter() |
134 | .filter_map(|krate| { | 311 | .filter_map(|krate| { |
135 | let cdata = &crate_graph[krate]; | 312 | let cdata = &crate_graph[krate]; |
@@ -139,9 +316,41 @@ mod tests { | |||
139 | 316 | ||
140 | Some(format!("{}:\n{:?}", name, map)) | 317 | Some(format!("{}:\n{:?}", name, map)) |
141 | }) | 318 | }) |
142 | .collect(); | 319 | .join("\n"); |
320 | s | ||
321 | } | ||
143 | 322 | ||
144 | import_maps.join("\n") | 323 | fn search_dependencies_of(ra_fixture: &str, krate_name: &str, query: Query) -> String { |
324 | let db = TestDB::with_files(ra_fixture); | ||
325 | let crate_graph = db.crate_graph(); | ||
326 | let krate = crate_graph | ||
327 | .iter() | ||
328 | .find(|krate| { | ||
329 | crate_graph[*krate].display_name.as_ref().map(|n| n.to_string()) | ||
330 | == Some(krate_name.to_string()) | ||
331 | }) | ||
332 | .unwrap(); | ||
333 | |||
334 | search_dependencies(db.upcast(), krate, query) | ||
335 | .into_iter() | ||
336 | .filter_map(|item| { | ||
337 | let mark = match item { | ||
338 | ItemInNs::Types(_) => "t", | ||
339 | ItemInNs::Values(_) => "v", | ||
340 | ItemInNs::Macros(_) => "m", | ||
341 | }; | ||
342 | item.krate(db.upcast()).map(|krate| { | ||
343 | let map = db.import_map(krate); | ||
344 | let path = map.path_of(item).unwrap(); | ||
345 | format!( | ||
346 | "{}::{} ({})", | ||
347 | crate_graph[krate].display_name.as_ref().unwrap(), | ||
348 | path, | ||
349 | mark | ||
350 | ) | ||
351 | }) | ||
352 | }) | ||
353 | .join("\n") | ||
145 | } | 354 | } |
146 | 355 | ||
147 | #[test] | 356 | #[test] |
@@ -328,4 +537,143 @@ mod tests { | |||
328 | lib: | 537 | lib: |
329 | "###); | 538 | "###); |
330 | } | 539 | } |
540 | |||
541 | #[test] | ||
542 | fn namespacing() { | ||
543 | let map = import_map( | ||
544 | r" | ||
545 | //- /lib.rs crate:lib | ||
546 | pub struct Thing; // t + v | ||
547 | #[macro_export] | ||
548 | macro_rules! Thing { // m | ||
549 | () => {}; | ||
550 | } | ||
551 | ", | ||
552 | ); | ||
553 | |||
554 | assert_snapshot!(map, @r###" | ||
555 | lib: | ||
556 | - Thing (m) | ||
557 | - Thing (t) | ||
558 | - Thing (v) | ||
559 | "###); | ||
560 | |||
561 | let map = import_map( | ||
562 | r" | ||
563 | //- /lib.rs crate:lib | ||
564 | pub mod Thing {} // t | ||
565 | #[macro_export] | ||
566 | macro_rules! Thing { // m | ||
567 | () => {}; | ||
568 | } | ||
569 | ", | ||
570 | ); | ||
571 | |||
572 | assert_snapshot!(map, @r###" | ||
573 | lib: | ||
574 | - Thing (m) | ||
575 | - Thing (t) | ||
576 | "###); | ||
577 | } | ||
578 | |||
579 | #[test] | ||
580 | fn search() { | ||
581 | let ra_fixture = r#" | ||
582 | //- /main.rs crate:main deps:dep | ||
583 | //- /dep.rs crate:dep deps:tdep | ||
584 | use tdep::fmt as fmt_dep; | ||
585 | pub mod fmt { | ||
586 | pub trait Display { | ||
587 | fn fmt(); | ||
588 | } | ||
589 | } | ||
590 | #[macro_export] | ||
591 | macro_rules! Fmt { | ||
592 | () => {}; | ||
593 | } | ||
594 | pub struct Fmt; | ||
595 | |||
596 | pub fn format() {} | ||
597 | pub fn no() {} | ||
598 | |||
599 | //- /tdep.rs crate:tdep | ||
600 | pub mod fmt { | ||
601 | pub struct NotImportableFromMain; | ||
602 | } | ||
603 | "#; | ||
604 | |||
605 | let res = search_dependencies_of(ra_fixture, "main", Query::new("fmt")); | ||
606 | assert_snapshot!(res, @r###" | ||
607 | dep::fmt (t) | ||
608 | dep::Fmt (t) | ||
609 | dep::Fmt (v) | ||
610 | dep::Fmt (m) | ||
611 | dep::fmt::Display (t) | ||
612 | dep::format (v) | ||
613 | "###); | ||
614 | |||
615 | let res = search_dependencies_of(ra_fixture, "main", Query::new("fmt").anchor_end()); | ||
616 | assert_snapshot!(res, @r###" | ||
617 | dep::fmt (t) | ||
618 | dep::Fmt (t) | ||
619 | dep::Fmt (v) | ||
620 | dep::Fmt (m) | ||
621 | "###); | ||
622 | } | ||
623 | |||
624 | #[test] | ||
625 | fn search_casing() { | ||
626 | let ra_fixture = r#" | ||
627 | //- /main.rs crate:main deps:dep | ||
628 | //- /dep.rs crate:dep | ||
629 | |||
630 | pub struct fmt; | ||
631 | pub struct FMT; | ||
632 | "#; | ||
633 | |||
634 | let res = search_dependencies_of(ra_fixture, "main", Query::new("FMT")); | ||
635 | |||
636 | assert_snapshot!(res, @r###" | ||
637 | dep::fmt (t) | ||
638 | dep::fmt (v) | ||
639 | dep::FMT (t) | ||
640 | dep::FMT (v) | ||
641 | "###); | ||
642 | |||
643 | let res = search_dependencies_of(ra_fixture, "main", Query::new("FMT").case_sensitive()); | ||
644 | |||
645 | assert_snapshot!(res, @r###" | ||
646 | dep::FMT (t) | ||
647 | dep::FMT (v) | ||
648 | "###); | ||
649 | } | ||
650 | |||
651 | #[test] | ||
652 | fn search_limit() { | ||
653 | let res = search_dependencies_of( | ||
654 | r#" | ||
655 | //- /main.rs crate:main deps:dep | ||
656 | //- /dep.rs crate:dep | ||
657 | pub mod fmt { | ||
658 | pub trait Display { | ||
659 | fn fmt(); | ||
660 | } | ||
661 | } | ||
662 | #[macro_export] | ||
663 | macro_rules! Fmt { | ||
664 | () => {}; | ||
665 | } | ||
666 | pub struct Fmt; | ||
667 | |||
668 | pub fn format() {} | ||
669 | pub fn no() {} | ||
670 | "#, | ||
671 | "main", | ||
672 | Query::new("").limit(2), | ||
673 | ); | ||
674 | assert_snapshot!(res, @r###" | ||
675 | dep::fmt (t) | ||
676 | dep::Fmt (t) | ||
677 | "###); | ||
678 | } | ||
331 | } | 679 | } |
diff --git a/crates/ra_hir_def/src/lib.rs b/crates/ra_hir_def/src/lib.rs index de490fcc5..edc59e5a8 100644 --- a/crates/ra_hir_def/src/lib.rs +++ b/crates/ra_hir_def/src/lib.rs | |||
@@ -417,6 +417,7 @@ pub trait AsMacroCall { | |||
417 | fn as_call_id( | 417 | fn as_call_id( |
418 | &self, | 418 | &self, |
419 | db: &dyn db::DefDatabase, | 419 | db: &dyn db::DefDatabase, |
420 | krate: CrateId, | ||
420 | resolver: impl Fn(path::ModPath) -> Option<MacroDefId>, | 421 | resolver: impl Fn(path::ModPath) -> Option<MacroDefId>, |
421 | ) -> Option<MacroCallId>; | 422 | ) -> Option<MacroCallId>; |
422 | } | 423 | } |
@@ -425,13 +426,14 @@ impl AsMacroCall for InFile<&ast::MacroCall> { | |||
425 | fn as_call_id( | 426 | fn as_call_id( |
426 | &self, | 427 | &self, |
427 | db: &dyn db::DefDatabase, | 428 | db: &dyn db::DefDatabase, |
429 | krate: CrateId, | ||
428 | resolver: impl Fn(path::ModPath) -> Option<MacroDefId>, | 430 | resolver: impl Fn(path::ModPath) -> Option<MacroDefId>, |
429 | ) -> Option<MacroCallId> { | 431 | ) -> Option<MacroCallId> { |
430 | let ast_id = AstId::new(self.file_id, db.ast_id_map(self.file_id).ast_id(self.value)); | 432 | let ast_id = AstId::new(self.file_id, db.ast_id_map(self.file_id).ast_id(self.value)); |
431 | let h = Hygiene::new(db.upcast(), self.file_id); | 433 | let h = Hygiene::new(db.upcast(), self.file_id); |
432 | let path = path::ModPath::from_src(self.value.path()?, &h)?; | 434 | let path = path::ModPath::from_src(self.value.path()?, &h)?; |
433 | 435 | ||
434 | AstIdWithPath::new(ast_id.file_id, ast_id.value, path).as_call_id(db, resolver) | 436 | AstIdWithPath::new(ast_id.file_id, ast_id.value, path).as_call_id(db, krate, resolver) |
435 | } | 437 | } |
436 | } | 438 | } |
437 | 439 | ||
@@ -452,6 +454,7 @@ impl AsMacroCall for AstIdWithPath<ast::MacroCall> { | |||
452 | fn as_call_id( | 454 | fn as_call_id( |
453 | &self, | 455 | &self, |
454 | db: &dyn db::DefDatabase, | 456 | db: &dyn db::DefDatabase, |
457 | krate: CrateId, | ||
455 | resolver: impl Fn(path::ModPath) -> Option<MacroDefId>, | 458 | resolver: impl Fn(path::ModPath) -> Option<MacroDefId>, |
456 | ) -> Option<MacroCallId> { | 459 | ) -> Option<MacroCallId> { |
457 | let def: MacroDefId = resolver(self.path.clone())?; | 460 | let def: MacroDefId = resolver(self.path.clone())?; |
@@ -461,13 +464,13 @@ impl AsMacroCall for AstIdWithPath<ast::MacroCall> { | |||
461 | let hygiene = Hygiene::new(db.upcast(), self.ast_id.file_id); | 464 | let hygiene = Hygiene::new(db.upcast(), self.ast_id.file_id); |
462 | 465 | ||
463 | Some( | 466 | Some( |
464 | expand_eager_macro(db.upcast(), macro_call, def, &|path: ast::Path| { | 467 | expand_eager_macro(db.upcast(), krate, macro_call, def, &|path: ast::Path| { |
465 | resolver(path::ModPath::from_src(path, &hygiene)?) | 468 | resolver(path::ModPath::from_src(path, &hygiene)?) |
466 | })? | 469 | })? |
467 | .into(), | 470 | .into(), |
468 | ) | 471 | ) |
469 | } else { | 472 | } else { |
470 | Some(def.as_lazy_macro(db.upcast(), MacroCallKind::FnLike(self.ast_id)).into()) | 473 | Some(def.as_lazy_macro(db.upcast(), krate, MacroCallKind::FnLike(self.ast_id)).into()) |
471 | } | 474 | } |
472 | } | 475 | } |
473 | } | 476 | } |
@@ -476,12 +479,14 @@ impl AsMacroCall for AstIdWithPath<ast::ModuleItem> { | |||
476 | fn as_call_id( | 479 | fn as_call_id( |
477 | &self, | 480 | &self, |
478 | db: &dyn db::DefDatabase, | 481 | db: &dyn db::DefDatabase, |
482 | krate: CrateId, | ||
479 | resolver: impl Fn(path::ModPath) -> Option<MacroDefId>, | 483 | resolver: impl Fn(path::ModPath) -> Option<MacroDefId>, |
480 | ) -> Option<MacroCallId> { | 484 | ) -> Option<MacroCallId> { |
481 | let def = resolver(self.path.clone())?; | 485 | let def = resolver(self.path.clone())?; |
482 | Some( | 486 | Some( |
483 | def.as_lazy_macro( | 487 | def.as_lazy_macro( |
484 | db.upcast(), | 488 | db.upcast(), |
489 | krate, | ||
485 | MacroCallKind::Attr(self.ast_id, self.path.segments.last()?.to_string()), | 490 | MacroCallKind::Attr(self.ast_id, self.path.segments.last()?.to_string()), |
486 | ) | 491 | ) |
487 | .into(), | 492 | .into(), |
diff --git a/crates/ra_hir_def/src/nameres.rs b/crates/ra_hir_def/src/nameres.rs index f279c2ad4..b3e5f491a 100644 --- a/crates/ra_hir_def/src/nameres.rs +++ b/crates/ra_hir_def/src/nameres.rs | |||
@@ -296,7 +296,6 @@ pub enum ModuleSource { | |||
296 | 296 | ||
297 | mod diagnostics { | 297 | mod diagnostics { |
298 | use hir_expand::diagnostics::DiagnosticSink; | 298 | use hir_expand::diagnostics::DiagnosticSink; |
299 | use ra_db::RelativePathBuf; | ||
300 | use ra_syntax::{ast, AstPtr}; | 299 | use ra_syntax::{ast, AstPtr}; |
301 | 300 | ||
302 | use crate::{db::DefDatabase, diagnostics::UnresolvedModule, nameres::LocalModuleId, AstId}; | 301 | use crate::{db::DefDatabase, diagnostics::UnresolvedModule, nameres::LocalModuleId, AstId}; |
@@ -306,7 +305,7 @@ mod diagnostics { | |||
306 | UnresolvedModule { | 305 | UnresolvedModule { |
307 | module: LocalModuleId, | 306 | module: LocalModuleId, |
308 | declaration: AstId<ast::Module>, | 307 | declaration: AstId<ast::Module>, |
309 | candidate: RelativePathBuf, | 308 | candidate: String, |
310 | }, | 309 | }, |
311 | } | 310 | } |
312 | 311 | ||
diff --git a/crates/ra_hir_def/src/nameres/collector.rs b/crates/ra_hir_def/src/nameres/collector.rs index 353a31ad4..976e5e585 100644 --- a/crates/ra_hir_def/src/nameres/collector.rs +++ b/crates/ra_hir_def/src/nameres/collector.rs | |||
@@ -571,16 +571,18 @@ impl DefCollector<'_> { | |||
571 | return false; | 571 | return false; |
572 | } | 572 | } |
573 | 573 | ||
574 | if let Some(call_id) = directive.ast_id.as_call_id(self.db, |path| { | 574 | if let Some(call_id) = |
575 | let resolved_res = self.def_map.resolve_path_fp_with_macro( | 575 | directive.ast_id.as_call_id(self.db, self.def_map.krate, |path| { |
576 | self.db, | 576 | let resolved_res = self.def_map.resolve_path_fp_with_macro( |
577 | ResolveMode::Other, | 577 | self.db, |
578 | directive.module_id, | 578 | ResolveMode::Other, |
579 | &path, | 579 | directive.module_id, |
580 | BuiltinShadowMode::Module, | 580 | &path, |
581 | ); | 581 | BuiltinShadowMode::Module, |
582 | resolved_res.resolved_def.take_macros() | 582 | ); |
583 | }) { | 583 | resolved_res.resolved_def.take_macros() |
584 | }) | ||
585 | { | ||
584 | resolved.push((directive.module_id, call_id, directive.depth)); | 586 | resolved.push((directive.module_id, call_id, directive.depth)); |
585 | res = ReachedFixedPoint::No; | 587 | res = ReachedFixedPoint::No; |
586 | return false; | 588 | return false; |
@@ -589,9 +591,10 @@ impl DefCollector<'_> { | |||
589 | true | 591 | true |
590 | }); | 592 | }); |
591 | attribute_macros.retain(|directive| { | 593 | attribute_macros.retain(|directive| { |
592 | if let Some(call_id) = directive | 594 | if let Some(call_id) = |
593 | .ast_id | 595 | directive.ast_id.as_call_id(self.db, self.def_map.krate, |path| { |
594 | .as_call_id(self.db, |path| self.resolve_attribute_macro(&directive, &path)) | 596 | self.resolve_attribute_macro(&directive, &path) |
597 | }) | ||
595 | { | 598 | { |
596 | resolved.push((directive.module_id, call_id, 0)); | 599 | resolved.push((directive.module_id, call_id, 0)); |
597 | res = ReachedFixedPoint::No; | 600 | res = ReachedFixedPoint::No; |
@@ -957,11 +960,13 @@ impl ModCollector<'_, '_> { | |||
957 | } | 960 | } |
958 | 961 | ||
959 | // Case 2: try to resolve in legacy scope and expand macro_rules | 962 | // Case 2: try to resolve in legacy scope and expand macro_rules |
960 | if let Some(macro_call_id) = ast_id.as_call_id(self.def_collector.db, |path| { | 963 | if let Some(macro_call_id) = |
961 | path.as_ident().and_then(|name| { | 964 | ast_id.as_call_id(self.def_collector.db, self.def_collector.def_map.krate, |path| { |
962 | self.def_collector.def_map[self.module_id].scope.get_legacy_macro(&name) | 965 | path.as_ident().and_then(|name| { |
966 | self.def_collector.def_map[self.module_id].scope.get_legacy_macro(&name) | ||
967 | }) | ||
963 | }) | 968 | }) |
964 | }) { | 969 | { |
965 | self.def_collector.unexpanded_macros.push(MacroDirective { | 970 | self.def_collector.unexpanded_macros.push(MacroDirective { |
966 | module_id: self.module_id, | 971 | module_id: self.module_id, |
967 | ast_id, | 972 | ast_id, |
diff --git a/crates/ra_hir_def/src/nameres/mod_resolution.rs b/crates/ra_hir_def/src/nameres/mod_resolution.rs index cede4a6fc..19fe0615a 100644 --- a/crates/ra_hir_def/src/nameres/mod_resolution.rs +++ b/crates/ra_hir_def/src/nameres/mod_resolution.rs | |||
@@ -44,7 +44,7 @@ impl ModDir { | |||
44 | file_id: HirFileId, | 44 | file_id: HirFileId, |
45 | name: &Name, | 45 | name: &Name, |
46 | attr_path: Option<&SmolStr>, | 46 | attr_path: Option<&SmolStr>, |
47 | ) -> Result<(FileId, ModDir), RelativePathBuf> { | 47 | ) -> Result<(FileId, ModDir), String> { |
48 | let file_id = file_id.original_file(db.upcast()); | 48 | let file_id = file_id.original_file(db.upcast()); |
49 | 49 | ||
50 | let mut candidate_files = Vec::new(); | 50 | let mut candidate_files = Vec::new(); |
@@ -52,11 +52,11 @@ impl ModDir { | |||
52 | Some(attr_path) => { | 52 | Some(attr_path) => { |
53 | let base = | 53 | let base = |
54 | if self.root_non_dir_owner { self.path.parent().unwrap() } else { &self.path }; | 54 | if self.root_non_dir_owner { self.path.parent().unwrap() } else { &self.path }; |
55 | candidate_files.push(base.join(attr_path)) | 55 | candidate_files.push(base.join(attr_path).to_string()) |
56 | } | 56 | } |
57 | None => { | 57 | None => { |
58 | candidate_files.push(self.path.join(&format!("{}.rs", name))); | 58 | candidate_files.push(self.path.join(&format!("{}.rs", name)).to_string()); |
59 | candidate_files.push(self.path.join(&format!("{}/mod.rs", name))); | 59 | candidate_files.push(self.path.join(&format!("{}/mod.rs", name)).to_string()); |
60 | } | 60 | } |
61 | }; | 61 | }; |
62 | 62 | ||
diff --git a/crates/ra_hir_def/src/path.rs b/crates/ra_hir_def/src/path.rs index bfa921de2..190d6d98d 100644 --- a/crates/ra_hir_def/src/path.rs +++ b/crates/ra_hir_def/src/path.rs | |||
@@ -154,7 +154,7 @@ pub enum GenericArg { | |||
154 | 154 | ||
155 | impl Path { | 155 | impl Path { |
156 | /// Converts an `ast::Path` to `Path`. Works with use trees. | 156 | /// Converts an `ast::Path` to `Path`. Works with use trees. |
157 | /// DEPRECATED: It does not handle `$crate` from macro call. | 157 | #[deprecated = "Doesn't handle hygiene, don't add new calls, remove old ones"] |
158 | pub fn from_ast(path: ast::Path) -> Option<Path> { | 158 | pub fn from_ast(path: ast::Path) -> Option<Path> { |
159 | lower::lower_path(path, &Hygiene::new_unhygienic()) | 159 | lower::lower_path(path, &Hygiene::new_unhygienic()) |
160 | } | 160 | } |
@@ -323,16 +323,16 @@ pub use hir_expand::name as __name; | |||
323 | 323 | ||
324 | #[macro_export] | 324 | #[macro_export] |
325 | macro_rules! __known_path { | 325 | macro_rules! __known_path { |
326 | (std::iter::IntoIterator) => {}; | 326 | (core::iter::IntoIterator) => {}; |
327 | (std::result::Result) => {}; | 327 | (core::result::Result) => {}; |
328 | (std::ops::Range) => {}; | 328 | (core::ops::Range) => {}; |
329 | (std::ops::RangeFrom) => {}; | 329 | (core::ops::RangeFrom) => {}; |
330 | (std::ops::RangeFull) => {}; | 330 | (core::ops::RangeFull) => {}; |
331 | (std::ops::RangeTo) => {}; | 331 | (core::ops::RangeTo) => {}; |
332 | (std::ops::RangeToInclusive) => {}; | 332 | (core::ops::RangeToInclusive) => {}; |
333 | (std::ops::RangeInclusive) => {}; | 333 | (core::ops::RangeInclusive) => {}; |
334 | (std::future::Future) => {}; | 334 | (core::future::Future) => {}; |
335 | (std::ops::Try) => {}; | 335 | (core::ops::Try) => {}; |
336 | ($path:path) => { | 336 | ($path:path) => { |
337 | compile_error!("Please register your known path in the path module") | 337 | compile_error!("Please register your known path in the path module") |
338 | }; | 338 | }; |
diff --git a/crates/ra_hir_def/src/test_db.rs b/crates/ra_hir_def/src/test_db.rs index bcfa66ac9..4581d8745 100644 --- a/crates/ra_hir_def/src/test_db.rs +++ b/crates/ra_hir_def/src/test_db.rs | |||
@@ -7,6 +7,7 @@ use std::{ | |||
7 | 7 | ||
8 | use hir_expand::db::AstDatabase; | 8 | use hir_expand::db::AstDatabase; |
9 | use ra_db::{salsa, CrateId, FileId, FileLoader, FileLoaderDelegate, Upcast}; | 9 | use ra_db::{salsa, CrateId, FileId, FileLoader, FileLoaderDelegate, Upcast}; |
10 | use rustc_hash::FxHashSet; | ||
10 | 11 | ||
11 | use crate::db::DefDatabase; | 12 | use crate::db::DefDatabase; |
12 | 13 | ||
@@ -59,7 +60,7 @@ impl FileLoader for TestDB { | |||
59 | fn resolve_path(&self, anchor: FileId, path: &str) -> Option<FileId> { | 60 | fn resolve_path(&self, anchor: FileId, path: &str) -> Option<FileId> { |
60 | FileLoaderDelegate(self).resolve_path(anchor, path) | 61 | FileLoaderDelegate(self).resolve_path(anchor, path) |
61 | } | 62 | } |
62 | fn relevant_crates(&self, file_id: FileId) -> Arc<Vec<CrateId>> { | 63 | fn relevant_crates(&self, file_id: FileId) -> Arc<FxHashSet<CrateId>> { |
63 | FileLoaderDelegate(self).relevant_crates(file_id) | 64 | FileLoaderDelegate(self).relevant_crates(file_id) |
64 | } | 65 | } |
65 | } | 66 | } |