From b7be2b1d3cc2fd8ca8e7aa7542aaf9d4f905f9f5 Mon Sep 17 00:00:00 2001 From: Jonas Schievink Date: Mon, 1 Feb 2021 13:19:55 +0100 Subject: Use block_def_map in body lowering --- crates/hir_def/src/body/lower.rs | 35 +++++++++++++++++++++++++---------- 1 file changed, 25 insertions(+), 10 deletions(-) (limited to 'crates/hir_def/src/body') diff --git a/crates/hir_def/src/body/lower.rs b/crates/hir_def/src/body/lower.rs index 209965fca..bc61730a7 100644 --- a/crates/hir_def/src/body/lower.rs +++ b/crates/hir_def/src/body/lower.rs @@ -1,7 +1,7 @@ //! Transforms `ast::Expr` into an equivalent `hir_def::expr::Expr` //! representation. -use std::{any::type_name, sync::Arc}; +use std::{any::type_name, mem, sync::Arc}; use either::Either; use hir_expand::{ @@ -36,8 +36,8 @@ use crate::{ item_tree::{ItemTree, ItemTreeId, ItemTreeNode}, path::{GenericArgs, Path}, type_ref::{Mutability, Rawness, TypeRef}, - AdtId, ConstLoc, ContainerId, DefWithBodyId, EnumLoc, FunctionLoc, Intern, ModuleDefId, - StaticLoc, StructLoc, TraitLoc, TypeAliasLoc, UnionLoc, + AdtId, BlockLoc, ConstLoc, ContainerId, DefWithBodyId, EnumLoc, FunctionLoc, Intern, + ModuleDefId, StaticLoc, StructLoc, TraitLoc, TypeAliasLoc, UnionLoc, }; use super::{diagnostics::BodyDiagnostic, ExprSource, PatSource}; @@ -152,8 +152,8 @@ impl ExprCollector<'_> { fn alloc_expr_desugared(&mut self, expr: Expr) -> ExprId { self.make_expr(expr, Err(SyntheticSyntax)) } - fn empty_block(&mut self) -> ExprId { - self.alloc_expr_desugared(Expr::Block { statements: Vec::new(), tail: None, label: None }) + fn unit(&mut self) -> ExprId { + self.alloc_expr_desugared(Expr::Tuple { exprs: Vec::new() }) } fn missing_expr(&mut self) -> ExprId { self.alloc_expr_desugared(Expr::Missing) @@ -222,7 +222,7 @@ impl ExprCollector<'_> { MatchArm { pat, expr: then_branch, guard: None }, MatchArm { pat: placeholder_pat, - expr: else_branch.unwrap_or_else(|| self.empty_block()), + expr: else_branch.unwrap_or_else(|| self.unit()), guard: None, }, ]; @@ -561,7 +561,7 @@ impl ExprCollector<'_> { let outer_file = self.expander.current_file_id; let macro_call = self.expander.to_source(AstPtr::new(&e)); - let res = self.expander.enter_expand(self.db, Some(&self.body.item_scope), e); + let res = self.expander.enter_expand(self.db, e); match &res.err { Some(ExpandError::UnresolvedProcMacro) => { @@ -697,12 +697,27 @@ impl ExprCollector<'_> { } fn collect_block(&mut self, block: ast::BlockExpr) -> ExprId { - let syntax_node_ptr = AstPtr::new(&block.clone().into()); + let ast_id = self.expander.ast_id(&block); + let block_loc = BlockLoc { ast_id, module: self.expander.module }; + let block_id = self.db.intern_block(block_loc); + let def_map = self.db.block_def_map(block_id); + let root = def_map.module_id(def_map.root()); + let prev_def_map = mem::replace(&mut self.expander.def_map, def_map); + let prev_module = mem::replace(&mut self.expander.module, root); + self.collect_stmts_items(block.statements()); let statements = block.statements().filter_map(|s| self.collect_stmt(s)).flatten().collect(); let tail = block.tail_expr().map(|e| self.collect_expr(e)); - self.alloc_expr(Expr::Block { statements, tail, label: None }, syntax_node_ptr) + let syntax_node_ptr = AstPtr::new(&block.clone().into()); + let expr_id = self.alloc_expr( + Expr::Block { id: block_id, statements, tail, label: None }, + syntax_node_ptr, + ); + + self.expander.def_map = prev_def_map; + self.expander.module = prev_module; + expr_id } fn collect_stmts_items(&mut self, stmts: ast::AstChildren) { @@ -832,7 +847,7 @@ impl ExprCollector<'_> { if annotation == BindingAnnotation::Unannotated && subpat.is_none() { // This could also be a single-segment path pattern. To // decide that, we need to try resolving the name. - let (resolved, _) = self.expander.crate_def_map.resolve_path( + let (resolved, _) = self.expander.def_map.resolve_path( self.db, self.expander.module.local_id, &name.clone().into(), -- cgit v1.2.3 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 +++++++++++++++++++++++++++++++++ 2 files changed, 301 insertions(+), 2 deletions(-) create mode 100644 crates/hir_def/src/body/tests/block.rs (limited to 'crates/hir_def/src/body') 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 + "#]], + ); +} -- cgit v1.2.3 From da57f5dc17303cfd5ba318d1735c7f325f6b7130 Mon Sep 17 00:00:00 2001 From: Jonas Schievink Date: Mon, 1 Feb 2021 13:32:43 +0100 Subject: Shortcut `block_def_map` if there's no inner items This previously didn't work, but apparently only because of the wonky test setup --- crates/hir_def/src/body/lower.rs | 9 ++++++--- crates/hir_def/src/body/tests.rs | 4 ++-- 2 files changed, 8 insertions(+), 5 deletions(-) (limited to 'crates/hir_def/src/body') diff --git a/crates/hir_def/src/body/lower.rs b/crates/hir_def/src/body/lower.rs index bc61730a7..540c6c9ad 100644 --- a/crates/hir_def/src/body/lower.rs +++ b/crates/hir_def/src/body/lower.rs @@ -700,10 +700,13 @@ impl ExprCollector<'_> { let ast_id = self.expander.ast_id(&block); let block_loc = BlockLoc { ast_id, module: self.expander.module }; let block_id = self.db.intern_block(block_loc); - let def_map = self.db.block_def_map(block_id); - let root = def_map.module_id(def_map.root()); + let opt_def_map = self.db.block_def_map(block_id); + let has_def_map = opt_def_map.is_some(); + let def_map = opt_def_map.unwrap_or_else(|| self.expander.def_map.clone()); + let module = + if has_def_map { def_map.module_id(def_map.root()) } else { self.expander.module }; let prev_def_map = mem::replace(&mut self.expander.def_map, def_map); - let prev_module = mem::replace(&mut self.expander.module, root); + let prev_module = mem::replace(&mut self.expander.module, module); self.collect_stmts_items(block.statements()); let statements = diff --git a/crates/hir_def/src/body/tests.rs b/crates/hir_def/src/body/tests.rs index da60072ce..404603360 100644 --- a/crates/hir_def/src/body/tests.rs +++ b/crates/hir_def/src/body/tests.rs @@ -43,7 +43,7 @@ fn block_def_map_at(ra_fixture: &str) -> Arc { 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 def_map = db.block_def_map(block).unwrap_or_else(|| def_map.clone()); let new_block = block_at_pos(&db, &def_map, position); match new_block { Some(new_block) => { @@ -58,6 +58,7 @@ fn block_def_map_at(ra_fixture: &str) -> Arc { } fn block_at_pos(db: &dyn DefDatabase, def_map: &DefMap, position: FilePosition) -> Option { + // Find the smallest (innermost) function containing the cursor. let mut size = None; let mut fn_def = None; for (_, module) in def_map.modules() { @@ -73,7 +74,6 @@ fn block_at_pos(db: &dyn DefDatabase, def_map: &DefMap, position: FilePosition) 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; } -- cgit v1.2.3 From 63744fe128193464eb0ce63fbe6c30c4f98b6135 Mon Sep 17 00:00:00 2001 From: Jonas Schievink Date: Tue, 2 Feb 2021 15:55:33 +0100 Subject: Test for name resolution with DefMap shortcut --- crates/hir_def/src/body/tests/block.rs | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) (limited to 'crates/hir_def/src/body') diff --git a/crates/hir_def/src/body/tests/block.rs b/crates/hir_def/src/body/tests/block.rs index 6b1ed2555..d5f3ac4c5 100644 --- a/crates/hir_def/src/body/tests/block.rs +++ b/crates/hir_def/src/body/tests/block.rs @@ -185,3 +185,36 @@ pub mod mark { "#]], ); } + +#[test] +fn macro_resolve_legacy() { + check_at( + r#" +//- /lib.rs +mod module; + +//- /module.rs +macro_rules! m { + () => { + struct Def {} + }; +} + +fn f() { + { + m!(); + $0 + } +} + "#, + expect![[r#" + block scope + Def: t + crate + module: t + + crate::module + f: v + "#]], + ) +} -- cgit v1.2.3