mirror of
https://github.com/rust-lang/rust-analyzer.git
synced 2025-09-28 12:54:58 +00:00
Merge #7375
7375: Add support for running name resolution in block expressions r=jonas-schievink a=jonas-schievink This adds a `block_def_map` query that runs the name resolution algorithm on a block expression, and returns a `DefMap` that stores links to the parent `DefMap` (either the containing block or the crate-level `DefMap`). Blocks with no inner items return the parent's `DefMap` as-is, to avoid creating unnecessarily long `DefMap` chains. Path resolution is updated to recurse into the parent `DefMap` after looking up a path in the original `DefMap`. I've added a few new tests for this, but outside of those this isn't used yet. bors r+ Co-authored-by: Jonas Schievink <jonasschievink@gmail.com>
This commit is contained in:
commit
47a70aadce
10 changed files with 291 additions and 58 deletions
|
@ -1,13 +1,13 @@
|
||||||
//! FIXME: write short doc here
|
//! FIXME: write short doc here
|
||||||
|
|
||||||
pub use hir_def::db::{
|
pub use hir_def::db::{
|
||||||
AttrsQuery, BodyQuery, BodyWithSourceMapQuery, ConstDataQuery, CrateDefMapQueryQuery,
|
AttrsQuery, BlockDefMapQuery, BodyQuery, BodyWithSourceMapQuery, ConstDataQuery,
|
||||||
CrateLangItemsQuery, DefDatabase, DefDatabaseStorage, EnumDataQuery, ExprScopesQuery,
|
CrateDefMapQueryQuery, CrateLangItemsQuery, DefDatabase, DefDatabaseStorage, EnumDataQuery,
|
||||||
FunctionDataQuery, GenericParamsQuery, ImplDataQuery, ImportMapQuery, InternConstQuery,
|
ExprScopesQuery, FunctionDataQuery, GenericParamsQuery, ImplDataQuery, ImportMapQuery,
|
||||||
InternDatabase, InternDatabaseStorage, InternEnumQuery, InternFunctionQuery, InternImplQuery,
|
InternConstQuery, InternDatabase, InternDatabaseStorage, InternEnumQuery, InternFunctionQuery,
|
||||||
InternStaticQuery, InternStructQuery, InternTraitQuery, InternTypeAliasQuery, InternUnionQuery,
|
InternImplQuery, InternStaticQuery, InternStructQuery, InternTraitQuery, InternTypeAliasQuery,
|
||||||
ItemTreeQuery, LangItemQuery, StaticDataQuery, StructDataQuery, TraitDataQuery,
|
InternUnionQuery, ItemTreeQuery, LangItemQuery, StaticDataQuery, StructDataQuery,
|
||||||
TypeAliasDataQuery, UnionDataQuery,
|
TraitDataQuery, TypeAliasDataQuery, UnionDataQuery,
|
||||||
};
|
};
|
||||||
pub use hir_expand::db::{
|
pub use hir_expand::db::{
|
||||||
AstDatabase, AstDatabaseStorage, AstIdMapQuery, HygieneFrameQuery, InternEagerExpansionQuery,
|
AstDatabase, AstDatabaseStorage, AstIdMapQuery, HygieneFrameQuery, InternEagerExpansionQuery,
|
||||||
|
|
|
@ -2,9 +2,9 @@
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
use base_db::{salsa, CrateId, SourceDatabase, Upcast};
|
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 la_arena::ArenaMap;
|
||||||
use syntax::SmolStr;
|
use syntax::{ast, SmolStr};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
adt::{EnumData, StructData},
|
adt::{EnumData, StructData},
|
||||||
|
@ -55,6 +55,9 @@ pub trait DefDatabase: InternDatabase + AstDatabase + Upcast<dyn AstDatabase> {
|
||||||
#[salsa::invoke(DefMap::crate_def_map_query)]
|
#[salsa::invoke(DefMap::crate_def_map_query)]
|
||||||
fn crate_def_map_query(&self, krate: CrateId) -> Arc<DefMap>;
|
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)]
|
#[salsa::invoke(StructData::struct_data_query)]
|
||||||
fn struct_data(&self, id: StructId) -> Arc<StructData>;
|
fn struct_data(&self, id: StructId) -> Arc<StructData>;
|
||||||
#[salsa::invoke(StructData::union_data_query)]
|
#[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 {
|
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
|
// 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).
|
// ItemTree (in which case there is no valid `FileItemTreeId` to call this method with).
|
||||||
|
|
|
@ -151,19 +151,21 @@ impl Ctx {
|
||||||
fn collect_inner_items(&mut self, container: &SyntaxNode) {
|
fn collect_inner_items(&mut self, container: &SyntaxNode) {
|
||||||
let forced_vis = self.forced_visibility.take();
|
let forced_vis = self.forced_visibility.take();
|
||||||
|
|
||||||
let mut current_block = None;
|
let mut block_stack = Vec::new();
|
||||||
for event in container.preorder().skip(1) {
|
for event in container.preorder().skip(1) {
|
||||||
if let WalkEvent::Enter(node) = event {
|
match event {
|
||||||
|
WalkEvent::Enter(node) => {
|
||||||
match_ast! {
|
match_ast! {
|
||||||
match node {
|
match node {
|
||||||
ast::BlockExpr(block) => {
|
ast::BlockExpr(block) => {
|
||||||
current_block = Some(self.source_ast_id_map.ast_id(&block));
|
block_stack.push(self.source_ast_id_map.ast_id(&block));
|
||||||
},
|
},
|
||||||
ast::Item(item) => {
|
ast::Item(item) => {
|
||||||
let mod_items = self.lower_mod_item(&item, true);
|
let mod_items = self.lower_mod_item(&item, true);
|
||||||
|
let current_block = block_stack.last();
|
||||||
if let (Some(mod_items), Some(block)) = (mod_items, current_block) {
|
if let (Some(mod_items), Some(block)) = (mod_items, current_block) {
|
||||||
if !mod_items.0.is_empty() {
|
if !mod_items.0.is_empty() {
|
||||||
self.data().inner_items.entry(block).or_default().extend(mod_items.0.iter().copied());
|
self.data().inner_items.entry(*block).or_default().extend(mod_items.0.iter().copied());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -171,6 +173,12 @@ impl Ctx {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
WalkEvent::Leave(node) => {
|
||||||
|
if ast::BlockExpr::cast(node).is_some() {
|
||||||
|
block_stack.pop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
self.forced_visibility = forced_vis;
|
self.forced_visibility = forced_vis;
|
||||||
|
|
|
@ -61,7 +61,7 @@ use hir_expand::{diagnostics::DiagnosticSink, name::Name, InFile};
|
||||||
use la_arena::Arena;
|
use la_arena::Arena;
|
||||||
use rustc_hash::FxHashMap;
|
use rustc_hash::FxHashMap;
|
||||||
use stdx::format_to;
|
use stdx::format_to;
|
||||||
use syntax::ast;
|
use syntax::{ast, AstNode};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
db::DefDatabase,
|
db::DefDatabase,
|
||||||
|
@ -75,6 +75,7 @@ use crate::{
|
||||||
/// Contains all top-level defs from a macro-expanded crate
|
/// Contains all top-level defs from a macro-expanded crate
|
||||||
#[derive(Debug, PartialEq, Eq)]
|
#[derive(Debug, PartialEq, Eq)]
|
||||||
pub struct DefMap {
|
pub struct DefMap {
|
||||||
|
parent: Option<Arc<DefMap>>,
|
||||||
root: LocalModuleId,
|
root: LocalModuleId,
|
||||||
modules: Arena<ModuleData>,
|
modules: Arena<ModuleData>,
|
||||||
krate: CrateId,
|
krate: CrateId,
|
||||||
|
@ -181,11 +182,40 @@ impl DefMap {
|
||||||
let _p = profile::span("crate_def_map_query").detail(|| {
|
let _p = profile::span("crate_def_map_query").detail(|| {
|
||||||
db.crate_graph()[krate].display_name.as_deref().unwrap_or_default().to_string()
|
db.crate_graph()[krate].display_name.as_deref().unwrap_or_default().to_string()
|
||||||
});
|
});
|
||||||
let def_map = {
|
|
||||||
let edition = db.crate_graph()[krate].edition;
|
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 mut modules: Arena<ModuleData> = Arena::default();
|
||||||
let root = modules.alloc(ModuleData::default());
|
let root = modules.alloc(ModuleData::default());
|
||||||
DefMap {
|
DefMap {
|
||||||
|
parent: None,
|
||||||
krate,
|
krate,
|
||||||
edition,
|
edition,
|
||||||
extern_prelude: FxHashMap::default(),
|
extern_prelude: FxHashMap::default(),
|
||||||
|
@ -194,9 +224,6 @@ impl DefMap {
|
||||||
modules,
|
modules,
|
||||||
diagnostics: Vec::new(),
|
diagnostics: Vec::new(),
|
||||||
}
|
}
|
||||||
};
|
|
||||||
let def_map = collector::collect_defs(db, def_map);
|
|
||||||
Arc::new(def_map)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn add_diagnostics(
|
pub fn add_diagnostics(
|
||||||
|
@ -251,7 +278,12 @@ impl DefMap {
|
||||||
// even), as this should be a great debugging aid.
|
// even), as this should be a great debugging aid.
|
||||||
pub fn dump(&self) -> String {
|
pub fn dump(&self) -> String {
|
||||||
let mut buf = String::new();
|
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;
|
return buf;
|
||||||
|
|
||||||
fn go(buf: &mut String, map: &DefMap, path: &str, module: LocalModuleId) {
|
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)]
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||||
pub enum ModuleSource {
|
pub enum ModuleSource {
|
||||||
SourceFile(ast::SourceFile),
|
SourceFile(ast::SourceFile),
|
||||||
|
|
|
@ -45,7 +45,11 @@ const GLOB_RECURSION_LIMIT: usize = 100;
|
||||||
const EXPANSION_DEPTH_LIMIT: usize = 128;
|
const EXPANSION_DEPTH_LIMIT: usize = 128;
|
||||||
const FIXED_POINT_LIMIT: usize = 8192;
|
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();
|
let crate_graph = db.crate_graph();
|
||||||
|
|
||||||
// populate external prelude
|
// populate external prelude
|
||||||
|
@ -93,6 +97,14 @@ pub(super) fn collect_defs(db: &dyn DefDatabase, mut def_map: DefMap) -> DefMap
|
||||||
exports_proc_macros: false,
|
exports_proc_macros: false,
|
||||||
from_glob_import: Default::default(),
|
from_glob_import: Default::default(),
|
||||||
};
|
};
|
||||||
|
match block {
|
||||||
|
Some(block) => {
|
||||||
|
collector.seed_with_inner(block);
|
||||||
|
}
|
||||||
|
None => {
|
||||||
|
collector.seed_with_top_level();
|
||||||
|
}
|
||||||
|
}
|
||||||
collector.collect();
|
collector.collect();
|
||||||
collector.finish()
|
collector.finish()
|
||||||
}
|
}
|
||||||
|
@ -228,7 +240,7 @@ struct DefCollector<'a> {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl DefCollector<'_> {
|
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 file_id = self.db.crate_graph()[self.def_map.krate].root_file_id;
|
||||||
let item_tree = self.db.item_tree(file_id.into());
|
let item_tree = self.db.item_tree(file_id.into());
|
||||||
let module_id = self.def_map.root;
|
let module_id = self.def_map.root;
|
||||||
|
@ -248,7 +260,31 @@ impl DefCollector<'_> {
|
||||||
}
|
}
|
||||||
.collect(item_tree.top_level_items());
|
.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.
|
// main name resolution fixed-point loop.
|
||||||
let mut i = 0;
|
let mut i = 0;
|
||||||
loop {
|
loop {
|
||||||
|
@ -1470,7 +1506,6 @@ impl ModCollector<'_, '_> {
|
||||||
mod tests {
|
mod tests {
|
||||||
use crate::{db::DefDatabase, test_db::TestDB};
|
use crate::{db::DefDatabase, test_db::TestDB};
|
||||||
use base_db::{fixture::WithFixture, SourceDatabase};
|
use base_db::{fixture::WithFixture, SourceDatabase};
|
||||||
use la_arena::Arena;
|
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
|
@ -1489,6 +1524,7 @@ mod tests {
|
||||||
exports_proc_macros: false,
|
exports_proc_macros: false,
|
||||||
from_glob_import: Default::default(),
|
from_glob_import: Default::default(),
|
||||||
};
|
};
|
||||||
|
collector.seed_with_top_level();
|
||||||
collector.collect();
|
collector.collect();
|
||||||
collector.def_map
|
collector.def_map
|
||||||
}
|
}
|
||||||
|
@ -1497,20 +1533,8 @@ mod tests {
|
||||||
let (db, _file_id) = TestDB::with_single_file(&code);
|
let (db, _file_id) = TestDB::with_single_file(&code);
|
||||||
let krate = db.test_crate();
|
let krate = db.test_crate();
|
||||||
|
|
||||||
let def_map = {
|
|
||||||
let edition = db.crate_graph()[krate].edition;
|
let edition = db.crate_graph()[krate].edition;
|
||||||
let mut modules: Arena<ModuleData> = Arena::default();
|
let def_map = DefMap::empty(krate, edition);
|
||||||
let root = modules.alloc(ModuleData::default());
|
|
||||||
DefMap {
|
|
||||||
krate,
|
|
||||||
edition,
|
|
||||||
extern_prelude: FxHashMap::default(),
|
|
||||||
prelude: None,
|
|
||||||
root,
|
|
||||||
modules,
|
|
||||||
diagnostics: Vec::new(),
|
|
||||||
}
|
|
||||||
};
|
|
||||||
do_collect_defs(&db, def_map)
|
do_collect_defs(&db, def_map)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -103,6 +103,43 @@ impl DefMap {
|
||||||
original_module: LocalModuleId,
|
original_module: LocalModuleId,
|
||||||
path: &ModPath,
|
path: &ModPath,
|
||||||
shadow: BuiltinShadowMode,
|
shadow: BuiltinShadowMode,
|
||||||
|
) -> ResolvePathResult {
|
||||||
|
let mut result = ResolvePathResult::empty(ReachedFixedPoint::No);
|
||||||
|
result.segment_index = Some(usize::max_value());
|
||||||
|
|
||||||
|
let mut current_map = self;
|
||||||
|
loop {
|
||||||
|
let new = current_map.resolve_path_fp_with_macro_single(
|
||||||
|
db,
|
||||||
|
mode,
|
||||||
|
original_module,
|
||||||
|
path,
|
||||||
|
shadow,
|
||||||
|
);
|
||||||
|
|
||||||
|
// Merge `new` into `result`.
|
||||||
|
result.resolved_def = result.resolved_def.or(new.resolved_def);
|
||||||
|
if result.reached_fixedpoint == ReachedFixedPoint::No {
|
||||||
|
result.reached_fixedpoint = new.reached_fixedpoint;
|
||||||
|
}
|
||||||
|
// FIXME: this doesn't seem right; what if the different namespace resolutions come from different crates?
|
||||||
|
result.krate = result.krate.or(new.krate);
|
||||||
|
result.segment_index = result.segment_index.min(new.segment_index);
|
||||||
|
|
||||||
|
match ¤t_map.parent {
|
||||||
|
Some(map) => current_map = map,
|
||||||
|
None => return result,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(super) fn resolve_path_fp_with_macro_single(
|
||||||
|
&self,
|
||||||
|
db: &dyn DefDatabase,
|
||||||
|
mode: ResolveMode,
|
||||||
|
original_module: LocalModuleId,
|
||||||
|
path: &ModPath,
|
||||||
|
shadow: BuiltinShadowMode,
|
||||||
) -> ResolvePathResult {
|
) -> ResolvePathResult {
|
||||||
let mut segments = path.segments.iter().enumerate();
|
let mut segments = path.segments.iter().enumerate();
|
||||||
let mut curr_per_ns: PerNs = match path.kind {
|
let mut curr_per_ns: PerNs = match path.kind {
|
||||||
|
|
|
@ -4,11 +4,13 @@ mod macros;
|
||||||
mod mod_resolution;
|
mod mod_resolution;
|
||||||
mod diagnostics;
|
mod diagnostics;
|
||||||
mod primitives;
|
mod primitives;
|
||||||
|
mod block;
|
||||||
|
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
use base_db::{fixture::WithFixture, SourceDatabase};
|
use base_db::{fixture::WithFixture, SourceDatabase};
|
||||||
use expect_test::{expect, Expect};
|
use expect_test::{expect, Expect};
|
||||||
|
use hir_expand::db::AstDatabase;
|
||||||
use test_utils::mark;
|
use test_utils::mark;
|
||||||
|
|
||||||
use crate::{db::DefDatabase, nameres::*, test_db::TestDB};
|
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)
|
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) {
|
fn check(ra_fixture: &str, expect: Expect) {
|
||||||
let def_map = compute_crate_def_map(ra_fixture);
|
let def_map = compute_crate_def_map(ra_fixture);
|
||||||
let actual = def_map.dump();
|
let actual = def_map.dump();
|
||||||
expect.assert_eq(&actual);
|
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]
|
#[test]
|
||||||
fn crate_def_map_smoke_test() {
|
fn crate_def_map_smoke_test() {
|
||||||
check(
|
check(
|
||||||
|
|
72
crates/hir_def/src/nameres/tests/block.rs
Normal file
72
crates/hir_def/src/nameres/tests/block.rs
Normal file
|
@ -0,0 +1,72 @@
|
||||||
|
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
|
||||||
|
"#]],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn merge_namespaces() {
|
||||||
|
check_at(
|
||||||
|
r#"
|
||||||
|
//- /lib.rs
|
||||||
|
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
|
||||||
|
"#]],
|
||||||
|
);
|
||||||
|
}
|
|
@ -149,6 +149,7 @@ impl RootDatabase {
|
||||||
|
|
||||||
// DefDatabase
|
// DefDatabase
|
||||||
hir::db::ItemTreeQuery
|
hir::db::ItemTreeQuery
|
||||||
|
hir::db::BlockDefMapQuery
|
||||||
hir::db::CrateDefMapQueryQuery
|
hir::db::CrateDefMapQueryQuery
|
||||||
hir::db::StructDataQuery
|
hir::db::StructDataQuery
|
||||||
hir::db::UnionDataQuery
|
hir::db::UnionDataQuery
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue