7359: ItemTree: store a mapping from blocks to inner items r=jonas-schievink a=jonas-schievink

To do name resolution within block expressions, we need to know which inner items are located inside each block expression. This adds such a mapping to `ItemTree`, replacing the previous one, which was seemingly unused other than to access all the inner items.

This also assigns `AstId`s to block expressions, which is needed to store the mapping in salsa.

Co-authored-by: Jonas Schievink <jonasschievink@gmail.com>
This commit is contained in:
bors[bot] 2021-01-20 16:09:22 +00:00 committed by GitHub
commit e62533c3ec
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 49 additions and 32 deletions

View file

@ -69,13 +69,12 @@ impl GenericParamsId {
pub struct ItemTree { pub struct ItemTree {
top_level: SmallVec<[ModItem; 1]>, top_level: SmallVec<[ModItem; 1]>,
attrs: FxHashMap<AttrOwner, RawAttrs>, attrs: FxHashMap<AttrOwner, RawAttrs>,
inner_items: FxHashMap<FileAstId<ast::Item>, SmallVec<[ModItem; 1]>>,
data: Option<Box<ItemTreeData>>, data: Option<Box<ItemTreeData>>,
} }
impl ItemTree { impl ItemTree {
pub fn item_tree_query(db: &dyn DefDatabase, file_id: HirFileId) -> Arc<ItemTree> { pub(crate) fn item_tree_query(db: &dyn DefDatabase, file_id: HirFileId) -> Arc<ItemTree> {
let _p = profile::span("item_tree_query").detail(|| format!("{:?}", file_id)); let _p = profile::span("item_tree_query").detail(|| format!("{:?}", file_id));
let syntax = if let Some(node) = db.parse_or_expand(file_id) { let syntax = if let Some(node) = db.parse_or_expand(file_id) {
node node
@ -117,12 +116,7 @@ impl ItemTree {
} }
fn empty() -> Self { fn empty() -> Self {
Self { Self { top_level: Default::default(), attrs: Default::default(), data: Default::default() }
top_level: Default::default(),
attrs: Default::default(),
inner_items: Default::default(),
data: Default::default(),
}
} }
fn shrink_to_fit(&mut self) { fn shrink_to_fit(&mut self) {
@ -147,6 +141,7 @@ impl ItemTree {
macro_defs, macro_defs,
vis, vis,
generics, generics,
inner_items,
} = &mut **data; } = &mut **data;
imports.shrink_to_fit(); imports.shrink_to_fit();
@ -169,6 +164,8 @@ impl ItemTree {
vis.arena.shrink_to_fit(); vis.arena.shrink_to_fit();
generics.arena.shrink_to_fit(); generics.arena.shrink_to_fit();
inner_items.shrink_to_fit();
} }
} }
@ -191,16 +188,11 @@ impl ItemTree {
self.raw_attrs(of).clone().filter(db, krate) self.raw_attrs(of).clone().filter(db, krate)
} }
/// Returns the lowered inner items that `ast` corresponds to.
///
/// Most AST items are lowered to a single `ModItem`, but some (eg. `use` items) may be lowered
/// to multiple items in the `ItemTree`.
pub fn inner_items(&self, ast: FileAstId<ast::Item>) -> &[ModItem] {
&self.inner_items[&ast]
}
pub fn all_inner_items(&self) -> impl Iterator<Item = ModItem> + '_ { pub fn all_inner_items(&self) -> impl Iterator<Item = ModItem> + '_ {
self.inner_items.values().flatten().copied() match &self.data {
Some(data) => Some(data.inner_items.values().flatten().copied()).into_iter().flatten(),
None => None.into_iter().flatten(),
}
} }
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 {
@ -297,6 +289,8 @@ struct ItemTreeData {
vis: ItemVisibilities, vis: ItemVisibilities,
generics: GenericParamsStorage, generics: GenericParamsStorage,
inner_items: FxHashMap<FileAstId<ast::BlockExpr>, SmallVec<[ModItem; 1]>>,
} }
#[derive(Debug, Eq, PartialEq, Hash)] #[derive(Debug, Eq, PartialEq, Hash)]

View file

@ -6,7 +6,7 @@ use hir_expand::{ast_id_map::AstIdMap, hygiene::Hygiene, name::known, HirFileId}
use smallvec::SmallVec; use smallvec::SmallVec;
use syntax::{ use syntax::{
ast::{self, ModuleItemOwner}, ast::{self, ModuleItemOwner},
SyntaxNode, SyntaxNode, WalkEvent,
}; };
use crate::{ use crate::{
@ -150,14 +150,29 @@ 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 inner_items = mem::take(&mut self.tree.inner_items);
inner_items.extend(container.descendants().skip(1).filter_map(ast::Item::cast).filter_map( let mut current_block = None;
|item| { for event in container.preorder().skip(1) {
let ast_id = self.source_ast_id_map.ast_id(&item); if let WalkEvent::Enter(node) = event {
Some((ast_id, self.lower_mod_item(&item, true)?.0)) match_ast! {
match node {
ast::BlockExpr(block) => {
current_block = Some(self.source_ast_id_map.ast_id(&block));
}, },
)); ast::Item(item) => {
self.tree.inner_items = inner_items; let mod_items = self.lower_mod_item(&item, true);
if let (Some(mod_items), Some(block)) = (mod_items, current_block) {
if !mod_items.0.is_empty() {
self.data().inner_items.entry(block).or_default().extend(mod_items.0.iter().copied());
}
}
},
_ => {}
}
}
}
}
self.forced_visibility = forced_vis; self.forced_visibility = forced_vis;
} }

View file

@ -13,7 +13,7 @@ use std::{
}; };
use la_arena::{Arena, Idx}; use la_arena::{Arena, Idx};
use syntax::{ast, AstNode, AstPtr, SyntaxNode, SyntaxNodePtr}; use syntax::{ast, match_ast, AstNode, AstPtr, SyntaxNode, SyntaxNodePtr};
/// `AstId` points to an AST node in a specific file. /// `AstId` points to an AST node in a specific file.
pub struct FileAstId<N: AstNode> { pub struct FileAstId<N: AstNode> {
@ -72,12 +72,20 @@ impl AstIdMap {
// get lower ids then children. That is, adding a new child does not // get lower ids then children. That is, adding a new child does not
// change parent's id. This means that, say, adding a new function to a // change parent's id. This means that, say, adding a new function to a
// trait does not change ids of top-level items, which helps caching. // trait does not change ids of top-level items, which helps caching.
bdfs(node, |it| match ast::Item::cast(it) { bdfs(node, |it| {
Some(module_item) => { match_ast! {
match it {
ast::Item(module_item) => {
res.alloc(module_item.syntax()); res.alloc(module_item.syntax());
true true
},
ast::BlockExpr(block) => {
res.alloc(block.syntax());
true
},
_ => false,
}
} }
None => false,
}); });
res res
} }