From 7eff6705cc1c1d4399a7c9da360d344a96df59b6 Mon Sep 17 00:00:00 2001 From: Jonas Schievink Date: Mon, 1 Feb 2021 13:20:35 +0100 Subject: Use body lowering for block_def_map tests Removes the hacky and buggy custom lowering code --- crates/hir_def/src/body/tests.rs | 116 +++++++++++++++++- crates/hir_def/src/body/tests/block.rs | 187 ++++++++++++++++++++++++++++++ crates/hir_def/src/nameres/tests.rs | 68 +---------- crates/hir_def/src/nameres/tests/block.rs | 186 ----------------------------- 4 files changed, 303 insertions(+), 254 deletions(-) create mode 100644 crates/hir_def/src/body/tests/block.rs delete mode 100644 crates/hir_def/src/nameres/tests/block.rs diff --git a/crates/hir_def/src/body/tests.rs b/crates/hir_def/src/body/tests.rs index 2e5d0a01e..da60072ce 100644 --- a/crates/hir_def/src/body/tests.rs +++ b/crates/hir_def/src/body/tests.rs @@ -1,7 +1,10 @@ -use base_db::{fixture::WithFixture, SourceDatabase}; +mod block; + +use base_db::{fixture::WithFixture, FilePosition, SourceDatabase}; +use expect_test::Expect; use test_utils::mark; -use crate::{test_db::TestDB, ModuleDefId}; +use crate::{test_db::TestDB, BlockId, ModuleDefId}; use super::*; @@ -31,6 +34,115 @@ fn check_diagnostics(ra_fixture: &str) { db.check_diagnostics(); } +fn block_def_map_at(ra_fixture: &str) -> Arc { + let (db, position) = crate::test_db::TestDB::with_position(ra_fixture); + + let krate = db.crate_graph().iter().next().unwrap(); + let def_map = db.crate_def_map(krate); + + let mut block = + block_at_pos(&db, &def_map, position).expect("couldn't find enclosing function or block"); + loop { + let def_map = db.block_def_map(block); + let new_block = block_at_pos(&db, &def_map, position); + match new_block { + Some(new_block) => { + assert_ne!(block, new_block); + block = new_block; + } + None => { + return def_map; + } + } + } +} + +fn block_at_pos(db: &dyn DefDatabase, def_map: &DefMap, position: FilePosition) -> Option { + let mut size = None; + let mut fn_def = None; + for (_, module) in def_map.modules() { + let file_id = module.definition_source(db).file_id; + if file_id != position.file_id.into() { + continue; + } + let root = db.parse_or_expand(file_id).unwrap(); + let ast_map = db.ast_id_map(file_id); + let item_tree = db.item_tree(file_id); + for decl in module.scope.declarations() { + if let ModuleDefId::FunctionId(it) = decl { + let ast = ast_map.get(item_tree[it.lookup(db).id.value].ast_id).to_node(&root); + let range = ast.syntax().text_range(); + + // Find the smallest (innermost) function containing the cursor. + if !range.contains(position.offset) { + continue; + } + + let new_size = match size { + None => range.len(), + Some(size) => { + if range.len() < size { + range.len() + } else { + size + } + } + }; + if size != Some(new_size) { + size = Some(new_size); + fn_def = Some(it); + } + } + } + } + + let (body, source_map) = db.body_with_source_map(fn_def?.into()); + + // Now find the smallest encompassing block expression in the function body. + let mut size = None; + let mut block_id = None; + for (expr_id, expr) in body.exprs.iter() { + if let Expr::Block { id, .. } = expr { + if let Ok(ast) = source_map.expr_syntax(expr_id) { + if ast.file_id != position.file_id.into() { + continue; + } + + let root = db.parse_or_expand(ast.file_id).unwrap(); + let ast = ast.value.to_node(&root); + let range = ast.syntax().text_range(); + + if !range.contains(position.offset) { + continue; + } + + let new_size = match size { + None => range.len(), + Some(size) => { + if range.len() < size { + range.len() + } else { + size + } + } + }; + if size != Some(new_size) { + size = Some(new_size); + block_id = Some(*id); + } + } + } + } + + Some(block_id.expect("can't find block containing cursor")) +} + +fn check_at(ra_fixture: &str, expect: Expect) { + let def_map = block_def_map_at(ra_fixture); + let actual = def_map.dump(); + expect.assert_eq(&actual); +} + #[test] fn your_stack_belongs_to_me() { mark::check!(your_stack_belongs_to_me); diff --git a/crates/hir_def/src/body/tests/block.rs b/crates/hir_def/src/body/tests/block.rs new file mode 100644 index 000000000..6b1ed2555 --- /dev/null +++ b/crates/hir_def/src/body/tests/block.rs @@ -0,0 +1,187 @@ +use super::*; +use expect_test::expect; + +#[test] +fn inner_item_smoke() { + check_at( + r#" +struct inner {} +fn outer() { + $0 + fn inner() {} +} +"#, + expect![[r#" + block scope + inner: v + crate + inner: t + outer: v + "#]], + ); +} + +#[test] +fn use_from_crate() { + check_at( + r#" +struct Struct; +fn outer() { + use Struct; + use crate::Struct as CrateStruct; + use self::Struct as SelfStruct; + $0 +} +"#, + expect![[r#" + block scope + CrateStruct: t v + SelfStruct: t v + Struct: t v + crate + Struct: t v + outer: v + "#]], + ); +} + +#[test] +fn merge_namespaces() { + check_at( + r#" +struct name {} +fn outer() { + fn name() {} + + use name as imported; // should import both `name`s + + $0 +} +"#, + expect![[r#" + block scope + imported: t v + name: v + crate + name: t + outer: v + "#]], + ); +} + +#[test] +fn nested_blocks() { + check_at( + r#" +fn outer() { + struct inner1 {} + fn inner() { + use inner1; + use outer; + fn inner2() {} + $0 + } +} +"#, + expect![[r#" + block scope + inner1: t + inner2: v + outer: v + block scope + inner: v + inner1: t + crate + outer: v + "#]], + ); +} + +#[test] +fn super_imports() { + check_at( + r#" +mod module { + fn f() { + use super::Struct; + $0 + } +} + +struct Struct {} +"#, + expect![[r#" + block scope + Struct: t + crate + Struct: t + module: t + + crate::module + f: v + "#]], + ); +} + +#[test] +fn legacy_macro_items() { + // Checks that legacy-scoped `macro_rules!` from parent namespaces are resolved and expanded + // correctly. + check_at( + r#" +macro_rules! hit { + () => { + struct Hit {} + } +} + +fn f() { + hit!(); + $0 +} +"#, + expect![[r#" + block scope + Hit: t + crate + f: v + "#]], + ); +} + +#[test] +fn macro_resolve() { + check_at( + r#" +//- /lib.rs crate:lib deps:core +use core::mark; + +fn f() { + fn nested() { + mark::hit!(Hit); + $0 + } +} +//- /core.rs crate:core +pub mod mark { + #[macro_export] + macro_rules! _hit { + ($name:ident) => { + struct $name {} + } + } + + pub use crate::_hit as hit; +} +"#, + expect![[r#" + block scope + Hit: t + block scope + nested: v + crate + f: v + mark: t + "#]], + ); +} diff --git a/crates/hir_def/src/nameres/tests.rs b/crates/hir_def/src/nameres/tests.rs index b36d0b59b..723481c36 100644 --- a/crates/hir_def/src/nameres/tests.rs +++ b/crates/hir_def/src/nameres/tests.rs @@ -4,16 +4,14 @@ mod macros; mod mod_resolution; mod diagnostics; mod primitives; -mod block; use std::sync::Arc; -use base_db::{fixture::WithFixture, FilePosition, SourceDatabase}; +use base_db::{fixture::WithFixture, SourceDatabase}; use expect_test::{expect, Expect}; -use syntax::AstNode; use test_utils::mark; -use crate::{db::DefDatabase, nameres::*, test_db::TestDB, Lookup}; +use crate::{db::DefDatabase, nameres::*, test_db::TestDB}; fn compute_crate_def_map(ra_fixture: &str) -> Arc { let db = TestDB::with_files(ra_fixture); @@ -21,74 +19,12 @@ fn compute_crate_def_map(ra_fixture: &str) -> Arc { db.crate_def_map(krate) } -fn compute_block_def_map(ra_fixture: &str) -> Arc { - let (db, position) = TestDB::with_position(ra_fixture); - - // FIXME: perhaps we should make this use body lowering tests instead? - - let module = db.module_for_file(position.file_id); - let mut def_map = db.crate_def_map(module.krate); - while let Some(new_def_map) = descend_def_map_at_position(&db, position, def_map.clone()) { - def_map = new_def_map; - } - - // FIXME: select the right module, not the root - - def_map -} - -fn descend_def_map_at_position( - db: &dyn DefDatabase, - position: FilePosition, - def_map: Arc, -) -> Option> { - for (local_id, module_data) in def_map.modules() { - let mod_def = module_data.origin.definition_source(db); - let ast_map = db.ast_id_map(mod_def.file_id); - let item_tree = db.item_tree(mod_def.file_id); - let root = db.parse_or_expand(mod_def.file_id).unwrap(); - for item in module_data.scope.declarations() { - match item { - ModuleDefId::FunctionId(it) => { - // Technically blocks can be inside any type (due to arrays and const generics), - // and also in const/static initializers. For tests we only really care about - // functions though. - - let ast = ast_map.get(item_tree[it.lookup(db).id.value].ast_id).to_node(&root); - - if ast.syntax().text_range().contains(position.offset) { - // Cursor inside function, descend into its body's DefMap. - // Note that we don't handle block *expressions* inside function bodies. - let ast_map = db.ast_id_map(position.file_id.into()); - let ast_id = ast_map.ast_id(&ast.body().unwrap()); - let block = BlockLoc { - ast_id: InFile::new(position.file_id.into(), ast_id), - module: def_map.module_id(local_id), - }; - let block_id = db.intern_block(block); - return Some(db.block_def_map(block_id)); - } - } - _ => continue, - } - } - } - - None -} - fn check(ra_fixture: &str, expect: Expect) { let def_map = compute_crate_def_map(ra_fixture); let actual = def_map.dump(); expect.assert_eq(&actual); } -fn check_at(ra_fixture: &str, expect: Expect) { - let def_map = compute_block_def_map(ra_fixture); - let actual = def_map.dump(); - expect.assert_eq(&actual); -} - #[test] fn crate_def_map_smoke_test() { check( diff --git a/crates/hir_def/src/nameres/tests/block.rs b/crates/hir_def/src/nameres/tests/block.rs deleted file mode 100644 index 6cc659513..000000000 --- a/crates/hir_def/src/nameres/tests/block.rs +++ /dev/null @@ -1,186 +0,0 @@ -use super::*; - -#[test] -fn inner_item_smoke() { - check_at( - r#" -struct inner {} -fn outer() { - $0 - fn inner() {} -} -"#, - expect![[r#" - block scope - inner: v - crate - inner: t - outer: v - "#]], - ); -} - -#[test] -fn use_from_crate() { - check_at( - r#" -struct Struct; -fn outer() { - use Struct; - use crate::Struct as CrateStruct; - use self::Struct as SelfStruct; - $0 -} -"#, - expect![[r#" - block scope - CrateStruct: t v - SelfStruct: t v - Struct: t v - crate - Struct: t v - outer: v - "#]], - ); -} - -#[test] -fn merge_namespaces() { - check_at( - r#" -struct name {} -fn outer() { - fn name() {} - - use name as imported; // should import both `name`s - - $0 -} -"#, - expect![[r#" - block scope - imported: t v - name: v - crate - name: t - outer: v - "#]], - ); -} - -#[test] -fn nested_blocks() { - check_at( - r#" -fn outer() { - struct inner1 {} - fn inner() { - use inner1; - use outer; - fn inner2() {} - $0 - } -} -"#, - expect![[r#" - block scope - inner1: t - inner2: v - outer: v - block scope - inner: v - inner1: t - crate - outer: v - "#]], - ); -} - -#[test] -fn super_imports() { - check_at( - r#" -mod module { - fn f() { - use super::Struct; - $0 - } -} - -struct Struct {} -"#, - expect![[r#" - block scope - Struct: t - crate - Struct: t - module: t - - crate::module - f: v - "#]], - ); -} - -#[test] -fn legacy_macro_items() { - // Checks that legacy-scoped `macro_rules!` from parent namespaces are resolved and expanded - // correctly. - check_at( - r#" -macro_rules! hit { - () => { - struct Hit {} - } -} - -fn f() { - hit!(); - $0 -} -"#, - expect![[r#" - block scope - Hit: t - crate - f: v - "#]], - ); -} - -#[test] -fn macro_resolve() { - check_at( - r#" -//- /lib.rs crate:lib deps:core -use core::mark; - -fn f() { - fn nested() { - mark::hit!(Hit); - $0 - } -} -//- /core.rs crate:core -pub mod mark { - #[macro_export] - macro_rules! _hit { - ($name:ident) => { - struct $name {} - } - } - - pub use crate::_hit as hit; -} -"#, - expect![[r#" - block scope - Hit: t - block scope - nested: v - crate - f: v - mark: t - "#]], - ); -} -- cgit v1.2.3