Skip to content

Commit 4029628

Browse files
bors[bot]vsrsjonas-schievinkJonas Schievink
authored
Merge #4729 #4748
4729: Hover actions r=matklad a=vsrs This PR adds a `hoverActions` LSP extension and a `Go to Implementations` action as an example: ![hover_actions_impl](https://user-images.githubusercontent.com/62505555/83335732-6d9de280-a2b7-11ea-8cc3-75253d062fe0.gif) 4748: Add an `ImportMap` and use it to resolve item paths in `find_path` r=matklad a=jonas-schievink Removes the "go faster" queries I added in #4501 and #4506. I've checked this PR on the rustc code base and the assists are still fast. This should fix #4515. Note that this does introduce a change in behavior: We now always refer to items defined in external crates using paths through the external crate. Previously we could also use a local path (if for example the extern crate was reexported locally), as seen in the changed test. If that is undesired I can fix that, but the test didn't say why the previous behavior would be preferable. Co-authored-by: vsrs <vit@conrlab.com> Co-authored-by: Jonas Schievink <jonasschievink@gmail.com> Co-authored-by: Jonas Schievink <jonas.schievink@ferrous-systems.com>
3 parents 9c52f52 + bd9d7b6 + bc2d172 commit 4029628

File tree

22 files changed

+899
-169
lines changed

22 files changed

+899
-169
lines changed

crates/ra_db/src/lib.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,8 @@ use ra_syntax::{ast, Parse, SourceFile, TextRange, TextSize};
1111
pub use crate::{
1212
cancellation::Canceled,
1313
input::{
14-
CrateGraph, CrateId, CrateName, Dependency, Edition, Env, ExternSource, ExternSourceId,
15-
FileId, ProcMacroId, SourceRoot, SourceRootId,
14+
CrateData, CrateGraph, CrateId, CrateName, Dependency, Edition, Env, ExternSource,
15+
ExternSourceId, FileId, ProcMacroId, SourceRoot, SourceRootId,
1616
},
1717
};
1818
pub use relative_path::{RelativePath, RelativePathBuf};

crates/ra_hir/src/db.rs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,11 @@
33
pub use hir_def::db::{
44
AttrsQuery, BodyQuery, BodyWithSourceMapQuery, ConstDataQuery, CrateDefMapQueryQuery,
55
CrateLangItemsQuery, DefDatabase, DefDatabaseStorage, DocumentationQuery, EnumDataQuery,
6-
ExprScopesQuery, FunctionDataQuery, GenericParamsQuery, ImplDataQuery, InternConstQuery,
7-
InternDatabase, InternDatabaseStorage, InternEnumQuery, InternFunctionQuery, InternImplQuery,
8-
InternStaticQuery, InternStructQuery, InternTraitQuery, InternTypeAliasQuery, InternUnionQuery,
9-
LangItemQuery, ModuleLangItemsQuery, RawItemsQuery, StaticDataQuery, StructDataQuery,
10-
TraitDataQuery, TypeAliasDataQuery, UnionDataQuery,
6+
ExprScopesQuery, FunctionDataQuery, GenericParamsQuery, ImplDataQuery, ImportMapQuery,
7+
InternConstQuery, InternDatabase, InternDatabaseStorage, InternEnumQuery, InternFunctionQuery,
8+
InternImplQuery, InternStaticQuery, InternStructQuery, InternTraitQuery, InternTypeAliasQuery,
9+
InternUnionQuery, LangItemQuery, ModuleLangItemsQuery, RawItemsQuery, StaticDataQuery,
10+
StructDataQuery, TraitDataQuery, TypeAliasDataQuery, UnionDataQuery,
1111
};
1212
pub use hir_expand::db::{
1313
AstDatabase, AstDatabaseStorage, AstIdMapQuery, InternEagerExpansionQuery, InternMacroQuery,

crates/ra_hir_def/src/db.rs

Lines changed: 4 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
//! Defines database & queries for name resolution.
22
use std::sync::Arc;
33

4-
use hir_expand::{db::AstDatabase, name::Name, HirFileId};
4+
use hir_expand::{db::AstDatabase, HirFileId};
55
use ra_db::{salsa, CrateId, SourceDatabase, Upcast};
66
use ra_prof::profile;
77
use ra_syntax::SmolStr;
@@ -12,13 +12,10 @@ use crate::{
1212
body::{scope::ExprScopes, Body, BodySourceMap},
1313
data::{ConstData, FunctionData, ImplData, StaticData, TraitData, TypeAliasData},
1414
docs::Documentation,
15-
find_path,
1615
generics::GenericParams,
17-
item_scope::ItemInNs,
16+
import_map::ImportMap,
1817
lang_item::{LangItemTarget, LangItems},
1918
nameres::{raw::RawItems, CrateDefMap},
20-
path::ModPath,
21-
visibility::Visibility,
2219
AttrDefId, ConstId, ConstLoc, DefWithBodyId, EnumId, EnumLoc, FunctionId, FunctionLoc,
2320
GenericDefId, ImplId, ImplLoc, ModuleId, StaticId, StaticLoc, StructId, StructLoc, TraitId,
2421
TraitLoc, TypeAliasId, TypeAliasLoc, UnionId, UnionLoc,
@@ -113,15 +110,8 @@ pub trait DefDatabase: InternDatabase + AstDatabase + Upcast<dyn AstDatabase> {
113110
#[salsa::invoke(Documentation::documentation_query)]
114111
fn documentation(&self, def: AttrDefId) -> Option<Documentation>;
115112

116-
#[salsa::invoke(find_path::importable_locations_of_query)]
117-
fn importable_locations_of(
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>;
113+
#[salsa::invoke(ImportMap::import_map_query)]
114+
fn import_map(&self, krate: CrateId) -> Arc<ImportMap>;
125115
}
126116

127117
fn crate_def_map_wait(db: &impl DefDatabase, krate: CrateId) -> Arc<CrateDefMap> {

crates/ra_hir_def/src/find_path.rs

Lines changed: 119 additions & 83 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,16 @@
11
//! An algorithm to find a path to refer to a certain item.
22
3-
use std::sync::Arc;
4-
53
use hir_expand::name::{known, AsName, Name};
64
use ra_prof::profile;
5+
use rustc_hash::FxHashSet;
76
use test_utils::mark;
87

98
use crate::{
109
db::DefDatabase,
1110
item_scope::ItemInNs,
1211
path::{ModPath, PathKind},
1312
visibility::Visibility,
14-
CrateId, ModuleDefId, ModuleId,
13+
ModuleDefId, ModuleId,
1514
};
1615

1716
// FIXME: handle local items
@@ -20,7 +19,7 @@ use crate::{
2019
/// *from where* you're referring to the item, hence the `from` parameter.
2120
pub fn find_path(db: &dyn DefDatabase, item: ItemInNs, from: ModuleId) -> Option<ModPath> {
2221
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)
2423
}
2524

2625
const MAX_PATH_LEN: usize = 15;
@@ -36,20 +35,9 @@ impl ModPath {
3635
let first_segment = self.segments.first();
3736
first_segment == Some(&known::alloc) || first_segment == Some(&known::core)
3837
}
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-
}
5038
}
5139

52-
pub(crate) fn find_path_inner_query(
40+
fn find_path_inner(
5341
db: &dyn DefDatabase,
5442
item: ItemInNs,
5543
from: ModuleId,
@@ -133,31 +121,61 @@ pub(crate) fn find_path_inner_query(
133121
}
134122

135123
// - otherwise, look for modules containing (reexporting) it and import it from one of those
124+
136125
let crate_root = ModuleId { local_id: def_map.root, krate: from.krate };
137126
let crate_attrs = db.attrs(crate_root.into());
138127
let prefer_no_std = crate_attrs.by_key("no_std").exists();
139-
let importable_locations = find_importable_locations(db, item, from);
140128
let mut best_path = None;
141129
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);
152130

153-
let new_path = if let Some(best_path) = best_path {
154-
select_best_path(best_path, path, prefer_no_std)
155-
} else {
156-
path
157-
};
158-
best_path_len = new_path.len();
159-
best_path = Some(new_path);
131+
if item.krate(db) == Some(from.krate) {
132+
// Item was defined in the same crate that wants to import it. It cannot be found in any
133+
// dependency in this case.
134+
135+
let local_imports = find_local_import_locations(db, item, from);
136+
for (module_id, name) in local_imports {
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+
}
160177
}
178+
161179
best_path
162180
}
163181

@@ -185,69 +203,86 @@ fn select_best_path(old_path: ModPath, new_path: ModPath, prefer_no_std: bool) -
185203
}
186204
}
187205

188-
fn find_importable_locations(
206+
/// Finds locations in `from.krate` from which `item` can be imported by `from`.
207+
fn find_local_import_locations(
189208
db: &dyn DefDatabase,
190209
item: ItemInNs,
191210
from: ModuleId,
192211
) -> Vec<(ModuleId, Name)> {
193-
let crate_graph = db.crate_graph();
194-
let mut result = Vec::new();
195-
// We only look in the crate from which we are importing, and the direct
196-
// dependencies. We cannot refer to names from transitive dependencies
197-
// directly (only through reexports in direct dependencies).
198-
for krate in Some(from.krate)
199-
.into_iter()
200-
.chain(crate_graph[from.krate].dependencies.iter().map(|dep| dep.crate_id))
201-
{
202-
result.extend(
203-
db.importable_locations_of(item, krate)
204-
.iter()
205-
.filter(|(_, _, vis)| vis.is_visible_from(db, from))
206-
.map(|(m, n, _)| (*m, n.clone())),
207-
);
208-
}
209-
result
210-
}
212+
let _p = profile("find_local_import_locations");
213+
214+
// `from` can import anything below `from` with visibility of at least `from`, and anything
215+
// above `from` with any visibility. That means we do not need to descend into private siblings
216+
// of `from` (and similar).
217+
218+
let def_map = db.crate_def_map(from.krate);
219+
220+
// Compute the initial worklist. We start with all direct child modules of `from` as well as all
221+
// of its (recursive) parent modules.
222+
let data = &def_map.modules[from.local_id];
223+
let mut worklist = data
224+
.children
225+
.values()
226+
.map(|child| ModuleId { krate: from.krate, local_id: *child })
227+
.collect::<Vec<_>>();
228+
let mut parent = data.parent;
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+
};
211250

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.
218-
pub(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() {
227251
if let Some((name, vis)) = data.scope.name_of(item) {
228-
let is_private = if let Visibility::Module(private_to) = vis {
229-
private_to.local_id == local_id
230-
} else {
231-
false
232-
};
233-
let is_original_def = if let Some(module_def_id) = item.as_module_def_id() {
234-
data.scope.declarations().any(|it| it == module_def_id)
235-
} else {
236-
false
237-
};
238-
if is_private && !is_original_def {
252+
if vis.is_visible_from(db, from) {
253+
let is_private = if let Visibility::Module(private_to) = vis {
254+
private_to.local_id == module.local_id
255+
} else {
256+
false
257+
};
258+
let is_original_def = if let Some(module_def_id) = item.as_module_def_id() {
259+
data.scope.declarations().any(|it| it == module_def_id)
260+
} else {
261+
false
262+
};
263+
239264
// Ignore private imports. these could be used if we are
240265
// in a submodule of this module, but that's usually not
241266
// what the user wants; and if this module can import
242267
// the item and we're a submodule of it, so can we.
243268
// 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+
}
245281
}
246-
result.push((ModuleId { krate, local_id }, name.clone(), vis));
247282
}
248283
}
249284

250-
Arc::from(result)
285+
locations
251286
}
252287

253288
#[cfg(test)]
@@ -385,14 +420,15 @@ mod tests {
385420

386421
#[test]
387422
fn different_crate_renamed() {
423+
// Even if a local path exists, if the item is defined externally, prefer an external path.
388424
let code = r#"
389425
//- /main.rs crate:main deps:std
390426
extern crate std as std_renamed;
391427
<|>
392428
//- /std.rs crate:std
393429
pub struct S;
394430
"#;
395-
check_found_path(code, "std_renamed::S");
431+
check_found_path(code, "std::S");
396432
}
397433

398434
#[test]

0 commit comments

Comments
 (0)