mirror of
https://github.com/rust-lang/rust.git
synced 2024-11-24 15:54:15 +00:00
Add name resolution query for block expressions
This commit is contained in:
parent
0045d7c6db
commit
896dfacfc4
@ -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,
|
||||
|
@ -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)]
|
||||
|
@ -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).
|
||||
|
@ -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) = ¤t_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),
|
||||
|
@ -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)
|
||||
}
|
||||
|
||||
|
@ -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(
|
||||
|
47
crates/hir_def/src/nameres/tests/block.rs
Normal file
47
crates/hir_def/src/nameres/tests/block.rs
Normal 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
|
||||
"#]],
|
||||
);
|
||||
}
|
@ -149,6 +149,7 @@ impl RootDatabase {
|
||||
|
||||
// DefDatabase
|
||||
hir::db::ItemTreeQuery
|
||||
hir::db::BlockDefMapQuery
|
||||
hir::db::CrateDefMapQueryQuery
|
||||
hir::db::StructDataQuery
|
||||
hir::db::UnionDataQuery
|
||||
|
Loading…
Reference in New Issue
Block a user