mirror of
https://github.com/rust-lang/rust-analyzer.git
synced 2025-09-28 11:20:54 +00:00

This fixes two bugs: - completion for paths works again - we handle extern prelude shadowing more correctly
686 lines
13 KiB
Rust
686 lines
13 KiB
Rust
use std::sync::Arc;
|
|
|
|
use ra_db::SourceDatabase;
|
|
use test_utils::{assert_eq_text, covers};
|
|
|
|
use crate::{
|
|
ItemMap,
|
|
PersistentHirDatabase,
|
|
mock::MockDatabase,
|
|
module_tree::ModuleId,
|
|
};
|
|
use super::Resolution;
|
|
|
|
fn item_map(fixture: &str) -> (Arc<ItemMap>, ModuleId) {
|
|
let (db, pos) = MockDatabase::with_position(fixture);
|
|
let module = crate::source_binder::module_from_position(&db, pos).unwrap();
|
|
let krate = module.krate(&db).unwrap();
|
|
let module_id = module.module_id;
|
|
(db.item_map(krate), module_id)
|
|
}
|
|
|
|
fn check_module_item_map(map: &ItemMap, module_id: ModuleId, expected: &str) {
|
|
let mut lines = map[module_id]
|
|
.items
|
|
.iter()
|
|
.map(|(name, res)| format!("{}: {}", name, dump_resolution(res)))
|
|
.collect::<Vec<_>>();
|
|
lines.sort();
|
|
let actual = lines.join("\n");
|
|
let expected = expected.trim().lines().map(|it| it.trim()).collect::<Vec<_>>().join("\n");
|
|
assert_eq_text!(&expected, &actual);
|
|
|
|
fn dump_resolution(resolution: &Resolution) -> &'static str {
|
|
match (resolution.def.types.is_some(), resolution.def.values.is_some()) {
|
|
(true, true) => "t v",
|
|
(true, false) => "t",
|
|
(false, true) => "v",
|
|
(false, false) => "_",
|
|
}
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn item_map_smoke_test() {
|
|
let (item_map, module_id) = item_map(
|
|
"
|
|
//- /lib.rs
|
|
mod foo;
|
|
|
|
use crate::foo::bar::Baz;
|
|
<|>
|
|
|
|
//- /foo/mod.rs
|
|
pub mod bar;
|
|
|
|
//- /foo/bar.rs
|
|
pub struct Baz;
|
|
",
|
|
);
|
|
check_module_item_map(
|
|
&item_map,
|
|
module_id,
|
|
"
|
|
Baz: t v
|
|
foo: t
|
|
",
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn use_as() {
|
|
let (item_map, module_id) = item_map(
|
|
"
|
|
//- /lib.rs
|
|
mod foo;
|
|
|
|
use crate::foo::Baz as Foo;
|
|
<|>
|
|
|
|
//- /foo/mod.rs
|
|
pub struct Baz;
|
|
",
|
|
);
|
|
check_module_item_map(
|
|
&item_map,
|
|
module_id,
|
|
"
|
|
Foo: t v
|
|
foo: t
|
|
",
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn use_trees() {
|
|
let (item_map, module_id) = item_map(
|
|
"
|
|
//- /lib.rs
|
|
mod foo;
|
|
|
|
use crate::foo::bar::{Baz, Quux};
|
|
<|>
|
|
|
|
//- /foo/mod.rs
|
|
pub mod bar;
|
|
|
|
//- /foo/bar.rs
|
|
pub struct Baz;
|
|
pub enum Quux {};
|
|
",
|
|
);
|
|
check_module_item_map(
|
|
&item_map,
|
|
module_id,
|
|
"
|
|
Baz: t v
|
|
Quux: t
|
|
foo: t
|
|
",
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn re_exports() {
|
|
let (item_map, module_id) = item_map(
|
|
"
|
|
//- /lib.rs
|
|
mod foo;
|
|
|
|
use self::foo::Baz;
|
|
<|>
|
|
|
|
//- /foo/mod.rs
|
|
pub mod bar;
|
|
|
|
pub use self::bar::Baz;
|
|
|
|
//- /foo/bar.rs
|
|
pub struct Baz;
|
|
",
|
|
);
|
|
check_module_item_map(
|
|
&item_map,
|
|
module_id,
|
|
"
|
|
Baz: t v
|
|
foo: t
|
|
",
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn glob_1() {
|
|
let (item_map, module_id) = item_map(
|
|
"
|
|
//- /lib.rs
|
|
mod foo;
|
|
use foo::*;
|
|
<|>
|
|
|
|
//- /foo/mod.rs
|
|
pub mod bar;
|
|
pub use self::bar::Baz;
|
|
pub struct Foo;
|
|
|
|
//- /foo/bar.rs
|
|
pub struct Baz;
|
|
",
|
|
);
|
|
check_module_item_map(
|
|
&item_map,
|
|
module_id,
|
|
"
|
|
Baz: t v
|
|
Foo: t v
|
|
bar: t
|
|
foo: t
|
|
",
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn glob_2() {
|
|
let (item_map, module_id) = item_map(
|
|
"
|
|
//- /lib.rs
|
|
mod foo;
|
|
use foo::*;
|
|
<|>
|
|
|
|
//- /foo/mod.rs
|
|
pub mod bar;
|
|
pub use self::bar::*;
|
|
pub struct Foo;
|
|
|
|
//- /foo/bar.rs
|
|
pub struct Baz;
|
|
pub use super::*;
|
|
",
|
|
);
|
|
check_module_item_map(
|
|
&item_map,
|
|
module_id,
|
|
"
|
|
Baz: t v
|
|
Foo: t v
|
|
bar: t
|
|
foo: t
|
|
",
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn glob_enum() {
|
|
covers!(glob_enum);
|
|
let (item_map, module_id) = item_map(
|
|
"
|
|
//- /lib.rs
|
|
enum Foo {
|
|
Bar, Baz
|
|
}
|
|
use self::Foo::*;
|
|
<|>
|
|
",
|
|
);
|
|
check_module_item_map(
|
|
&item_map,
|
|
module_id,
|
|
"
|
|
Bar: t v
|
|
Baz: t v
|
|
Foo: t
|
|
",
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn glob_across_crates() {
|
|
covers!(glob_across_crates);
|
|
let mut db = MockDatabase::with_files(
|
|
"
|
|
//- /main.rs
|
|
use test_crate::*;
|
|
|
|
//- /lib.rs
|
|
pub struct Baz;
|
|
",
|
|
);
|
|
db.set_crate_graph_from_fixture(crate_graph! {
|
|
"main": ("/main.rs", ["test_crate"]),
|
|
"test_crate": ("/lib.rs", []),
|
|
});
|
|
let main_id = db.file_id_of("/main.rs");
|
|
|
|
let module = crate::source_binder::module_from_file_id(&db, main_id).unwrap();
|
|
let krate = module.krate(&db).unwrap();
|
|
let item_map = db.item_map(krate);
|
|
|
|
check_module_item_map(
|
|
&item_map,
|
|
module.module_id,
|
|
"
|
|
Baz: t v
|
|
",
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn module_resolution_works_for_non_standard_filenames() {
|
|
let mut db = MockDatabase::with_files(
|
|
"
|
|
//- /my_library.rs
|
|
mod foo;
|
|
use self::foo::Bar;
|
|
|
|
//- /foo/mod.rs
|
|
pub struct Bar;
|
|
",
|
|
);
|
|
db.set_crate_graph_from_fixture(crate_graph! {
|
|
"my_library": ("/my_library.rs", []),
|
|
});
|
|
let file_id = db.file_id_of("/my_library.rs");
|
|
|
|
let module = crate::source_binder::module_from_file_id(&db, file_id).unwrap();
|
|
let krate = module.krate(&db).unwrap();
|
|
let module_id = module.module_id;
|
|
let item_map = db.item_map(krate);
|
|
check_module_item_map(
|
|
&item_map,
|
|
module_id,
|
|
"
|
|
Bar: t v
|
|
foo: t
|
|
",
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn name_res_works_for_broken_modules() {
|
|
covers!(name_res_works_for_broken_modules);
|
|
let (item_map, module_id) = item_map(
|
|
"
|
|
//- /lib.rs
|
|
mod foo // no `;`, no body
|
|
|
|
use self::foo::Baz;
|
|
<|>
|
|
|
|
//- /foo/mod.rs
|
|
pub mod bar;
|
|
|
|
pub use self::bar::Baz;
|
|
|
|
//- /foo/bar.rs
|
|
pub struct Baz;
|
|
",
|
|
);
|
|
check_module_item_map(
|
|
&item_map,
|
|
module_id,
|
|
"
|
|
Baz: _
|
|
",
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn item_map_contains_items_from_expansions() {
|
|
let (item_map, module_id) = item_map(
|
|
"
|
|
//- /lib.rs
|
|
mod foo;
|
|
|
|
use crate::foo::bar::Baz;
|
|
<|>
|
|
|
|
//- /foo/mod.rs
|
|
pub mod bar;
|
|
|
|
//- /foo/bar.rs
|
|
salsa::query_group! {
|
|
trait Baz {}
|
|
}
|
|
",
|
|
);
|
|
check_module_item_map(
|
|
&item_map,
|
|
module_id,
|
|
"
|
|
Baz: t
|
|
foo: t
|
|
",
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn item_map_using_self() {
|
|
let (item_map, module_id) = item_map(
|
|
"
|
|
//- /lib.rs
|
|
mod foo;
|
|
use crate::foo::bar::Baz::{self};
|
|
<|>
|
|
//- /foo/mod.rs
|
|
pub mod bar;
|
|
//- /foo/bar.rs
|
|
pub struct Baz;
|
|
",
|
|
);
|
|
check_module_item_map(
|
|
&item_map,
|
|
module_id,
|
|
"
|
|
Baz: t v
|
|
foo: t
|
|
",
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn item_map_enum_importing() {
|
|
covers!(item_map_enum_importing);
|
|
let (item_map, module_id) = item_map(
|
|
"
|
|
//- /lib.rs
|
|
enum E { V }
|
|
use self::E::V;
|
|
<|>
|
|
",
|
|
);
|
|
check_module_item_map(
|
|
&item_map,
|
|
module_id,
|
|
"
|
|
E: t
|
|
V: t v
|
|
",
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn item_map_across_crates() {
|
|
let mut db = MockDatabase::with_files(
|
|
"
|
|
//- /main.rs
|
|
use test_crate::Baz;
|
|
|
|
//- /lib.rs
|
|
pub struct Baz;
|
|
",
|
|
);
|
|
db.set_crate_graph_from_fixture(crate_graph! {
|
|
"main": ("/main.rs", ["test_crate"]),
|
|
"test_crate": ("/lib.rs", []),
|
|
});
|
|
let main_id = db.file_id_of("/main.rs");
|
|
|
|
let module = crate::source_binder::module_from_file_id(&db, main_id).unwrap();
|
|
let krate = module.krate(&db).unwrap();
|
|
let item_map = db.item_map(krate);
|
|
|
|
check_module_item_map(
|
|
&item_map,
|
|
module.module_id,
|
|
"
|
|
Baz: t v
|
|
",
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn extern_crate_rename() {
|
|
let mut db = MockDatabase::with_files(
|
|
"
|
|
//- /main.rs
|
|
extern crate alloc as alloc_crate;
|
|
|
|
mod alloc;
|
|
mod sync;
|
|
|
|
//- /sync.rs
|
|
use alloc_crate::Arc;
|
|
|
|
//- /lib.rs
|
|
struct Arc;
|
|
",
|
|
);
|
|
db.set_crate_graph_from_fixture(crate_graph! {
|
|
"main": ("/main.rs", ["alloc"]),
|
|
"alloc": ("/lib.rs", []),
|
|
});
|
|
let sync_id = db.file_id_of("/sync.rs");
|
|
|
|
let module = crate::source_binder::module_from_file_id(&db, sync_id).unwrap();
|
|
let krate = module.krate(&db).unwrap();
|
|
let item_map = db.item_map(krate);
|
|
|
|
check_module_item_map(
|
|
&item_map,
|
|
module.module_id,
|
|
"
|
|
Arc: t v
|
|
",
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn import_across_source_roots() {
|
|
let mut db = MockDatabase::with_files(
|
|
"
|
|
//- /lib.rs
|
|
pub mod a {
|
|
pub mod b {
|
|
pub struct C;
|
|
}
|
|
}
|
|
|
|
//- root /main/
|
|
|
|
//- /main/main.rs
|
|
use test_crate::a::b::C;
|
|
",
|
|
);
|
|
db.set_crate_graph_from_fixture(crate_graph! {
|
|
"main": ("/main/main.rs", ["test_crate"]),
|
|
"test_crate": ("/lib.rs", []),
|
|
});
|
|
let main_id = db.file_id_of("/main/main.rs");
|
|
|
|
let module = crate::source_binder::module_from_file_id(&db, main_id).unwrap();
|
|
let krate = module.krate(&db).unwrap();
|
|
let item_map = db.item_map(krate);
|
|
|
|
check_module_item_map(
|
|
&item_map,
|
|
module.module_id,
|
|
"
|
|
C: t v
|
|
",
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn reexport_across_crates() {
|
|
let mut db = MockDatabase::with_files(
|
|
"
|
|
//- /main.rs
|
|
use test_crate::Baz;
|
|
|
|
//- /lib.rs
|
|
pub use foo::Baz;
|
|
|
|
mod foo;
|
|
|
|
//- /foo.rs
|
|
pub struct Baz;
|
|
",
|
|
);
|
|
db.set_crate_graph_from_fixture(crate_graph! {
|
|
"main": ("/main.rs", ["test_crate"]),
|
|
"test_crate": ("/lib.rs", []),
|
|
});
|
|
let main_id = db.file_id_of("/main.rs");
|
|
|
|
let module = crate::source_binder::module_from_file_id(&db, main_id).unwrap();
|
|
let krate = module.krate(&db).unwrap();
|
|
let item_map = db.item_map(krate);
|
|
|
|
check_module_item_map(
|
|
&item_map,
|
|
module.module_id,
|
|
"
|
|
Baz: t v
|
|
",
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn values_dont_shadow_extern_crates() {
|
|
let mut db = MockDatabase::with_files(
|
|
"
|
|
//- /main.rs
|
|
fn foo() {}
|
|
use foo::Bar;
|
|
|
|
//- /foo/lib.rs
|
|
pub struct Bar;
|
|
",
|
|
);
|
|
db.set_crate_graph_from_fixture(crate_graph! {
|
|
"main": ("/main.rs", ["foo"]),
|
|
"foo": ("/foo/lib.rs", []),
|
|
});
|
|
let main_id = db.file_id_of("/main.rs");
|
|
|
|
let module = crate::source_binder::module_from_file_id(&db, main_id).unwrap();
|
|
let krate = module.krate(&db).unwrap();
|
|
let item_map = db.item_map(krate);
|
|
|
|
check_module_item_map(
|
|
&item_map,
|
|
module.module_id,
|
|
"
|
|
Bar: t v
|
|
foo: v
|
|
",
|
|
);
|
|
}
|
|
|
|
fn check_item_map_is_not_recomputed(initial: &str, file_change: &str) {
|
|
let (mut db, pos) = MockDatabase::with_position(initial);
|
|
let module = crate::source_binder::module_from_file_id(&db, pos.file_id).unwrap();
|
|
let krate = module.krate(&db).unwrap();
|
|
{
|
|
let events = db.log_executed(|| {
|
|
db.item_map(krate);
|
|
});
|
|
assert!(format!("{:?}", events).contains("item_map"))
|
|
}
|
|
db.set_file_text(pos.file_id, Arc::new(file_change.to_string()));
|
|
|
|
{
|
|
let events = db.log_executed(|| {
|
|
db.item_map(krate);
|
|
});
|
|
assert!(!format!("{:?}", events).contains("item_map"), "{:#?}", events)
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn typing_inside_a_function_should_not_invalidate_item_map() {
|
|
check_item_map_is_not_recomputed(
|
|
"
|
|
//- /lib.rs
|
|
mod foo;<|>
|
|
|
|
use crate::foo::bar::Baz;
|
|
|
|
fn foo() -> i32 {
|
|
1 + 1
|
|
}
|
|
//- /foo/mod.rs
|
|
pub mod bar;
|
|
|
|
//- /foo/bar.rs
|
|
pub struct Baz;
|
|
",
|
|
"
|
|
mod foo;
|
|
|
|
use crate::foo::bar::Baz;
|
|
|
|
fn foo() -> i32 { 92 }
|
|
",
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn adding_inner_items_should_not_invalidate_item_map() {
|
|
check_item_map_is_not_recomputed(
|
|
"
|
|
//- /lib.rs
|
|
struct S { a: i32}
|
|
enum E { A }
|
|
trait T {
|
|
fn a() {}
|
|
}
|
|
mod foo;<|>
|
|
impl S {
|
|
fn a() {}
|
|
}
|
|
use crate::foo::bar::Baz;
|
|
//- /foo/mod.rs
|
|
pub mod bar;
|
|
|
|
//- /foo/bar.rs
|
|
pub struct Baz;
|
|
",
|
|
"
|
|
struct S { a: i32, b: () }
|
|
enum E { A, B }
|
|
trait T {
|
|
fn a() {}
|
|
fn b() {}
|
|
}
|
|
mod foo;<|>
|
|
impl S {
|
|
fn a() {}
|
|
fn b() {}
|
|
}
|
|
use crate::foo::bar::Baz;
|
|
",
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn typing_inside_a_function_inside_a_macro_should_not_invalidate_item_map() {
|
|
check_item_map_is_not_recomputed(
|
|
"
|
|
//- /lib.rs
|
|
mod foo;
|
|
|
|
use crate::foo::bar::Baz;
|
|
|
|
//- /foo/mod.rs
|
|
pub mod bar;
|
|
|
|
//- /foo/bar.rs
|
|
<|>
|
|
salsa::query_group! {
|
|
trait Baz {
|
|
fn foo() -> i32 { 1 + 1 }
|
|
}
|
|
}
|
|
",
|
|
"
|
|
salsa::query_group! {
|
|
trait Baz {
|
|
fn foo() -> i32 { 92 }
|
|
}
|
|
}
|
|
",
|
|
);
|
|
}
|