rust-analyzer/crates/ra_hir_def/src/find_path.rs
Florian Diebold 2c50f996b6 more WIP
2020-01-11 23:33:04 +01:00

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");
}
}