diff options
author | bors[bot] <26634292+bors[bot]@users.noreply.github.com> | 2020-06-12 12:47:56 +0100 |
---|---|---|
committer | GitHub <[email protected]> | 2020-06-12 12:47:56 +0100 |
commit | 08a58901e8deee22a541ed14f7e6b02e3c57132e (patch) | |
tree | 21b1df550c05bae5b9d9ff23107e69727605c552 | |
parent | 36353bb1827dbd2efcde2d18c8598c4cc5e2e296 (diff) | |
parent | 0231e4ac77dacf6ca30f6b68c6081415f2da54ba (diff) |
Merge #4858
4858: find_path: return shorter paths for external items r=flodiebold a=jonas-schievink
If a containing module is already in scope, there's no need to use the full path to the item.
Fixes https://github.com/rust-analyzer/rust-analyzer/issues/4846
Co-authored-by: Jonas Schievink <[email protected]>
-rw-r--r-- | crates/ra_hir_def/src/find_path.rs | 59 | ||||
-rw-r--r-- | crates/ra_hir_def/src/import_map.rs | 39 |
2 files changed, 77 insertions, 21 deletions
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 36b4fdd81..68e20d06b 100644 --- a/crates/ra_hir_def/src/import_map.rs +++ b/crates/ra_hir_def/src/import_map.rs | |||
@@ -17,6 +17,15 @@ use crate::{ | |||
17 | 17 | ||
18 | type FxIndexMap<K, V> = IndexMap<K, V, BuildHasherDefault<FxHasher>>; | 18 | type FxIndexMap<K, V> = IndexMap<K, V, BuildHasherDefault<FxHasher>>; |
19 | 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 | |||
20 | /// 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 |
21 | /// crate. | 30 | /// crate. |
22 | /// | 31 | /// |
@@ -26,7 +35,7 @@ type FxIndexMap<K, V> = IndexMap<K, V, BuildHasherDefault<FxHasher>>; | |||
26 | /// 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 |
27 | /// to be prepended to the `ModPath` before the path is valid. | 36 | /// to be prepended to the `ModPath` before the path is valid. |
28 | pub struct ImportMap { | 37 | pub struct ImportMap { |
29 | map: FxIndexMap<ItemInNs, ModPath>, | 38 | map: FxIndexMap<ItemInNs, ImportInfo>, |
30 | 39 | ||
31 | /// List of keys stored in `map`, sorted lexicographically by their `ModPath`. Indexed by the | 40 | /// List of keys stored in `map`, sorted lexicographically by their `ModPath`. Indexed by the |
32 | /// values returned by running `fst`. | 41 | /// values returned by running `fst`. |
@@ -78,12 +87,12 @@ impl ImportMap { | |||
78 | let path = mk_path(); | 87 | let path = mk_path(); |
79 | match import_map.entry(item) { | 88 | match import_map.entry(item) { |
80 | Entry::Vacant(entry) => { | 89 | Entry::Vacant(entry) => { |
81 | entry.insert(path); | 90 | entry.insert(ImportInfo { path, container: module }); |
82 | } | 91 | } |
83 | Entry::Occupied(mut entry) => { | 92 | Entry::Occupied(mut entry) => { |
84 | // If the new path is shorter, prefer that one. | 93 | // If the new path is shorter, prefer that one. |
85 | if path.len() < entry.get().len() { | 94 | if path.len() < entry.get().path.len() { |
86 | *entry.get_mut() = path; | 95 | *entry.get_mut() = ImportInfo { path, container: module }; |
87 | } else { | 96 | } else { |
88 | continue; | 97 | continue; |
89 | } | 98 | } |
@@ -119,7 +128,7 @@ impl ImportMap { | |||
119 | let start = last_batch_start; | 128 | let start = last_batch_start; |
120 | last_batch_start = idx + 1; | 129 | last_batch_start = idx + 1; |
121 | 130 | ||
122 | let key = fst_path(&importables[start].1); | 131 | let key = fst_path(&importables[start].1.path); |
123 | 132 | ||
124 | builder.insert(key, start as u64).unwrap(); | 133 | builder.insert(key, start as u64).unwrap(); |
125 | } | 134 | } |
@@ -132,6 +141,10 @@ impl ImportMap { | |||
132 | 141 | ||
133 | /// 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. |
134 | 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> { | ||
135 | self.map.get(&item) | 148 | self.map.get(&item) |
136 | } | 149 | } |
137 | } | 150 | } |
@@ -150,13 +163,13 @@ impl fmt::Debug for ImportMap { | |||
150 | let mut importable_paths: Vec<_> = self | 163 | let mut importable_paths: Vec<_> = self |
151 | .map | 164 | .map |
152 | .iter() | 165 | .iter() |
153 | .map(|(item, modpath)| { | 166 | .map(|(item, info)| { |
154 | let ns = match item { | 167 | let ns = match item { |
155 | ItemInNs::Types(_) => "t", | 168 | ItemInNs::Types(_) => "t", |
156 | ItemInNs::Values(_) => "v", | 169 | ItemInNs::Values(_) => "v", |
157 | ItemInNs::Macros(_) => "m", | 170 | ItemInNs::Macros(_) => "m", |
158 | }; | 171 | }; |
159 | format!("- {} ({})", modpath, ns) | 172 | format!("- {} ({})", info.path, ns) |
160 | }) | 173 | }) |
161 | .collect(); | 174 | .collect(); |
162 | 175 | ||
@@ -171,9 +184,9 @@ fn fst_path(path: &ModPath) -> String { | |||
171 | s | 184 | s |
172 | } | 185 | } |
173 | 186 | ||
174 | fn cmp((_, lhs): &(&ItemInNs, &ModPath), (_, rhs): &(&ItemInNs, &ModPath)) -> Ordering { | 187 | fn cmp((_, lhs): &(&ItemInNs, &ImportInfo), (_, rhs): &(&ItemInNs, &ImportInfo)) -> Ordering { |
175 | let lhs_str = fst_path(lhs); | 188 | let lhs_str = fst_path(&lhs.path); |
176 | let rhs_str = fst_path(rhs); | 189 | let rhs_str = fst_path(&rhs.path); |
177 | lhs_str.cmp(&rhs_str) | 190 | lhs_str.cmp(&rhs_str) |
178 | } | 191 | } |
179 | 192 | ||
@@ -243,7 +256,7 @@ pub fn search_dependencies<'a>( | |||
243 | let importables = &import_map.importables[indexed_value.value as usize..]; | 256 | let importables = &import_map.importables[indexed_value.value as usize..]; |
244 | 257 | ||
245 | // Path shared by the importable items in this group. | 258 | // Path shared by the importable items in this group. |
246 | let path = &import_map.map[&importables[0]]; | 259 | let path = &import_map.map[&importables[0]].path; |
247 | 260 | ||
248 | if query.anchor_end { | 261 | if query.anchor_end { |
249 | // Last segment must match query. | 262 | // Last segment must match query. |
@@ -256,14 +269,14 @@ pub fn search_dependencies<'a>( | |||
256 | // Add the items from this `ModPath` group. Those are all subsequent items in | 269 | // Add the items from this `ModPath` group. Those are all subsequent items in |
257 | // `importables` whose paths match `path`. | 270 | // `importables` whose paths match `path`. |
258 | let iter = importables.iter().copied().take_while(|item| { | 271 | let iter = importables.iter().copied().take_while(|item| { |
259 | let item_path = &import_map.map[item]; | 272 | let item_path = &import_map.map[item].path; |
260 | fst_path(item_path) == fst_path(path) | 273 | fst_path(item_path) == fst_path(path) |
261 | }); | 274 | }); |
262 | 275 | ||
263 | if query.case_sensitive { | 276 | if query.case_sensitive { |
264 | // FIXME: This does not do a subsequence match. | 277 | // FIXME: This does not do a subsequence match. |
265 | res.extend(iter.filter(|item| { | 278 | res.extend(iter.filter(|item| { |
266 | let item_path = &import_map.map[item]; | 279 | let item_path = &import_map.map[item].path; |
267 | item_path.to_string().contains(&query.query) | 280 | item_path.to_string().contains(&query.query) |
268 | })); | 281 | })); |
269 | } else { | 282 | } else { |