mirror of
https://github.com/rust-lang/rust-analyzer
synced 2024-12-28 14:03:35 +00:00
122 lines
3.5 KiB
Rust
122 lines
3.5 KiB
Rust
//! An algorithm to find a path to refer to a certain item.
|
|
|
|
use crate::{
|
|
db::DefDatabase,
|
|
item_scope::ItemInNs,
|
|
path::{ModPath, PathKind},
|
|
ModuleId,
|
|
};
|
|
|
|
pub fn find_path(db: &impl DefDatabase, item: ItemInNs, from: ModuleId) -> ModPath {
|
|
// 1. Find all locations that the item could be imported from (i.e. that are visible)
|
|
// - this needs to consider other crates, for reexports from transitive dependencies
|
|
// - filter by visibility
|
|
// 2. For each of these, go up the module tree until we find an
|
|
// item/module/crate that is already in scope (including because it is in
|
|
// the prelude, and including aliases!)
|
|
// 3. Then select the one that gives the shortest path
|
|
let def_map = db.crate_def_map(from.krate);
|
|
let from_scope: &crate::item_scope::ItemScope = &def_map.modules[from.local_id].scope;
|
|
if let Some((name, _)) = from_scope.reverse_get(item) {
|
|
return ModPath::from_simple_segments(PathKind::Plain, vec![name.clone()]);
|
|
}
|
|
todo!()
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use super::*;
|
|
use crate::test_db::TestDB;
|
|
use hir_expand::hygiene::Hygiene;
|
|
use ra_db::fixture::WithFixture;
|
|
use ra_syntax::ast::AstNode;
|
|
|
|
/// `code` needs to contain a cursor marker; checks that `find_path` for the
|
|
/// item the `path` refers to returns that same path when called from the
|
|
/// module the cursor is in.
|
|
fn check_found_path(code: &str, path: &str) {
|
|
let (db, pos) = TestDB::with_position(code);
|
|
let module = db.module_for_file(pos.file_id);
|
|
let parsed_path_file = ra_syntax::SourceFile::parse(&format!("use {};", path));
|
|
let ast_path = parsed_path_file
|
|
.syntax_node()
|
|
.descendants()
|
|
.find_map(ra_syntax::ast::Path::cast)
|
|
.unwrap();
|
|
let mod_path = ModPath::from_src(ast_path, &Hygiene::new_unhygienic()).unwrap();
|
|
|
|
let crate_def_map = db.crate_def_map(module.krate);
|
|
let resolved = crate_def_map
|
|
.resolve_path(
|
|
&db,
|
|
module.local_id,
|
|
&mod_path,
|
|
crate::item_scope::BuiltinShadowMode::Module,
|
|
)
|
|
.0
|
|
.take_types()
|
|
.unwrap();
|
|
|
|
let found_path = find_path(&db, ItemInNs::Types(resolved), module);
|
|
|
|
assert_eq!(mod_path, found_path);
|
|
}
|
|
|
|
#[test]
|
|
fn same_module() {
|
|
let code = r#"
|
|
//- /main.rs
|
|
struct S;
|
|
<|>
|
|
"#;
|
|
check_found_path(code, "S");
|
|
}
|
|
|
|
#[test]
|
|
fn sub_module() {
|
|
let code = r#"
|
|
//- /main.rs
|
|
mod foo {
|
|
pub struct S;
|
|
}
|
|
<|>
|
|
"#;
|
|
check_found_path(code, "foo::S");
|
|
}
|
|
|
|
#[test]
|
|
fn same_crate() {
|
|
let code = r#"
|
|
//- /main.rs
|
|
mod foo;
|
|
struct S;
|
|
//- /foo.rs
|
|
<|>
|
|
"#;
|
|
check_found_path(code, "crate::S");
|
|
}
|
|
|
|
#[test]
|
|
fn different_crate() {
|
|
let code = r#"
|
|
//- /main.rs crate:main deps:std
|
|
<|>
|
|
//- /std.rs crate:std
|
|
pub struct S;
|
|
"#;
|
|
check_found_path(code, "std::S");
|
|
}
|
|
|
|
#[test]
|
|
fn same_crate_reexport() {
|
|
let code = r#"
|
|
//- /main.rs
|
|
mod bar {
|
|
mod foo { pub(super) struct S; }
|
|
pub(crate) use foo::*;
|
|
}
|
|
<|>
|
|
"#;
|
|
check_found_path(code, "bar::S");
|
|
}
|
|
}
|