From 6fba51c5fc05264abcbf971dcf28142746588d74 Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Sun, 3 Nov 2019 23:35:48 +0300 Subject: move crate_def_map tests to hir_def --- crates/ra_hir_def/Cargo.toml | 4 + crates/ra_hir_def/src/nameres.rs | 3 + crates/ra_hir_def/src/nameres/tests.rs | 522 ++++++++++++++ crates/ra_hir_def/src/nameres/tests/globs.rs | 114 +++ crates/ra_hir_def/src/nameres/tests/incremental.rs | 133 ++++ crates/ra_hir_def/src/nameres/tests/macros.rs | 602 ++++++++++++++++ .../ra_hir_def/src/nameres/tests/mod_resolution.rs | 782 +++++++++++++++++++++ crates/ra_hir_def/src/nameres/tests/primitives.rs | 24 + crates/ra_hir_def/src/test_db.rs | 36 +- 9 files changed, 2219 insertions(+), 1 deletion(-) create mode 100644 crates/ra_hir_def/src/nameres/tests.rs create mode 100644 crates/ra_hir_def/src/nameres/tests/globs.rs create mode 100644 crates/ra_hir_def/src/nameres/tests/incremental.rs create mode 100644 crates/ra_hir_def/src/nameres/tests/macros.rs create mode 100644 crates/ra_hir_def/src/nameres/tests/mod_resolution.rs create mode 100644 crates/ra_hir_def/src/nameres/tests/primitives.rs (limited to 'crates/ra_hir_def') diff --git a/crates/ra_hir_def/Cargo.toml b/crates/ra_hir_def/Cargo.toml index 746c907e8..15055db64 100644 --- a/crates/ra_hir_def/Cargo.toml +++ b/crates/ra_hir_def/Cargo.toml @@ -19,3 +19,7 @@ test_utils = { path = "../test_utils" } mbe = { path = "../ra_mbe", package = "ra_mbe" } ra_cfg = { path = "../ra_cfg" } tt = { path = "../ra_tt", package = "ra_tt" } + +[dev-dependencies] +insta = "0.12.0" + diff --git a/crates/ra_hir_def/src/nameres.rs b/crates/ra_hir_def/src/nameres.rs index db59344aa..b3640da3d 100644 --- a/crates/ra_hir_def/src/nameres.rs +++ b/crates/ra_hir_def/src/nameres.rs @@ -6,6 +6,9 @@ pub mod per_ns; pub mod collector; pub mod mod_resolution; +#[cfg(test)] +mod tests; + use std::sync::Arc; use hir_expand::{diagnostics::DiagnosticSink, name::Name, MacroDefId}; diff --git a/crates/ra_hir_def/src/nameres/tests.rs b/crates/ra_hir_def/src/nameres/tests.rs new file mode 100644 index 000000000..f9a8edd43 --- /dev/null +++ b/crates/ra_hir_def/src/nameres/tests.rs @@ -0,0 +1,522 @@ +mod globs; +mod incremental; +mod macros; +mod mod_resolution; +mod primitives; + +use std::sync::Arc; + +use insta::assert_snapshot; +use ra_db::{fixture::WithFixture, SourceDatabase}; +// use test_utils::covers; + +use crate::{db::DefDatabase2, nameres::*, test_db::TestDB, CrateModuleId}; + +fn def_map(fixtute: &str) -> String { + let dm = compute_crate_def_map(fixtute); + render_crate_def_map(&dm) +} + +fn compute_crate_def_map(fixture: &str) -> Arc { + let db = TestDB::with_files(fixture); + let krate = db.crate_graph().iter().next().unwrap(); + db.crate_def_map(krate) +} + +fn render_crate_def_map(map: &CrateDefMap) -> String { + let mut buf = String::new(); + go(&mut buf, map, "\ncrate", map.root()); + return buf.trim().to_string(); + + fn go(buf: &mut String, map: &CrateDefMap, path: &str, module: CrateModuleId) { + *buf += path; + *buf += "\n"; + + let mut entries = map.modules[module] + .scope + .items + .iter() + .map(|(name, res)| (name, res.def)) + .collect::>(); + entries.sort_by_key(|(name, _)| *name); + + for (name, res) in entries { + *buf += &format!("{}:", name); + + if res.types.is_some() { + *buf += " t"; + } + if res.values.is_some() { + *buf += " v"; + } + if res.macros.is_some() { + *buf += " m"; + } + if res.is_none() { + *buf += " _"; + } + + *buf += "\n"; + } + + for (name, child) in map.modules[module].children.iter() { + let path = path.to_string() + &format!("::{}", name); + go(buf, map, &path, *child); + } + } +} + +#[test] +fn crate_def_map_smoke_test() { + let map = def_map( + " + //- /lib.rs + mod foo; + struct S; + use crate::foo::bar::E; + use self::E::V; + + //- /foo/mod.rs + pub mod bar; + fn f() {} + + //- /foo/bar.rs + pub struct Baz; + enum E { V } + ", + ); + assert_snapshot!(map, @r###" + ⋮crate + ⋮E: t + ⋮S: t v + ⋮V: t v + ⋮foo: t + ⋮ + ⋮crate::foo + ⋮bar: t + ⋮f: v + ⋮ + ⋮crate::foo::bar + ⋮Baz: t v + ⋮E: t + "###) +} + +#[test] +fn bogus_paths() { + // covers!(bogus_paths); + let map = def_map( + " + //- /lib.rs + mod foo; + struct S; + use self; + + //- /foo/mod.rs + use super; + use crate; + + ", + ); + assert_snapshot!(map, @r###" + ⋮crate + ⋮S: t v + ⋮foo: t + ⋮ + ⋮crate::foo + "### + ) +} + +#[test] +fn use_as() { + let map = def_map( + " + //- /lib.rs + mod foo; + + use crate::foo::Baz as Foo; + + //- /foo/mod.rs + pub struct Baz; + ", + ); + assert_snapshot!(map, + @r###" + ⋮crate + ⋮Foo: t v + ⋮foo: t + ⋮ + ⋮crate::foo + ⋮Baz: t v + "### + ); +} + +#[test] +fn use_trees() { + let map = def_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 {}; + ", + ); + assert_snapshot!(map, @r###" + ⋮crate + ⋮Baz: t v + ⋮Quux: t + ⋮foo: t + ⋮ + ⋮crate::foo + ⋮bar: t + ⋮ + ⋮crate::foo::bar + ⋮Baz: t v + ⋮Quux: t + "###); +} + +#[test] +fn re_exports() { + let map = def_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; + ", + ); + assert_snapshot!(map, @r###" + ⋮crate + ⋮Baz: t v + ⋮foo: t + ⋮ + ⋮crate::foo + ⋮Baz: t v + ⋮bar: t + ⋮ + ⋮crate::foo::bar + ⋮Baz: t v + "###); +} + +#[test] +fn std_prelude() { + // covers!(std_prelude); + let map = def_map( + " + //- /main.rs crate:main deps:test_crate + use Foo::*; + + //- /lib.rs crate:test_crate + mod prelude; + #[prelude_import] + use prelude::*; + + //- /prelude.rs + pub enum Foo { Bar, Baz }; + ", + ); + assert_snapshot!(map, @r###" + ⋮crate + ⋮Bar: t v + ⋮Baz: t v + "###); +} + +#[test] +fn can_import_enum_variant() { + // covers!(can_import_enum_variant); + let map = def_map( + " + //- /lib.rs + enum E { V } + use self::E::V; + ", + ); + assert_snapshot!(map, @r###" + ⋮crate + ⋮E: t + ⋮V: t v + "### + ); +} + +#[test] +fn edition_2015_imports() { + let map = def_map( + " + //- /main.rs crate:main deps:other_crate edition:2015 + mod foo; + mod bar; + + //- /bar.rs + struct Bar; + + //- /foo.rs + use bar::Bar; + use other_crate::FromLib; + + //- /lib.rs crate:other_crate edition:2018 + struct FromLib; + ", + ); + + assert_snapshot!(map, @r###" + ⋮crate + ⋮bar: t + ⋮foo: t + ⋮ + ⋮crate::bar + ⋮Bar: t v + ⋮ + ⋮crate::foo + ⋮Bar: t v + ⋮FromLib: t v + "###); +} + +#[test] +fn item_map_using_self() { + let map = def_map( + " + //- /lib.rs + mod foo; + use crate::foo::bar::Baz::{self}; + //- /foo/mod.rs + pub mod bar; + //- /foo/bar.rs + pub struct Baz; + ", + ); + assert_snapshot!(map, @r###" + ⋮crate + ⋮Baz: t v + ⋮foo: t + ⋮ + ⋮crate::foo + ⋮bar: t + ⋮ + ⋮crate::foo::bar + ⋮Baz: t v + "###); +} + +#[test] +fn item_map_across_crates() { + let map = def_map( + " + //- /main.rs crate:main deps:test_crate + use test_crate::Baz; + + //- /lib.rs crate:test_crate + pub struct Baz; + ", + ); + + assert_snapshot!(map, @r###" + ⋮crate + ⋮Baz: t v + "###); +} + +#[test] +fn extern_crate_rename() { + let map = def_map( + " + //- /main.rs crate:main deps:alloc + extern crate alloc as alloc_crate; + + mod alloc; + mod sync; + + //- /sync.rs + use alloc_crate::Arc; + + //- /lib.rs crate:alloc + struct Arc; + ", + ); + + assert_snapshot!(map, @r###" + ⋮crate + ⋮alloc_crate: t + ⋮sync: t + ⋮ + ⋮crate::sync + ⋮Arc: t v + "###); +} + +#[test] +fn extern_crate_rename_2015_edition() { + let map = def_map( + " + //- /main.rs crate:main deps:alloc edition:2015 + extern crate alloc as alloc_crate; + + mod alloc; + mod sync; + + //- /sync.rs + use alloc_crate::Arc; + + //- /lib.rs crate:alloc + struct Arc; + ", + ); + + assert_snapshot!(map, + @r###" + ⋮crate + ⋮alloc_crate: t + ⋮sync: t + ⋮ + ⋮crate::sync + ⋮Arc: t v + "### + ); +} + +#[test] +fn import_across_source_roots() { + let map = def_map( + " + //- /main.rs crate:main deps:test_crate + use test_crate::a::b::C; + + //- root /test_crate/ + + //- /test_crate/lib.rs crate:test_crate + pub mod a { + pub mod b { + pub struct C; + } + } + + ", + ); + + assert_snapshot!(map, @r###" + ⋮crate + ⋮C: t v + "###); +} + +#[test] +fn reexport_across_crates() { + let map = def_map( + " + //- /main.rs crate:main deps:test_crate + use test_crate::Baz; + + //- /lib.rs crate:test_crate + pub use foo::Baz; + + mod foo; + + //- /foo.rs + pub struct Baz; + ", + ); + + assert_snapshot!(map, @r###" + ⋮crate + ⋮Baz: t v + "###); +} + +#[test] +fn values_dont_shadow_extern_crates() { + let map = def_map( + " + //- /main.rs crate:main deps:foo + fn foo() {} + use foo::Bar; + + //- /foo/lib.rs crate:foo + pub struct Bar; + ", + ); + + assert_snapshot!(map, @r###" + ⋮crate + ⋮Bar: t v + ⋮foo: v + "###); +} + +#[test] +fn cfg_not_test() { + let map = def_map( + r#" + //- /main.rs crate:main deps:std + use {Foo, Bar, Baz}; + + //- /lib.rs crate:std + #[prelude_import] + pub use self::prelude::*; + mod prelude { + #[cfg(test)] + pub struct Foo; + #[cfg(not(test))] + pub struct Bar; + #[cfg(all(not(any()), feature = "foo", feature = "bar", opt = "42"))] + pub struct Baz; + } + "#, + ); + + assert_snapshot!(map, @r###" + ⋮crate + ⋮Bar: t v + ⋮Baz: _ + ⋮Foo: _ + "###); +} + +#[test] +fn cfg_test() { + let map = def_map( + r#" + //- /main.rs crate:main deps:std + use {Foo, Bar, Baz}; + + //- /lib.rs crate:std cfg:test,feature=foo,feature=bar,opt=42 + #[prelude_import] + pub use self::prelude::*; + mod prelude { + #[cfg(test)] + pub struct Foo; + #[cfg(not(test))] + pub struct Bar; + #[cfg(all(not(any()), feature = "foo", feature = "bar", opt = "42"))] + pub struct Baz; + } + "#, + ); + + assert_snapshot!(map, @r###" + ⋮crate + ⋮Bar: _ + ⋮Baz: t v + ⋮Foo: t v + "###); +} diff --git a/crates/ra_hir_def/src/nameres/tests/globs.rs b/crates/ra_hir_def/src/nameres/tests/globs.rs new file mode 100644 index 000000000..cf4a2a851 --- /dev/null +++ b/crates/ra_hir_def/src/nameres/tests/globs.rs @@ -0,0 +1,114 @@ +use super::*; + +#[test] +fn glob_1() { + let map = def_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; + ", + ); + assert_snapshot!(map, @r###" + ⋮crate + ⋮Baz: t v + ⋮Foo: t v + ⋮bar: t + ⋮foo: t + ⋮ + ⋮crate::foo + ⋮Baz: t v + ⋮Foo: t v + ⋮bar: t + ⋮ + ⋮crate::foo::bar + ⋮Baz: t v + "### + ); +} + +#[test] +fn glob_2() { + let map = def_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::*; + ", + ); + assert_snapshot!(map, @r###" + ⋮crate + ⋮Baz: t v + ⋮Foo: t v + ⋮bar: t + ⋮foo: t + ⋮ + ⋮crate::foo + ⋮Baz: t v + ⋮Foo: t v + ⋮bar: t + ⋮ + ⋮crate::foo::bar + ⋮Baz: t v + ⋮Foo: t v + ⋮bar: t + "### + ); +} + +#[test] +fn glob_across_crates() { + // covers!(glob_across_crates); + let map = def_map( + " + //- /main.rs crate:main deps:test_crate + use test_crate::*; + + //- /lib.rs crate:test_crate + pub struct Baz; + ", + ); + assert_snapshot!(map, @r###" + ⋮crate + ⋮Baz: t v + "### + ); +} + +#[test] +fn glob_enum() { + // covers!(glob_enum); + let map = def_map( + " + //- /lib.rs + enum Foo { + Bar, Baz + } + use self::Foo::*; + ", + ); + assert_snapshot!(map, @r###" + ⋮crate + ⋮Bar: t v + ⋮Baz: t v + ⋮Foo: t + "### + ); +} diff --git a/crates/ra_hir_def/src/nameres/tests/incremental.rs b/crates/ra_hir_def/src/nameres/tests/incremental.rs new file mode 100644 index 000000000..80dcec62f --- /dev/null +++ b/crates/ra_hir_def/src/nameres/tests/incremental.rs @@ -0,0 +1,133 @@ +use std::sync::Arc; + +use ra_db::{SourceDatabase, SourceDatabaseExt}; + +use super::*; + +fn check_def_map_is_not_recomputed(initial: &str, file_change: &str) { + let (mut db, pos) = TestDB::with_position(initial); + let krate = db.crate_graph().iter().next().unwrap(); + { + let events = db.log_executed(|| { + db.crate_def_map(krate); + }); + assert!(format!("{:?}", events).contains("crate_def_map"), "{:#?}", events) + } + db.set_file_text(pos.file_id, Arc::new(file_change.to_string())); + + { + let events = db.log_executed(|| { + db.crate_def_map(krate); + }); + assert!(!format!("{:?}", events).contains("crate_def_map"), "{:#?}", events) + } +} + +#[test] +fn typing_inside_a_function_should_not_invalidate_def_map() { + check_def_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_def_map() { + check_def_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_macro_should_not_invalidate_def_map() { + let (mut db, pos) = TestDB::with_position( + " + //- /lib.rs + macro_rules! m { + ($ident:ident) => { + fn f() { + $ident + $ident; + }; + } + } + mod foo; + + //- /foo/mod.rs + pub mod bar; + + //- /foo/bar.rs + <|> + m!(X); + ", + ); + let krate = db.crate_graph().iter().next().unwrap(); + { + let events = db.log_executed(|| { + let crate_def_map = db.crate_def_map(krate); + let (_, module_data) = crate_def_map.modules.iter().last().unwrap(); + assert_eq!(module_data.scope.items.len(), 1); + }); + assert!(format!("{:?}", events).contains("crate_def_map"), "{:#?}", events) + } + db.set_file_text(pos.file_id, Arc::new("m!(Y);".to_string())); + + { + let events = db.log_executed(|| { + let crate_def_map = db.crate_def_map(krate); + let (_, module_data) = crate_def_map.modules.iter().last().unwrap(); + assert_eq!(module_data.scope.items.len(), 1); + }); + assert!(!format!("{:?}", events).contains("crate_def_map"), "{:#?}", events) + } +} diff --git a/crates/ra_hir_def/src/nameres/tests/macros.rs b/crates/ra_hir_def/src/nameres/tests/macros.rs new file mode 100644 index 000000000..9bb3895ad --- /dev/null +++ b/crates/ra_hir_def/src/nameres/tests/macros.rs @@ -0,0 +1,602 @@ +use super::*; + +#[test] +fn macro_rules_are_globally_visible() { + let map = def_map( + " + //- /lib.rs + macro_rules! structs { + ($($i:ident),*) => { + $(struct $i { field: u32 } )* + } + } + structs!(Foo); + mod nested; + + //- /nested.rs + structs!(Bar, Baz); + ", + ); + assert_snapshot!(map, @r###" + ⋮crate + ⋮Foo: t v + ⋮nested: t + ⋮ + ⋮crate::nested + ⋮Bar: t v + ⋮Baz: t v + "###); +} + +#[test] +fn macro_rules_can_define_modules() { + let map = def_map( + " + //- /lib.rs + macro_rules! m { + ($name:ident) => { mod $name; } + } + m!(n1); + + mod m { + m!(n3) + } + + //- /n1.rs + m!(n2) + //- /n1/n2.rs + struct X; + //- /m/n3.rs + struct Y; + ", + ); + assert_snapshot!(map, @r###" + crate + m: t + n1: t + + crate::m + n3: t + + crate::m::n3 + Y: t v + + crate::n1 + n2: t + + crate::n1::n2 + X: t v + "###); +} + +#[test] +fn macro_rules_from_other_crates_are_visible() { + let map = def_map( + " + //- /main.rs crate:main deps:foo + foo::structs!(Foo, Bar) + mod bar; + + //- /bar.rs + use crate::*; + + //- /lib.rs crate:foo + #[macro_export] + macro_rules! structs { + ($($i:ident),*) => { + $(struct $i { field: u32 } )* + } + } + ", + ); + assert_snapshot!(map, @r###" + ⋮crate + ⋮Bar: t v + ⋮Foo: t v + ⋮bar: t + ⋮ + ⋮crate::bar + ⋮Bar: t v + ⋮Foo: t v + ⋮bar: t + "###); +} + +#[test] +fn macro_rules_export_with_local_inner_macros_are_visible() { + let map = def_map( + " + //- /main.rs crate:main deps:foo + foo::structs!(Foo, Bar) + mod bar; + + //- /bar.rs + use crate::*; + + //- /lib.rs crate:foo + #[macro_export(local_inner_macros)] + macro_rules! structs { + ($($i:ident),*) => { + $(struct $i { field: u32 } )* + } + } + ", + ); + assert_snapshot!(map, @r###" + ⋮crate + ⋮Bar: t v + ⋮Foo: t v + ⋮bar: t + ⋮ + ⋮crate::bar + ⋮Bar: t v + ⋮Foo: t v + ⋮bar: t + "###); +} + +#[test] +fn unexpanded_macro_should_expand_by_fixedpoint_loop() { + let map = def_map( + " + //- /main.rs crate:main deps:foo + macro_rules! baz { + () => { + use foo::bar; + } + } + + foo!(); + bar!(); + baz!(); + + //- /lib.rs crate:foo + #[macro_export] + macro_rules! foo { + () => { + struct Foo { field: u32 } + } + } + #[macro_export] + macro_rules! bar { + () => { + use foo::foo; + } + } + ", + ); + assert_snapshot!(map, @r###" + ⋮crate + ⋮Foo: t v + ⋮bar: m + ⋮foo: m + "###); +} + +#[test] +fn macro_rules_from_other_crates_are_visible_with_macro_use() { + // covers!(macro_rules_from_other_crates_are_visible_with_macro_use); + let map = def_map( + " + //- /main.rs crate:main deps:foo + structs!(Foo); + structs_priv!(Bar); + structs_not_exported!(MacroNotResolved1); + crate::structs!(MacroNotResolved2); + + mod bar; + + #[macro_use] + extern crate foo; + + //- /bar.rs + structs!(Baz); + crate::structs!(MacroNotResolved3); + + //- /lib.rs crate:foo + #[macro_export] + macro_rules! structs { + ($i:ident) => { struct $i; } + } + + macro_rules! structs_not_exported { + ($i:ident) => { struct $i; } + } + + mod priv_mod { + #[macro_export] + macro_rules! structs_priv { + ($i:ident) => { struct $i; } + } + } + ", + ); + assert_snapshot!(map, @r###" + ⋮crate + ⋮Bar: t v + ⋮Foo: t v + ⋮bar: t + ⋮foo: t + ⋮ + ⋮crate::bar + ⋮Baz: t v + "###); +} + +#[test] +fn prelude_is_macro_use() { + // covers!(prelude_is_macro_use); + let map = def_map( + " + //- /main.rs crate:main deps:foo + structs!(Foo); + structs_priv!(Bar); + structs_outside!(Out); + crate::structs!(MacroNotResolved2); + + mod bar; + + //- /bar.rs + structs!(Baz); + crate::structs!(MacroNotResolved3); + + //- /lib.rs crate:foo + #[prelude_import] + use self::prelude::*; + + mod prelude { + #[macro_export] + macro_rules! structs { + ($i:ident) => { struct $i; } + } + + mod priv_mod { + #[macro_export] + macro_rules! structs_priv { + ($i:ident) => { struct $i; } + } + } + } + + #[macro_export] + macro_rules! structs_outside { + ($i:ident) => { struct $i; } + } + ", + ); + assert_snapshot!(map, @r###" + ⋮crate + ⋮Bar: t v + ⋮Foo: t v + ⋮Out: t v + ⋮bar: t + ⋮ + ⋮crate::bar + ⋮Baz: t v + "###); +} + +#[test] +fn prelude_cycle() { + let map = def_map( + " + //- /lib.rs + #[prelude_import] + use self::prelude::*; + + declare_mod!(); + + mod prelude { + macro_rules! declare_mod { + () => (mod foo {}) + } + } + ", + ); + assert_snapshot!(map, @r###" + ⋮crate + ⋮prelude: t + ⋮ + ⋮crate::prelude + "###); +} + +#[test] +fn plain_macros_are_legacy_textual_scoped() { + let map = def_map( + r#" + //- /main.rs + mod m1; + bar!(NotFoundNotMacroUse); + + mod m2 { + foo!(NotFoundBeforeInside2); + } + + macro_rules! foo { + ($x:ident) => { struct $x; } + } + foo!(Ok); + + mod m3; + foo!(OkShadowStop); + bar!(NotFoundMacroUseStop); + + #[macro_use] + mod m5 { + #[macro_use] + mod m6 { + macro_rules! foo { + ($x:ident) => { fn $x() {} } + } + } + } + foo!(ok_double_macro_use_shadow); + + baz!(NotFoundBefore); + #[macro_use] + mod m7 { + macro_rules! baz { + ($x:ident) => { struct $x; } + } + } + baz!(OkAfter); + + //- /m1.rs + foo!(NotFoundBeforeInside1); + macro_rules! bar { + ($x:ident) => { struct $x; } + } + + //- /m3/mod.rs + foo!(OkAfterInside); + macro_rules! foo { + ($x:ident) => { fn $x() {} } + } + foo!(ok_shadow); + + #[macro_use] + mod m4; + bar!(OkMacroUse); + + //- /m3/m4.rs + foo!(ok_shadow_deep); + macro_rules! bar { + ($x:ident) => { struct $x; } + } + "#, + ); + assert_snapshot!(map, @r###" + ⋮crate + ⋮Ok: t v + ⋮OkAfter: t v + ⋮OkShadowStop: t v + ⋮m1: t + ⋮m2: t + ⋮m3: t + ⋮m5: t + ⋮m7: t + ⋮ok_double_macro_use_shadow: v + ⋮ + ⋮crate::m7 + ⋮ + ⋮crate::m1 + ⋮ + ⋮crate::m5 + ⋮m6: t + ⋮ + ⋮crate::m5::m6 + ⋮ + ⋮crate::m2 + ⋮ + ⋮crate::m3 + ⋮OkAfterInside: t v + ⋮OkMacroUse: t v + ⋮m4: t + ⋮ok_shadow: v + ⋮ + ⋮crate::m3::m4 + ⋮ok_shadow_deep: v + "###); +} + +#[test] +fn type_value_macro_live_in_different_scopes() { + let map = def_map( + " + //- /main.rs + #[macro_export] + macro_rules! foo { + ($x:ident) => { type $x = (); } + } + + foo!(foo); + use foo as bar; + + use self::foo as baz; + fn baz() {} + ", + ); + assert_snapshot!(map, @r###" + ⋮crate + ⋮bar: t m + ⋮baz: t v m + ⋮foo: t m + "###); +} + +#[test] +fn macro_use_can_be_aliased() { + let map = def_map( + " + //- /main.rs crate:main deps:foo + #[macro_use] + extern crate foo; + + foo!(Direct); + bar!(Alias); + + //- /lib.rs crate:foo + use crate::foo as bar; + + mod m { + #[macro_export] + macro_rules! foo { + ($x:ident) => { struct $x; } + } + } + ", + ); + assert_snapshot!(map, @r###" + ⋮crate + ⋮Alias: t v + ⋮Direct: t v + ⋮foo: t + "###); +} + +#[test] +fn path_qualified_macros() { + let map = def_map( + " + //- /main.rs + macro_rules! foo { + ($x:ident) => { struct $x; } + } + + crate::foo!(NotResolved); + + crate::bar!(OkCrate); + bar!(OkPlain); + alias1!(NotHere); + m::alias1!(OkAliasPlain); + m::alias2!(OkAliasSuper); + m::alias3!(OkAliasCrate); + not_found!(NotFound); + + mod m { + #[macro_export] + macro_rules! bar { + ($x:ident) => { struct $x; } + } + + pub use bar as alias1; + pub use super::bar as alias2; + pub use crate::bar as alias3; + pub use self::bar as not_found; + } + ", + ); + assert_snapshot!(map, @r###" + ⋮crate + ⋮OkAliasCrate: t v + ⋮OkAliasPlain: t v + ⋮OkAliasSuper: t v + ⋮OkCrate: t v + ⋮OkPlain: t v + ⋮bar: m + ⋮m: t + ⋮ + ⋮crate::m + ⋮alias1: m + ⋮alias2: m + ⋮alias3: m + ⋮not_found: _ + "###); +} + +#[test] +fn macro_dollar_crate_is_correct_in_item() { + // covers!(macro_dollar_crate_self); + // covers!(macro_dollar_crate_other); + let map = def_map( + " + //- /main.rs crate:main deps:foo + #[macro_use] + extern crate foo; + + #[macro_use] + mod m { + macro_rules! current { + () => { + use $crate::Foo as FooSelf; + } + } + } + + struct Foo; + + current!(); + not_current1!(); + foo::not_current2!(); + + //- /lib.rs crate:foo + mod m { + #[macro_export] + macro_rules! not_current1 { + () => { + use $crate::Bar; + } + } + } + + #[macro_export] + macro_rules! not_current2 { + () => { + use $crate::Baz; + } + } + + struct Bar; + struct Baz; + ", + ); + assert_snapshot!(map, @r###" + ⋮crate + ⋮Bar: t v + ⋮Baz: t v + ⋮Foo: t v + ⋮FooSelf: t v + ⋮foo: t + ⋮m: t + ⋮ + ⋮crate::m + "###); +} + +#[test] +fn macro_dollar_crate_is_correct_in_indirect_deps() { + // covers!(macro_dollar_crate_other); + // From std + let map = def_map( + r#" + //- /main.rs crate:main deps:std + foo!(); + + //- /std.rs crate:std deps:core + #[prelude_import] + use self::prelude::*; + + pub use core::foo; + + mod prelude {} + + #[macro_use] + mod std_macros; + + //- /core.rs crate:core + #[macro_export] + macro_rules! foo { + () => { + use $crate::bar; + } + } + + pub struct bar; + "#, + ); + assert_snapshot!(map, @r###" + ⋮crate + ⋮bar: t v + "###); +} diff --git a/crates/ra_hir_def/src/nameres/tests/mod_resolution.rs b/crates/ra_hir_def/src/nameres/tests/mod_resolution.rs new file mode 100644 index 000000000..8d804a63e --- /dev/null +++ b/crates/ra_hir_def/src/nameres/tests/mod_resolution.rs @@ -0,0 +1,782 @@ +use super::*; + +#[test] +fn name_res_works_for_broken_modules() { + // covers!(name_res_works_for_broken_modules); + let map = def_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; + ", + ); + assert_snapshot!(map, @r###" + ⋮crate + ⋮Baz: _ + "###); +} + +#[test] +fn nested_module_resolution() { + let map = def_map( + " + //- /lib.rs + mod n1; + + //- /n1.rs + mod n2; + + //- /n1/n2.rs + struct X; + ", + ); + + assert_snapshot!(map, @r###" + ⋮crate + ⋮n1: t + ⋮ + ⋮crate::n1 + ⋮n2: t + ⋮ + ⋮crate::n1::n2 + ⋮X: t v + "###); +} + +#[test] +fn module_resolution_works_for_non_standard_filenames() { + let map = def_map( + " + //- /my_library.rs crate:my_library + mod foo; + use self::foo::Bar; + + //- /foo/mod.rs + pub struct Bar; + ", + ); + + assert_snapshot!(map, @r###" + ⋮crate + ⋮Bar: t v + ⋮foo: t + ⋮ + ⋮crate::foo + ⋮Bar: t v + "###); +} + +#[test] +fn module_resolution_works_for_raw_modules() { + let map = def_map( + " + //- /lib.rs + mod r#async; + use self::r#async::Bar; + + //- /async.rs + pub struct Bar; + ", + ); + + assert_snapshot!(map, @r###" + ⋮crate + ⋮Bar: t v + ⋮async: t + ⋮ + ⋮crate::async + ⋮Bar: t v + "###); +} + +#[test] +fn module_resolution_decl_path() { + let map = def_map( + r###" + //- /lib.rs + #[path = "bar/baz/foo.rs"] + mod foo; + use self::foo::Bar; + + //- /bar/baz/foo.rs + pub struct Bar; + "###, + ); + + assert_snapshot!(map, @r###" + ⋮crate + ⋮Bar: t v + ⋮foo: t + ⋮ + ⋮crate::foo + ⋮Bar: t v + "###); +} + +#[test] +fn module_resolution_module_with_path_in_mod_rs() { + let map = def_map( + r###" + //- /main.rs + mod foo; + + //- /foo/mod.rs + #[path = "baz.rs"] + pub mod bar; + + use self::bar::Baz; + + //- /foo/baz.rs + pub struct Baz; + "###, + ); + + assert_snapshot!(map, @r###" + ⋮crate + ⋮foo: t + ⋮ + ⋮crate::foo + ⋮Baz: t v + ⋮bar: t + ⋮ + ⋮crate::foo::bar + ⋮Baz: t v + "###); +} + +#[test] +fn module_resolution_module_with_path_non_crate_root() { + let map = def_map( + r###" + //- /main.rs + mod foo; + + //- /foo.rs + #[path = "baz.rs"] + pub mod bar; + + use self::bar::Baz; + + //- /baz.rs + pub struct Baz; + "###, + ); + + assert_snapshot!(map, @r###" + ⋮crate + ⋮foo: t + ⋮ + ⋮crate::foo + ⋮Baz: t v + ⋮bar: t + ⋮ + ⋮crate::foo::bar + ⋮Baz: t v + "###); +} + +#[test] +fn module_resolution_module_decl_path_super() { + let map = def_map( + r###" + //- /main.rs + #[path = "bar/baz/module.rs"] + mod foo; + pub struct Baz; + + //- /bar/baz/module.rs + use super::Baz; + "###, + ); + + assert_snapshot!(map, @r###" + ⋮crate + ⋮Baz: t v + ⋮foo: t + ⋮ + ⋮crate::foo + ⋮Baz: t v + "###); +} + +#[test] +fn module_resolution_explicit_path_mod_rs() { + let map = def_map( + r###" + //- /main.rs + #[path = "module/mod.rs"] + mod foo; + + //- /module/mod.rs + pub struct Baz; + "###, + ); + + assert_snapshot!(map, @r###" + ⋮crate + ⋮foo: t + ⋮ + ⋮crate::foo + ⋮Baz: t v + "###); +} + +#[test] +fn module_resolution_relative_path() { + let map = def_map( + r###" + //- /main.rs + mod foo; + + //- /foo.rs + #[path = "./sub.rs"] + pub mod foo_bar; + + //- /sub.rs + pub struct Baz; + "###, + ); + + assert_snapshot!(map, @r###" + ⋮crate + ⋮foo: t + ⋮ + ⋮crate::foo + ⋮foo_bar: t + ⋮ + ⋮crate::foo::foo_bar + ⋮Baz: t v + "###); +} + +#[test] +fn module_resolution_relative_path_2() { + let map = def_map( + r###" + //- /main.rs + mod foo; + + //- /foo/mod.rs + #[path="../sub.rs"] + pub mod foo_bar; + + //- /sub.rs + pub struct Baz; + "###, + ); + + assert_snapshot!(map, @r###" + ⋮crate + ⋮foo: t + ⋮ + ⋮crate::foo + ⋮foo_bar: t + ⋮ + ⋮crate::foo::foo_bar + ⋮Baz: t v + "###); +} + +#[test] +fn module_resolution_explicit_path_mod_rs_2() { + let map = def_map( + r###" + //- /main.rs + #[path = "module/bar/mod.rs"] + mod foo; + + //- /module/bar/mod.rs + pub struct Baz; + "###, + ); + + assert_snapshot!(map, @r###" + ⋮crate + ⋮foo: t + ⋮ + ⋮crate::foo + ⋮Baz: t v + "###); +} + +#[test] +fn module_resolution_explicit_path_mod_rs_with_win_separator() { + let map = def_map( + r###" + //- /main.rs + #[path = "module\bar\mod.rs"] + mod foo; + + //- /module/bar/mod.rs + pub struct Baz; + "###, + ); + + assert_snapshot!(map, @r###" + ⋮crate + ⋮foo: t + ⋮ + ⋮crate::foo + ⋮Baz: t v + "###); +} + +#[test] +fn module_resolution_decl_inside_inline_module_with_path_attribute() { + let map = def_map( + r###" + //- /main.rs + #[path = "models"] + mod foo { + mod bar; + } + + //- /models/bar.rs + pub struct Baz; + "###, + ); + + assert_snapshot!(map, @r###" + ⋮crate + ⋮foo: t + ⋮ + ⋮crate::foo + ⋮bar: t + ⋮ + ⋮crate::foo::bar + ⋮Baz: t v + "###); +} + +#[test] +fn module_resolution_decl_inside_inline_module() { + let map = def_map( + r###" + //- /main.rs + mod foo { + mod bar; + } + + //- /foo/bar.rs + pub struct Baz; + "###, + ); + + assert_snapshot!(map, @r###" + ⋮crate + ⋮foo: t + ⋮ + ⋮crate::foo + ⋮bar: t + ⋮ + ⋮crate::foo::bar + ⋮Baz: t v + "###); +} + +#[test] +fn module_resolution_decl_inside_inline_module_2_with_path_attribute() { + let map = def_map( + r###" + //- /main.rs + #[path = "models/db"] + mod foo { + mod bar; + } + + //- /models/db/bar.rs + pub struct Baz; + "###, + ); + + assert_snapshot!(map, @r###" + ⋮crate + ⋮foo: t + ⋮ + ⋮crate::foo + ⋮bar: t + ⋮ + ⋮crate::foo::bar + ⋮Baz: t v + "###); +} + +#[test] +fn module_resolution_decl_inside_inline_module_3() { + let map = def_map( + r###" + //- /main.rs + #[path = "models/db"] + mod foo { + #[path = "users.rs"] + mod bar; + } + + //- /models/db/users.rs + pub struct Baz; + "###, + ); + + assert_snapshot!(map, @r###" + ⋮crate + ⋮foo: t + ⋮ + ⋮crate::foo + ⋮bar: t + ⋮ + ⋮crate::foo::bar + ⋮Baz: t v + "###); +} + +#[test] +fn module_resolution_decl_inside_inline_module_empty_path() { + let map = def_map( + r###" + //- /main.rs + #[path = ""] + mod foo { + #[path = "users.rs"] + mod bar; + } + + //- /users.rs + pub struct Baz; + "###, + ); + + assert_snapshot!(map, @r###" + ⋮crate + ⋮foo: t + ⋮ + ⋮crate::foo + ⋮bar: t + ⋮ + ⋮crate::foo::bar + ⋮Baz: t v + "###); +} + +#[test] +fn module_resolution_decl_empty_path() { + let map = def_map( + r###" + //- /main.rs + #[path = ""] // Should try to read `/` (a directory) + mod foo; + + //- /foo.rs + pub struct Baz; + "###, + ); + + assert_snapshot!(map, @r###" + ⋮crate + "###); +} + +#[test] +fn module_resolution_decl_inside_inline_module_relative_path() { + let map = def_map( + r###" + //- /main.rs + #[path = "./models"] + mod foo { + mod bar; + } + + //- /models/bar.rs + pub struct Baz; + "###, + ); + + assert_snapshot!(map, @r###" + ⋮crate + ⋮foo: t + ⋮ + ⋮crate::foo + ⋮bar: t + ⋮ + ⋮crate::foo::bar + ⋮Baz: t v + "###); +} + +#[test] +fn module_resolution_decl_inside_inline_module_in_crate_root() { + let map = def_map( + r###" + //- /main.rs + mod foo { + #[path = "baz.rs"] + mod bar; + } + use self::foo::bar::Baz; + + //- /foo/baz.rs + pub struct Baz; + "###, + ); + + assert_snapshot!(map, @r###" + ⋮crate + ⋮Baz: t v + ⋮foo: t + ⋮ + ⋮crate::foo + ⋮bar: t + ⋮ + ⋮crate::foo::bar + ⋮Baz: t v + "###); +} + +#[test] +fn module_resolution_decl_inside_inline_module_in_mod_rs() { + let map = def_map( + r###" + //- /main.rs + mod foo; + + //- /foo/mod.rs + mod bar { + #[path = "qwe.rs"] + pub mod baz; + } + use self::bar::baz::Baz; + + //- /foo/bar/qwe.rs + pub struct Baz; + "###, + ); + + assert_snapshot!(map, @r###" + ⋮crate + ⋮foo: t + ⋮ + ⋮crate::foo + ⋮Baz: t v + ⋮bar: t + ⋮ + ⋮crate::foo::bar + ⋮baz: t + ⋮ + ⋮crate::foo::bar::baz + ⋮Baz: t v + "###); +} + +#[test] +fn module_resolution_decl_inside_inline_module_in_non_crate_root() { + let map = def_map( + r###" + //- /main.rs + mod foo; + + //- /foo.rs + mod bar { + #[path = "qwe.rs"] + pub mod baz; + } + use self::bar::baz::Baz; + + //- /foo/bar/qwe.rs + pub struct Baz; + "###, + ); + + assert_snapshot!(map, @r###" + ⋮crate + ⋮foo: t + ⋮ + ⋮crate::foo + ⋮Baz: t v + ⋮bar: t + ⋮ + ⋮crate::foo::bar + ⋮baz: t + ⋮ + ⋮crate::foo::bar::baz + ⋮Baz: t v + "###); +} + +#[test] +fn module_resolution_decl_inside_inline_module_in_non_crate_root_2() { + let map = def_map( + r###" + //- /main.rs + mod foo; + + //- /foo.rs + #[path = "bar"] + mod bar { + pub mod baz; + } + use self::bar::baz::Baz; + + //- /bar/baz.rs + pub struct Baz; + "###, + ); + + assert_snapshot!(map, @r###" + ⋮crate + ⋮foo: t + ⋮ + ⋮crate::foo + ⋮Baz: t v + ⋮bar: t + ⋮ + ⋮crate::foo::bar + ⋮baz: t + ⋮ + ⋮crate::foo::bar::baz + ⋮Baz: t v + "###); +} + +#[test] +fn unresolved_module_diagnostics() { + let db = TestDB::with_files( + r" + //- /lib.rs + mod foo; + mod bar; + mod baz {} + //- /foo.rs + ", + ); + let krate = db.crate_graph().iter().next().unwrap(); + + let crate_def_map = db.crate_def_map(krate); + + insta::assert_debug_snapshot!( + crate_def_map.diagnostics, + @r###" + [ + UnresolvedModule { + module: CrateModuleId( + 0, + ), + declaration: AstId { + file_id: HirFileId( + FileId( + FileId( + 0, + ), + ), + ), + file_ast_id: FileAstId { + raw: ErasedFileAstId( + 1, + ), + _ty: PhantomData, + }, + }, + candidate: "bar.rs", + }, + ] + "### + ); +} + +#[test] +fn module_resolution_decl_inside_module_in_non_crate_root_2() { + let map = def_map( + r###" + //- /main.rs + #[path="module/m2.rs"] + mod module; + + //- /module/m2.rs + pub mod submod; + + //- /module/submod.rs + pub struct Baz; + "###, + ); + + assert_snapshot!(map, @r###" + ⋮crate + ⋮module: t + ⋮ + ⋮crate::module + ⋮submod: t + ⋮ + ⋮crate::module::submod + ⋮Baz: t v + "###); +} + +#[test] +fn nested_out_of_line_module() { + let map = def_map( + r###" + //- /lib.rs + mod a { + mod b { + mod c; + } + } + + //- /a/b/c.rs + struct X; + "###, + ); + + assert_snapshot!(map, @r###" + crate + a: t + + crate::a + b: t + + crate::a::b + c: t + + crate::a::b::c + X: t v + "###); +} + +#[test] +fn nested_out_of_line_module_with_path() { + let map = def_map( + r###" + //- /lib.rs + mod a { + #[path = "d/e"] + mod b { + mod c; + } + } + + //- /a/d/e/c.rs + struct X; + "###, + ); + + assert_snapshot!(map, @r###" + crate + a: t + + crate::a + b: t + + crate::a::b + c: t + + crate::a::b::c + X: t v + "###); +} diff --git a/crates/ra_hir_def/src/nameres/tests/primitives.rs b/crates/ra_hir_def/src/nameres/tests/primitives.rs new file mode 100644 index 000000000..0e2708658 --- /dev/null +++ b/crates/ra_hir_def/src/nameres/tests/primitives.rs @@ -0,0 +1,24 @@ +use super::*; + +#[test] +fn primitive_reexport() { + let map = def_map( + " + //- /lib.rs + mod foo; + use foo::int; + + //- /foo.rs + pub use i32 as int; + ", + ); + assert_snapshot!(map, @r###" + ⋮crate + ⋮foo: t + ⋮int: t + ⋮ + ⋮crate::foo + ⋮int: t + "### + ); +} diff --git a/crates/ra_hir_def/src/test_db.rs b/crates/ra_hir_def/src/test_db.rs index 67714c68e..05018f8e4 100644 --- a/crates/ra_hir_def/src/test_db.rs +++ b/crates/ra_hir_def/src/test_db.rs @@ -1,4 +1,7 @@ -use std::{panic, sync::Arc}; +use std::{ + panic, + sync::{Arc, Mutex}, +}; use ra_db::{salsa, CrateId, FileId, FileLoader, FileLoaderDelegate}; use relative_path::RelativePath; @@ -13,12 +16,20 @@ use relative_path::RelativePath; #[derive(Debug, Default)] pub struct TestDB { runtime: salsa::Runtime, + events: Mutex>>>, } impl salsa::Database for TestDB { fn salsa_runtime(&self) -> &salsa::Runtime { &self.runtime } + + fn salsa_event(&self, event: impl Fn() -> salsa::Event) { + let mut events = self.events.lock().unwrap(); + if let Some(events) = &mut *events { + events.push(event()); + } + } } impl panic::RefUnwindSafe for TestDB {} @@ -38,3 +49,26 @@ impl FileLoader for TestDB { FileLoaderDelegate(self).relevant_crates(file_id) } } + +impl TestDB { + pub fn log(&self, f: impl FnOnce()) -> Vec> { + *self.events.lock().unwrap() = Some(Vec::new()); + f(); + self.events.lock().unwrap().take().unwrap() + } + + pub fn log_executed(&self, f: impl FnOnce()) -> Vec { + let events = self.log(f); + events + .into_iter() + .filter_map(|e| match e.kind { + // This pretty horrible, but `Debug` is the only way to inspect + // QueryDescriptor at the moment. + salsa::EventKind::WillExecute { database_key } => { + Some(format!("{:?}", database_key)) + } + _ => None, + }) + .collect() + } +} -- cgit v1.2.3