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, 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::>(); lines.sort(); let actual = lines.join("\n"); let expected = expected.trim().lines().map(|it| it.trim()).collect::>().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 edition_2015_imports() { let mut db = MockDatabase::with_files( " //- /main.rs mod foo; mod bar; //- /bar.rs struct Bar; //- /foo.rs use bar::Bar; use other_crate::FromLib; //- /lib.rs struct FromLib; ", ); db.set_crate_graph_from_fixture(crate_graph! { "main": ("/main.rs", "2015", ["other_crate"]), "other_crate": ("/lib.rs", "2018", []), }); let foo_id = db.file_id_of("/foo.rs"); let module = crate::source_binder::module_from_file_id(&db, foo_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 FromLib: 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 std_prelude() { covers!(std_prelude); let mut db = MockDatabase::with_files( " //- /main.rs use Foo::*; //- /lib.rs mod prelude; #[prelude_import] use prelude::*; //- /prelude.rs pub enum Foo { Bar, 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, " Bar: t v Baz: t v ", ); } #[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_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 extern_crate_rename_2015_edition() { 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", "2015", ["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 } } } ", ); }