4990: Introduce an ItemTree layer to avoid reparsing files r=matklad a=jonas-schievink

This reduces the latency of "go to definition" in a simple benchmark on rust-analyzer by around 30%.

cc https://github.com/rust-analyzer/rust-analyzer/issues/1650
Closes https://github.com/rust-analyzer/rust-analyzer/issues/3485

Co-authored-by: Aleksey Kladov <aleksey.kladov@gmail.com>
Co-authored-by: Jonas Schievink <jonas.schievink@ferrous-systems.com>
Co-authored-by: Jonas Schievink <jonasschievink@gmail.com>
This commit is contained in:
bors[bot] 2020-06-24 15:07:37 +00:00 committed by GitHub
commit e9bdb05e96
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
26 changed files with 2590 additions and 1046 deletions

1
Cargo.lock generated
View file

@ -1018,6 +1018,7 @@ dependencies = [
"ra_syntax", "ra_syntax",
"ra_tt", "ra_tt",
"rustc-hash", "rustc-hash",
"smallvec",
"stdx", "stdx",
"test_utils", "test_utils",
] ]

View file

@ -116,6 +116,9 @@ impl<T> Arena<T> {
) -> impl Iterator<Item = (Idx<T>, &T)> + ExactSizeIterator + DoubleEndedIterator { ) -> impl Iterator<Item = (Idx<T>, &T)> + ExactSizeIterator + DoubleEndedIterator {
self.data.iter().enumerate().map(|(idx, value)| (Idx::from_raw(RawId(idx as u32)), value)) self.data.iter().enumerate().map(|(idx, value)| (Idx::from_raw(RawId(idx as u32)), value))
} }
pub fn shrink_to_fit(&mut self) {
self.data.shrink_to_fit();
}
} }
impl<T> Default for Arena<T> { impl<T> Default for Arena<T> {

View file

@ -114,7 +114,7 @@ pub trait SourceDatabase: CheckCanceled + FileLoader + std::fmt::Debug {
} }
fn parse_query(db: &impl SourceDatabase, file_id: FileId) -> Parse<ast::SourceFile> { fn parse_query(db: &impl SourceDatabase, file_id: FileId) -> Parse<ast::SourceFile> {
let _p = profile("parse_query"); let _p = profile("parse_query").detail(|| format!("{:?}", file_id));
let text = db.file_text(file_id); let text = db.file_text(file_id);
SourceFile::parse(&*text) SourceFile::parse(&*text)
} }

View file

@ -31,10 +31,7 @@ use hir_ty::{
}; };
use ra_db::{CrateId, CrateName, Edition, FileId}; use ra_db::{CrateId, CrateName, Edition, FileId};
use ra_prof::profile; use ra_prof::profile;
use ra_syntax::{ use ra_syntax::ast::{self, AttrsOwner, NameOwner};
ast::{self, AttrsOwner, NameOwner},
AstNode,
};
use rustc_hash::FxHashSet; use rustc_hash::FxHashSet;
use crate::{ use crate::{
@ -205,7 +202,8 @@ impl ModuleDef {
} }
pub use hir_def::{ pub use hir_def::{
attr::Attrs, item_scope::ItemInNs, visibility::Visibility, AssocItemId, AssocItemLoc, attr::Attrs, item_scope::ItemInNs, item_tree::ItemTreeNode, visibility::Visibility,
AssocItemId, AssocItemLoc,
}; };
impl Module { impl Module {
@ -872,7 +870,7 @@ where
ID: Lookup<Data = AssocItemLoc<AST>>, ID: Lookup<Data = AssocItemLoc<AST>>,
DEF: From<ID>, DEF: From<ID>,
CTOR: FnOnce(DEF) -> AssocItem, CTOR: FnOnce(DEF) -> AssocItem,
AST: AstNode, AST: ItemTreeNode,
{ {
match id.lookup(db.upcast()).container { match id.lookup(db.upcast()).container {
AssocContainerId::TraitId(_) | AssocContainerId::ImplId(_) => Some(ctor(DEF::from(id))), AssocContainerId::TraitId(_) | AssocContainerId::ImplId(_) => Some(ctor(DEF::from(id))),

View file

@ -6,7 +6,7 @@ pub use hir_def::db::{
ExprScopesQuery, FunctionDataQuery, GenericParamsQuery, ImplDataQuery, ImportMapQuery, ExprScopesQuery, FunctionDataQuery, GenericParamsQuery, ImplDataQuery, ImportMapQuery,
InternConstQuery, InternDatabase, InternDatabaseStorage, InternEnumQuery, InternFunctionQuery, InternConstQuery, InternDatabase, InternDatabaseStorage, InternEnumQuery, InternFunctionQuery,
InternImplQuery, InternStaticQuery, InternStructQuery, InternTraitQuery, InternTypeAliasQuery, InternImplQuery, InternStaticQuery, InternStructQuery, InternTraitQuery, InternTypeAliasQuery,
InternUnionQuery, LangItemQuery, ModuleLangItemsQuery, RawItemsQuery, StaticDataQuery, InternUnionQuery, ItemTreeQuery, LangItemQuery, ModuleLangItemsQuery, StaticDataQuery,
StructDataQuery, TraitDataQuery, TypeAliasDataQuery, UnionDataQuery, StructDataQuery, TraitDataQuery, TypeAliasDataQuery, UnionDataQuery,
}; };
pub use hir_expand::db::{ pub use hir_expand::db::{

View file

@ -17,6 +17,7 @@ drop_bomb = "0.1.4"
fst = { version = "0.4", default-features = false } fst = { version = "0.4", default-features = false }
itertools = "0.9.0" itertools = "0.9.0"
indexmap = "1.4.0" indexmap = "1.4.0"
smallvec = "1.4.0"
stdx = { path = "../stdx" } stdx = { path = "../stdx" }

View file

@ -13,7 +13,11 @@ use ra_syntax::{
use tt::Subtree; use tt::Subtree;
use crate::{ use crate::{
db::DefDatabase, nameres::ModuleSource, path::ModPath, src::HasChildSource, src::HasSource, db::DefDatabase,
item_tree::{ItemTreeId, ItemTreeNode},
nameres::ModuleSource,
path::ModPath,
src::HasChildSource,
AdtId, AttrDefId, Lookup, AdtId, AttrDefId, Lookup,
}; };
@ -34,6 +38,8 @@ impl ops::Deref for Attrs {
} }
impl Attrs { impl Attrs {
pub const EMPTY: Attrs = Attrs { entries: None };
pub(crate) fn attrs_query(db: &dyn DefDatabase, def: AttrDefId) -> Attrs { pub(crate) fn attrs_query(db: &dyn DefDatabase, def: AttrDefId) -> Attrs {
match def { match def {
AttrDefId::ModuleId(module) => { AttrDefId::ModuleId(module) => {
@ -65,19 +71,19 @@ impl Attrs {
Attrs::from_attrs_owner(db, src.map(|it| it as &dyn AttrsOwner)) Attrs::from_attrs_owner(db, src.map(|it| it as &dyn AttrsOwner))
} }
AttrDefId::AdtId(it) => match it { AttrDefId::AdtId(it) => match it {
AdtId::StructId(it) => attrs_from_loc(it.lookup(db), db), AdtId::StructId(it) => attrs_from_item_tree(it.lookup(db).id, db),
AdtId::EnumId(it) => attrs_from_loc(it.lookup(db), db), AdtId::EnumId(it) => attrs_from_item_tree(it.lookup(db).id, db),
AdtId::UnionId(it) => attrs_from_loc(it.lookup(db), db), AdtId::UnionId(it) => attrs_from_item_tree(it.lookup(db).id, db),
}, },
AttrDefId::TraitId(it) => attrs_from_loc(it.lookup(db), db), AttrDefId::TraitId(it) => attrs_from_item_tree(it.lookup(db).id, db),
AttrDefId::MacroDefId(it) => { AttrDefId::MacroDefId(it) => {
it.ast_id.map_or_else(Default::default, |ast_id| attrs_from_ast(ast_id, db)) it.ast_id.map_or_else(Default::default, |ast_id| attrs_from_ast(ast_id, db))
} }
AttrDefId::ImplId(it) => attrs_from_loc(it.lookup(db), db), AttrDefId::ImplId(it) => attrs_from_item_tree(it.lookup(db).id, db),
AttrDefId::ConstId(it) => attrs_from_loc(it.lookup(db), db), AttrDefId::ConstId(it) => attrs_from_item_tree(it.lookup(db).id, db),
AttrDefId::StaticId(it) => attrs_from_loc(it.lookup(db), db), AttrDefId::StaticId(it) => attrs_from_item_tree(it.lookup(db).id, db),
AttrDefId::FunctionId(it) => attrs_from_loc(it.lookup(db), db), AttrDefId::FunctionId(it) => attrs_from_item_tree(it.lookup(db).id, db),
AttrDefId::TypeAliasId(it) => attrs_from_loc(it.lookup(db), db), AttrDefId::TypeAliasId(it) => attrs_from_item_tree(it.lookup(db).id, db),
} }
} }
@ -103,6 +109,18 @@ impl Attrs {
Attrs { entries } Attrs { entries }
} }
pub fn merge(&self, other: Attrs) -> Attrs {
match (&self.entries, &other.entries) {
(None, None) => Attrs { entries: None },
(Some(entries), None) | (None, Some(entries)) => {
Attrs { entries: Some(entries.clone()) }
}
(Some(a), Some(b)) => {
Attrs { entries: Some(a.iter().chain(b.iter()).cloned().collect()) }
}
}
}
pub fn by_key(&self, key: &'static str) -> AttrQuery<'_> { pub fn by_key(&self, key: &'static str) -> AttrQuery<'_> {
AttrQuery { attrs: self, key } AttrQuery { attrs: self, key }
} }
@ -187,11 +205,8 @@ where
Attrs::from_attrs_owner(db, src.as_ref().map(|it| it as &dyn AttrsOwner)) Attrs::from_attrs_owner(db, src.as_ref().map(|it| it as &dyn AttrsOwner))
} }
fn attrs_from_loc<T>(node: T, db: &dyn DefDatabase) -> Attrs fn attrs_from_item_tree<N: ItemTreeNode>(id: ItemTreeId<N>, db: &dyn DefDatabase) -> Attrs {
where let tree = db.item_tree(id.file_id);
T: HasSource, let mod_item = N::id_to_mod_item(id.value);
T::Value: ast::AttrsOwner, tree.attrs(mod_item).clone()
{
let src = node.source(db);
Attrs::from_attrs_owner(db, src.as_ref().map(|it| it as &dyn AttrsOwner))
} }

View file

@ -5,7 +5,7 @@ use either::Either;
use hir_expand::{ use hir_expand::{
hygiene::Hygiene, hygiene::Hygiene,
name::{name, AsName, Name}, name::{name, AsName, Name},
HirFileId, MacroDefId, MacroDefKind, AstId, HirFileId, MacroDefId, MacroDefKind,
}; };
use ra_arena::Arena; use ra_arena::Arena;
use ra_syntax::{ use ra_syntax::{
@ -27,6 +27,7 @@ use crate::{
LogicOp, MatchArm, Ordering, Pat, PatId, RecordFieldPat, RecordLitField, Statement, LogicOp, MatchArm, Ordering, Pat, PatId, RecordFieldPat, RecordLitField, Statement,
}, },
item_scope::BuiltinShadowMode, item_scope::BuiltinShadowMode,
item_tree::{FileItemTreeId, ItemTree, ItemTreeNode},
path::{GenericArgs, Path}, path::{GenericArgs, Path},
type_ref::{Mutability, Rawness, TypeRef}, type_ref::{Mutability, Rawness, TypeRef},
AdtId, ConstLoc, ContainerId, DefWithBodyId, EnumLoc, FunctionLoc, Intern, ModuleDefId, AdtId, ConstLoc, ContainerId, DefWithBodyId, EnumLoc, FunctionLoc, Intern, ModuleDefId,
@ -35,6 +36,8 @@ use crate::{
use super::{ExprSource, PatSource}; use super::{ExprSource, PatSource};
use ast::AstChildren; use ast::AstChildren;
use rustc_hash::FxHashMap;
use std::sync::Arc;
pub(crate) struct LowerCtx { pub(crate) struct LowerCtx {
hygiene: Hygiene, hygiene: Hygiene,
@ -60,10 +63,10 @@ pub(super) fn lower(
params: Option<ast::ParamList>, params: Option<ast::ParamList>,
body: Option<ast::Expr>, body: Option<ast::Expr>,
) -> (Body, BodySourceMap) { ) -> (Body, BodySourceMap) {
let item_tree = db.item_tree(expander.current_file_id);
ExprCollector { ExprCollector {
db, db,
def, def,
expander,
source_map: BodySourceMap::default(), source_map: BodySourceMap::default(),
body: Body { body: Body {
exprs: Arena::default(), exprs: Arena::default(),
@ -72,6 +75,12 @@ pub(super) fn lower(
body_expr: dummy_expr_id(), body_expr: dummy_expr_id(),
item_scope: Default::default(), item_scope: Default::default(),
}, },
item_trees: {
let mut map = FxHashMap::default();
map.insert(expander.current_file_id, item_tree);
map
},
expander,
} }
.collect(params, body) .collect(params, body)
} }
@ -82,6 +91,8 @@ struct ExprCollector<'a> {
expander: Expander, expander: Expander,
body: Body, body: Body,
source_map: BodySourceMap, source_map: BodySourceMap,
item_trees: FxHashMap<HirFileId, Arc<ItemTree>>,
} }
impl ExprCollector<'_> { impl ExprCollector<'_> {
@ -533,6 +544,9 @@ impl ExprCollector<'_> {
self.source_map self.source_map
.expansions .expansions
.insert(macro_call, self.expander.current_file_id); .insert(macro_call, self.expander.current_file_id);
let item_tree = self.db.item_tree(self.expander.current_file_id);
self.item_trees.insert(self.expander.current_file_id, item_tree);
let id = self.collect_expr(expansion); let id = self.collect_expr(expansion);
self.expander.exit(self.db, mark); self.expander.exit(self.db, mark);
id id
@ -547,6 +561,19 @@ impl ExprCollector<'_> {
} }
} }
fn find_inner_item<S: ItemTreeNode>(&self, id: AstId<ast::ModuleItem>) -> FileItemTreeId<S> {
let tree = &self.item_trees[&id.file_id];
// FIXME: This probably breaks with `use` items, since they produce multiple item tree nodes
// Root file (non-macro).
tree.all_inner_items()
.chain(tree.top_level_items().iter().copied())
.filter_map(|mod_item| mod_item.downcast::<S>())
.find(|tree_id| tree[*tree_id].ast_id().upcast() == id.value)
.unwrap_or_else(|| panic!("couldn't find inner item for {:?}", id))
}
fn collect_expr_opt(&mut self, expr: Option<ast::Expr>) -> ExprId { fn collect_expr_opt(&mut self, expr: Option<ast::Expr>) -> ExprId {
if let Some(expr) = expr { if let Some(expr) = expr {
self.collect_expr(expr) self.collect_expr(expr)
@ -578,56 +605,102 @@ impl ExprCollector<'_> {
fn collect_block_items(&mut self, block: &ast::BlockExpr) { fn collect_block_items(&mut self, block: &ast::BlockExpr) {
let container = ContainerId::DefWithBodyId(self.def); let container = ContainerId::DefWithBodyId(self.def);
for item in block.items() {
let (def, name): (ModuleDefId, Option<ast::Name>) = match item { let items = block
ast::ModuleItem::FnDef(def) => { .items()
let ast_id = self.expander.ast_id(&def); .filter_map(|item| {
( let (def, name): (ModuleDefId, Option<ast::Name>) = match item {
FunctionLoc { container: container.into(), ast_id }.intern(self.db).into(), ast::ModuleItem::FnDef(def) => {
def.name(), let ast_id = self.expander.ast_id(&def);
) let id = self.find_inner_item(ast_id.map(|id| id.upcast()));
} (
ast::ModuleItem::TypeAliasDef(def) => { FunctionLoc { container: container.into(), id: ast_id.with_value(id) }
let ast_id = self.expander.ast_id(&def); .intern(self.db)
( .into(),
TypeAliasLoc { container: container.into(), ast_id }.intern(self.db).into(), def.name(),
def.name(), )
) }
} ast::ModuleItem::TypeAliasDef(def) => {
ast::ModuleItem::ConstDef(def) => { let ast_id = self.expander.ast_id(&def);
let ast_id = self.expander.ast_id(&def); let id = self.find_inner_item(ast_id.map(|id| id.upcast()));
( (
ConstLoc { container: container.into(), ast_id }.intern(self.db).into(), TypeAliasLoc { container: container.into(), id: ast_id.with_value(id) }
def.name(), .intern(self.db)
) .into(),
} def.name(),
ast::ModuleItem::StaticDef(def) => { )
let ast_id = self.expander.ast_id(&def); }
(StaticLoc { container, ast_id }.intern(self.db).into(), def.name()) ast::ModuleItem::ConstDef(def) => {
} let ast_id = self.expander.ast_id(&def);
ast::ModuleItem::StructDef(def) => { let id = self.find_inner_item(ast_id.map(|id| id.upcast()));
let ast_id = self.expander.ast_id(&def); (
(StructLoc { container, ast_id }.intern(self.db).into(), def.name()) ConstLoc { container: container.into(), id: ast_id.with_value(id) }
} .intern(self.db)
ast::ModuleItem::EnumDef(def) => { .into(),
let ast_id = self.expander.ast_id(&def); def.name(),
(EnumLoc { container, ast_id }.intern(self.db).into(), def.name()) )
} }
ast::ModuleItem::UnionDef(def) => { ast::ModuleItem::StaticDef(def) => {
let ast_id = self.expander.ast_id(&def); let ast_id = self.expander.ast_id(&def);
(UnionLoc { container, ast_id }.intern(self.db).into(), def.name()) let id = self.find_inner_item(ast_id.map(|id| id.upcast()));
} (
ast::ModuleItem::TraitDef(def) => { StaticLoc { container, id: ast_id.with_value(id) }
let ast_id = self.expander.ast_id(&def); .intern(self.db)
(TraitLoc { container, ast_id }.intern(self.db).into(), def.name()) .into(),
} def.name(),
ast::ModuleItem::ExternBlock(_) => continue, // FIXME: collect from extern blocks )
ast::ModuleItem::ImplDef(_) }
| ast::ModuleItem::UseItem(_) ast::ModuleItem::StructDef(def) => {
| ast::ModuleItem::ExternCrateItem(_) let ast_id = self.expander.ast_id(&def);
| ast::ModuleItem::Module(_) let id = self.find_inner_item(ast_id.map(|id| id.upcast()));
| ast::ModuleItem::MacroCall(_) => continue, (
}; StructLoc { container, id: ast_id.with_value(id) }
.intern(self.db)
.into(),
def.name(),
)
}
ast::ModuleItem::EnumDef(def) => {
let ast_id = self.expander.ast_id(&def);
let id = self.find_inner_item(ast_id.map(|id| id.upcast()));
(
EnumLoc { container, id: ast_id.with_value(id) }.intern(self.db).into(),
def.name(),
)
}
ast::ModuleItem::UnionDef(def) => {
let ast_id = self.expander.ast_id(&def);
let id = self.find_inner_item(ast_id.map(|id| id.upcast()));
(
UnionLoc { container, id: ast_id.with_value(id) }
.intern(self.db)
.into(),
def.name(),
)
}
ast::ModuleItem::TraitDef(def) => {
let ast_id = self.expander.ast_id(&def);
let id = self.find_inner_item(ast_id.map(|id| id.upcast()));
(
TraitLoc { container, id: ast_id.with_value(id) }
.intern(self.db)
.into(),
def.name(),
)
}
ast::ModuleItem::ExternBlock(_) => return None, // FIXME: collect from extern blocks
ast::ModuleItem::ImplDef(_)
| ast::ModuleItem::UseItem(_)
| ast::ModuleItem::ExternCrateItem(_)
| ast::ModuleItem::Module(_)
| ast::ModuleItem::MacroCall(_) => return None,
};
Some((def, name))
})
.collect::<Vec<_>>();
for (def, name) in items {
self.body.item_scope.define_def(def); self.body.item_scope.define_def(def);
if let Some(name) = name { if let Some(name) = name {
let vis = crate::visibility::Visibility::Public; // FIXME determine correctly let vis = crate::visibility::Visibility::Public; // FIXME determine correctly

View file

@ -317,6 +317,26 @@ fn foo() {
); );
} }
#[test]
fn macro_inner_item() {
do_check(
r"
macro_rules! mac {
() => {{
fn inner() {}
inner();
}};
}
fn foo() {
mac!();
<|>
}
",
&[],
);
}
fn do_check_local_name(ra_fixture: &str, expected_offset: u32) { fn do_check_local_name(ra_fixture: &str, expected_offset: u32) {
let (db, position) = TestDB::with_position(ra_fixture); let (db, position) = TestDB::with_position(ra_fixture);
let file_id = position.file_id; let file_id = position.file_id;

View file

@ -2,27 +2,19 @@
use std::sync::Arc; use std::sync::Arc;
use hir_expand::{ use hir_expand::{name::Name, InFile};
hygiene::Hygiene,
name::{name, AsName, Name},
AstId, InFile,
};
use ra_prof::profile; use ra_prof::profile;
use ra_syntax::ast::{ use ra_syntax::ast;
self, AssocItem, AstNode, ModuleItemOwner, NameOwner, TypeAscriptionOwner, TypeBoundsOwner,
VisibilityOwner,
};
use crate::{ use crate::{
attr::Attrs, attr::Attrs,
body::LowerCtx, body::Expander,
db::DefDatabase, db::DefDatabase,
path::{path, AssociatedTypeBinding, GenericArgs, Path}, item_tree::{AssocItem, ItemTreeId, ModItem},
src::HasSource, type_ref::{TypeBound, TypeRef},
type_ref::{Mutability, TypeBound, TypeRef},
visibility::RawVisibility, visibility::RawVisibility,
AssocContainerId, AssocItemId, ConstId, ConstLoc, Expander, FunctionId, FunctionLoc, HasModule, AssocContainerId, AssocItemId, ConstId, ConstLoc, FunctionId, FunctionLoc, HasModule, ImplId,
ImplId, Intern, Lookup, StaticId, TraitId, TypeAliasId, TypeAliasLoc, Intern, Lookup, ModuleId, StaticId, TraitId, TypeAliasId, TypeAliasLoc,
}; };
#[derive(Debug, Clone, PartialEq, Eq)] #[derive(Debug, Clone, PartialEq, Eq)]
@ -41,82 +33,27 @@ pub struct FunctionData {
impl FunctionData { impl FunctionData {
pub(crate) fn fn_data_query(db: &impl DefDatabase, func: FunctionId) -> Arc<FunctionData> { pub(crate) fn fn_data_query(db: &impl DefDatabase, func: FunctionId) -> Arc<FunctionData> {
let loc = func.lookup(db); let loc = func.lookup(db);
let src = loc.source(db); let item_tree = db.item_tree(loc.id.file_id);
let ctx = LowerCtx::new(db, src.file_id); let func = &item_tree[loc.id.value];
let name = src.value.name().map(|n| n.as_name()).unwrap_or_else(Name::missing);
let mut params = Vec::new();
let mut has_self_param = false;
if let Some(param_list) = src.value.param_list() {
if let Some(self_param) = param_list.self_param() {
let self_type = if let Some(type_ref) = self_param.ascribed_type() {
TypeRef::from_ast(&ctx, type_ref)
} else {
let self_type = TypeRef::Path(name![Self].into());
match self_param.kind() {
ast::SelfParamKind::Owned => self_type,
ast::SelfParamKind::Ref => {
TypeRef::Reference(Box::new(self_type), Mutability::Shared)
}
ast::SelfParamKind::MutRef => {
TypeRef::Reference(Box::new(self_type), Mutability::Mut)
}
}
};
params.push(self_type);
has_self_param = true;
}
for param in param_list.params() {
let type_ref = TypeRef::from_ast_opt(&ctx, param.ascribed_type());
params.push(type_ref);
}
}
let attrs = Attrs::new(&src.value, &Hygiene::new(db.upcast(), src.file_id));
let ret_type = if let Some(type_ref) = src.value.ret_type().and_then(|rt| rt.type_ref()) { Arc::new(FunctionData {
TypeRef::from_ast(&ctx, type_ref) name: func.name.clone(),
} else { params: func.params.to_vec(),
TypeRef::unit() ret_type: func.ret_type.clone(),
}; attrs: item_tree.attrs(loc.id.value.into()).clone(),
has_self_param: func.has_self_param,
let ret_type = if src.value.async_token().is_some() { is_unsafe: func.is_unsafe,
let future_impl = desugar_future_path(ret_type); visibility: item_tree[func.visibility].clone(),
let ty_bound = TypeBound::Path(future_impl); })
TypeRef::ImplTrait(vec![ty_bound])
} else {
ret_type
};
let is_unsafe = src.value.unsafe_token().is_some();
let vis_default = RawVisibility::default_for_container(loc.container);
let visibility =
RawVisibility::from_ast_with_default(db, vis_default, src.map(|s| s.visibility()));
let sig =
FunctionData { name, params, ret_type, has_self_param, is_unsafe, visibility, attrs };
Arc::new(sig)
} }
} }
fn desugar_future_path(orig: TypeRef) -> Path {
let path = path![core::future::Future];
let mut generic_args: Vec<_> = std::iter::repeat(None).take(path.segments.len() - 1).collect();
let mut last = GenericArgs::empty();
last.bindings.push(AssociatedTypeBinding {
name: name![Output],
type_ref: Some(orig),
bounds: Vec::new(),
});
generic_args.push(Some(Arc::new(last)));
Path::from_known_path(path, generic_args)
}
#[derive(Debug, Clone, PartialEq, Eq)] #[derive(Debug, Clone, PartialEq, Eq)]
pub struct TypeAliasData { pub struct TypeAliasData {
pub name: Name, pub name: Name,
pub type_ref: Option<TypeRef>, pub type_ref: Option<TypeRef>,
pub visibility: RawVisibility, pub visibility: RawVisibility,
/// Bounds restricting the type alias itself (eg. `type Ty: Bound;` in a trait or impl).
pub bounds: Vec<TypeBound>, pub bounds: Vec<TypeBound>,
} }
@ -126,22 +63,15 @@ impl TypeAliasData {
typ: TypeAliasId, typ: TypeAliasId,
) -> Arc<TypeAliasData> { ) -> Arc<TypeAliasData> {
let loc = typ.lookup(db); let loc = typ.lookup(db);
let node = loc.source(db); let item_tree = db.item_tree(loc.id.file_id);
let name = node.value.name().map_or_else(Name::missing, |n| n.as_name()); let typ = &item_tree[loc.id.value];
let lower_ctx = LowerCtx::new(db, node.file_id);
let type_ref = node.value.type_ref().map(|it| TypeRef::from_ast(&lower_ctx, it)); Arc::new(TypeAliasData {
let vis_default = RawVisibility::default_for_container(loc.container); name: typ.name.clone(),
let visibility = RawVisibility::from_ast_with_default( type_ref: typ.type_ref.clone(),
db, visibility: item_tree[typ.visibility].clone(),
vis_default, bounds: typ.bounds.to_vec(),
node.as_ref().map(|n| n.visibility()), })
);
let bounds = if let Some(bound_list) = node.value.type_bound_list() {
bound_list.bounds().map(|it| TypeBound::from_ast(&lower_ctx, it)).collect()
} else {
Vec::new()
};
Arc::new(TypeAliasData { name, type_ref, visibility, bounds })
} }
} }
@ -155,30 +85,24 @@ pub struct TraitData {
impl TraitData { impl TraitData {
pub(crate) fn trait_data_query(db: &dyn DefDatabase, tr: TraitId) -> Arc<TraitData> { pub(crate) fn trait_data_query(db: &dyn DefDatabase, tr: TraitId) -> Arc<TraitData> {
let tr_loc = tr.lookup(db); let tr_loc = tr.lookup(db);
let src = tr_loc.source(db); let item_tree = db.item_tree(tr_loc.id.file_id);
let name = src.value.name().map_or_else(Name::missing, |n| n.as_name()); let tr_def = &item_tree[tr_loc.id.value];
let auto = src.value.auto_token().is_some(); let name = tr_def.name.clone();
let auto = tr_def.auto;
let module_id = tr_loc.container.module(db); let module_id = tr_loc.container.module(db);
let container = AssocContainerId::TraitId(tr); let container = AssocContainerId::TraitId(tr);
let mut items = Vec::new(); let mut expander = Expander::new(db, tr_loc.id.file_id, module_id);
let items = collect_items(
db,
module_id,
&mut expander,
tr_def.items.iter().copied(),
tr_loc.id.file_id,
container,
100,
);
if let Some(item_list) = src.value.item_list() {
let mut expander = Expander::new(db, tr_loc.ast_id.file_id, module_id);
items.extend(collect_items(
db,
&mut expander,
item_list.assoc_items(),
src.file_id,
container,
));
items.extend(collect_items_in_macros(
db,
&mut expander,
&src.with_value(item_list),
container,
));
}
Arc::new(TraitData { name, items, auto }) Arc::new(TraitData { name, items, auto })
} }
@ -209,33 +133,28 @@ impl ImplData {
pub(crate) fn impl_data_query(db: &dyn DefDatabase, id: ImplId) -> Arc<ImplData> { pub(crate) fn impl_data_query(db: &dyn DefDatabase, id: ImplId) -> Arc<ImplData> {
let _p = profile("impl_data_query"); let _p = profile("impl_data_query");
let impl_loc = id.lookup(db); let impl_loc = id.lookup(db);
let src = impl_loc.source(db);
let lower_ctx = LowerCtx::new(db, src.file_id);
let target_trait = src.value.target_trait().map(|it| TypeRef::from_ast(&lower_ctx, it)); let item_tree = db.item_tree(impl_loc.id.file_id);
let target_type = TypeRef::from_ast_opt(&lower_ctx, src.value.target_type()); let impl_def = &item_tree[impl_loc.id.value];
let is_negative = src.value.excl_token().is_some(); let target_trait = impl_def.target_trait.clone();
let target_type = impl_def.target_type.clone();
let is_negative = impl_def.is_negative;
let module_id = impl_loc.container.module(db); let module_id = impl_loc.container.module(db);
let container = AssocContainerId::ImplId(id); let container = AssocContainerId::ImplId(id);
let mut expander = Expander::new(db, impl_loc.id.file_id, module_id);
let mut items: Vec<AssocItemId> = Vec::new(); let items = collect_items(
db,
module_id,
&mut expander,
impl_def.items.iter().copied(),
impl_loc.id.file_id,
container,
100,
);
let items = items.into_iter().map(|(_, item)| item).collect();
if let Some(item_list) = src.value.item_list() { Arc::new(ImplData { target_trait, target_type, items, is_negative })
let mut expander = Expander::new(db, impl_loc.ast_id.file_id, module_id);
items.extend(
collect_items(db, &mut expander, item_list.assoc_items(), src.file_id, container)
.into_iter()
.map(|(_, item)| item),
);
items.extend(
collect_items_in_macros(db, &mut expander, &src.with_value(item_list), container)
.into_iter()
.map(|(_, item)| item),
);
}
let res = ImplData { target_trait, target_type, items, is_negative };
Arc::new(res)
} }
} }
@ -250,22 +169,14 @@ pub struct ConstData {
impl ConstData { impl ConstData {
pub(crate) fn const_data_query(db: &dyn DefDatabase, konst: ConstId) -> Arc<ConstData> { pub(crate) fn const_data_query(db: &dyn DefDatabase, konst: ConstId) -> Arc<ConstData> {
let loc = konst.lookup(db); let loc = konst.lookup(db);
let node = loc.source(db); let item_tree = db.item_tree(loc.id.file_id);
let vis_default = RawVisibility::default_for_container(loc.container); let konst = &item_tree[loc.id.value];
Arc::new(ConstData::new(db, vis_default, node))
}
fn new<N: NameOwner + TypeAscriptionOwner + VisibilityOwner>( Arc::new(ConstData {
db: &dyn DefDatabase, name: konst.name.clone(),
vis_default: RawVisibility, type_ref: konst.type_ref.clone(),
node: InFile<N>, visibility: item_tree[konst.visibility].clone(),
) -> ConstData { })
let ctx = LowerCtx::new(db, node.file_id);
let name = node.value.name().map(|n| n.as_name());
let type_ref = TypeRef::from_ast_opt(&ctx, node.value.ascribed_type());
let visibility =
RawVisibility::from_ast_with_default(db, vis_default, node.map(|n| n.visibility()));
ConstData { name, type_ref, visibility }
} }
} }
@ -279,44 +190,25 @@ pub struct StaticData {
impl StaticData { impl StaticData {
pub(crate) fn static_data_query(db: &dyn DefDatabase, konst: StaticId) -> Arc<StaticData> { pub(crate) fn static_data_query(db: &dyn DefDatabase, konst: StaticId) -> Arc<StaticData> {
let node = konst.lookup(db).source(db); let node = konst.lookup(db);
let ctx = LowerCtx::new(db, node.file_id); let item_tree = db.item_tree(node.id.file_id);
let statik = &item_tree[node.id.value];
let name = node.value.name().map(|n| n.as_name()); Arc::new(StaticData {
let type_ref = TypeRef::from_ast_opt(&ctx, node.value.ascribed_type()); name: Some(statik.name.clone()),
let mutable = node.value.mut_token().is_some(); type_ref: statik.type_ref.clone(),
let visibility = RawVisibility::from_ast_with_default( visibility: item_tree[statik.visibility].clone(),
db, mutable: statik.mutable,
RawVisibility::private(), })
node.map(|n| n.visibility()),
);
Arc::new(StaticData { name, type_ref, visibility, mutable })
} }
} }
fn collect_items_in_macros( fn collect_items(
db: &dyn DefDatabase, db: &dyn DefDatabase,
module: ModuleId,
expander: &mut Expander, expander: &mut Expander,
impl_def: &InFile<ast::ItemList>, assoc_items: impl Iterator<Item = AssocItem>,
container: AssocContainerId, file_id: crate::HirFileId,
) -> Vec<(Name, AssocItemId)> {
let mut res = Vec::new();
// We set a limit to protect against infinite recursion
let limit = 100;
for m in impl_def.value.syntax().children().filter_map(ast::MacroCall::cast) {
res.extend(collect_items_in_macro(db, expander, m, container, limit))
}
res
}
fn collect_items_in_macro(
db: &dyn DefDatabase,
expander: &mut Expander,
m: ast::MacroCall,
container: AssocContainerId, container: AssocContainerId,
limit: usize, limit: usize,
) -> Vec<(Name, AssocItemId)> { ) -> Vec<(Name, AssocItemId)> {
@ -324,62 +216,62 @@ fn collect_items_in_macro(
return Vec::new(); return Vec::new();
} }
if let Some((mark, items)) = expander.enter_expand(db, None, m) { let item_tree = db.item_tree(file_id);
let items: InFile<ast::MacroItems> = expander.to_source(items); let cfg_options = db.crate_graph()[module.krate].cfg_options.clone();
let mut res = collect_items(
db,
expander,
items.value.items().filter_map(|it| AssocItem::cast(it.syntax().clone())),
items.file_id,
container,
);
// Recursive collect macros let mut items = Vec::new();
// Note that ast::ModuleItem do not include ast::MacroCall for item in assoc_items {
// We cannot use ModuleItemOwner::items here match item {
for it in items.value.syntax().children().filter_map(ast::MacroCall::cast) { AssocItem::Function(id) => {
res.extend(collect_items_in_macro(db, expander, it, container, limit - 1)) let item = &item_tree[id];
} let attrs = item_tree.attrs(id.into());
expander.exit(db, mark); if !attrs.is_cfg_enabled(&cfg_options) {
res continue;
} else {
Vec::new()
}
}
fn collect_items(
db: &dyn DefDatabase,
expander: &mut Expander,
assoc_items: impl Iterator<Item = AssocItem>,
file_id: crate::HirFileId,
container: AssocContainerId,
) -> Vec<(Name, AssocItemId)> {
let items = db.ast_id_map(file_id);
assoc_items
.filter_map(|item_node| match item_node {
ast::AssocItem::FnDef(it) => {
let name = it.name().map_or_else(Name::missing, |it| it.as_name());
if !expander.is_cfg_enabled(&it) {
return None;
} }
let def = FunctionLoc { container, ast_id: AstId::new(file_id, items.ast_id(&it)) } let def = FunctionLoc { container, id: ItemTreeId::new(file_id, id) }.intern(db);
.intern(db); items.push((item.name.clone(), def.into()));
Some((name, def.into()))
} }
ast::AssocItem::ConstDef(it) => { // FIXME: cfg?
let name = it.name().map_or_else(Name::missing, |it| it.as_name()); AssocItem::Const(id) => {
let def = ConstLoc { container, ast_id: AstId::new(file_id, items.ast_id(&it)) } let item = &item_tree[id];
.intern(db); let name = match item.name.clone() {
Some((name, def.into())) Some(name) => name,
None => continue,
};
let def = ConstLoc { container, id: ItemTreeId::new(file_id, id) }.intern(db);
items.push((name, def.into()));
} }
ast::AssocItem::TypeAliasDef(it) => { AssocItem::TypeAlias(id) => {
let name = it.name().map_or_else(Name::missing, |it| it.as_name()); let item = &item_tree[id];
let def = let def = TypeAliasLoc { container, id: ItemTreeId::new(file_id, id) }.intern(db);
TypeAliasLoc { container, ast_id: AstId::new(file_id, items.ast_id(&it)) } items.push((item.name.clone(), def.into()));
.intern(db);
Some((name, def.into()))
} }
}) AssocItem::MacroCall(call) => {
.collect() let call = &item_tree[call];
let ast_id_map = db.ast_id_map(file_id);
let root = db.parse_or_expand(file_id).unwrap();
let call = ast_id_map.get(call.ast_id).to_node(&root);
if let Some((mark, mac)) = expander.enter_expand(db, None, call) {
let src: InFile<ast::MacroItems> = expander.to_source(mac);
let item_tree = db.item_tree(src.file_id);
let iter =
item_tree.top_level_items().iter().filter_map(ModItem::as_assoc_item);
items.extend(collect_items(
db,
module,
expander,
iter,
src.file_id,
container,
limit - 1,
));
expander.exit(db, mark);
}
}
}
}
items
} }

View file

@ -14,8 +14,9 @@ use crate::{
docs::Documentation, docs::Documentation,
generics::GenericParams, generics::GenericParams,
import_map::ImportMap, import_map::ImportMap,
item_tree::ItemTree,
lang_item::{LangItemTarget, LangItems}, lang_item::{LangItemTarget, LangItems},
nameres::{raw::RawItems, CrateDefMap}, nameres::CrateDefMap,
AttrDefId, ConstId, ConstLoc, DefWithBodyId, EnumId, EnumLoc, FunctionId, FunctionLoc, AttrDefId, ConstId, ConstLoc, DefWithBodyId, EnumId, EnumLoc, FunctionId, FunctionLoc,
GenericDefId, ImplId, ImplLoc, ModuleId, StaticId, StaticLoc, StructId, StructLoc, TraitId, GenericDefId, ImplId, ImplLoc, ModuleId, StaticId, StaticLoc, StructId, StructLoc, TraitId,
TraitLoc, TypeAliasId, TypeAliasLoc, UnionId, UnionLoc, TraitLoc, TypeAliasId, TypeAliasLoc, UnionId, UnionLoc,
@ -45,8 +46,8 @@ pub trait InternDatabase: SourceDatabase {
#[salsa::query_group(DefDatabaseStorage)] #[salsa::query_group(DefDatabaseStorage)]
pub trait DefDatabase: InternDatabase + AstDatabase + Upcast<dyn AstDatabase> { pub trait DefDatabase: InternDatabase + AstDatabase + Upcast<dyn AstDatabase> {
#[salsa::invoke(RawItems::raw_items_query)] #[salsa::invoke(ItemTree::item_tree_query)]
fn raw_items(&self, file_id: HirFileId) -> Arc<RawItems>; fn item_tree(&self, file_id: HirFileId) -> Arc<ItemTree>;
#[salsa::invoke(crate_def_map_wait)] #[salsa::invoke(crate_def_map_wait)]
#[salsa::transparent] #[salsa::transparent]

View file

@ -42,7 +42,7 @@ pub enum TypeParamProvenance {
} }
/// Data about the generic parameters of a function, struct, impl, etc. /// Data about the generic parameters of a function, struct, impl, etc.
#[derive(Clone, PartialEq, Eq, Debug)] #[derive(Clone, PartialEq, Eq, Debug, Default)]
pub struct GenericParams { pub struct GenericParams {
pub types: Arena<TypeParamData>, pub types: Arena<TypeParamData>,
// lifetimes: Arena<LocalLifetimeParamId, LifetimeParamData>, // lifetimes: Arena<LocalLifetimeParamId, LifetimeParamData>,
@ -74,8 +74,53 @@ impl GenericParams {
def: GenericDefId, def: GenericDefId,
) -> Arc<GenericParams> { ) -> Arc<GenericParams> {
let _p = profile("generic_params_query"); let _p = profile("generic_params_query");
let (params, _source_map) = GenericParams::new(db, def);
Arc::new(params) let generics = match def {
GenericDefId::FunctionId(id) => {
let id = id.lookup(db).id;
let tree = db.item_tree(id.file_id);
let item = &tree[id.value];
tree[item.generic_params].clone()
}
GenericDefId::AdtId(AdtId::StructId(id)) => {
let id = id.lookup(db).id;
let tree = db.item_tree(id.file_id);
let item = &tree[id.value];
tree[item.generic_params].clone()
}
GenericDefId::AdtId(AdtId::EnumId(id)) => {
let id = id.lookup(db).id;
let tree = db.item_tree(id.file_id);
let item = &tree[id.value];
tree[item.generic_params].clone()
}
GenericDefId::AdtId(AdtId::UnionId(id)) => {
let id = id.lookup(db).id;
let tree = db.item_tree(id.file_id);
let item = &tree[id.value];
tree[item.generic_params].clone()
}
GenericDefId::TraitId(id) => {
let id = id.lookup(db).id;
let tree = db.item_tree(id.file_id);
let item = &tree[id.value];
tree[item.generic_params].clone()
}
GenericDefId::TypeAliasId(id) => {
let id = id.lookup(db).id;
let tree = db.item_tree(id.file_id);
let item = &tree[id.value];
tree[item.generic_params].clone()
}
GenericDefId::ImplId(id) => {
let id = id.lookup(db).id;
let tree = db.item_tree(id.file_id);
let item = &tree[id.value];
tree[item.generic_params].clone()
}
GenericDefId::EnumVariantId(_) | GenericDefId::ConstId(_) => GenericParams::default(),
};
Arc::new(generics)
} }
fn new(db: &dyn DefDatabase, def: GenericDefId) -> (GenericParams, InFile<SourceMap>) { fn new(db: &dyn DefDatabase, def: GenericDefId) -> (GenericParams, InFile<SourceMap>) {
@ -156,7 +201,12 @@ impl GenericParams {
(generics, InFile::new(file_id, sm)) (generics, InFile::new(file_id, sm))
} }
fn fill(&mut self, lower_ctx: &LowerCtx, sm: &mut SourceMap, node: &dyn TypeParamsOwner) { pub(crate) fn fill(
&mut self,
lower_ctx: &LowerCtx,
sm: &mut SourceMap,
node: &dyn TypeParamsOwner,
) {
if let Some(params) = node.type_param_list() { if let Some(params) = node.type_param_list() {
self.fill_params(lower_ctx, sm, params) self.fill_params(lower_ctx, sm, params)
} }
@ -165,7 +215,7 @@ impl GenericParams {
} }
} }
fn fill_bounds( pub(crate) fn fill_bounds(
&mut self, &mut self,
lower_ctx: &LowerCtx, lower_ctx: &LowerCtx,
node: &dyn ast::TypeBoundsOwner, node: &dyn ast::TypeBoundsOwner,
@ -229,7 +279,7 @@ impl GenericParams {
.push(WherePredicate { target: WherePredicateTarget::TypeRef(type_ref), bound }); .push(WherePredicate { target: WherePredicateTarget::TypeRef(type_ref), bound });
} }
fn fill_implicit_impl_trait_args(&mut self, type_ref: &TypeRef) { pub(crate) fn fill_implicit_impl_trait_args(&mut self, type_ref: &TypeRef) {
type_ref.walk(&mut |type_ref| { type_ref.walk(&mut |type_ref| {
if let TypeRef::ImplTrait(bounds) = type_ref { if let TypeRef::ImplTrait(bounds) = type_ref {
let param = TypeParamData { let param = TypeParamData {

View file

@ -0,0 +1,697 @@
//! A simplified AST that only contains items.
mod lower;
#[cfg(test)]
mod tests;
use std::{
fmt::{self, Debug},
hash::{Hash, Hasher},
marker::PhantomData,
ops::{Index, Range},
sync::Arc,
};
use ast::{AstNode, AttrsOwner, NameOwner, StructKind, TypeAscriptionOwner};
use either::Either;
use hir_expand::{
ast_id_map::FileAstId,
hygiene::Hygiene,
name::{name, AsName, Name},
HirFileId, InFile,
};
use ra_arena::{Arena, Idx, RawId};
use ra_syntax::{ast, match_ast};
use rustc_hash::FxHashMap;
use smallvec::SmallVec;
use test_utils::mark;
use crate::{
attr::Attrs,
db::DefDatabase,
generics::GenericParams,
path::{path, AssociatedTypeBinding, GenericArgs, ImportAlias, ModPath, Path, PathKind},
type_ref::{Mutability, TypeBound, TypeRef},
visibility::RawVisibility,
};
#[derive(Copy, Clone, Eq, PartialEq)]
pub struct RawVisibilityId(u32);
impl RawVisibilityId {
pub const PUB: Self = RawVisibilityId(u32::max_value());
pub const PRIV: Self = RawVisibilityId(u32::max_value() - 1);
pub const PUB_CRATE: Self = RawVisibilityId(u32::max_value() - 2);
}
impl fmt::Debug for RawVisibilityId {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let mut f = f.debug_tuple("RawVisibilityId");
match *self {
Self::PUB => f.field(&"pub"),
Self::PRIV => f.field(&"pub(self)"),
Self::PUB_CRATE => f.field(&"pub(crate)"),
_ => f.field(&self.0),
};
f.finish()
}
}
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
pub struct GenericParamsId(u32);
impl GenericParamsId {
pub const EMPTY: Self = GenericParamsId(u32::max_value());
}
/// The item tree of a source file.
#[derive(Debug, Eq, PartialEq)]
pub struct ItemTree {
top_level: SmallVec<[ModItem; 1]>,
attrs: FxHashMap<AttrOwner, Attrs>,
inner_items: FxHashMap<FileAstId<ast::ModuleItem>, SmallVec<[ModItem; 1]>>,
data: Option<Box<ItemTreeData>>,
}
impl ItemTree {
pub fn item_tree_query(db: &dyn DefDatabase, file_id: HirFileId) -> Arc<ItemTree> {
let _p = ra_prof::profile("item_tree_query").detail(|| format!("{:?}", file_id));
let syntax = if let Some(node) = db.parse_or_expand(file_id) {
node
} else {
return Arc::new(Self::empty());
};
let hygiene = Hygiene::new(db.upcast(), file_id);
let ctx = lower::Ctx::new(db, hygiene.clone(), file_id);
let mut top_attrs = None;
let mut item_tree = match_ast! {
match syntax {
ast::SourceFile(file) => {
top_attrs = Some(Attrs::new(&file, &hygiene));
ctx.lower_module_items(&file)
},
ast::MacroItems(items) => {
ctx.lower_module_items(&items)
},
// Macros can expand to expressions. We return an empty item tree in this case, but
// still need to collect inner items.
ast::Expr(e) => {
ctx.lower_inner_items(e.syntax())
},
_ => {
panic!("cannot create item tree from {:?}", syntax);
},
}
};
if let Some(attrs) = top_attrs {
item_tree.attrs.insert(AttrOwner::TopLevel, attrs);
}
item_tree.shrink_to_fit();
Arc::new(item_tree)
}
fn empty() -> Self {
Self {
top_level: Default::default(),
attrs: Default::default(),
inner_items: Default::default(),
data: Default::default(),
}
}
fn shrink_to_fit(&mut self) {
if let Some(data) = &mut self.data {
let ItemTreeData {
imports,
extern_crates,
functions,
structs,
fields,
unions,
enums,
variants,
consts,
statics,
traits,
impls,
type_aliases,
mods,
macro_calls,
exprs,
vis,
generics,
} = &mut **data;
imports.shrink_to_fit();
extern_crates.shrink_to_fit();
functions.shrink_to_fit();
structs.shrink_to_fit();
fields.shrink_to_fit();
unions.shrink_to_fit();
enums.shrink_to_fit();
variants.shrink_to_fit();
consts.shrink_to_fit();
statics.shrink_to_fit();
traits.shrink_to_fit();
impls.shrink_to_fit();
type_aliases.shrink_to_fit();
mods.shrink_to_fit();
macro_calls.shrink_to_fit();
exprs.shrink_to_fit();
vis.arena.shrink_to_fit();
generics.arena.shrink_to_fit();
}
}
/// Returns an iterator over all items located at the top level of the `HirFileId` this
/// `ItemTree` was created from.
pub fn top_level_items(&self) -> &[ModItem] {
&self.top_level
}
/// Returns the inner attributes of the source file.
pub fn top_level_attrs(&self) -> &Attrs {
self.attrs.get(&AttrOwner::TopLevel).unwrap_or(&Attrs::EMPTY)
}
pub fn attrs(&self, of: ModItem) -> &Attrs {
self.attrs.get(&AttrOwner::ModItem(of)).unwrap_or(&Attrs::EMPTY)
}
/// 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::ModuleItem>) -> &[ModItem] {
&self.inner_items[&ast]
}
pub fn all_inner_items(&self) -> impl Iterator<Item = ModItem> + '_ {
self.inner_items.values().flatten().copied()
}
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).
let root =
db.parse_or_expand(of.file_id).expect("parse_or_expand failed on constructed ItemTree");
let id = self[of.value].ast_id();
let map = db.ast_id_map(of.file_id);
let ptr = map.get(id);
ptr.to_node(&root)
}
fn data(&self) -> &ItemTreeData {
self.data.as_ref().expect("attempted to access data of empty ItemTree")
}
fn data_mut(&mut self) -> &mut ItemTreeData {
self.data.get_or_insert_with(Box::default)
}
}
#[derive(Default, Debug, Eq, PartialEq)]
struct ItemVisibilities {
arena: Arena<RawVisibility>,
}
impl ItemVisibilities {
fn alloc(&mut self, vis: RawVisibility) -> RawVisibilityId {
match &vis {
RawVisibility::Public => RawVisibilityId::PUB,
RawVisibility::Module(path) if path.segments.is_empty() => match &path.kind {
PathKind::Super(0) => RawVisibilityId::PRIV,
PathKind::Crate => RawVisibilityId::PUB_CRATE,
_ => RawVisibilityId(self.arena.alloc(vis).into_raw().into()),
},
_ => RawVisibilityId(self.arena.alloc(vis).into_raw().into()),
}
}
}
static VIS_PUB: RawVisibility = RawVisibility::Public;
static VIS_PRIV: RawVisibility =
RawVisibility::Module(ModPath { kind: PathKind::Super(0), segments: Vec::new() });
static VIS_PUB_CRATE: RawVisibility =
RawVisibility::Module(ModPath { kind: PathKind::Crate, segments: Vec::new() });
#[derive(Default, Debug, Eq, PartialEq)]
struct GenericParamsStorage {
arena: Arena<GenericParams>,
}
impl GenericParamsStorage {
fn alloc(&mut self, params: GenericParams) -> GenericParamsId {
if params.types.is_empty() && params.where_predicates.is_empty() {
return GenericParamsId::EMPTY;
}
GenericParamsId(self.arena.alloc(params).into_raw().into())
}
}
static EMPTY_GENERICS: GenericParams =
GenericParams { types: Arena::new(), where_predicates: Vec::new() };
#[derive(Default, Debug, Eq, PartialEq)]
struct ItemTreeData {
imports: Arena<Import>,
extern_crates: Arena<ExternCrate>,
functions: Arena<Function>,
structs: Arena<Struct>,
fields: Arena<Field>,
unions: Arena<Union>,
enums: Arena<Enum>,
variants: Arena<Variant>,
consts: Arena<Const>,
statics: Arena<Static>,
traits: Arena<Trait>,
impls: Arena<Impl>,
type_aliases: Arena<TypeAlias>,
mods: Arena<Mod>,
macro_calls: Arena<MacroCall>,
exprs: Arena<Expr>,
vis: ItemVisibilities,
generics: GenericParamsStorage,
}
#[derive(Debug, Eq, PartialEq, Hash)]
enum AttrOwner {
/// Attributes on an item.
ModItem(ModItem),
/// Inner attributes of the source file.
TopLevel,
// FIXME: Store variant and field attrs, and stop reparsing them in `attrs_query`.
}
/// Trait implemented by all nodes in the item tree.
pub trait ItemTreeNode: Clone {
type Source: AstNode + Into<ast::ModuleItem>;
fn ast_id(&self) -> FileAstId<Self::Source>;
/// Looks up an instance of `Self` in an item tree.
fn lookup(tree: &ItemTree, index: Idx<Self>) -> &Self;
/// Downcasts a `ModItem` to a `FileItemTreeId` specific to this type.
fn id_from_mod_item(mod_item: ModItem) -> Option<FileItemTreeId<Self>>;
/// Upcasts a `FileItemTreeId` to a generic `ModItem`.
fn id_to_mod_item(id: FileItemTreeId<Self>) -> ModItem;
}
pub struct FileItemTreeId<N: ItemTreeNode> {
index: Idx<N>,
_p: PhantomData<N>,
}
impl<N: ItemTreeNode> Clone for FileItemTreeId<N> {
fn clone(&self) -> Self {
Self { index: self.index, _p: PhantomData }
}
}
impl<N: ItemTreeNode> Copy for FileItemTreeId<N> {}
impl<N: ItemTreeNode> PartialEq for FileItemTreeId<N> {
fn eq(&self, other: &FileItemTreeId<N>) -> bool {
self.index == other.index
}
}
impl<N: ItemTreeNode> Eq for FileItemTreeId<N> {}
impl<N: ItemTreeNode> Hash for FileItemTreeId<N> {
fn hash<H: Hasher>(&self, state: &mut H) {
self.index.hash(state)
}
}
impl<N: ItemTreeNode> fmt::Debug for FileItemTreeId<N> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.index.fmt(f)
}
}
pub type ItemTreeId<N> = InFile<FileItemTreeId<N>>;
macro_rules! mod_items {
( $( $typ:ident in $fld:ident -> $ast:ty ),+ $(,)? ) => {
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
pub enum ModItem {
$(
$typ(FileItemTreeId<$typ>),
)+
}
$(
impl From<FileItemTreeId<$typ>> for ModItem {
fn from(id: FileItemTreeId<$typ>) -> ModItem {
ModItem::$typ(id)
}
}
)+
$(
impl ItemTreeNode for $typ {
type Source = $ast;
fn ast_id(&self) -> FileAstId<Self::Source> {
self.ast_id
}
fn lookup(tree: &ItemTree, index: Idx<Self>) -> &Self {
&tree.data().$fld[index]
}
fn id_from_mod_item(mod_item: ModItem) -> Option<FileItemTreeId<Self>> {
if let ModItem::$typ(id) = mod_item {
Some(id)
} else {
None
}
}
fn id_to_mod_item(id: FileItemTreeId<Self>) -> ModItem {
ModItem::$typ(id)
}
}
impl Index<Idx<$typ>> for ItemTree {
type Output = $typ;
fn index(&self, index: Idx<$typ>) -> &Self::Output {
&self.data().$fld[index]
}
}
)+
};
}
mod_items! {
Import in imports -> ast::UseItem,
ExternCrate in extern_crates -> ast::ExternCrateItem,
Function in functions -> ast::FnDef,
Struct in structs -> ast::StructDef,
Union in unions -> ast::UnionDef,
Enum in enums -> ast::EnumDef,
Const in consts -> ast::ConstDef,
Static in statics -> ast::StaticDef,
Trait in traits -> ast::TraitDef,
Impl in impls -> ast::ImplDef,
TypeAlias in type_aliases -> ast::TypeAliasDef,
Mod in mods -> ast::Module,
MacroCall in macro_calls -> ast::MacroCall,
}
macro_rules! impl_index {
( $($fld:ident: $t:ty),+ $(,)? ) => {
$(
impl Index<Idx<$t>> for ItemTree {
type Output = $t;
fn index(&self, index: Idx<$t>) -> &Self::Output {
&self.data().$fld[index]
}
}
)+
};
}
impl_index!(fields: Field, variants: Variant, exprs: Expr);
impl Index<RawVisibilityId> for ItemTree {
type Output = RawVisibility;
fn index(&self, index: RawVisibilityId) -> &Self::Output {
match index {
RawVisibilityId::PRIV => &VIS_PRIV,
RawVisibilityId::PUB => &VIS_PUB,
RawVisibilityId::PUB_CRATE => &VIS_PUB_CRATE,
_ => &self.data().vis.arena[Idx::from_raw(index.0.into())],
}
}
}
impl Index<GenericParamsId> for ItemTree {
type Output = GenericParams;
fn index(&self, index: GenericParamsId) -> &Self::Output {
match index {
GenericParamsId::EMPTY => &EMPTY_GENERICS,
_ => &self.data().generics.arena[Idx::from_raw(index.0.into())],
}
}
}
impl<N: ItemTreeNode> Index<FileItemTreeId<N>> for ItemTree {
type Output = N;
fn index(&self, id: FileItemTreeId<N>) -> &N {
N::lookup(self, id.index)
}
}
/// A desugared `use` import.
#[derive(Debug, Clone, Eq, PartialEq)]
pub struct Import {
pub path: ModPath,
pub alias: Option<ImportAlias>,
pub visibility: RawVisibilityId,
pub is_glob: bool,
pub is_prelude: bool,
/// AST ID of the `use` or `extern crate` item this import was derived from. Note that many
/// `Import`s can map to the same `use` item.
pub ast_id: FileAstId<ast::UseItem>,
}
#[derive(Debug, Clone, Eq, PartialEq)]
pub struct ExternCrate {
pub path: ModPath,
pub alias: Option<ImportAlias>,
pub visibility: RawVisibilityId,
/// Whether this is a `#[macro_use] extern crate ...`.
pub is_macro_use: bool,
pub ast_id: FileAstId<ast::ExternCrateItem>,
}
#[derive(Debug, Clone, Eq, PartialEq)]
pub struct Function {
pub name: Name,
pub visibility: RawVisibilityId,
pub generic_params: GenericParamsId,
pub has_self_param: bool,
pub is_unsafe: bool,
pub params: Box<[TypeRef]>,
pub ret_type: TypeRef,
pub ast_id: FileAstId<ast::FnDef>,
}
#[derive(Debug, Clone, Eq, PartialEq)]
pub struct Struct {
pub name: Name,
pub visibility: RawVisibilityId,
pub generic_params: GenericParamsId,
pub fields: Fields,
pub ast_id: FileAstId<ast::StructDef>,
pub kind: StructDefKind,
}
#[derive(Debug, Clone, Eq, PartialEq)]
pub enum StructDefKind {
/// `struct S { ... }` - type namespace only.
Record,
/// `struct S(...);`
Tuple,
/// `struct S;`
Unit,
}
#[derive(Debug, Clone, Eq, PartialEq)]
pub struct Union {
pub name: Name,
pub visibility: RawVisibilityId,
pub generic_params: GenericParamsId,
pub fields: Fields,
pub ast_id: FileAstId<ast::UnionDef>,
}
#[derive(Debug, Clone, Eq, PartialEq)]
pub struct Enum {
pub name: Name,
pub visibility: RawVisibilityId,
pub generic_params: GenericParamsId,
pub variants: Range<Idx<Variant>>,
pub ast_id: FileAstId<ast::EnumDef>,
}
#[derive(Debug, Clone, Eq, PartialEq)]
pub struct Const {
/// const _: () = ();
pub name: Option<Name>,
pub visibility: RawVisibilityId,
pub type_ref: TypeRef,
pub ast_id: FileAstId<ast::ConstDef>,
}
#[derive(Debug, Clone, Eq, PartialEq)]
pub struct Static {
pub name: Name,
pub visibility: RawVisibilityId,
pub mutable: bool,
pub type_ref: TypeRef,
pub ast_id: FileAstId<ast::StaticDef>,
}
#[derive(Debug, Clone, Eq, PartialEq)]
pub struct Trait {
pub name: Name,
pub visibility: RawVisibilityId,
pub generic_params: GenericParamsId,
pub auto: bool,
pub items: Box<[AssocItem]>,
pub ast_id: FileAstId<ast::TraitDef>,
}
#[derive(Debug, Clone, Eq, PartialEq)]
pub struct Impl {
pub generic_params: GenericParamsId,
pub target_trait: Option<TypeRef>,
pub target_type: TypeRef,
pub is_negative: bool,
pub items: Box<[AssocItem]>,
pub ast_id: FileAstId<ast::ImplDef>,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct TypeAlias {
pub name: Name,
pub visibility: RawVisibilityId,
/// Bounds on the type alias itself. Only valid in trait declarations, eg. `type Assoc: Copy;`.
pub bounds: Box<[TypeBound]>,
pub generic_params: GenericParamsId,
pub type_ref: Option<TypeRef>,
pub ast_id: FileAstId<ast::TypeAliasDef>,
}
#[derive(Debug, Clone, Eq, PartialEq)]
pub struct Mod {
pub name: Name,
pub visibility: RawVisibilityId,
pub kind: ModKind,
pub ast_id: FileAstId<ast::Module>,
}
#[derive(Debug, Clone, Eq, PartialEq)]
pub enum ModKind {
/// `mod m { ... }`
Inline { items: Box<[ModItem]> },
/// `mod m;`
Outline {},
}
#[derive(Debug, Clone, Eq, PartialEq)]
pub struct MacroCall {
/// For `macro_rules!` declarations, this is the name of the declared macro.
pub name: Option<Name>,
/// Path to the called macro.
pub path: ModPath,
/// Has `#[macro_export]`.
pub is_export: bool,
/// Has `#[macro_export(local_inner_macros)]`.
pub is_local_inner: bool,
/// Has `#[rustc_builtin_macro]`.
pub is_builtin: bool,
pub ast_id: FileAstId<ast::MacroCall>,
}
// NB: There's no `FileAstId` for `Expr`. The only case where this would be useful is for array
// lengths, but we don't do much with them yet.
#[derive(Debug, Clone, Eq, PartialEq)]
pub struct Expr;
macro_rules! impl_froms {
($e:ident { $($v:ident ($t:ty)),* $(,)? }) => {
$(
impl From<$t> for $e {
fn from(it: $t) -> $e {
$e::$v(it)
}
}
)*
}
}
impl ModItem {
pub fn as_assoc_item(&self) -> Option<AssocItem> {
match self {
ModItem::Import(_)
| ModItem::ExternCrate(_)
| ModItem::Struct(_)
| ModItem::Union(_)
| ModItem::Enum(_)
| ModItem::Static(_)
| ModItem::Trait(_)
| ModItem::Impl(_)
| ModItem::Mod(_) => None,
ModItem::MacroCall(call) => Some(AssocItem::MacroCall(*call)),
ModItem::Const(konst) => Some(AssocItem::Const(*konst)),
ModItem::TypeAlias(alias) => Some(AssocItem::TypeAlias(*alias)),
ModItem::Function(func) => Some(AssocItem::Function(*func)),
}
}
pub fn downcast<N: ItemTreeNode>(self) -> Option<FileItemTreeId<N>> {
N::id_from_mod_item(self)
}
}
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
pub enum AssocItem {
Function(FileItemTreeId<Function>),
TypeAlias(FileItemTreeId<TypeAlias>),
Const(FileItemTreeId<Const>),
MacroCall(FileItemTreeId<MacroCall>),
}
impl_froms!(AssocItem {
Function(FileItemTreeId<Function>),
TypeAlias(FileItemTreeId<TypeAlias>),
Const(FileItemTreeId<Const>),
MacroCall(FileItemTreeId<MacroCall>),
});
impl From<AssocItem> for ModItem {
fn from(item: AssocItem) -> Self {
match item {
AssocItem::Function(it) => it.into(),
AssocItem::TypeAlias(it) => it.into(),
AssocItem::Const(it) => it.into(),
AssocItem::MacroCall(it) => it.into(),
}
}
}
#[derive(Debug, Eq, PartialEq)]
pub struct Variant {
pub name: Name,
pub fields: Fields,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum Fields {
Record(Range<Idx<Field>>),
Tuple(Range<Idx<Field>>),
Unit,
}
/// A single field of an enum variant or struct
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Field {
pub name: Name,
pub type_ref: TypeRef,
pub visibility: RawVisibilityId,
}

View file

@ -0,0 +1,695 @@
//! AST -> `ItemTree` lowering code.
use super::*;
use crate::{
attr::Attrs,
generics::{GenericParams, TypeParamData, TypeParamProvenance},
};
use hir_expand::{ast_id_map::AstIdMap, hygiene::Hygiene, HirFileId};
use ra_arena::map::ArenaMap;
use ra_syntax::{
ast::{self, ModuleItemOwner},
SyntaxNode,
};
use smallvec::SmallVec;
use std::{collections::hash_map::Entry, mem, sync::Arc};
fn id<N: ItemTreeNode>(index: Idx<N>) -> FileItemTreeId<N> {
FileItemTreeId { index, _p: PhantomData }
}
struct ModItems(SmallVec<[ModItem; 1]>);
impl<T> From<T> for ModItems
where
T: Into<ModItem>,
{
fn from(t: T) -> Self {
ModItems(SmallVec::from_buf([t.into(); 1]))
}
}
pub(super) struct Ctx {
tree: ItemTree,
hygiene: Hygiene,
file: HirFileId,
source_ast_id_map: Arc<AstIdMap>,
body_ctx: crate::body::LowerCtx,
inner_items: Vec<ModItem>,
forced_visibility: Option<RawVisibilityId>,
}
impl Ctx {
pub(super) fn new(db: &dyn DefDatabase, hygiene: Hygiene, file: HirFileId) -> Self {
Self {
tree: ItemTree::empty(),
hygiene,
file,
source_ast_id_map: db.ast_id_map(file),
body_ctx: crate::body::LowerCtx::new(db, file),
inner_items: Vec::new(),
forced_visibility: None,
}
}
pub(super) fn lower_module_items(mut self, item_owner: &dyn ModuleItemOwner) -> ItemTree {
self.tree.top_level = item_owner
.items()
.flat_map(|item| self.lower_mod_item(&item, false))
.flat_map(|items| items.0)
.collect();
self.tree
}
pub(super) fn lower_inner_items(mut self, within: &SyntaxNode) -> ItemTree {
self.collect_inner_items(within);
self.tree
}
fn data(&mut self) -> &mut ItemTreeData {
self.tree.data_mut()
}
fn lower_mod_item(&mut self, item: &ast::ModuleItem, inner: bool) -> Option<ModItems> {
assert!(inner || self.inner_items.is_empty());
// Collect inner items for 1-to-1-lowered items.
match item {
ast::ModuleItem::StructDef(_)
| ast::ModuleItem::UnionDef(_)
| ast::ModuleItem::EnumDef(_)
| ast::ModuleItem::FnDef(_)
| ast::ModuleItem::TypeAliasDef(_)
| ast::ModuleItem::ConstDef(_)
| ast::ModuleItem::StaticDef(_)
| ast::ModuleItem::MacroCall(_) => {
// Skip this if we're already collecting inner items. We'll descend into all nodes
// already.
if !inner {
self.collect_inner_items(item.syntax());
}
}
// These are handled in their respective `lower_X` method (since we can't just blindly
// walk them).
ast::ModuleItem::TraitDef(_)
| ast::ModuleItem::ImplDef(_)
| ast::ModuleItem::ExternBlock(_) => {}
// These don't have inner items.
ast::ModuleItem::Module(_)
| ast::ModuleItem::ExternCrateItem(_)
| ast::ModuleItem::UseItem(_) => {}
};
let attrs = Attrs::new(item, &self.hygiene);
let items = match item {
ast::ModuleItem::StructDef(ast) => self.lower_struct(ast).map(Into::into),
ast::ModuleItem::UnionDef(ast) => self.lower_union(ast).map(Into::into),
ast::ModuleItem::EnumDef(ast) => self.lower_enum(ast).map(Into::into),
ast::ModuleItem::FnDef(ast) => self.lower_function(ast).map(Into::into),
ast::ModuleItem::TypeAliasDef(ast) => self.lower_type_alias(ast).map(Into::into),
ast::ModuleItem::StaticDef(ast) => self.lower_static(ast).map(Into::into),
ast::ModuleItem::ConstDef(ast) => Some(self.lower_const(ast).into()),
ast::ModuleItem::Module(ast) => self.lower_module(ast).map(Into::into),
ast::ModuleItem::TraitDef(ast) => self.lower_trait(ast).map(Into::into),
ast::ModuleItem::ImplDef(ast) => self.lower_impl(ast).map(Into::into),
ast::ModuleItem::UseItem(ast) => Some(ModItems(
self.lower_use(ast).into_iter().map(Into::into).collect::<SmallVec<_>>(),
)),
ast::ModuleItem::ExternCrateItem(ast) => self.lower_extern_crate(ast).map(Into::into),
ast::ModuleItem::MacroCall(ast) => self.lower_macro_call(ast).map(Into::into),
ast::ModuleItem::ExternBlock(ast) => {
Some(ModItems(self.lower_extern_block(ast).into_iter().collect::<SmallVec<_>>()))
}
};
if !attrs.is_empty() {
for item in items.iter().flat_map(|items| &items.0) {
self.add_attrs(*item, attrs.clone());
}
}
items
}
fn add_attrs(&mut self, item: ModItem, attrs: Attrs) {
match self.tree.attrs.entry(AttrOwner::ModItem(item)) {
Entry::Occupied(mut entry) => {
*entry.get_mut() = entry.get().merge(attrs);
}
Entry::Vacant(entry) => {
entry.insert(attrs);
}
}
}
fn collect_inner_items(&mut self, container: &SyntaxNode) {
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::ModuleItem::cast).filter_map(|item| {
let ast_id = self.source_ast_id_map.ast_id(&item);
Some((ast_id, self.lower_mod_item(&item, true)?.0))
}),
);
self.tree.inner_items = inner_items;
self.forced_visibility = forced_vis;
}
fn lower_assoc_item(&mut self, item: &ast::ModuleItem) -> Option<AssocItem> {
match item {
ast::ModuleItem::FnDef(ast) => self.lower_function(ast).map(Into::into),
ast::ModuleItem::TypeAliasDef(ast) => self.lower_type_alias(ast).map(Into::into),
ast::ModuleItem::ConstDef(ast) => Some(self.lower_const(ast).into()),
ast::ModuleItem::MacroCall(ast) => self.lower_macro_call(ast).map(Into::into),
_ => None,
}
}
fn lower_struct(&mut self, strukt: &ast::StructDef) -> Option<FileItemTreeId<Struct>> {
let visibility = self.lower_visibility(strukt);
let name = strukt.name()?.as_name();
let generic_params = self.lower_generic_params(GenericsOwner::Struct, strukt);
let fields = self.lower_fields(&strukt.kind());
let ast_id = self.source_ast_id_map.ast_id(strukt);
let kind = match strukt.kind() {
ast::StructKind::Record(_) => StructDefKind::Record,
ast::StructKind::Tuple(_) => StructDefKind::Tuple,
ast::StructKind::Unit => StructDefKind::Unit,
};
let res = Struct { name, visibility, generic_params, fields, ast_id, kind };
Some(id(self.data().structs.alloc(res)))
}
fn lower_fields(&mut self, strukt_kind: &ast::StructKind) -> Fields {
match strukt_kind {
ast::StructKind::Record(it) => {
let range = self.lower_record_fields(it);
Fields::Record(range)
}
ast::StructKind::Tuple(it) => {
let range = self.lower_tuple_fields(it);
Fields::Tuple(range)
}
ast::StructKind::Unit => Fields::Unit,
}
}
fn lower_record_fields(&mut self, fields: &ast::RecordFieldDefList) -> Range<Idx<Field>> {
let start = self.next_field_idx();
for field in fields.fields() {
if let Some(data) = self.lower_record_field(&field) {
self.data().fields.alloc(data);
}
}
let end = self.next_field_idx();
start..end
}
fn lower_record_field(&mut self, field: &ast::RecordFieldDef) -> Option<Field> {
let name = field.name()?.as_name();
let visibility = self.lower_visibility(field);
let type_ref = self.lower_type_ref(&field.ascribed_type()?);
let res = Field { name, type_ref, visibility };
Some(res)
}
fn lower_tuple_fields(&mut self, fields: &ast::TupleFieldDefList) -> Range<Idx<Field>> {
let start = self.next_field_idx();
for (i, field) in fields.fields().enumerate() {
if let Some(data) = self.lower_tuple_field(i, &field) {
self.data().fields.alloc(data);
}
}
let end = self.next_field_idx();
start..end
}
fn lower_tuple_field(&mut self, idx: usize, field: &ast::TupleFieldDef) -> Option<Field> {
let name = Name::new_tuple_field(idx);
let visibility = self.lower_visibility(field);
let type_ref = self.lower_type_ref(&field.type_ref()?);
let res = Field { name, type_ref, visibility };
Some(res)
}
fn lower_union(&mut self, union: &ast::UnionDef) -> Option<FileItemTreeId<Union>> {
let visibility = self.lower_visibility(union);
let name = union.name()?.as_name();
let generic_params = self.lower_generic_params(GenericsOwner::Union, union);
let fields = match union.record_field_def_list() {
Some(record_field_def_list) => {
self.lower_fields(&StructKind::Record(record_field_def_list))
}
None => Fields::Record(self.next_field_idx()..self.next_field_idx()),
};
let ast_id = self.source_ast_id_map.ast_id(union);
let res = Union { name, visibility, generic_params, fields, ast_id };
Some(id(self.data().unions.alloc(res)))
}
fn lower_enum(&mut self, enum_: &ast::EnumDef) -> Option<FileItemTreeId<Enum>> {
let visibility = self.lower_visibility(enum_);
let name = enum_.name()?.as_name();
let generic_params = self.lower_generic_params(GenericsOwner::Enum, enum_);
let variants = match &enum_.variant_list() {
Some(variant_list) => self.lower_variants(variant_list),
None => self.next_variant_idx()..self.next_variant_idx(),
};
let ast_id = self.source_ast_id_map.ast_id(enum_);
let res = Enum { name, visibility, generic_params, variants, ast_id };
Some(id(self.data().enums.alloc(res)))
}
fn lower_variants(&mut self, variants: &ast::EnumVariantList) -> Range<Idx<Variant>> {
let start = self.next_variant_idx();
for variant in variants.variants() {
if let Some(data) = self.lower_variant(&variant) {
self.data().variants.alloc(data);
}
}
let end = self.next_variant_idx();
start..end
}
fn lower_variant(&mut self, variant: &ast::EnumVariant) -> Option<Variant> {
let name = variant.name()?.as_name();
let fields = self.lower_fields(&variant.kind());
let res = Variant { name, fields };
Some(res)
}
fn lower_function(&mut self, func: &ast::FnDef) -> Option<FileItemTreeId<Function>> {
let visibility = self.lower_visibility(func);
let name = func.name()?.as_name();
let mut params = Vec::new();
let mut has_self_param = false;
if let Some(param_list) = func.param_list() {
if let Some(self_param) = param_list.self_param() {
let self_type = match self_param.ascribed_type() {
Some(type_ref) => TypeRef::from_ast(&self.body_ctx, type_ref),
None => {
let self_type = TypeRef::Path(name![Self].into());
match self_param.kind() {
ast::SelfParamKind::Owned => self_type,
ast::SelfParamKind::Ref => {
TypeRef::Reference(Box::new(self_type), Mutability::Shared)
}
ast::SelfParamKind::MutRef => {
TypeRef::Reference(Box::new(self_type), Mutability::Mut)
}
}
}
};
params.push(self_type);
has_self_param = true;
}
for param in param_list.params() {
let type_ref = TypeRef::from_ast_opt(&self.body_ctx, param.ascribed_type());
params.push(type_ref);
}
}
let ret_type = match func.ret_type().and_then(|rt| rt.type_ref()) {
Some(type_ref) => TypeRef::from_ast(&self.body_ctx, type_ref),
_ => TypeRef::unit(),
};
let ret_type = if func.async_token().is_some() {
let future_impl = desugar_future_path(ret_type);
let ty_bound = TypeBound::Path(future_impl);
TypeRef::ImplTrait(vec![ty_bound])
} else {
ret_type
};
let ast_id = self.source_ast_id_map.ast_id(func);
let mut res = Function {
name,
visibility,
generic_params: GenericParamsId::EMPTY,
has_self_param,
is_unsafe: func.unsafe_token().is_some(),
params: params.into_boxed_slice(),
ret_type,
ast_id,
};
res.generic_params = self.lower_generic_params(GenericsOwner::Function(&res), func);
Some(id(self.data().functions.alloc(res)))
}
fn lower_type_alias(
&mut self,
type_alias: &ast::TypeAliasDef,
) -> Option<FileItemTreeId<TypeAlias>> {
let name = type_alias.name()?.as_name();
let type_ref = type_alias.type_ref().map(|it| self.lower_type_ref(&it));
let visibility = self.lower_visibility(type_alias);
let bounds = self.lower_type_bounds(type_alias);
let generic_params = self.lower_generic_params(GenericsOwner::TypeAlias, type_alias);
let ast_id = self.source_ast_id_map.ast_id(type_alias);
let res = TypeAlias {
name,
visibility,
bounds: bounds.into_boxed_slice(),
generic_params,
type_ref,
ast_id,
};
Some(id(self.data().type_aliases.alloc(res)))
}
fn lower_static(&mut self, static_: &ast::StaticDef) -> Option<FileItemTreeId<Static>> {
let name = static_.name()?.as_name();
let type_ref = self.lower_type_ref_opt(static_.ascribed_type());
let visibility = self.lower_visibility(static_);
let mutable = static_.mut_token().is_some();
let ast_id = self.source_ast_id_map.ast_id(static_);
let res = Static { name, visibility, mutable, type_ref, ast_id };
Some(id(self.data().statics.alloc(res)))
}
fn lower_const(&mut self, konst: &ast::ConstDef) -> FileItemTreeId<Const> {
let name = konst.name().map(|it| it.as_name());
let type_ref = self.lower_type_ref_opt(konst.ascribed_type());
let visibility = self.lower_visibility(konst);
let ast_id = self.source_ast_id_map.ast_id(konst);
let res = Const { name, visibility, type_ref, ast_id };
id(self.data().consts.alloc(res))
}
fn lower_module(&mut self, module: &ast::Module) -> Option<FileItemTreeId<Mod>> {
let name = module.name()?.as_name();
let visibility = self.lower_visibility(module);
let kind = if module.semicolon_token().is_some() {
ModKind::Outline {}
} else {
ModKind::Inline {
items: module
.item_list()
.map(|list| {
list.items()
.flat_map(|item| self.lower_mod_item(&item, false))
.flat_map(|items| items.0)
.collect()
})
.unwrap_or_else(|| {
mark::hit!(name_res_works_for_broken_modules);
Box::new([]) as Box<[_]>
}),
}
};
let ast_id = self.source_ast_id_map.ast_id(module);
let res = Mod { name, visibility, kind, ast_id };
Some(id(self.data().mods.alloc(res)))
}
fn lower_trait(&mut self, trait_def: &ast::TraitDef) -> Option<FileItemTreeId<Trait>> {
let name = trait_def.name()?.as_name();
let visibility = self.lower_visibility(trait_def);
let generic_params =
self.lower_generic_params_and_inner_items(GenericsOwner::Trait(trait_def), trait_def);
let auto = trait_def.auto_token().is_some();
let items = trait_def.item_list().map(|list| {
self.with_inherited_visibility(visibility, |this| {
list.items()
.filter_map(|item| {
let attrs = Attrs::new(&item, &this.hygiene);
this.collect_inner_items(item.syntax());
this.lower_assoc_item(&item).map(|item| {
this.add_attrs(item.into(), attrs);
item
})
})
.collect()
})
});
let ast_id = self.source_ast_id_map.ast_id(trait_def);
let res = Trait {
name,
visibility,
generic_params,
auto,
items: items.unwrap_or_default(),
ast_id,
};
Some(id(self.data().traits.alloc(res)))
}
fn lower_impl(&mut self, impl_def: &ast::ImplDef) -> Option<FileItemTreeId<Impl>> {
let generic_params =
self.lower_generic_params_and_inner_items(GenericsOwner::Impl, impl_def);
let target_trait = impl_def.target_trait().map(|tr| self.lower_type_ref(&tr));
let target_type = self.lower_type_ref(&impl_def.target_type()?);
let is_negative = impl_def.excl_token().is_some();
// We cannot use `assoc_items()` here as that does not include macro calls.
let items = impl_def
.item_list()?
.items()
.filter_map(|item| {
self.collect_inner_items(item.syntax());
let assoc = self.lower_assoc_item(&item)?;
let attrs = Attrs::new(&item, &self.hygiene);
self.add_attrs(assoc.into(), attrs);
Some(assoc)
})
.collect();
let ast_id = self.source_ast_id_map.ast_id(impl_def);
let res = Impl { generic_params, target_trait, target_type, is_negative, items, ast_id };
Some(id(self.data().impls.alloc(res)))
}
fn lower_use(&mut self, use_item: &ast::UseItem) -> Vec<FileItemTreeId<Import>> {
// FIXME: cfg_attr
let is_prelude = use_item.has_atom_attr("prelude_import");
let visibility = self.lower_visibility(use_item);
let ast_id = self.source_ast_id_map.ast_id(use_item);
// Every use item can expand to many `Import`s.
let mut imports = Vec::new();
let tree = self.tree.data_mut();
ModPath::expand_use_item(
InFile::new(self.file, use_item.clone()),
&self.hygiene,
|path, _tree, is_glob, alias| {
imports.push(id(tree.imports.alloc(Import {
path,
alias,
visibility,
is_glob,
is_prelude,
ast_id,
})));
},
);
imports
}
fn lower_extern_crate(
&mut self,
extern_crate: &ast::ExternCrateItem,
) -> Option<FileItemTreeId<ExternCrate>> {
let path = ModPath::from_name_ref(&extern_crate.name_ref()?);
let alias = extern_crate.alias().map(|a| {
a.name().map(|it| it.as_name()).map_or(ImportAlias::Underscore, ImportAlias::Alias)
});
let visibility = self.lower_visibility(extern_crate);
let ast_id = self.source_ast_id_map.ast_id(extern_crate);
// FIXME: cfg_attr
let is_macro_use = extern_crate.has_atom_attr("macro_use");
let res = ExternCrate { path, alias, visibility, is_macro_use, ast_id };
Some(id(self.data().extern_crates.alloc(res)))
}
fn lower_macro_call(&mut self, m: &ast::MacroCall) -> Option<FileItemTreeId<MacroCall>> {
let name = m.name().map(|it| it.as_name());
let attrs = Attrs::new(m, &self.hygiene);
let path = ModPath::from_src(m.path()?, &self.hygiene)?;
let ast_id = self.source_ast_id_map.ast_id(m);
// FIXME: cfg_attr
let export_attr = attrs.by_key("macro_export");
let is_export = export_attr.exists();
let is_local_inner = if is_export {
export_attr.tt_values().map(|it| &it.token_trees).flatten().any(|it| match it {
tt::TokenTree::Leaf(tt::Leaf::Ident(ident)) => {
ident.text.contains("local_inner_macros")
}
_ => false,
})
} else {
false
};
let is_builtin = attrs.by_key("rustc_builtin_macro").exists();
let res = MacroCall { name, path, is_export, is_builtin, is_local_inner, ast_id };
Some(id(self.data().macro_calls.alloc(res)))
}
fn lower_extern_block(&mut self, block: &ast::ExternBlock) -> Vec<ModItem> {
block.extern_item_list().map_or(Vec::new(), |list| {
list.extern_items()
.filter_map(|item| {
self.collect_inner_items(item.syntax());
let attrs = Attrs::new(&item, &self.hygiene);
let id = match item {
ast::ExternItem::FnDef(ast) => {
let func = self.lower_function(&ast)?;
func.into()
}
ast::ExternItem::StaticDef(ast) => {
let statik = self.lower_static(&ast)?;
statik.into()
}
};
self.add_attrs(id, attrs);
Some(id)
})
.collect()
})
}
/// Lowers generics defined on `node` and collects inner items defined within.
fn lower_generic_params_and_inner_items(
&mut self,
owner: GenericsOwner<'_>,
node: &impl ast::TypeParamsOwner,
) -> GenericParamsId {
// Generics are part of item headers and may contain inner items we need to collect.
if let Some(params) = node.type_param_list() {
self.collect_inner_items(params.syntax());
}
if let Some(clause) = node.where_clause() {
self.collect_inner_items(clause.syntax());
}
self.lower_generic_params(owner, node)
}
fn lower_generic_params(
&mut self,
owner: GenericsOwner<'_>,
node: &impl ast::TypeParamsOwner,
) -> GenericParamsId {
let mut sm = &mut ArenaMap::default();
let mut generics = GenericParams::default();
match owner {
GenericsOwner::Function(func) => {
generics.fill(&self.body_ctx, sm, node);
// lower `impl Trait` in arguments
for param in &*func.params {
generics.fill_implicit_impl_trait_args(param);
}
}
GenericsOwner::Struct
| GenericsOwner::Enum
| GenericsOwner::Union
| GenericsOwner::TypeAlias => {
generics.fill(&self.body_ctx, sm, node);
}
GenericsOwner::Trait(trait_def) => {
// traits get the Self type as an implicit first type parameter
let self_param_id = generics.types.alloc(TypeParamData {
name: Some(name![Self]),
default: None,
provenance: TypeParamProvenance::TraitSelf,
});
sm.insert(self_param_id, Either::Left(trait_def.clone()));
// add super traits as bounds on Self
// i.e., trait Foo: Bar is equivalent to trait Foo where Self: Bar
let self_param = TypeRef::Path(name![Self].into());
generics.fill_bounds(&self.body_ctx, trait_def, self_param);
generics.fill(&self.body_ctx, &mut sm, node);
}
GenericsOwner::Impl => {
// Note that we don't add `Self` here: in `impl`s, `Self` is not a
// type-parameter, but rather is a type-alias for impl's target
// type, so this is handled by the resolver.
generics.fill(&self.body_ctx, &mut sm, node);
}
}
self.data().generics.alloc(generics)
}
fn lower_type_bounds(&mut self, node: &impl ast::TypeBoundsOwner) -> Vec<TypeBound> {
match node.type_bound_list() {
Some(bound_list) => {
bound_list.bounds().map(|it| TypeBound::from_ast(&self.body_ctx, it)).collect()
}
None => Vec::new(),
}
}
fn lower_visibility(&mut self, item: &impl ast::VisibilityOwner) -> RawVisibilityId {
let vis = match self.forced_visibility {
Some(vis) => return vis,
None => RawVisibility::from_ast_with_hygiene(item.visibility(), &self.hygiene),
};
self.data().vis.alloc(vis)
}
fn lower_type_ref(&self, type_ref: &ast::TypeRef) -> TypeRef {
TypeRef::from_ast(&self.body_ctx, type_ref.clone())
}
fn lower_type_ref_opt(&self, type_ref: Option<ast::TypeRef>) -> TypeRef {
type_ref.map(|ty| self.lower_type_ref(&ty)).unwrap_or(TypeRef::Error)
}
/// Forces the visibility `vis` to be used for all items lowered during execution of `f`.
fn with_inherited_visibility<R>(
&mut self,
vis: RawVisibilityId,
f: impl FnOnce(&mut Self) -> R,
) -> R {
let old = mem::replace(&mut self.forced_visibility, Some(vis));
let res = f(self);
self.forced_visibility = old;
res
}
fn next_field_idx(&self) -> Idx<Field> {
Idx::from_raw(RawId::from(
self.tree.data.as_ref().map_or(0, |data| data.fields.len() as u32),
))
}
fn next_variant_idx(&self) -> Idx<Variant> {
Idx::from_raw(RawId::from(
self.tree.data.as_ref().map_or(0, |data| data.variants.len() as u32),
))
}
}
fn desugar_future_path(orig: TypeRef) -> Path {
let path = path![core::future::Future];
let mut generic_args: Vec<_> = std::iter::repeat(None).take(path.segments.len() - 1).collect();
let mut last = GenericArgs::empty();
let binding =
AssociatedTypeBinding { name: name![Output], type_ref: Some(orig), bounds: Vec::new() };
last.bindings.push(binding);
generic_args.push(Some(Arc::new(last)));
Path::from_known_path(path, generic_args)
}
enum GenericsOwner<'a> {
/// We need access to the partially-lowered `Function` for lowering `impl Trait` in argument
/// position.
Function(&'a Function),
Struct,
Enum,
Union,
/// The `TraitDef` is needed to fill the source map for the implicit `Self` parameter.
Trait(&'a ast::TraitDef),
TypeAlias,
Impl,
}

View file

@ -0,0 +1,435 @@
use super::{ItemTree, ModItem, ModKind};
use crate::{db::DefDatabase, test_db::TestDB};
use hir_expand::{db::AstDatabase, HirFileId, InFile};
use insta::assert_snapshot;
use ra_db::fixture::WithFixture;
use ra_syntax::{ast, AstNode};
use rustc_hash::FxHashSet;
use std::sync::Arc;
use stdx::format_to;
fn test_inner_items(ra_fixture: &str) {
let (db, file_id) = TestDB::with_single_file(ra_fixture);
let file_id = HirFileId::from(file_id);
let tree = db.item_tree(file_id);
let root = db.parse_or_expand(file_id).unwrap();
let ast_id_map = db.ast_id_map(file_id);
// Traverse the item tree and collect all module/impl/trait-level items as AST nodes.
let mut outer_items = FxHashSet::default();
let mut worklist = tree.top_level_items().to_vec();
while let Some(item) = worklist.pop() {
let node: ast::ModuleItem = match item {
ModItem::Import(it) => tree.source(&db, InFile::new(file_id, it)).into(),
ModItem::ExternCrate(it) => tree.source(&db, InFile::new(file_id, it)).into(),
ModItem::Function(it) => tree.source(&db, InFile::new(file_id, it)).into(),
ModItem::Struct(it) => tree.source(&db, InFile::new(file_id, it)).into(),
ModItem::Union(it) => tree.source(&db, InFile::new(file_id, it)).into(),
ModItem::Enum(it) => tree.source(&db, InFile::new(file_id, it)).into(),
ModItem::Const(it) => tree.source(&db, InFile::new(file_id, it)).into(),
ModItem::Static(it) => tree.source(&db, InFile::new(file_id, it)).into(),
ModItem::TypeAlias(it) => tree.source(&db, InFile::new(file_id, it)).into(),
ModItem::Mod(it) => {
if let ModKind::Inline { items } = &tree[it].kind {
worklist.extend(&**items);
}
tree.source(&db, InFile::new(file_id, it)).into()
}
ModItem::Trait(it) => {
worklist.extend(tree[it].items.iter().map(|item| ModItem::from(*item)));
tree.source(&db, InFile::new(file_id, it)).into()
}
ModItem::Impl(it) => {
worklist.extend(tree[it].items.iter().map(|item| ModItem::from(*item)));
tree.source(&db, InFile::new(file_id, it)).into()
}
ModItem::MacroCall(_) => continue,
};
outer_items.insert(node);
}
// Now descend the root node and check that all `ast::ModuleItem`s are either recorded above, or
// registered as inner items.
for item in root.descendants().skip(1).filter_map(ast::ModuleItem::cast) {
if outer_items.contains(&item) {
continue;
}
let ast_id = ast_id_map.ast_id(&item);
assert!(!tree.inner_items(ast_id).is_empty());
}
}
fn item_tree(ra_fixture: &str) -> Arc<ItemTree> {
let (db, file_id) = TestDB::with_single_file(ra_fixture);
db.item_tree(file_id.into())
}
fn print_item_tree(ra_fixture: &str) -> String {
let tree = item_tree(ra_fixture);
let mut out = String::new();
format_to!(out, "inner attrs: {:?}\n\n", tree.top_level_attrs());
format_to!(out, "top-level items:\n");
for item in tree.top_level_items() {
fmt_mod_item(&mut out, &tree, *item);
format_to!(out, "\n");
}
if !tree.inner_items.is_empty() {
format_to!(out, "\ninner items:\n\n");
for (ast_id, items) in &tree.inner_items {
format_to!(out, "for AST {:?}:\n", ast_id);
for inner in items {
fmt_mod_item(&mut out, &tree, *inner);
format_to!(out, "\n\n");
}
}
}
out
}
fn fmt_mod_item(out: &mut String, tree: &ItemTree, item: ModItem) {
let attrs = tree.attrs(item);
if !attrs.is_empty() {
format_to!(out, "#[{:?}]\n", attrs);
}
let mut children = String::new();
match item {
ModItem::ExternCrate(it) => {
format_to!(out, "{:?}", tree[it]);
}
ModItem::Import(it) => {
format_to!(out, "{:?}", tree[it]);
}
ModItem::Function(it) => {
format_to!(out, "{:?}", tree[it]);
}
ModItem::Struct(it) => {
format_to!(out, "{:?}", tree[it]);
}
ModItem::Union(it) => {
format_to!(out, "{:?}", tree[it]);
}
ModItem::Enum(it) => {
format_to!(out, "{:?}", tree[it]);
}
ModItem::Const(it) => {
format_to!(out, "{:?}", tree[it]);
}
ModItem::Static(it) => {
format_to!(out, "{:?}", tree[it]);
}
ModItem::Trait(it) => {
format_to!(out, "{:?}", tree[it]);
for item in &*tree[it].items {
fmt_mod_item(&mut children, tree, ModItem::from(*item));
format_to!(children, "\n");
}
}
ModItem::Impl(it) => {
format_to!(out, "{:?}", tree[it]);
for item in &*tree[it].items {
fmt_mod_item(&mut children, tree, ModItem::from(*item));
format_to!(children, "\n");
}
}
ModItem::TypeAlias(it) => {
format_to!(out, "{:?}", tree[it]);
}
ModItem::Mod(it) => {
format_to!(out, "{:?}", tree[it]);
match &tree[it].kind {
ModKind::Inline { items } => {
for item in &**items {
fmt_mod_item(&mut children, tree, *item);
format_to!(children, "\n");
}
}
ModKind::Outline {} => {}
}
}
ModItem::MacroCall(it) => {
format_to!(out, "{:?}", tree[it]);
}
}
for line in children.lines() {
format_to!(out, "\n> {}", line);
}
}
#[test]
fn smoke() {
assert_snapshot!(print_item_tree(r"
#![attr]
#[attr_on_use]
use {a, b::*};
#[ext_crate]
extern crate krate;
#[on_trait]
trait Tr<U> {
#[assoc_ty]
type AssocTy: Tr<()>;
#[assoc_const]
const CONST: u8;
#[assoc_method]
fn method(&self);
#[assoc_dfl_method]
fn dfl_method(&mut self) {}
}
#[struct0]
struct Struct0<T = ()>;
#[struct1]
struct Struct1<T>(#[struct1fld] u8);
#[struct2]
struct Struct2<T> {
#[struct2fld]
fld: (T, ),
}
#[en]
enum En {
#[enum_variant]
Variant {
#[enum_field]
field: u8,
},
}
#[un]
union Un {
#[union_fld]
fld: u16,
}
"), @r###"
inner attrs: Attrs { entries: Some([Attr { path: ModPath { kind: Plain, segments: [Name(Text("attr"))] }, input: None }]) }
top-level items:
#[Attrs { entries: Some([Attr { path: ModPath { kind: Plain, segments: [Name(Text("attr_on_use"))] }, input: None }]) }]
Import { path: ModPath { kind: Plain, segments: [Name(Text("a"))] }, alias: None, visibility: RawVisibilityId("pub(self)"), is_glob: false, is_prelude: false, ast_id: FileAstId::<ra_syntax::ast::generated::nodes::UseItem>(0) }
#[Attrs { entries: Some([Attr { path: ModPath { kind: Plain, segments: [Name(Text("attr_on_use"))] }, input: None }]) }]
Import { path: ModPath { kind: Plain, segments: [Name(Text("b"))] }, alias: None, visibility: RawVisibilityId("pub(self)"), is_glob: true, is_prelude: false, ast_id: FileAstId::<ra_syntax::ast::generated::nodes::UseItem>(0) }
#[Attrs { entries: Some([Attr { path: ModPath { kind: Plain, segments: [Name(Text("ext_crate"))] }, input: None }]) }]
ExternCrate { path: ModPath { kind: Plain, segments: [Name(Text("krate"))] }, alias: None, visibility: RawVisibilityId("pub(self)"), is_macro_use: false, ast_id: FileAstId::<ra_syntax::ast::generated::nodes::ExternCrateItem>(1) }
#[Attrs { entries: Some([Attr { path: ModPath { kind: Plain, segments: [Name(Text("on_trait"))] }, input: None }]) }]
Trait { name: Name(Text("Tr")), visibility: RawVisibilityId("pub(self)"), generic_params: GenericParamsId(0), auto: false, items: [TypeAlias(Idx::<TypeAlias>(0)), Const(Idx::<Const>(0)), Function(Idx::<Function>(0)), Function(Idx::<Function>(1))], ast_id: FileAstId::<ra_syntax::ast::generated::nodes::TraitDef>(2) }
> #[Attrs { entries: Some([Attr { path: ModPath { kind: Plain, segments: [Name(Text("assoc_ty"))] }, input: None }]) }]
> TypeAlias { name: Name(Text("AssocTy")), visibility: RawVisibilityId("pub(self)"), bounds: [Path(Path { type_anchor: None, mod_path: ModPath { kind: Plain, segments: [Name(Text("Tr"))] }, generic_args: [Some(GenericArgs { args: [Type(Tuple([]))], has_self_type: false, bindings: [] })] })], generic_params: GenericParamsId(4294967295), type_ref: None, ast_id: FileAstId::<ra_syntax::ast::generated::nodes::TypeAliasDef>(8) }
> #[Attrs { entries: Some([Attr { path: ModPath { kind: Plain, segments: [Name(Text("assoc_const"))] }, input: None }]) }]
> Const { name: Some(Name(Text("CONST"))), visibility: RawVisibilityId("pub(self)"), type_ref: Path(Path { type_anchor: None, mod_path: ModPath { kind: Plain, segments: [Name(Text("u8"))] }, generic_args: [None] }), ast_id: FileAstId::<ra_syntax::ast::generated::nodes::ConstDef>(9) }
> #[Attrs { entries: Some([Attr { path: ModPath { kind: Plain, segments: [Name(Text("assoc_method"))] }, input: None }]) }]
> Function { name: Name(Text("method")), visibility: RawVisibilityId("pub(self)"), generic_params: GenericParamsId(4294967295), has_self_param: true, is_unsafe: false, params: [Reference(Path(Path { type_anchor: None, mod_path: ModPath { kind: Plain, segments: [Name(Text("Self"))] }, generic_args: [None] }), Shared)], ret_type: Tuple([]), ast_id: FileAstId::<ra_syntax::ast::generated::nodes::FnDef>(10) }
> #[Attrs { entries: Some([Attr { path: ModPath { kind: Plain, segments: [Name(Text("assoc_dfl_method"))] }, input: None }]) }]
> Function { name: Name(Text("dfl_method")), visibility: RawVisibilityId("pub(self)"), generic_params: GenericParamsId(4294967295), has_self_param: true, is_unsafe: false, params: [Reference(Path(Path { type_anchor: None, mod_path: ModPath { kind: Plain, segments: [Name(Text("Self"))] }, generic_args: [None] }), Mut)], ret_type: Tuple([]), ast_id: FileAstId::<ra_syntax::ast::generated::nodes::FnDef>(11) }
#[Attrs { entries: Some([Attr { path: ModPath { kind: Plain, segments: [Name(Text("struct0"))] }, input: None }]) }]
Struct { name: Name(Text("Struct0")), visibility: RawVisibilityId("pub(self)"), generic_params: GenericParamsId(1), fields: Unit, ast_id: FileAstId::<ra_syntax::ast::generated::nodes::StructDef>(3), kind: Unit }
#[Attrs { entries: Some([Attr { path: ModPath { kind: Plain, segments: [Name(Text("struct1"))] }, input: None }]) }]
Struct { name: Name(Text("Struct1")), visibility: RawVisibilityId("pub(self)"), generic_params: GenericParamsId(2), fields: Tuple(Idx::<Field>(0)..Idx::<Field>(1)), ast_id: FileAstId::<ra_syntax::ast::generated::nodes::StructDef>(4), kind: Tuple }
#[Attrs { entries: Some([Attr { path: ModPath { kind: Plain, segments: [Name(Text("struct2"))] }, input: None }]) }]
Struct { name: Name(Text("Struct2")), visibility: RawVisibilityId("pub(self)"), generic_params: GenericParamsId(3), fields: Record(Idx::<Field>(1)..Idx::<Field>(2)), ast_id: FileAstId::<ra_syntax::ast::generated::nodes::StructDef>(5), kind: Record }
#[Attrs { entries: Some([Attr { path: ModPath { kind: Plain, segments: [Name(Text("en"))] }, input: None }]) }]
Enum { name: Name(Text("En")), visibility: RawVisibilityId("pub(self)"), generic_params: GenericParamsId(4294967295), variants: Idx::<Variant>(0)..Idx::<Variant>(1), ast_id: FileAstId::<ra_syntax::ast::generated::nodes::EnumDef>(6) }
#[Attrs { entries: Some([Attr { path: ModPath { kind: Plain, segments: [Name(Text("un"))] }, input: None }]) }]
Union { name: Name(Text("Un")), visibility: RawVisibilityId("pub(self)"), generic_params: GenericParamsId(4294967295), fields: Record(Idx::<Field>(3)..Idx::<Field>(4)), ast_id: FileAstId::<ra_syntax::ast::generated::nodes::UnionDef>(7) }
"###);
}
#[test]
fn simple_inner_items() {
let tree = print_item_tree(
r"
impl<T:A> D for Response<T> {
fn foo() {
end();
fn end<W: Write>() {
let _x: T = loop {};
}
}
}
",
);
assert_snapshot!(tree, @r###"
inner attrs: Attrs { entries: None }
top-level items:
Impl { generic_params: GenericParamsId(0), target_trait: Some(Path(Path { type_anchor: None, mod_path: ModPath { kind: Plain, segments: [Name(Text("D"))] }, generic_args: [None] })), target_type: Path(Path { type_anchor: None, mod_path: ModPath { kind: Plain, segments: [Name(Text("Response"))] }, generic_args: [Some(GenericArgs { args: [Type(Path(Path { type_anchor: None, mod_path: ModPath { kind: Plain, segments: [Name(Text("T"))] }, generic_args: [None] }))], has_self_type: false, bindings: [] })] }), is_negative: false, items: [Function(Idx::<Function>(1))], ast_id: FileAstId::<ra_syntax::ast::generated::nodes::ImplDef>(0) }
> Function { name: Name(Text("foo")), visibility: RawVisibilityId("pub(self)"), generic_params: GenericParamsId(4294967295), has_self_param: false, is_unsafe: false, params: [], ret_type: Tuple([]), ast_id: FileAstId::<ra_syntax::ast::generated::nodes::FnDef>(1) }
inner items:
for AST FileAstId::<ra_syntax::ast::generated::nodes::ModuleItem>(2):
Function { name: Name(Text("end")), visibility: RawVisibilityId("pub(self)"), generic_params: GenericParamsId(1), has_self_param: false, is_unsafe: false, params: [], ret_type: Tuple([]), ast_id: FileAstId::<ra_syntax::ast::generated::nodes::FnDef>(2) }
"###);
}
#[test]
fn extern_attrs() {
let tree = print_item_tree(
r#"
#[block_attr]
extern "C" {
#[attr_a]
fn a() {}
#[attr_b]
fn b() {}
}
"#,
);
assert_snapshot!(tree, @r###"
inner attrs: Attrs { entries: None }
top-level items:
#[Attrs { entries: Some([Attr { path: ModPath { kind: Plain, segments: [Name(Text("attr_a"))] }, input: None }, Attr { path: ModPath { kind: Plain, segments: [Name(Text("block_attr"))] }, input: None }]) }]
Function { name: Name(Text("a")), visibility: RawVisibilityId("pub(self)"), generic_params: GenericParamsId(4294967295), has_self_param: false, is_unsafe: false, params: [], ret_type: Tuple([]), ast_id: FileAstId::<ra_syntax::ast::generated::nodes::FnDef>(1) }
#[Attrs { entries: Some([Attr { path: ModPath { kind: Plain, segments: [Name(Text("attr_b"))] }, input: None }, Attr { path: ModPath { kind: Plain, segments: [Name(Text("block_attr"))] }, input: None }]) }]
Function { name: Name(Text("b")), visibility: RawVisibilityId("pub(self)"), generic_params: GenericParamsId(4294967295), has_self_param: false, is_unsafe: false, params: [], ret_type: Tuple([]), ast_id: FileAstId::<ra_syntax::ast::generated::nodes::FnDef>(2) }
"###);
}
#[test]
fn trait_attrs() {
let tree = print_item_tree(
r#"
#[trait_attr]
trait Tr {
#[attr_a]
fn a() {}
#[attr_b]
fn b() {}
}
"#,
);
assert_snapshot!(tree, @r###"
inner attrs: Attrs { entries: None }
top-level items:
#[Attrs { entries: Some([Attr { path: ModPath { kind: Plain, segments: [Name(Text("trait_attr"))] }, input: None }]) }]
Trait { name: Name(Text("Tr")), visibility: RawVisibilityId("pub(self)"), generic_params: GenericParamsId(0), auto: false, items: [Function(Idx::<Function>(0)), Function(Idx::<Function>(1))], ast_id: FileAstId::<ra_syntax::ast::generated::nodes::TraitDef>(0) }
> #[Attrs { entries: Some([Attr { path: ModPath { kind: Plain, segments: [Name(Text("attr_a"))] }, input: None }]) }]
> Function { name: Name(Text("a")), visibility: RawVisibilityId("pub(self)"), generic_params: GenericParamsId(4294967295), has_self_param: false, is_unsafe: false, params: [], ret_type: Tuple([]), ast_id: FileAstId::<ra_syntax::ast::generated::nodes::FnDef>(1) }
> #[Attrs { entries: Some([Attr { path: ModPath { kind: Plain, segments: [Name(Text("attr_b"))] }, input: None }]) }]
> Function { name: Name(Text("b")), visibility: RawVisibilityId("pub(self)"), generic_params: GenericParamsId(4294967295), has_self_param: false, is_unsafe: false, params: [], ret_type: Tuple([]), ast_id: FileAstId::<ra_syntax::ast::generated::nodes::FnDef>(2) }
"###);
}
#[test]
fn impl_attrs() {
let tree = print_item_tree(
r#"
#[impl_attr]
impl Ty {
#[attr_a]
fn a() {}
#[attr_b]
fn b() {}
}
"#,
);
assert_snapshot!(tree, @r###"
inner attrs: Attrs { entries: None }
top-level items:
#[Attrs { entries: Some([Attr { path: ModPath { kind: Plain, segments: [Name(Text("impl_attr"))] }, input: None }]) }]
Impl { generic_params: GenericParamsId(4294967295), target_trait: None, target_type: Path(Path { type_anchor: None, mod_path: ModPath { kind: Plain, segments: [Name(Text("Ty"))] }, generic_args: [None] }), is_negative: false, items: [Function(Idx::<Function>(0)), Function(Idx::<Function>(1))], ast_id: FileAstId::<ra_syntax::ast::generated::nodes::ImplDef>(0) }
> #[Attrs { entries: Some([Attr { path: ModPath { kind: Plain, segments: [Name(Text("attr_a"))] }, input: None }]) }]
> Function { name: Name(Text("a")), visibility: RawVisibilityId("pub(self)"), generic_params: GenericParamsId(4294967295), has_self_param: false, is_unsafe: false, params: [], ret_type: Tuple([]), ast_id: FileAstId::<ra_syntax::ast::generated::nodes::FnDef>(1) }
> #[Attrs { entries: Some([Attr { path: ModPath { kind: Plain, segments: [Name(Text("attr_b"))] }, input: None }]) }]
> Function { name: Name(Text("b")), visibility: RawVisibilityId("pub(self)"), generic_params: GenericParamsId(4294967295), has_self_param: false, is_unsafe: false, params: [], ret_type: Tuple([]), ast_id: FileAstId::<ra_syntax::ast::generated::nodes::FnDef>(2) }
"###);
}
#[test]
fn cursed_inner_items() {
test_inner_items(
r"
struct S<T: Trait = [u8; { fn f() {} 0 }]>(T);
enum En {
Var1 {
t: [(); { trait Inner {} 0 }],
},
Var2([u16; { enum Inner {} 0 }]),
}
type Ty = [En; { struct Inner; 0 }];
impl En {
fn assoc() {
trait InnerTrait<T = [u8; { fn f() {} }]> {}
struct InnerStruct<T = [u8; { fn f() {} }]> {}
impl<T = [u8; { fn f() {} }]> InnerTrait for InnerStruct {}
}
}
trait Tr<T = [u8; { fn f() {} }]> {
type AssocTy = [u8; { fn f() {} }];
const AssocConst: [u8; { fn f() {} }];
}
",
);
}
#[test]
fn inner_item_attrs() {
let tree = print_item_tree(
r"
fn foo() {
#[on_inner]
fn inner() {}
}
",
);
assert_snapshot!(tree, @r###"
inner attrs: Attrs { entries: None }
top-level items:
Function { name: Name(Text("foo")), visibility: RawVisibilityId("pub(self)"), generic_params: GenericParamsId(4294967295), has_self_param: false, is_unsafe: false, params: [], ret_type: Tuple([]), ast_id: FileAstId::<ra_syntax::ast::generated::nodes::FnDef>(0) }
inner items:
for AST FileAstId::<ra_syntax::ast::generated::nodes::ModuleItem>(1):
#[Attrs { entries: Some([Attr { path: ModPath { kind: Plain, segments: [Name(Text("on_inner"))] }, input: None }]) }]
Function { name: Name(Text("inner")), visibility: RawVisibilityId("pub(self)"), generic_params: GenericParamsId(4294967295), has_self_param: false, is_unsafe: false, params: [], ret_type: Tuple([]), ast_id: FileAstId::<ra_syntax::ast::generated::nodes::FnDef>(1) }
"###);
}
#[test]
fn assoc_item_macros() {
let tree = print_item_tree(
r"
impl S {
items!();
}
",
);
assert_snapshot!(tree, @r###"
inner attrs: Attrs { entries: None }
top-level items:
Impl { generic_params: GenericParamsId(4294967295), target_trait: None, target_type: Path(Path { type_anchor: None, mod_path: ModPath { kind: Plain, segments: [Name(Text("S"))] }, generic_args: [None] }), is_negative: false, items: [MacroCall(Idx::<MacroCall>(0))], ast_id: FileAstId::<ra_syntax::ast::generated::nodes::ImplDef>(0) }
> MacroCall { name: None, path: ModPath { kind: Plain, segments: [Name(Text("items"))] }, is_export: false, is_local_inner: false, is_builtin: false, ast_id: FileAstId::<ra_syntax::ast::generated::nodes::MacroCall>(1) }
"###);
}

View file

@ -25,6 +25,8 @@ pub mod item_scope;
pub mod dyn_map; pub mod dyn_map;
pub mod keys; pub mod keys;
pub mod item_tree;
pub mod adt; pub mod adt;
pub mod data; pub mod data;
pub mod generics; pub mod generics;
@ -48,7 +50,7 @@ pub mod import_map;
#[cfg(test)] #[cfg(test)]
mod test_db; mod test_db;
use std::hash::Hash; use std::hash::{Hash, Hasher};
use hir_expand::{ use hir_expand::{
ast_id_map::FileAstId, eager::expand_eager_macro, hygiene::Hygiene, AstId, HirFileId, InFile, ast_id_map::FileAstId, eager::expand_eager_macro, hygiene::Hygiene, AstId, HirFileId, InFile,
@ -56,10 +58,13 @@ use hir_expand::{
}; };
use ra_arena::Idx; use ra_arena::Idx;
use ra_db::{impl_intern_key, salsa, CrateId}; use ra_db::{impl_intern_key, salsa, CrateId};
use ra_syntax::{ast, AstNode}; use ra_syntax::ast;
use crate::body::Expander;
use crate::builtin_type::BuiltinType; use crate::builtin_type::BuiltinType;
use item_tree::{
Const, Enum, Function, Impl, ItemTreeId, ItemTreeNode, ModItem, Static, Struct, Trait,
TypeAlias, Union,
};
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct ModuleId { pub struct ModuleId {
@ -70,16 +75,62 @@ pub struct ModuleId {
/// An ID of a module, **local** to a specific crate /// An ID of a module, **local** to a specific crate
pub type LocalModuleId = Idx<nameres::ModuleData>; pub type LocalModuleId = Idx<nameres::ModuleData>;
#[derive(Debug, Clone, PartialEq, Eq, Hash)] #[derive(Debug)]
pub struct ItemLoc<N: AstNode> { pub struct ItemLoc<N: ItemTreeNode> {
pub container: ContainerId, pub container: ContainerId,
pub ast_id: AstId<N>, pub id: ItemTreeId<N>,
} }
#[derive(Debug, Clone, PartialEq, Eq, Hash)] impl<N: ItemTreeNode> Clone for ItemLoc<N> {
pub struct AssocItemLoc<N: AstNode> { fn clone(&self) -> Self {
Self { container: self.container, id: self.id }
}
}
impl<N: ItemTreeNode> Copy for ItemLoc<N> {}
impl<N: ItemTreeNode> PartialEq for ItemLoc<N> {
fn eq(&self, other: &Self) -> bool {
self.container == other.container && self.id == other.id
}
}
impl<N: ItemTreeNode> Eq for ItemLoc<N> {}
impl<N: ItemTreeNode> Hash for ItemLoc<N> {
fn hash<H: Hasher>(&self, state: &mut H) {
self.container.hash(state);
self.id.hash(state);
}
}
#[derive(Debug)]
pub struct AssocItemLoc<N: ItemTreeNode> {
pub container: AssocContainerId, pub container: AssocContainerId,
pub ast_id: AstId<N>, pub id: ItemTreeId<N>,
}
impl<N: ItemTreeNode> Clone for AssocItemLoc<N> {
fn clone(&self) -> Self {
Self { container: self.container, id: self.id }
}
}
impl<N: ItemTreeNode> Copy for AssocItemLoc<N> {}
impl<N: ItemTreeNode> PartialEq for AssocItemLoc<N> {
fn eq(&self, other: &Self) -> bool {
self.container == other.container && self.id == other.id
}
}
impl<N: ItemTreeNode> Eq for AssocItemLoc<N> {}
impl<N: ItemTreeNode> Hash for AssocItemLoc<N> {
fn hash<H: Hasher>(&self, state: &mut H) {
self.container.hash(state);
self.id.hash(state);
}
} }
macro_rules! impl_intern { macro_rules! impl_intern {
@ -104,22 +155,22 @@ macro_rules! impl_intern {
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct FunctionId(salsa::InternId); pub struct FunctionId(salsa::InternId);
type FunctionLoc = AssocItemLoc<ast::FnDef>; type FunctionLoc = AssocItemLoc<Function>;
impl_intern!(FunctionId, FunctionLoc, intern_function, lookup_intern_function); impl_intern!(FunctionId, FunctionLoc, intern_function, lookup_intern_function);
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct StructId(salsa::InternId); pub struct StructId(salsa::InternId);
type StructLoc = ItemLoc<ast::StructDef>; type StructLoc = ItemLoc<Struct>;
impl_intern!(StructId, StructLoc, intern_struct, lookup_intern_struct); impl_intern!(StructId, StructLoc, intern_struct, lookup_intern_struct);
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct UnionId(salsa::InternId); pub struct UnionId(salsa::InternId);
pub type UnionLoc = ItemLoc<ast::UnionDef>; pub type UnionLoc = ItemLoc<Union>;
impl_intern!(UnionId, UnionLoc, intern_union, lookup_intern_union); impl_intern!(UnionId, UnionLoc, intern_union, lookup_intern_union);
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct EnumId(salsa::InternId); pub struct EnumId(salsa::InternId);
pub type EnumLoc = ItemLoc<ast::EnumDef>; pub type EnumLoc = ItemLoc<Enum>;
impl_intern!(EnumId, EnumLoc, intern_enum, lookup_intern_enum); impl_intern!(EnumId, EnumLoc, intern_enum, lookup_intern_enum);
// FIXME: rename to `VariantId`, only enums can ave variants // FIXME: rename to `VariantId`, only enums can ave variants
@ -141,27 +192,27 @@ pub type LocalFieldId = Idx<adt::FieldData>;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct ConstId(salsa::InternId); pub struct ConstId(salsa::InternId);
type ConstLoc = AssocItemLoc<ast::ConstDef>; type ConstLoc = AssocItemLoc<Const>;
impl_intern!(ConstId, ConstLoc, intern_const, lookup_intern_const); impl_intern!(ConstId, ConstLoc, intern_const, lookup_intern_const);
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct StaticId(salsa::InternId); pub struct StaticId(salsa::InternId);
pub type StaticLoc = ItemLoc<ast::StaticDef>; pub type StaticLoc = ItemLoc<Static>;
impl_intern!(StaticId, StaticLoc, intern_static, lookup_intern_static); impl_intern!(StaticId, StaticLoc, intern_static, lookup_intern_static);
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct TraitId(salsa::InternId); pub struct TraitId(salsa::InternId);
pub type TraitLoc = ItemLoc<ast::TraitDef>; pub type TraitLoc = ItemLoc<Trait>;
impl_intern!(TraitId, TraitLoc, intern_trait, lookup_intern_trait); impl_intern!(TraitId, TraitLoc, intern_trait, lookup_intern_trait);
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct TypeAliasId(salsa::InternId); pub struct TypeAliasId(salsa::InternId);
type TypeAliasLoc = AssocItemLoc<ast::TypeAliasDef>; type TypeAliasLoc = AssocItemLoc<TypeAlias>;
impl_intern!(TypeAliasId, TypeAliasLoc, intern_type_alias, lookup_intern_type_alias); impl_intern!(TypeAliasId, TypeAliasLoc, intern_type_alias, lookup_intern_type_alias);
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Ord, PartialOrd)] #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Ord, PartialOrd)]
pub struct ImplId(salsa::InternId); pub struct ImplId(salsa::InternId);
type ImplLoc = ItemLoc<ast::ImplDef>; type ImplLoc = ItemLoc<Impl>;
impl_intern!(ImplId, ImplLoc, intern_impl, lookup_intern_impl); impl_intern!(ImplId, ImplLoc, intern_impl, lookup_intern_impl);
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
@ -365,7 +416,7 @@ impl HasModule for AssocContainerId {
} }
} }
impl<N: AstNode> HasModule for AssocItemLoc<N> { impl<N: ItemTreeNode> HasModule for AssocItemLoc<N> {
fn module(&self, db: &dyn db::DefDatabase) -> ModuleId { fn module(&self, db: &dyn db::DefDatabase) -> ModuleId {
self.container.module(db) self.container.module(db)
} }
@ -392,6 +443,16 @@ impl HasModule for DefWithBodyId {
} }
} }
impl DefWithBodyId {
pub fn as_mod_item(self, db: &dyn db::DefDatabase) -> ModItem {
match self {
DefWithBodyId::FunctionId(it) => it.lookup(db).id.value.into(),
DefWithBodyId::StaticId(it) => it.lookup(db).id.value.into(),
DefWithBodyId::ConstId(it) => it.lookup(db).id.value.into(),
}
}
}
impl HasModule for GenericDefId { impl HasModule for GenericDefId {
fn module(&self, db: &dyn db::DefDatabase) -> ModuleId { fn module(&self, db: &dyn db::DefDatabase) -> ModuleId {
match self { match self {

View file

@ -47,7 +47,6 @@
//! path and, upon success, we run macro expansion and "collect module" phase on //! path and, upon success, we run macro expansion and "collect module" phase on
//! the result //! the result
pub(crate) mod raw;
mod collector; mod collector;
mod mod_resolution; mod mod_resolution;
mod path_resolution; mod path_resolution;

View file

@ -4,6 +4,7 @@
//! resolves imports and expands macros. //! resolves imports and expands macros.
use hir_expand::{ use hir_expand::{
ast_id_map::FileAstId,
builtin_derive::find_builtin_derive, builtin_derive::find_builtin_derive,
builtin_macro::find_builtin_macro, builtin_macro::find_builtin_macro,
name::{name, AsName, Name}, name::{name, AsName, Name},
@ -19,13 +20,16 @@ use test_utils::mark;
use crate::{ use crate::{
attr::Attrs, attr::Attrs,
db::DefDatabase, db::DefDatabase,
item_tree::{
self, FileItemTreeId, ItemTree, ItemTreeId, MacroCall, Mod, ModItem, ModKind, StructDefKind,
},
nameres::{ nameres::{
diagnostics::DefDiagnostic, mod_resolution::ModDir, path_resolution::ReachedFixedPoint, diagnostics::DefDiagnostic, mod_resolution::ModDir, path_resolution::ReachedFixedPoint,
raw, BuiltinShadowMode, CrateDefMap, ModuleData, ModuleOrigin, ResolveMode, BuiltinShadowMode, CrateDefMap, ModuleData, ModuleOrigin, ResolveMode,
}, },
path::{ImportAlias, ModPath, PathKind}, path::{ImportAlias, ModPath, PathKind},
per_ns::PerNs, per_ns::PerNs,
visibility::Visibility, visibility::{RawVisibility, Visibility},
AdtId, AsMacroCall, AstId, AstIdWithPath, ConstLoc, ContainerId, EnumLoc, EnumVariantId, AdtId, AsMacroCall, AstId, AstIdWithPath, ConstLoc, ContainerId, EnumLoc, EnumVariantId,
FunctionLoc, ImplLoc, Intern, LocalModuleId, ModuleDefId, ModuleId, StaticLoc, StructLoc, FunctionLoc, ImplLoc, Intern, LocalModuleId, ModuleDefId, ModuleId, StaticLoc, StructLoc,
TraitLoc, TypeAliasLoc, UnionLoc, TraitLoc, TypeAliasLoc, UnionLoc,
@ -101,11 +105,51 @@ impl PartialResolvedImport {
} }
} }
#[derive(Clone, Debug, Eq, PartialEq)]
struct Import {
pub path: ModPath,
pub alias: Option<ImportAlias>,
pub visibility: RawVisibility,
pub is_glob: bool,
pub is_prelude: bool,
pub is_extern_crate: bool,
pub is_macro_use: bool,
}
impl Import {
fn from_use(tree: &ItemTree, id: FileItemTreeId<item_tree::Import>) -> Self {
let it = &tree[id];
let visibility = &tree[it.visibility];
Self {
path: it.path.clone(),
alias: it.alias.clone(),
visibility: visibility.clone(),
is_glob: it.is_glob,
is_prelude: it.is_prelude,
is_extern_crate: false,
is_macro_use: false,
}
}
fn from_extern_crate(tree: &ItemTree, id: FileItemTreeId<item_tree::ExternCrate>) -> Self {
let it = &tree[id];
let visibility = &tree[it.visibility];
Self {
path: it.path.clone(),
alias: it.alias.clone(),
visibility: visibility.clone(),
is_glob: false,
is_prelude: false,
is_extern_crate: true,
is_macro_use: it.is_macro_use,
}
}
}
#[derive(Clone, Debug, Eq, PartialEq)] #[derive(Clone, Debug, Eq, PartialEq)]
struct ImportDirective { struct ImportDirective {
module_id: LocalModuleId, module_id: LocalModuleId,
import_id: raw::Import, import: Import,
import: raw::ImportData,
status: PartialResolvedImport, status: PartialResolvedImport,
} }
@ -123,6 +167,13 @@ struct DeriveDirective {
ast_id: AstIdWithPath<ast::ModuleItem>, ast_id: AstIdWithPath<ast::ModuleItem>,
} }
struct DefData<'a> {
id: ModuleDefId,
name: &'a Name,
visibility: &'a RawVisibility,
has_constructor: bool,
}
/// Walks the tree of module recursively /// Walks the tree of module recursively
struct DefCollector<'a> { struct DefCollector<'a> {
db: &'a dyn DefDatabase, db: &'a dyn DefDatabase,
@ -140,7 +191,7 @@ struct DefCollector<'a> {
impl DefCollector<'_> { impl DefCollector<'_> {
fn collect(&mut self) { fn collect(&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 raw_items = self.db.raw_items(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;
self.def_map.modules[module_id].origin = ModuleOrigin::CrateRoot { definition: file_id }; self.def_map.modules[module_id].origin = ModuleOrigin::CrateRoot { definition: file_id };
ModCollector { ModCollector {
@ -148,10 +199,10 @@ impl DefCollector<'_> {
macro_depth: 0, macro_depth: 0,
module_id, module_id,
file_id: file_id.into(), file_id: file_id.into(),
raw_items: &raw_items, item_tree: &item_tree,
mod_dir: ModDir::root(), mod_dir: ModDir::root(),
} }
.collect(raw_items.items()); .collect(item_tree.top_level_items());
// main name resolution fixed-point loop. // main name resolution fixed-point loop.
let mut i = 0; let mut i = 0;
@ -286,7 +337,7 @@ impl DefCollector<'_> {
fn import_macros_from_extern_crate( fn import_macros_from_extern_crate(
&mut self, &mut self,
current_module_id: LocalModuleId, current_module_id: LocalModuleId,
import: &raw::ImportData, import: &item_tree::ExternCrate,
) { ) {
log::debug!( log::debug!(
"importing macros from extern crate: {:?} ({:?})", "importing macros from extern crate: {:?} ({:?})",
@ -352,11 +403,7 @@ impl DefCollector<'_> {
} }
} }
fn resolve_import( fn resolve_import(&self, module_id: LocalModuleId, import: &Import) -> PartialResolvedImport {
&self,
module_id: LocalModuleId,
import: &raw::ImportData,
) -> PartialResolvedImport {
log::debug!("resolving import: {:?} ({:?})", import, self.def_map.edition); log::debug!("resolving import: {:?} ({:?})", import, self.def_map.edition);
if import.is_extern_crate { if import.is_extern_crate {
let res = self.def_map.resolve_name_in_extern_prelude( let res = self.def_map.resolve_name_in_extern_prelude(
@ -649,17 +696,17 @@ impl DefCollector<'_> {
depth: usize, depth: usize,
) { ) {
let file_id: HirFileId = macro_call_id.as_file(); let file_id: HirFileId = macro_call_id.as_file();
let raw_items = self.db.raw_items(file_id); let item_tree = self.db.item_tree(file_id);
let mod_dir = self.mod_dirs[&module_id].clone(); let mod_dir = self.mod_dirs[&module_id].clone();
ModCollector { ModCollector {
def_collector: &mut *self, def_collector: &mut *self,
macro_depth: depth, macro_depth: depth,
file_id, file_id,
module_id, module_id,
raw_items: &raw_items, item_tree: &item_tree,
mod_dir, mod_dir,
} }
.collect(raw_items.items()); .collect(item_tree.top_level_items());
} }
fn finish(self) -> CrateDefMap { fn finish(self) -> CrateDefMap {
@ -673,12 +720,12 @@ struct ModCollector<'a, 'b> {
macro_depth: usize, macro_depth: usize,
module_id: LocalModuleId, module_id: LocalModuleId,
file_id: HirFileId, file_id: HirFileId,
raw_items: &'a raw::RawItems, item_tree: &'a ItemTree,
mod_dir: ModDir, mod_dir: ModDir,
} }
impl ModCollector<'_, '_> { impl ModCollector<'_, '_> {
fn collect(&mut self, items: &[raw::RawItem]) { fn collect(&mut self, items: &[ModItem]) {
// Note: don't assert that inserted value is fresh: it's simply not true // Note: don't assert that inserted value is fresh: it's simply not true
// for macros. // for macros.
self.def_collector.mod_dirs.insert(self.module_id, self.mod_dir.clone()); self.def_collector.mod_dirs.insert(self.module_id, self.mod_dir.clone());
@ -695,64 +742,204 @@ impl ModCollector<'_, '_> {
// `#[macro_use] extern crate` is hoisted to imports macros before collecting // `#[macro_use] extern crate` is hoisted to imports macros before collecting
// any other items. // any other items.
for item in items { for item in items {
if self.is_cfg_enabled(&item.attrs) { if self.is_cfg_enabled(self.item_tree.attrs(*item)) {
if let raw::RawItemKind::Import(import_id) = item.kind { if let ModItem::ExternCrate(id) = item {
let import = self.raw_items[import_id].clone(); let import = self.item_tree[*id].clone();
if import.is_extern_crate && import.is_macro_use { if import.is_macro_use {
self.def_collector.import_macros_from_extern_crate(self.module_id, &import); self.def_collector.import_macros_from_extern_crate(self.module_id, &import);
} }
} }
} }
} }
for item in items { for &item in items {
if self.is_cfg_enabled(&item.attrs) { let attrs = self.item_tree.attrs(item);
match item.kind { if self.is_cfg_enabled(attrs) {
raw::RawItemKind::Module(m) => { let module =
self.collect_module(&self.raw_items[m], &item.attrs) ModuleId { krate: self.def_collector.def_map.krate, local_id: self.module_id };
} let container = ContainerId::ModuleId(module);
raw::RawItemKind::Import(import_id) => {
let mut def = None;
match item {
ModItem::Mod(m) => self.collect_module(&self.item_tree[m], attrs),
ModItem::Import(import_id) => {
self.def_collector.unresolved_imports.push(ImportDirective { self.def_collector.unresolved_imports.push(ImportDirective {
module_id: self.module_id, module_id: self.module_id,
import_id, import: Import::from_use(&self.item_tree, import_id),
import: self.raw_items[import_id].clone(),
status: PartialResolvedImport::Unresolved, status: PartialResolvedImport::Unresolved,
}) })
} }
raw::RawItemKind::Def(def) => { ModItem::ExternCrate(import_id) => {
self.define_def(&self.raw_items[def], &item.attrs) self.def_collector.unresolved_imports.push(ImportDirective {
module_id: self.module_id,
import: Import::from_extern_crate(&self.item_tree, import_id),
status: PartialResolvedImport::Unresolved,
})
} }
raw::RawItemKind::Macro(mac) => self.collect_macro(&self.raw_items[mac]), ModItem::MacroCall(mac) => self.collect_macro(&self.item_tree[mac]),
raw::RawItemKind::Impl(imp) => { ModItem::Impl(imp) => {
let module = ModuleId { let module = ModuleId {
krate: self.def_collector.def_map.krate, krate: self.def_collector.def_map.krate,
local_id: self.module_id, local_id: self.module_id,
}; };
let container = ContainerId::ModuleId(module); let container = ContainerId::ModuleId(module);
let ast_id = self.raw_items[imp].ast_id; let impl_id = ImplLoc { container, id: ItemTreeId::new(self.file_id, imp) }
let impl_id = .intern(self.def_collector.db);
ImplLoc { container, ast_id: AstId::new(self.file_id, ast_id) }
.intern(self.def_collector.db);
self.def_collector.def_map.modules[self.module_id] self.def_collector.def_map.modules[self.module_id]
.scope .scope
.define_impl(impl_id) .define_impl(impl_id)
} }
ModItem::Function(id) => {
let func = &self.item_tree[id];
def = Some(DefData {
id: FunctionLoc {
container: container.into(),
id: ItemTreeId::new(self.file_id, id),
}
.intern(self.def_collector.db)
.into(),
name: &func.name,
visibility: &self.item_tree[func.visibility],
has_constructor: false,
});
}
ModItem::Struct(id) => {
let it = &self.item_tree[id];
// FIXME: check attrs to see if this is an attribute macro invocation;
// in which case we don't add the invocation, just a single attribute
// macro invocation
self.collect_derives(attrs, it.ast_id.upcast());
def = Some(DefData {
id: StructLoc { container, id: ItemTreeId::new(self.file_id, id) }
.intern(self.def_collector.db)
.into(),
name: &it.name,
visibility: &self.item_tree[it.visibility],
has_constructor: it.kind != StructDefKind::Record,
});
}
ModItem::Union(id) => {
let it = &self.item_tree[id];
// FIXME: check attrs to see if this is an attribute macro invocation;
// in which case we don't add the invocation, just a single attribute
// macro invocation
self.collect_derives(attrs, it.ast_id.upcast());
def = Some(DefData {
id: UnionLoc { container, id: ItemTreeId::new(self.file_id, id) }
.intern(self.def_collector.db)
.into(),
name: &it.name,
visibility: &self.item_tree[it.visibility],
has_constructor: false,
});
}
ModItem::Enum(id) => {
let it = &self.item_tree[id];
// FIXME: check attrs to see if this is an attribute macro invocation;
// in which case we don't add the invocation, just a single attribute
// macro invocation
self.collect_derives(attrs, it.ast_id.upcast());
def = Some(DefData {
id: EnumLoc { container, id: ItemTreeId::new(self.file_id, id) }
.intern(self.def_collector.db)
.into(),
name: &it.name,
visibility: &self.item_tree[it.visibility],
has_constructor: false,
});
}
ModItem::Const(id) => {
let it = &self.item_tree[id];
if let Some(name) = &it.name {
def = Some(DefData {
id: ConstLoc {
container: container.into(),
id: ItemTreeId::new(self.file_id, id),
}
.intern(self.def_collector.db)
.into(),
name,
visibility: &self.item_tree[it.visibility],
has_constructor: false,
});
}
}
ModItem::Static(id) => {
let it = &self.item_tree[id];
def = Some(DefData {
id: StaticLoc { container, id: ItemTreeId::new(self.file_id, id) }
.intern(self.def_collector.db)
.into(),
name: &it.name,
visibility: &self.item_tree[it.visibility],
has_constructor: false,
});
}
ModItem::Trait(id) => {
let it = &self.item_tree[id];
def = Some(DefData {
id: TraitLoc { container, id: ItemTreeId::new(self.file_id, id) }
.intern(self.def_collector.db)
.into(),
name: &it.name,
visibility: &self.item_tree[it.visibility],
has_constructor: false,
});
}
ModItem::TypeAlias(id) => {
let it = &self.item_tree[id];
def = Some(DefData {
id: TypeAliasLoc {
container: container.into(),
id: ItemTreeId::new(self.file_id, id),
}
.intern(self.def_collector.db)
.into(),
name: &it.name,
visibility: &self.item_tree[it.visibility],
has_constructor: false,
});
}
}
if let Some(DefData { id, name, visibility, has_constructor }) = def {
self.def_collector.def_map.modules[self.module_id].scope.define_def(id);
let vis = self
.def_collector
.def_map
.resolve_visibility(self.def_collector.db, self.module_id, visibility)
.unwrap_or(Visibility::Public);
self.def_collector.update(
self.module_id,
&[(name.clone(), PerNs::from_def(id, vis, has_constructor))],
vis,
)
} }
} }
} }
} }
fn collect_module(&mut self, module: &raw::ModuleData, attrs: &Attrs) { fn collect_module(&mut self, module: &Mod, attrs: &Attrs) {
let path_attr = attrs.by_key("path").string_value(); let path_attr = attrs.by_key("path").string_value();
let is_macro_use = attrs.by_key("macro_use").exists(); let is_macro_use = attrs.by_key("macro_use").exists();
match module { match &module.kind {
// inline module, just recurse // inline module, just recurse
raw::ModuleData::Definition { name, visibility, items, ast_id } => { ModKind::Inline { items } => {
let module_id = self.push_child_module( let module_id = self.push_child_module(
name.clone(), module.name.clone(),
AstId::new(self.file_id, *ast_id), AstId::new(self.file_id, module.ast_id),
None, None,
&visibility, &self.item_tree[module.visibility],
); );
ModCollector { ModCollector {
@ -760,8 +947,8 @@ impl ModCollector<'_, '_> {
macro_depth: self.macro_depth, macro_depth: self.macro_depth,
module_id, module_id,
file_id: self.file_id, file_id: self.file_id,
raw_items: self.raw_items, item_tree: self.item_tree,
mod_dir: self.mod_dir.descend_into_definition(name, path_attr), mod_dir: self.mod_dir.descend_into_definition(&module.name, path_attr),
} }
.collect(&*items); .collect(&*items);
if is_macro_use { if is_macro_use {
@ -769,31 +956,31 @@ impl ModCollector<'_, '_> {
} }
} }
// out of line module, resolve, parse and recurse // out of line module, resolve, parse and recurse
raw::ModuleData::Declaration { name, visibility, ast_id } => { ModKind::Outline {} => {
let ast_id = AstId::new(self.file_id, *ast_id); let ast_id = AstId::new(self.file_id, module.ast_id);
match self.mod_dir.resolve_declaration( match self.mod_dir.resolve_declaration(
self.def_collector.db, self.def_collector.db,
self.file_id, self.file_id,
name, &module.name,
path_attr, path_attr,
) { ) {
Ok((file_id, is_mod_rs, mod_dir)) => { Ok((file_id, is_mod_rs, mod_dir)) => {
let module_id = self.push_child_module( let module_id = self.push_child_module(
name.clone(), module.name.clone(),
ast_id, ast_id,
Some((file_id, is_mod_rs)), Some((file_id, is_mod_rs)),
&visibility, &self.item_tree[module.visibility],
); );
let raw_items = self.def_collector.db.raw_items(file_id.into()); let item_tree = self.def_collector.db.item_tree(file_id.into());
ModCollector { ModCollector {
def_collector: &mut *self.def_collector, def_collector: &mut *self.def_collector,
macro_depth: self.macro_depth, macro_depth: self.macro_depth,
module_id, module_id,
file_id: file_id.into(), file_id: file_id.into(),
raw_items: &raw_items, item_tree: &item_tree,
mod_dir, mod_dir,
} }
.collect(raw_items.items()); .collect(item_tree.top_level_items());
if is_macro_use { if is_macro_use {
self.import_all_legacy_macros(module_id); self.import_all_legacy_macros(module_id);
} }
@ -842,77 +1029,7 @@ impl ModCollector<'_, '_> {
res res
} }
fn define_def(&mut self, def: &raw::DefData, attrs: &Attrs) { fn collect_derives(&mut self, attrs: &Attrs, ast_id: FileAstId<ast::ModuleItem>) {
let module = ModuleId { krate: self.def_collector.def_map.krate, local_id: self.module_id };
// FIXME: check attrs to see if this is an attribute macro invocation;
// in which case we don't add the invocation, just a single attribute
// macro invocation
self.collect_derives(attrs, def);
let name = def.name.clone();
let container = ContainerId::ModuleId(module);
let vis = &def.visibility;
let mut has_constructor = false;
let def: ModuleDefId = match def.kind {
raw::DefKind::Function(ast_id) => FunctionLoc {
container: container.into(),
ast_id: AstId::new(self.file_id, ast_id),
}
.intern(self.def_collector.db)
.into(),
raw::DefKind::Struct(ast_id, mode) => {
has_constructor = mode != raw::StructDefKind::Record;
StructLoc { container, ast_id: AstId::new(self.file_id, ast_id) }
.intern(self.def_collector.db)
.into()
}
raw::DefKind::Union(ast_id) => {
UnionLoc { container, ast_id: AstId::new(self.file_id, ast_id) }
.intern(self.def_collector.db)
.into()
}
raw::DefKind::Enum(ast_id) => {
EnumLoc { container, ast_id: AstId::new(self.file_id, ast_id) }
.intern(self.def_collector.db)
.into()
}
raw::DefKind::Const(ast_id) => {
ConstLoc { container: container.into(), ast_id: AstId::new(self.file_id, ast_id) }
.intern(self.def_collector.db)
.into()
}
raw::DefKind::Static(ast_id) => {
StaticLoc { container, ast_id: AstId::new(self.file_id, ast_id) }
.intern(self.def_collector.db)
.into()
}
raw::DefKind::Trait(ast_id) => {
TraitLoc { container, ast_id: AstId::new(self.file_id, ast_id) }
.intern(self.def_collector.db)
.into()
}
raw::DefKind::TypeAlias(ast_id) => TypeAliasLoc {
container: container.into(),
ast_id: AstId::new(self.file_id, ast_id),
}
.intern(self.def_collector.db)
.into(),
};
self.def_collector.def_map.modules[self.module_id].scope.define_def(def);
let vis = self
.def_collector
.def_map
.resolve_visibility(self.def_collector.db, self.module_id, vis)
.unwrap_or(Visibility::Public);
self.def_collector.update(
self.module_id,
&[(name, PerNs::from_def(def, vis, has_constructor))],
vis,
)
}
fn collect_derives(&mut self, attrs: &Attrs, def: &raw::DefData) {
for derive_subtree in attrs.by_key("derive").tt_values() { for derive_subtree in attrs.by_key("derive").tt_values() {
// for #[derive(Copy, Clone)], `derive_subtree` is the `(Copy, Clone)` subtree // for #[derive(Copy, Clone)], `derive_subtree` is the `(Copy, Clone)` subtree
for tt in &derive_subtree.token_trees { for tt in &derive_subtree.token_trees {
@ -923,7 +1040,7 @@ impl ModCollector<'_, '_> {
}; };
let path = ModPath::from_tt_ident(ident); let path = ModPath::from_tt_ident(ident);
let ast_id = AstIdWithPath::new(self.file_id, def.kind.ast_id(), path); let ast_id = AstIdWithPath::new(self.file_id, ast_id, path);
self.def_collector self.def_collector
.unexpanded_attribute_macros .unexpanded_attribute_macros
.push(DeriveDirective { module_id: self.module_id, ast_id }); .push(DeriveDirective { module_id: self.module_id, ast_id });
@ -931,11 +1048,11 @@ impl ModCollector<'_, '_> {
} }
} }
fn collect_macro(&mut self, mac: &raw::MacroData) { fn collect_macro(&mut self, mac: &MacroCall) {
let mut ast_id = AstIdWithPath::new(self.file_id, mac.ast_id, mac.path.clone()); let mut ast_id = AstIdWithPath::new(self.file_id, mac.ast_id, mac.path.clone());
// Case 0: builtin macros // Case 0: builtin macros
if mac.builtin { if mac.is_builtin {
if let Some(name) = &mac.name { if let Some(name) = &mac.name {
let krate = self.def_collector.def_map.krate; let krate = self.def_collector.def_map.krate;
if let Some(macro_id) = find_builtin_macro(name, krate, ast_id.ast_id) { if let Some(macro_id) = find_builtin_macro(name, krate, ast_id.ast_id) {
@ -943,7 +1060,7 @@ impl ModCollector<'_, '_> {
self.module_id, self.module_id,
name.clone(), name.clone(),
macro_id, macro_id,
mac.export, mac.is_export,
); );
return; return;
} }
@ -957,9 +1074,14 @@ impl ModCollector<'_, '_> {
ast_id: Some(ast_id.ast_id), ast_id: Some(ast_id.ast_id),
krate: Some(self.def_collector.def_map.krate), krate: Some(self.def_collector.def_map.krate),
kind: MacroDefKind::Declarative, kind: MacroDefKind::Declarative,
local_inner: mac.local_inner, local_inner: mac.is_local_inner,
}; };
self.def_collector.define_macro(self.module_id, name.clone(), macro_id, mac.export); self.def_collector.define_macro(
self.module_id,
name.clone(),
macro_id,
mac.is_export,
);
} }
return; return;
} }

View file

@ -1,482 +0,0 @@
//! Lowers syntax tree of a rust file into a raw representation of containing
//! items, *without* attaching them to a module structure.
//!
//! That is, raw items don't have semantics, just as syntax, but, unlike syntax,
//! they don't change with trivial source code edits, making them a great tool
//! for building salsa recomputation firewalls.
use std::{ops::Index, sync::Arc};
use hir_expand::{
ast_id_map::AstIdMap,
hygiene::Hygiene,
name::{AsName, Name},
};
use ra_arena::{Arena, Idx};
use ra_prof::profile;
use ra_syntax::{
ast::{self, AttrsOwner, NameOwner, VisibilityOwner},
AstNode,
};
use test_utils::mark;
use crate::{
attr::Attrs,
db::DefDatabase,
path::{ImportAlias, ModPath},
visibility::RawVisibility,
FileAstId, HirFileId, InFile,
};
/// `RawItems` is a set of top-level items in a file (except for impls).
///
/// It is the input to name resolution algorithm. `RawItems` are not invalidated
/// on most edits.
#[derive(Debug, Default, PartialEq, Eq)]
pub struct RawItems {
modules: Arena<ModuleData>,
imports: Arena<ImportData>,
defs: Arena<DefData>,
macros: Arena<MacroData>,
impls: Arena<ImplData>,
/// items for top-level module
items: Vec<RawItem>,
}
impl RawItems {
pub(crate) fn raw_items_query(db: &dyn DefDatabase, file_id: HirFileId) -> Arc<RawItems> {
let _p = profile("raw_items_query");
let mut collector = RawItemsCollector {
raw_items: RawItems::default(),
source_ast_id_map: db.ast_id_map(file_id),
file_id,
hygiene: Hygiene::new(db.upcast(), file_id),
};
if let Some(node) = db.parse_or_expand(file_id) {
if let Some(source_file) = ast::SourceFile::cast(node.clone()) {
collector.process_module(None, source_file);
} else if let Some(item_list) = ast::MacroItems::cast(node) {
collector.process_module(None, item_list);
}
}
let raw_items = collector.raw_items;
Arc::new(raw_items)
}
pub(super) fn items(&self) -> &[RawItem] {
&self.items
}
}
impl Index<Idx<ModuleData>> for RawItems {
type Output = ModuleData;
fn index(&self, idx: Idx<ModuleData>) -> &ModuleData {
&self.modules[idx]
}
}
impl Index<Import> for RawItems {
type Output = ImportData;
fn index(&self, idx: Import) -> &ImportData {
&self.imports[idx]
}
}
impl Index<Idx<DefData>> for RawItems {
type Output = DefData;
fn index(&self, idx: Idx<DefData>) -> &DefData {
&self.defs[idx]
}
}
impl Index<Idx<MacroData>> for RawItems {
type Output = MacroData;
fn index(&self, idx: Idx<MacroData>) -> &MacroData {
&self.macros[idx]
}
}
impl Index<Idx<ImplData>> for RawItems {
type Output = ImplData;
fn index(&self, idx: Idx<ImplData>) -> &ImplData {
&self.impls[idx]
}
}
#[derive(Debug, PartialEq, Eq, Clone)]
pub(super) struct RawItem {
pub(super) attrs: Attrs,
pub(super) kind: RawItemKind,
}
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
pub(super) enum RawItemKind {
Module(Idx<ModuleData>),
Import(Import),
Def(Idx<DefData>),
Macro(Idx<MacroData>),
Impl(Idx<ImplData>),
}
#[derive(Debug, PartialEq, Eq)]
pub(super) enum ModuleData {
Declaration {
name: Name,
visibility: RawVisibility,
ast_id: FileAstId<ast::Module>,
},
Definition {
name: Name,
visibility: RawVisibility,
ast_id: FileAstId<ast::Module>,
items: Vec<RawItem>,
},
}
pub(crate) type Import = Idx<ImportData>;
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct ImportData {
pub(super) path: ModPath,
pub(super) alias: Option<ImportAlias>,
pub(super) is_glob: bool,
pub(super) is_prelude: bool,
pub(super) is_extern_crate: bool,
pub(super) is_macro_use: bool,
pub(super) visibility: RawVisibility,
}
// type Def = Idx<DefData>;
#[derive(Debug, PartialEq, Eq)]
pub(super) struct DefData {
pub(super) name: Name,
pub(super) kind: DefKind,
pub(super) visibility: RawVisibility,
}
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
pub(super) enum StructDefKind {
Record,
Tuple,
Unit,
}
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
pub(super) enum DefKind {
Function(FileAstId<ast::FnDef>),
Struct(FileAstId<ast::StructDef>, StructDefKind),
Union(FileAstId<ast::UnionDef>),
Enum(FileAstId<ast::EnumDef>),
Const(FileAstId<ast::ConstDef>),
Static(FileAstId<ast::StaticDef>),
Trait(FileAstId<ast::TraitDef>),
TypeAlias(FileAstId<ast::TypeAliasDef>),
}
impl DefKind {
pub fn ast_id(self) -> FileAstId<ast::ModuleItem> {
match self {
DefKind::Function(it) => it.upcast(),
DefKind::Struct(it, _) => it.upcast(),
DefKind::Union(it) => it.upcast(),
DefKind::Enum(it) => it.upcast(),
DefKind::Const(it) => it.upcast(),
DefKind::Static(it) => it.upcast(),
DefKind::Trait(it) => it.upcast(),
DefKind::TypeAlias(it) => it.upcast(),
}
}
}
#[derive(Debug, PartialEq, Eq)]
pub(super) struct MacroData {
pub(super) ast_id: FileAstId<ast::MacroCall>,
pub(super) path: ModPath,
pub(super) name: Option<Name>,
pub(super) export: bool,
pub(super) local_inner: bool,
pub(super) builtin: bool,
}
#[derive(Debug, PartialEq, Eq)]
pub(super) struct ImplData {
pub(super) ast_id: FileAstId<ast::ImplDef>,
}
struct RawItemsCollector {
raw_items: RawItems,
source_ast_id_map: Arc<AstIdMap>,
file_id: HirFileId,
hygiene: Hygiene,
}
impl RawItemsCollector {
fn process_module(
&mut self,
current_module: Option<Idx<ModuleData>>,
body: impl ast::ModuleItemOwner,
) {
for item in body.items() {
self.add_item(current_module, item)
}
}
fn add_item(&mut self, current_module: Option<Idx<ModuleData>>, item: ast::ModuleItem) {
let attrs = self.parse_attrs(&item);
let visibility = RawVisibility::from_ast_with_hygiene(item.visibility(), &self.hygiene);
let (kind, name) = match item {
ast::ModuleItem::Module(module) => {
self.add_module(current_module, module);
return;
}
ast::ModuleItem::UseItem(use_item) => {
self.add_use_item(current_module, use_item);
return;
}
ast::ModuleItem::ExternCrateItem(extern_crate) => {
self.add_extern_crate_item(current_module, extern_crate);
return;
}
ast::ModuleItem::ImplDef(it) => {
self.add_impl(current_module, it);
return;
}
ast::ModuleItem::StructDef(it) => {
let kind = match it.kind() {
ast::StructKind::Record(_) => StructDefKind::Record,
ast::StructKind::Tuple(_) => StructDefKind::Tuple,
ast::StructKind::Unit => StructDefKind::Unit,
};
let id = self.source_ast_id_map.ast_id(&it);
let name = it.name();
(DefKind::Struct(id, kind), name)
}
ast::ModuleItem::UnionDef(it) => {
let id = self.source_ast_id_map.ast_id(&it);
let name = it.name();
(DefKind::Union(id), name)
}
ast::ModuleItem::EnumDef(it) => {
(DefKind::Enum(self.source_ast_id_map.ast_id(&it)), it.name())
}
ast::ModuleItem::FnDef(it) => {
(DefKind::Function(self.source_ast_id_map.ast_id(&it)), it.name())
}
ast::ModuleItem::TraitDef(it) => {
(DefKind::Trait(self.source_ast_id_map.ast_id(&it)), it.name())
}
ast::ModuleItem::TypeAliasDef(it) => {
(DefKind::TypeAlias(self.source_ast_id_map.ast_id(&it)), it.name())
}
ast::ModuleItem::ConstDef(it) => {
(DefKind::Const(self.source_ast_id_map.ast_id(&it)), it.name())
}
ast::ModuleItem::StaticDef(it) => {
(DefKind::Static(self.source_ast_id_map.ast_id(&it)), it.name())
}
ast::ModuleItem::MacroCall(it) => {
self.add_macro(current_module, it);
return;
}
ast::ModuleItem::ExternBlock(it) => {
self.add_extern_block(current_module, it);
return;
}
};
if let Some(name) = name {
let name = name.as_name();
let def = self.raw_items.defs.alloc(DefData { name, kind, visibility });
self.push_item(current_module, attrs, RawItemKind::Def(def));
}
}
fn add_extern_block(
&mut self,
current_module: Option<Idx<ModuleData>>,
block: ast::ExternBlock,
) {
if let Some(items) = block.extern_item_list() {
for item in items.extern_items() {
let attrs = self.parse_attrs(&item);
let visibility =
RawVisibility::from_ast_with_hygiene(item.visibility(), &self.hygiene);
let (kind, name) = match item {
ast::ExternItem::FnDef(it) => {
(DefKind::Function(self.source_ast_id_map.ast_id(&it)), it.name())
}
ast::ExternItem::StaticDef(it) => {
(DefKind::Static(self.source_ast_id_map.ast_id(&it)), it.name())
}
};
if let Some(name) = name {
let name = name.as_name();
let def = self.raw_items.defs.alloc(DefData { name, kind, visibility });
self.push_item(current_module, attrs, RawItemKind::Def(def));
}
}
}
}
fn add_module(&mut self, current_module: Option<Idx<ModuleData>>, module: ast::Module) {
let name = match module.name() {
Some(it) => it.as_name(),
None => return,
};
let attrs = self.parse_attrs(&module);
let visibility = RawVisibility::from_ast_with_hygiene(module.visibility(), &self.hygiene);
let ast_id = self.source_ast_id_map.ast_id(&module);
if module.semicolon_token().is_some() {
let item =
self.raw_items.modules.alloc(ModuleData::Declaration { name, visibility, ast_id });
self.push_item(current_module, attrs, RawItemKind::Module(item));
return;
}
if let Some(item_list) = module.item_list() {
let item = self.raw_items.modules.alloc(ModuleData::Definition {
name,
visibility,
ast_id,
items: Vec::new(),
});
self.process_module(Some(item), item_list);
self.push_item(current_module, attrs, RawItemKind::Module(item));
return;
}
mark::hit!(name_res_works_for_broken_modules);
}
fn add_use_item(&mut self, current_module: Option<Idx<ModuleData>>, use_item: ast::UseItem) {
// FIXME: cfg_attr
let is_prelude = use_item.has_atom_attr("prelude_import");
let attrs = self.parse_attrs(&use_item);
let visibility = RawVisibility::from_ast_with_hygiene(use_item.visibility(), &self.hygiene);
let mut buf = Vec::new();
ModPath::expand_use_item(
InFile { value: use_item, file_id: self.file_id },
&self.hygiene,
|path, _use_tree, is_glob, alias| {
let import_data = ImportData {
path,
alias,
is_glob,
is_prelude,
is_extern_crate: false,
is_macro_use: false,
visibility: visibility.clone(),
};
buf.push(import_data);
},
);
for import_data in buf {
self.push_import(current_module, attrs.clone(), import_data);
}
}
fn add_extern_crate_item(
&mut self,
current_module: Option<Idx<ModuleData>>,
extern_crate: ast::ExternCrateItem,
) {
if let Some(name_ref) = extern_crate.name_ref() {
let path = ModPath::from_name_ref(&name_ref);
let visibility =
RawVisibility::from_ast_with_hygiene(extern_crate.visibility(), &self.hygiene);
let alias = extern_crate.alias().map(|a| {
a.name().map(|it| it.as_name()).map_or(ImportAlias::Underscore, ImportAlias::Alias)
});
let attrs = self.parse_attrs(&extern_crate);
// FIXME: cfg_attr
let is_macro_use = extern_crate.has_atom_attr("macro_use");
let import_data = ImportData {
path,
alias,
is_glob: false,
is_prelude: false,
is_extern_crate: true,
is_macro_use,
visibility,
};
self.push_import(current_module, attrs, import_data);
}
}
fn add_macro(&mut self, current_module: Option<Idx<ModuleData>>, m: ast::MacroCall) {
let attrs = self.parse_attrs(&m);
let path = match m.path().and_then(|path| ModPath::from_src(path, &self.hygiene)) {
Some(it) => it,
_ => return,
};
let name = m.name().map(|it| it.as_name());
let ast_id = self.source_ast_id_map.ast_id(&m);
// FIXME: cfg_attr
let export_attr = attrs.by_key("macro_export");
let export = export_attr.exists();
let local_inner = if export {
export_attr.tt_values().map(|it| &it.token_trees).flatten().any(|it| match it {
tt::TokenTree::Leaf(tt::Leaf::Ident(ident)) => {
ident.text.contains("local_inner_macros")
}
_ => false,
})
} else {
false
};
let builtin = attrs.by_key("rustc_builtin_macro").exists();
let m = self.raw_items.macros.alloc(MacroData {
ast_id,
path,
name,
export,
local_inner,
builtin,
});
self.push_item(current_module, attrs, RawItemKind::Macro(m));
}
fn add_impl(&mut self, current_module: Option<Idx<ModuleData>>, imp: ast::ImplDef) {
let attrs = self.parse_attrs(&imp);
let ast_id = self.source_ast_id_map.ast_id(&imp);
let imp = self.raw_items.impls.alloc(ImplData { ast_id });
self.push_item(current_module, attrs, RawItemKind::Impl(imp))
}
fn push_import(
&mut self,
current_module: Option<Idx<ModuleData>>,
attrs: Attrs,
data: ImportData,
) {
let import = self.raw_items.imports.alloc(data);
self.push_item(current_module, attrs, RawItemKind::Import(import))
}
fn push_item(
&mut self,
current_module: Option<Idx<ModuleData>>,
attrs: Attrs,
kind: RawItemKind,
) {
match current_module {
Some(module) => match &mut self.raw_items.modules[module] {
ModuleData::Definition { items, .. } => items,
ModuleData::Declaration { .. } => unreachable!(),
},
None => &mut self.raw_items.items,
}
.push(RawItem { attrs, kind })
}
fn parse_attrs(&self, item: &impl ast::AttrsOwner) -> Attrs {
Attrs::new(item, &self.hygiene)
}
}

View file

@ -57,44 +57,6 @@ fn typing_inside_a_function_should_not_invalidate_def_map() {
); );
} }
#[test]
fn adding_inner_items_should_not_invalidate_def_map() {
check_def_map_is_not_recomputed(
r"
//- /lib.rs
struct S { a: i32}
enum E { A }
trait T {
fn a() {}
}
mod foo;<|>
impl S {
fn a() {}
}
use crate::foo::bar::Baz;
//- /foo/mod.rs
pub mod bar;
//- /foo/bar.rs
pub struct Baz;
",
r"
struct S { a: i32, b: () }
enum E { A, B }
trait T {
fn a() {}
fn b() {}
}
mod foo;<|>
impl S {
fn a() {}
fn b() {}
}
use crate::foo::bar::Baz;
",
);
}
#[test] #[test]
fn typing_inside_a_macro_should_not_invalidate_def_map() { fn typing_inside_a_macro_should_not_invalidate_def_map() {
let (mut db, pos) = TestDB::with_position( let (mut db, pos) = TestDB::with_position(

View file

@ -20,8 +20,11 @@ fn name_res_works_for_broken_modules() {
", ",
); );
assert_snapshot!(map, @r###" assert_snapshot!(map, @r###"
crate crate
Baz: _ Baz: _
foo: t
crate::foo
"###); "###);
} }
@ -719,10 +722,7 @@ fn unresolved_module_diagnostics() {
), ),
), ),
), ),
value: FileAstId { value: FileAstId::<ra_syntax::ast::generated::nodes::Module>(1),
raw: Idx::<SyntaxNodePtr>(1),
_ty: PhantomData,
},
}, },
candidate: "bar.rs", candidate: "bar.rs",
}, },

View file

@ -2,30 +2,37 @@
use hir_expand::InFile; use hir_expand::InFile;
use ra_arena::map::ArenaMap; use ra_arena::map::ArenaMap;
use ra_syntax::AstNode;
use crate::{db::DefDatabase, AssocItemLoc, ItemLoc}; use crate::{db::DefDatabase, item_tree::ItemTreeNode, AssocItemLoc, ItemLoc};
pub trait HasSource { pub trait HasSource {
type Value; type Value;
fn source(&self, db: &dyn DefDatabase) -> InFile<Self::Value>; fn source(&self, db: &dyn DefDatabase) -> InFile<Self::Value>;
} }
impl<N: AstNode> HasSource for AssocItemLoc<N> { impl<N: ItemTreeNode> HasSource for AssocItemLoc<N> {
type Value = N; type Value = N::Source;
fn source(&self, db: &dyn DefDatabase) -> InFile<N> { fn source(&self, db: &dyn DefDatabase) -> InFile<N::Source> {
let node = self.ast_id.to_node(db.upcast()); let tree = db.item_tree(self.id.file_id);
InFile::new(self.ast_id.file_id, node) let ast_id_map = db.ast_id_map(self.id.file_id);
let root = db.parse_or_expand(self.id.file_id).unwrap();
let node = &tree[self.id.value];
InFile::new(self.id.file_id, ast_id_map.get(node.ast_id()).to_node(&root))
} }
} }
impl<N: AstNode> HasSource for ItemLoc<N> { impl<N: ItemTreeNode> HasSource for ItemLoc<N> {
type Value = N; type Value = N::Source;
fn source(&self, db: &dyn DefDatabase) -> InFile<N> { fn source(&self, db: &dyn DefDatabase) -> InFile<N::Source> {
let node = self.ast_id.to_node(db.upcast()); let tree = db.item_tree(self.id.file_id);
InFile::new(self.ast_id.file_id, node) let ast_id_map = db.ast_id_map(self.id.file_id);
let root = db.parse_or_expand(self.id.file_id).unwrap();
let node = &tree[self.id.value];
InFile::new(self.id.file_id, ast_id_map.get(node.ast_id()).to_node(&root))
} }
} }

View file

@ -6,7 +6,7 @@ use ra_syntax::ast;
use crate::{ use crate::{
db::DefDatabase, db::DefDatabase,
path::{ModPath, PathKind}, path::{ModPath, PathKind},
AssocContainerId, ModuleId, ModuleId,
}; };
/// Visibility of an item, not yet resolved. /// Visibility of an item, not yet resolved.
@ -25,25 +25,6 @@ impl RawVisibility {
RawVisibility::Module(path) RawVisibility::Module(path)
} }
pub(crate) fn default_for_container(container_id: AssocContainerId) -> Self {
match container_id {
AssocContainerId::TraitId(_) => RawVisibility::Public,
_ => RawVisibility::private(),
}
}
pub(crate) fn from_ast_with_default(
db: &dyn DefDatabase,
default: RawVisibility,
node: InFile<Option<ast::Visibility>>,
) -> RawVisibility {
Self::from_ast_with_hygiene_and_default(
node.value,
default,
&Hygiene::new(db.upcast(), node.file_id),
)
}
pub(crate) fn from_ast( pub(crate) fn from_ast(
db: &dyn DefDatabase, db: &dyn DefDatabase,
node: InFile<Option<ast::Visibility>>, node: InFile<Option<ast::Visibility>>,

View file

@ -6,6 +6,8 @@
//! changes. //! changes.
use std::{ use std::{
any::type_name,
fmt,
hash::{Hash, Hasher}, hash::{Hash, Hasher},
marker::PhantomData, marker::PhantomData,
}; };
@ -14,7 +16,6 @@ use ra_arena::{Arena, Idx};
use ra_syntax::{ast, AstNode, AstPtr, SyntaxNode, SyntaxNodePtr}; use ra_syntax::{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.
#[derive(Debug)]
pub struct FileAstId<N: AstNode> { pub struct FileAstId<N: AstNode> {
raw: ErasedFileAstId, raw: ErasedFileAstId,
_ty: PhantomData<fn() -> N>, _ty: PhantomData<fn() -> N>,
@ -39,11 +40,17 @@ impl<N: AstNode> Hash for FileAstId<N> {
} }
} }
impl<N: AstNode> fmt::Debug for FileAstId<N> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "FileAstId::<{}>({})", type_name::<N>(), self.raw.into_raw())
}
}
impl<N: AstNode> FileAstId<N> { impl<N: AstNode> FileAstId<N> {
// Can't make this a From implementation because of coherence // Can't make this a From implementation because of coherence
pub fn upcast<M: AstNode>(self) -> FileAstId<M> pub fn upcast<M: AstNode>(self) -> FileAstId<M>
where where
M: From<N>, N: Into<M>,
{ {
FileAstId { raw: self.raw, _ty: PhantomData } FileAstId { raw: self.raw, _ty: PhantomData }
} }
@ -89,7 +96,7 @@ impl AstIdMap {
} }
} }
pub(crate) fn get<N: AstNode>(&self, id: FileAstId<N>) -> AstPtr<N> { pub fn get<N: AstNode>(&self, id: FileAstId<N>) -> AstPtr<N> {
self.arena[id.raw].clone().cast::<N>().unwrap() self.arena[id.raw].clone().cast::<N>().unwrap()
} }

View file

@ -67,8 +67,8 @@ fn type_at_pos_displayed(
panic!("Can't find expression") panic!("Can't find expression")
} }
fn type_at(content: &str) -> String { fn type_at(ra_fixture: &str) -> String {
let (db, file_pos) = TestDB::with_position(content); let (db, file_pos) = TestDB::with_position(ra_fixture);
type_at_pos(&db, file_pos) type_at_pos(&db, file_pos)
} }
@ -164,13 +164,19 @@ fn infer_with_mismatches(content: &str, include_mismatches: bool) -> String {
visit_module(&db, &crate_def_map, module.local_id, &mut |it| defs.push(it)); visit_module(&db, &crate_def_map, module.local_id, &mut |it| defs.push(it));
defs.sort_by_key(|def| match def { defs.sort_by_key(|def| match def {
DefWithBodyId::FunctionId(it) => { DefWithBodyId::FunctionId(it) => {
it.lookup(&db).ast_id.to_node(&db).syntax().text_range().start() let loc = it.lookup(&db);
let tree = db.item_tree(loc.id.file_id);
tree.source(&db, loc.id).syntax().text_range().start()
} }
DefWithBodyId::ConstId(it) => { DefWithBodyId::ConstId(it) => {
it.lookup(&db).ast_id.to_node(&db).syntax().text_range().start() let loc = it.lookup(&db);
let tree = db.item_tree(loc.id.file_id);
tree.source(&db, loc.id).syntax().text_range().start()
} }
DefWithBodyId::StaticId(it) => { DefWithBodyId::StaticId(it) => {
it.lookup(&db).ast_id.to_node(&db).syntax().text_range().start() let loc = it.lookup(&db);
let tree = db.item_tree(loc.id.file_id);
tree.source(&db, loc.id).syntax().text_range().start()
} }
}); });
for def in defs { for def in defs {

View file

@ -199,7 +199,7 @@ impl RootDatabase {
hir::db::InternEagerExpansionQuery hir::db::InternEagerExpansionQuery
// DefDatabase // DefDatabase
hir::db::RawItemsQuery hir::db::ItemTreeQuery
hir::db::CrateDefMapQueryQuery hir::db::CrateDefMapQueryQuery
hir::db::StructDataQuery hir::db::StructDataQuery
hir::db::UnionDataQuery hir::db::UnionDataQuery