mirror of
https://github.com/rust-lang/rust-analyzer
synced 2024-12-26 13:03:31 +00:00
draw the rest of the owl
This commit is contained in:
parent
b94caeb88b
commit
4b03b39d5b
13 changed files with 959 additions and 415 deletions
|
@ -31,10 +31,7 @@ use hir_ty::{
|
|||
};
|
||||
use ra_db::{CrateId, CrateName, Edition, FileId};
|
||||
use ra_prof::profile;
|
||||
use ra_syntax::{
|
||||
ast::{self, AttrsOwner, NameOwner},
|
||||
AstNode,
|
||||
};
|
||||
use ra_syntax::ast::{self, AttrsOwner, NameOwner};
|
||||
use rustc_hash::FxHashSet;
|
||||
|
||||
use crate::{
|
||||
|
@ -205,7 +202,8 @@ impl ModuleDef {
|
|||
}
|
||||
|
||||
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 {
|
||||
|
@ -872,7 +870,7 @@ where
|
|||
ID: Lookup<Data = AssocItemLoc<AST>>,
|
||||
DEF: From<ID>,
|
||||
CTOR: FnOnce(DEF) -> AssocItem,
|
||||
AST: AstNode,
|
||||
AST: ItemTreeNode,
|
||||
{
|
||||
match id.lookup(db.upcast()).container {
|
||||
AssocContainerId::TraitId(_) | AssocContainerId::ImplId(_) => Some(ctor(DEF::from(id))),
|
||||
|
|
|
@ -243,7 +243,7 @@ impl Body {
|
|||
}
|
||||
};
|
||||
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))
|
||||
}
|
||||
|
||||
|
@ -253,12 +253,13 @@ impl Body {
|
|||
|
||||
fn new(
|
||||
db: &dyn DefDatabase,
|
||||
file_id: HirFileId,
|
||||
def: DefWithBodyId,
|
||||
expander: Expander,
|
||||
params: Option<ast::ParamList>,
|
||||
body: Option<ast::Expr>,
|
||||
) -> (Body, BodySourceMap) {
|
||||
lower::lower(db, def, expander, params, body)
|
||||
lower::lower(db, file_id, def, expander, params, body)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -5,7 +5,7 @@ use either::Either;
|
|||
use hir_expand::{
|
||||
hygiene::Hygiene,
|
||||
name::{name, AsName, Name},
|
||||
HirFileId, MacroDefId, MacroDefKind,
|
||||
AstId, HirFileId, MacroDefId, MacroDefKind,
|
||||
};
|
||||
use ra_arena::Arena;
|
||||
use ra_syntax::{
|
||||
|
@ -27,6 +27,7 @@ use crate::{
|
|||
LogicOp, MatchArm, Ordering, Pat, PatId, RecordFieldPat, RecordLitField, Statement,
|
||||
},
|
||||
item_scope::BuiltinShadowMode,
|
||||
item_tree::{FileItemTreeId, ItemTree, ItemTreeSource},
|
||||
path::{GenericArgs, Path},
|
||||
type_ref::{Mutability, Rawness, TypeRef},
|
||||
AdtId, ConstLoc, ContainerId, DefWithBodyId, EnumLoc, FunctionLoc, Intern, ModuleDefId,
|
||||
|
@ -35,6 +36,7 @@ use crate::{
|
|||
|
||||
use super::{ExprSource, PatSource};
|
||||
use ast::AstChildren;
|
||||
use std::sync::Arc;
|
||||
|
||||
pub(crate) struct LowerCtx {
|
||||
hygiene: Hygiene,
|
||||
|
@ -55,11 +57,13 @@ impl LowerCtx {
|
|||
|
||||
pub(super) fn lower(
|
||||
db: &dyn DefDatabase,
|
||||
file_id: HirFileId,
|
||||
def: DefWithBodyId,
|
||||
expander: Expander,
|
||||
params: Option<ast::ParamList>,
|
||||
body: Option<ast::Expr>,
|
||||
) -> (Body, BodySourceMap) {
|
||||
let item_tree = db.item_tree(file_id);
|
||||
ExprCollector {
|
||||
db,
|
||||
def,
|
||||
|
@ -72,6 +76,7 @@ pub(super) fn lower(
|
|||
body_expr: dummy_expr_id(),
|
||||
item_scope: Default::default(),
|
||||
},
|
||||
item_trees: vec![(file_id, item_tree)],
|
||||
}
|
||||
.collect(params, body)
|
||||
}
|
||||
|
@ -82,6 +87,8 @@ struct ExprCollector<'a> {
|
|||
expander: Expander,
|
||||
body: Body,
|
||||
source_map: BodySourceMap,
|
||||
|
||||
item_trees: Vec<(HirFileId, Arc<ItemTree>)>,
|
||||
}
|
||||
|
||||
impl ExprCollector<'_> {
|
||||
|
@ -533,6 +540,9 @@ impl ExprCollector<'_> {
|
|||
self.source_map
|
||||
.expansions
|
||||
.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);
|
||||
self.expander.exit(self.db, mark);
|
||||
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 {
|
||||
if let Some(expr) = expr {
|
||||
self.collect_expr(expr)
|
||||
|
@ -578,56 +603,102 @@ impl ExprCollector<'_> {
|
|||
|
||||
fn collect_block_items(&mut self, block: &ast::BlockExpr) {
|
||||
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 {
|
||||
ast::ModuleItem::FnDef(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(),
|
||||
)
|
||||
}
|
||||
ast::ModuleItem::TypeAliasDef(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(),
|
||||
)
|
||||
}
|
||||
ast::ModuleItem::ConstDef(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(),
|
||||
)
|
||||
}
|
||||
ast::ModuleItem::StaticDef(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) => {
|
||||
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) => {
|
||||
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) => {
|
||||
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) => {
|
||||
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::UseItem(_)
|
||||
| ast::ModuleItem::ExternCrateItem(_)
|
||||
| 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);
|
||||
if let Some(name) = name {
|
||||
let vis = crate::visibility::Visibility::Public; // FIXME determine correctly
|
||||
|
|
|
@ -5,24 +5,23 @@ use std::sync::Arc;
|
|||
use hir_expand::{
|
||||
hygiene::Hygiene,
|
||||
name::{name, AsName, Name},
|
||||
AstId, InFile,
|
||||
InFile,
|
||||
};
|
||||
use ra_prof::profile;
|
||||
use ra_syntax::ast::{
|
||||
self, AssocItem, AstNode, ModuleItemOwner, NameOwner, TypeAscriptionOwner, TypeBoundsOwner,
|
||||
VisibilityOwner,
|
||||
};
|
||||
use ra_syntax::ast::{self, NameOwner, TypeAscriptionOwner, TypeBoundsOwner, VisibilityOwner};
|
||||
|
||||
use crate::{
|
||||
attr::Attrs,
|
||||
body::Expander,
|
||||
body::LowerCtx,
|
||||
db::DefDatabase,
|
||||
item_tree::{AssocItem, ItemTreeId, ModItem},
|
||||
path::{path, AssociatedTypeBinding, GenericArgs, Path},
|
||||
src::HasSource,
|
||||
type_ref::{Mutability, TypeBound, TypeRef},
|
||||
visibility::RawVisibility,
|
||||
AssocContainerId, AssocItemId, ConstId, ConstLoc, Expander, FunctionId, FunctionLoc, HasModule,
|
||||
ImplId, Intern, Lookup, StaticId, TraitId, TypeAliasId, TypeAliasLoc,
|
||||
AssocContainerId, AssocItemId, ConstId, ConstLoc, FunctionId, FunctionLoc, HasModule, ImplId,
|
||||
Intern, Lookup, ModuleId, StaticId, TraitId, TypeAliasId, TypeAliasLoc,
|
||||
};
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
|
@ -155,30 +154,24 @@ pub struct TraitData {
|
|||
impl TraitData {
|
||||
pub(crate) fn trait_data_query(db: &dyn DefDatabase, tr: TraitId) -> Arc<TraitData> {
|
||||
let tr_loc = tr.lookup(db);
|
||||
let src = tr_loc.source(db);
|
||||
let name = src.value.name().map_or_else(Name::missing, |n| n.as_name());
|
||||
let auto = src.value.auto_token().is_some();
|
||||
let item_tree = db.item_tree(tr_loc.id.file_id);
|
||||
let tr_def = &item_tree[tr_loc.id.value];
|
||||
let name = tr_def.name.clone();
|
||||
let auto = tr_def.auto;
|
||||
let module_id = tr_loc.container.module(db);
|
||||
|
||||
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 mut expander = Expander::new(db, tr_loc.ast_id.file_id, module_id);
|
||||
items.extend(collect_items(
|
||||
let items = collect_items(
|
||||
db,
|
||||
module_id,
|
||||
&mut expander,
|
||||
item_list.assoc_items(),
|
||||
src.file_id,
|
||||
tr_def.items.iter().copied(),
|
||||
tr_loc.id.file_id,
|
||||
container,
|
||||
));
|
||||
items.extend(collect_items_in_macros(
|
||||
db,
|
||||
&mut expander,
|
||||
&src.with_value(item_list),
|
||||
container,
|
||||
));
|
||||
}
|
||||
100,
|
||||
);
|
||||
|
||||
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> {
|
||||
let _p = profile("impl_data_query");
|
||||
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 target_type = TypeRef::from_ast_opt(&lower_ctx, src.value.target_type());
|
||||
let is_negative = src.value.excl_token().is_some();
|
||||
let item_tree = db.item_tree(impl_loc.id.file_id);
|
||||
let impl_def = &item_tree[impl_loc.id.value];
|
||||
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 container = AssocContainerId::ImplId(id);
|
||||
let mut expander = Expander::new(db, impl_loc.id.file_id, module_id);
|
||||
|
||||
let mut items: Vec<AssocItemId> = Vec::new();
|
||||
|
||||
if let Some(item_list) = src.value.item_list() {
|
||||
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),
|
||||
let items = collect_items(
|
||||
db,
|
||||
module_id,
|
||||
&mut expander,
|
||||
impl_def.items.iter().copied(),
|
||||
impl_loc.id.file_id,
|
||||
container,
|
||||
100,
|
||||
);
|
||||
items.extend(
|
||||
collect_items_in_macros(db, &mut expander, &src.with_value(item_list), container)
|
||||
.into_iter()
|
||||
.map(|(_, item)| item),
|
||||
);
|
||||
}
|
||||
let items = items.into_iter().map(|(_, item)| item).collect();
|
||||
|
||||
let res = ImplData { target_trait, target_type, items, is_negative };
|
||||
Arc::new(res)
|
||||
Arc::new(ImplData { target_trait, target_type, items, is_negative })
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -295,28 +283,12 @@ impl StaticData {
|
|||
}
|
||||
}
|
||||
|
||||
fn collect_items_in_macros(
|
||||
fn collect_items(
|
||||
db: &dyn DefDatabase,
|
||||
module: ModuleId,
|
||||
expander: &mut Expander,
|
||||
impl_def: &InFile<ast::ItemList>,
|
||||
container: AssocContainerId,
|
||||
) -> 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,
|
||||
assoc_items: impl Iterator<Item = AssocItem>,
|
||||
file_id: crate::HirFileId,
|
||||
container: AssocContainerId,
|
||||
limit: usize,
|
||||
) -> Vec<(Name, AssocItemId)> {
|
||||
|
@ -324,62 +296,62 @@ fn collect_items_in_macro(
|
|||
return Vec::new();
|
||||
}
|
||||
|
||||
if let Some((mark, items)) = expander.enter_expand(db, None, m) {
|
||||
let items: InFile<ast::MacroItems> = expander.to_source(items);
|
||||
let mut res = collect_items(
|
||||
db,
|
||||
expander,
|
||||
items.value.items().filter_map(|it| AssocItem::cast(it.syntax().clone())),
|
||||
items.file_id,
|
||||
container,
|
||||
);
|
||||
let item_tree = db.item_tree(file_id);
|
||||
let cfg_options = db.crate_graph()[module.krate].cfg_options.clone();
|
||||
|
||||
// Recursive collect macros
|
||||
// Note that ast::ModuleItem do not include ast::MacroCall
|
||||
// We cannot use ModuleItemOwner::items here
|
||||
for it in items.value.syntax().children().filter_map(ast::MacroCall::cast) {
|
||||
res.extend(collect_items_in_macro(db, expander, it, container, limit - 1))
|
||||
let mut items = Vec::new();
|
||||
for item in assoc_items {
|
||||
match item {
|
||||
AssocItem::Function(id) => {
|
||||
let item = &item_tree[id];
|
||||
if !item.attrs.is_cfg_enabled(&cfg_options) {
|
||||
continue;
|
||||
}
|
||||
expander.exit(db, mark);
|
||||
res
|
||||
let def = FunctionLoc { container, id: ItemTreeId::new(file_id, id) }.intern(db);
|
||||
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 {
|
||||
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(
|
||||
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);
|
||||
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,
|
||||
));
|
||||
|
||||
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;
|
||||
expander.exit(db, mark);
|
||||
}
|
||||
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
|
||||
}
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
//! A simplified AST that only contains items.
|
||||
|
||||
mod lower;
|
||||
#[cfg(test)]
|
||||
mod tests;
|
||||
|
||||
use std::{
|
||||
fmt::{self, Debug},
|
||||
|
@ -31,16 +33,20 @@ use crate::{
|
|||
type_ref::{Mutability, TypeBound, TypeRef},
|
||||
visibility::RawVisibility,
|
||||
};
|
||||
use smallvec::SmallVec;
|
||||
|
||||
/// The item tree of a source file.
|
||||
#[derive(Debug, Default, Eq, PartialEq)]
|
||||
#[derive(Debug, Eq, PartialEq)]
|
||||
pub struct ItemTree {
|
||||
file_id: HirFileId,
|
||||
top_level: Vec<ModItem>,
|
||||
top_attrs: Attrs,
|
||||
attrs: FxHashMap<ModItem, Attrs>,
|
||||
empty_attrs: Attrs,
|
||||
inner_items: FxHashMap<FileAstId<ast::ModuleItem>, SmallVec<[ModItem; 1]>>,
|
||||
|
||||
imports: Arena<Import>,
|
||||
extern_crates: Arena<ExternCrate>,
|
||||
functions: Arena<Function>,
|
||||
structs: Arena<Struct>,
|
||||
fields: Arena<Field>,
|
||||
|
@ -63,7 +69,7 @@ impl ItemTree {
|
|||
let syntax = if let Some(node) = db.parse_or_expand(file_id) {
|
||||
node
|
||||
} else {
|
||||
return Default::default();
|
||||
return Arc::new(Self::empty(file_id));
|
||||
};
|
||||
|
||||
let hygiene = Hygiene::new(db.upcast(), file_id);
|
||||
|
@ -80,20 +86,41 @@ impl ItemTree {
|
|||
file_storage = file;
|
||||
&file_storage
|
||||
},
|
||||
_ => return Default::default(),
|
||||
_ => return Arc::new(Self::empty(file_id)),
|
||||
}
|
||||
};
|
||||
|
||||
let map = db.ast_id_map(file_id);
|
||||
let mut ctx = lower::Ctx {
|
||||
tree: ItemTree::default(),
|
||||
hygiene,
|
||||
file: file_id,
|
||||
source_ast_id_map: map,
|
||||
body_ctx: crate::body::LowerCtx::new(db, file_id),
|
||||
};
|
||||
ctx.tree.top_attrs = top_attrs.unwrap_or_default();
|
||||
Arc::new(ctx.lower(item_owner))
|
||||
let ctx = lower::Ctx::new(db, hygiene, file_id);
|
||||
let mut item_tree = ctx.lower(item_owner);
|
||||
item_tree.top_attrs = top_attrs.unwrap_or_default();
|
||||
Arc::new(item_tree)
|
||||
}
|
||||
|
||||
fn empty(file_id: HirFileId) -> Self {
|
||||
Self {
|
||||
file_id,
|
||||
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
|
||||
|
@ -110,17 +137,49 @@ impl ItemTree {
|
|||
pub fn attrs(&self, of: ModItem) -> &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.
|
||||
pub trait ItemTreeNode: Clone {
|
||||
/// 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>>;
|
||||
}
|
||||
|
||||
/// Trait for item tree nodes that allow accessing the original AST node.
|
||||
pub trait ItemTreeSource: ItemTreeNode {
|
||||
type Source: AstNode;
|
||||
type Source: AstNode + Into<ast::ModuleItem>;
|
||||
|
||||
fn ast_id(&self) -> FileAstId<Self::Source>;
|
||||
}
|
||||
|
@ -164,12 +223,22 @@ macro_rules! nodes {
|
|||
fn lookup(tree: &ItemTree, index: Idx<Self>) -> &Self {
|
||||
&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!(
|
||||
Import in imports,
|
||||
ExternCrate in extern_crates,
|
||||
Function in functions,
|
||||
Struct in structs,
|
||||
Union in unions,
|
||||
|
@ -196,6 +265,8 @@ macro_rules! source {
|
|||
}
|
||||
|
||||
source! {
|
||||
Import -> ast::UseItem,
|
||||
ExternCrate -> ast::ExternCrateItem,
|
||||
Function -> ast::FnDef,
|
||||
Struct -> ast::StructDef,
|
||||
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)]
|
||||
pub struct Import {
|
||||
pub path: ModPath,
|
||||
|
@ -256,8 +327,19 @@ pub struct Import {
|
|||
pub visibility: RawVisibility,
|
||||
pub is_glob: 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 ast_id: FileAstId<ast::ExternCrateItem>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Eq, PartialEq)]
|
||||
|
@ -270,7 +352,6 @@ pub struct Function {
|
|||
pub params: Vec<TypeRef>,
|
||||
pub ret_type: TypeRef,
|
||||
pub ast_id: FileAstId<ast::FnDef>,
|
||||
// FIXME inner items
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Eq, PartialEq)]
|
||||
|
@ -412,6 +493,7 @@ macro_rules! impl_froms {
|
|||
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
|
||||
pub enum ModItem {
|
||||
Import(FileItemTreeId<Import>),
|
||||
ExternCrate(FileItemTreeId<ExternCrate>),
|
||||
Function(FileItemTreeId<Function>),
|
||||
Struct(FileItemTreeId<Struct>),
|
||||
Union(FileItemTreeId<Union>),
|
||||
|
@ -429,6 +511,7 @@ impl ModItem {
|
|||
pub fn as_assoc_item(&self) -> Option<AssocItem> {
|
||||
match self {
|
||||
ModItem::Import(_)
|
||||
| ModItem::ExternCrate(_)
|
||||
| ModItem::Struct(_)
|
||||
| ModItem::Union(_)
|
||||
| ModItem::Enum(_)
|
||||
|
@ -442,10 +525,15 @@ impl ModItem {
|
|||
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 {
|
||||
Import(FileItemTreeId<Import>),
|
||||
ExternCrate(FileItemTreeId<ExternCrate>),
|
||||
Function(FileItemTreeId<Function>),
|
||||
Struct(FileItemTreeId<Struct>),
|
||||
Union(FileItemTreeId<Union>),
|
||||
|
@ -474,6 +562,17 @@ impl_froms!(AssocItem {
|
|||
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,
|
||||
|
|
|
@ -7,9 +7,12 @@ use crate::{
|
|||
};
|
||||
use hir_expand::{ast_id_map::AstIdMap, hygiene::Hygiene, HirFileId};
|
||||
use ra_arena::map::ArenaMap;
|
||||
use ra_syntax::ast::{self, ModuleItemOwner};
|
||||
use ra_syntax::{
|
||||
ast::{self, ModuleItemOwner},
|
||||
SyntaxNode,
|
||||
};
|
||||
use smallvec::SmallVec;
|
||||
use std::sync::Arc;
|
||||
use std::{mem, sync::Arc};
|
||||
|
||||
fn id<N: ItemTreeNode>(index: Idx<N>) -> FileItemTreeId<N> {
|
||||
FileItemTreeId { index, _p: PhantomData }
|
||||
|
@ -27,78 +30,81 @@ where
|
|||
}
|
||||
|
||||
pub(super) struct Ctx {
|
||||
pub tree: ItemTree,
|
||||
pub hygiene: Hygiene,
|
||||
pub file: HirFileId,
|
||||
pub source_ast_id_map: Arc<AstIdMap>,
|
||||
pub body_ctx: crate::body::LowerCtx,
|
||||
tree: ItemTree,
|
||||
hygiene: Hygiene,
|
||||
file: HirFileId,
|
||||
source_ast_id_map: Arc<AstIdMap>,
|
||||
body_ctx: crate::body::LowerCtx,
|
||||
inner_items: Vec<ModItem>,
|
||||
}
|
||||
|
||||
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 {
|
||||
self.tree.top_level = item_owner
|
||||
.items()
|
||||
.flat_map(|item| self.lower_mod_item(&item))
|
||||
.flat_map(|item| self.lower_mod_item(&item, false))
|
||||
.flat_map(|items| items.0)
|
||||
.collect();
|
||||
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 items = match item {
|
||||
ast::ModuleItem::StructDef(ast) => {
|
||||
self.lower_struct(ast).map(|data| id(self.tree.structs.alloc(data)).into())
|
||||
}
|
||||
ast::ModuleItem::UnionDef(ast) => {
|
||||
self.lower_union(ast).map(|data| id(self.tree.unions.alloc(data)).into())
|
||||
}
|
||||
ast::ModuleItem::EnumDef(ast) => {
|
||||
self.lower_enum(ast).map(|data| id(self.tree.enums.alloc(data)).into())
|
||||
}
|
||||
ast::ModuleItem::FnDef(ast) => {
|
||||
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::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(|data| id(self.tree.imports.alloc(data)).into())
|
||||
.collect::<SmallVec<_>>(),
|
||||
self.lower_use(ast).into_iter().map(Into::into).collect::<SmallVec<_>>(),
|
||||
)),
|
||||
ast::ModuleItem::ExternCrateItem(ast) => {
|
||||
self.lower_extern_crate(ast).map(|data| id(self.tree.imports.alloc(data)).into())
|
||||
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<_>>()))
|
||||
}
|
||||
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() {
|
||||
|
@ -110,22 +116,28 @@ impl Ctx {
|
|||
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 {
|
||||
ast::AssocItem::FnDef(ast) => {
|
||||
self.lower_function(ast).map(|data| id(self.tree.functions.alloc(data)).into())
|
||||
}
|
||||
ast::AssocItem::TypeAliasDef(ast) => {
|
||||
self.lower_type_alias(ast).map(|data| id(self.tree.type_aliases.alloc(data)).into())
|
||||
}
|
||||
ast::AssocItem::ConstDef(ast) => {
|
||||
let data = self.lower_const(ast);
|
||||
Some(id(self.tree.consts.alloc(data)).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::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<Struct> {
|
||||
fn lower_struct(&mut self, strukt: &ast::StructDef) -> Option<FileItemTreeId<Struct>> {
|
||||
let attrs = self.lower_attrs(strukt);
|
||||
let visibility = self.lower_visibility(strukt);
|
||||
let name = strukt.name()?.as_name();
|
||||
|
@ -138,7 +150,7 @@ impl Ctx {
|
|||
ast::StructKind::Unit => StructDefKind::Unit,
|
||||
};
|
||||
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 {
|
||||
|
@ -193,7 +205,7 @@ impl Ctx {
|
|||
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 visibility = self.lower_visibility(union);
|
||||
let name = union.name()?.as_name();
|
||||
|
@ -206,10 +218,10 @@ impl Ctx {
|
|||
};
|
||||
let ast_id = self.source_ast_id_map.ast_id(union);
|
||||
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 visibility = self.lower_visibility(enum_);
|
||||
let name = enum_.name()?.as_name();
|
||||
|
@ -220,7 +232,7 @@ impl Ctx {
|
|||
};
|
||||
let ast_id = self.source_ast_id_map.ast_id(enum_);
|
||||
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>> {
|
||||
|
@ -241,7 +253,7 @@ impl Ctx {
|
|||
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 visibility = self.lower_visibility(func);
|
||||
let name = func.name()?.as_name();
|
||||
|
@ -297,37 +309,42 @@ impl Ctx {
|
|||
ast_id,
|
||||
};
|
||||
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 type_ref = type_alias.type_ref().map(|it| self.lower_type_ref(&it));
|
||||
let visibility = self.lower_visibility(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, 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 type_ref = self.lower_type_ref_opt(static_.ascribed_type());
|
||||
let visibility = self.lower_visibility(static_);
|
||||
let ast_id = self.source_ast_id_map.ast_id(static_);
|
||||
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 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);
|
||||
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 visibility = self.lower_visibility(module);
|
||||
let kind = if module.semicolon_token().is_some() {
|
||||
|
@ -338,7 +355,7 @@ impl Ctx {
|
|||
.item_list()
|
||||
.map(|list| {
|
||||
list.items()
|
||||
.flat_map(|item| self.lower_mod_item(&item))
|
||||
.flat_map(|item| self.lower_mod_item(&item, false))
|
||||
.flat_map(|items| items.0)
|
||||
.collect()
|
||||
})
|
||||
|
@ -349,90 +366,101 @@ impl Ctx {
|
|||
}
|
||||
};
|
||||
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 visibility = self.lower_visibility(trait_def);
|
||||
let generic_params = self.lower_generic_params(GenericsOwner::Trait(trait_def), trait_def);
|
||||
let auto = trait_def.auto_token().is_some();
|
||||
let items = trait_def.item_list().map(|list| {
|
||||
// FIXME: Does not handle macros
|
||||
list.assoc_items().flat_map(|item| self.lower_assoc_item(&item)).collect()
|
||||
list.items()
|
||||
.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);
|
||||
Some(Trait {
|
||||
let res = Trait {
|
||||
name,
|
||||
visibility,
|
||||
generic_params,
|
||||
auto,
|
||||
items: items.unwrap_or_default(),
|
||||
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 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()?
|
||||
.assoc_items()
|
||||
.filter_map(|item| self.lower_assoc_item(&item))
|
||||
.items()
|
||||
.filter_map(|item| {
|
||||
self.collect_inner_items(item.syntax());
|
||||
let assoc = self.lower_assoc_item(&item)?;
|
||||
Some(assoc)
|
||||
})
|
||||
.collect();
|
||||
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
|
||||
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 = &mut self.tree;
|
||||
ModPath::expand_use_item(
|
||||
InFile::new(self.file, use_item.clone()),
|
||||
&self.hygiene,
|
||||
|path, _tree, is_glob, alias| {
|
||||
imports.push(Import {
|
||||
imports.push(id(tree.imports.alloc(Import {
|
||||
path,
|
||||
alias,
|
||||
visibility: visibility.clone(),
|
||||
is_glob,
|
||||
is_prelude,
|
||||
is_extern_crate: false,
|
||||
is_macro_use: false,
|
||||
});
|
||||
ast_id,
|
||||
})));
|
||||
},
|
||||
);
|
||||
|
||||
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 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");
|
||||
|
||||
Some(Import {
|
||||
path,
|
||||
alias,
|
||||
visibility,
|
||||
is_glob: false,
|
||||
is_prelude: false,
|
||||
is_extern_crate: true,
|
||||
is_macro_use,
|
||||
})
|
||||
let res = ExternCrate { path, alias, visibility, is_macro_use, ast_id };
|
||||
Some(id(self.tree.extern_crates.alloc(res)))
|
||||
}
|
||||
|
||||
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 attrs = Attrs::new(m, &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();
|
||||
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| {
|
||||
list.extern_items()
|
||||
.filter_map(|item| match item {
|
||||
ast::ExternItem::FnDef(ast) => self.lower_function(&ast).map(Either::Left),
|
||||
ast::ExternItem::StaticDef(ast) => self.lower_static(&ast).map(Either::Right),
|
||||
.filter_map(|item| {
|
||||
self.collect_inner_items(item.syntax());
|
||||
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()
|
||||
})
|
||||
|
|
254
crates/ra_hir_def/src/item_tree/tests.rs
Normal file
254
crates/ra_hir_def/src/item_tree/tests.rs
Normal 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) }
|
||||
"###);
|
||||
}
|
|
@ -50,7 +50,7 @@ pub mod import_map;
|
|||
#[cfg(test)]
|
||||
mod test_db;
|
||||
|
||||
use std::hash::Hash;
|
||||
use std::hash::{Hash, Hasher};
|
||||
|
||||
use hir_expand::{
|
||||
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_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 item_tree::{
|
||||
Const, Enum, Function, Impl, ItemTreeId, ItemTreeNode, ModItem, Static, Struct, Trait,
|
||||
TypeAlias, Union,
|
||||
};
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||
pub struct ModuleId {
|
||||
|
@ -72,16 +75,62 @@ pub struct ModuleId {
|
|||
/// An ID of a module, **local** to a specific crate
|
||||
pub type LocalModuleId = Idx<nameres::ModuleData>;
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||
pub struct ItemLoc<N: AstNode> {
|
||||
#[derive(Debug)]
|
||||
pub struct ItemLoc<N: ItemTreeNode> {
|
||||
pub container: ContainerId,
|
||||
pub ast_id: AstId<N>,
|
||||
pub id: ItemTreeId<N>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||
pub struct AssocItemLoc<N: AstNode> {
|
||||
impl<N: ItemTreeNode> Clone for ItemLoc<N> {
|
||||
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 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 {
|
||||
|
@ -106,22 +155,22 @@ macro_rules! impl_intern {
|
|||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||
pub struct FunctionId(salsa::InternId);
|
||||
type FunctionLoc = AssocItemLoc<ast::FnDef>;
|
||||
type FunctionLoc = AssocItemLoc<Function>;
|
||||
impl_intern!(FunctionId, FunctionLoc, intern_function, lookup_intern_function);
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||
pub struct StructId(salsa::InternId);
|
||||
type StructLoc = ItemLoc<ast::StructDef>;
|
||||
type StructLoc = ItemLoc<Struct>;
|
||||
impl_intern!(StructId, StructLoc, intern_struct, lookup_intern_struct);
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||
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);
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||
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);
|
||||
|
||||
// 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)]
|
||||
pub struct ConstId(salsa::InternId);
|
||||
type ConstLoc = AssocItemLoc<ast::ConstDef>;
|
||||
type ConstLoc = AssocItemLoc<Const>;
|
||||
impl_intern!(ConstId, ConstLoc, intern_const, lookup_intern_const);
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||
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);
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||
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);
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||
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);
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Ord, PartialOrd)]
|
||||
pub struct ImplId(salsa::InternId);
|
||||
type ImplLoc = ItemLoc<ast::ImplDef>;
|
||||
type ImplLoc = ItemLoc<Impl>;
|
||||
impl_intern!(ImplId, ImplLoc, intern_impl, lookup_intern_impl);
|
||||
|
||||
#[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 {
|
||||
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 {
|
||||
fn module(&self, db: &dyn db::DefDatabase) -> ModuleId {
|
||||
match self {
|
||||
|
|
|
@ -20,9 +20,7 @@ use test_utils::mark;
|
|||
use crate::{
|
||||
attr::Attrs,
|
||||
db::DefDatabase,
|
||||
item_tree::{
|
||||
FileItemTreeId, Import, ItemTree, MacroCall, Mod, ModItem, ModKind, StructDefKind,
|
||||
},
|
||||
item_tree::{self, ItemTree, ItemTreeId, MacroCall, Mod, ModItem, ModKind, StructDefKind},
|
||||
nameres::{
|
||||
diagnostics::DefDiagnostic, mod_resolution::ModDir, path_resolution::ReachedFixedPoint,
|
||||
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)]
|
||||
struct ImportDirective {
|
||||
module_id: LocalModuleId,
|
||||
import_id: FileItemTreeId<Import>,
|
||||
import: Import,
|
||||
status: PartialResolvedImport,
|
||||
}
|
||||
|
@ -297,7 +333,7 @@ impl DefCollector<'_> {
|
|||
fn import_macros_from_extern_crate(
|
||||
&mut self,
|
||||
current_module_id: LocalModuleId,
|
||||
import: &Import,
|
||||
import: &item_tree::ExternCrate,
|
||||
) {
|
||||
log::debug!(
|
||||
"importing macros from extern crate: {:?} ({:?})",
|
||||
|
@ -703,9 +739,9 @@ impl ModCollector<'_, '_> {
|
|||
// any other items.
|
||||
for item in items {
|
||||
if self.is_cfg_enabled(self.item_tree.attrs(*item)) {
|
||||
if let ModItem::Import(import_id) = item {
|
||||
let import = self.item_tree[*import_id].clone();
|
||||
if import.is_extern_crate && import.is_macro_use {
|
||||
if let ModItem::ExternCrate(id) = item {
|
||||
let import = self.item_tree[*id].clone();
|
||||
if import.is_macro_use {
|
||||
self.def_collector.import_macros_from_extern_crate(self.module_id, &import);
|
||||
}
|
||||
}
|
||||
|
@ -725,8 +761,14 @@ impl ModCollector<'_, '_> {
|
|||
ModItem::Import(import_id) => {
|
||||
self.def_collector.unresolved_imports.push(ImportDirective {
|
||||
module_id: self.module_id,
|
||||
import_id,
|
||||
import: self.item_tree[import_id].clone(),
|
||||
import: self.item_tree[import_id].clone().into(),
|
||||
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,
|
||||
})
|
||||
}
|
||||
|
@ -737,30 +779,28 @@ impl ModCollector<'_, '_> {
|
|||
local_id: self.module_id,
|
||||
};
|
||||
let container = ContainerId::ModuleId(module);
|
||||
let ast_id = self.item_tree[imp].ast_id;
|
||||
let impl_id =
|
||||
ImplLoc { container, ast_id: AstId::new(self.file_id, ast_id) }
|
||||
let impl_id = ImplLoc { container, id: ItemTreeId::new(self.file_id, imp) }
|
||||
.intern(self.def_collector.db);
|
||||
self.def_collector.def_map.modules[self.module_id]
|
||||
.scope
|
||||
.define_impl(impl_id)
|
||||
}
|
||||
ModItem::Function(it) => {
|
||||
let it = &self.item_tree[it];
|
||||
ModItem::Function(id) => {
|
||||
let func = &self.item_tree[id];
|
||||
def = Some(DefData {
|
||||
id: FunctionLoc {
|
||||
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)
|
||||
.into(),
|
||||
name: &it.name,
|
||||
visibility: &it.visibility,
|
||||
name: &func.name,
|
||||
visibility: &func.visibility,
|
||||
has_constructor: false,
|
||||
});
|
||||
}
|
||||
ModItem::Struct(it) => {
|
||||
let it = &self.item_tree[it];
|
||||
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
|
||||
|
@ -768,10 +808,7 @@ impl ModCollector<'_, '_> {
|
|||
self.collect_derives(attrs, it.ast_id.upcast());
|
||||
|
||||
def = Some(DefData {
|
||||
id: StructLoc {
|
||||
container,
|
||||
ast_id: AstId::new(self.file_id, it.ast_id),
|
||||
}
|
||||
id: StructLoc { container, id: ItemTreeId::new(self.file_id, id) }
|
||||
.intern(self.def_collector.db)
|
||||
.into(),
|
||||
name: &it.name,
|
||||
|
@ -779,8 +816,8 @@ impl ModCollector<'_, '_> {
|
|||
has_constructor: it.kind != StructDefKind::Record,
|
||||
});
|
||||
}
|
||||
ModItem::Union(it) => {
|
||||
let it = &self.item_tree[it];
|
||||
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
|
||||
|
@ -788,7 +825,7 @@ impl ModCollector<'_, '_> {
|
|||
self.collect_derives(attrs, it.ast_id.upcast());
|
||||
|
||||
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)
|
||||
.into(),
|
||||
name: &it.name,
|
||||
|
@ -796,8 +833,8 @@ impl ModCollector<'_, '_> {
|
|||
has_constructor: false,
|
||||
});
|
||||
}
|
||||
ModItem::Enum(it) => {
|
||||
let it = &self.item_tree[it];
|
||||
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
|
||||
|
@ -805,7 +842,7 @@ impl ModCollector<'_, '_> {
|
|||
self.collect_derives(attrs, it.ast_id.upcast());
|
||||
|
||||
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)
|
||||
.into(),
|
||||
name: &it.name,
|
||||
|
@ -813,14 +850,14 @@ impl ModCollector<'_, '_> {
|
|||
has_constructor: false,
|
||||
});
|
||||
}
|
||||
ModItem::Const(it) => {
|
||||
let it = &self.item_tree[it];
|
||||
ModItem::Const(id) => {
|
||||
let it = &self.item_tree[id];
|
||||
|
||||
if let Some(name) = &it.name {
|
||||
def = Some(DefData {
|
||||
id: ConstLoc {
|
||||
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)
|
||||
.into(),
|
||||
|
@ -830,14 +867,11 @@ impl ModCollector<'_, '_> {
|
|||
});
|
||||
}
|
||||
}
|
||||
ModItem::Static(it) => {
|
||||
let it = &self.item_tree[it];
|
||||
ModItem::Static(id) => {
|
||||
let it = &self.item_tree[id];
|
||||
|
||||
def = Some(DefData {
|
||||
id: StaticLoc {
|
||||
container,
|
||||
ast_id: AstId::new(self.file_id, it.ast_id),
|
||||
}
|
||||
id: StaticLoc { container, id: ItemTreeId::new(self.file_id, id) }
|
||||
.intern(self.def_collector.db)
|
||||
.into(),
|
||||
name: &it.name,
|
||||
|
@ -845,11 +879,11 @@ impl ModCollector<'_, '_> {
|
|||
has_constructor: false,
|
||||
});
|
||||
}
|
||||
ModItem::Trait(it) => {
|
||||
let it = &self.item_tree[it];
|
||||
ModItem::Trait(id) => {
|
||||
let it = &self.item_tree[id];
|
||||
|
||||
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)
|
||||
.into(),
|
||||
name: &it.name,
|
||||
|
@ -857,13 +891,13 @@ impl ModCollector<'_, '_> {
|
|||
has_constructor: false,
|
||||
});
|
||||
}
|
||||
ModItem::TypeAlias(it) => {
|
||||
let it = &self.item_tree[it];
|
||||
ModItem::TypeAlias(id) => {
|
||||
let it = &self.item_tree[id];
|
||||
|
||||
def = Some(DefData {
|
||||
id: TypeAliasLoc {
|
||||
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)
|
||||
.into(),
|
||||
|
|
|
@ -722,10 +722,7 @@ fn unresolved_module_diagnostics() {
|
|||
),
|
||||
),
|
||||
),
|
||||
value: FileAstId {
|
||||
raw: Idx::<SyntaxNodePtr>(1),
|
||||
_ty: PhantomData,
|
||||
},
|
||||
value: FileAstId::<ra_syntax::ast::generated::nodes::Module>(1),
|
||||
},
|
||||
candidate: "bar.rs",
|
||||
},
|
||||
|
|
|
@ -2,30 +2,37 @@
|
|||
|
||||
use hir_expand::InFile;
|
||||
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 {
|
||||
type Value;
|
||||
fn source(&self, db: &dyn DefDatabase) -> InFile<Self::Value>;
|
||||
}
|
||||
|
||||
impl<N: AstNode> HasSource for AssocItemLoc<N> {
|
||||
type Value = N;
|
||||
impl<N: ItemTreeSource> HasSource for AssocItemLoc<N> {
|
||||
type Value = N::Source;
|
||||
|
||||
fn source(&self, db: &dyn DefDatabase) -> InFile<N> {
|
||||
let node = self.ast_id.to_node(db.upcast());
|
||||
InFile::new(self.ast_id.file_id, node)
|
||||
fn source(&self, db: &dyn DefDatabase) -> InFile<N::Source> {
|
||||
let tree = db.item_tree(self.id.file_id);
|
||||
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> {
|
||||
type Value = N;
|
||||
impl<N: ItemTreeSource> HasSource for ItemLoc<N> {
|
||||
type Value = N::Source;
|
||||
|
||||
fn source(&self, db: &dyn DefDatabase) -> InFile<N> {
|
||||
let node = self.ast_id.to_node(db.upcast());
|
||||
InFile::new(self.ast_id.file_id, node)
|
||||
fn source(&self, db: &dyn DefDatabase) -> InFile<N::Source> {
|
||||
let tree = db.item_tree(self.id.file_id);
|
||||
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))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -6,6 +6,8 @@
|
|||
//! changes.
|
||||
|
||||
use std::{
|
||||
any::type_name,
|
||||
fmt,
|
||||
hash::{Hash, Hasher},
|
||||
marker::PhantomData,
|
||||
};
|
||||
|
@ -14,7 +16,6 @@ use ra_arena::{Arena, Idx};
|
|||
use ra_syntax::{ast, AstNode, AstPtr, SyntaxNode, SyntaxNodePtr};
|
||||
|
||||
/// `AstId` points to an AST node in a specific file.
|
||||
#[derive(Debug)]
|
||||
pub struct FileAstId<N: AstNode> {
|
||||
raw: ErasedFileAstId,
|
||||
_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> {
|
||||
// Can't make this a From implementation because of coherence
|
||||
pub fn upcast<M: AstNode>(self) -> FileAstId<M>
|
||||
where
|
||||
M: From<N>,
|
||||
N: Into<M>,
|
||||
{
|
||||
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()
|
||||
}
|
||||
|
||||
|
|
|
@ -67,8 +67,8 @@ fn type_at_pos_displayed(
|
|||
panic!("Can't find expression")
|
||||
}
|
||||
|
||||
fn type_at(content: &str) -> String {
|
||||
let (db, file_pos) = TestDB::with_position(content);
|
||||
fn type_at(ra_fixture: &str) -> String {
|
||||
let (db, file_pos) = TestDB::with_position(ra_fixture);
|
||||
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));
|
||||
defs.sort_by_key(|def| match def {
|
||||
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) => {
|
||||
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) => {
|
||||
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 {
|
||||
|
|
Loading…
Reference in a new issue