Skip to content

Commit 08a5890

Browse files
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 #4846 Co-authored-by: Jonas Schievink <jonasschievink@gmail.com>
2 parents 36353bb + 0231e4a commit 08a5890

File tree

2 files changed

+77
-21
lines changed

2 files changed

+77
-21
lines changed

crates/ra_hir_def/src/find_path.rs

Lines changed: 51 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -159,10 +159,16 @@ fn find_path_inner(
159159
let crate_graph = db.crate_graph();
160160
let extern_paths = crate_graph[from.krate].dependencies.iter().filter_map(|dep| {
161161
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
162+
import_map.import_info_for(item).and_then(|info| {
163+
// Determine best path for containing module and append last segment from `info`.
164+
let mut path = find_path_inner(
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)
166172
})
167173
});
168174

@@ -299,8 +305,8 @@ mod tests {
299305
/// `code` needs to contain a cursor marker; checks that `find_path` for the
300306
/// item the `path` refers to returns that same path when called from the
301307
/// module the cursor is in.
302-
fn check_found_path(code: &str, path: &str) {
303-
let (db, pos) = TestDB::with_position(code);
308+
fn check_found_path(ra_fixture: &str, path: &str) {
309+
let (db, pos) = TestDB::with_position(ra_fixture);
304310
let module = db.module_for_file(pos.file_id);
305311
let parsed_path_file = ra_syntax::SourceFile::parse(&format!("use {};", path));
306312
let ast_path = parsed_path_file
@@ -420,15 +426,52 @@ mod tests {
420426

421427
#[test]
422428
fn different_crate_renamed() {
423-
// Even if a local path exists, if the item is defined externally, prefer an external path.
424429
let code = r#"
425430
//- /main.rs crate:main deps:std
426431
extern crate std as std_renamed;
427432
<|>
428433
//- /std.rs crate:std
429434
pub struct S;
430435
"#;
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+
);
432475
}
433476

434477
#[test]

crates/ra_hir_def/src/import_map.rs

Lines changed: 26 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,15 @@ use crate::{
1717

1818
type FxIndexMap<K, V> = IndexMap<K, V, BuildHasherDefault<FxHasher>>;
1919

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+
2029
/// A map from publicly exported items to the path needed to import/name them from a downstream
2130
/// crate.
2231
///
@@ -26,7 +35,7 @@ type FxIndexMap<K, V> = IndexMap<K, V, BuildHasherDefault<FxHasher>>;
2635
/// Note that all paths are relative to the containing crate's root, so the crate name still needs
2736
/// to be prepended to the `ModPath` before the path is valid.
2837
pub struct ImportMap {
29-
map: FxIndexMap<ItemInNs, ModPath>,
38+
map: FxIndexMap<ItemInNs, ImportInfo>,
3039

3140
/// List of keys stored in `map`, sorted lexicographically by their `ModPath`. Indexed by the
3241
/// values returned by running `fst`.
@@ -78,12 +87,12 @@ impl ImportMap {
7887
let path = mk_path();
7988
match import_map.entry(item) {
8089
Entry::Vacant(entry) => {
81-
entry.insert(path);
90+
entry.insert(ImportInfo { path, container: module });
8291
}
8392
Entry::Occupied(mut entry) => {
8493
// If the new path is shorter, prefer that one.
85-
if path.len() < entry.get().len() {
86-
*entry.get_mut() = path;
94+
if path.len() < entry.get().path.len() {
95+
*entry.get_mut() = ImportInfo { path, container: module };
8796
} else {
8897
continue;
8998
}
@@ -119,7 +128,7 @@ impl ImportMap {
119128
let start = last_batch_start;
120129
last_batch_start = idx + 1;
121130

122-
let key = fst_path(&importables[start].1);
131+
let key = fst_path(&importables[start].1.path);
123132

124133
builder.insert(key, start as u64).unwrap();
125134
}
@@ -132,6 +141,10 @@ impl ImportMap {
132141

133142
/// Returns the `ModPath` needed to import/mention `item`, relative to this crate's root.
134143
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> {
135148
self.map.get(&item)
136149
}
137150
}
@@ -150,13 +163,13 @@ impl fmt::Debug for ImportMap {
150163
let mut importable_paths: Vec<_> = self
151164
.map
152165
.iter()
153-
.map(|(item, modpath)| {
166+
.map(|(item, info)| {
154167
let ns = match item {
155168
ItemInNs::Types(_) => "t",
156169
ItemInNs::Values(_) => "v",
157170
ItemInNs::Macros(_) => "m",
158171
};
159-
format!("- {} ({})", modpath, ns)
172+
format!("- {} ({})", info.path, ns)
160173
})
161174
.collect();
162175

@@ -171,9 +184,9 @@ fn fst_path(path: &ModPath) -> String {
171184
s
172185
}
173186

174-
fn cmp((_, lhs): &(&ItemInNs, &ModPath), (_, rhs): &(&ItemInNs, &ModPath)) -> Ordering {
175-
let lhs_str = fst_path(lhs);
176-
let rhs_str = fst_path(rhs);
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);
177190
lhs_str.cmp(&rhs_str)
178191
}
179192

@@ -243,7 +256,7 @@ pub fn search_dependencies<'a>(
243256
let importables = &import_map.importables[indexed_value.value as usize..];
244257

245258
// 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;
247260

248261
if query.anchor_end {
249262
// Last segment must match query.
@@ -256,14 +269,14 @@ pub fn search_dependencies<'a>(
256269
// Add the items from this `ModPath` group. Those are all subsequent items in
257270
// `importables` whose paths match `path`.
258271
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;
260273
fst_path(item_path) == fst_path(path)
261274
});
262275

263276
if query.case_sensitive {
264277
// FIXME: This does not do a subsequence match.
265278
res.extend(iter.filter(|item| {
266-
let item_path = &import_map.map[item];
279+
let item_path = &import_map.map[item].path;
267280
item_path.to_string().contains(&query.query)
268281
}));
269282
} else {

0 commit comments

Comments
 (0)