mirror of
https://github.com/rust-lang/rust-analyzer
synced 2024-12-26 04:53:34 +00:00
Merge #4990
4990: Introduce an ItemTree layer to avoid reparsing files r=matklad a=jonas-schievink This reduces the latency of "go to definition" in a simple benchmark on rust-analyzer by around 30%. cc https://github.com/rust-analyzer/rust-analyzer/issues/1650 Closes https://github.com/rust-analyzer/rust-analyzer/issues/3485 Co-authored-by: Aleksey Kladov <aleksey.kladov@gmail.com> Co-authored-by: Jonas Schievink <jonas.schievink@ferrous-systems.com> Co-authored-by: Jonas Schievink <jonasschievink@gmail.com>
This commit is contained in:
commit
e9bdb05e96
26 changed files with 2590 additions and 1046 deletions
1
Cargo.lock
generated
1
Cargo.lock
generated
|
@ -1018,6 +1018,7 @@ dependencies = [
|
|||
"ra_syntax",
|
||||
"ra_tt",
|
||||
"rustc-hash",
|
||||
"smallvec",
|
||||
"stdx",
|
||||
"test_utils",
|
||||
]
|
||||
|
|
|
@ -116,6 +116,9 @@ impl<T> Arena<T> {
|
|||
) -> impl Iterator<Item = (Idx<T>, &T)> + ExactSizeIterator + DoubleEndedIterator {
|
||||
self.data.iter().enumerate().map(|(idx, value)| (Idx::from_raw(RawId(idx as u32)), value))
|
||||
}
|
||||
pub fn shrink_to_fit(&mut self) {
|
||||
self.data.shrink_to_fit();
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Default for Arena<T> {
|
||||
|
|
|
@ -114,7 +114,7 @@ pub trait SourceDatabase: CheckCanceled + FileLoader + std::fmt::Debug {
|
|||
}
|
||||
|
||||
fn parse_query(db: &impl SourceDatabase, file_id: FileId) -> Parse<ast::SourceFile> {
|
||||
let _p = profile("parse_query");
|
||||
let _p = profile("parse_query").detail(|| format!("{:?}", file_id));
|
||||
let text = db.file_text(file_id);
|
||||
SourceFile::parse(&*text)
|
||||
}
|
||||
|
|
|
@ -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))),
|
||||
|
|
|
@ -6,7 +6,7 @@ pub use hir_def::db::{
|
|||
ExprScopesQuery, FunctionDataQuery, GenericParamsQuery, ImplDataQuery, ImportMapQuery,
|
||||
InternConstQuery, InternDatabase, InternDatabaseStorage, InternEnumQuery, InternFunctionQuery,
|
||||
InternImplQuery, InternStaticQuery, InternStructQuery, InternTraitQuery, InternTypeAliasQuery,
|
||||
InternUnionQuery, LangItemQuery, ModuleLangItemsQuery, RawItemsQuery, StaticDataQuery,
|
||||
InternUnionQuery, ItemTreeQuery, LangItemQuery, ModuleLangItemsQuery, StaticDataQuery,
|
||||
StructDataQuery, TraitDataQuery, TypeAliasDataQuery, UnionDataQuery,
|
||||
};
|
||||
pub use hir_expand::db::{
|
||||
|
|
|
@ -17,6 +17,7 @@ drop_bomb = "0.1.4"
|
|||
fst = { version = "0.4", default-features = false }
|
||||
itertools = "0.9.0"
|
||||
indexmap = "1.4.0"
|
||||
smallvec = "1.4.0"
|
||||
|
||||
stdx = { path = "../stdx" }
|
||||
|
||||
|
|
|
@ -13,7 +13,11 @@ use ra_syntax::{
|
|||
use tt::Subtree;
|
||||
|
||||
use crate::{
|
||||
db::DefDatabase, nameres::ModuleSource, path::ModPath, src::HasChildSource, src::HasSource,
|
||||
db::DefDatabase,
|
||||
item_tree::{ItemTreeId, ItemTreeNode},
|
||||
nameres::ModuleSource,
|
||||
path::ModPath,
|
||||
src::HasChildSource,
|
||||
AdtId, AttrDefId, Lookup,
|
||||
};
|
||||
|
||||
|
@ -34,6 +38,8 @@ impl ops::Deref for Attrs {
|
|||
}
|
||||
|
||||
impl Attrs {
|
||||
pub const EMPTY: Attrs = Attrs { entries: None };
|
||||
|
||||
pub(crate) fn attrs_query(db: &dyn DefDatabase, def: AttrDefId) -> Attrs {
|
||||
match def {
|
||||
AttrDefId::ModuleId(module) => {
|
||||
|
@ -65,19 +71,19 @@ impl Attrs {
|
|||
Attrs::from_attrs_owner(db, src.map(|it| it as &dyn AttrsOwner))
|
||||
}
|
||||
AttrDefId::AdtId(it) => match it {
|
||||
AdtId::StructId(it) => attrs_from_loc(it.lookup(db), db),
|
||||
AdtId::EnumId(it) => attrs_from_loc(it.lookup(db), db),
|
||||
AdtId::UnionId(it) => attrs_from_loc(it.lookup(db), db),
|
||||
AdtId::StructId(it) => attrs_from_item_tree(it.lookup(db).id, db),
|
||||
AdtId::EnumId(it) => attrs_from_item_tree(it.lookup(db).id, db),
|
||||
AdtId::UnionId(it) => attrs_from_item_tree(it.lookup(db).id, db),
|
||||
},
|
||||
AttrDefId::TraitId(it) => attrs_from_loc(it.lookup(db), db),
|
||||
AttrDefId::TraitId(it) => attrs_from_item_tree(it.lookup(db).id, db),
|
||||
AttrDefId::MacroDefId(it) => {
|
||||
it.ast_id.map_or_else(Default::default, |ast_id| attrs_from_ast(ast_id, db))
|
||||
}
|
||||
AttrDefId::ImplId(it) => attrs_from_loc(it.lookup(db), db),
|
||||
AttrDefId::ConstId(it) => attrs_from_loc(it.lookup(db), db),
|
||||
AttrDefId::StaticId(it) => attrs_from_loc(it.lookup(db), db),
|
||||
AttrDefId::FunctionId(it) => attrs_from_loc(it.lookup(db), db),
|
||||
AttrDefId::TypeAliasId(it) => attrs_from_loc(it.lookup(db), db),
|
||||
AttrDefId::ImplId(it) => attrs_from_item_tree(it.lookup(db).id, db),
|
||||
AttrDefId::ConstId(it) => attrs_from_item_tree(it.lookup(db).id, db),
|
||||
AttrDefId::StaticId(it) => attrs_from_item_tree(it.lookup(db).id, db),
|
||||
AttrDefId::FunctionId(it) => attrs_from_item_tree(it.lookup(db).id, db),
|
||||
AttrDefId::TypeAliasId(it) => attrs_from_item_tree(it.lookup(db).id, db),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -103,6 +109,18 @@ impl Attrs {
|
|||
Attrs { entries }
|
||||
}
|
||||
|
||||
pub fn merge(&self, other: Attrs) -> Attrs {
|
||||
match (&self.entries, &other.entries) {
|
||||
(None, None) => Attrs { entries: None },
|
||||
(Some(entries), None) | (None, Some(entries)) => {
|
||||
Attrs { entries: Some(entries.clone()) }
|
||||
}
|
||||
(Some(a), Some(b)) => {
|
||||
Attrs { entries: Some(a.iter().chain(b.iter()).cloned().collect()) }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn by_key(&self, key: &'static str) -> AttrQuery<'_> {
|
||||
AttrQuery { attrs: self, key }
|
||||
}
|
||||
|
@ -187,11 +205,8 @@ where
|
|||
Attrs::from_attrs_owner(db, src.as_ref().map(|it| it as &dyn AttrsOwner))
|
||||
}
|
||||
|
||||
fn attrs_from_loc<T>(node: T, db: &dyn DefDatabase) -> Attrs
|
||||
where
|
||||
T: HasSource,
|
||||
T::Value: ast::AttrsOwner,
|
||||
{
|
||||
let src = node.source(db);
|
||||
Attrs::from_attrs_owner(db, src.as_ref().map(|it| it as &dyn AttrsOwner))
|
||||
fn attrs_from_item_tree<N: ItemTreeNode>(id: ItemTreeId<N>, db: &dyn DefDatabase) -> Attrs {
|
||||
let tree = db.item_tree(id.file_id);
|
||||
let mod_item = N::id_to_mod_item(id.value);
|
||||
tree.attrs(mod_item).clone()
|
||||
}
|
||||
|
|
|
@ -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, ItemTreeNode},
|
||||
path::{GenericArgs, Path},
|
||||
type_ref::{Mutability, Rawness, TypeRef},
|
||||
AdtId, ConstLoc, ContainerId, DefWithBodyId, EnumLoc, FunctionLoc, Intern, ModuleDefId,
|
||||
|
@ -35,6 +36,8 @@ use crate::{
|
|||
|
||||
use super::{ExprSource, PatSource};
|
||||
use ast::AstChildren;
|
||||
use rustc_hash::FxHashMap;
|
||||
use std::sync::Arc;
|
||||
|
||||
pub(crate) struct LowerCtx {
|
||||
hygiene: Hygiene,
|
||||
|
@ -60,10 +63,10 @@ pub(super) fn lower(
|
|||
params: Option<ast::ParamList>,
|
||||
body: Option<ast::Expr>,
|
||||
) -> (Body, BodySourceMap) {
|
||||
let item_tree = db.item_tree(expander.current_file_id);
|
||||
ExprCollector {
|
||||
db,
|
||||
def,
|
||||
expander,
|
||||
source_map: BodySourceMap::default(),
|
||||
body: Body {
|
||||
exprs: Arena::default(),
|
||||
|
@ -72,6 +75,12 @@ pub(super) fn lower(
|
|||
body_expr: dummy_expr_id(),
|
||||
item_scope: Default::default(),
|
||||
},
|
||||
item_trees: {
|
||||
let mut map = FxHashMap::default();
|
||||
map.insert(expander.current_file_id, item_tree);
|
||||
map
|
||||
},
|
||||
expander,
|
||||
}
|
||||
.collect(params, body)
|
||||
}
|
||||
|
@ -82,6 +91,8 @@ struct ExprCollector<'a> {
|
|||
expander: Expander,
|
||||
body: Body,
|
||||
source_map: BodySourceMap,
|
||||
|
||||
item_trees: FxHashMap<HirFileId, Arc<ItemTree>>,
|
||||
}
|
||||
|
||||
impl ExprCollector<'_> {
|
||||
|
@ -533,6 +544,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.insert(self.expander.current_file_id, item_tree);
|
||||
let id = self.collect_expr(expansion);
|
||||
self.expander.exit(self.db, mark);
|
||||
id
|
||||
|
@ -547,6 +561,19 @@ impl ExprCollector<'_> {
|
|||
}
|
||||
}
|
||||
|
||||
fn find_inner_item<S: ItemTreeNode>(&self, id: AstId<ast::ModuleItem>) -> FileItemTreeId<S> {
|
||||
let tree = &self.item_trees[&id.file_id];
|
||||
|
||||
// FIXME: This probably breaks with `use` items, since they produce multiple item tree nodes
|
||||
|
||||
// Root file (non-macro).
|
||||
tree.all_inner_items()
|
||||
.chain(tree.top_level_items().iter().copied())
|
||||
.filter_map(|mod_item| mod_item.downcast::<S>())
|
||||
.find(|tree_id| tree[*tree_id].ast_id().upcast() == id.value)
|
||||
.unwrap_or_else(|| panic!("couldn't find inner item for {:?}", id))
|
||||
}
|
||||
|
||||
fn collect_expr_opt(&mut self, expr: Option<ast::Expr>) -> ExprId {
|
||||
if let Some(expr) = expr {
|
||||
self.collect_expr(expr)
|
||||
|
@ -578,56 +605,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
|
||||
|
|
|
@ -317,6 +317,26 @@ fn foo() {
|
|||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn macro_inner_item() {
|
||||
do_check(
|
||||
r"
|
||||
macro_rules! mac {
|
||||
() => {{
|
||||
fn inner() {}
|
||||
inner();
|
||||
}};
|
||||
}
|
||||
|
||||
fn foo() {
|
||||
mac!();
|
||||
<|>
|
||||
}
|
||||
",
|
||||
&[],
|
||||
);
|
||||
}
|
||||
|
||||
fn do_check_local_name(ra_fixture: &str, expected_offset: u32) {
|
||||
let (db, position) = TestDB::with_position(ra_fixture);
|
||||
let file_id = position.file_id;
|
||||
|
|
|
@ -2,27 +2,19 @@
|
|||
|
||||
use std::sync::Arc;
|
||||
|
||||
use hir_expand::{
|
||||
hygiene::Hygiene,
|
||||
name::{name, AsName, Name},
|
||||
AstId, InFile,
|
||||
};
|
||||
use hir_expand::{name::Name, InFile};
|
||||
use ra_prof::profile;
|
||||
use ra_syntax::ast::{
|
||||
self, AssocItem, AstNode, ModuleItemOwner, NameOwner, TypeAscriptionOwner, TypeBoundsOwner,
|
||||
VisibilityOwner,
|
||||
};
|
||||
use ra_syntax::ast;
|
||||
|
||||
use crate::{
|
||||
attr::Attrs,
|
||||
body::LowerCtx,
|
||||
body::Expander,
|
||||
db::DefDatabase,
|
||||
path::{path, AssociatedTypeBinding, GenericArgs, Path},
|
||||
src::HasSource,
|
||||
type_ref::{Mutability, TypeBound, TypeRef},
|
||||
item_tree::{AssocItem, ItemTreeId, ModItem},
|
||||
type_ref::{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)]
|
||||
|
@ -41,82 +33,27 @@ pub struct FunctionData {
|
|||
impl FunctionData {
|
||||
pub(crate) fn fn_data_query(db: &impl DefDatabase, func: FunctionId) -> Arc<FunctionData> {
|
||||
let loc = func.lookup(db);
|
||||
let src = loc.source(db);
|
||||
let ctx = LowerCtx::new(db, src.file_id);
|
||||
let name = src.value.name().map(|n| n.as_name()).unwrap_or_else(Name::missing);
|
||||
let mut params = Vec::new();
|
||||
let mut has_self_param = false;
|
||||
if let Some(param_list) = src.value.param_list() {
|
||||
if let Some(self_param) = param_list.self_param() {
|
||||
let self_type = if let Some(type_ref) = self_param.ascribed_type() {
|
||||
TypeRef::from_ast(&ctx, type_ref)
|
||||
} else {
|
||||
let self_type = TypeRef::Path(name![Self].into());
|
||||
match self_param.kind() {
|
||||
ast::SelfParamKind::Owned => self_type,
|
||||
ast::SelfParamKind::Ref => {
|
||||
TypeRef::Reference(Box::new(self_type), Mutability::Shared)
|
||||
}
|
||||
ast::SelfParamKind::MutRef => {
|
||||
TypeRef::Reference(Box::new(self_type), Mutability::Mut)
|
||||
}
|
||||
}
|
||||
};
|
||||
params.push(self_type);
|
||||
has_self_param = true;
|
||||
}
|
||||
for param in param_list.params() {
|
||||
let type_ref = TypeRef::from_ast_opt(&ctx, param.ascribed_type());
|
||||
params.push(type_ref);
|
||||
}
|
||||
}
|
||||
let attrs = Attrs::new(&src.value, &Hygiene::new(db.upcast(), src.file_id));
|
||||
let item_tree = db.item_tree(loc.id.file_id);
|
||||
let func = &item_tree[loc.id.value];
|
||||
|
||||
let ret_type = if let Some(type_ref) = src.value.ret_type().and_then(|rt| rt.type_ref()) {
|
||||
TypeRef::from_ast(&ctx, type_ref)
|
||||
} else {
|
||||
TypeRef::unit()
|
||||
};
|
||||
|
||||
let ret_type = if src.value.async_token().is_some() {
|
||||
let future_impl = desugar_future_path(ret_type);
|
||||
let ty_bound = TypeBound::Path(future_impl);
|
||||
TypeRef::ImplTrait(vec![ty_bound])
|
||||
} else {
|
||||
ret_type
|
||||
};
|
||||
|
||||
let is_unsafe = src.value.unsafe_token().is_some();
|
||||
|
||||
let vis_default = RawVisibility::default_for_container(loc.container);
|
||||
let visibility =
|
||||
RawVisibility::from_ast_with_default(db, vis_default, src.map(|s| s.visibility()));
|
||||
|
||||
let sig =
|
||||
FunctionData { name, params, ret_type, has_self_param, is_unsafe, visibility, attrs };
|
||||
Arc::new(sig)
|
||||
Arc::new(FunctionData {
|
||||
name: func.name.clone(),
|
||||
params: func.params.to_vec(),
|
||||
ret_type: func.ret_type.clone(),
|
||||
attrs: item_tree.attrs(loc.id.value.into()).clone(),
|
||||
has_self_param: func.has_self_param,
|
||||
is_unsafe: func.is_unsafe,
|
||||
visibility: item_tree[func.visibility].clone(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
fn desugar_future_path(orig: TypeRef) -> Path {
|
||||
let path = path![core::future::Future];
|
||||
let mut generic_args: Vec<_> = std::iter::repeat(None).take(path.segments.len() - 1).collect();
|
||||
let mut last = GenericArgs::empty();
|
||||
last.bindings.push(AssociatedTypeBinding {
|
||||
name: name![Output],
|
||||
type_ref: Some(orig),
|
||||
bounds: Vec::new(),
|
||||
});
|
||||
generic_args.push(Some(Arc::new(last)));
|
||||
|
||||
Path::from_known_path(path, generic_args)
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct TypeAliasData {
|
||||
pub name: Name,
|
||||
pub type_ref: Option<TypeRef>,
|
||||
pub visibility: RawVisibility,
|
||||
/// Bounds restricting the type alias itself (eg. `type Ty: Bound;` in a trait or impl).
|
||||
pub bounds: Vec<TypeBound>,
|
||||
}
|
||||
|
||||
|
@ -126,22 +63,15 @@ impl TypeAliasData {
|
|||
typ: TypeAliasId,
|
||||
) -> Arc<TypeAliasData> {
|
||||
let loc = typ.lookup(db);
|
||||
let node = loc.source(db);
|
||||
let name = node.value.name().map_or_else(Name::missing, |n| n.as_name());
|
||||
let lower_ctx = LowerCtx::new(db, node.file_id);
|
||||
let type_ref = node.value.type_ref().map(|it| TypeRef::from_ast(&lower_ctx, it));
|
||||
let vis_default = RawVisibility::default_for_container(loc.container);
|
||||
let visibility = RawVisibility::from_ast_with_default(
|
||||
db,
|
||||
vis_default,
|
||||
node.as_ref().map(|n| n.visibility()),
|
||||
);
|
||||
let bounds = if let Some(bound_list) = node.value.type_bound_list() {
|
||||
bound_list.bounds().map(|it| TypeBound::from_ast(&lower_ctx, it)).collect()
|
||||
} else {
|
||||
Vec::new()
|
||||
};
|
||||
Arc::new(TypeAliasData { name, type_ref, visibility, bounds })
|
||||
let item_tree = db.item_tree(loc.id.file_id);
|
||||
let typ = &item_tree[loc.id.value];
|
||||
|
||||
Arc::new(TypeAliasData {
|
||||
name: typ.name.clone(),
|
||||
type_ref: typ.type_ref.clone(),
|
||||
visibility: item_tree[typ.visibility].clone(),
|
||||
bounds: typ.bounds.to_vec(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -155,30 +85,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 +133,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 })
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -250,22 +169,14 @@ pub struct ConstData {
|
|||
impl ConstData {
|
||||
pub(crate) fn const_data_query(db: &dyn DefDatabase, konst: ConstId) -> Arc<ConstData> {
|
||||
let loc = konst.lookup(db);
|
||||
let node = loc.source(db);
|
||||
let vis_default = RawVisibility::default_for_container(loc.container);
|
||||
Arc::new(ConstData::new(db, vis_default, node))
|
||||
}
|
||||
let item_tree = db.item_tree(loc.id.file_id);
|
||||
let konst = &item_tree[loc.id.value];
|
||||
|
||||
fn new<N: NameOwner + TypeAscriptionOwner + VisibilityOwner>(
|
||||
db: &dyn DefDatabase,
|
||||
vis_default: RawVisibility,
|
||||
node: InFile<N>,
|
||||
) -> ConstData {
|
||||
let ctx = LowerCtx::new(db, node.file_id);
|
||||
let name = node.value.name().map(|n| n.as_name());
|
||||
let type_ref = TypeRef::from_ast_opt(&ctx, node.value.ascribed_type());
|
||||
let visibility =
|
||||
RawVisibility::from_ast_with_default(db, vis_default, node.map(|n| n.visibility()));
|
||||
ConstData { name, type_ref, visibility }
|
||||
Arc::new(ConstData {
|
||||
name: konst.name.clone(),
|
||||
type_ref: konst.type_ref.clone(),
|
||||
visibility: item_tree[konst.visibility].clone(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -279,44 +190,25 @@ pub struct StaticData {
|
|||
|
||||
impl StaticData {
|
||||
pub(crate) fn static_data_query(db: &dyn DefDatabase, konst: StaticId) -> Arc<StaticData> {
|
||||
let node = konst.lookup(db).source(db);
|
||||
let ctx = LowerCtx::new(db, node.file_id);
|
||||
let node = konst.lookup(db);
|
||||
let item_tree = db.item_tree(node.id.file_id);
|
||||
let statik = &item_tree[node.id.value];
|
||||
|
||||
let name = node.value.name().map(|n| n.as_name());
|
||||
let type_ref = TypeRef::from_ast_opt(&ctx, node.value.ascribed_type());
|
||||
let mutable = node.value.mut_token().is_some();
|
||||
let visibility = RawVisibility::from_ast_with_default(
|
||||
db,
|
||||
RawVisibility::private(),
|
||||
node.map(|n| n.visibility()),
|
||||
);
|
||||
|
||||
Arc::new(StaticData { name, type_ref, visibility, mutable })
|
||||
Arc::new(StaticData {
|
||||
name: Some(statik.name.clone()),
|
||||
type_ref: statik.type_ref.clone(),
|
||||
visibility: item_tree[statik.visibility].clone(),
|
||||
mutable: statik.mutable,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
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 +216,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(
|
||||
let item_tree = db.item_tree(file_id);
|
||||
let cfg_options = db.crate_graph()[module.krate].cfg_options.clone();
|
||||
|
||||
let mut items = Vec::new();
|
||||
for item in assoc_items {
|
||||
match item {
|
||||
AssocItem::Function(id) => {
|
||||
let item = &item_tree[id];
|
||||
let attrs = item_tree.attrs(id.into());
|
||||
if !attrs.is_cfg_enabled(&cfg_options) {
|
||||
continue;
|
||||
}
|
||||
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 = match item.name.clone() {
|
||||
Some(name) => name,
|
||||
None => 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);
|
||||
|
||||
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,
|
||||
items.value.items().filter_map(|it| AssocItem::cast(it.syntax().clone())),
|
||||
items.file_id,
|
||||
iter,
|
||||
src.file_id,
|
||||
container,
|
||||
);
|
||||
limit - 1,
|
||||
));
|
||||
|
||||
// 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))
|
||||
}
|
||||
expander.exit(db, mark);
|
||||
res
|
||||
} else {
|
||||
Vec::new()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn collect_items(
|
||||
db: &dyn DefDatabase,
|
||||
expander: &mut Expander,
|
||||
assoc_items: impl Iterator<Item = AssocItem>,
|
||||
file_id: crate::HirFileId,
|
||||
container: AssocContainerId,
|
||||
) -> Vec<(Name, AssocItemId)> {
|
||||
let items = db.ast_id_map(file_id);
|
||||
|
||||
assoc_items
|
||||
.filter_map(|item_node| match item_node {
|
||||
ast::AssocItem::FnDef(it) => {
|
||||
let name = it.name().map_or_else(Name::missing, |it| it.as_name());
|
||||
if !expander.is_cfg_enabled(&it) {
|
||||
return None;
|
||||
}
|
||||
let def = FunctionLoc { container, ast_id: AstId::new(file_id, items.ast_id(&it)) }
|
||||
.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
|
||||
}
|
||||
|
|
|
@ -14,8 +14,9 @@ use crate::{
|
|||
docs::Documentation,
|
||||
generics::GenericParams,
|
||||
import_map::ImportMap,
|
||||
item_tree::ItemTree,
|
||||
lang_item::{LangItemTarget, LangItems},
|
||||
nameres::{raw::RawItems, CrateDefMap},
|
||||
nameres::CrateDefMap,
|
||||
AttrDefId, ConstId, ConstLoc, DefWithBodyId, EnumId, EnumLoc, FunctionId, FunctionLoc,
|
||||
GenericDefId, ImplId, ImplLoc, ModuleId, StaticId, StaticLoc, StructId, StructLoc, TraitId,
|
||||
TraitLoc, TypeAliasId, TypeAliasLoc, UnionId, UnionLoc,
|
||||
|
@ -45,8 +46,8 @@ pub trait InternDatabase: SourceDatabase {
|
|||
|
||||
#[salsa::query_group(DefDatabaseStorage)]
|
||||
pub trait DefDatabase: InternDatabase + AstDatabase + Upcast<dyn AstDatabase> {
|
||||
#[salsa::invoke(RawItems::raw_items_query)]
|
||||
fn raw_items(&self, file_id: HirFileId) -> Arc<RawItems>;
|
||||
#[salsa::invoke(ItemTree::item_tree_query)]
|
||||
fn item_tree(&self, file_id: HirFileId) -> Arc<ItemTree>;
|
||||
|
||||
#[salsa::invoke(crate_def_map_wait)]
|
||||
#[salsa::transparent]
|
||||
|
|
|
@ -42,7 +42,7 @@ pub enum TypeParamProvenance {
|
|||
}
|
||||
|
||||
/// Data about the generic parameters of a function, struct, impl, etc.
|
||||
#[derive(Clone, PartialEq, Eq, Debug)]
|
||||
#[derive(Clone, PartialEq, Eq, Debug, Default)]
|
||||
pub struct GenericParams {
|
||||
pub types: Arena<TypeParamData>,
|
||||
// lifetimes: Arena<LocalLifetimeParamId, LifetimeParamData>,
|
||||
|
@ -74,8 +74,53 @@ impl GenericParams {
|
|||
def: GenericDefId,
|
||||
) -> Arc<GenericParams> {
|
||||
let _p = profile("generic_params_query");
|
||||
let (params, _source_map) = GenericParams::new(db, def);
|
||||
Arc::new(params)
|
||||
|
||||
let generics = match def {
|
||||
GenericDefId::FunctionId(id) => {
|
||||
let id = id.lookup(db).id;
|
||||
let tree = db.item_tree(id.file_id);
|
||||
let item = &tree[id.value];
|
||||
tree[item.generic_params].clone()
|
||||
}
|
||||
GenericDefId::AdtId(AdtId::StructId(id)) => {
|
||||
let id = id.lookup(db).id;
|
||||
let tree = db.item_tree(id.file_id);
|
||||
let item = &tree[id.value];
|
||||
tree[item.generic_params].clone()
|
||||
}
|
||||
GenericDefId::AdtId(AdtId::EnumId(id)) => {
|
||||
let id = id.lookup(db).id;
|
||||
let tree = db.item_tree(id.file_id);
|
||||
let item = &tree[id.value];
|
||||
tree[item.generic_params].clone()
|
||||
}
|
||||
GenericDefId::AdtId(AdtId::UnionId(id)) => {
|
||||
let id = id.lookup(db).id;
|
||||
let tree = db.item_tree(id.file_id);
|
||||
let item = &tree[id.value];
|
||||
tree[item.generic_params].clone()
|
||||
}
|
||||
GenericDefId::TraitId(id) => {
|
||||
let id = id.lookup(db).id;
|
||||
let tree = db.item_tree(id.file_id);
|
||||
let item = &tree[id.value];
|
||||
tree[item.generic_params].clone()
|
||||
}
|
||||
GenericDefId::TypeAliasId(id) => {
|
||||
let id = id.lookup(db).id;
|
||||
let tree = db.item_tree(id.file_id);
|
||||
let item = &tree[id.value];
|
||||
tree[item.generic_params].clone()
|
||||
}
|
||||
GenericDefId::ImplId(id) => {
|
||||
let id = id.lookup(db).id;
|
||||
let tree = db.item_tree(id.file_id);
|
||||
let item = &tree[id.value];
|
||||
tree[item.generic_params].clone()
|
||||
}
|
||||
GenericDefId::EnumVariantId(_) | GenericDefId::ConstId(_) => GenericParams::default(),
|
||||
};
|
||||
Arc::new(generics)
|
||||
}
|
||||
|
||||
fn new(db: &dyn DefDatabase, def: GenericDefId) -> (GenericParams, InFile<SourceMap>) {
|
||||
|
@ -156,7 +201,12 @@ impl GenericParams {
|
|||
(generics, InFile::new(file_id, sm))
|
||||
}
|
||||
|
||||
fn fill(&mut self, lower_ctx: &LowerCtx, sm: &mut SourceMap, node: &dyn TypeParamsOwner) {
|
||||
pub(crate) fn fill(
|
||||
&mut self,
|
||||
lower_ctx: &LowerCtx,
|
||||
sm: &mut SourceMap,
|
||||
node: &dyn TypeParamsOwner,
|
||||
) {
|
||||
if let Some(params) = node.type_param_list() {
|
||||
self.fill_params(lower_ctx, sm, params)
|
||||
}
|
||||
|
@ -165,7 +215,7 @@ impl GenericParams {
|
|||
}
|
||||
}
|
||||
|
||||
fn fill_bounds(
|
||||
pub(crate) fn fill_bounds(
|
||||
&mut self,
|
||||
lower_ctx: &LowerCtx,
|
||||
node: &dyn ast::TypeBoundsOwner,
|
||||
|
@ -229,7 +279,7 @@ impl GenericParams {
|
|||
.push(WherePredicate { target: WherePredicateTarget::TypeRef(type_ref), bound });
|
||||
}
|
||||
|
||||
fn fill_implicit_impl_trait_args(&mut self, type_ref: &TypeRef) {
|
||||
pub(crate) fn fill_implicit_impl_trait_args(&mut self, type_ref: &TypeRef) {
|
||||
type_ref.walk(&mut |type_ref| {
|
||||
if let TypeRef::ImplTrait(bounds) = type_ref {
|
||||
let param = TypeParamData {
|
||||
|
|
697
crates/ra_hir_def/src/item_tree.rs
Normal file
697
crates/ra_hir_def/src/item_tree.rs
Normal file
|
@ -0,0 +1,697 @@
|
|||
//! A simplified AST that only contains items.
|
||||
|
||||
mod lower;
|
||||
#[cfg(test)]
|
||||
mod tests;
|
||||
|
||||
use std::{
|
||||
fmt::{self, Debug},
|
||||
hash::{Hash, Hasher},
|
||||
marker::PhantomData,
|
||||
ops::{Index, Range},
|
||||
sync::Arc,
|
||||
};
|
||||
|
||||
use ast::{AstNode, AttrsOwner, NameOwner, StructKind, TypeAscriptionOwner};
|
||||
use either::Either;
|
||||
use hir_expand::{
|
||||
ast_id_map::FileAstId,
|
||||
hygiene::Hygiene,
|
||||
name::{name, AsName, Name},
|
||||
HirFileId, InFile,
|
||||
};
|
||||
use ra_arena::{Arena, Idx, RawId};
|
||||
use ra_syntax::{ast, match_ast};
|
||||
use rustc_hash::FxHashMap;
|
||||
use smallvec::SmallVec;
|
||||
use test_utils::mark;
|
||||
|
||||
use crate::{
|
||||
attr::Attrs,
|
||||
db::DefDatabase,
|
||||
generics::GenericParams,
|
||||
path::{path, AssociatedTypeBinding, GenericArgs, ImportAlias, ModPath, Path, PathKind},
|
||||
type_ref::{Mutability, TypeBound, TypeRef},
|
||||
visibility::RawVisibility,
|
||||
};
|
||||
|
||||
#[derive(Copy, Clone, Eq, PartialEq)]
|
||||
pub struct RawVisibilityId(u32);
|
||||
|
||||
impl RawVisibilityId {
|
||||
pub const PUB: Self = RawVisibilityId(u32::max_value());
|
||||
pub const PRIV: Self = RawVisibilityId(u32::max_value() - 1);
|
||||
pub const PUB_CRATE: Self = RawVisibilityId(u32::max_value() - 2);
|
||||
}
|
||||
|
||||
impl fmt::Debug for RawVisibilityId {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
let mut f = f.debug_tuple("RawVisibilityId");
|
||||
match *self {
|
||||
Self::PUB => f.field(&"pub"),
|
||||
Self::PRIV => f.field(&"pub(self)"),
|
||||
Self::PUB_CRATE => f.field(&"pub(crate)"),
|
||||
_ => f.field(&self.0),
|
||||
};
|
||||
f.finish()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
|
||||
pub struct GenericParamsId(u32);
|
||||
|
||||
impl GenericParamsId {
|
||||
pub const EMPTY: Self = GenericParamsId(u32::max_value());
|
||||
}
|
||||
|
||||
/// The item tree of a source file.
|
||||
#[derive(Debug, Eq, PartialEq)]
|
||||
pub struct ItemTree {
|
||||
top_level: SmallVec<[ModItem; 1]>,
|
||||
attrs: FxHashMap<AttrOwner, Attrs>,
|
||||
inner_items: FxHashMap<FileAstId<ast::ModuleItem>, SmallVec<[ModItem; 1]>>,
|
||||
|
||||
data: Option<Box<ItemTreeData>>,
|
||||
}
|
||||
|
||||
impl ItemTree {
|
||||
pub fn item_tree_query(db: &dyn DefDatabase, file_id: HirFileId) -> Arc<ItemTree> {
|
||||
let _p = ra_prof::profile("item_tree_query").detail(|| format!("{:?}", file_id));
|
||||
let syntax = if let Some(node) = db.parse_or_expand(file_id) {
|
||||
node
|
||||
} else {
|
||||
return Arc::new(Self::empty());
|
||||
};
|
||||
|
||||
let hygiene = Hygiene::new(db.upcast(), file_id);
|
||||
let ctx = lower::Ctx::new(db, hygiene.clone(), file_id);
|
||||
let mut top_attrs = None;
|
||||
let mut item_tree = match_ast! {
|
||||
match syntax {
|
||||
ast::SourceFile(file) => {
|
||||
top_attrs = Some(Attrs::new(&file, &hygiene));
|
||||
ctx.lower_module_items(&file)
|
||||
},
|
||||
ast::MacroItems(items) => {
|
||||
ctx.lower_module_items(&items)
|
||||
},
|
||||
// Macros can expand to expressions. We return an empty item tree in this case, but
|
||||
// still need to collect inner items.
|
||||
ast::Expr(e) => {
|
||||
ctx.lower_inner_items(e.syntax())
|
||||
},
|
||||
_ => {
|
||||
panic!("cannot create item tree from {:?}", syntax);
|
||||
},
|
||||
}
|
||||
};
|
||||
|
||||
if let Some(attrs) = top_attrs {
|
||||
item_tree.attrs.insert(AttrOwner::TopLevel, attrs);
|
||||
}
|
||||
item_tree.shrink_to_fit();
|
||||
Arc::new(item_tree)
|
||||
}
|
||||
|
||||
fn empty() -> Self {
|
||||
Self {
|
||||
top_level: Default::default(),
|
||||
attrs: Default::default(),
|
||||
inner_items: Default::default(),
|
||||
data: Default::default(),
|
||||
}
|
||||
}
|
||||
|
||||
fn shrink_to_fit(&mut self) {
|
||||
if let Some(data) = &mut self.data {
|
||||
let ItemTreeData {
|
||||
imports,
|
||||
extern_crates,
|
||||
functions,
|
||||
structs,
|
||||
fields,
|
||||
unions,
|
||||
enums,
|
||||
variants,
|
||||
consts,
|
||||
statics,
|
||||
traits,
|
||||
impls,
|
||||
type_aliases,
|
||||
mods,
|
||||
macro_calls,
|
||||
exprs,
|
||||
vis,
|
||||
generics,
|
||||
} = &mut **data;
|
||||
|
||||
imports.shrink_to_fit();
|
||||
extern_crates.shrink_to_fit();
|
||||
functions.shrink_to_fit();
|
||||
structs.shrink_to_fit();
|
||||
fields.shrink_to_fit();
|
||||
unions.shrink_to_fit();
|
||||
enums.shrink_to_fit();
|
||||
variants.shrink_to_fit();
|
||||
consts.shrink_to_fit();
|
||||
statics.shrink_to_fit();
|
||||
traits.shrink_to_fit();
|
||||
impls.shrink_to_fit();
|
||||
type_aliases.shrink_to_fit();
|
||||
mods.shrink_to_fit();
|
||||
macro_calls.shrink_to_fit();
|
||||
exprs.shrink_to_fit();
|
||||
|
||||
vis.arena.shrink_to_fit();
|
||||
generics.arena.shrink_to_fit();
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns an iterator over all items located at the top level of the `HirFileId` this
|
||||
/// `ItemTree` was created from.
|
||||
pub fn top_level_items(&self) -> &[ModItem] {
|
||||
&self.top_level
|
||||
}
|
||||
|
||||
/// Returns the inner attributes of the source file.
|
||||
pub fn top_level_attrs(&self) -> &Attrs {
|
||||
self.attrs.get(&AttrOwner::TopLevel).unwrap_or(&Attrs::EMPTY)
|
||||
}
|
||||
|
||||
pub fn attrs(&self, of: ModItem) -> &Attrs {
|
||||
self.attrs.get(&AttrOwner::ModItem(of)).unwrap_or(&Attrs::EMPTY)
|
||||
}
|
||||
|
||||
/// Returns the lowered inner items that `ast` corresponds to.
|
||||
///
|
||||
/// Most AST items are lowered to a single `ModItem`, but some (eg. `use` items) may be lowered
|
||||
/// to multiple items in the `ItemTree`.
|
||||
pub fn inner_items(&self, ast: FileAstId<ast::ModuleItem>) -> &[ModItem] {
|
||||
&self.inner_items[&ast]
|
||||
}
|
||||
|
||||
pub fn all_inner_items(&self) -> impl Iterator<Item = ModItem> + '_ {
|
||||
self.inner_items.values().flatten().copied()
|
||||
}
|
||||
|
||||
pub fn source<S: ItemTreeNode>(&self, db: &dyn DefDatabase, of: ItemTreeId<S>) -> S::Source {
|
||||
// This unwrap cannot fail, since it has either succeeded above, or resulted in an empty
|
||||
// ItemTree (in which case there is no valid `FileItemTreeId` to call this method with).
|
||||
let root =
|
||||
db.parse_or_expand(of.file_id).expect("parse_or_expand failed on constructed ItemTree");
|
||||
|
||||
let id = self[of.value].ast_id();
|
||||
let map = db.ast_id_map(of.file_id);
|
||||
let ptr = map.get(id);
|
||||
ptr.to_node(&root)
|
||||
}
|
||||
|
||||
fn data(&self) -> &ItemTreeData {
|
||||
self.data.as_ref().expect("attempted to access data of empty ItemTree")
|
||||
}
|
||||
|
||||
fn data_mut(&mut self) -> &mut ItemTreeData {
|
||||
self.data.get_or_insert_with(Box::default)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default, Debug, Eq, PartialEq)]
|
||||
struct ItemVisibilities {
|
||||
arena: Arena<RawVisibility>,
|
||||
}
|
||||
|
||||
impl ItemVisibilities {
|
||||
fn alloc(&mut self, vis: RawVisibility) -> RawVisibilityId {
|
||||
match &vis {
|
||||
RawVisibility::Public => RawVisibilityId::PUB,
|
||||
RawVisibility::Module(path) if path.segments.is_empty() => match &path.kind {
|
||||
PathKind::Super(0) => RawVisibilityId::PRIV,
|
||||
PathKind::Crate => RawVisibilityId::PUB_CRATE,
|
||||
_ => RawVisibilityId(self.arena.alloc(vis).into_raw().into()),
|
||||
},
|
||||
_ => RawVisibilityId(self.arena.alloc(vis).into_raw().into()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static VIS_PUB: RawVisibility = RawVisibility::Public;
|
||||
static VIS_PRIV: RawVisibility =
|
||||
RawVisibility::Module(ModPath { kind: PathKind::Super(0), segments: Vec::new() });
|
||||
static VIS_PUB_CRATE: RawVisibility =
|
||||
RawVisibility::Module(ModPath { kind: PathKind::Crate, segments: Vec::new() });
|
||||
|
||||
#[derive(Default, Debug, Eq, PartialEq)]
|
||||
struct GenericParamsStorage {
|
||||
arena: Arena<GenericParams>,
|
||||
}
|
||||
|
||||
impl GenericParamsStorage {
|
||||
fn alloc(&mut self, params: GenericParams) -> GenericParamsId {
|
||||
if params.types.is_empty() && params.where_predicates.is_empty() {
|
||||
return GenericParamsId::EMPTY;
|
||||
}
|
||||
|
||||
GenericParamsId(self.arena.alloc(params).into_raw().into())
|
||||
}
|
||||
}
|
||||
|
||||
static EMPTY_GENERICS: GenericParams =
|
||||
GenericParams { types: Arena::new(), where_predicates: Vec::new() };
|
||||
|
||||
#[derive(Default, Debug, Eq, PartialEq)]
|
||||
struct ItemTreeData {
|
||||
imports: Arena<Import>,
|
||||
extern_crates: Arena<ExternCrate>,
|
||||
functions: Arena<Function>,
|
||||
structs: Arena<Struct>,
|
||||
fields: Arena<Field>,
|
||||
unions: Arena<Union>,
|
||||
enums: Arena<Enum>,
|
||||
variants: Arena<Variant>,
|
||||
consts: Arena<Const>,
|
||||
statics: Arena<Static>,
|
||||
traits: Arena<Trait>,
|
||||
impls: Arena<Impl>,
|
||||
type_aliases: Arena<TypeAlias>,
|
||||
mods: Arena<Mod>,
|
||||
macro_calls: Arena<MacroCall>,
|
||||
exprs: Arena<Expr>,
|
||||
|
||||
vis: ItemVisibilities,
|
||||
generics: GenericParamsStorage,
|
||||
}
|
||||
|
||||
#[derive(Debug, Eq, PartialEq, Hash)]
|
||||
enum AttrOwner {
|
||||
/// Attributes on an item.
|
||||
ModItem(ModItem),
|
||||
/// Inner attributes of the source file.
|
||||
TopLevel,
|
||||
// FIXME: Store variant and field attrs, and stop reparsing them in `attrs_query`.
|
||||
}
|
||||
|
||||
/// Trait implemented by all nodes in the item tree.
|
||||
pub trait ItemTreeNode: Clone {
|
||||
type Source: AstNode + Into<ast::ModuleItem>;
|
||||
|
||||
fn ast_id(&self) -> FileAstId<Self::Source>;
|
||||
|
||||
/// Looks up an instance of `Self` in an item tree.
|
||||
fn lookup(tree: &ItemTree, index: Idx<Self>) -> &Self;
|
||||
|
||||
/// Downcasts a `ModItem` to a `FileItemTreeId` specific to this type.
|
||||
fn id_from_mod_item(mod_item: ModItem) -> Option<FileItemTreeId<Self>>;
|
||||
|
||||
/// Upcasts a `FileItemTreeId` to a generic `ModItem`.
|
||||
fn id_to_mod_item(id: FileItemTreeId<Self>) -> ModItem;
|
||||
}
|
||||
|
||||
pub struct FileItemTreeId<N: ItemTreeNode> {
|
||||
index: Idx<N>,
|
||||
_p: PhantomData<N>,
|
||||
}
|
||||
|
||||
impl<N: ItemTreeNode> Clone for FileItemTreeId<N> {
|
||||
fn clone(&self) -> Self {
|
||||
Self { index: self.index, _p: PhantomData }
|
||||
}
|
||||
}
|
||||
impl<N: ItemTreeNode> Copy for FileItemTreeId<N> {}
|
||||
|
||||
impl<N: ItemTreeNode> PartialEq for FileItemTreeId<N> {
|
||||
fn eq(&self, other: &FileItemTreeId<N>) -> bool {
|
||||
self.index == other.index
|
||||
}
|
||||
}
|
||||
impl<N: ItemTreeNode> Eq for FileItemTreeId<N> {}
|
||||
|
||||
impl<N: ItemTreeNode> Hash for FileItemTreeId<N> {
|
||||
fn hash<H: Hasher>(&self, state: &mut H) {
|
||||
self.index.hash(state)
|
||||
}
|
||||
}
|
||||
|
||||
impl<N: ItemTreeNode> fmt::Debug for FileItemTreeId<N> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
self.index.fmt(f)
|
||||
}
|
||||
}
|
||||
|
||||
pub type ItemTreeId<N> = InFile<FileItemTreeId<N>>;
|
||||
|
||||
macro_rules! mod_items {
|
||||
( $( $typ:ident in $fld:ident -> $ast:ty ),+ $(,)? ) => {
|
||||
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
|
||||
pub enum ModItem {
|
||||
$(
|
||||
$typ(FileItemTreeId<$typ>),
|
||||
)+
|
||||
}
|
||||
|
||||
$(
|
||||
impl From<FileItemTreeId<$typ>> for ModItem {
|
||||
fn from(id: FileItemTreeId<$typ>) -> ModItem {
|
||||
ModItem::$typ(id)
|
||||
}
|
||||
}
|
||||
)+
|
||||
|
||||
$(
|
||||
impl ItemTreeNode for $typ {
|
||||
type Source = $ast;
|
||||
|
||||
fn ast_id(&self) -> FileAstId<Self::Source> {
|
||||
self.ast_id
|
||||
}
|
||||
|
||||
fn lookup(tree: &ItemTree, index: Idx<Self>) -> &Self {
|
||||
&tree.data().$fld[index]
|
||||
}
|
||||
|
||||
fn id_from_mod_item(mod_item: ModItem) -> Option<FileItemTreeId<Self>> {
|
||||
if let ModItem::$typ(id) = mod_item {
|
||||
Some(id)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
fn id_to_mod_item(id: FileItemTreeId<Self>) -> ModItem {
|
||||
ModItem::$typ(id)
|
||||
}
|
||||
}
|
||||
|
||||
impl Index<Idx<$typ>> for ItemTree {
|
||||
type Output = $typ;
|
||||
|
||||
fn index(&self, index: Idx<$typ>) -> &Self::Output {
|
||||
&self.data().$fld[index]
|
||||
}
|
||||
}
|
||||
)+
|
||||
};
|
||||
}
|
||||
|
||||
mod_items! {
|
||||
Import in imports -> ast::UseItem,
|
||||
ExternCrate in extern_crates -> ast::ExternCrateItem,
|
||||
Function in functions -> ast::FnDef,
|
||||
Struct in structs -> ast::StructDef,
|
||||
Union in unions -> ast::UnionDef,
|
||||
Enum in enums -> ast::EnumDef,
|
||||
Const in consts -> ast::ConstDef,
|
||||
Static in statics -> ast::StaticDef,
|
||||
Trait in traits -> ast::TraitDef,
|
||||
Impl in impls -> ast::ImplDef,
|
||||
TypeAlias in type_aliases -> ast::TypeAliasDef,
|
||||
Mod in mods -> ast::Module,
|
||||
MacroCall in macro_calls -> ast::MacroCall,
|
||||
}
|
||||
|
||||
macro_rules! impl_index {
|
||||
( $($fld:ident: $t:ty),+ $(,)? ) => {
|
||||
$(
|
||||
impl Index<Idx<$t>> for ItemTree {
|
||||
type Output = $t;
|
||||
|
||||
fn index(&self, index: Idx<$t>) -> &Self::Output {
|
||||
&self.data().$fld[index]
|
||||
}
|
||||
}
|
||||
)+
|
||||
};
|
||||
}
|
||||
|
||||
impl_index!(fields: Field, variants: Variant, exprs: Expr);
|
||||
|
||||
impl Index<RawVisibilityId> for ItemTree {
|
||||
type Output = RawVisibility;
|
||||
fn index(&self, index: RawVisibilityId) -> &Self::Output {
|
||||
match index {
|
||||
RawVisibilityId::PRIV => &VIS_PRIV,
|
||||
RawVisibilityId::PUB => &VIS_PUB,
|
||||
RawVisibilityId::PUB_CRATE => &VIS_PUB_CRATE,
|
||||
_ => &self.data().vis.arena[Idx::from_raw(index.0.into())],
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Index<GenericParamsId> for ItemTree {
|
||||
type Output = GenericParams;
|
||||
|
||||
fn index(&self, index: GenericParamsId) -> &Self::Output {
|
||||
match index {
|
||||
GenericParamsId::EMPTY => &EMPTY_GENERICS,
|
||||
_ => &self.data().generics.arena[Idx::from_raw(index.0.into())],
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<N: ItemTreeNode> Index<FileItemTreeId<N>> for ItemTree {
|
||||
type Output = N;
|
||||
fn index(&self, id: FileItemTreeId<N>) -> &N {
|
||||
N::lookup(self, id.index)
|
||||
}
|
||||
}
|
||||
|
||||
/// A desugared `use` import.
|
||||
#[derive(Debug, Clone, Eq, PartialEq)]
|
||||
pub struct Import {
|
||||
pub path: ModPath,
|
||||
pub alias: Option<ImportAlias>,
|
||||
pub visibility: RawVisibilityId,
|
||||
pub is_glob: bool,
|
||||
pub is_prelude: bool,
|
||||
/// AST ID of the `use` or `extern crate` item this import was derived from. Note that many
|
||||
/// `Import`s can map to the same `use` item.
|
||||
pub ast_id: FileAstId<ast::UseItem>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Eq, PartialEq)]
|
||||
pub struct ExternCrate {
|
||||
pub path: ModPath,
|
||||
pub alias: Option<ImportAlias>,
|
||||
pub visibility: RawVisibilityId,
|
||||
/// Whether this is a `#[macro_use] extern crate ...`.
|
||||
pub is_macro_use: bool,
|
||||
pub ast_id: FileAstId<ast::ExternCrateItem>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Eq, PartialEq)]
|
||||
pub struct Function {
|
||||
pub name: Name,
|
||||
pub visibility: RawVisibilityId,
|
||||
pub generic_params: GenericParamsId,
|
||||
pub has_self_param: bool,
|
||||
pub is_unsafe: bool,
|
||||
pub params: Box<[TypeRef]>,
|
||||
pub ret_type: TypeRef,
|
||||
pub ast_id: FileAstId<ast::FnDef>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Eq, PartialEq)]
|
||||
pub struct Struct {
|
||||
pub name: Name,
|
||||
pub visibility: RawVisibilityId,
|
||||
pub generic_params: GenericParamsId,
|
||||
pub fields: Fields,
|
||||
pub ast_id: FileAstId<ast::StructDef>,
|
||||
pub kind: StructDefKind,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Eq, PartialEq)]
|
||||
pub enum StructDefKind {
|
||||
/// `struct S { ... }` - type namespace only.
|
||||
Record,
|
||||
/// `struct S(...);`
|
||||
Tuple,
|
||||
/// `struct S;`
|
||||
Unit,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Eq, PartialEq)]
|
||||
pub struct Union {
|
||||
pub name: Name,
|
||||
pub visibility: RawVisibilityId,
|
||||
pub generic_params: GenericParamsId,
|
||||
pub fields: Fields,
|
||||
pub ast_id: FileAstId<ast::UnionDef>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Eq, PartialEq)]
|
||||
pub struct Enum {
|
||||
pub name: Name,
|
||||
pub visibility: RawVisibilityId,
|
||||
pub generic_params: GenericParamsId,
|
||||
pub variants: Range<Idx<Variant>>,
|
||||
pub ast_id: FileAstId<ast::EnumDef>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Eq, PartialEq)]
|
||||
pub struct Const {
|
||||
/// const _: () = ();
|
||||
pub name: Option<Name>,
|
||||
pub visibility: RawVisibilityId,
|
||||
pub type_ref: TypeRef,
|
||||
pub ast_id: FileAstId<ast::ConstDef>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Eq, PartialEq)]
|
||||
pub struct Static {
|
||||
pub name: Name,
|
||||
pub visibility: RawVisibilityId,
|
||||
pub mutable: bool,
|
||||
pub type_ref: TypeRef,
|
||||
pub ast_id: FileAstId<ast::StaticDef>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Eq, PartialEq)]
|
||||
pub struct Trait {
|
||||
pub name: Name,
|
||||
pub visibility: RawVisibilityId,
|
||||
pub generic_params: GenericParamsId,
|
||||
pub auto: bool,
|
||||
pub items: Box<[AssocItem]>,
|
||||
pub ast_id: FileAstId<ast::TraitDef>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Eq, PartialEq)]
|
||||
pub struct Impl {
|
||||
pub generic_params: GenericParamsId,
|
||||
pub target_trait: Option<TypeRef>,
|
||||
pub target_type: TypeRef,
|
||||
pub is_negative: bool,
|
||||
pub items: Box<[AssocItem]>,
|
||||
pub ast_id: FileAstId<ast::ImplDef>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct TypeAlias {
|
||||
pub name: Name,
|
||||
pub visibility: RawVisibilityId,
|
||||
/// Bounds on the type alias itself. Only valid in trait declarations, eg. `type Assoc: Copy;`.
|
||||
pub bounds: Box<[TypeBound]>,
|
||||
pub generic_params: GenericParamsId,
|
||||
pub type_ref: Option<TypeRef>,
|
||||
pub ast_id: FileAstId<ast::TypeAliasDef>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Eq, PartialEq)]
|
||||
pub struct Mod {
|
||||
pub name: Name,
|
||||
pub visibility: RawVisibilityId,
|
||||
pub kind: ModKind,
|
||||
pub ast_id: FileAstId<ast::Module>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Eq, PartialEq)]
|
||||
pub enum ModKind {
|
||||
/// `mod m { ... }`
|
||||
Inline { items: Box<[ModItem]> },
|
||||
|
||||
/// `mod m;`
|
||||
Outline {},
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Eq, PartialEq)]
|
||||
pub struct MacroCall {
|
||||
/// For `macro_rules!` declarations, this is the name of the declared macro.
|
||||
pub name: Option<Name>,
|
||||
/// Path to the called macro.
|
||||
pub path: ModPath,
|
||||
/// Has `#[macro_export]`.
|
||||
pub is_export: bool,
|
||||
/// Has `#[macro_export(local_inner_macros)]`.
|
||||
pub is_local_inner: bool,
|
||||
/// Has `#[rustc_builtin_macro]`.
|
||||
pub is_builtin: bool,
|
||||
pub ast_id: FileAstId<ast::MacroCall>,
|
||||
}
|
||||
|
||||
// NB: There's no `FileAstId` for `Expr`. The only case where this would be useful is for array
|
||||
// lengths, but we don't do much with them yet.
|
||||
#[derive(Debug, Clone, Eq, PartialEq)]
|
||||
pub struct Expr;
|
||||
|
||||
macro_rules! impl_froms {
|
||||
($e:ident { $($v:ident ($t:ty)),* $(,)? }) => {
|
||||
$(
|
||||
impl From<$t> for $e {
|
||||
fn from(it: $t) -> $e {
|
||||
$e::$v(it)
|
||||
}
|
||||
}
|
||||
)*
|
||||
}
|
||||
}
|
||||
|
||||
impl ModItem {
|
||||
pub fn as_assoc_item(&self) -> Option<AssocItem> {
|
||||
match self {
|
||||
ModItem::Import(_)
|
||||
| ModItem::ExternCrate(_)
|
||||
| ModItem::Struct(_)
|
||||
| ModItem::Union(_)
|
||||
| ModItem::Enum(_)
|
||||
| ModItem::Static(_)
|
||||
| ModItem::Trait(_)
|
||||
| ModItem::Impl(_)
|
||||
| ModItem::Mod(_) => None,
|
||||
ModItem::MacroCall(call) => Some(AssocItem::MacroCall(*call)),
|
||||
ModItem::Const(konst) => Some(AssocItem::Const(*konst)),
|
||||
ModItem::TypeAlias(alias) => Some(AssocItem::TypeAlias(*alias)),
|
||||
ModItem::Function(func) => Some(AssocItem::Function(*func)),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn downcast<N: ItemTreeNode>(self) -> Option<FileItemTreeId<N>> {
|
||||
N::id_from_mod_item(self)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
|
||||
pub enum AssocItem {
|
||||
Function(FileItemTreeId<Function>),
|
||||
TypeAlias(FileItemTreeId<TypeAlias>),
|
||||
Const(FileItemTreeId<Const>),
|
||||
MacroCall(FileItemTreeId<MacroCall>),
|
||||
}
|
||||
|
||||
impl_froms!(AssocItem {
|
||||
Function(FileItemTreeId<Function>),
|
||||
TypeAlias(FileItemTreeId<TypeAlias>),
|
||||
Const(FileItemTreeId<Const>),
|
||||
MacroCall(FileItemTreeId<MacroCall>),
|
||||
});
|
||||
|
||||
impl From<AssocItem> for ModItem {
|
||||
fn from(item: AssocItem) -> Self {
|
||||
match item {
|
||||
AssocItem::Function(it) => it.into(),
|
||||
AssocItem::TypeAlias(it) => it.into(),
|
||||
AssocItem::Const(it) => it.into(),
|
||||
AssocItem::MacroCall(it) => it.into(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Eq, PartialEq)]
|
||||
pub struct Variant {
|
||||
pub name: Name,
|
||||
pub fields: Fields,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub enum Fields {
|
||||
Record(Range<Idx<Field>>),
|
||||
Tuple(Range<Idx<Field>>),
|
||||
Unit,
|
||||
}
|
||||
|
||||
/// A single field of an enum variant or struct
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct Field {
|
||||
pub name: Name,
|
||||
pub type_ref: TypeRef,
|
||||
pub visibility: RawVisibilityId,
|
||||
}
|
695
crates/ra_hir_def/src/item_tree/lower.rs
Normal file
695
crates/ra_hir_def/src/item_tree/lower.rs
Normal file
|
@ -0,0 +1,695 @@
|
|||
//! AST -> `ItemTree` lowering code.
|
||||
|
||||
use super::*;
|
||||
use crate::{
|
||||
attr::Attrs,
|
||||
generics::{GenericParams, TypeParamData, TypeParamProvenance},
|
||||
};
|
||||
use hir_expand::{ast_id_map::AstIdMap, hygiene::Hygiene, HirFileId};
|
||||
use ra_arena::map::ArenaMap;
|
||||
use ra_syntax::{
|
||||
ast::{self, ModuleItemOwner},
|
||||
SyntaxNode,
|
||||
};
|
||||
use smallvec::SmallVec;
|
||||
use std::{collections::hash_map::Entry, mem, sync::Arc};
|
||||
|
||||
fn id<N: ItemTreeNode>(index: Idx<N>) -> FileItemTreeId<N> {
|
||||
FileItemTreeId { index, _p: PhantomData }
|
||||
}
|
||||
|
||||
struct ModItems(SmallVec<[ModItem; 1]>);
|
||||
|
||||
impl<T> From<T> for ModItems
|
||||
where
|
||||
T: Into<ModItem>,
|
||||
{
|
||||
fn from(t: T) -> Self {
|
||||
ModItems(SmallVec::from_buf([t.into(); 1]))
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) struct Ctx {
|
||||
tree: ItemTree,
|
||||
hygiene: Hygiene,
|
||||
file: HirFileId,
|
||||
source_ast_id_map: Arc<AstIdMap>,
|
||||
body_ctx: crate::body::LowerCtx,
|
||||
inner_items: Vec<ModItem>,
|
||||
forced_visibility: Option<RawVisibilityId>,
|
||||
}
|
||||
|
||||
impl Ctx {
|
||||
pub(super) fn new(db: &dyn DefDatabase, hygiene: Hygiene, file: HirFileId) -> Self {
|
||||
Self {
|
||||
tree: ItemTree::empty(),
|
||||
hygiene,
|
||||
file,
|
||||
source_ast_id_map: db.ast_id_map(file),
|
||||
body_ctx: crate::body::LowerCtx::new(db, file),
|
||||
inner_items: Vec::new(),
|
||||
forced_visibility: None,
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn lower_module_items(mut self, item_owner: &dyn ModuleItemOwner) -> ItemTree {
|
||||
self.tree.top_level = item_owner
|
||||
.items()
|
||||
.flat_map(|item| self.lower_mod_item(&item, false))
|
||||
.flat_map(|items| items.0)
|
||||
.collect();
|
||||
self.tree
|
||||
}
|
||||
|
||||
pub(super) fn lower_inner_items(mut self, within: &SyntaxNode) -> ItemTree {
|
||||
self.collect_inner_items(within);
|
||||
self.tree
|
||||
}
|
||||
|
||||
fn data(&mut self) -> &mut ItemTreeData {
|
||||
self.tree.data_mut()
|
||||
}
|
||||
|
||||
fn lower_mod_item(&mut self, item: &ast::ModuleItem, inner: bool) -> Option<ModItems> {
|
||||
assert!(inner || self.inner_items.is_empty());
|
||||
|
||||
// Collect inner items for 1-to-1-lowered items.
|
||||
match item {
|
||||
ast::ModuleItem::StructDef(_)
|
||||
| ast::ModuleItem::UnionDef(_)
|
||||
| ast::ModuleItem::EnumDef(_)
|
||||
| ast::ModuleItem::FnDef(_)
|
||||
| ast::ModuleItem::TypeAliasDef(_)
|
||||
| ast::ModuleItem::ConstDef(_)
|
||||
| ast::ModuleItem::StaticDef(_)
|
||||
| ast::ModuleItem::MacroCall(_) => {
|
||||
// Skip this if we're already collecting inner items. We'll descend into all nodes
|
||||
// already.
|
||||
if !inner {
|
||||
self.collect_inner_items(item.syntax());
|
||||
}
|
||||
}
|
||||
|
||||
// These are handled in their respective `lower_X` method (since we can't just blindly
|
||||
// walk them).
|
||||
ast::ModuleItem::TraitDef(_)
|
||||
| ast::ModuleItem::ImplDef(_)
|
||||
| ast::ModuleItem::ExternBlock(_) => {}
|
||||
|
||||
// These don't have inner items.
|
||||
ast::ModuleItem::Module(_)
|
||||
| ast::ModuleItem::ExternCrateItem(_)
|
||||
| ast::ModuleItem::UseItem(_) => {}
|
||||
};
|
||||
|
||||
let attrs = Attrs::new(item, &self.hygiene);
|
||||
let items = match item {
|
||||
ast::ModuleItem::StructDef(ast) => self.lower_struct(ast).map(Into::into),
|
||||
ast::ModuleItem::UnionDef(ast) => self.lower_union(ast).map(Into::into),
|
||||
ast::ModuleItem::EnumDef(ast) => self.lower_enum(ast).map(Into::into),
|
||||
ast::ModuleItem::FnDef(ast) => self.lower_function(ast).map(Into::into),
|
||||
ast::ModuleItem::TypeAliasDef(ast) => self.lower_type_alias(ast).map(Into::into),
|
||||
ast::ModuleItem::StaticDef(ast) => self.lower_static(ast).map(Into::into),
|
||||
ast::ModuleItem::ConstDef(ast) => Some(self.lower_const(ast).into()),
|
||||
ast::ModuleItem::Module(ast) => self.lower_module(ast).map(Into::into),
|
||||
ast::ModuleItem::TraitDef(ast) => self.lower_trait(ast).map(Into::into),
|
||||
ast::ModuleItem::ImplDef(ast) => self.lower_impl(ast).map(Into::into),
|
||||
ast::ModuleItem::UseItem(ast) => Some(ModItems(
|
||||
self.lower_use(ast).into_iter().map(Into::into).collect::<SmallVec<_>>(),
|
||||
)),
|
||||
ast::ModuleItem::ExternCrateItem(ast) => self.lower_extern_crate(ast).map(Into::into),
|
||||
ast::ModuleItem::MacroCall(ast) => self.lower_macro_call(ast).map(Into::into),
|
||||
ast::ModuleItem::ExternBlock(ast) => {
|
||||
Some(ModItems(self.lower_extern_block(ast).into_iter().collect::<SmallVec<_>>()))
|
||||
}
|
||||
};
|
||||
|
||||
if !attrs.is_empty() {
|
||||
for item in items.iter().flat_map(|items| &items.0) {
|
||||
self.add_attrs(*item, attrs.clone());
|
||||
}
|
||||
}
|
||||
|
||||
items
|
||||
}
|
||||
|
||||
fn add_attrs(&mut self, item: ModItem, attrs: Attrs) {
|
||||
match self.tree.attrs.entry(AttrOwner::ModItem(item)) {
|
||||
Entry::Occupied(mut entry) => {
|
||||
*entry.get_mut() = entry.get().merge(attrs);
|
||||
}
|
||||
Entry::Vacant(entry) => {
|
||||
entry.insert(attrs);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn collect_inner_items(&mut self, container: &SyntaxNode) {
|
||||
let forced_vis = self.forced_visibility.take();
|
||||
let mut inner_items = mem::take(&mut self.tree.inner_items);
|
||||
inner_items.extend(
|
||||
container.descendants().skip(1).filter_map(ast::ModuleItem::cast).filter_map(|item| {
|
||||
let ast_id = self.source_ast_id_map.ast_id(&item);
|
||||
Some((ast_id, self.lower_mod_item(&item, true)?.0))
|
||||
}),
|
||||
);
|
||||
self.tree.inner_items = inner_items;
|
||||
self.forced_visibility = forced_vis;
|
||||
}
|
||||
|
||||
fn lower_assoc_item(&mut self, item: &ast::ModuleItem) -> Option<AssocItem> {
|
||||
match item {
|
||||
ast::ModuleItem::FnDef(ast) => self.lower_function(ast).map(Into::into),
|
||||
ast::ModuleItem::TypeAliasDef(ast) => self.lower_type_alias(ast).map(Into::into),
|
||||
ast::ModuleItem::ConstDef(ast) => Some(self.lower_const(ast).into()),
|
||||
ast::ModuleItem::MacroCall(ast) => self.lower_macro_call(ast).map(Into::into),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
fn lower_struct(&mut self, strukt: &ast::StructDef) -> Option<FileItemTreeId<Struct>> {
|
||||
let visibility = self.lower_visibility(strukt);
|
||||
let name = strukt.name()?.as_name();
|
||||
let generic_params = self.lower_generic_params(GenericsOwner::Struct, strukt);
|
||||
let fields = self.lower_fields(&strukt.kind());
|
||||
let ast_id = self.source_ast_id_map.ast_id(strukt);
|
||||
let kind = match strukt.kind() {
|
||||
ast::StructKind::Record(_) => StructDefKind::Record,
|
||||
ast::StructKind::Tuple(_) => StructDefKind::Tuple,
|
||||
ast::StructKind::Unit => StructDefKind::Unit,
|
||||
};
|
||||
let res = Struct { name, visibility, generic_params, fields, ast_id, kind };
|
||||
Some(id(self.data().structs.alloc(res)))
|
||||
}
|
||||
|
||||
fn lower_fields(&mut self, strukt_kind: &ast::StructKind) -> Fields {
|
||||
match strukt_kind {
|
||||
ast::StructKind::Record(it) => {
|
||||
let range = self.lower_record_fields(it);
|
||||
Fields::Record(range)
|
||||
}
|
||||
ast::StructKind::Tuple(it) => {
|
||||
let range = self.lower_tuple_fields(it);
|
||||
Fields::Tuple(range)
|
||||
}
|
||||
ast::StructKind::Unit => Fields::Unit,
|
||||
}
|
||||
}
|
||||
|
||||
fn lower_record_fields(&mut self, fields: &ast::RecordFieldDefList) -> Range<Idx<Field>> {
|
||||
let start = self.next_field_idx();
|
||||
for field in fields.fields() {
|
||||
if let Some(data) = self.lower_record_field(&field) {
|
||||
self.data().fields.alloc(data);
|
||||
}
|
||||
}
|
||||
let end = self.next_field_idx();
|
||||
start..end
|
||||
}
|
||||
|
||||
fn lower_record_field(&mut self, field: &ast::RecordFieldDef) -> Option<Field> {
|
||||
let name = field.name()?.as_name();
|
||||
let visibility = self.lower_visibility(field);
|
||||
let type_ref = self.lower_type_ref(&field.ascribed_type()?);
|
||||
let res = Field { name, type_ref, visibility };
|
||||
Some(res)
|
||||
}
|
||||
|
||||
fn lower_tuple_fields(&mut self, fields: &ast::TupleFieldDefList) -> Range<Idx<Field>> {
|
||||
let start = self.next_field_idx();
|
||||
for (i, field) in fields.fields().enumerate() {
|
||||
if let Some(data) = self.lower_tuple_field(i, &field) {
|
||||
self.data().fields.alloc(data);
|
||||
}
|
||||
}
|
||||
let end = self.next_field_idx();
|
||||
start..end
|
||||
}
|
||||
|
||||
fn lower_tuple_field(&mut self, idx: usize, field: &ast::TupleFieldDef) -> Option<Field> {
|
||||
let name = Name::new_tuple_field(idx);
|
||||
let visibility = self.lower_visibility(field);
|
||||
let type_ref = self.lower_type_ref(&field.type_ref()?);
|
||||
let res = Field { name, type_ref, visibility };
|
||||
Some(res)
|
||||
}
|
||||
|
||||
fn lower_union(&mut self, union: &ast::UnionDef) -> Option<FileItemTreeId<Union>> {
|
||||
let visibility = self.lower_visibility(union);
|
||||
let name = union.name()?.as_name();
|
||||
let generic_params = self.lower_generic_params(GenericsOwner::Union, union);
|
||||
let fields = match union.record_field_def_list() {
|
||||
Some(record_field_def_list) => {
|
||||
self.lower_fields(&StructKind::Record(record_field_def_list))
|
||||
}
|
||||
None => Fields::Record(self.next_field_idx()..self.next_field_idx()),
|
||||
};
|
||||
let ast_id = self.source_ast_id_map.ast_id(union);
|
||||
let res = Union { name, visibility, generic_params, fields, ast_id };
|
||||
Some(id(self.data().unions.alloc(res)))
|
||||
}
|
||||
|
||||
fn lower_enum(&mut self, enum_: &ast::EnumDef) -> Option<FileItemTreeId<Enum>> {
|
||||
let visibility = self.lower_visibility(enum_);
|
||||
let name = enum_.name()?.as_name();
|
||||
let generic_params = self.lower_generic_params(GenericsOwner::Enum, enum_);
|
||||
let variants = match &enum_.variant_list() {
|
||||
Some(variant_list) => self.lower_variants(variant_list),
|
||||
None => self.next_variant_idx()..self.next_variant_idx(),
|
||||
};
|
||||
let ast_id = self.source_ast_id_map.ast_id(enum_);
|
||||
let res = Enum { name, visibility, generic_params, variants, ast_id };
|
||||
Some(id(self.data().enums.alloc(res)))
|
||||
}
|
||||
|
||||
fn lower_variants(&mut self, variants: &ast::EnumVariantList) -> Range<Idx<Variant>> {
|
||||
let start = self.next_variant_idx();
|
||||
for variant in variants.variants() {
|
||||
if let Some(data) = self.lower_variant(&variant) {
|
||||
self.data().variants.alloc(data);
|
||||
}
|
||||
}
|
||||
let end = self.next_variant_idx();
|
||||
start..end
|
||||
}
|
||||
|
||||
fn lower_variant(&mut self, variant: &ast::EnumVariant) -> Option<Variant> {
|
||||
let name = variant.name()?.as_name();
|
||||
let fields = self.lower_fields(&variant.kind());
|
||||
let res = Variant { name, fields };
|
||||
Some(res)
|
||||
}
|
||||
|
||||
fn lower_function(&mut self, func: &ast::FnDef) -> Option<FileItemTreeId<Function>> {
|
||||
let visibility = self.lower_visibility(func);
|
||||
let name = func.name()?.as_name();
|
||||
|
||||
let mut params = Vec::new();
|
||||
let mut has_self_param = false;
|
||||
if let Some(param_list) = func.param_list() {
|
||||
if let Some(self_param) = param_list.self_param() {
|
||||
let self_type = match self_param.ascribed_type() {
|
||||
Some(type_ref) => TypeRef::from_ast(&self.body_ctx, type_ref),
|
||||
None => {
|
||||
let self_type = TypeRef::Path(name![Self].into());
|
||||
match self_param.kind() {
|
||||
ast::SelfParamKind::Owned => self_type,
|
||||
ast::SelfParamKind::Ref => {
|
||||
TypeRef::Reference(Box::new(self_type), Mutability::Shared)
|
||||
}
|
||||
ast::SelfParamKind::MutRef => {
|
||||
TypeRef::Reference(Box::new(self_type), Mutability::Mut)
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
params.push(self_type);
|
||||
has_self_param = true;
|
||||
}
|
||||
for param in param_list.params() {
|
||||
let type_ref = TypeRef::from_ast_opt(&self.body_ctx, param.ascribed_type());
|
||||
params.push(type_ref);
|
||||
}
|
||||
}
|
||||
let ret_type = match func.ret_type().and_then(|rt| rt.type_ref()) {
|
||||
Some(type_ref) => TypeRef::from_ast(&self.body_ctx, type_ref),
|
||||
_ => TypeRef::unit(),
|
||||
};
|
||||
|
||||
let ret_type = if func.async_token().is_some() {
|
||||
let future_impl = desugar_future_path(ret_type);
|
||||
let ty_bound = TypeBound::Path(future_impl);
|
||||
TypeRef::ImplTrait(vec![ty_bound])
|
||||
} else {
|
||||
ret_type
|
||||
};
|
||||
|
||||
let ast_id = self.source_ast_id_map.ast_id(func);
|
||||
let mut res = Function {
|
||||
name,
|
||||
visibility,
|
||||
generic_params: GenericParamsId::EMPTY,
|
||||
has_self_param,
|
||||
is_unsafe: func.unsafe_token().is_some(),
|
||||
params: params.into_boxed_slice(),
|
||||
ret_type,
|
||||
ast_id,
|
||||
};
|
||||
res.generic_params = self.lower_generic_params(GenericsOwner::Function(&res), func);
|
||||
|
||||
Some(id(self.data().functions.alloc(res)))
|
||||
}
|
||||
|
||||
fn lower_type_alias(
|
||||
&mut self,
|
||||
type_alias: &ast::TypeAliasDef,
|
||||
) -> Option<FileItemTreeId<TypeAlias>> {
|
||||
let name = type_alias.name()?.as_name();
|
||||
let type_ref = type_alias.type_ref().map(|it| self.lower_type_ref(&it));
|
||||
let visibility = self.lower_visibility(type_alias);
|
||||
let bounds = self.lower_type_bounds(type_alias);
|
||||
let generic_params = self.lower_generic_params(GenericsOwner::TypeAlias, type_alias);
|
||||
let ast_id = self.source_ast_id_map.ast_id(type_alias);
|
||||
let res = TypeAlias {
|
||||
name,
|
||||
visibility,
|
||||
bounds: bounds.into_boxed_slice(),
|
||||
generic_params,
|
||||
type_ref,
|
||||
ast_id,
|
||||
};
|
||||
Some(id(self.data().type_aliases.alloc(res)))
|
||||
}
|
||||
|
||||
fn lower_static(&mut self, static_: &ast::StaticDef) -> Option<FileItemTreeId<Static>> {
|
||||
let name = static_.name()?.as_name();
|
||||
let type_ref = self.lower_type_ref_opt(static_.ascribed_type());
|
||||
let visibility = self.lower_visibility(static_);
|
||||
let mutable = static_.mut_token().is_some();
|
||||
let ast_id = self.source_ast_id_map.ast_id(static_);
|
||||
let res = Static { name, visibility, mutable, type_ref, ast_id };
|
||||
Some(id(self.data().statics.alloc(res)))
|
||||
}
|
||||
|
||||
fn lower_const(&mut self, konst: &ast::ConstDef) -> FileItemTreeId<Const> {
|
||||
let name = konst.name().map(|it| it.as_name());
|
||||
let type_ref = self.lower_type_ref_opt(konst.ascribed_type());
|
||||
let visibility = self.lower_visibility(konst);
|
||||
let ast_id = self.source_ast_id_map.ast_id(konst);
|
||||
let res = Const { name, visibility, type_ref, ast_id };
|
||||
id(self.data().consts.alloc(res))
|
||||
}
|
||||
|
||||
fn lower_module(&mut self, module: &ast::Module) -> Option<FileItemTreeId<Mod>> {
|
||||
let name = module.name()?.as_name();
|
||||
let visibility = self.lower_visibility(module);
|
||||
let kind = if module.semicolon_token().is_some() {
|
||||
ModKind::Outline {}
|
||||
} else {
|
||||
ModKind::Inline {
|
||||
items: module
|
||||
.item_list()
|
||||
.map(|list| {
|
||||
list.items()
|
||||
.flat_map(|item| self.lower_mod_item(&item, false))
|
||||
.flat_map(|items| items.0)
|
||||
.collect()
|
||||
})
|
||||
.unwrap_or_else(|| {
|
||||
mark::hit!(name_res_works_for_broken_modules);
|
||||
Box::new([]) as Box<[_]>
|
||||
}),
|
||||
}
|
||||
};
|
||||
let ast_id = self.source_ast_id_map.ast_id(module);
|
||||
let res = Mod { name, visibility, kind, ast_id };
|
||||
Some(id(self.data().mods.alloc(res)))
|
||||
}
|
||||
|
||||
fn lower_trait(&mut self, trait_def: &ast::TraitDef) -> Option<FileItemTreeId<Trait>> {
|
||||
let name = trait_def.name()?.as_name();
|
||||
let visibility = self.lower_visibility(trait_def);
|
||||
let generic_params =
|
||||
self.lower_generic_params_and_inner_items(GenericsOwner::Trait(trait_def), trait_def);
|
||||
let auto = trait_def.auto_token().is_some();
|
||||
let items = trait_def.item_list().map(|list| {
|
||||
self.with_inherited_visibility(visibility, |this| {
|
||||
list.items()
|
||||
.filter_map(|item| {
|
||||
let attrs = Attrs::new(&item, &this.hygiene);
|
||||
this.collect_inner_items(item.syntax());
|
||||
this.lower_assoc_item(&item).map(|item| {
|
||||
this.add_attrs(item.into(), attrs);
|
||||
item
|
||||
})
|
||||
})
|
||||
.collect()
|
||||
})
|
||||
});
|
||||
let ast_id = self.source_ast_id_map.ast_id(trait_def);
|
||||
let res = Trait {
|
||||
name,
|
||||
visibility,
|
||||
generic_params,
|
||||
auto,
|
||||
items: items.unwrap_or_default(),
|
||||
ast_id,
|
||||
};
|
||||
Some(id(self.data().traits.alloc(res)))
|
||||
}
|
||||
|
||||
fn lower_impl(&mut self, impl_def: &ast::ImplDef) -> Option<FileItemTreeId<Impl>> {
|
||||
let generic_params =
|
||||
self.lower_generic_params_and_inner_items(GenericsOwner::Impl, impl_def);
|
||||
let target_trait = impl_def.target_trait().map(|tr| self.lower_type_ref(&tr));
|
||||
let target_type = self.lower_type_ref(&impl_def.target_type()?);
|
||||
let is_negative = impl_def.excl_token().is_some();
|
||||
|
||||
// We cannot use `assoc_items()` here as that does not include macro calls.
|
||||
let items = impl_def
|
||||
.item_list()?
|
||||
.items()
|
||||
.filter_map(|item| {
|
||||
self.collect_inner_items(item.syntax());
|
||||
let assoc = self.lower_assoc_item(&item)?;
|
||||
let attrs = Attrs::new(&item, &self.hygiene);
|
||||
self.add_attrs(assoc.into(), attrs);
|
||||
Some(assoc)
|
||||
})
|
||||
.collect();
|
||||
let ast_id = self.source_ast_id_map.ast_id(impl_def);
|
||||
let res = Impl { generic_params, target_trait, target_type, is_negative, items, ast_id };
|
||||
Some(id(self.data().impls.alloc(res)))
|
||||
}
|
||||
|
||||
fn lower_use(&mut self, use_item: &ast::UseItem) -> Vec<FileItemTreeId<Import>> {
|
||||
// FIXME: cfg_attr
|
||||
let is_prelude = use_item.has_atom_attr("prelude_import");
|
||||
let visibility = self.lower_visibility(use_item);
|
||||
let ast_id = self.source_ast_id_map.ast_id(use_item);
|
||||
|
||||
// Every use item can expand to many `Import`s.
|
||||
let mut imports = Vec::new();
|
||||
let tree = self.tree.data_mut();
|
||||
ModPath::expand_use_item(
|
||||
InFile::new(self.file, use_item.clone()),
|
||||
&self.hygiene,
|
||||
|path, _tree, is_glob, alias| {
|
||||
imports.push(id(tree.imports.alloc(Import {
|
||||
path,
|
||||
alias,
|
||||
visibility,
|
||||
is_glob,
|
||||
is_prelude,
|
||||
ast_id,
|
||||
})));
|
||||
},
|
||||
);
|
||||
|
||||
imports
|
||||
}
|
||||
|
||||
fn lower_extern_crate(
|
||||
&mut self,
|
||||
extern_crate: &ast::ExternCrateItem,
|
||||
) -> Option<FileItemTreeId<ExternCrate>> {
|
||||
let path = ModPath::from_name_ref(&extern_crate.name_ref()?);
|
||||
let alias = extern_crate.alias().map(|a| {
|
||||
a.name().map(|it| it.as_name()).map_or(ImportAlias::Underscore, ImportAlias::Alias)
|
||||
});
|
||||
let visibility = self.lower_visibility(extern_crate);
|
||||
let ast_id = self.source_ast_id_map.ast_id(extern_crate);
|
||||
// FIXME: cfg_attr
|
||||
let is_macro_use = extern_crate.has_atom_attr("macro_use");
|
||||
|
||||
let res = ExternCrate { path, alias, visibility, is_macro_use, ast_id };
|
||||
Some(id(self.data().extern_crates.alloc(res)))
|
||||
}
|
||||
|
||||
fn lower_macro_call(&mut self, m: &ast::MacroCall) -> Option<FileItemTreeId<MacroCall>> {
|
||||
let name = m.name().map(|it| it.as_name());
|
||||
let attrs = Attrs::new(m, &self.hygiene);
|
||||
let path = ModPath::from_src(m.path()?, &self.hygiene)?;
|
||||
|
||||
let ast_id = self.source_ast_id_map.ast_id(m);
|
||||
|
||||
// FIXME: cfg_attr
|
||||
let export_attr = attrs.by_key("macro_export");
|
||||
|
||||
let is_export = export_attr.exists();
|
||||
let is_local_inner = if is_export {
|
||||
export_attr.tt_values().map(|it| &it.token_trees).flatten().any(|it| match it {
|
||||
tt::TokenTree::Leaf(tt::Leaf::Ident(ident)) => {
|
||||
ident.text.contains("local_inner_macros")
|
||||
}
|
||||
_ => false,
|
||||
})
|
||||
} else {
|
||||
false
|
||||
};
|
||||
|
||||
let is_builtin = attrs.by_key("rustc_builtin_macro").exists();
|
||||
let res = MacroCall { name, path, is_export, is_builtin, is_local_inner, ast_id };
|
||||
Some(id(self.data().macro_calls.alloc(res)))
|
||||
}
|
||||
|
||||
fn lower_extern_block(&mut self, block: &ast::ExternBlock) -> Vec<ModItem> {
|
||||
block.extern_item_list().map_or(Vec::new(), |list| {
|
||||
list.extern_items()
|
||||
.filter_map(|item| {
|
||||
self.collect_inner_items(item.syntax());
|
||||
let attrs = Attrs::new(&item, &self.hygiene);
|
||||
let id = match item {
|
||||
ast::ExternItem::FnDef(ast) => {
|
||||
let func = self.lower_function(&ast)?;
|
||||
func.into()
|
||||
}
|
||||
ast::ExternItem::StaticDef(ast) => {
|
||||
let statik = self.lower_static(&ast)?;
|
||||
statik.into()
|
||||
}
|
||||
};
|
||||
self.add_attrs(id, attrs);
|
||||
Some(id)
|
||||
})
|
||||
.collect()
|
||||
})
|
||||
}
|
||||
|
||||
/// Lowers generics defined on `node` and collects inner items defined within.
|
||||
fn lower_generic_params_and_inner_items(
|
||||
&mut self,
|
||||
owner: GenericsOwner<'_>,
|
||||
node: &impl ast::TypeParamsOwner,
|
||||
) -> GenericParamsId {
|
||||
// Generics are part of item headers and may contain inner items we need to collect.
|
||||
if let Some(params) = node.type_param_list() {
|
||||
self.collect_inner_items(params.syntax());
|
||||
}
|
||||
if let Some(clause) = node.where_clause() {
|
||||
self.collect_inner_items(clause.syntax());
|
||||
}
|
||||
|
||||
self.lower_generic_params(owner, node)
|
||||
}
|
||||
|
||||
fn lower_generic_params(
|
||||
&mut self,
|
||||
owner: GenericsOwner<'_>,
|
||||
node: &impl ast::TypeParamsOwner,
|
||||
) -> GenericParamsId {
|
||||
let mut sm = &mut ArenaMap::default();
|
||||
let mut generics = GenericParams::default();
|
||||
match owner {
|
||||
GenericsOwner::Function(func) => {
|
||||
generics.fill(&self.body_ctx, sm, node);
|
||||
// lower `impl Trait` in arguments
|
||||
for param in &*func.params {
|
||||
generics.fill_implicit_impl_trait_args(param);
|
||||
}
|
||||
}
|
||||
GenericsOwner::Struct
|
||||
| GenericsOwner::Enum
|
||||
| GenericsOwner::Union
|
||||
| GenericsOwner::TypeAlias => {
|
||||
generics.fill(&self.body_ctx, sm, node);
|
||||
}
|
||||
GenericsOwner::Trait(trait_def) => {
|
||||
// traits get the Self type as an implicit first type parameter
|
||||
let self_param_id = generics.types.alloc(TypeParamData {
|
||||
name: Some(name![Self]),
|
||||
default: None,
|
||||
provenance: TypeParamProvenance::TraitSelf,
|
||||
});
|
||||
sm.insert(self_param_id, Either::Left(trait_def.clone()));
|
||||
// add super traits as bounds on Self
|
||||
// i.e., trait Foo: Bar is equivalent to trait Foo where Self: Bar
|
||||
let self_param = TypeRef::Path(name![Self].into());
|
||||
generics.fill_bounds(&self.body_ctx, trait_def, self_param);
|
||||
|
||||
generics.fill(&self.body_ctx, &mut sm, node);
|
||||
}
|
||||
GenericsOwner::Impl => {
|
||||
// Note that we don't add `Self` here: in `impl`s, `Self` is not a
|
||||
// type-parameter, but rather is a type-alias for impl's target
|
||||
// type, so this is handled by the resolver.
|
||||
generics.fill(&self.body_ctx, &mut sm, node);
|
||||
}
|
||||
}
|
||||
|
||||
self.data().generics.alloc(generics)
|
||||
}
|
||||
|
||||
fn lower_type_bounds(&mut self, node: &impl ast::TypeBoundsOwner) -> Vec<TypeBound> {
|
||||
match node.type_bound_list() {
|
||||
Some(bound_list) => {
|
||||
bound_list.bounds().map(|it| TypeBound::from_ast(&self.body_ctx, it)).collect()
|
||||
}
|
||||
None => Vec::new(),
|
||||
}
|
||||
}
|
||||
|
||||
fn lower_visibility(&mut self, item: &impl ast::VisibilityOwner) -> RawVisibilityId {
|
||||
let vis = match self.forced_visibility {
|
||||
Some(vis) => return vis,
|
||||
None => RawVisibility::from_ast_with_hygiene(item.visibility(), &self.hygiene),
|
||||
};
|
||||
|
||||
self.data().vis.alloc(vis)
|
||||
}
|
||||
|
||||
fn lower_type_ref(&self, type_ref: &ast::TypeRef) -> TypeRef {
|
||||
TypeRef::from_ast(&self.body_ctx, type_ref.clone())
|
||||
}
|
||||
fn lower_type_ref_opt(&self, type_ref: Option<ast::TypeRef>) -> TypeRef {
|
||||
type_ref.map(|ty| self.lower_type_ref(&ty)).unwrap_or(TypeRef::Error)
|
||||
}
|
||||
|
||||
/// Forces the visibility `vis` to be used for all items lowered during execution of `f`.
|
||||
fn with_inherited_visibility<R>(
|
||||
&mut self,
|
||||
vis: RawVisibilityId,
|
||||
f: impl FnOnce(&mut Self) -> R,
|
||||
) -> R {
|
||||
let old = mem::replace(&mut self.forced_visibility, Some(vis));
|
||||
let res = f(self);
|
||||
self.forced_visibility = old;
|
||||
res
|
||||
}
|
||||
|
||||
fn next_field_idx(&self) -> Idx<Field> {
|
||||
Idx::from_raw(RawId::from(
|
||||
self.tree.data.as_ref().map_or(0, |data| data.fields.len() as u32),
|
||||
))
|
||||
}
|
||||
fn next_variant_idx(&self) -> Idx<Variant> {
|
||||
Idx::from_raw(RawId::from(
|
||||
self.tree.data.as_ref().map_or(0, |data| data.variants.len() as u32),
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
fn desugar_future_path(orig: TypeRef) -> Path {
|
||||
let path = path![core::future::Future];
|
||||
let mut generic_args: Vec<_> = std::iter::repeat(None).take(path.segments.len() - 1).collect();
|
||||
let mut last = GenericArgs::empty();
|
||||
let binding =
|
||||
AssociatedTypeBinding { name: name![Output], type_ref: Some(orig), bounds: Vec::new() };
|
||||
last.bindings.push(binding);
|
||||
generic_args.push(Some(Arc::new(last)));
|
||||
|
||||
Path::from_known_path(path, generic_args)
|
||||
}
|
||||
|
||||
enum GenericsOwner<'a> {
|
||||
/// We need access to the partially-lowered `Function` for lowering `impl Trait` in argument
|
||||
/// position.
|
||||
Function(&'a Function),
|
||||
Struct,
|
||||
Enum,
|
||||
Union,
|
||||
/// The `TraitDef` is needed to fill the source map for the implicit `Self` parameter.
|
||||
Trait(&'a ast::TraitDef),
|
||||
TypeAlias,
|
||||
Impl,
|
||||
}
|
435
crates/ra_hir_def/src/item_tree/tests.rs
Normal file
435
crates/ra_hir_def/src/item_tree/tests.rs
Normal file
|
@ -0,0 +1,435 @@
|
|||
use super::{ItemTree, ModItem, ModKind};
|
||||
use crate::{db::DefDatabase, test_db::TestDB};
|
||||
use hir_expand::{db::AstDatabase, HirFileId, InFile};
|
||||
use insta::assert_snapshot;
|
||||
use ra_db::fixture::WithFixture;
|
||||
use ra_syntax::{ast, AstNode};
|
||||
use rustc_hash::FxHashSet;
|
||||
use std::sync::Arc;
|
||||
use stdx::format_to;
|
||||
|
||||
fn test_inner_items(ra_fixture: &str) {
|
||||
let (db, file_id) = TestDB::with_single_file(ra_fixture);
|
||||
let file_id = HirFileId::from(file_id);
|
||||
let tree = db.item_tree(file_id);
|
||||
let root = db.parse_or_expand(file_id).unwrap();
|
||||
let ast_id_map = db.ast_id_map(file_id);
|
||||
|
||||
// Traverse the item tree and collect all module/impl/trait-level items as AST nodes.
|
||||
let mut outer_items = FxHashSet::default();
|
||||
let mut worklist = tree.top_level_items().to_vec();
|
||||
while let Some(item) = worklist.pop() {
|
||||
let node: ast::ModuleItem = match item {
|
||||
ModItem::Import(it) => tree.source(&db, InFile::new(file_id, it)).into(),
|
||||
ModItem::ExternCrate(it) => tree.source(&db, InFile::new(file_id, it)).into(),
|
||||
ModItem::Function(it) => tree.source(&db, InFile::new(file_id, it)).into(),
|
||||
ModItem::Struct(it) => tree.source(&db, InFile::new(file_id, it)).into(),
|
||||
ModItem::Union(it) => tree.source(&db, InFile::new(file_id, it)).into(),
|
||||
ModItem::Enum(it) => tree.source(&db, InFile::new(file_id, it)).into(),
|
||||
ModItem::Const(it) => tree.source(&db, InFile::new(file_id, it)).into(),
|
||||
ModItem::Static(it) => tree.source(&db, InFile::new(file_id, it)).into(),
|
||||
ModItem::TypeAlias(it) => tree.source(&db, InFile::new(file_id, it)).into(),
|
||||
ModItem::Mod(it) => {
|
||||
if let ModKind::Inline { items } = &tree[it].kind {
|
||||
worklist.extend(&**items);
|
||||
}
|
||||
tree.source(&db, InFile::new(file_id, it)).into()
|
||||
}
|
||||
ModItem::Trait(it) => {
|
||||
worklist.extend(tree[it].items.iter().map(|item| ModItem::from(*item)));
|
||||
tree.source(&db, InFile::new(file_id, it)).into()
|
||||
}
|
||||
ModItem::Impl(it) => {
|
||||
worklist.extend(tree[it].items.iter().map(|item| ModItem::from(*item)));
|
||||
tree.source(&db, InFile::new(file_id, it)).into()
|
||||
}
|
||||
ModItem::MacroCall(_) => continue,
|
||||
};
|
||||
|
||||
outer_items.insert(node);
|
||||
}
|
||||
|
||||
// Now descend the root node and check that all `ast::ModuleItem`s are either recorded above, or
|
||||
// registered as inner items.
|
||||
for item in root.descendants().skip(1).filter_map(ast::ModuleItem::cast) {
|
||||
if outer_items.contains(&item) {
|
||||
continue;
|
||||
}
|
||||
|
||||
let ast_id = ast_id_map.ast_id(&item);
|
||||
assert!(!tree.inner_items(ast_id).is_empty());
|
||||
}
|
||||
}
|
||||
|
||||
fn item_tree(ra_fixture: &str) -> Arc<ItemTree> {
|
||||
let (db, file_id) = TestDB::with_single_file(ra_fixture);
|
||||
db.item_tree(file_id.into())
|
||||
}
|
||||
|
||||
fn print_item_tree(ra_fixture: &str) -> String {
|
||||
let tree = item_tree(ra_fixture);
|
||||
let mut out = String::new();
|
||||
|
||||
format_to!(out, "inner attrs: {:?}\n\n", tree.top_level_attrs());
|
||||
format_to!(out, "top-level items:\n");
|
||||
for item in tree.top_level_items() {
|
||||
fmt_mod_item(&mut out, &tree, *item);
|
||||
format_to!(out, "\n");
|
||||
}
|
||||
|
||||
if !tree.inner_items.is_empty() {
|
||||
format_to!(out, "\ninner items:\n\n");
|
||||
for (ast_id, items) in &tree.inner_items {
|
||||
format_to!(out, "for AST {:?}:\n", ast_id);
|
||||
for inner in items {
|
||||
fmt_mod_item(&mut out, &tree, *inner);
|
||||
format_to!(out, "\n\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
out
|
||||
}
|
||||
|
||||
fn fmt_mod_item(out: &mut String, tree: &ItemTree, item: ModItem) {
|
||||
let attrs = tree.attrs(item);
|
||||
if !attrs.is_empty() {
|
||||
format_to!(out, "#[{:?}]\n", attrs);
|
||||
}
|
||||
|
||||
let mut children = String::new();
|
||||
match item {
|
||||
ModItem::ExternCrate(it) => {
|
||||
format_to!(out, "{:?}", tree[it]);
|
||||
}
|
||||
ModItem::Import(it) => {
|
||||
format_to!(out, "{:?}", tree[it]);
|
||||
}
|
||||
ModItem::Function(it) => {
|
||||
format_to!(out, "{:?}", tree[it]);
|
||||
}
|
||||
ModItem::Struct(it) => {
|
||||
format_to!(out, "{:?}", tree[it]);
|
||||
}
|
||||
ModItem::Union(it) => {
|
||||
format_to!(out, "{:?}", tree[it]);
|
||||
}
|
||||
ModItem::Enum(it) => {
|
||||
format_to!(out, "{:?}", tree[it]);
|
||||
}
|
||||
ModItem::Const(it) => {
|
||||
format_to!(out, "{:?}", tree[it]);
|
||||
}
|
||||
ModItem::Static(it) => {
|
||||
format_to!(out, "{:?}", tree[it]);
|
||||
}
|
||||
ModItem::Trait(it) => {
|
||||
format_to!(out, "{:?}", tree[it]);
|
||||
for item in &*tree[it].items {
|
||||
fmt_mod_item(&mut children, tree, ModItem::from(*item));
|
||||
format_to!(children, "\n");
|
||||
}
|
||||
}
|
||||
ModItem::Impl(it) => {
|
||||
format_to!(out, "{:?}", tree[it]);
|
||||
for item in &*tree[it].items {
|
||||
fmt_mod_item(&mut children, tree, ModItem::from(*item));
|
||||
format_to!(children, "\n");
|
||||
}
|
||||
}
|
||||
ModItem::TypeAlias(it) => {
|
||||
format_to!(out, "{:?}", tree[it]);
|
||||
}
|
||||
ModItem::Mod(it) => {
|
||||
format_to!(out, "{:?}", tree[it]);
|
||||
match &tree[it].kind {
|
||||
ModKind::Inline { items } => {
|
||||
for item in &**items {
|
||||
fmt_mod_item(&mut children, tree, *item);
|
||||
format_to!(children, "\n");
|
||||
}
|
||||
}
|
||||
ModKind::Outline {} => {}
|
||||
}
|
||||
}
|
||||
ModItem::MacroCall(it) => {
|
||||
format_to!(out, "{:?}", tree[it]);
|
||||
}
|
||||
}
|
||||
|
||||
for line in children.lines() {
|
||||
format_to!(out, "\n> {}", line);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn smoke() {
|
||||
assert_snapshot!(print_item_tree(r"
|
||||
#![attr]
|
||||
|
||||
#[attr_on_use]
|
||||
use {a, b::*};
|
||||
|
||||
#[ext_crate]
|
||||
extern crate krate;
|
||||
|
||||
#[on_trait]
|
||||
trait Tr<U> {
|
||||
#[assoc_ty]
|
||||
type AssocTy: Tr<()>;
|
||||
|
||||
#[assoc_const]
|
||||
const CONST: u8;
|
||||
|
||||
#[assoc_method]
|
||||
fn method(&self);
|
||||
|
||||
#[assoc_dfl_method]
|
||||
fn dfl_method(&mut self) {}
|
||||
}
|
||||
|
||||
#[struct0]
|
||||
struct Struct0<T = ()>;
|
||||
|
||||
#[struct1]
|
||||
struct Struct1<T>(#[struct1fld] u8);
|
||||
|
||||
#[struct2]
|
||||
struct Struct2<T> {
|
||||
#[struct2fld]
|
||||
fld: (T, ),
|
||||
}
|
||||
|
||||
#[en]
|
||||
enum En {
|
||||
#[enum_variant]
|
||||
Variant {
|
||||
#[enum_field]
|
||||
field: u8,
|
||||
},
|
||||
}
|
||||
|
||||
#[un]
|
||||
union Un {
|
||||
#[union_fld]
|
||||
fld: u16,
|
||||
}
|
||||
"), @r###"
|
||||
inner attrs: Attrs { entries: Some([Attr { path: ModPath { kind: Plain, segments: [Name(Text("attr"))] }, input: None }]) }
|
||||
|
||||
top-level items:
|
||||
#[Attrs { entries: Some([Attr { path: ModPath { kind: Plain, segments: [Name(Text("attr_on_use"))] }, input: None }]) }]
|
||||
Import { path: ModPath { kind: Plain, segments: [Name(Text("a"))] }, alias: None, visibility: RawVisibilityId("pub(self)"), is_glob: false, is_prelude: false, ast_id: FileAstId::<ra_syntax::ast::generated::nodes::UseItem>(0) }
|
||||
#[Attrs { entries: Some([Attr { path: ModPath { kind: Plain, segments: [Name(Text("attr_on_use"))] }, input: None }]) }]
|
||||
Import { path: ModPath { kind: Plain, segments: [Name(Text("b"))] }, alias: None, visibility: RawVisibilityId("pub(self)"), is_glob: true, is_prelude: false, ast_id: FileAstId::<ra_syntax::ast::generated::nodes::UseItem>(0) }
|
||||
#[Attrs { entries: Some([Attr { path: ModPath { kind: Plain, segments: [Name(Text("ext_crate"))] }, input: None }]) }]
|
||||
ExternCrate { path: ModPath { kind: Plain, segments: [Name(Text("krate"))] }, alias: None, visibility: RawVisibilityId("pub(self)"), is_macro_use: false, ast_id: FileAstId::<ra_syntax::ast::generated::nodes::ExternCrateItem>(1) }
|
||||
#[Attrs { entries: Some([Attr { path: ModPath { kind: Plain, segments: [Name(Text("on_trait"))] }, input: None }]) }]
|
||||
Trait { name: Name(Text("Tr")), visibility: RawVisibilityId("pub(self)"), generic_params: GenericParamsId(0), auto: false, items: [TypeAlias(Idx::<TypeAlias>(0)), Const(Idx::<Const>(0)), Function(Idx::<Function>(0)), Function(Idx::<Function>(1))], ast_id: FileAstId::<ra_syntax::ast::generated::nodes::TraitDef>(2) }
|
||||
> #[Attrs { entries: Some([Attr { path: ModPath { kind: Plain, segments: [Name(Text("assoc_ty"))] }, input: None }]) }]
|
||||
> TypeAlias { name: Name(Text("AssocTy")), visibility: RawVisibilityId("pub(self)"), bounds: [Path(Path { type_anchor: None, mod_path: ModPath { kind: Plain, segments: [Name(Text("Tr"))] }, generic_args: [Some(GenericArgs { args: [Type(Tuple([]))], has_self_type: false, bindings: [] })] })], generic_params: GenericParamsId(4294967295), type_ref: None, ast_id: FileAstId::<ra_syntax::ast::generated::nodes::TypeAliasDef>(8) }
|
||||
> #[Attrs { entries: Some([Attr { path: ModPath { kind: Plain, segments: [Name(Text("assoc_const"))] }, input: None }]) }]
|
||||
> Const { name: Some(Name(Text("CONST"))), visibility: RawVisibilityId("pub(self)"), type_ref: Path(Path { type_anchor: None, mod_path: ModPath { kind: Plain, segments: [Name(Text("u8"))] }, generic_args: [None] }), ast_id: FileAstId::<ra_syntax::ast::generated::nodes::ConstDef>(9) }
|
||||
> #[Attrs { entries: Some([Attr { path: ModPath { kind: Plain, segments: [Name(Text("assoc_method"))] }, input: None }]) }]
|
||||
> Function { name: Name(Text("method")), visibility: RawVisibilityId("pub(self)"), generic_params: GenericParamsId(4294967295), has_self_param: true, is_unsafe: false, params: [Reference(Path(Path { type_anchor: None, mod_path: ModPath { kind: Plain, segments: [Name(Text("Self"))] }, generic_args: [None] }), Shared)], ret_type: Tuple([]), ast_id: FileAstId::<ra_syntax::ast::generated::nodes::FnDef>(10) }
|
||||
> #[Attrs { entries: Some([Attr { path: ModPath { kind: Plain, segments: [Name(Text("assoc_dfl_method"))] }, input: None }]) }]
|
||||
> Function { name: Name(Text("dfl_method")), visibility: RawVisibilityId("pub(self)"), generic_params: GenericParamsId(4294967295), has_self_param: true, is_unsafe: false, params: [Reference(Path(Path { type_anchor: None, mod_path: ModPath { kind: Plain, segments: [Name(Text("Self"))] }, generic_args: [None] }), Mut)], ret_type: Tuple([]), ast_id: FileAstId::<ra_syntax::ast::generated::nodes::FnDef>(11) }
|
||||
#[Attrs { entries: Some([Attr { path: ModPath { kind: Plain, segments: [Name(Text("struct0"))] }, input: None }]) }]
|
||||
Struct { name: Name(Text("Struct0")), visibility: RawVisibilityId("pub(self)"), generic_params: GenericParamsId(1), fields: Unit, ast_id: FileAstId::<ra_syntax::ast::generated::nodes::StructDef>(3), kind: Unit }
|
||||
#[Attrs { entries: Some([Attr { path: ModPath { kind: Plain, segments: [Name(Text("struct1"))] }, input: None }]) }]
|
||||
Struct { name: Name(Text("Struct1")), visibility: RawVisibilityId("pub(self)"), generic_params: GenericParamsId(2), fields: Tuple(Idx::<Field>(0)..Idx::<Field>(1)), ast_id: FileAstId::<ra_syntax::ast::generated::nodes::StructDef>(4), kind: Tuple }
|
||||
#[Attrs { entries: Some([Attr { path: ModPath { kind: Plain, segments: [Name(Text("struct2"))] }, input: None }]) }]
|
||||
Struct { name: Name(Text("Struct2")), visibility: RawVisibilityId("pub(self)"), generic_params: GenericParamsId(3), fields: Record(Idx::<Field>(1)..Idx::<Field>(2)), ast_id: FileAstId::<ra_syntax::ast::generated::nodes::StructDef>(5), kind: Record }
|
||||
#[Attrs { entries: Some([Attr { path: ModPath { kind: Plain, segments: [Name(Text("en"))] }, input: None }]) }]
|
||||
Enum { name: Name(Text("En")), visibility: RawVisibilityId("pub(self)"), generic_params: GenericParamsId(4294967295), variants: Idx::<Variant>(0)..Idx::<Variant>(1), ast_id: FileAstId::<ra_syntax::ast::generated::nodes::EnumDef>(6) }
|
||||
#[Attrs { entries: Some([Attr { path: ModPath { kind: Plain, segments: [Name(Text("un"))] }, input: None }]) }]
|
||||
Union { name: Name(Text("Un")), visibility: RawVisibilityId("pub(self)"), generic_params: GenericParamsId(4294967295), fields: Record(Idx::<Field>(3)..Idx::<Field>(4)), ast_id: FileAstId::<ra_syntax::ast::generated::nodes::UnionDef>(7) }
|
||||
"###);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn simple_inner_items() {
|
||||
let tree = print_item_tree(
|
||||
r"
|
||||
impl<T:A> D for Response<T> {
|
||||
fn foo() {
|
||||
end();
|
||||
fn end<W: Write>() {
|
||||
let _x: T = loop {};
|
||||
}
|
||||
}
|
||||
}
|
||||
",
|
||||
);
|
||||
|
||||
assert_snapshot!(tree, @r###"
|
||||
inner attrs: Attrs { entries: None }
|
||||
|
||||
top-level items:
|
||||
Impl { generic_params: GenericParamsId(0), target_trait: Some(Path(Path { type_anchor: None, mod_path: ModPath { kind: Plain, segments: [Name(Text("D"))] }, generic_args: [None] })), target_type: Path(Path { type_anchor: None, mod_path: ModPath { kind: Plain, segments: [Name(Text("Response"))] }, generic_args: [Some(GenericArgs { args: [Type(Path(Path { type_anchor: None, mod_path: ModPath { kind: Plain, segments: [Name(Text("T"))] }, generic_args: [None] }))], has_self_type: false, bindings: [] })] }), is_negative: false, items: [Function(Idx::<Function>(1))], ast_id: FileAstId::<ra_syntax::ast::generated::nodes::ImplDef>(0) }
|
||||
> Function { name: Name(Text("foo")), visibility: RawVisibilityId("pub(self)"), generic_params: GenericParamsId(4294967295), has_self_param: false, is_unsafe: false, params: [], ret_type: Tuple([]), ast_id: FileAstId::<ra_syntax::ast::generated::nodes::FnDef>(1) }
|
||||
|
||||
inner items:
|
||||
|
||||
for AST FileAstId::<ra_syntax::ast::generated::nodes::ModuleItem>(2):
|
||||
Function { name: Name(Text("end")), visibility: RawVisibilityId("pub(self)"), generic_params: GenericParamsId(1), has_self_param: false, is_unsafe: false, params: [], ret_type: Tuple([]), ast_id: FileAstId::<ra_syntax::ast::generated::nodes::FnDef>(2) }
|
||||
|
||||
"###);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn extern_attrs() {
|
||||
let tree = print_item_tree(
|
||||
r#"
|
||||
#[block_attr]
|
||||
extern "C" {
|
||||
#[attr_a]
|
||||
fn a() {}
|
||||
#[attr_b]
|
||||
fn b() {}
|
||||
}
|
||||
"#,
|
||||
);
|
||||
|
||||
assert_snapshot!(tree, @r###"
|
||||
inner attrs: Attrs { entries: None }
|
||||
|
||||
top-level items:
|
||||
#[Attrs { entries: Some([Attr { path: ModPath { kind: Plain, segments: [Name(Text("attr_a"))] }, input: None }, Attr { path: ModPath { kind: Plain, segments: [Name(Text("block_attr"))] }, input: None }]) }]
|
||||
Function { name: Name(Text("a")), visibility: RawVisibilityId("pub(self)"), generic_params: GenericParamsId(4294967295), has_self_param: false, is_unsafe: false, params: [], ret_type: Tuple([]), ast_id: FileAstId::<ra_syntax::ast::generated::nodes::FnDef>(1) }
|
||||
#[Attrs { entries: Some([Attr { path: ModPath { kind: Plain, segments: [Name(Text("attr_b"))] }, input: None }, Attr { path: ModPath { kind: Plain, segments: [Name(Text("block_attr"))] }, input: None }]) }]
|
||||
Function { name: Name(Text("b")), visibility: RawVisibilityId("pub(self)"), generic_params: GenericParamsId(4294967295), has_self_param: false, is_unsafe: false, params: [], ret_type: Tuple([]), ast_id: FileAstId::<ra_syntax::ast::generated::nodes::FnDef>(2) }
|
||||
"###);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn trait_attrs() {
|
||||
let tree = print_item_tree(
|
||||
r#"
|
||||
#[trait_attr]
|
||||
trait Tr {
|
||||
#[attr_a]
|
||||
fn a() {}
|
||||
#[attr_b]
|
||||
fn b() {}
|
||||
}
|
||||
"#,
|
||||
);
|
||||
|
||||
assert_snapshot!(tree, @r###"
|
||||
inner attrs: Attrs { entries: None }
|
||||
|
||||
top-level items:
|
||||
#[Attrs { entries: Some([Attr { path: ModPath { kind: Plain, segments: [Name(Text("trait_attr"))] }, input: None }]) }]
|
||||
Trait { name: Name(Text("Tr")), visibility: RawVisibilityId("pub(self)"), generic_params: GenericParamsId(0), auto: false, items: [Function(Idx::<Function>(0)), Function(Idx::<Function>(1))], ast_id: FileAstId::<ra_syntax::ast::generated::nodes::TraitDef>(0) }
|
||||
> #[Attrs { entries: Some([Attr { path: ModPath { kind: Plain, segments: [Name(Text("attr_a"))] }, input: None }]) }]
|
||||
> Function { name: Name(Text("a")), visibility: RawVisibilityId("pub(self)"), generic_params: GenericParamsId(4294967295), has_self_param: false, is_unsafe: false, params: [], ret_type: Tuple([]), ast_id: FileAstId::<ra_syntax::ast::generated::nodes::FnDef>(1) }
|
||||
> #[Attrs { entries: Some([Attr { path: ModPath { kind: Plain, segments: [Name(Text("attr_b"))] }, input: None }]) }]
|
||||
> Function { name: Name(Text("b")), visibility: RawVisibilityId("pub(self)"), generic_params: GenericParamsId(4294967295), has_self_param: false, is_unsafe: false, params: [], ret_type: Tuple([]), ast_id: FileAstId::<ra_syntax::ast::generated::nodes::FnDef>(2) }
|
||||
"###);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn impl_attrs() {
|
||||
let tree = print_item_tree(
|
||||
r#"
|
||||
#[impl_attr]
|
||||
impl Ty {
|
||||
#[attr_a]
|
||||
fn a() {}
|
||||
#[attr_b]
|
||||
fn b() {}
|
||||
}
|
||||
"#,
|
||||
);
|
||||
|
||||
assert_snapshot!(tree, @r###"
|
||||
inner attrs: Attrs { entries: None }
|
||||
|
||||
top-level items:
|
||||
#[Attrs { entries: Some([Attr { path: ModPath { kind: Plain, segments: [Name(Text("impl_attr"))] }, input: None }]) }]
|
||||
Impl { generic_params: GenericParamsId(4294967295), target_trait: None, target_type: Path(Path { type_anchor: None, mod_path: ModPath { kind: Plain, segments: [Name(Text("Ty"))] }, generic_args: [None] }), is_negative: false, items: [Function(Idx::<Function>(0)), Function(Idx::<Function>(1))], ast_id: FileAstId::<ra_syntax::ast::generated::nodes::ImplDef>(0) }
|
||||
> #[Attrs { entries: Some([Attr { path: ModPath { kind: Plain, segments: [Name(Text("attr_a"))] }, input: None }]) }]
|
||||
> Function { name: Name(Text("a")), visibility: RawVisibilityId("pub(self)"), generic_params: GenericParamsId(4294967295), has_self_param: false, is_unsafe: false, params: [], ret_type: Tuple([]), ast_id: FileAstId::<ra_syntax::ast::generated::nodes::FnDef>(1) }
|
||||
> #[Attrs { entries: Some([Attr { path: ModPath { kind: Plain, segments: [Name(Text("attr_b"))] }, input: None }]) }]
|
||||
> Function { name: Name(Text("b")), visibility: RawVisibilityId("pub(self)"), generic_params: GenericParamsId(4294967295), has_self_param: false, is_unsafe: false, params: [], ret_type: Tuple([]), ast_id: FileAstId::<ra_syntax::ast::generated::nodes::FnDef>(2) }
|
||||
"###);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn cursed_inner_items() {
|
||||
test_inner_items(
|
||||
r"
|
||||
struct S<T: Trait = [u8; { fn f() {} 0 }]>(T);
|
||||
|
||||
enum En {
|
||||
Var1 {
|
||||
t: [(); { trait Inner {} 0 }],
|
||||
},
|
||||
|
||||
Var2([u16; { enum Inner {} 0 }]),
|
||||
}
|
||||
|
||||
type Ty = [En; { struct Inner; 0 }];
|
||||
|
||||
impl En {
|
||||
fn assoc() {
|
||||
trait InnerTrait<T = [u8; { fn f() {} }]> {}
|
||||
struct InnerStruct<T = [u8; { fn f() {} }]> {}
|
||||
impl<T = [u8; { fn f() {} }]> InnerTrait for InnerStruct {}
|
||||
}
|
||||
}
|
||||
|
||||
trait Tr<T = [u8; { fn f() {} }]> {
|
||||
type AssocTy = [u8; { fn f() {} }];
|
||||
|
||||
const AssocConst: [u8; { fn f() {} }];
|
||||
}
|
||||
",
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn inner_item_attrs() {
|
||||
let tree = print_item_tree(
|
||||
r"
|
||||
fn foo() {
|
||||
#[on_inner]
|
||||
fn inner() {}
|
||||
}
|
||||
",
|
||||
);
|
||||
|
||||
assert_snapshot!(tree, @r###"
|
||||
inner attrs: Attrs { entries: None }
|
||||
|
||||
top-level items:
|
||||
Function { name: Name(Text("foo")), visibility: RawVisibilityId("pub(self)"), generic_params: GenericParamsId(4294967295), has_self_param: false, is_unsafe: false, params: [], ret_type: Tuple([]), ast_id: FileAstId::<ra_syntax::ast::generated::nodes::FnDef>(0) }
|
||||
|
||||
inner items:
|
||||
|
||||
for AST FileAstId::<ra_syntax::ast::generated::nodes::ModuleItem>(1):
|
||||
#[Attrs { entries: Some([Attr { path: ModPath { kind: Plain, segments: [Name(Text("on_inner"))] }, input: None }]) }]
|
||||
Function { name: Name(Text("inner")), visibility: RawVisibilityId("pub(self)"), generic_params: GenericParamsId(4294967295), has_self_param: false, is_unsafe: false, params: [], ret_type: Tuple([]), ast_id: FileAstId::<ra_syntax::ast::generated::nodes::FnDef>(1) }
|
||||
|
||||
"###);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn assoc_item_macros() {
|
||||
let tree = print_item_tree(
|
||||
r"
|
||||
impl S {
|
||||
items!();
|
||||
}
|
||||
",
|
||||
);
|
||||
|
||||
assert_snapshot!(tree, @r###"
|
||||
inner attrs: Attrs { entries: None }
|
||||
|
||||
top-level items:
|
||||
Impl { generic_params: GenericParamsId(4294967295), target_trait: None, target_type: Path(Path { type_anchor: None, mod_path: ModPath { kind: Plain, segments: [Name(Text("S"))] }, generic_args: [None] }), is_negative: false, items: [MacroCall(Idx::<MacroCall>(0))], ast_id: FileAstId::<ra_syntax::ast::generated::nodes::ImplDef>(0) }
|
||||
> MacroCall { name: None, path: ModPath { kind: Plain, segments: [Name(Text("items"))] }, is_export: false, is_local_inner: false, is_builtin: false, ast_id: FileAstId::<ra_syntax::ast::generated::nodes::MacroCall>(1) }
|
||||
"###);
|
||||
}
|
|
@ -25,6 +25,8 @@ pub mod item_scope;
|
|||
pub mod dyn_map;
|
||||
pub mod keys;
|
||||
|
||||
pub mod item_tree;
|
||||
|
||||
pub mod adt;
|
||||
pub mod data;
|
||||
pub mod generics;
|
||||
|
@ -48,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,
|
||||
|
@ -56,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 {
|
||||
|
@ -70,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 {
|
||||
|
@ -104,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
|
||||
|
@ -141,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)]
|
||||
|
@ -365,7 +416,7 @@ impl HasModule for AssocContainerId {
|
|||
}
|
||||
}
|
||||
|
||||
impl<N: AstNode> HasModule for AssocItemLoc<N> {
|
||||
impl<N: ItemTreeNode> HasModule for AssocItemLoc<N> {
|
||||
fn module(&self, db: &dyn db::DefDatabase) -> ModuleId {
|
||||
self.container.module(db)
|
||||
}
|
||||
|
@ -392,6 +443,16 @@ impl HasModule for DefWithBodyId {
|
|||
}
|
||||
}
|
||||
|
||||
impl DefWithBodyId {
|
||||
pub fn as_mod_item(self, db: &dyn db::DefDatabase) -> ModItem {
|
||||
match self {
|
||||
DefWithBodyId::FunctionId(it) => it.lookup(db).id.value.into(),
|
||||
DefWithBodyId::StaticId(it) => it.lookup(db).id.value.into(),
|
||||
DefWithBodyId::ConstId(it) => it.lookup(db).id.value.into(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl HasModule for GenericDefId {
|
||||
fn module(&self, db: &dyn db::DefDatabase) -> ModuleId {
|
||||
match self {
|
||||
|
|
|
@ -47,7 +47,6 @@
|
|||
//! path and, upon success, we run macro expansion and "collect module" phase on
|
||||
//! the result
|
||||
|
||||
pub(crate) mod raw;
|
||||
mod collector;
|
||||
mod mod_resolution;
|
||||
mod path_resolution;
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
//! resolves imports and expands macros.
|
||||
|
||||
use hir_expand::{
|
||||
ast_id_map::FileAstId,
|
||||
builtin_derive::find_builtin_derive,
|
||||
builtin_macro::find_builtin_macro,
|
||||
name::{name, AsName, Name},
|
||||
|
@ -19,13 +20,16 @@ use test_utils::mark;
|
|||
use crate::{
|
||||
attr::Attrs,
|
||||
db::DefDatabase,
|
||||
item_tree::{
|
||||
self, FileItemTreeId, ItemTree, ItemTreeId, MacroCall, Mod, ModItem, ModKind, StructDefKind,
|
||||
},
|
||||
nameres::{
|
||||
diagnostics::DefDiagnostic, mod_resolution::ModDir, path_resolution::ReachedFixedPoint,
|
||||
raw, BuiltinShadowMode, CrateDefMap, ModuleData, ModuleOrigin, ResolveMode,
|
||||
BuiltinShadowMode, CrateDefMap, ModuleData, ModuleOrigin, ResolveMode,
|
||||
},
|
||||
path::{ImportAlias, ModPath, PathKind},
|
||||
per_ns::PerNs,
|
||||
visibility::Visibility,
|
||||
visibility::{RawVisibility, Visibility},
|
||||
AdtId, AsMacroCall, AstId, AstIdWithPath, ConstLoc, ContainerId, EnumLoc, EnumVariantId,
|
||||
FunctionLoc, ImplLoc, Intern, LocalModuleId, ModuleDefId, ModuleId, StaticLoc, StructLoc,
|
||||
TraitLoc, TypeAliasLoc, UnionLoc,
|
||||
|
@ -101,11 +105,51 @@ impl PartialResolvedImport {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Eq, PartialEq)]
|
||||
struct Import {
|
||||
pub path: ModPath,
|
||||
pub alias: Option<ImportAlias>,
|
||||
pub visibility: RawVisibility,
|
||||
pub is_glob: bool,
|
||||
pub is_prelude: bool,
|
||||
pub is_extern_crate: bool,
|
||||
pub is_macro_use: bool,
|
||||
}
|
||||
|
||||
impl Import {
|
||||
fn from_use(tree: &ItemTree, id: FileItemTreeId<item_tree::Import>) -> Self {
|
||||
let it = &tree[id];
|
||||
let visibility = &tree[it.visibility];
|
||||
Self {
|
||||
path: it.path.clone(),
|
||||
alias: it.alias.clone(),
|
||||
visibility: visibility.clone(),
|
||||
is_glob: it.is_glob,
|
||||
is_prelude: it.is_prelude,
|
||||
is_extern_crate: false,
|
||||
is_macro_use: false,
|
||||
}
|
||||
}
|
||||
|
||||
fn from_extern_crate(tree: &ItemTree, id: FileItemTreeId<item_tree::ExternCrate>) -> Self {
|
||||
let it = &tree[id];
|
||||
let visibility = &tree[it.visibility];
|
||||
Self {
|
||||
path: it.path.clone(),
|
||||
alias: it.alias.clone(),
|
||||
visibility: visibility.clone(),
|
||||
is_glob: false,
|
||||
is_prelude: false,
|
||||
is_extern_crate: true,
|
||||
is_macro_use: it.is_macro_use,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Eq, PartialEq)]
|
||||
struct ImportDirective {
|
||||
module_id: LocalModuleId,
|
||||
import_id: raw::Import,
|
||||
import: raw::ImportData,
|
||||
import: Import,
|
||||
status: PartialResolvedImport,
|
||||
}
|
||||
|
||||
|
@ -123,6 +167,13 @@ struct DeriveDirective {
|
|||
ast_id: AstIdWithPath<ast::ModuleItem>,
|
||||
}
|
||||
|
||||
struct DefData<'a> {
|
||||
id: ModuleDefId,
|
||||
name: &'a Name,
|
||||
visibility: &'a RawVisibility,
|
||||
has_constructor: bool,
|
||||
}
|
||||
|
||||
/// Walks the tree of module recursively
|
||||
struct DefCollector<'a> {
|
||||
db: &'a dyn DefDatabase,
|
||||
|
@ -140,7 +191,7 @@ struct DefCollector<'a> {
|
|||
impl DefCollector<'_> {
|
||||
fn collect(&mut self) {
|
||||
let file_id = self.db.crate_graph()[self.def_map.krate].root_file_id;
|
||||
let raw_items = self.db.raw_items(file_id.into());
|
||||
let item_tree = self.db.item_tree(file_id.into());
|
||||
let module_id = self.def_map.root;
|
||||
self.def_map.modules[module_id].origin = ModuleOrigin::CrateRoot { definition: file_id };
|
||||
ModCollector {
|
||||
|
@ -148,10 +199,10 @@ impl DefCollector<'_> {
|
|||
macro_depth: 0,
|
||||
module_id,
|
||||
file_id: file_id.into(),
|
||||
raw_items: &raw_items,
|
||||
item_tree: &item_tree,
|
||||
mod_dir: ModDir::root(),
|
||||
}
|
||||
.collect(raw_items.items());
|
||||
.collect(item_tree.top_level_items());
|
||||
|
||||
// main name resolution fixed-point loop.
|
||||
let mut i = 0;
|
||||
|
@ -286,7 +337,7 @@ impl DefCollector<'_> {
|
|||
fn import_macros_from_extern_crate(
|
||||
&mut self,
|
||||
current_module_id: LocalModuleId,
|
||||
import: &raw::ImportData,
|
||||
import: &item_tree::ExternCrate,
|
||||
) {
|
||||
log::debug!(
|
||||
"importing macros from extern crate: {:?} ({:?})",
|
||||
|
@ -352,11 +403,7 @@ impl DefCollector<'_> {
|
|||
}
|
||||
}
|
||||
|
||||
fn resolve_import(
|
||||
&self,
|
||||
module_id: LocalModuleId,
|
||||
import: &raw::ImportData,
|
||||
) -> PartialResolvedImport {
|
||||
fn resolve_import(&self, module_id: LocalModuleId, import: &Import) -> PartialResolvedImport {
|
||||
log::debug!("resolving import: {:?} ({:?})", import, self.def_map.edition);
|
||||
if import.is_extern_crate {
|
||||
let res = self.def_map.resolve_name_in_extern_prelude(
|
||||
|
@ -649,17 +696,17 @@ impl DefCollector<'_> {
|
|||
depth: usize,
|
||||
) {
|
||||
let file_id: HirFileId = macro_call_id.as_file();
|
||||
let raw_items = self.db.raw_items(file_id);
|
||||
let item_tree = self.db.item_tree(file_id);
|
||||
let mod_dir = self.mod_dirs[&module_id].clone();
|
||||
ModCollector {
|
||||
def_collector: &mut *self,
|
||||
macro_depth: depth,
|
||||
file_id,
|
||||
module_id,
|
||||
raw_items: &raw_items,
|
||||
item_tree: &item_tree,
|
||||
mod_dir,
|
||||
}
|
||||
.collect(raw_items.items());
|
||||
.collect(item_tree.top_level_items());
|
||||
}
|
||||
|
||||
fn finish(self) -> CrateDefMap {
|
||||
|
@ -673,12 +720,12 @@ struct ModCollector<'a, 'b> {
|
|||
macro_depth: usize,
|
||||
module_id: LocalModuleId,
|
||||
file_id: HirFileId,
|
||||
raw_items: &'a raw::RawItems,
|
||||
item_tree: &'a ItemTree,
|
||||
mod_dir: ModDir,
|
||||
}
|
||||
|
||||
impl ModCollector<'_, '_> {
|
||||
fn collect(&mut self, items: &[raw::RawItem]) {
|
||||
fn collect(&mut self, items: &[ModItem]) {
|
||||
// Note: don't assert that inserted value is fresh: it's simply not true
|
||||
// for macros.
|
||||
self.def_collector.mod_dirs.insert(self.module_id, self.mod_dir.clone());
|
||||
|
@ -695,64 +742,204 @@ impl ModCollector<'_, '_> {
|
|||
// `#[macro_use] extern crate` is hoisted to imports macros before collecting
|
||||
// any other items.
|
||||
for item in items {
|
||||
if self.is_cfg_enabled(&item.attrs) {
|
||||
if let raw::RawItemKind::Import(import_id) = item.kind {
|
||||
let import = self.raw_items[import_id].clone();
|
||||
if import.is_extern_crate && import.is_macro_use {
|
||||
if self.is_cfg_enabled(self.item_tree.attrs(*item)) {
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for item in items {
|
||||
if self.is_cfg_enabled(&item.attrs) {
|
||||
match item.kind {
|
||||
raw::RawItemKind::Module(m) => {
|
||||
self.collect_module(&self.raw_items[m], &item.attrs)
|
||||
}
|
||||
raw::RawItemKind::Import(import_id) => {
|
||||
for &item in items {
|
||||
let attrs = self.item_tree.attrs(item);
|
||||
if self.is_cfg_enabled(attrs) {
|
||||
let module =
|
||||
ModuleId { krate: self.def_collector.def_map.krate, local_id: self.module_id };
|
||||
let container = ContainerId::ModuleId(module);
|
||||
|
||||
let mut def = None;
|
||||
match item {
|
||||
ModItem::Mod(m) => self.collect_module(&self.item_tree[m], attrs),
|
||||
ModItem::Import(import_id) => {
|
||||
self.def_collector.unresolved_imports.push(ImportDirective {
|
||||
module_id: self.module_id,
|
||||
import_id,
|
||||
import: self.raw_items[import_id].clone(),
|
||||
import: Import::from_use(&self.item_tree, import_id),
|
||||
status: PartialResolvedImport::Unresolved,
|
||||
})
|
||||
}
|
||||
raw::RawItemKind::Def(def) => {
|
||||
self.define_def(&self.raw_items[def], &item.attrs)
|
||||
ModItem::ExternCrate(import_id) => {
|
||||
self.def_collector.unresolved_imports.push(ImportDirective {
|
||||
module_id: self.module_id,
|
||||
import: Import::from_extern_crate(&self.item_tree, import_id),
|
||||
status: PartialResolvedImport::Unresolved,
|
||||
})
|
||||
}
|
||||
raw::RawItemKind::Macro(mac) => self.collect_macro(&self.raw_items[mac]),
|
||||
raw::RawItemKind::Impl(imp) => {
|
||||
ModItem::MacroCall(mac) => self.collect_macro(&self.item_tree[mac]),
|
||||
ModItem::Impl(imp) => {
|
||||
let module = ModuleId {
|
||||
krate: self.def_collector.def_map.krate,
|
||||
local_id: self.module_id,
|
||||
};
|
||||
let container = ContainerId::ModuleId(module);
|
||||
let ast_id = self.raw_items[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(id) => {
|
||||
let func = &self.item_tree[id];
|
||||
def = Some(DefData {
|
||||
id: FunctionLoc {
|
||||
container: container.into(),
|
||||
id: ItemTreeId::new(self.file_id, id),
|
||||
}
|
||||
.intern(self.def_collector.db)
|
||||
.into(),
|
||||
name: &func.name,
|
||||
visibility: &self.item_tree[func.visibility],
|
||||
has_constructor: false,
|
||||
});
|
||||
}
|
||||
ModItem::Struct(id) => {
|
||||
let it = &self.item_tree[id];
|
||||
|
||||
// FIXME: check attrs to see if this is an attribute macro invocation;
|
||||
// in which case we don't add the invocation, just a single attribute
|
||||
// macro invocation
|
||||
self.collect_derives(attrs, it.ast_id.upcast());
|
||||
|
||||
def = Some(DefData {
|
||||
id: StructLoc { container, id: ItemTreeId::new(self.file_id, id) }
|
||||
.intern(self.def_collector.db)
|
||||
.into(),
|
||||
name: &it.name,
|
||||
visibility: &self.item_tree[it.visibility],
|
||||
has_constructor: it.kind != StructDefKind::Record,
|
||||
});
|
||||
}
|
||||
ModItem::Union(id) => {
|
||||
let it = &self.item_tree[id];
|
||||
|
||||
// FIXME: check attrs to see if this is an attribute macro invocation;
|
||||
// in which case we don't add the invocation, just a single attribute
|
||||
// macro invocation
|
||||
self.collect_derives(attrs, it.ast_id.upcast());
|
||||
|
||||
def = Some(DefData {
|
||||
id: UnionLoc { container, id: ItemTreeId::new(self.file_id, id) }
|
||||
.intern(self.def_collector.db)
|
||||
.into(),
|
||||
name: &it.name,
|
||||
visibility: &self.item_tree[it.visibility],
|
||||
has_constructor: false,
|
||||
});
|
||||
}
|
||||
ModItem::Enum(id) => {
|
||||
let it = &self.item_tree[id];
|
||||
|
||||
// FIXME: check attrs to see if this is an attribute macro invocation;
|
||||
// in which case we don't add the invocation, just a single attribute
|
||||
// macro invocation
|
||||
self.collect_derives(attrs, it.ast_id.upcast());
|
||||
|
||||
def = Some(DefData {
|
||||
id: EnumLoc { container, id: ItemTreeId::new(self.file_id, id) }
|
||||
.intern(self.def_collector.db)
|
||||
.into(),
|
||||
name: &it.name,
|
||||
visibility: &self.item_tree[it.visibility],
|
||||
has_constructor: false,
|
||||
});
|
||||
}
|
||||
ModItem::Const(id) => {
|
||||
let it = &self.item_tree[id];
|
||||
|
||||
if let Some(name) = &it.name {
|
||||
def = Some(DefData {
|
||||
id: ConstLoc {
|
||||
container: container.into(),
|
||||
id: ItemTreeId::new(self.file_id, id),
|
||||
}
|
||||
.intern(self.def_collector.db)
|
||||
.into(),
|
||||
name,
|
||||
visibility: &self.item_tree[it.visibility],
|
||||
has_constructor: false,
|
||||
});
|
||||
}
|
||||
}
|
||||
ModItem::Static(id) => {
|
||||
let it = &self.item_tree[id];
|
||||
|
||||
def = Some(DefData {
|
||||
id: StaticLoc { container, id: ItemTreeId::new(self.file_id, id) }
|
||||
.intern(self.def_collector.db)
|
||||
.into(),
|
||||
name: &it.name,
|
||||
visibility: &self.item_tree[it.visibility],
|
||||
has_constructor: false,
|
||||
});
|
||||
}
|
||||
ModItem::Trait(id) => {
|
||||
let it = &self.item_tree[id];
|
||||
|
||||
def = Some(DefData {
|
||||
id: TraitLoc { container, id: ItemTreeId::new(self.file_id, id) }
|
||||
.intern(self.def_collector.db)
|
||||
.into(),
|
||||
name: &it.name,
|
||||
visibility: &self.item_tree[it.visibility],
|
||||
has_constructor: false,
|
||||
});
|
||||
}
|
||||
ModItem::TypeAlias(id) => {
|
||||
let it = &self.item_tree[id];
|
||||
|
||||
def = Some(DefData {
|
||||
id: TypeAliasLoc {
|
||||
container: container.into(),
|
||||
id: ItemTreeId::new(self.file_id, id),
|
||||
}
|
||||
.intern(self.def_collector.db)
|
||||
.into(),
|
||||
name: &it.name,
|
||||
visibility: &self.item_tree[it.visibility],
|
||||
has_constructor: false,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(DefData { id, name, visibility, has_constructor }) = def {
|
||||
self.def_collector.def_map.modules[self.module_id].scope.define_def(id);
|
||||
let vis = self
|
||||
.def_collector
|
||||
.def_map
|
||||
.resolve_visibility(self.def_collector.db, self.module_id, visibility)
|
||||
.unwrap_or(Visibility::Public);
|
||||
self.def_collector.update(
|
||||
self.module_id,
|
||||
&[(name.clone(), PerNs::from_def(id, vis, has_constructor))],
|
||||
vis,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn collect_module(&mut self, module: &raw::ModuleData, attrs: &Attrs) {
|
||||
fn collect_module(&mut self, module: &Mod, attrs: &Attrs) {
|
||||
let path_attr = attrs.by_key("path").string_value();
|
||||
let is_macro_use = attrs.by_key("macro_use").exists();
|
||||
match module {
|
||||
match &module.kind {
|
||||
// inline module, just recurse
|
||||
raw::ModuleData::Definition { name, visibility, items, ast_id } => {
|
||||
ModKind::Inline { items } => {
|
||||
let module_id = self.push_child_module(
|
||||
name.clone(),
|
||||
AstId::new(self.file_id, *ast_id),
|
||||
module.name.clone(),
|
||||
AstId::new(self.file_id, module.ast_id),
|
||||
None,
|
||||
&visibility,
|
||||
&self.item_tree[module.visibility],
|
||||
);
|
||||
|
||||
ModCollector {
|
||||
|
@ -760,8 +947,8 @@ impl ModCollector<'_, '_> {
|
|||
macro_depth: self.macro_depth,
|
||||
module_id,
|
||||
file_id: self.file_id,
|
||||
raw_items: self.raw_items,
|
||||
mod_dir: self.mod_dir.descend_into_definition(name, path_attr),
|
||||
item_tree: self.item_tree,
|
||||
mod_dir: self.mod_dir.descend_into_definition(&module.name, path_attr),
|
||||
}
|
||||
.collect(&*items);
|
||||
if is_macro_use {
|
||||
|
@ -769,31 +956,31 @@ impl ModCollector<'_, '_> {
|
|||
}
|
||||
}
|
||||
// out of line module, resolve, parse and recurse
|
||||
raw::ModuleData::Declaration { name, visibility, ast_id } => {
|
||||
let ast_id = AstId::new(self.file_id, *ast_id);
|
||||
ModKind::Outline {} => {
|
||||
let ast_id = AstId::new(self.file_id, module.ast_id);
|
||||
match self.mod_dir.resolve_declaration(
|
||||
self.def_collector.db,
|
||||
self.file_id,
|
||||
name,
|
||||
&module.name,
|
||||
path_attr,
|
||||
) {
|
||||
Ok((file_id, is_mod_rs, mod_dir)) => {
|
||||
let module_id = self.push_child_module(
|
||||
name.clone(),
|
||||
module.name.clone(),
|
||||
ast_id,
|
||||
Some((file_id, is_mod_rs)),
|
||||
&visibility,
|
||||
&self.item_tree[module.visibility],
|
||||
);
|
||||
let raw_items = self.def_collector.db.raw_items(file_id.into());
|
||||
let item_tree = self.def_collector.db.item_tree(file_id.into());
|
||||
ModCollector {
|
||||
def_collector: &mut *self.def_collector,
|
||||
macro_depth: self.macro_depth,
|
||||
module_id,
|
||||
file_id: file_id.into(),
|
||||
raw_items: &raw_items,
|
||||
item_tree: &item_tree,
|
||||
mod_dir,
|
||||
}
|
||||
.collect(raw_items.items());
|
||||
.collect(item_tree.top_level_items());
|
||||
if is_macro_use {
|
||||
self.import_all_legacy_macros(module_id);
|
||||
}
|
||||
|
@ -842,77 +1029,7 @@ impl ModCollector<'_, '_> {
|
|||
res
|
||||
}
|
||||
|
||||
fn define_def(&mut self, def: &raw::DefData, attrs: &Attrs) {
|
||||
let module = ModuleId { krate: self.def_collector.def_map.krate, local_id: self.module_id };
|
||||
// FIXME: check attrs to see if this is an attribute macro invocation;
|
||||
// in which case we don't add the invocation, just a single attribute
|
||||
// macro invocation
|
||||
self.collect_derives(attrs, def);
|
||||
|
||||
let name = def.name.clone();
|
||||
let container = ContainerId::ModuleId(module);
|
||||
let vis = &def.visibility;
|
||||
let mut has_constructor = false;
|
||||
|
||||
let def: ModuleDefId = match def.kind {
|
||||
raw::DefKind::Function(ast_id) => FunctionLoc {
|
||||
container: container.into(),
|
||||
ast_id: AstId::new(self.file_id, ast_id),
|
||||
}
|
||||
.intern(self.def_collector.db)
|
||||
.into(),
|
||||
raw::DefKind::Struct(ast_id, mode) => {
|
||||
has_constructor = mode != raw::StructDefKind::Record;
|
||||
StructLoc { container, ast_id: AstId::new(self.file_id, ast_id) }
|
||||
.intern(self.def_collector.db)
|
||||
.into()
|
||||
}
|
||||
raw::DefKind::Union(ast_id) => {
|
||||
UnionLoc { container, ast_id: AstId::new(self.file_id, ast_id) }
|
||||
.intern(self.def_collector.db)
|
||||
.into()
|
||||
}
|
||||
raw::DefKind::Enum(ast_id) => {
|
||||
EnumLoc { container, ast_id: AstId::new(self.file_id, ast_id) }
|
||||
.intern(self.def_collector.db)
|
||||
.into()
|
||||
}
|
||||
raw::DefKind::Const(ast_id) => {
|
||||
ConstLoc { container: container.into(), ast_id: AstId::new(self.file_id, ast_id) }
|
||||
.intern(self.def_collector.db)
|
||||
.into()
|
||||
}
|
||||
raw::DefKind::Static(ast_id) => {
|
||||
StaticLoc { container, ast_id: AstId::new(self.file_id, ast_id) }
|
||||
.intern(self.def_collector.db)
|
||||
.into()
|
||||
}
|
||||
raw::DefKind::Trait(ast_id) => {
|
||||
TraitLoc { container, ast_id: AstId::new(self.file_id, ast_id) }
|
||||
.intern(self.def_collector.db)
|
||||
.into()
|
||||
}
|
||||
raw::DefKind::TypeAlias(ast_id) => TypeAliasLoc {
|
||||
container: container.into(),
|
||||
ast_id: AstId::new(self.file_id, ast_id),
|
||||
}
|
||||
.intern(self.def_collector.db)
|
||||
.into(),
|
||||
};
|
||||
self.def_collector.def_map.modules[self.module_id].scope.define_def(def);
|
||||
let vis = self
|
||||
.def_collector
|
||||
.def_map
|
||||
.resolve_visibility(self.def_collector.db, self.module_id, vis)
|
||||
.unwrap_or(Visibility::Public);
|
||||
self.def_collector.update(
|
||||
self.module_id,
|
||||
&[(name, PerNs::from_def(def, vis, has_constructor))],
|
||||
vis,
|
||||
)
|
||||
}
|
||||
|
||||
fn collect_derives(&mut self, attrs: &Attrs, def: &raw::DefData) {
|
||||
fn collect_derives(&mut self, attrs: &Attrs, ast_id: FileAstId<ast::ModuleItem>) {
|
||||
for derive_subtree in attrs.by_key("derive").tt_values() {
|
||||
// for #[derive(Copy, Clone)], `derive_subtree` is the `(Copy, Clone)` subtree
|
||||
for tt in &derive_subtree.token_trees {
|
||||
|
@ -923,7 +1040,7 @@ impl ModCollector<'_, '_> {
|
|||
};
|
||||
let path = ModPath::from_tt_ident(ident);
|
||||
|
||||
let ast_id = AstIdWithPath::new(self.file_id, def.kind.ast_id(), path);
|
||||
let ast_id = AstIdWithPath::new(self.file_id, ast_id, path);
|
||||
self.def_collector
|
||||
.unexpanded_attribute_macros
|
||||
.push(DeriveDirective { module_id: self.module_id, ast_id });
|
||||
|
@ -931,11 +1048,11 @@ impl ModCollector<'_, '_> {
|
|||
}
|
||||
}
|
||||
|
||||
fn collect_macro(&mut self, mac: &raw::MacroData) {
|
||||
fn collect_macro(&mut self, mac: &MacroCall) {
|
||||
let mut ast_id = AstIdWithPath::new(self.file_id, mac.ast_id, mac.path.clone());
|
||||
|
||||
// Case 0: builtin macros
|
||||
if mac.builtin {
|
||||
if mac.is_builtin {
|
||||
if let Some(name) = &mac.name {
|
||||
let krate = self.def_collector.def_map.krate;
|
||||
if let Some(macro_id) = find_builtin_macro(name, krate, ast_id.ast_id) {
|
||||
|
@ -943,7 +1060,7 @@ impl ModCollector<'_, '_> {
|
|||
self.module_id,
|
||||
name.clone(),
|
||||
macro_id,
|
||||
mac.export,
|
||||
mac.is_export,
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
@ -957,9 +1074,14 @@ impl ModCollector<'_, '_> {
|
|||
ast_id: Some(ast_id.ast_id),
|
||||
krate: Some(self.def_collector.def_map.krate),
|
||||
kind: MacroDefKind::Declarative,
|
||||
local_inner: mac.local_inner,
|
||||
local_inner: mac.is_local_inner,
|
||||
};
|
||||
self.def_collector.define_macro(self.module_id, name.clone(), macro_id, mac.export);
|
||||
self.def_collector.define_macro(
|
||||
self.module_id,
|
||||
name.clone(),
|
||||
macro_id,
|
||||
mac.is_export,
|
||||
);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -1,482 +0,0 @@
|
|||
//! Lowers syntax tree of a rust file into a raw representation of containing
|
||||
//! items, *without* attaching them to a module structure.
|
||||
//!
|
||||
//! That is, raw items don't have semantics, just as syntax, but, unlike syntax,
|
||||
//! they don't change with trivial source code edits, making them a great tool
|
||||
//! for building salsa recomputation firewalls.
|
||||
|
||||
use std::{ops::Index, sync::Arc};
|
||||
|
||||
use hir_expand::{
|
||||
ast_id_map::AstIdMap,
|
||||
hygiene::Hygiene,
|
||||
name::{AsName, Name},
|
||||
};
|
||||
use ra_arena::{Arena, Idx};
|
||||
use ra_prof::profile;
|
||||
use ra_syntax::{
|
||||
ast::{self, AttrsOwner, NameOwner, VisibilityOwner},
|
||||
AstNode,
|
||||
};
|
||||
use test_utils::mark;
|
||||
|
||||
use crate::{
|
||||
attr::Attrs,
|
||||
db::DefDatabase,
|
||||
path::{ImportAlias, ModPath},
|
||||
visibility::RawVisibility,
|
||||
FileAstId, HirFileId, InFile,
|
||||
};
|
||||
|
||||
/// `RawItems` is a set of top-level items in a file (except for impls).
|
||||
///
|
||||
/// It is the input to name resolution algorithm. `RawItems` are not invalidated
|
||||
/// on most edits.
|
||||
#[derive(Debug, Default, PartialEq, Eq)]
|
||||
pub struct RawItems {
|
||||
modules: Arena<ModuleData>,
|
||||
imports: Arena<ImportData>,
|
||||
defs: Arena<DefData>,
|
||||
macros: Arena<MacroData>,
|
||||
impls: Arena<ImplData>,
|
||||
/// items for top-level module
|
||||
items: Vec<RawItem>,
|
||||
}
|
||||
|
||||
impl RawItems {
|
||||
pub(crate) fn raw_items_query(db: &dyn DefDatabase, file_id: HirFileId) -> Arc<RawItems> {
|
||||
let _p = profile("raw_items_query");
|
||||
let mut collector = RawItemsCollector {
|
||||
raw_items: RawItems::default(),
|
||||
source_ast_id_map: db.ast_id_map(file_id),
|
||||
file_id,
|
||||
hygiene: Hygiene::new(db.upcast(), file_id),
|
||||
};
|
||||
if let Some(node) = db.parse_or_expand(file_id) {
|
||||
if let Some(source_file) = ast::SourceFile::cast(node.clone()) {
|
||||
collector.process_module(None, source_file);
|
||||
} else if let Some(item_list) = ast::MacroItems::cast(node) {
|
||||
collector.process_module(None, item_list);
|
||||
}
|
||||
}
|
||||
let raw_items = collector.raw_items;
|
||||
Arc::new(raw_items)
|
||||
}
|
||||
|
||||
pub(super) fn items(&self) -> &[RawItem] {
|
||||
&self.items
|
||||
}
|
||||
}
|
||||
|
||||
impl Index<Idx<ModuleData>> for RawItems {
|
||||
type Output = ModuleData;
|
||||
fn index(&self, idx: Idx<ModuleData>) -> &ModuleData {
|
||||
&self.modules[idx]
|
||||
}
|
||||
}
|
||||
|
||||
impl Index<Import> for RawItems {
|
||||
type Output = ImportData;
|
||||
fn index(&self, idx: Import) -> &ImportData {
|
||||
&self.imports[idx]
|
||||
}
|
||||
}
|
||||
|
||||
impl Index<Idx<DefData>> for RawItems {
|
||||
type Output = DefData;
|
||||
fn index(&self, idx: Idx<DefData>) -> &DefData {
|
||||
&self.defs[idx]
|
||||
}
|
||||
}
|
||||
|
||||
impl Index<Idx<MacroData>> for RawItems {
|
||||
type Output = MacroData;
|
||||
fn index(&self, idx: Idx<MacroData>) -> &MacroData {
|
||||
&self.macros[idx]
|
||||
}
|
||||
}
|
||||
|
||||
impl Index<Idx<ImplData>> for RawItems {
|
||||
type Output = ImplData;
|
||||
fn index(&self, idx: Idx<ImplData>) -> &ImplData {
|
||||
&self.impls[idx]
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, Clone)]
|
||||
pub(super) struct RawItem {
|
||||
pub(super) attrs: Attrs,
|
||||
pub(super) kind: RawItemKind,
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
|
||||
pub(super) enum RawItemKind {
|
||||
Module(Idx<ModuleData>),
|
||||
Import(Import),
|
||||
Def(Idx<DefData>),
|
||||
Macro(Idx<MacroData>),
|
||||
Impl(Idx<ImplData>),
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
pub(super) enum ModuleData {
|
||||
Declaration {
|
||||
name: Name,
|
||||
visibility: RawVisibility,
|
||||
ast_id: FileAstId<ast::Module>,
|
||||
},
|
||||
Definition {
|
||||
name: Name,
|
||||
visibility: RawVisibility,
|
||||
ast_id: FileAstId<ast::Module>,
|
||||
items: Vec<RawItem>,
|
||||
},
|
||||
}
|
||||
|
||||
pub(crate) type Import = Idx<ImportData>;
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct ImportData {
|
||||
pub(super) path: ModPath,
|
||||
pub(super) alias: Option<ImportAlias>,
|
||||
pub(super) is_glob: bool,
|
||||
pub(super) is_prelude: bool,
|
||||
pub(super) is_extern_crate: bool,
|
||||
pub(super) is_macro_use: bool,
|
||||
pub(super) visibility: RawVisibility,
|
||||
}
|
||||
|
||||
// type Def = Idx<DefData>;
|
||||
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
pub(super) struct DefData {
|
||||
pub(super) name: Name,
|
||||
pub(super) kind: DefKind,
|
||||
pub(super) visibility: RawVisibility,
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
|
||||
pub(super) enum StructDefKind {
|
||||
Record,
|
||||
Tuple,
|
||||
Unit,
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
|
||||
pub(super) enum DefKind {
|
||||
Function(FileAstId<ast::FnDef>),
|
||||
Struct(FileAstId<ast::StructDef>, StructDefKind),
|
||||
Union(FileAstId<ast::UnionDef>),
|
||||
Enum(FileAstId<ast::EnumDef>),
|
||||
Const(FileAstId<ast::ConstDef>),
|
||||
Static(FileAstId<ast::StaticDef>),
|
||||
Trait(FileAstId<ast::TraitDef>),
|
||||
TypeAlias(FileAstId<ast::TypeAliasDef>),
|
||||
}
|
||||
|
||||
impl DefKind {
|
||||
pub fn ast_id(self) -> FileAstId<ast::ModuleItem> {
|
||||
match self {
|
||||
DefKind::Function(it) => it.upcast(),
|
||||
DefKind::Struct(it, _) => it.upcast(),
|
||||
DefKind::Union(it) => it.upcast(),
|
||||
DefKind::Enum(it) => it.upcast(),
|
||||
DefKind::Const(it) => it.upcast(),
|
||||
DefKind::Static(it) => it.upcast(),
|
||||
DefKind::Trait(it) => it.upcast(),
|
||||
DefKind::TypeAlias(it) => it.upcast(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
pub(super) struct MacroData {
|
||||
pub(super) ast_id: FileAstId<ast::MacroCall>,
|
||||
pub(super) path: ModPath,
|
||||
pub(super) name: Option<Name>,
|
||||
pub(super) export: bool,
|
||||
pub(super) local_inner: bool,
|
||||
pub(super) builtin: bool,
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
pub(super) struct ImplData {
|
||||
pub(super) ast_id: FileAstId<ast::ImplDef>,
|
||||
}
|
||||
|
||||
struct RawItemsCollector {
|
||||
raw_items: RawItems,
|
||||
source_ast_id_map: Arc<AstIdMap>,
|
||||
file_id: HirFileId,
|
||||
hygiene: Hygiene,
|
||||
}
|
||||
|
||||
impl RawItemsCollector {
|
||||
fn process_module(
|
||||
&mut self,
|
||||
current_module: Option<Idx<ModuleData>>,
|
||||
body: impl ast::ModuleItemOwner,
|
||||
) {
|
||||
for item in body.items() {
|
||||
self.add_item(current_module, item)
|
||||
}
|
||||
}
|
||||
|
||||
fn add_item(&mut self, current_module: Option<Idx<ModuleData>>, item: ast::ModuleItem) {
|
||||
let attrs = self.parse_attrs(&item);
|
||||
let visibility = RawVisibility::from_ast_with_hygiene(item.visibility(), &self.hygiene);
|
||||
let (kind, name) = match item {
|
||||
ast::ModuleItem::Module(module) => {
|
||||
self.add_module(current_module, module);
|
||||
return;
|
||||
}
|
||||
ast::ModuleItem::UseItem(use_item) => {
|
||||
self.add_use_item(current_module, use_item);
|
||||
return;
|
||||
}
|
||||
ast::ModuleItem::ExternCrateItem(extern_crate) => {
|
||||
self.add_extern_crate_item(current_module, extern_crate);
|
||||
return;
|
||||
}
|
||||
ast::ModuleItem::ImplDef(it) => {
|
||||
self.add_impl(current_module, it);
|
||||
return;
|
||||
}
|
||||
ast::ModuleItem::StructDef(it) => {
|
||||
let kind = match it.kind() {
|
||||
ast::StructKind::Record(_) => StructDefKind::Record,
|
||||
ast::StructKind::Tuple(_) => StructDefKind::Tuple,
|
||||
ast::StructKind::Unit => StructDefKind::Unit,
|
||||
};
|
||||
let id = self.source_ast_id_map.ast_id(&it);
|
||||
let name = it.name();
|
||||
(DefKind::Struct(id, kind), name)
|
||||
}
|
||||
ast::ModuleItem::UnionDef(it) => {
|
||||
let id = self.source_ast_id_map.ast_id(&it);
|
||||
let name = it.name();
|
||||
(DefKind::Union(id), name)
|
||||
}
|
||||
ast::ModuleItem::EnumDef(it) => {
|
||||
(DefKind::Enum(self.source_ast_id_map.ast_id(&it)), it.name())
|
||||
}
|
||||
ast::ModuleItem::FnDef(it) => {
|
||||
(DefKind::Function(self.source_ast_id_map.ast_id(&it)), it.name())
|
||||
}
|
||||
ast::ModuleItem::TraitDef(it) => {
|
||||
(DefKind::Trait(self.source_ast_id_map.ast_id(&it)), it.name())
|
||||
}
|
||||
ast::ModuleItem::TypeAliasDef(it) => {
|
||||
(DefKind::TypeAlias(self.source_ast_id_map.ast_id(&it)), it.name())
|
||||
}
|
||||
ast::ModuleItem::ConstDef(it) => {
|
||||
(DefKind::Const(self.source_ast_id_map.ast_id(&it)), it.name())
|
||||
}
|
||||
ast::ModuleItem::StaticDef(it) => {
|
||||
(DefKind::Static(self.source_ast_id_map.ast_id(&it)), it.name())
|
||||
}
|
||||
ast::ModuleItem::MacroCall(it) => {
|
||||
self.add_macro(current_module, it);
|
||||
return;
|
||||
}
|
||||
ast::ModuleItem::ExternBlock(it) => {
|
||||
self.add_extern_block(current_module, it);
|
||||
return;
|
||||
}
|
||||
};
|
||||
if let Some(name) = name {
|
||||
let name = name.as_name();
|
||||
let def = self.raw_items.defs.alloc(DefData { name, kind, visibility });
|
||||
self.push_item(current_module, attrs, RawItemKind::Def(def));
|
||||
}
|
||||
}
|
||||
|
||||
fn add_extern_block(
|
||||
&mut self,
|
||||
current_module: Option<Idx<ModuleData>>,
|
||||
block: ast::ExternBlock,
|
||||
) {
|
||||
if let Some(items) = block.extern_item_list() {
|
||||
for item in items.extern_items() {
|
||||
let attrs = self.parse_attrs(&item);
|
||||
let visibility =
|
||||
RawVisibility::from_ast_with_hygiene(item.visibility(), &self.hygiene);
|
||||
let (kind, name) = match item {
|
||||
ast::ExternItem::FnDef(it) => {
|
||||
(DefKind::Function(self.source_ast_id_map.ast_id(&it)), it.name())
|
||||
}
|
||||
ast::ExternItem::StaticDef(it) => {
|
||||
(DefKind::Static(self.source_ast_id_map.ast_id(&it)), it.name())
|
||||
}
|
||||
};
|
||||
|
||||
if let Some(name) = name {
|
||||
let name = name.as_name();
|
||||
let def = self.raw_items.defs.alloc(DefData { name, kind, visibility });
|
||||
self.push_item(current_module, attrs, RawItemKind::Def(def));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn add_module(&mut self, current_module: Option<Idx<ModuleData>>, module: ast::Module) {
|
||||
let name = match module.name() {
|
||||
Some(it) => it.as_name(),
|
||||
None => return,
|
||||
};
|
||||
let attrs = self.parse_attrs(&module);
|
||||
let visibility = RawVisibility::from_ast_with_hygiene(module.visibility(), &self.hygiene);
|
||||
|
||||
let ast_id = self.source_ast_id_map.ast_id(&module);
|
||||
if module.semicolon_token().is_some() {
|
||||
let item =
|
||||
self.raw_items.modules.alloc(ModuleData::Declaration { name, visibility, ast_id });
|
||||
self.push_item(current_module, attrs, RawItemKind::Module(item));
|
||||
return;
|
||||
}
|
||||
|
||||
if let Some(item_list) = module.item_list() {
|
||||
let item = self.raw_items.modules.alloc(ModuleData::Definition {
|
||||
name,
|
||||
visibility,
|
||||
ast_id,
|
||||
items: Vec::new(),
|
||||
});
|
||||
self.process_module(Some(item), item_list);
|
||||
self.push_item(current_module, attrs, RawItemKind::Module(item));
|
||||
return;
|
||||
}
|
||||
mark::hit!(name_res_works_for_broken_modules);
|
||||
}
|
||||
|
||||
fn add_use_item(&mut self, current_module: Option<Idx<ModuleData>>, use_item: ast::UseItem) {
|
||||
// FIXME: cfg_attr
|
||||
let is_prelude = use_item.has_atom_attr("prelude_import");
|
||||
let attrs = self.parse_attrs(&use_item);
|
||||
let visibility = RawVisibility::from_ast_with_hygiene(use_item.visibility(), &self.hygiene);
|
||||
|
||||
let mut buf = Vec::new();
|
||||
ModPath::expand_use_item(
|
||||
InFile { value: use_item, file_id: self.file_id },
|
||||
&self.hygiene,
|
||||
|path, _use_tree, is_glob, alias| {
|
||||
let import_data = ImportData {
|
||||
path,
|
||||
alias,
|
||||
is_glob,
|
||||
is_prelude,
|
||||
is_extern_crate: false,
|
||||
is_macro_use: false,
|
||||
visibility: visibility.clone(),
|
||||
};
|
||||
buf.push(import_data);
|
||||
},
|
||||
);
|
||||
for import_data in buf {
|
||||
self.push_import(current_module, attrs.clone(), import_data);
|
||||
}
|
||||
}
|
||||
|
||||
fn add_extern_crate_item(
|
||||
&mut self,
|
||||
current_module: Option<Idx<ModuleData>>,
|
||||
extern_crate: ast::ExternCrateItem,
|
||||
) {
|
||||
if let Some(name_ref) = extern_crate.name_ref() {
|
||||
let path = ModPath::from_name_ref(&name_ref);
|
||||
let visibility =
|
||||
RawVisibility::from_ast_with_hygiene(extern_crate.visibility(), &self.hygiene);
|
||||
let alias = extern_crate.alias().map(|a| {
|
||||
a.name().map(|it| it.as_name()).map_or(ImportAlias::Underscore, ImportAlias::Alias)
|
||||
});
|
||||
let attrs = self.parse_attrs(&extern_crate);
|
||||
// FIXME: cfg_attr
|
||||
let is_macro_use = extern_crate.has_atom_attr("macro_use");
|
||||
let import_data = ImportData {
|
||||
path,
|
||||
alias,
|
||||
is_glob: false,
|
||||
is_prelude: false,
|
||||
is_extern_crate: true,
|
||||
is_macro_use,
|
||||
visibility,
|
||||
};
|
||||
self.push_import(current_module, attrs, import_data);
|
||||
}
|
||||
}
|
||||
|
||||
fn add_macro(&mut self, current_module: Option<Idx<ModuleData>>, m: ast::MacroCall) {
|
||||
let attrs = self.parse_attrs(&m);
|
||||
let path = match m.path().and_then(|path| ModPath::from_src(path, &self.hygiene)) {
|
||||
Some(it) => it,
|
||||
_ => return,
|
||||
};
|
||||
|
||||
let name = m.name().map(|it| it.as_name());
|
||||
let ast_id = self.source_ast_id_map.ast_id(&m);
|
||||
|
||||
// FIXME: cfg_attr
|
||||
let export_attr = attrs.by_key("macro_export");
|
||||
|
||||
let export = export_attr.exists();
|
||||
let local_inner = if export {
|
||||
export_attr.tt_values().map(|it| &it.token_trees).flatten().any(|it| match it {
|
||||
tt::TokenTree::Leaf(tt::Leaf::Ident(ident)) => {
|
||||
ident.text.contains("local_inner_macros")
|
||||
}
|
||||
_ => false,
|
||||
})
|
||||
} else {
|
||||
false
|
||||
};
|
||||
|
||||
let builtin = attrs.by_key("rustc_builtin_macro").exists();
|
||||
|
||||
let m = self.raw_items.macros.alloc(MacroData {
|
||||
ast_id,
|
||||
path,
|
||||
name,
|
||||
export,
|
||||
local_inner,
|
||||
builtin,
|
||||
});
|
||||
self.push_item(current_module, attrs, RawItemKind::Macro(m));
|
||||
}
|
||||
|
||||
fn add_impl(&mut self, current_module: Option<Idx<ModuleData>>, imp: ast::ImplDef) {
|
||||
let attrs = self.parse_attrs(&imp);
|
||||
let ast_id = self.source_ast_id_map.ast_id(&imp);
|
||||
let imp = self.raw_items.impls.alloc(ImplData { ast_id });
|
||||
self.push_item(current_module, attrs, RawItemKind::Impl(imp))
|
||||
}
|
||||
|
||||
fn push_import(
|
||||
&mut self,
|
||||
current_module: Option<Idx<ModuleData>>,
|
||||
attrs: Attrs,
|
||||
data: ImportData,
|
||||
) {
|
||||
let import = self.raw_items.imports.alloc(data);
|
||||
self.push_item(current_module, attrs, RawItemKind::Import(import))
|
||||
}
|
||||
|
||||
fn push_item(
|
||||
&mut self,
|
||||
current_module: Option<Idx<ModuleData>>,
|
||||
attrs: Attrs,
|
||||
kind: RawItemKind,
|
||||
) {
|
||||
match current_module {
|
||||
Some(module) => match &mut self.raw_items.modules[module] {
|
||||
ModuleData::Definition { items, .. } => items,
|
||||
ModuleData::Declaration { .. } => unreachable!(),
|
||||
},
|
||||
None => &mut self.raw_items.items,
|
||||
}
|
||||
.push(RawItem { attrs, kind })
|
||||
}
|
||||
|
||||
fn parse_attrs(&self, item: &impl ast::AttrsOwner) -> Attrs {
|
||||
Attrs::new(item, &self.hygiene)
|
||||
}
|
||||
}
|
|
@ -57,44 +57,6 @@ fn typing_inside_a_function_should_not_invalidate_def_map() {
|
|||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn adding_inner_items_should_not_invalidate_def_map() {
|
||||
check_def_map_is_not_recomputed(
|
||||
r"
|
||||
//- /lib.rs
|
||||
struct S { a: i32}
|
||||
enum E { A }
|
||||
trait T {
|
||||
fn a() {}
|
||||
}
|
||||
mod foo;<|>
|
||||
impl S {
|
||||
fn a() {}
|
||||
}
|
||||
use crate::foo::bar::Baz;
|
||||
//- /foo/mod.rs
|
||||
pub mod bar;
|
||||
|
||||
//- /foo/bar.rs
|
||||
pub struct Baz;
|
||||
",
|
||||
r"
|
||||
struct S { a: i32, b: () }
|
||||
enum E { A, B }
|
||||
trait T {
|
||||
fn a() {}
|
||||
fn b() {}
|
||||
}
|
||||
mod foo;<|>
|
||||
impl S {
|
||||
fn a() {}
|
||||
fn b() {}
|
||||
}
|
||||
use crate::foo::bar::Baz;
|
||||
",
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn typing_inside_a_macro_should_not_invalidate_def_map() {
|
||||
let (mut db, pos) = TestDB::with_position(
|
||||
|
|
|
@ -20,8 +20,11 @@ fn name_res_works_for_broken_modules() {
|
|||
",
|
||||
);
|
||||
assert_snapshot!(map, @r###"
|
||||
⋮crate
|
||||
⋮Baz: _
|
||||
crate
|
||||
Baz: _
|
||||
foo: t
|
||||
|
||||
crate::foo
|
||||
"###);
|
||||
}
|
||||
|
||||
|
@ -719,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::ItemTreeNode, 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: ItemTreeNode> 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: ItemTreeNode> 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,7 +6,7 @@ use ra_syntax::ast;
|
|||
use crate::{
|
||||
db::DefDatabase,
|
||||
path::{ModPath, PathKind},
|
||||
AssocContainerId, ModuleId,
|
||||
ModuleId,
|
||||
};
|
||||
|
||||
/// Visibility of an item, not yet resolved.
|
||||
|
@ -25,25 +25,6 @@ impl RawVisibility {
|
|||
RawVisibility::Module(path)
|
||||
}
|
||||
|
||||
pub(crate) fn default_for_container(container_id: AssocContainerId) -> Self {
|
||||
match container_id {
|
||||
AssocContainerId::TraitId(_) => RawVisibility::Public,
|
||||
_ => RawVisibility::private(),
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn from_ast_with_default(
|
||||
db: &dyn DefDatabase,
|
||||
default: RawVisibility,
|
||||
node: InFile<Option<ast::Visibility>>,
|
||||
) -> RawVisibility {
|
||||
Self::from_ast_with_hygiene_and_default(
|
||||
node.value,
|
||||
default,
|
||||
&Hygiene::new(db.upcast(), node.file_id),
|
||||
)
|
||||
}
|
||||
|
||||
pub(crate) fn from_ast(
|
||||
db: &dyn DefDatabase,
|
||||
node: InFile<Option<ast::Visibility>>,
|
||||
|
|
|
@ -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).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).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).syntax().text_range().start()
|
||||
}
|
||||
});
|
||||
for def in defs {
|
||||
|
|
|
@ -199,7 +199,7 @@ impl RootDatabase {
|
|||
hir::db::InternEagerExpansionQuery
|
||||
|
||||
// DefDatabase
|
||||
hir::db::RawItemsQuery
|
||||
hir::db::ItemTreeQuery
|
||||
hir::db::CrateDefMapQueryQuery
|
||||
hir::db::StructDataQuery
|
||||
hir::db::UnionDataQuery
|
||||
|
|
Loading…
Reference in a new issue