Add name resolution query for block expressions

This commit is contained in:
Jonas Schievink 2021-01-21 15:22:17 +01:00
parent 0045d7c6db
commit 896dfacfc4
8 changed files with 206 additions and 43 deletions

View File

@ -1,13 +1,13 @@
//! FIXME: write short doc here
pub use hir_def::db::{
AttrsQuery, BodyQuery, BodyWithSourceMapQuery, ConstDataQuery, CrateDefMapQueryQuery,
CrateLangItemsQuery, DefDatabase, DefDatabaseStorage, EnumDataQuery, ExprScopesQuery,
FunctionDataQuery, GenericParamsQuery, ImplDataQuery, ImportMapQuery, InternConstQuery,
InternDatabase, InternDatabaseStorage, InternEnumQuery, InternFunctionQuery, InternImplQuery,
InternStaticQuery, InternStructQuery, InternTraitQuery, InternTypeAliasQuery, InternUnionQuery,
ItemTreeQuery, LangItemQuery, StaticDataQuery, StructDataQuery, TraitDataQuery,
TypeAliasDataQuery, UnionDataQuery,
AttrsQuery, BlockDefMapQuery, BodyQuery, BodyWithSourceMapQuery, ConstDataQuery,
CrateDefMapQueryQuery, CrateLangItemsQuery, DefDatabase, DefDatabaseStorage, EnumDataQuery,
ExprScopesQuery, FunctionDataQuery, GenericParamsQuery, ImplDataQuery, ImportMapQuery,
InternConstQuery, InternDatabase, InternDatabaseStorage, InternEnumQuery, InternFunctionQuery,
InternImplQuery, InternStaticQuery, InternStructQuery, InternTraitQuery, InternTypeAliasQuery,
InternUnionQuery, ItemTreeQuery, LangItemQuery, StaticDataQuery, StructDataQuery,
TraitDataQuery, TypeAliasDataQuery, UnionDataQuery,
};
pub use hir_expand::db::{
AstDatabase, AstDatabaseStorage, AstIdMapQuery, HygieneFrameQuery, InternEagerExpansionQuery,

View File

@ -2,9 +2,9 @@
use std::sync::Arc;
use base_db::{salsa, CrateId, SourceDatabase, Upcast};
use hir_expand::{db::AstDatabase, HirFileId};
use hir_expand::{db::AstDatabase, AstId, HirFileId};
use la_arena::ArenaMap;
use syntax::SmolStr;
use syntax::{ast, SmolStr};
use crate::{
adt::{EnumData, StructData},
@ -55,6 +55,9 @@ pub trait DefDatabase: InternDatabase + AstDatabase + Upcast<dyn AstDatabase> {
#[salsa::invoke(DefMap::crate_def_map_query)]
fn crate_def_map_query(&self, krate: CrateId) -> Arc<DefMap>;
#[salsa::invoke(DefMap::block_def_map_query)]
fn block_def_map(&self, krate: CrateId, block: AstId<ast::BlockExpr>) -> Arc<DefMap>;
#[salsa::invoke(StructData::struct_data_query)]
fn struct_data(&self, id: StructId) -> Arc<StructData>;
#[salsa::invoke(StructData::union_data_query)]

View File

@ -195,6 +195,13 @@ impl ItemTree {
}
}
pub fn inner_items_of_block(&self, block: FileAstId<ast::BlockExpr>) -> &[ModItem] {
match &self.data {
Some(data) => data.inner_items.get(&block).map(|it| &**it).unwrap_or(&[]),
None => &[],
}
}
pub fn source<S: ItemTreeNode>(&self, db: &dyn DefDatabase, of: ItemTreeId<S>) -> S::Source {
// This unwrap cannot fail, since it has either succeeded above, or resulted in an empty
// ItemTree (in which case there is no valid `FileItemTreeId` to call this method with).

View File

@ -61,7 +61,7 @@ use hir_expand::{diagnostics::DiagnosticSink, name::Name, InFile};
use la_arena::Arena;
use rustc_hash::FxHashMap;
use stdx::format_to;
use syntax::ast;
use syntax::{ast, AstNode};
use crate::{
db::DefDatabase,
@ -75,6 +75,7 @@ use crate::{
/// Contains all top-level defs from a macro-expanded crate
#[derive(Debug, PartialEq, Eq)]
pub struct DefMap {
parent: Option<Arc<DefMap>>,
root: LocalModuleId,
modules: Arena<ModuleData>,
krate: CrateId,
@ -181,24 +182,50 @@ impl DefMap {
let _p = profile::span("crate_def_map_query").detail(|| {
db.crate_graph()[krate].display_name.as_deref().unwrap_or_default().to_string()
});
let def_map = {
let edition = db.crate_graph()[krate].edition;
let mut modules: Arena<ModuleData> = Arena::default();
let root = modules.alloc(ModuleData::default());
DefMap {
krate,
edition,
extern_prelude: FxHashMap::default(),
prelude: None,
root,
modules,
diagnostics: Vec::new(),
}
};
let def_map = collector::collect_defs(db, def_map);
let edition = db.crate_graph()[krate].edition;
let def_map = DefMap::empty(krate, edition);
let def_map = collector::collect_defs(db, def_map, None);
Arc::new(def_map)
}
pub(crate) fn block_def_map_query(
db: &dyn DefDatabase,
krate: CrateId,
block: AstId<ast::BlockExpr>,
) -> Arc<DefMap> {
let item_tree = db.item_tree(block.file_id);
let block_items = item_tree.inner_items_of_block(block.value);
let parent = parent_def_map(db, krate, block);
if block_items.is_empty() {
// If there are no inner items, nothing new is brought into scope, so we can just return
// the parent DefMap. This keeps DefMap parent chains short.
return parent;
}
let mut def_map = DefMap::empty(krate, parent.edition);
def_map.parent = Some(parent);
let def_map = collector::collect_defs(db, def_map, Some(block.value));
Arc::new(def_map)
}
fn empty(krate: CrateId, edition: Edition) -> DefMap {
let mut modules: Arena<ModuleData> = Arena::default();
let root = modules.alloc(ModuleData::default());
DefMap {
parent: None,
krate,
edition,
extern_prelude: FxHashMap::default(),
prelude: None,
root,
modules,
diagnostics: Vec::new(),
}
}
pub fn add_diagnostics(
&self,
db: &dyn DefDatabase,
@ -251,7 +278,12 @@ impl DefMap {
// even), as this should be a great debugging aid.
pub fn dump(&self) -> String {
let mut buf = String::new();
go(&mut buf, self, "crate", self.root);
let mut current_map = self;
while let Some(parent) = &current_map.parent {
go(&mut buf, current_map, "block scope", current_map.root);
current_map = &**parent;
}
go(&mut buf, current_map, "crate", current_map.root);
return buf;
fn go(buf: &mut String, map: &DefMap, path: &str, module: LocalModuleId) {
@ -303,6 +335,35 @@ impl ModuleData {
}
}
fn parent_def_map(
db: &dyn DefDatabase,
krate: CrateId,
block: AstId<ast::BlockExpr>,
) -> Arc<DefMap> {
// FIXME: store this info in the item tree instead of reparsing here
let ast_id_map = db.ast_id_map(block.file_id);
let block_ptr = ast_id_map.get(block.value);
let root = match db.parse_or_expand(block.file_id) {
Some(it) => it,
None => {
return Arc::new(DefMap::empty(krate, Edition::Edition2018));
}
};
let ast = block_ptr.to_node(&root);
for ancestor in ast.syntax().ancestors().skip(1) {
if let Some(block_expr) = ast::BlockExpr::cast(ancestor) {
let ancestor_id = ast_id_map.ast_id(&block_expr);
let ast_id = InFile::new(block.file_id, ancestor_id);
let parent_map = db.block_def_map(krate, ast_id);
return parent_map;
}
}
// No enclosing block scope, so the parent is the crate-level DefMap.
db.crate_def_map(krate)
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum ModuleSource {
SourceFile(ast::SourceFile),

View File

@ -45,7 +45,11 @@ const GLOB_RECURSION_LIMIT: usize = 100;
const EXPANSION_DEPTH_LIMIT: usize = 128;
const FIXED_POINT_LIMIT: usize = 8192;
pub(super) fn collect_defs(db: &dyn DefDatabase, mut def_map: DefMap) -> DefMap {
pub(super) fn collect_defs(
db: &dyn DefDatabase,
mut def_map: DefMap,
block: Option<FileAstId<ast::BlockExpr>>,
) -> DefMap {
let crate_graph = db.crate_graph();
// populate external prelude
@ -93,6 +97,14 @@ pub(super) fn collect_defs(db: &dyn DefDatabase, mut def_map: DefMap) -> DefMap
exports_proc_macros: false,
from_glob_import: Default::default(),
};
match block {
Some(block) => {
collector.seed_with_inner(block);
}
None => {
collector.seed_with_top_level();
}
}
collector.collect();
collector.finish()
}
@ -228,7 +240,7 @@ struct DefCollector<'a> {
}
impl DefCollector<'_> {
fn collect(&mut self) {
fn seed_with_top_level(&mut self) {
let file_id = self.db.crate_graph()[self.def_map.krate].root_file_id;
let item_tree = self.db.item_tree(file_id.into());
let module_id = self.def_map.root;
@ -248,7 +260,31 @@ impl DefCollector<'_> {
}
.collect(item_tree.top_level_items());
}
}
fn seed_with_inner(&mut self, block: FileAstId<ast::BlockExpr>) {
let file_id = self.db.crate_graph()[self.def_map.krate].root_file_id;
let item_tree = self.db.item_tree(file_id.into());
let module_id = self.def_map.root;
self.def_map.modules[module_id].origin = ModuleOrigin::CrateRoot { definition: file_id };
if item_tree
.top_level_attrs(self.db, self.def_map.krate)
.cfg()
.map_or(true, |cfg| self.cfg_options.check(&cfg) != Some(false))
{
ModCollector {
def_collector: &mut *self,
macro_depth: 0,
module_id,
file_id: file_id.into(),
item_tree: &item_tree,
mod_dir: ModDir::root(),
}
.collect(item_tree.inner_items_of_block(block));
}
}
fn collect(&mut self) {
// main name resolution fixed-point loop.
let mut i = 0;
loop {
@ -1470,7 +1506,6 @@ impl ModCollector<'_, '_> {
mod tests {
use crate::{db::DefDatabase, test_db::TestDB};
use base_db::{fixture::WithFixture, SourceDatabase};
use la_arena::Arena;
use super::*;
@ -1489,6 +1524,7 @@ mod tests {
exports_proc_macros: false,
from_glob_import: Default::default(),
};
collector.seed_with_top_level();
collector.collect();
collector.def_map
}
@ -1497,20 +1533,8 @@ mod tests {
let (db, _file_id) = TestDB::with_single_file(&code);
let krate = db.test_crate();
let def_map = {
let edition = db.crate_graph()[krate].edition;
let mut modules: Arena<ModuleData> = Arena::default();
let root = modules.alloc(ModuleData::default());
DefMap {
krate,
edition,
extern_prelude: FxHashMap::default(),
prelude: None,
root,
modules,
diagnostics: Vec::new(),
}
};
let edition = db.crate_graph()[krate].edition;
let def_map = DefMap::empty(krate, edition);
do_collect_defs(&db, def_map)
}

View File

@ -4,11 +4,13 @@ mod macros;
mod mod_resolution;
mod diagnostics;
mod primitives;
mod block;
use std::sync::Arc;
use base_db::{fixture::WithFixture, SourceDatabase};
use expect_test::{expect, Expect};
use hir_expand::db::AstDatabase;
use test_utils::mark;
use crate::{db::DefDatabase, nameres::*, test_db::TestDB};
@ -19,12 +21,30 @@ fn compute_crate_def_map(ra_fixture: &str) -> Arc<DefMap> {
db.crate_def_map(krate)
}
fn compute_block_def_map(ra_fixture: &str) -> Arc<DefMap> {
let (db, position) = TestDB::with_position(ra_fixture);
let module = db.module_for_file(position.file_id);
let ast_map = db.ast_id_map(position.file_id.into());
let ast = db.parse(position.file_id);
let block: ast::BlockExpr =
syntax::algo::find_node_at_offset(&ast.syntax_node(), position.offset).unwrap();
let block_id = ast_map.ast_id(&block);
db.block_def_map(module.krate, InFile::new(position.file_id.into(), block_id))
}
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(

View File

@ -0,0 +1,47 @@
use super::*;
#[test]
fn inner_item_smoke() {
check_at(
r#"
//- /lib.rs
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#"
//- /lib.rs
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
"#]],
);
}

View File

@ -149,6 +149,7 @@ impl RootDatabase {
// DefDatabase
hir::db::ItemTreeQuery
hir::db::BlockDefMapQuery
hir::db::CrateDefMapQueryQuery
hir::db::StructDataQuery
hir::db::UnionDataQuery