draw the rest of the owl

This commit is contained in:
Jonas Schievink 2020-06-22 15:07:06 +02:00
parent b94caeb88b
commit 4b03b39d5b
13 changed files with 959 additions and 415 deletions

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

@ -243,7 +243,7 @@ impl Body {
} }
}; };
let expander = Expander::new(db, file_id, module); let expander = Expander::new(db, file_id, module);
let (body, source_map) = Body::new(db, def, expander, params, body); let (body, source_map) = Body::new(db, file_id, def, expander, params, body);
(Arc::new(body), Arc::new(source_map)) (Arc::new(body), Arc::new(source_map))
} }
@ -253,12 +253,13 @@ impl Body {
fn new( fn new(
db: &dyn DefDatabase, db: &dyn DefDatabase,
file_id: HirFileId,
def: DefWithBodyId, def: DefWithBodyId,
expander: Expander, expander: Expander,
params: Option<ast::ParamList>, params: Option<ast::ParamList>,
body: Option<ast::Expr>, body: Option<ast::Expr>,
) -> (Body, BodySourceMap) { ) -> (Body, BodySourceMap) {
lower::lower(db, def, expander, params, body) lower::lower(db, file_id, def, expander, params, body)
} }
} }

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, ItemTreeSource},
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,7 @@ use crate::{
use super::{ExprSource, PatSource}; use super::{ExprSource, PatSource};
use ast::AstChildren; use ast::AstChildren;
use std::sync::Arc;
pub(crate) struct LowerCtx { pub(crate) struct LowerCtx {
hygiene: Hygiene, hygiene: Hygiene,
@ -55,11 +57,13 @@ impl LowerCtx {
pub(super) fn lower( pub(super) fn lower(
db: &dyn DefDatabase, db: &dyn DefDatabase,
file_id: HirFileId,
def: DefWithBodyId, def: DefWithBodyId,
expander: Expander, expander: Expander,
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(file_id);
ExprCollector { ExprCollector {
db, db,
def, def,
@ -72,6 +76,7 @@ pub(super) fn lower(
body_expr: dummy_expr_id(), body_expr: dummy_expr_id(),
item_scope: Default::default(), item_scope: Default::default(),
}, },
item_trees: vec![(file_id, item_tree)],
} }
.collect(params, body) .collect(params, body)
} }
@ -82,6 +87,8 @@ struct ExprCollector<'a> {
expander: Expander, expander: Expander,
body: Body, body: Body,
source_map: BodySourceMap, source_map: BodySourceMap,
item_trees: Vec<(HirFileId, Arc<ItemTree>)>,
} }
impl ExprCollector<'_> { impl ExprCollector<'_> {
@ -533,6 +540,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.push((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 +557,21 @@ impl ExprCollector<'_> {
} }
} }
fn find_inner_item<S: ItemTreeSource>(&self, id: AstId<ast::ModuleItem>) -> FileItemTreeId<S> {
let index =
self.item_trees.iter().position(|(file, _)| *file == id.file_id).unwrap_or_else(|| {
panic!("couldn't find item tree for file {:?}", id.file_id);
});
let tree = &self.item_trees[index].1;
// 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()
}
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 +603,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 items = block
.items()
.filter_map(|item| {
let (def, name): (ModuleDefId, Option<ast::Name>) = match item { let (def, name): (ModuleDefId, Option<ast::Name>) = match item {
ast::ModuleItem::FnDef(def) => { ast::ModuleItem::FnDef(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()));
( (
FunctionLoc { container: container.into(), ast_id }.intern(self.db).into(), FunctionLoc { container: container.into(), id: ast_id.with_value(id) }
.intern(self.db)
.into(),
def.name(), def.name(),
) )
} }
ast::ModuleItem::TypeAliasDef(def) => { ast::ModuleItem::TypeAliasDef(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()));
( (
TypeAliasLoc { container: container.into(), ast_id }.intern(self.db).into(), TypeAliasLoc { container: container.into(), id: ast_id.with_value(id) }
.intern(self.db)
.into(),
def.name(), def.name(),
) )
} }
ast::ModuleItem::ConstDef(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(), ConstLoc { container: container.into(), id: ast_id.with_value(id) }
.intern(self.db)
.into(),
def.name(), def.name(),
) )
} }
ast::ModuleItem::StaticDef(def) => { ast::ModuleItem::StaticDef(def) => {
let ast_id = self.expander.ast_id(&def); let ast_id = self.expander.ast_id(&def);
(StaticLoc { container, ast_id }.intern(self.db).into(), def.name()) let id = self.find_inner_item(ast_id.map(|id| id.upcast()));
(
StaticLoc { container, id: ast_id.with_value(id) }
.intern(self.db)
.into(),
def.name(),
)
} }
ast::ModuleItem::StructDef(def) => { ast::ModuleItem::StructDef(def) => {
let ast_id = self.expander.ast_id(&def); let ast_id = self.expander.ast_id(&def);
(StructLoc { container, ast_id }.intern(self.db).into(), def.name()) let id = self.find_inner_item(ast_id.map(|id| id.upcast()));
(
StructLoc { container, id: ast_id.with_value(id) }
.intern(self.db)
.into(),
def.name(),
)
} }
ast::ModuleItem::EnumDef(def) => { ast::ModuleItem::EnumDef(def) => {
let ast_id = self.expander.ast_id(&def); let ast_id = self.expander.ast_id(&def);
(EnumLoc { container, ast_id }.intern(self.db).into(), def.name()) 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) => { ast::ModuleItem::UnionDef(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()));
(
UnionLoc { container, id: ast_id.with_value(id) }
.intern(self.db)
.into(),
def.name(),
)
} }
ast::ModuleItem::TraitDef(def) => { ast::ModuleItem::TraitDef(def) => {
let ast_id = self.expander.ast_id(&def); let ast_id = self.expander.ast_id(&def);
(TraitLoc { container, ast_id }.intern(self.db).into(), def.name()) 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(_) => continue, // FIXME: collect from extern blocks ast::ModuleItem::ExternBlock(_) => return None, // FIXME: collect from extern blocks
ast::ModuleItem::ImplDef(_) ast::ModuleItem::ImplDef(_)
| ast::ModuleItem::UseItem(_) | ast::ModuleItem::UseItem(_)
| ast::ModuleItem::ExternCrateItem(_) | ast::ModuleItem::ExternCrateItem(_)
| ast::ModuleItem::Module(_) | ast::ModuleItem::Module(_)
| ast::ModuleItem::MacroCall(_) => continue, | 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

@ -5,24 +5,23 @@ use std::sync::Arc;
use hir_expand::{ use hir_expand::{
hygiene::Hygiene, hygiene::Hygiene,
name::{name, AsName, Name}, name::{name, AsName, Name},
AstId, InFile, InFile,
}; };
use ra_prof::profile; use ra_prof::profile;
use ra_syntax::ast::{ use ra_syntax::ast::{self, NameOwner, TypeAscriptionOwner, TypeBoundsOwner, VisibilityOwner};
self, AssocItem, AstNode, ModuleItemOwner, NameOwner, TypeAscriptionOwner, TypeBoundsOwner,
VisibilityOwner,
};
use crate::{ use crate::{
attr::Attrs, attr::Attrs,
body::Expander,
body::LowerCtx, body::LowerCtx,
db::DefDatabase, db::DefDatabase,
item_tree::{AssocItem, ItemTreeId, ModItem},
path::{path, AssociatedTypeBinding, GenericArgs, Path}, path::{path, AssociatedTypeBinding, GenericArgs, Path},
src::HasSource, src::HasSource,
type_ref::{Mutability, 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)]
@ -155,30 +154,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);
if let Some(item_list) = src.value.item_list() { let items = collect_items(
let mut expander = Expander::new(db, tr_loc.ast_id.file_id, module_id);
items.extend(collect_items(
db, db,
module_id,
&mut expander, &mut expander,
item_list.assoc_items(), tr_def.items.iter().copied(),
src.file_id, tr_loc.id.file_id,
container, container,
)); 100,
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 +202,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,
if let Some(item_list) = src.value.item_list() { module_id,
let mut expander = Expander::new(db, impl_loc.ast_id.file_id, module_id); &mut expander,
items.extend( impl_def.items.iter().copied(),
collect_items(db, &mut expander, item_list.assoc_items(), src.file_id, container) impl_loc.id.file_id,
.into_iter() container,
.map(|(_, item)| item), 100,
); );
items.extend( let items = items.into_iter().map(|(_, item)| item).collect();
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(ImplData { target_trait, target_type, items, is_negative })
Arc::new(res)
} }
} }
@ -295,28 +283,12 @@ impl StaticData {
} }
} }
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 +296,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];
if !item.attrs.is_cfg_enabled(&cfg_options) {
continue;
} }
expander.exit(db, mark); let def = FunctionLoc { container, id: ItemTreeId::new(file_id, id) }.intern(db);
res items.push((item.name.clone(), def.into()));
}
// FIXME: cfg?
AssocItem::Const(id) => {
let item = &item_tree[id];
let name = if let Some(name) = item.name.clone() {
name
} else { } else {
Vec::new() continue;
};
let def = ConstLoc { container, id: ItemTreeId::new(file_id, id) }.intern(db);
items.push((name, def.into()));
} }
} AssocItem::TypeAlias(id) => {
let item = &item_tree[id];
let def = TypeAliasLoc { container, id: ItemTreeId::new(file_id, id) }.intern(db);
items.push((item.name.clone(), def.into()));
}
AssocItem::MacroCall(call) => {
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);
fn collect_items( if let Some((mark, mac)) = expander.enter_expand(db, None, call) {
db: &dyn DefDatabase, let src: InFile<ast::MacroItems> = expander.to_source(mac);
expander: &mut Expander, let item_tree = db.item_tree(src.file_id);
assoc_items: impl Iterator<Item = AssocItem>, let iter =
file_id: crate::HirFileId, item_tree.top_level_items().iter().filter_map(ModItem::as_assoc_item);
container: AssocContainerId, items.extend(collect_items(
) -> Vec<(Name, AssocItemId)> { db,
let items = db.ast_id_map(file_id); module,
expander,
iter,
src.file_id,
container,
limit - 1,
));
assoc_items expander.exit(db, mark);
.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)) }
.intern(db);
Some((name, def.into()))
} }
ast::AssocItem::ConstDef(it) => {
let name = it.name().map_or_else(Name::missing, |it| it.as_name());
let def = ConstLoc { container, ast_id: AstId::new(file_id, items.ast_id(&it)) }
.intern(db);
Some((name, def.into()))
} }
ast::AssocItem::TypeAliasDef(it) => {
let name = it.name().map_or_else(Name::missing, |it| it.as_name());
let def =
TypeAliasLoc { container, ast_id: AstId::new(file_id, items.ast_id(&it)) }
.intern(db);
Some((name, def.into()))
} }
})
.collect() items
} }

View file

@ -1,6 +1,8 @@
//! A simplified AST that only contains items. //! A simplified AST that only contains items.
mod lower; mod lower;
#[cfg(test)]
mod tests;
use std::{ use std::{
fmt::{self, Debug}, fmt::{self, Debug},
@ -31,16 +33,20 @@ use crate::{
type_ref::{Mutability, TypeBound, TypeRef}, type_ref::{Mutability, TypeBound, TypeRef},
visibility::RawVisibility, visibility::RawVisibility,
}; };
use smallvec::SmallVec;
/// The item tree of a source file. /// The item tree of a source file.
#[derive(Debug, Default, Eq, PartialEq)] #[derive(Debug, Eq, PartialEq)]
pub struct ItemTree { pub struct ItemTree {
file_id: HirFileId,
top_level: Vec<ModItem>, top_level: Vec<ModItem>,
top_attrs: Attrs, top_attrs: Attrs,
attrs: FxHashMap<ModItem, Attrs>, attrs: FxHashMap<ModItem, Attrs>,
empty_attrs: Attrs, empty_attrs: Attrs,
inner_items: FxHashMap<FileAstId<ast::ModuleItem>, SmallVec<[ModItem; 1]>>,
imports: Arena<Import>, imports: Arena<Import>,
extern_crates: Arena<ExternCrate>,
functions: Arena<Function>, functions: Arena<Function>,
structs: Arena<Struct>, structs: Arena<Struct>,
fields: Arena<Field>, fields: Arena<Field>,
@ -63,7 +69,7 @@ impl ItemTree {
let syntax = if let Some(node) = db.parse_or_expand(file_id) { let syntax = if let Some(node) = db.parse_or_expand(file_id) {
node node
} else { } else {
return Default::default(); return Arc::new(Self::empty(file_id));
}; };
let hygiene = Hygiene::new(db.upcast(), file_id); let hygiene = Hygiene::new(db.upcast(), file_id);
@ -80,20 +86,41 @@ impl ItemTree {
file_storage = file; file_storage = file;
&file_storage &file_storage
}, },
_ => return Default::default(), _ => return Arc::new(Self::empty(file_id)),
} }
}; };
let map = db.ast_id_map(file_id); let ctx = lower::Ctx::new(db, hygiene, file_id);
let mut ctx = lower::Ctx { let mut item_tree = ctx.lower(item_owner);
tree: ItemTree::default(), item_tree.top_attrs = top_attrs.unwrap_or_default();
hygiene, Arc::new(item_tree)
file: file_id, }
source_ast_id_map: map,
body_ctx: crate::body::LowerCtx::new(db, file_id), fn empty(file_id: HirFileId) -> Self {
}; Self {
ctx.tree.top_attrs = top_attrs.unwrap_or_default(); file_id,
Arc::new(ctx.lower(item_owner)) top_level: Default::default(),
top_attrs: Default::default(),
attrs: Default::default(),
empty_attrs: Default::default(),
inner_items: Default::default(),
imports: Default::default(),
extern_crates: Default::default(),
functions: Default::default(),
structs: Default::default(),
fields: Default::default(),
unions: Default::default(),
enums: Default::default(),
variants: Default::default(),
consts: Default::default(),
statics: Default::default(),
traits: Default::default(),
impls: Default::default(),
type_aliases: Default::default(),
mods: Default::default(),
macro_calls: Default::default(),
exprs: Default::default(),
}
} }
/// Returns an iterator over all items located at the top level of the `HirFileId` this /// Returns an iterator over all items located at the top level of the `HirFileId` this
@ -110,17 +137,49 @@ impl ItemTree {
pub fn attrs(&self, of: ModItem) -> &Attrs { pub fn attrs(&self, of: ModItem) -> &Attrs {
self.attrs.get(&of).unwrap_or(&self.empty_attrs) self.attrs.get(&of).unwrap_or(&self.empty_attrs)
} }
/// 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: ItemTreeSource>(
&self,
db: &dyn DefDatabase,
of: FileItemTreeId<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(self.file_id)
.expect("parse_or_expand failed on constructed ItemTree");
let id = self[of].ast_id();
let map = db.ast_id_map(self.file_id);
let ptr = map.get(id);
ptr.to_node(&root)
}
} }
/// Trait implemented by all nodes in the item tree. /// Trait implemented by all nodes in the item tree.
pub trait ItemTreeNode: Clone { pub trait ItemTreeNode: Clone {
/// Looks up an instance of `Self` in an item tree. /// Looks up an instance of `Self` in an item tree.
fn lookup(tree: &ItemTree, index: Idx<Self>) -> &Self; 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>>;
} }
/// Trait for item tree nodes that allow accessing the original AST node. /// Trait for item tree nodes that allow accessing the original AST node.
pub trait ItemTreeSource: ItemTreeNode { pub trait ItemTreeSource: ItemTreeNode {
type Source: AstNode; type Source: AstNode + Into<ast::ModuleItem>;
fn ast_id(&self) -> FileAstId<Self::Source>; fn ast_id(&self) -> FileAstId<Self::Source>;
} }
@ -164,12 +223,22 @@ macro_rules! nodes {
fn lookup(tree: &ItemTree, index: Idx<Self>) -> &Self { fn lookup(tree: &ItemTree, index: Idx<Self>) -> &Self {
&tree.$fld[index] &tree.$fld[index]
} }
fn id_from_mod_item(mod_item: ModItem) -> Option<FileItemTreeId<Self>> {
if let ModItem::$node(id) = mod_item {
Some(id)
} else {
None
}
}
} }
)+ }; )+ };
} }
nodes!( nodes!(
Import in imports, Import in imports,
ExternCrate in extern_crates,
Function in functions, Function in functions,
Struct in structs, Struct in structs,
Union in unions, Union in unions,
@ -196,6 +265,8 @@ macro_rules! source {
} }
source! { source! {
Import -> ast::UseItem,
ExternCrate -> ast::ExternCrateItem,
Function -> ast::FnDef, Function -> ast::FnDef,
Struct -> ast::StructDef, Struct -> ast::StructDef,
Union -> ast::UnionDef, Union -> ast::UnionDef,
@ -248,7 +319,7 @@ impl<N: ItemTreeNode> Index<FileItemTreeId<N>> for ItemTree {
} }
} }
/// A desugared `extern crate` or `use` import. /// A desugared `use` import.
#[derive(Debug, Clone, Eq, PartialEq)] #[derive(Debug, Clone, Eq, PartialEq)]
pub struct Import { pub struct Import {
pub path: ModPath, pub path: ModPath,
@ -256,8 +327,19 @@ pub struct Import {
pub visibility: RawVisibility, pub visibility: RawVisibility,
pub is_glob: bool, pub is_glob: bool,
pub is_prelude: bool, pub is_prelude: bool,
pub is_extern_crate: 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: RawVisibility,
/// Whether this is a `#[macro_use] extern crate ...`.
pub is_macro_use: bool, pub is_macro_use: bool,
pub ast_id: FileAstId<ast::ExternCrateItem>,
} }
#[derive(Debug, Clone, Eq, PartialEq)] #[derive(Debug, Clone, Eq, PartialEq)]
@ -270,7 +352,6 @@ pub struct Function {
pub params: Vec<TypeRef>, pub params: Vec<TypeRef>,
pub ret_type: TypeRef, pub ret_type: TypeRef,
pub ast_id: FileAstId<ast::FnDef>, pub ast_id: FileAstId<ast::FnDef>,
// FIXME inner items
} }
#[derive(Debug, Clone, Eq, PartialEq)] #[derive(Debug, Clone, Eq, PartialEq)]
@ -412,6 +493,7 @@ macro_rules! impl_froms {
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)] #[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
pub enum ModItem { pub enum ModItem {
Import(FileItemTreeId<Import>), Import(FileItemTreeId<Import>),
ExternCrate(FileItemTreeId<ExternCrate>),
Function(FileItemTreeId<Function>), Function(FileItemTreeId<Function>),
Struct(FileItemTreeId<Struct>), Struct(FileItemTreeId<Struct>),
Union(FileItemTreeId<Union>), Union(FileItemTreeId<Union>),
@ -429,6 +511,7 @@ impl ModItem {
pub fn as_assoc_item(&self) -> Option<AssocItem> { pub fn as_assoc_item(&self) -> Option<AssocItem> {
match self { match self {
ModItem::Import(_) ModItem::Import(_)
| ModItem::ExternCrate(_)
| ModItem::Struct(_) | ModItem::Struct(_)
| ModItem::Union(_) | ModItem::Union(_)
| ModItem::Enum(_) | ModItem::Enum(_)
@ -442,10 +525,15 @@ impl ModItem {
ModItem::Function(func) => Some(AssocItem::Function(*func)), ModItem::Function(func) => Some(AssocItem::Function(*func)),
} }
} }
pub fn downcast<N: ItemTreeNode>(self) -> Option<FileItemTreeId<N>> {
N::id_from_mod_item(self)
}
} }
impl_froms!(ModItem { impl_froms!(ModItem {
Import(FileItemTreeId<Import>), Import(FileItemTreeId<Import>),
ExternCrate(FileItemTreeId<ExternCrate>),
Function(FileItemTreeId<Function>), Function(FileItemTreeId<Function>),
Struct(FileItemTreeId<Struct>), Struct(FileItemTreeId<Struct>),
Union(FileItemTreeId<Union>), Union(FileItemTreeId<Union>),
@ -474,6 +562,17 @@ impl_froms!(AssocItem {
MacroCall(FileItemTreeId<MacroCall>), 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)] #[derive(Debug, Eq, PartialEq)]
pub struct Variant { pub struct Variant {
pub name: Name, pub name: Name,

View file

@ -7,9 +7,12 @@ use crate::{
}; };
use hir_expand::{ast_id_map::AstIdMap, hygiene::Hygiene, HirFileId}; use hir_expand::{ast_id_map::AstIdMap, hygiene::Hygiene, HirFileId};
use ra_arena::map::ArenaMap; use ra_arena::map::ArenaMap;
use ra_syntax::ast::{self, ModuleItemOwner}; use ra_syntax::{
ast::{self, ModuleItemOwner},
SyntaxNode,
};
use smallvec::SmallVec; use smallvec::SmallVec;
use std::sync::Arc; use std::{mem, sync::Arc};
fn id<N: ItemTreeNode>(index: Idx<N>) -> FileItemTreeId<N> { fn id<N: ItemTreeNode>(index: Idx<N>) -> FileItemTreeId<N> {
FileItemTreeId { index, _p: PhantomData } FileItemTreeId { index, _p: PhantomData }
@ -27,78 +30,81 @@ where
} }
pub(super) struct Ctx { pub(super) struct Ctx {
pub tree: ItemTree, tree: ItemTree,
pub hygiene: Hygiene, hygiene: Hygiene,
pub file: HirFileId, file: HirFileId,
pub source_ast_id_map: Arc<AstIdMap>, source_ast_id_map: Arc<AstIdMap>,
pub body_ctx: crate::body::LowerCtx, body_ctx: crate::body::LowerCtx,
inner_items: Vec<ModItem>,
} }
impl Ctx { impl Ctx {
pub(super) fn new(db: &dyn DefDatabase, hygiene: Hygiene, file: HirFileId) -> Self {
Self {
tree: ItemTree::empty(file),
hygiene,
file,
source_ast_id_map: db.ast_id_map(file),
body_ctx: crate::body::LowerCtx::new(db, file),
inner_items: Vec::new(),
}
}
pub(super) fn lower(mut self, item_owner: &dyn ModuleItemOwner) -> ItemTree { pub(super) fn lower(mut self, item_owner: &dyn ModuleItemOwner) -> ItemTree {
self.tree.top_level = item_owner self.tree.top_level = item_owner
.items() .items()
.flat_map(|item| self.lower_mod_item(&item)) .flat_map(|item| self.lower_mod_item(&item, false))
.flat_map(|items| items.0) .flat_map(|items| items.0)
.collect(); .collect();
self.tree self.tree
} }
fn lower_mod_item(&mut self, item: &ast::ModuleItem) -> Option<ModItems> { 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(_) => 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 attrs = Attrs::new(item, &self.hygiene);
let items = match item { let items = match item {
ast::ModuleItem::StructDef(ast) => { ast::ModuleItem::StructDef(ast) => self.lower_struct(ast).map(Into::into),
self.lower_struct(ast).map(|data| id(self.tree.structs.alloc(data)).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::UnionDef(ast) => { ast::ModuleItem::FnDef(ast) => self.lower_function(ast).map(Into::into),
self.lower_union(ast).map(|data| id(self.tree.unions.alloc(data)).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::EnumDef(ast) => { ast::ModuleItem::ConstDef(ast) => Some(self.lower_const(ast).into()),
self.lower_enum(ast).map(|data| id(self.tree.enums.alloc(data)).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::FnDef(ast) => { ast::ModuleItem::ImplDef(ast) => self.lower_impl(ast).map(Into::into),
self.lower_function(ast).map(|data| id(self.tree.functions.alloc(data)).into())
}
ast::ModuleItem::TypeAliasDef(ast) => {
self.lower_type_alias(ast).map(|data| id(self.tree.type_aliases.alloc(data)).into())
}
ast::ModuleItem::StaticDef(ast) => {
self.lower_static(ast).map(|data| id(self.tree.statics.alloc(data)).into())
}
ast::ModuleItem::ConstDef(ast) => {
let data = self.lower_const(ast);
Some(id(self.tree.consts.alloc(data)).into())
}
ast::ModuleItem::Module(ast) => {
self.lower_module(ast).map(|data| id(self.tree.mods.alloc(data)).into())
}
ast::ModuleItem::TraitDef(ast) => {
self.lower_trait(ast).map(|data| id(self.tree.traits.alloc(data)).into())
}
ast::ModuleItem::ImplDef(ast) => {
self.lower_impl(ast).map(|data| id(self.tree.impls.alloc(data)).into())
}
ast::ModuleItem::UseItem(ast) => Some(ModItems( ast::ModuleItem::UseItem(ast) => Some(ModItems(
self.lower_use(ast) self.lower_use(ast).into_iter().map(Into::into).collect::<SmallVec<_>>(),
.into_iter()
.map(|data| id(self.tree.imports.alloc(data)).into())
.collect::<SmallVec<_>>(),
)), )),
ast::ModuleItem::ExternCrateItem(ast) => { ast::ModuleItem::ExternCrateItem(ast) => self.lower_extern_crate(ast).map(Into::into),
self.lower_extern_crate(ast).map(|data| id(self.tree.imports.alloc(data)).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<_>>()))
} }
ast::ModuleItem::MacroCall(ast) => {
self.lower_macro_call(ast).map(|data| id(self.tree.macro_calls.alloc(data)).into())
}
ast::ModuleItem::ExternBlock(ast) => Some(ModItems(
self.lower_extern_block(ast)
.into_iter()
.map(|item| match item {
Either::Left(func) => id(self.tree.functions.alloc(func)).into(),
Either::Right(statik) => id(self.tree.statics.alloc(statik)).into(),
})
.collect::<SmallVec<_>>(),
)),
}; };
if !attrs.is_empty() { if !attrs.is_empty() {
@ -110,22 +116,28 @@ impl Ctx {
items items
} }
fn lower_assoc_item(&mut self, item: &ast::AssocItem) -> Option<AssocItem> { fn collect_inner_items(&mut self, container: &SyntaxNode) {
let mut inner_items = mem::replace(&mut self.tree.inner_items, FxHashMap::default());
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;
}
fn lower_assoc_item(&mut self, item: &ast::ModuleItem) -> Option<AssocItem> {
match item { match item {
ast::AssocItem::FnDef(ast) => { ast::ModuleItem::FnDef(ast) => self.lower_function(ast).map(Into::into),
self.lower_function(ast).map(|data| id(self.tree.functions.alloc(data)).into()) ast::ModuleItem::TypeAliasDef(ast) => self.lower_type_alias(ast).map(Into::into),
} ast::ModuleItem::ConstDef(ast) => Some(self.lower_const(ast).into()),
ast::AssocItem::TypeAliasDef(ast) => { ast::ModuleItem::MacroCall(ast) => self.lower_macro_call(ast).map(Into::into),
self.lower_type_alias(ast).map(|data| id(self.tree.type_aliases.alloc(data)).into()) _ => None,
}
ast::AssocItem::ConstDef(ast) => {
let data = self.lower_const(ast);
Some(id(self.tree.consts.alloc(data)).into())
}
} }
} }
fn lower_struct(&mut self, strukt: &ast::StructDef) -> Option<Struct> { fn lower_struct(&mut self, strukt: &ast::StructDef) -> Option<FileItemTreeId<Struct>> {
let attrs = self.lower_attrs(strukt); let attrs = self.lower_attrs(strukt);
let visibility = self.lower_visibility(strukt); let visibility = self.lower_visibility(strukt);
let name = strukt.name()?.as_name(); let name = strukt.name()?.as_name();
@ -138,7 +150,7 @@ impl Ctx {
ast::StructKind::Unit => StructDefKind::Unit, ast::StructKind::Unit => StructDefKind::Unit,
}; };
let res = Struct { name, attrs, visibility, generic_params, fields, ast_id, kind }; let res = Struct { name, attrs, visibility, generic_params, fields, ast_id, kind };
Some(res) Some(id(self.tree.structs.alloc(res)))
} }
fn lower_fields(&mut self, strukt_kind: &ast::StructKind) -> Fields { fn lower_fields(&mut self, strukt_kind: &ast::StructKind) -> Fields {
@ -193,7 +205,7 @@ impl Ctx {
Some(res) Some(res)
} }
fn lower_union(&mut self, union: &ast::UnionDef) -> Option<Union> { fn lower_union(&mut self, union: &ast::UnionDef) -> Option<FileItemTreeId<Union>> {
let attrs = self.lower_attrs(union); let attrs = self.lower_attrs(union);
let visibility = self.lower_visibility(union); let visibility = self.lower_visibility(union);
let name = union.name()?.as_name(); let name = union.name()?.as_name();
@ -206,10 +218,10 @@ impl Ctx {
}; };
let ast_id = self.source_ast_id_map.ast_id(union); let ast_id = self.source_ast_id_map.ast_id(union);
let res = Union { name, attrs, visibility, generic_params, fields, ast_id }; let res = Union { name, attrs, visibility, generic_params, fields, ast_id };
Some(res) Some(id(self.tree.unions.alloc(res)))
} }
fn lower_enum(&mut self, enum_: &ast::EnumDef) -> Option<Enum> { fn lower_enum(&mut self, enum_: &ast::EnumDef) -> Option<FileItemTreeId<Enum>> {
let attrs = self.lower_attrs(enum_); let attrs = self.lower_attrs(enum_);
let visibility = self.lower_visibility(enum_); let visibility = self.lower_visibility(enum_);
let name = enum_.name()?.as_name(); let name = enum_.name()?.as_name();
@ -220,7 +232,7 @@ impl Ctx {
}; };
let ast_id = self.source_ast_id_map.ast_id(enum_); let ast_id = self.source_ast_id_map.ast_id(enum_);
let res = Enum { name, attrs, visibility, generic_params, variants, ast_id }; let res = Enum { name, attrs, visibility, generic_params, variants, ast_id };
Some(res) Some(id(self.tree.enums.alloc(res)))
} }
fn lower_variants(&mut self, variants: &ast::EnumVariantList) -> Range<Idx<Variant>> { fn lower_variants(&mut self, variants: &ast::EnumVariantList) -> Range<Idx<Variant>> {
@ -241,7 +253,7 @@ impl Ctx {
Some(res) Some(res)
} }
fn lower_function(&mut self, func: &ast::FnDef) -> Option<Function> { fn lower_function(&mut self, func: &ast::FnDef) -> Option<FileItemTreeId<Function>> {
let attrs = self.lower_attrs(func); let attrs = self.lower_attrs(func);
let visibility = self.lower_visibility(func); let visibility = self.lower_visibility(func);
let name = func.name()?.as_name(); let name = func.name()?.as_name();
@ -297,37 +309,42 @@ impl Ctx {
ast_id, ast_id,
}; };
res.generic_params = self.lower_generic_params(GenericsOwner::Function(&res), func); res.generic_params = self.lower_generic_params(GenericsOwner::Function(&res), func);
Some(res)
Some(id(self.tree.functions.alloc(res)))
} }
fn lower_type_alias(&mut self, type_alias: &ast::TypeAliasDef) -> Option<TypeAlias> { fn lower_type_alias(
&mut self,
type_alias: &ast::TypeAliasDef,
) -> Option<FileItemTreeId<TypeAlias>> {
let name = type_alias.name()?.as_name(); let name = type_alias.name()?.as_name();
let type_ref = type_alias.type_ref().map(|it| self.lower_type_ref(&it)); let type_ref = type_alias.type_ref().map(|it| self.lower_type_ref(&it));
let visibility = self.lower_visibility(type_alias); let visibility = self.lower_visibility(type_alias);
let generic_params = self.lower_generic_params(GenericsOwner::TypeAlias, 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 ast_id = self.source_ast_id_map.ast_id(type_alias);
let res = TypeAlias { name, visibility, generic_params, type_ref, ast_id }; let res = TypeAlias { name, visibility, generic_params, type_ref, ast_id };
Some(res) Some(id(self.tree.type_aliases.alloc(res)))
} }
fn lower_static(&mut self, static_: &ast::StaticDef) -> Option<Static> { fn lower_static(&mut self, static_: &ast::StaticDef) -> Option<FileItemTreeId<Static>> {
let name = static_.name()?.as_name(); let name = static_.name()?.as_name();
let type_ref = self.lower_type_ref_opt(static_.ascribed_type()); let type_ref = self.lower_type_ref_opt(static_.ascribed_type());
let visibility = self.lower_visibility(static_); let visibility = self.lower_visibility(static_);
let ast_id = self.source_ast_id_map.ast_id(static_); let ast_id = self.source_ast_id_map.ast_id(static_);
let res = Static { name, visibility, type_ref, ast_id }; let res = Static { name, visibility, type_ref, ast_id };
Some(res) Some(id(self.tree.statics.alloc(res)))
} }
fn lower_const(&mut self, konst: &ast::ConstDef) -> Const { fn lower_const(&mut self, konst: &ast::ConstDef) -> FileItemTreeId<Const> {
let name = konst.name().map(|it| it.as_name()); let name = konst.name().map(|it| it.as_name());
let type_ref = self.lower_type_ref_opt(konst.ascribed_type()); let type_ref = self.lower_type_ref_opt(konst.ascribed_type());
let visibility = self.lower_visibility(konst); let visibility = self.lower_visibility(konst);
let ast_id = self.source_ast_id_map.ast_id(konst); let ast_id = self.source_ast_id_map.ast_id(konst);
Const { name, visibility, type_ref, ast_id } let res = Const { name, visibility, type_ref, ast_id };
id(self.tree.consts.alloc(res))
} }
fn lower_module(&mut self, module: &ast::Module) -> Option<Mod> { fn lower_module(&mut self, module: &ast::Module) -> Option<FileItemTreeId<Mod>> {
let name = module.name()?.as_name(); let name = module.name()?.as_name();
let visibility = self.lower_visibility(module); let visibility = self.lower_visibility(module);
let kind = if module.semicolon_token().is_some() { let kind = if module.semicolon_token().is_some() {
@ -338,7 +355,7 @@ impl Ctx {
.item_list() .item_list()
.map(|list| { .map(|list| {
list.items() list.items()
.flat_map(|item| self.lower_mod_item(&item)) .flat_map(|item| self.lower_mod_item(&item, false))
.flat_map(|items| items.0) .flat_map(|items| items.0)
.collect() .collect()
}) })
@ -349,90 +366,101 @@ impl Ctx {
} }
}; };
let ast_id = self.source_ast_id_map.ast_id(module); let ast_id = self.source_ast_id_map.ast_id(module);
Some(Mod { name, visibility, kind, ast_id }) let res = Mod { name, visibility, kind, ast_id };
Some(id(self.tree.mods.alloc(res)))
} }
fn lower_trait(&mut self, trait_def: &ast::TraitDef) -> Option<Trait> { fn lower_trait(&mut self, trait_def: &ast::TraitDef) -> Option<FileItemTreeId<Trait>> {
let name = trait_def.name()?.as_name(); let name = trait_def.name()?.as_name();
let visibility = self.lower_visibility(trait_def); let visibility = self.lower_visibility(trait_def);
let generic_params = self.lower_generic_params(GenericsOwner::Trait(trait_def), trait_def); let generic_params = self.lower_generic_params(GenericsOwner::Trait(trait_def), trait_def);
let auto = trait_def.auto_token().is_some(); let auto = trait_def.auto_token().is_some();
let items = trait_def.item_list().map(|list| { let items = trait_def.item_list().map(|list| {
// FIXME: Does not handle macros list.items()
list.assoc_items().flat_map(|item| self.lower_assoc_item(&item)).collect() .flat_map(|item| {
self.collect_inner_items(item.syntax());
self.lower_assoc_item(&item)
})
.collect()
}); });
let ast_id = self.source_ast_id_map.ast_id(trait_def); let ast_id = self.source_ast_id_map.ast_id(trait_def);
Some(Trait { let res = Trait {
name, name,
visibility, visibility,
generic_params, generic_params,
auto, auto,
items: items.unwrap_or_default(), items: items.unwrap_or_default(),
ast_id, ast_id,
}) };
Some(id(self.tree.traits.alloc(res)))
} }
fn lower_impl(&mut self, impl_def: &ast::ImplDef) -> Option<Impl> { fn lower_impl(&mut self, impl_def: &ast::ImplDef) -> Option<FileItemTreeId<Impl>> {
let generic_params = self.lower_generic_params(GenericsOwner::Impl, impl_def); let generic_params = self.lower_generic_params(GenericsOwner::Impl, impl_def);
let target_trait = impl_def.target_trait().map(|tr| self.lower_type_ref(&tr)); 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 target_type = self.lower_type_ref(&impl_def.target_type()?);
let is_negative = impl_def.excl_token().is_some(); 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 let items = impl_def
.item_list()? .item_list()?
.assoc_items() .items()
.filter_map(|item| self.lower_assoc_item(&item)) .filter_map(|item| {
self.collect_inner_items(item.syntax());
let assoc = self.lower_assoc_item(&item)?;
Some(assoc)
})
.collect(); .collect();
let ast_id = self.source_ast_id_map.ast_id(impl_def); let ast_id = self.source_ast_id_map.ast_id(impl_def);
Some(Impl { generic_params, target_trait, target_type, is_negative, items, ast_id }) let res = Impl { generic_params, target_trait, target_type, is_negative, items, ast_id };
Some(id(self.tree.impls.alloc(res)))
} }
fn lower_use(&mut self, use_item: &ast::UseItem) -> Vec<Import> { fn lower_use(&mut self, use_item: &ast::UseItem) -> Vec<FileItemTreeId<Import>> {
// FIXME: cfg_attr // FIXME: cfg_attr
let is_prelude = use_item.has_atom_attr("prelude_import"); let is_prelude = use_item.has_atom_attr("prelude_import");
let visibility = self.lower_visibility(use_item); 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. // Every use item can expand to many `Import`s.
let mut imports = Vec::new(); let mut imports = Vec::new();
let tree = &mut self.tree;
ModPath::expand_use_item( ModPath::expand_use_item(
InFile::new(self.file, use_item.clone()), InFile::new(self.file, use_item.clone()),
&self.hygiene, &self.hygiene,
|path, _tree, is_glob, alias| { |path, _tree, is_glob, alias| {
imports.push(Import { imports.push(id(tree.imports.alloc(Import {
path, path,
alias, alias,
visibility: visibility.clone(), visibility: visibility.clone(),
is_glob, is_glob,
is_prelude, is_prelude,
is_extern_crate: false, ast_id,
is_macro_use: false, })));
});
}, },
); );
imports imports
} }
fn lower_extern_crate(&mut self, extern_crate: &ast::ExternCrateItem) -> Option<Import> { fn lower_extern_crate(
&mut self,
extern_crate: &ast::ExternCrateItem,
) -> Option<FileItemTreeId<ExternCrate>> {
let path = ModPath::from_name_ref(&extern_crate.name_ref()?); let path = ModPath::from_name_ref(&extern_crate.name_ref()?);
let alias = extern_crate.alias().map(|a| { let alias = extern_crate.alias().map(|a| {
a.name().map(|it| it.as_name()).map_or(ImportAlias::Underscore, ImportAlias::Alias) a.name().map(|it| it.as_name()).map_or(ImportAlias::Underscore, ImportAlias::Alias)
}); });
let visibility = self.lower_visibility(extern_crate); let visibility = self.lower_visibility(extern_crate);
let ast_id = self.source_ast_id_map.ast_id(extern_crate);
// FIXME: cfg_attr // FIXME: cfg_attr
let is_macro_use = extern_crate.has_atom_attr("macro_use"); let is_macro_use = extern_crate.has_atom_attr("macro_use");
Some(Import { let res = ExternCrate { path, alias, visibility, is_macro_use, ast_id };
path, Some(id(self.tree.extern_crates.alloc(res)))
alias,
visibility,
is_glob: false,
is_prelude: false,
is_extern_crate: true,
is_macro_use,
})
} }
fn lower_macro_call(&mut self, m: &ast::MacroCall) -> Option<MacroCall> { fn lower_macro_call(&mut self, m: &ast::MacroCall) -> Option<FileItemTreeId<MacroCall>> {
let name = m.name().map(|it| it.as_name()); let name = m.name().map(|it| it.as_name());
let attrs = Attrs::new(m, &self.hygiene); let attrs = Attrs::new(m, &self.hygiene);
let path = ModPath::from_src(m.path()?, &self.hygiene)?; let path = ModPath::from_src(m.path()?, &self.hygiene)?;
@ -455,15 +483,26 @@ impl Ctx {
}; };
let is_builtin = attrs.by_key("rustc_builtin_macro").exists(); let is_builtin = attrs.by_key("rustc_builtin_macro").exists();
Some(MacroCall { name, path, is_export, is_builtin, is_local_inner, ast_id }) let res = MacroCall { name, path, is_export, is_builtin, is_local_inner, ast_id };
Some(id(self.tree.macro_calls.alloc(res)))
} }
fn lower_extern_block(&mut self, block: &ast::ExternBlock) -> Vec<Either<Function, Static>> { fn lower_extern_block(&mut self, block: &ast::ExternBlock) -> Vec<ModItem> {
block.extern_item_list().map_or(Vec::new(), |list| { block.extern_item_list().map_or(Vec::new(), |list| {
list.extern_items() list.extern_items()
.filter_map(|item| match item { .filter_map(|item| {
ast::ExternItem::FnDef(ast) => self.lower_function(&ast).map(Either::Left), self.collect_inner_items(item.syntax());
ast::ExternItem::StaticDef(ast) => self.lower_static(&ast).map(Either::Right), 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()
}
};
Some(id)
}) })
.collect() .collect()
}) })

View file

@ -0,0 +1,254 @@
use super::{ItemTree, ModItem, ModKind};
use crate::{db::DefDatabase, test_db::TestDB};
use hir_expand::db::AstDatabase;
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 tree = db.item_tree(file_id.into());
let root = db.parse_or_expand(file_id.into()).unwrap();
let ast_id_map = db.ast_id_map(file_id.into());
// 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, it).into(),
ModItem::ExternCrate(it) => tree.source(&db, it).into(),
ModItem::Function(it) => tree.source(&db, it).into(),
ModItem::Struct(it) => tree.source(&db, it).into(),
ModItem::Union(it) => tree.source(&db, it).into(),
ModItem::Enum(it) => tree.source(&db, it).into(),
ModItem::Const(it) => tree.source(&db, it).into(),
ModItem::Static(it) => tree.source(&db, it).into(),
ModItem::TypeAlias(it) => tree.source(&db, it).into(),
ModItem::Mod(it) => {
if let ModKind::Inline { items } = &tree[it].kind {
worklist.extend(items);
}
tree.source(&db, it).into()
}
ModItem::Trait(it) => {
worklist.extend(tree[it].items.iter().map(|item| ModItem::from(*item)));
tree.source(&db, it).into()
}
ModItem::Impl(it) => {
worklist.extend(tree[it].items.iter().map(|item| ModItem::from(*item)));
tree.source(&db, 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");
for (ast_id, items) in &tree.inner_items {
format_to!(out, "{:?}:\n", ast_id);
for inner in items {
format_to!(out, "- ");
fmt_mod_item(&mut out, &tree, *inner);
format_to!(out, "\n");
}
}
}
out
}
fn fmt_mod_item(out: &mut String, tree: &ItemTree, item: ModItem) {
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]);
}
ModItem::Impl(it) => {
format_to!(out, "{:?}", tree[it]);
}
ModItem::TypeAlias(it) => {
format_to!(out, "{:?}", tree[it]);
}
ModItem::Mod(it) => {
format_to!(out, "{:?}", tree[it]);
}
ModItem::MacroCall(it) => {
format_to!(out, "{:?}", tree[it]);
}
}
}
#[test]
fn smoke() {
assert_snapshot!(print_item_tree(r"
#![attr]
use {a, b::*};
extern crate krate;
trait Tr<U> {
type AssocTy: Tr<()>;
const CONST: u8;
fn method(&self);
fn dfl_method(&mut self) {}
}
struct Struct0<T = ()>;
struct Struct1<T>(u8);
struct Struct2<T> {
fld: (T, ),
}
enum En {
Variant {
field: u8,
},
}
union Un {
fld: u16,
}
"), @r###"
inner attrs: Attrs { entries: Some([Attr { path: ModPath { kind: Plain, segments: [Name(Text("attr"))] }, input: None }]) }
top-level items:
Import { path: ModPath { kind: Plain, segments: [Name(Text("a"))] }, alias: None, visibility: Module(ModPath { kind: Super(0), segments: [] }), is_glob: false, is_prelude: false, ast_id: FileAstId::<ra_syntax::ast::generated::nodes::UseItem>(0) }
Import { path: ModPath { kind: Plain, segments: [Name(Text("b"))] }, alias: None, visibility: Module(ModPath { kind: Super(0), segments: [] }), is_glob: true, is_prelude: false, ast_id: FileAstId::<ra_syntax::ast::generated::nodes::UseItem>(0) }
ExternCrate { path: ModPath { kind: Plain, segments: [Name(Text("krate"))] }, alias: None, visibility: Module(ModPath { kind: Super(0), segments: [] }), is_macro_use: false, ast_id: FileAstId::<ra_syntax::ast::generated::nodes::ExternCrateItem>(1) }
Trait { name: Name(Text("Tr")), visibility: Module(ModPath { kind: Super(0), segments: [] }), generic_params: GenericParams { types: Arena { len: 2, data: [TypeParamData { name: Some(Name(Text("Self"))), default: None, provenance: TraitSelf }, TypeParamData { name: Some(Name(Text("U"))), default: None, provenance: TypeParamList }] }, where_predicates: [] }, 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) }
Struct { name: Name(Text("Struct0")), attrs: Attrs { entries: None }, visibility: Module(ModPath { kind: Super(0), segments: [] }), generic_params: GenericParams { types: Arena { len: 1, data: [TypeParamData { name: Some(Name(Text("T"))), default: Some(Tuple([])), provenance: TypeParamList }] }, where_predicates: [] }, fields: Unit, ast_id: FileAstId::<ra_syntax::ast::generated::nodes::StructDef>(3), kind: Unit }
Struct { name: Name(Text("Struct1")), attrs: Attrs { entries: None }, visibility: Module(ModPath { kind: Super(0), segments: [] }), generic_params: GenericParams { types: Arena { len: 1, data: [TypeParamData { name: Some(Name(Text("T"))), default: None, provenance: TypeParamList }] }, where_predicates: [] }, fields: Tuple(Idx::<Field>(0)..Idx::<Field>(1)), ast_id: FileAstId::<ra_syntax::ast::generated::nodes::StructDef>(4), kind: Tuple }
Struct { name: Name(Text("Struct2")), attrs: Attrs { entries: None }, visibility: Module(ModPath { kind: Super(0), segments: [] }), generic_params: GenericParams { types: Arena { len: 1, data: [TypeParamData { name: Some(Name(Text("T"))), default: None, provenance: TypeParamList }] }, where_predicates: [] }, fields: Record(Idx::<Field>(1)..Idx::<Field>(2)), ast_id: FileAstId::<ra_syntax::ast::generated::nodes::StructDef>(5), kind: Record }
Enum { name: Name(Text("En")), attrs: Attrs { entries: None }, visibility: Module(ModPath { kind: Super(0), segments: [] }), generic_params: GenericParams { types: Arena { len: 0, data: [] }, where_predicates: [] }, variants: Idx::<Variant>(0)..Idx::<Variant>(1), ast_id: FileAstId::<ra_syntax::ast::generated::nodes::EnumDef>(6) }
Union { name: Name(Text("Un")), attrs: Attrs { entries: None }, visibility: Module(ModPath { kind: Super(0), segments: [] }), generic_params: GenericParams { types: Arena { len: 0, data: [] }, where_predicates: [] }, 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: GenericParams { types: Arena { len: 0, data: [] }, where_predicates: [] }, 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) }
inner items:
FileAstId::<ra_syntax::ast::generated::nodes::ModuleItem>(2):
- Function { name: Name(Text("end")), attrs: Attrs { entries: None }, visibility: Module(ModPath { kind: Super(0), segments: [] }), generic_params: GenericParams { types: Arena { len: 1, data: [TypeParamData { name: Some(Name(Text("W"))), default: None, provenance: TypeParamList }] }, where_predicates: [WherePredicate { target: TypeRef(Path(Path { type_anchor: None, mod_path: ModPath { kind: Plain, segments: [Name(Text("W"))] }, generic_args: [None] })), bound: Path(Path { type_anchor: None, mod_path: ModPath { kind: Plain, segments: [Name(Text("Write"))] }, generic_args: [None] }) }] }, has_self_param: 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 {}
struct InnerStruct {}
impl InnerTrait for InnerStruct {}
}
}
",
);
}
#[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: GenericParams { types: Arena { len: 0, data: [] }, where_predicates: [] }, 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) }
"###);
}

View file

@ -50,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,
@ -58,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 {
@ -72,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 {
@ -106,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
@ -143,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)]
@ -367,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)
} }
@ -394,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

@ -20,9 +20,7 @@ use test_utils::mark;
use crate::{ use crate::{
attr::Attrs, attr::Attrs,
db::DefDatabase, db::DefDatabase,
item_tree::{ item_tree::{self, ItemTree, ItemTreeId, MacroCall, Mod, ModItem, ModKind, StructDefKind},
FileItemTreeId, Import, ItemTree, MacroCall, Mod, ModItem, ModKind, StructDefKind,
},
nameres::{ nameres::{
diagnostics::DefDiagnostic, mod_resolution::ModDir, path_resolution::ReachedFixedPoint, diagnostics::DefDiagnostic, mod_resolution::ModDir, path_resolution::ReachedFixedPoint,
BuiltinShadowMode, CrateDefMap, ModuleData, ModuleOrigin, ResolveMode, BuiltinShadowMode, CrateDefMap, ModuleData, ModuleOrigin, ResolveMode,
@ -105,10 +103,48 @@ 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 From<item_tree::Import> for Import {
fn from(it: item_tree::Import) -> Self {
Self {
path: it.path,
alias: it.alias,
visibility: it.visibility,
is_glob: it.is_glob,
is_prelude: it.is_prelude,
is_extern_crate: false,
is_macro_use: false,
}
}
}
impl From<item_tree::ExternCrate> for Import {
fn from(it: item_tree::ExternCrate) -> Self {
Self {
path: it.path,
alias: it.alias,
visibility: it.visibility,
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: FileItemTreeId<Import>,
import: Import, import: Import,
status: PartialResolvedImport, status: PartialResolvedImport,
} }
@ -297,7 +333,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: &Import, import: &item_tree::ExternCrate,
) { ) {
log::debug!( log::debug!(
"importing macros from extern crate: {:?} ({:?})", "importing macros from extern crate: {:?} ({:?})",
@ -703,9 +739,9 @@ impl ModCollector<'_, '_> {
// any other items. // any other items.
for item in items { for item in items {
if self.is_cfg_enabled(self.item_tree.attrs(*item)) { if self.is_cfg_enabled(self.item_tree.attrs(*item)) {
if let ModItem::Import(import_id) = item { if let ModItem::ExternCrate(id) = item {
let import = self.item_tree[*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);
} }
} }
@ -725,8 +761,14 @@ impl ModCollector<'_, '_> {
ModItem::Import(import_id) => { 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: self.item_tree[import_id].clone().into(),
import: self.item_tree[import_id].clone(), status: PartialResolvedImport::Unresolved,
})
}
ModItem::ExternCrate(import_id) => {
self.def_collector.unresolved_imports.push(ImportDirective {
module_id: self.module_id,
import: self.item_tree[import_id].clone().into(),
status: PartialResolvedImport::Unresolved, status: PartialResolvedImport::Unresolved,
}) })
} }
@ -737,30 +779,28 @@ impl ModCollector<'_, '_> {
local_id: self.module_id, local_id: self.module_id,
}; };
let container = ContainerId::ModuleId(module); let container = ContainerId::ModuleId(module);
let ast_id = self.item_tree[imp].ast_id; let impl_id = ImplLoc { container, id: ItemTreeId::new(self.file_id, imp) }
let impl_id =
ImplLoc { container, ast_id: AstId::new(self.file_id, ast_id) }
.intern(self.def_collector.db); .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(it) => { ModItem::Function(id) => {
let it = &self.item_tree[it]; let func = &self.item_tree[id];
def = Some(DefData { def = Some(DefData {
id: FunctionLoc { id: FunctionLoc {
container: container.into(), container: container.into(),
ast_id: AstId::new(self.file_id, it.ast_id), id: ItemTreeId::new(self.file_id, id),
} }
.intern(self.def_collector.db) .intern(self.def_collector.db)
.into(), .into(),
name: &it.name, name: &func.name,
visibility: &it.visibility, visibility: &func.visibility,
has_constructor: false, has_constructor: false,
}); });
} }
ModItem::Struct(it) => { ModItem::Struct(id) => {
let it = &self.item_tree[it]; let it = &self.item_tree[id];
// FIXME: check attrs to see if this is an attribute macro invocation; // 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 // in which case we don't add the invocation, just a single attribute
@ -768,10 +808,7 @@ impl ModCollector<'_, '_> {
self.collect_derives(attrs, it.ast_id.upcast()); self.collect_derives(attrs, it.ast_id.upcast());
def = Some(DefData { def = Some(DefData {
id: StructLoc { id: StructLoc { container, id: ItemTreeId::new(self.file_id, id) }
container,
ast_id: AstId::new(self.file_id, it.ast_id),
}
.intern(self.def_collector.db) .intern(self.def_collector.db)
.into(), .into(),
name: &it.name, name: &it.name,
@ -779,8 +816,8 @@ impl ModCollector<'_, '_> {
has_constructor: it.kind != StructDefKind::Record, has_constructor: it.kind != StructDefKind::Record,
}); });
} }
ModItem::Union(it) => { ModItem::Union(id) => {
let it = &self.item_tree[it]; let it = &self.item_tree[id];
// FIXME: check attrs to see if this is an attribute macro invocation; // 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 // in which case we don't add the invocation, just a single attribute
@ -788,7 +825,7 @@ impl ModCollector<'_, '_> {
self.collect_derives(attrs, it.ast_id.upcast()); self.collect_derives(attrs, it.ast_id.upcast());
def = Some(DefData { def = Some(DefData {
id: UnionLoc { container, ast_id: AstId::new(self.file_id, it.ast_id) } id: UnionLoc { container, id: ItemTreeId::new(self.file_id, id) }
.intern(self.def_collector.db) .intern(self.def_collector.db)
.into(), .into(),
name: &it.name, name: &it.name,
@ -796,8 +833,8 @@ impl ModCollector<'_, '_> {
has_constructor: false, has_constructor: false,
}); });
} }
ModItem::Enum(it) => { ModItem::Enum(id) => {
let it = &self.item_tree[it]; let it = &self.item_tree[id];
// FIXME: check attrs to see if this is an attribute macro invocation; // 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 // in which case we don't add the invocation, just a single attribute
@ -805,7 +842,7 @@ impl ModCollector<'_, '_> {
self.collect_derives(attrs, it.ast_id.upcast()); self.collect_derives(attrs, it.ast_id.upcast());
def = Some(DefData { def = Some(DefData {
id: EnumLoc { container, ast_id: AstId::new(self.file_id, it.ast_id) } id: EnumLoc { container, id: ItemTreeId::new(self.file_id, id) }
.intern(self.def_collector.db) .intern(self.def_collector.db)
.into(), .into(),
name: &it.name, name: &it.name,
@ -813,14 +850,14 @@ impl ModCollector<'_, '_> {
has_constructor: false, has_constructor: false,
}); });
} }
ModItem::Const(it) => { ModItem::Const(id) => {
let it = &self.item_tree[it]; let it = &self.item_tree[id];
if let Some(name) = &it.name { if let Some(name) = &it.name {
def = Some(DefData { def = Some(DefData {
id: ConstLoc { id: ConstLoc {
container: container.into(), container: container.into(),
ast_id: AstId::new(self.file_id, it.ast_id), id: ItemTreeId::new(self.file_id, id),
} }
.intern(self.def_collector.db) .intern(self.def_collector.db)
.into(), .into(),
@ -830,14 +867,11 @@ impl ModCollector<'_, '_> {
}); });
} }
} }
ModItem::Static(it) => { ModItem::Static(id) => {
let it = &self.item_tree[it]; let it = &self.item_tree[id];
def = Some(DefData { def = Some(DefData {
id: StaticLoc { id: StaticLoc { container, id: ItemTreeId::new(self.file_id, id) }
container,
ast_id: AstId::new(self.file_id, it.ast_id),
}
.intern(self.def_collector.db) .intern(self.def_collector.db)
.into(), .into(),
name: &it.name, name: &it.name,
@ -845,11 +879,11 @@ impl ModCollector<'_, '_> {
has_constructor: false, has_constructor: false,
}); });
} }
ModItem::Trait(it) => { ModItem::Trait(id) => {
let it = &self.item_tree[it]; let it = &self.item_tree[id];
def = Some(DefData { def = Some(DefData {
id: TraitLoc { container, ast_id: AstId::new(self.file_id, it.ast_id) } id: TraitLoc { container, id: ItemTreeId::new(self.file_id, id) }
.intern(self.def_collector.db) .intern(self.def_collector.db)
.into(), .into(),
name: &it.name, name: &it.name,
@ -857,13 +891,13 @@ impl ModCollector<'_, '_> {
has_constructor: false, has_constructor: false,
}); });
} }
ModItem::TypeAlias(it) => { ModItem::TypeAlias(id) => {
let it = &self.item_tree[it]; let it = &self.item_tree[id];
def = Some(DefData { def = Some(DefData {
id: TypeAliasLoc { id: TypeAliasLoc {
container: container.into(), container: container.into(),
ast_id: AstId::new(self.file_id, it.ast_id), id: ItemTreeId::new(self.file_id, id),
} }
.intern(self.def_collector.db) .intern(self.def_collector.db)
.into(), .into(),

View file

@ -722,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::ItemTreeSource, 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: ItemTreeSource> 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: ItemTreeSource> 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,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.value).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.value).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.value).syntax().text_range().start()
} }
}); });
for def in defs { for def in defs {