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_syntax",
|
||||||
"ra_tt",
|
"ra_tt",
|
||||||
"rustc-hash",
|
"rustc-hash",
|
||||||
|
"smallvec",
|
||||||
"stdx",
|
"stdx",
|
||||||
"test_utils",
|
"test_utils",
|
||||||
]
|
]
|
||||||
|
|
|
@ -116,6 +116,9 @@ impl<T> Arena<T> {
|
||||||
) -> impl Iterator<Item = (Idx<T>, &T)> + ExactSizeIterator + DoubleEndedIterator {
|
) -> impl Iterator<Item = (Idx<T>, &T)> + ExactSizeIterator + DoubleEndedIterator {
|
||||||
self.data.iter().enumerate().map(|(idx, value)| (Idx::from_raw(RawId(idx as u32)), value))
|
self.data.iter().enumerate().map(|(idx, value)| (Idx::from_raw(RawId(idx as u32)), value))
|
||||||
}
|
}
|
||||||
|
pub fn shrink_to_fit(&mut self) {
|
||||||
|
self.data.shrink_to_fit();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T> Default for Arena<T> {
|
impl<T> Default for Arena<T> {
|
||||||
|
|
|
@ -114,7 +114,7 @@ pub trait SourceDatabase: CheckCanceled + FileLoader + std::fmt::Debug {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_query(db: &impl SourceDatabase, file_id: FileId) -> Parse<ast::SourceFile> {
|
fn parse_query(db: &impl SourceDatabase, file_id: FileId) -> Parse<ast::SourceFile> {
|
||||||
let _p = profile("parse_query");
|
let _p = profile("parse_query").detail(|| format!("{:?}", file_id));
|
||||||
let text = db.file_text(file_id);
|
let text = db.file_text(file_id);
|
||||||
SourceFile::parse(&*text)
|
SourceFile::parse(&*text)
|
||||||
}
|
}
|
||||||
|
|
|
@ -31,10 +31,7 @@ use hir_ty::{
|
||||||
};
|
};
|
||||||
use ra_db::{CrateId, CrateName, Edition, FileId};
|
use ra_db::{CrateId, CrateName, Edition, FileId};
|
||||||
use ra_prof::profile;
|
use ra_prof::profile;
|
||||||
use ra_syntax::{
|
use ra_syntax::ast::{self, AttrsOwner, NameOwner};
|
||||||
ast::{self, AttrsOwner, NameOwner},
|
|
||||||
AstNode,
|
|
||||||
};
|
|
||||||
use rustc_hash::FxHashSet;
|
use rustc_hash::FxHashSet;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
|
@ -205,7 +202,8 @@ impl ModuleDef {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub use hir_def::{
|
pub use hir_def::{
|
||||||
attr::Attrs, item_scope::ItemInNs, visibility::Visibility, AssocItemId, AssocItemLoc,
|
attr::Attrs, item_scope::ItemInNs, item_tree::ItemTreeNode, visibility::Visibility,
|
||||||
|
AssocItemId, AssocItemLoc,
|
||||||
};
|
};
|
||||||
|
|
||||||
impl Module {
|
impl Module {
|
||||||
|
@ -872,7 +870,7 @@ where
|
||||||
ID: Lookup<Data = AssocItemLoc<AST>>,
|
ID: Lookup<Data = AssocItemLoc<AST>>,
|
||||||
DEF: From<ID>,
|
DEF: From<ID>,
|
||||||
CTOR: FnOnce(DEF) -> AssocItem,
|
CTOR: FnOnce(DEF) -> AssocItem,
|
||||||
AST: AstNode,
|
AST: ItemTreeNode,
|
||||||
{
|
{
|
||||||
match id.lookup(db.upcast()).container {
|
match id.lookup(db.upcast()).container {
|
||||||
AssocContainerId::TraitId(_) | AssocContainerId::ImplId(_) => Some(ctor(DEF::from(id))),
|
AssocContainerId::TraitId(_) | AssocContainerId::ImplId(_) => Some(ctor(DEF::from(id))),
|
||||||
|
|
|
@ -6,7 +6,7 @@ pub use hir_def::db::{
|
||||||
ExprScopesQuery, FunctionDataQuery, GenericParamsQuery, ImplDataQuery, ImportMapQuery,
|
ExprScopesQuery, FunctionDataQuery, GenericParamsQuery, ImplDataQuery, ImportMapQuery,
|
||||||
InternConstQuery, InternDatabase, InternDatabaseStorage, InternEnumQuery, InternFunctionQuery,
|
InternConstQuery, InternDatabase, InternDatabaseStorage, InternEnumQuery, InternFunctionQuery,
|
||||||
InternImplQuery, InternStaticQuery, InternStructQuery, InternTraitQuery, InternTypeAliasQuery,
|
InternImplQuery, InternStaticQuery, InternStructQuery, InternTraitQuery, InternTypeAliasQuery,
|
||||||
InternUnionQuery, LangItemQuery, ModuleLangItemsQuery, RawItemsQuery, StaticDataQuery,
|
InternUnionQuery, ItemTreeQuery, LangItemQuery, ModuleLangItemsQuery, StaticDataQuery,
|
||||||
StructDataQuery, TraitDataQuery, TypeAliasDataQuery, UnionDataQuery,
|
StructDataQuery, TraitDataQuery, TypeAliasDataQuery, UnionDataQuery,
|
||||||
};
|
};
|
||||||
pub use hir_expand::db::{
|
pub use hir_expand::db::{
|
||||||
|
|
|
@ -17,6 +17,7 @@ drop_bomb = "0.1.4"
|
||||||
fst = { version = "0.4", default-features = false }
|
fst = { version = "0.4", default-features = false }
|
||||||
itertools = "0.9.0"
|
itertools = "0.9.0"
|
||||||
indexmap = "1.4.0"
|
indexmap = "1.4.0"
|
||||||
|
smallvec = "1.4.0"
|
||||||
|
|
||||||
stdx = { path = "../stdx" }
|
stdx = { path = "../stdx" }
|
||||||
|
|
||||||
|
|
|
@ -13,7 +13,11 @@ use ra_syntax::{
|
||||||
use tt::Subtree;
|
use tt::Subtree;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
db::DefDatabase, nameres::ModuleSource, path::ModPath, src::HasChildSource, src::HasSource,
|
db::DefDatabase,
|
||||||
|
item_tree::{ItemTreeId, ItemTreeNode},
|
||||||
|
nameres::ModuleSource,
|
||||||
|
path::ModPath,
|
||||||
|
src::HasChildSource,
|
||||||
AdtId, AttrDefId, Lookup,
|
AdtId, AttrDefId, Lookup,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -34,6 +38,8 @@ impl ops::Deref for Attrs {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Attrs {
|
impl Attrs {
|
||||||
|
pub const EMPTY: Attrs = Attrs { entries: None };
|
||||||
|
|
||||||
pub(crate) fn attrs_query(db: &dyn DefDatabase, def: AttrDefId) -> Attrs {
|
pub(crate) fn attrs_query(db: &dyn DefDatabase, def: AttrDefId) -> Attrs {
|
||||||
match def {
|
match def {
|
||||||
AttrDefId::ModuleId(module) => {
|
AttrDefId::ModuleId(module) => {
|
||||||
|
@ -65,19 +71,19 @@ impl Attrs {
|
||||||
Attrs::from_attrs_owner(db, src.map(|it| it as &dyn AttrsOwner))
|
Attrs::from_attrs_owner(db, src.map(|it| it as &dyn AttrsOwner))
|
||||||
}
|
}
|
||||||
AttrDefId::AdtId(it) => match it {
|
AttrDefId::AdtId(it) => match it {
|
||||||
AdtId::StructId(it) => attrs_from_loc(it.lookup(db), db),
|
AdtId::StructId(it) => attrs_from_item_tree(it.lookup(db).id, db),
|
||||||
AdtId::EnumId(it) => attrs_from_loc(it.lookup(db), db),
|
AdtId::EnumId(it) => attrs_from_item_tree(it.lookup(db).id, db),
|
||||||
AdtId::UnionId(it) => attrs_from_loc(it.lookup(db), db),
|
AdtId::UnionId(it) => attrs_from_item_tree(it.lookup(db).id, db),
|
||||||
},
|
},
|
||||||
AttrDefId::TraitId(it) => attrs_from_loc(it.lookup(db), db),
|
AttrDefId::TraitId(it) => attrs_from_item_tree(it.lookup(db).id, db),
|
||||||
AttrDefId::MacroDefId(it) => {
|
AttrDefId::MacroDefId(it) => {
|
||||||
it.ast_id.map_or_else(Default::default, |ast_id| attrs_from_ast(ast_id, db))
|
it.ast_id.map_or_else(Default::default, |ast_id| attrs_from_ast(ast_id, db))
|
||||||
}
|
}
|
||||||
AttrDefId::ImplId(it) => attrs_from_loc(it.lookup(db), db),
|
AttrDefId::ImplId(it) => attrs_from_item_tree(it.lookup(db).id, db),
|
||||||
AttrDefId::ConstId(it) => attrs_from_loc(it.lookup(db), db),
|
AttrDefId::ConstId(it) => attrs_from_item_tree(it.lookup(db).id, db),
|
||||||
AttrDefId::StaticId(it) => attrs_from_loc(it.lookup(db), db),
|
AttrDefId::StaticId(it) => attrs_from_item_tree(it.lookup(db).id, db),
|
||||||
AttrDefId::FunctionId(it) => attrs_from_loc(it.lookup(db), db),
|
AttrDefId::FunctionId(it) => attrs_from_item_tree(it.lookup(db).id, db),
|
||||||
AttrDefId::TypeAliasId(it) => attrs_from_loc(it.lookup(db), db),
|
AttrDefId::TypeAliasId(it) => attrs_from_item_tree(it.lookup(db).id, db),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -103,6 +109,18 @@ impl Attrs {
|
||||||
Attrs { entries }
|
Attrs { entries }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn merge(&self, other: Attrs) -> Attrs {
|
||||||
|
match (&self.entries, &other.entries) {
|
||||||
|
(None, None) => Attrs { entries: None },
|
||||||
|
(Some(entries), None) | (None, Some(entries)) => {
|
||||||
|
Attrs { entries: Some(entries.clone()) }
|
||||||
|
}
|
||||||
|
(Some(a), Some(b)) => {
|
||||||
|
Attrs { entries: Some(a.iter().chain(b.iter()).cloned().collect()) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn by_key(&self, key: &'static str) -> AttrQuery<'_> {
|
pub fn by_key(&self, key: &'static str) -> AttrQuery<'_> {
|
||||||
AttrQuery { attrs: self, key }
|
AttrQuery { attrs: self, key }
|
||||||
}
|
}
|
||||||
|
@ -187,11 +205,8 @@ where
|
||||||
Attrs::from_attrs_owner(db, src.as_ref().map(|it| it as &dyn AttrsOwner))
|
Attrs::from_attrs_owner(db, src.as_ref().map(|it| it as &dyn AttrsOwner))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn attrs_from_loc<T>(node: T, db: &dyn DefDatabase) -> Attrs
|
fn attrs_from_item_tree<N: ItemTreeNode>(id: ItemTreeId<N>, db: &dyn DefDatabase) -> Attrs {
|
||||||
where
|
let tree = db.item_tree(id.file_id);
|
||||||
T: HasSource,
|
let mod_item = N::id_to_mod_item(id.value);
|
||||||
T::Value: ast::AttrsOwner,
|
tree.attrs(mod_item).clone()
|
||||||
{
|
|
||||||
let src = node.source(db);
|
|
||||||
Attrs::from_attrs_owner(db, src.as_ref().map(|it| it as &dyn AttrsOwner))
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,7 +5,7 @@ use either::Either;
|
||||||
use hir_expand::{
|
use hir_expand::{
|
||||||
hygiene::Hygiene,
|
hygiene::Hygiene,
|
||||||
name::{name, AsName, Name},
|
name::{name, AsName, Name},
|
||||||
HirFileId, MacroDefId, MacroDefKind,
|
AstId, HirFileId, MacroDefId, MacroDefKind,
|
||||||
};
|
};
|
||||||
use ra_arena::Arena;
|
use ra_arena::Arena;
|
||||||
use ra_syntax::{
|
use ra_syntax::{
|
||||||
|
@ -27,6 +27,7 @@ use crate::{
|
||||||
LogicOp, MatchArm, Ordering, Pat, PatId, RecordFieldPat, RecordLitField, Statement,
|
LogicOp, MatchArm, Ordering, Pat, PatId, RecordFieldPat, RecordLitField, Statement,
|
||||||
},
|
},
|
||||||
item_scope::BuiltinShadowMode,
|
item_scope::BuiltinShadowMode,
|
||||||
|
item_tree::{FileItemTreeId, ItemTree, ItemTreeNode},
|
||||||
path::{GenericArgs, Path},
|
path::{GenericArgs, Path},
|
||||||
type_ref::{Mutability, Rawness, TypeRef},
|
type_ref::{Mutability, Rawness, TypeRef},
|
||||||
AdtId, ConstLoc, ContainerId, DefWithBodyId, EnumLoc, FunctionLoc, Intern, ModuleDefId,
|
AdtId, ConstLoc, ContainerId, DefWithBodyId, EnumLoc, FunctionLoc, Intern, ModuleDefId,
|
||||||
|
@ -35,6 +36,8 @@ use crate::{
|
||||||
|
|
||||||
use super::{ExprSource, PatSource};
|
use super::{ExprSource, PatSource};
|
||||||
use ast::AstChildren;
|
use ast::AstChildren;
|
||||||
|
use rustc_hash::FxHashMap;
|
||||||
|
use std::sync::Arc;
|
||||||
|
|
||||||
pub(crate) struct LowerCtx {
|
pub(crate) struct LowerCtx {
|
||||||
hygiene: Hygiene,
|
hygiene: Hygiene,
|
||||||
|
@ -60,10 +63,10 @@ pub(super) fn lower(
|
||||||
params: Option<ast::ParamList>,
|
params: Option<ast::ParamList>,
|
||||||
body: Option<ast::Expr>,
|
body: Option<ast::Expr>,
|
||||||
) -> (Body, BodySourceMap) {
|
) -> (Body, BodySourceMap) {
|
||||||
|
let item_tree = db.item_tree(expander.current_file_id);
|
||||||
ExprCollector {
|
ExprCollector {
|
||||||
db,
|
db,
|
||||||
def,
|
def,
|
||||||
expander,
|
|
||||||
source_map: BodySourceMap::default(),
|
source_map: BodySourceMap::default(),
|
||||||
body: Body {
|
body: Body {
|
||||||
exprs: Arena::default(),
|
exprs: Arena::default(),
|
||||||
|
@ -72,6 +75,12 @@ pub(super) fn lower(
|
||||||
body_expr: dummy_expr_id(),
|
body_expr: dummy_expr_id(),
|
||||||
item_scope: Default::default(),
|
item_scope: Default::default(),
|
||||||
},
|
},
|
||||||
|
item_trees: {
|
||||||
|
let mut map = FxHashMap::default();
|
||||||
|
map.insert(expander.current_file_id, item_tree);
|
||||||
|
map
|
||||||
|
},
|
||||||
|
expander,
|
||||||
}
|
}
|
||||||
.collect(params, body)
|
.collect(params, body)
|
||||||
}
|
}
|
||||||
|
@ -82,6 +91,8 @@ struct ExprCollector<'a> {
|
||||||
expander: Expander,
|
expander: Expander,
|
||||||
body: Body,
|
body: Body,
|
||||||
source_map: BodySourceMap,
|
source_map: BodySourceMap,
|
||||||
|
|
||||||
|
item_trees: FxHashMap<HirFileId, Arc<ItemTree>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ExprCollector<'_> {
|
impl ExprCollector<'_> {
|
||||||
|
@ -533,6 +544,9 @@ impl ExprCollector<'_> {
|
||||||
self.source_map
|
self.source_map
|
||||||
.expansions
|
.expansions
|
||||||
.insert(macro_call, self.expander.current_file_id);
|
.insert(macro_call, self.expander.current_file_id);
|
||||||
|
|
||||||
|
let item_tree = self.db.item_tree(self.expander.current_file_id);
|
||||||
|
self.item_trees.insert(self.expander.current_file_id, item_tree);
|
||||||
let id = self.collect_expr(expansion);
|
let id = self.collect_expr(expansion);
|
||||||
self.expander.exit(self.db, mark);
|
self.expander.exit(self.db, mark);
|
||||||
id
|
id
|
||||||
|
@ -547,6 +561,19 @@ impl ExprCollector<'_> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn find_inner_item<S: ItemTreeNode>(&self, id: AstId<ast::ModuleItem>) -> FileItemTreeId<S> {
|
||||||
|
let tree = &self.item_trees[&id.file_id];
|
||||||
|
|
||||||
|
// FIXME: This probably breaks with `use` items, since they produce multiple item tree nodes
|
||||||
|
|
||||||
|
// Root file (non-macro).
|
||||||
|
tree.all_inner_items()
|
||||||
|
.chain(tree.top_level_items().iter().copied())
|
||||||
|
.filter_map(|mod_item| mod_item.downcast::<S>())
|
||||||
|
.find(|tree_id| tree[*tree_id].ast_id().upcast() == id.value)
|
||||||
|
.unwrap_or_else(|| panic!("couldn't find inner item for {:?}", id))
|
||||||
|
}
|
||||||
|
|
||||||
fn collect_expr_opt(&mut self, expr: Option<ast::Expr>) -> ExprId {
|
fn collect_expr_opt(&mut self, expr: Option<ast::Expr>) -> ExprId {
|
||||||
if let Some(expr) = expr {
|
if let Some(expr) = expr {
|
||||||
self.collect_expr(expr)
|
self.collect_expr(expr)
|
||||||
|
@ -578,56 +605,102 @@ impl ExprCollector<'_> {
|
||||||
|
|
||||||
fn collect_block_items(&mut self, block: &ast::BlockExpr) {
|
fn collect_block_items(&mut self, block: &ast::BlockExpr) {
|
||||||
let container = ContainerId::DefWithBodyId(self.def);
|
let container = ContainerId::DefWithBodyId(self.def);
|
||||||
for item in block.items() {
|
|
||||||
let (def, name): (ModuleDefId, Option<ast::Name>) = match item {
|
let items = block
|
||||||
ast::ModuleItem::FnDef(def) => {
|
.items()
|
||||||
let ast_id = self.expander.ast_id(&def);
|
.filter_map(|item| {
|
||||||
(
|
let (def, name): (ModuleDefId, Option<ast::Name>) = match item {
|
||||||
FunctionLoc { container: container.into(), ast_id }.intern(self.db).into(),
|
ast::ModuleItem::FnDef(def) => {
|
||||||
def.name(),
|
let ast_id = self.expander.ast_id(&def);
|
||||||
)
|
let id = self.find_inner_item(ast_id.map(|id| id.upcast()));
|
||||||
}
|
(
|
||||||
ast::ModuleItem::TypeAliasDef(def) => {
|
FunctionLoc { container: container.into(), id: ast_id.with_value(id) }
|
||||||
let ast_id = self.expander.ast_id(&def);
|
.intern(self.db)
|
||||||
(
|
.into(),
|
||||||
TypeAliasLoc { container: container.into(), ast_id }.intern(self.db).into(),
|
def.name(),
|
||||||
def.name(),
|
)
|
||||||
)
|
}
|
||||||
}
|
ast::ModuleItem::TypeAliasDef(def) => {
|
||||||
ast::ModuleItem::ConstDef(def) => {
|
let ast_id = self.expander.ast_id(&def);
|
||||||
let ast_id = self.expander.ast_id(&def);
|
let id = self.find_inner_item(ast_id.map(|id| id.upcast()));
|
||||||
(
|
(
|
||||||
ConstLoc { container: container.into(), ast_id }.intern(self.db).into(),
|
TypeAliasLoc { container: container.into(), id: ast_id.with_value(id) }
|
||||||
def.name(),
|
.intern(self.db)
|
||||||
)
|
.into(),
|
||||||
}
|
def.name(),
|
||||||
ast::ModuleItem::StaticDef(def) => {
|
)
|
||||||
let ast_id = self.expander.ast_id(&def);
|
}
|
||||||
(StaticLoc { container, ast_id }.intern(self.db).into(), def.name())
|
ast::ModuleItem::ConstDef(def) => {
|
||||||
}
|
let ast_id = self.expander.ast_id(&def);
|
||||||
ast::ModuleItem::StructDef(def) => {
|
let id = self.find_inner_item(ast_id.map(|id| id.upcast()));
|
||||||
let ast_id = self.expander.ast_id(&def);
|
(
|
||||||
(StructLoc { container, ast_id }.intern(self.db).into(), def.name())
|
ConstLoc { container: container.into(), id: ast_id.with_value(id) }
|
||||||
}
|
.intern(self.db)
|
||||||
ast::ModuleItem::EnumDef(def) => {
|
.into(),
|
||||||
let ast_id = self.expander.ast_id(&def);
|
def.name(),
|
||||||
(EnumLoc { container, ast_id }.intern(self.db).into(), def.name())
|
)
|
||||||
}
|
}
|
||||||
ast::ModuleItem::UnionDef(def) => {
|
ast::ModuleItem::StaticDef(def) => {
|
||||||
let ast_id = self.expander.ast_id(&def);
|
let ast_id = self.expander.ast_id(&def);
|
||||||
(UnionLoc { container, ast_id }.intern(self.db).into(), def.name())
|
let id = self.find_inner_item(ast_id.map(|id| id.upcast()));
|
||||||
}
|
(
|
||||||
ast::ModuleItem::TraitDef(def) => {
|
StaticLoc { container, id: ast_id.with_value(id) }
|
||||||
let ast_id = self.expander.ast_id(&def);
|
.intern(self.db)
|
||||||
(TraitLoc { container, ast_id }.intern(self.db).into(), def.name())
|
.into(),
|
||||||
}
|
def.name(),
|
||||||
ast::ModuleItem::ExternBlock(_) => continue, // FIXME: collect from extern blocks
|
)
|
||||||
ast::ModuleItem::ImplDef(_)
|
}
|
||||||
| ast::ModuleItem::UseItem(_)
|
ast::ModuleItem::StructDef(def) => {
|
||||||
| ast::ModuleItem::ExternCrateItem(_)
|
let ast_id = self.expander.ast_id(&def);
|
||||||
| ast::ModuleItem::Module(_)
|
let id = self.find_inner_item(ast_id.map(|id| id.upcast()));
|
||||||
| ast::ModuleItem::MacroCall(_) => continue,
|
(
|
||||||
};
|
StructLoc { container, id: ast_id.with_value(id) }
|
||||||
|
.intern(self.db)
|
||||||
|
.into(),
|
||||||
|
def.name(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
ast::ModuleItem::EnumDef(def) => {
|
||||||
|
let ast_id = self.expander.ast_id(&def);
|
||||||
|
let id = self.find_inner_item(ast_id.map(|id| id.upcast()));
|
||||||
|
(
|
||||||
|
EnumLoc { container, id: ast_id.with_value(id) }.intern(self.db).into(),
|
||||||
|
def.name(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
ast::ModuleItem::UnionDef(def) => {
|
||||||
|
let ast_id = self.expander.ast_id(&def);
|
||||||
|
let id = self.find_inner_item(ast_id.map(|id| id.upcast()));
|
||||||
|
(
|
||||||
|
UnionLoc { container, id: ast_id.with_value(id) }
|
||||||
|
.intern(self.db)
|
||||||
|
.into(),
|
||||||
|
def.name(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
ast::ModuleItem::TraitDef(def) => {
|
||||||
|
let ast_id = self.expander.ast_id(&def);
|
||||||
|
let id = self.find_inner_item(ast_id.map(|id| id.upcast()));
|
||||||
|
(
|
||||||
|
TraitLoc { container, id: ast_id.with_value(id) }
|
||||||
|
.intern(self.db)
|
||||||
|
.into(),
|
||||||
|
def.name(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
ast::ModuleItem::ExternBlock(_) => return None, // FIXME: collect from extern blocks
|
||||||
|
ast::ModuleItem::ImplDef(_)
|
||||||
|
| ast::ModuleItem::UseItem(_)
|
||||||
|
| ast::ModuleItem::ExternCrateItem(_)
|
||||||
|
| ast::ModuleItem::Module(_)
|
||||||
|
| ast::ModuleItem::MacroCall(_) => return None,
|
||||||
|
};
|
||||||
|
|
||||||
|
Some((def, name))
|
||||||
|
})
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
|
for (def, name) in items {
|
||||||
self.body.item_scope.define_def(def);
|
self.body.item_scope.define_def(def);
|
||||||
if let Some(name) = name {
|
if let Some(name) = name {
|
||||||
let vis = crate::visibility::Visibility::Public; // FIXME determine correctly
|
let vis = crate::visibility::Visibility::Public; // FIXME determine correctly
|
||||||
|
|
|
@ -317,6 +317,26 @@ fn foo() {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn macro_inner_item() {
|
||||||
|
do_check(
|
||||||
|
r"
|
||||||
|
macro_rules! mac {
|
||||||
|
() => {{
|
||||||
|
fn inner() {}
|
||||||
|
inner();
|
||||||
|
}};
|
||||||
|
}
|
||||||
|
|
||||||
|
fn foo() {
|
||||||
|
mac!();
|
||||||
|
<|>
|
||||||
|
}
|
||||||
|
",
|
||||||
|
&[],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
fn do_check_local_name(ra_fixture: &str, expected_offset: u32) {
|
fn do_check_local_name(ra_fixture: &str, expected_offset: u32) {
|
||||||
let (db, position) = TestDB::with_position(ra_fixture);
|
let (db, position) = TestDB::with_position(ra_fixture);
|
||||||
let file_id = position.file_id;
|
let file_id = position.file_id;
|
||||||
|
|
|
@ -2,27 +2,19 @@
|
||||||
|
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
use hir_expand::{
|
use hir_expand::{name::Name, InFile};
|
||||||
hygiene::Hygiene,
|
|
||||||
name::{name, AsName, Name},
|
|
||||||
AstId, InFile,
|
|
||||||
};
|
|
||||||
use ra_prof::profile;
|
use ra_prof::profile;
|
||||||
use ra_syntax::ast::{
|
use ra_syntax::ast;
|
||||||
self, AssocItem, AstNode, ModuleItemOwner, NameOwner, TypeAscriptionOwner, TypeBoundsOwner,
|
|
||||||
VisibilityOwner,
|
|
||||||
};
|
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
attr::Attrs,
|
attr::Attrs,
|
||||||
body::LowerCtx,
|
body::Expander,
|
||||||
db::DefDatabase,
|
db::DefDatabase,
|
||||||
path::{path, AssociatedTypeBinding, GenericArgs, Path},
|
item_tree::{AssocItem, ItemTreeId, ModItem},
|
||||||
src::HasSource,
|
type_ref::{TypeBound, TypeRef},
|
||||||
type_ref::{Mutability, TypeBound, TypeRef},
|
|
||||||
visibility::RawVisibility,
|
visibility::RawVisibility,
|
||||||
AssocContainerId, AssocItemId, ConstId, ConstLoc, Expander, FunctionId, FunctionLoc, HasModule,
|
AssocContainerId, AssocItemId, ConstId, ConstLoc, FunctionId, FunctionLoc, HasModule, ImplId,
|
||||||
ImplId, Intern, Lookup, StaticId, TraitId, TypeAliasId, TypeAliasLoc,
|
Intern, Lookup, ModuleId, StaticId, TraitId, TypeAliasId, TypeAliasLoc,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||||
|
@ -41,82 +33,27 @@ pub struct FunctionData {
|
||||||
impl FunctionData {
|
impl FunctionData {
|
||||||
pub(crate) fn fn_data_query(db: &impl DefDatabase, func: FunctionId) -> Arc<FunctionData> {
|
pub(crate) fn fn_data_query(db: &impl DefDatabase, func: FunctionId) -> Arc<FunctionData> {
|
||||||
let loc = func.lookup(db);
|
let loc = func.lookup(db);
|
||||||
let src = loc.source(db);
|
let item_tree = db.item_tree(loc.id.file_id);
|
||||||
let ctx = LowerCtx::new(db, src.file_id);
|
let func = &item_tree[loc.id.value];
|
||||||
let name = src.value.name().map(|n| n.as_name()).unwrap_or_else(Name::missing);
|
|
||||||
let mut params = Vec::new();
|
|
||||||
let mut has_self_param = false;
|
|
||||||
if let Some(param_list) = src.value.param_list() {
|
|
||||||
if let Some(self_param) = param_list.self_param() {
|
|
||||||
let self_type = if let Some(type_ref) = self_param.ascribed_type() {
|
|
||||||
TypeRef::from_ast(&ctx, type_ref)
|
|
||||||
} else {
|
|
||||||
let self_type = TypeRef::Path(name![Self].into());
|
|
||||||
match self_param.kind() {
|
|
||||||
ast::SelfParamKind::Owned => self_type,
|
|
||||||
ast::SelfParamKind::Ref => {
|
|
||||||
TypeRef::Reference(Box::new(self_type), Mutability::Shared)
|
|
||||||
}
|
|
||||||
ast::SelfParamKind::MutRef => {
|
|
||||||
TypeRef::Reference(Box::new(self_type), Mutability::Mut)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
params.push(self_type);
|
|
||||||
has_self_param = true;
|
|
||||||
}
|
|
||||||
for param in param_list.params() {
|
|
||||||
let type_ref = TypeRef::from_ast_opt(&ctx, param.ascribed_type());
|
|
||||||
params.push(type_ref);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
let attrs = Attrs::new(&src.value, &Hygiene::new(db.upcast(), src.file_id));
|
|
||||||
|
|
||||||
let ret_type = if let Some(type_ref) = src.value.ret_type().and_then(|rt| rt.type_ref()) {
|
Arc::new(FunctionData {
|
||||||
TypeRef::from_ast(&ctx, type_ref)
|
name: func.name.clone(),
|
||||||
} else {
|
params: func.params.to_vec(),
|
||||||
TypeRef::unit()
|
ret_type: func.ret_type.clone(),
|
||||||
};
|
attrs: item_tree.attrs(loc.id.value.into()).clone(),
|
||||||
|
has_self_param: func.has_self_param,
|
||||||
let ret_type = if src.value.async_token().is_some() {
|
is_unsafe: func.is_unsafe,
|
||||||
let future_impl = desugar_future_path(ret_type);
|
visibility: item_tree[func.visibility].clone(),
|
||||||
let ty_bound = TypeBound::Path(future_impl);
|
})
|
||||||
TypeRef::ImplTrait(vec![ty_bound])
|
|
||||||
} else {
|
|
||||||
ret_type
|
|
||||||
};
|
|
||||||
|
|
||||||
let is_unsafe = src.value.unsafe_token().is_some();
|
|
||||||
|
|
||||||
let vis_default = RawVisibility::default_for_container(loc.container);
|
|
||||||
let visibility =
|
|
||||||
RawVisibility::from_ast_with_default(db, vis_default, src.map(|s| s.visibility()));
|
|
||||||
|
|
||||||
let sig =
|
|
||||||
FunctionData { name, params, ret_type, has_self_param, is_unsafe, visibility, attrs };
|
|
||||||
Arc::new(sig)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn desugar_future_path(orig: TypeRef) -> Path {
|
|
||||||
let path = path![core::future::Future];
|
|
||||||
let mut generic_args: Vec<_> = std::iter::repeat(None).take(path.segments.len() - 1).collect();
|
|
||||||
let mut last = GenericArgs::empty();
|
|
||||||
last.bindings.push(AssociatedTypeBinding {
|
|
||||||
name: name![Output],
|
|
||||||
type_ref: Some(orig),
|
|
||||||
bounds: Vec::new(),
|
|
||||||
});
|
|
||||||
generic_args.push(Some(Arc::new(last)));
|
|
||||||
|
|
||||||
Path::from_known_path(path, generic_args)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||||
pub struct TypeAliasData {
|
pub struct TypeAliasData {
|
||||||
pub name: Name,
|
pub name: Name,
|
||||||
pub type_ref: Option<TypeRef>,
|
pub type_ref: Option<TypeRef>,
|
||||||
pub visibility: RawVisibility,
|
pub visibility: RawVisibility,
|
||||||
|
/// Bounds restricting the type alias itself (eg. `type Ty: Bound;` in a trait or impl).
|
||||||
pub bounds: Vec<TypeBound>,
|
pub bounds: Vec<TypeBound>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -126,22 +63,15 @@ impl TypeAliasData {
|
||||||
typ: TypeAliasId,
|
typ: TypeAliasId,
|
||||||
) -> Arc<TypeAliasData> {
|
) -> Arc<TypeAliasData> {
|
||||||
let loc = typ.lookup(db);
|
let loc = typ.lookup(db);
|
||||||
let node = loc.source(db);
|
let item_tree = db.item_tree(loc.id.file_id);
|
||||||
let name = node.value.name().map_or_else(Name::missing, |n| n.as_name());
|
let typ = &item_tree[loc.id.value];
|
||||||
let lower_ctx = LowerCtx::new(db, node.file_id);
|
|
||||||
let type_ref = node.value.type_ref().map(|it| TypeRef::from_ast(&lower_ctx, it));
|
Arc::new(TypeAliasData {
|
||||||
let vis_default = RawVisibility::default_for_container(loc.container);
|
name: typ.name.clone(),
|
||||||
let visibility = RawVisibility::from_ast_with_default(
|
type_ref: typ.type_ref.clone(),
|
||||||
db,
|
visibility: item_tree[typ.visibility].clone(),
|
||||||
vis_default,
|
bounds: typ.bounds.to_vec(),
|
||||||
node.as_ref().map(|n| n.visibility()),
|
})
|
||||||
);
|
|
||||||
let bounds = if let Some(bound_list) = node.value.type_bound_list() {
|
|
||||||
bound_list.bounds().map(|it| TypeBound::from_ast(&lower_ctx, it)).collect()
|
|
||||||
} else {
|
|
||||||
Vec::new()
|
|
||||||
};
|
|
||||||
Arc::new(TypeAliasData { name, type_ref, visibility, bounds })
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -155,30 +85,24 @@ pub struct TraitData {
|
||||||
impl TraitData {
|
impl TraitData {
|
||||||
pub(crate) fn trait_data_query(db: &dyn DefDatabase, tr: TraitId) -> Arc<TraitData> {
|
pub(crate) fn trait_data_query(db: &dyn DefDatabase, tr: TraitId) -> Arc<TraitData> {
|
||||||
let tr_loc = tr.lookup(db);
|
let tr_loc = tr.lookup(db);
|
||||||
let src = tr_loc.source(db);
|
let item_tree = db.item_tree(tr_loc.id.file_id);
|
||||||
let name = src.value.name().map_or_else(Name::missing, |n| n.as_name());
|
let tr_def = &item_tree[tr_loc.id.value];
|
||||||
let auto = src.value.auto_token().is_some();
|
let name = tr_def.name.clone();
|
||||||
|
let auto = tr_def.auto;
|
||||||
let module_id = tr_loc.container.module(db);
|
let module_id = tr_loc.container.module(db);
|
||||||
|
|
||||||
let container = AssocContainerId::TraitId(tr);
|
let container = AssocContainerId::TraitId(tr);
|
||||||
let mut items = Vec::new();
|
let mut expander = Expander::new(db, tr_loc.id.file_id, module_id);
|
||||||
|
|
||||||
|
let items = collect_items(
|
||||||
|
db,
|
||||||
|
module_id,
|
||||||
|
&mut expander,
|
||||||
|
tr_def.items.iter().copied(),
|
||||||
|
tr_loc.id.file_id,
|
||||||
|
container,
|
||||||
|
100,
|
||||||
|
);
|
||||||
|
|
||||||
if let Some(item_list) = src.value.item_list() {
|
|
||||||
let mut expander = Expander::new(db, tr_loc.ast_id.file_id, module_id);
|
|
||||||
items.extend(collect_items(
|
|
||||||
db,
|
|
||||||
&mut expander,
|
|
||||||
item_list.assoc_items(),
|
|
||||||
src.file_id,
|
|
||||||
container,
|
|
||||||
));
|
|
||||||
items.extend(collect_items_in_macros(
|
|
||||||
db,
|
|
||||||
&mut expander,
|
|
||||||
&src.with_value(item_list),
|
|
||||||
container,
|
|
||||||
));
|
|
||||||
}
|
|
||||||
Arc::new(TraitData { name, items, auto })
|
Arc::new(TraitData { name, items, auto })
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -209,33 +133,28 @@ impl ImplData {
|
||||||
pub(crate) fn impl_data_query(db: &dyn DefDatabase, id: ImplId) -> Arc<ImplData> {
|
pub(crate) fn impl_data_query(db: &dyn DefDatabase, id: ImplId) -> Arc<ImplData> {
|
||||||
let _p = profile("impl_data_query");
|
let _p = profile("impl_data_query");
|
||||||
let impl_loc = id.lookup(db);
|
let impl_loc = id.lookup(db);
|
||||||
let src = impl_loc.source(db);
|
|
||||||
let lower_ctx = LowerCtx::new(db, src.file_id);
|
|
||||||
|
|
||||||
let target_trait = src.value.target_trait().map(|it| TypeRef::from_ast(&lower_ctx, it));
|
let item_tree = db.item_tree(impl_loc.id.file_id);
|
||||||
let target_type = TypeRef::from_ast_opt(&lower_ctx, src.value.target_type());
|
let impl_def = &item_tree[impl_loc.id.value];
|
||||||
let is_negative = src.value.excl_token().is_some();
|
let target_trait = impl_def.target_trait.clone();
|
||||||
|
let target_type = impl_def.target_type.clone();
|
||||||
|
let is_negative = impl_def.is_negative;
|
||||||
let module_id = impl_loc.container.module(db);
|
let module_id = impl_loc.container.module(db);
|
||||||
let container = AssocContainerId::ImplId(id);
|
let container = AssocContainerId::ImplId(id);
|
||||||
|
let mut expander = Expander::new(db, impl_loc.id.file_id, module_id);
|
||||||
|
|
||||||
let mut items: Vec<AssocItemId> = Vec::new();
|
let items = collect_items(
|
||||||
|
db,
|
||||||
|
module_id,
|
||||||
|
&mut expander,
|
||||||
|
impl_def.items.iter().copied(),
|
||||||
|
impl_loc.id.file_id,
|
||||||
|
container,
|
||||||
|
100,
|
||||||
|
);
|
||||||
|
let items = items.into_iter().map(|(_, item)| item).collect();
|
||||||
|
|
||||||
if let Some(item_list) = src.value.item_list() {
|
Arc::new(ImplData { target_trait, target_type, items, is_negative })
|
||||||
let mut expander = Expander::new(db, impl_loc.ast_id.file_id, module_id);
|
|
||||||
items.extend(
|
|
||||||
collect_items(db, &mut expander, item_list.assoc_items(), src.file_id, container)
|
|
||||||
.into_iter()
|
|
||||||
.map(|(_, item)| item),
|
|
||||||
);
|
|
||||||
items.extend(
|
|
||||||
collect_items_in_macros(db, &mut expander, &src.with_value(item_list), container)
|
|
||||||
.into_iter()
|
|
||||||
.map(|(_, item)| item),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
let res = ImplData { target_trait, target_type, items, is_negative };
|
|
||||||
Arc::new(res)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -250,22 +169,14 @@ pub struct ConstData {
|
||||||
impl ConstData {
|
impl ConstData {
|
||||||
pub(crate) fn const_data_query(db: &dyn DefDatabase, konst: ConstId) -> Arc<ConstData> {
|
pub(crate) fn const_data_query(db: &dyn DefDatabase, konst: ConstId) -> Arc<ConstData> {
|
||||||
let loc = konst.lookup(db);
|
let loc = konst.lookup(db);
|
||||||
let node = loc.source(db);
|
let item_tree = db.item_tree(loc.id.file_id);
|
||||||
let vis_default = RawVisibility::default_for_container(loc.container);
|
let konst = &item_tree[loc.id.value];
|
||||||
Arc::new(ConstData::new(db, vis_default, node))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn new<N: NameOwner + TypeAscriptionOwner + VisibilityOwner>(
|
Arc::new(ConstData {
|
||||||
db: &dyn DefDatabase,
|
name: konst.name.clone(),
|
||||||
vis_default: RawVisibility,
|
type_ref: konst.type_ref.clone(),
|
||||||
node: InFile<N>,
|
visibility: item_tree[konst.visibility].clone(),
|
||||||
) -> ConstData {
|
})
|
||||||
let ctx = LowerCtx::new(db, node.file_id);
|
|
||||||
let name = node.value.name().map(|n| n.as_name());
|
|
||||||
let type_ref = TypeRef::from_ast_opt(&ctx, node.value.ascribed_type());
|
|
||||||
let visibility =
|
|
||||||
RawVisibility::from_ast_with_default(db, vis_default, node.map(|n| n.visibility()));
|
|
||||||
ConstData { name, type_ref, visibility }
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -279,44 +190,25 @@ pub struct StaticData {
|
||||||
|
|
||||||
impl StaticData {
|
impl StaticData {
|
||||||
pub(crate) fn static_data_query(db: &dyn DefDatabase, konst: StaticId) -> Arc<StaticData> {
|
pub(crate) fn static_data_query(db: &dyn DefDatabase, konst: StaticId) -> Arc<StaticData> {
|
||||||
let node = konst.lookup(db).source(db);
|
let node = konst.lookup(db);
|
||||||
let ctx = LowerCtx::new(db, node.file_id);
|
let item_tree = db.item_tree(node.id.file_id);
|
||||||
|
let statik = &item_tree[node.id.value];
|
||||||
|
|
||||||
let name = node.value.name().map(|n| n.as_name());
|
Arc::new(StaticData {
|
||||||
let type_ref = TypeRef::from_ast_opt(&ctx, node.value.ascribed_type());
|
name: Some(statik.name.clone()),
|
||||||
let mutable = node.value.mut_token().is_some();
|
type_ref: statik.type_ref.clone(),
|
||||||
let visibility = RawVisibility::from_ast_with_default(
|
visibility: item_tree[statik.visibility].clone(),
|
||||||
db,
|
mutable: statik.mutable,
|
||||||
RawVisibility::private(),
|
})
|
||||||
node.map(|n| n.visibility()),
|
|
||||||
);
|
|
||||||
|
|
||||||
Arc::new(StaticData { name, type_ref, visibility, mutable })
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn collect_items_in_macros(
|
fn collect_items(
|
||||||
db: &dyn DefDatabase,
|
db: &dyn DefDatabase,
|
||||||
|
module: ModuleId,
|
||||||
expander: &mut Expander,
|
expander: &mut Expander,
|
||||||
impl_def: &InFile<ast::ItemList>,
|
assoc_items: impl Iterator<Item = AssocItem>,
|
||||||
container: AssocContainerId,
|
file_id: crate::HirFileId,
|
||||||
) -> Vec<(Name, AssocItemId)> {
|
|
||||||
let mut res = Vec::new();
|
|
||||||
|
|
||||||
// We set a limit to protect against infinite recursion
|
|
||||||
let limit = 100;
|
|
||||||
|
|
||||||
for m in impl_def.value.syntax().children().filter_map(ast::MacroCall::cast) {
|
|
||||||
res.extend(collect_items_in_macro(db, expander, m, container, limit))
|
|
||||||
}
|
|
||||||
|
|
||||||
res
|
|
||||||
}
|
|
||||||
|
|
||||||
fn collect_items_in_macro(
|
|
||||||
db: &dyn DefDatabase,
|
|
||||||
expander: &mut Expander,
|
|
||||||
m: ast::MacroCall,
|
|
||||||
container: AssocContainerId,
|
container: AssocContainerId,
|
||||||
limit: usize,
|
limit: usize,
|
||||||
) -> Vec<(Name, AssocItemId)> {
|
) -> Vec<(Name, AssocItemId)> {
|
||||||
|
@ -324,62 +216,62 @@ fn collect_items_in_macro(
|
||||||
return Vec::new();
|
return Vec::new();
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some((mark, items)) = expander.enter_expand(db, None, m) {
|
let item_tree = db.item_tree(file_id);
|
||||||
let items: InFile<ast::MacroItems> = expander.to_source(items);
|
let cfg_options = db.crate_graph()[module.krate].cfg_options.clone();
|
||||||
let mut res = collect_items(
|
|
||||||
db,
|
|
||||||
expander,
|
|
||||||
items.value.items().filter_map(|it| AssocItem::cast(it.syntax().clone())),
|
|
||||||
items.file_id,
|
|
||||||
container,
|
|
||||||
);
|
|
||||||
|
|
||||||
// Recursive collect macros
|
let mut items = Vec::new();
|
||||||
// Note that ast::ModuleItem do not include ast::MacroCall
|
for item in assoc_items {
|
||||||
// We cannot use ModuleItemOwner::items here
|
match item {
|
||||||
for it in items.value.syntax().children().filter_map(ast::MacroCall::cast) {
|
AssocItem::Function(id) => {
|
||||||
res.extend(collect_items_in_macro(db, expander, it, container, limit - 1))
|
let item = &item_tree[id];
|
||||||
}
|
let attrs = item_tree.attrs(id.into());
|
||||||
expander.exit(db, mark);
|
if !attrs.is_cfg_enabled(&cfg_options) {
|
||||||
res
|
continue;
|
||||||
} else {
|
|
||||||
Vec::new()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn collect_items(
|
|
||||||
db: &dyn DefDatabase,
|
|
||||||
expander: &mut Expander,
|
|
||||||
assoc_items: impl Iterator<Item = AssocItem>,
|
|
||||||
file_id: crate::HirFileId,
|
|
||||||
container: AssocContainerId,
|
|
||||||
) -> Vec<(Name, AssocItemId)> {
|
|
||||||
let items = db.ast_id_map(file_id);
|
|
||||||
|
|
||||||
assoc_items
|
|
||||||
.filter_map(|item_node| match item_node {
|
|
||||||
ast::AssocItem::FnDef(it) => {
|
|
||||||
let name = it.name().map_or_else(Name::missing, |it| it.as_name());
|
|
||||||
if !expander.is_cfg_enabled(&it) {
|
|
||||||
return None;
|
|
||||||
}
|
}
|
||||||
let def = FunctionLoc { container, ast_id: AstId::new(file_id, items.ast_id(&it)) }
|
let def = FunctionLoc { container, id: ItemTreeId::new(file_id, id) }.intern(db);
|
||||||
.intern(db);
|
items.push((item.name.clone(), def.into()));
|
||||||
Some((name, def.into()))
|
|
||||||
}
|
}
|
||||||
ast::AssocItem::ConstDef(it) => {
|
// FIXME: cfg?
|
||||||
let name = it.name().map_or_else(Name::missing, |it| it.as_name());
|
AssocItem::Const(id) => {
|
||||||
let def = ConstLoc { container, ast_id: AstId::new(file_id, items.ast_id(&it)) }
|
let item = &item_tree[id];
|
||||||
.intern(db);
|
let name = match item.name.clone() {
|
||||||
Some((name, def.into()))
|
Some(name) => name,
|
||||||
|
None => continue,
|
||||||
|
};
|
||||||
|
let def = ConstLoc { container, id: ItemTreeId::new(file_id, id) }.intern(db);
|
||||||
|
items.push((name, def.into()));
|
||||||
}
|
}
|
||||||
ast::AssocItem::TypeAliasDef(it) => {
|
AssocItem::TypeAlias(id) => {
|
||||||
let name = it.name().map_or_else(Name::missing, |it| it.as_name());
|
let item = &item_tree[id];
|
||||||
let def =
|
let def = TypeAliasLoc { container, id: ItemTreeId::new(file_id, id) }.intern(db);
|
||||||
TypeAliasLoc { container, ast_id: AstId::new(file_id, items.ast_id(&it)) }
|
items.push((item.name.clone(), def.into()));
|
||||||
.intern(db);
|
|
||||||
Some((name, def.into()))
|
|
||||||
}
|
}
|
||||||
})
|
AssocItem::MacroCall(call) => {
|
||||||
.collect()
|
let call = &item_tree[call];
|
||||||
|
let ast_id_map = db.ast_id_map(file_id);
|
||||||
|
let root = db.parse_or_expand(file_id).unwrap();
|
||||||
|
let call = ast_id_map.get(call.ast_id).to_node(&root);
|
||||||
|
|
||||||
|
if let Some((mark, mac)) = expander.enter_expand(db, None, call) {
|
||||||
|
let src: InFile<ast::MacroItems> = expander.to_source(mac);
|
||||||
|
let item_tree = db.item_tree(src.file_id);
|
||||||
|
let iter =
|
||||||
|
item_tree.top_level_items().iter().filter_map(ModItem::as_assoc_item);
|
||||||
|
items.extend(collect_items(
|
||||||
|
db,
|
||||||
|
module,
|
||||||
|
expander,
|
||||||
|
iter,
|
||||||
|
src.file_id,
|
||||||
|
container,
|
||||||
|
limit - 1,
|
||||||
|
));
|
||||||
|
|
||||||
|
expander.exit(db, mark);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
items
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,8 +14,9 @@ use crate::{
|
||||||
docs::Documentation,
|
docs::Documentation,
|
||||||
generics::GenericParams,
|
generics::GenericParams,
|
||||||
import_map::ImportMap,
|
import_map::ImportMap,
|
||||||
|
item_tree::ItemTree,
|
||||||
lang_item::{LangItemTarget, LangItems},
|
lang_item::{LangItemTarget, LangItems},
|
||||||
nameres::{raw::RawItems, CrateDefMap},
|
nameres::CrateDefMap,
|
||||||
AttrDefId, ConstId, ConstLoc, DefWithBodyId, EnumId, EnumLoc, FunctionId, FunctionLoc,
|
AttrDefId, ConstId, ConstLoc, DefWithBodyId, EnumId, EnumLoc, FunctionId, FunctionLoc,
|
||||||
GenericDefId, ImplId, ImplLoc, ModuleId, StaticId, StaticLoc, StructId, StructLoc, TraitId,
|
GenericDefId, ImplId, ImplLoc, ModuleId, StaticId, StaticLoc, StructId, StructLoc, TraitId,
|
||||||
TraitLoc, TypeAliasId, TypeAliasLoc, UnionId, UnionLoc,
|
TraitLoc, TypeAliasId, TypeAliasLoc, UnionId, UnionLoc,
|
||||||
|
@ -45,8 +46,8 @@ pub trait InternDatabase: SourceDatabase {
|
||||||
|
|
||||||
#[salsa::query_group(DefDatabaseStorage)]
|
#[salsa::query_group(DefDatabaseStorage)]
|
||||||
pub trait DefDatabase: InternDatabase + AstDatabase + Upcast<dyn AstDatabase> {
|
pub trait DefDatabase: InternDatabase + AstDatabase + Upcast<dyn AstDatabase> {
|
||||||
#[salsa::invoke(RawItems::raw_items_query)]
|
#[salsa::invoke(ItemTree::item_tree_query)]
|
||||||
fn raw_items(&self, file_id: HirFileId) -> Arc<RawItems>;
|
fn item_tree(&self, file_id: HirFileId) -> Arc<ItemTree>;
|
||||||
|
|
||||||
#[salsa::invoke(crate_def_map_wait)]
|
#[salsa::invoke(crate_def_map_wait)]
|
||||||
#[salsa::transparent]
|
#[salsa::transparent]
|
||||||
|
|
|
@ -42,7 +42,7 @@ pub enum TypeParamProvenance {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Data about the generic parameters of a function, struct, impl, etc.
|
/// Data about the generic parameters of a function, struct, impl, etc.
|
||||||
#[derive(Clone, PartialEq, Eq, Debug)]
|
#[derive(Clone, PartialEq, Eq, Debug, Default)]
|
||||||
pub struct GenericParams {
|
pub struct GenericParams {
|
||||||
pub types: Arena<TypeParamData>,
|
pub types: Arena<TypeParamData>,
|
||||||
// lifetimes: Arena<LocalLifetimeParamId, LifetimeParamData>,
|
// lifetimes: Arena<LocalLifetimeParamId, LifetimeParamData>,
|
||||||
|
@ -74,8 +74,53 @@ impl GenericParams {
|
||||||
def: GenericDefId,
|
def: GenericDefId,
|
||||||
) -> Arc<GenericParams> {
|
) -> Arc<GenericParams> {
|
||||||
let _p = profile("generic_params_query");
|
let _p = profile("generic_params_query");
|
||||||
let (params, _source_map) = GenericParams::new(db, def);
|
|
||||||
Arc::new(params)
|
let generics = match def {
|
||||||
|
GenericDefId::FunctionId(id) => {
|
||||||
|
let id = id.lookup(db).id;
|
||||||
|
let tree = db.item_tree(id.file_id);
|
||||||
|
let item = &tree[id.value];
|
||||||
|
tree[item.generic_params].clone()
|
||||||
|
}
|
||||||
|
GenericDefId::AdtId(AdtId::StructId(id)) => {
|
||||||
|
let id = id.lookup(db).id;
|
||||||
|
let tree = db.item_tree(id.file_id);
|
||||||
|
let item = &tree[id.value];
|
||||||
|
tree[item.generic_params].clone()
|
||||||
|
}
|
||||||
|
GenericDefId::AdtId(AdtId::EnumId(id)) => {
|
||||||
|
let id = id.lookup(db).id;
|
||||||
|
let tree = db.item_tree(id.file_id);
|
||||||
|
let item = &tree[id.value];
|
||||||
|
tree[item.generic_params].clone()
|
||||||
|
}
|
||||||
|
GenericDefId::AdtId(AdtId::UnionId(id)) => {
|
||||||
|
let id = id.lookup(db).id;
|
||||||
|
let tree = db.item_tree(id.file_id);
|
||||||
|
let item = &tree[id.value];
|
||||||
|
tree[item.generic_params].clone()
|
||||||
|
}
|
||||||
|
GenericDefId::TraitId(id) => {
|
||||||
|
let id = id.lookup(db).id;
|
||||||
|
let tree = db.item_tree(id.file_id);
|
||||||
|
let item = &tree[id.value];
|
||||||
|
tree[item.generic_params].clone()
|
||||||
|
}
|
||||||
|
GenericDefId::TypeAliasId(id) => {
|
||||||
|
let id = id.lookup(db).id;
|
||||||
|
let tree = db.item_tree(id.file_id);
|
||||||
|
let item = &tree[id.value];
|
||||||
|
tree[item.generic_params].clone()
|
||||||
|
}
|
||||||
|
GenericDefId::ImplId(id) => {
|
||||||
|
let id = id.lookup(db).id;
|
||||||
|
let tree = db.item_tree(id.file_id);
|
||||||
|
let item = &tree[id.value];
|
||||||
|
tree[item.generic_params].clone()
|
||||||
|
}
|
||||||
|
GenericDefId::EnumVariantId(_) | GenericDefId::ConstId(_) => GenericParams::default(),
|
||||||
|
};
|
||||||
|
Arc::new(generics)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn new(db: &dyn DefDatabase, def: GenericDefId) -> (GenericParams, InFile<SourceMap>) {
|
fn new(db: &dyn DefDatabase, def: GenericDefId) -> (GenericParams, InFile<SourceMap>) {
|
||||||
|
@ -156,7 +201,12 @@ impl GenericParams {
|
||||||
(generics, InFile::new(file_id, sm))
|
(generics, InFile::new(file_id, sm))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn fill(&mut self, lower_ctx: &LowerCtx, sm: &mut SourceMap, node: &dyn TypeParamsOwner) {
|
pub(crate) fn fill(
|
||||||
|
&mut self,
|
||||||
|
lower_ctx: &LowerCtx,
|
||||||
|
sm: &mut SourceMap,
|
||||||
|
node: &dyn TypeParamsOwner,
|
||||||
|
) {
|
||||||
if let Some(params) = node.type_param_list() {
|
if let Some(params) = node.type_param_list() {
|
||||||
self.fill_params(lower_ctx, sm, params)
|
self.fill_params(lower_ctx, sm, params)
|
||||||
}
|
}
|
||||||
|
@ -165,7 +215,7 @@ impl GenericParams {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn fill_bounds(
|
pub(crate) fn fill_bounds(
|
||||||
&mut self,
|
&mut self,
|
||||||
lower_ctx: &LowerCtx,
|
lower_ctx: &LowerCtx,
|
||||||
node: &dyn ast::TypeBoundsOwner,
|
node: &dyn ast::TypeBoundsOwner,
|
||||||
|
@ -229,7 +279,7 @@ impl GenericParams {
|
||||||
.push(WherePredicate { target: WherePredicateTarget::TypeRef(type_ref), bound });
|
.push(WherePredicate { target: WherePredicateTarget::TypeRef(type_ref), bound });
|
||||||
}
|
}
|
||||||
|
|
||||||
fn fill_implicit_impl_trait_args(&mut self, type_ref: &TypeRef) {
|
pub(crate) fn fill_implicit_impl_trait_args(&mut self, type_ref: &TypeRef) {
|
||||||
type_ref.walk(&mut |type_ref| {
|
type_ref.walk(&mut |type_ref| {
|
||||||
if let TypeRef::ImplTrait(bounds) = type_ref {
|
if let TypeRef::ImplTrait(bounds) = type_ref {
|
||||||
let param = TypeParamData {
|
let param = TypeParamData {
|
||||||
|
|
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 dyn_map;
|
||||||
pub mod keys;
|
pub mod keys;
|
||||||
|
|
||||||
|
pub mod item_tree;
|
||||||
|
|
||||||
pub mod adt;
|
pub mod adt;
|
||||||
pub mod data;
|
pub mod data;
|
||||||
pub mod generics;
|
pub mod generics;
|
||||||
|
@ -48,7 +50,7 @@ pub mod import_map;
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod test_db;
|
mod test_db;
|
||||||
|
|
||||||
use std::hash::Hash;
|
use std::hash::{Hash, Hasher};
|
||||||
|
|
||||||
use hir_expand::{
|
use hir_expand::{
|
||||||
ast_id_map::FileAstId, eager::expand_eager_macro, hygiene::Hygiene, AstId, HirFileId, InFile,
|
ast_id_map::FileAstId, eager::expand_eager_macro, hygiene::Hygiene, AstId, HirFileId, InFile,
|
||||||
|
@ -56,10 +58,13 @@ use hir_expand::{
|
||||||
};
|
};
|
||||||
use ra_arena::Idx;
|
use ra_arena::Idx;
|
||||||
use ra_db::{impl_intern_key, salsa, CrateId};
|
use ra_db::{impl_intern_key, salsa, CrateId};
|
||||||
use ra_syntax::{ast, AstNode};
|
use ra_syntax::ast;
|
||||||
|
|
||||||
use crate::body::Expander;
|
|
||||||
use crate::builtin_type::BuiltinType;
|
use crate::builtin_type::BuiltinType;
|
||||||
|
use item_tree::{
|
||||||
|
Const, Enum, Function, Impl, ItemTreeId, ItemTreeNode, ModItem, Static, Struct, Trait,
|
||||||
|
TypeAlias, Union,
|
||||||
|
};
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||||
pub struct ModuleId {
|
pub struct ModuleId {
|
||||||
|
@ -70,16 +75,62 @@ pub struct ModuleId {
|
||||||
/// An ID of a module, **local** to a specific crate
|
/// An ID of a module, **local** to a specific crate
|
||||||
pub type LocalModuleId = Idx<nameres::ModuleData>;
|
pub type LocalModuleId = Idx<nameres::ModuleData>;
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
#[derive(Debug)]
|
||||||
pub struct ItemLoc<N: AstNode> {
|
pub struct ItemLoc<N: ItemTreeNode> {
|
||||||
pub container: ContainerId,
|
pub container: ContainerId,
|
||||||
pub ast_id: AstId<N>,
|
pub id: ItemTreeId<N>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
impl<N: ItemTreeNode> Clone for ItemLoc<N> {
|
||||||
pub struct AssocItemLoc<N: AstNode> {
|
fn clone(&self) -> Self {
|
||||||
|
Self { container: self.container, id: self.id }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<N: ItemTreeNode> Copy for ItemLoc<N> {}
|
||||||
|
|
||||||
|
impl<N: ItemTreeNode> PartialEq for ItemLoc<N> {
|
||||||
|
fn eq(&self, other: &Self) -> bool {
|
||||||
|
self.container == other.container && self.id == other.id
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<N: ItemTreeNode> Eq for ItemLoc<N> {}
|
||||||
|
|
||||||
|
impl<N: ItemTreeNode> Hash for ItemLoc<N> {
|
||||||
|
fn hash<H: Hasher>(&self, state: &mut H) {
|
||||||
|
self.container.hash(state);
|
||||||
|
self.id.hash(state);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct AssocItemLoc<N: ItemTreeNode> {
|
||||||
pub container: AssocContainerId,
|
pub container: AssocContainerId,
|
||||||
pub ast_id: AstId<N>,
|
pub id: ItemTreeId<N>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<N: ItemTreeNode> Clone for AssocItemLoc<N> {
|
||||||
|
fn clone(&self) -> Self {
|
||||||
|
Self { container: self.container, id: self.id }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<N: ItemTreeNode> Copy for AssocItemLoc<N> {}
|
||||||
|
|
||||||
|
impl<N: ItemTreeNode> PartialEq for AssocItemLoc<N> {
|
||||||
|
fn eq(&self, other: &Self) -> bool {
|
||||||
|
self.container == other.container && self.id == other.id
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<N: ItemTreeNode> Eq for AssocItemLoc<N> {}
|
||||||
|
|
||||||
|
impl<N: ItemTreeNode> Hash for AssocItemLoc<N> {
|
||||||
|
fn hash<H: Hasher>(&self, state: &mut H) {
|
||||||
|
self.container.hash(state);
|
||||||
|
self.id.hash(state);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
macro_rules! impl_intern {
|
macro_rules! impl_intern {
|
||||||
|
@ -104,22 +155,22 @@ macro_rules! impl_intern {
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||||
pub struct FunctionId(salsa::InternId);
|
pub struct FunctionId(salsa::InternId);
|
||||||
type FunctionLoc = AssocItemLoc<ast::FnDef>;
|
type FunctionLoc = AssocItemLoc<Function>;
|
||||||
impl_intern!(FunctionId, FunctionLoc, intern_function, lookup_intern_function);
|
impl_intern!(FunctionId, FunctionLoc, intern_function, lookup_intern_function);
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||||
pub struct StructId(salsa::InternId);
|
pub struct StructId(salsa::InternId);
|
||||||
type StructLoc = ItemLoc<ast::StructDef>;
|
type StructLoc = ItemLoc<Struct>;
|
||||||
impl_intern!(StructId, StructLoc, intern_struct, lookup_intern_struct);
|
impl_intern!(StructId, StructLoc, intern_struct, lookup_intern_struct);
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||||
pub struct UnionId(salsa::InternId);
|
pub struct UnionId(salsa::InternId);
|
||||||
pub type UnionLoc = ItemLoc<ast::UnionDef>;
|
pub type UnionLoc = ItemLoc<Union>;
|
||||||
impl_intern!(UnionId, UnionLoc, intern_union, lookup_intern_union);
|
impl_intern!(UnionId, UnionLoc, intern_union, lookup_intern_union);
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||||
pub struct EnumId(salsa::InternId);
|
pub struct EnumId(salsa::InternId);
|
||||||
pub type EnumLoc = ItemLoc<ast::EnumDef>;
|
pub type EnumLoc = ItemLoc<Enum>;
|
||||||
impl_intern!(EnumId, EnumLoc, intern_enum, lookup_intern_enum);
|
impl_intern!(EnumId, EnumLoc, intern_enum, lookup_intern_enum);
|
||||||
|
|
||||||
// FIXME: rename to `VariantId`, only enums can ave variants
|
// FIXME: rename to `VariantId`, only enums can ave variants
|
||||||
|
@ -141,27 +192,27 @@ pub type LocalFieldId = Idx<adt::FieldData>;
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||||
pub struct ConstId(salsa::InternId);
|
pub struct ConstId(salsa::InternId);
|
||||||
type ConstLoc = AssocItemLoc<ast::ConstDef>;
|
type ConstLoc = AssocItemLoc<Const>;
|
||||||
impl_intern!(ConstId, ConstLoc, intern_const, lookup_intern_const);
|
impl_intern!(ConstId, ConstLoc, intern_const, lookup_intern_const);
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||||
pub struct StaticId(salsa::InternId);
|
pub struct StaticId(salsa::InternId);
|
||||||
pub type StaticLoc = ItemLoc<ast::StaticDef>;
|
pub type StaticLoc = ItemLoc<Static>;
|
||||||
impl_intern!(StaticId, StaticLoc, intern_static, lookup_intern_static);
|
impl_intern!(StaticId, StaticLoc, intern_static, lookup_intern_static);
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||||
pub struct TraitId(salsa::InternId);
|
pub struct TraitId(salsa::InternId);
|
||||||
pub type TraitLoc = ItemLoc<ast::TraitDef>;
|
pub type TraitLoc = ItemLoc<Trait>;
|
||||||
impl_intern!(TraitId, TraitLoc, intern_trait, lookup_intern_trait);
|
impl_intern!(TraitId, TraitLoc, intern_trait, lookup_intern_trait);
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||||
pub struct TypeAliasId(salsa::InternId);
|
pub struct TypeAliasId(salsa::InternId);
|
||||||
type TypeAliasLoc = AssocItemLoc<ast::TypeAliasDef>;
|
type TypeAliasLoc = AssocItemLoc<TypeAlias>;
|
||||||
impl_intern!(TypeAliasId, TypeAliasLoc, intern_type_alias, lookup_intern_type_alias);
|
impl_intern!(TypeAliasId, TypeAliasLoc, intern_type_alias, lookup_intern_type_alias);
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Ord, PartialOrd)]
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Ord, PartialOrd)]
|
||||||
pub struct ImplId(salsa::InternId);
|
pub struct ImplId(salsa::InternId);
|
||||||
type ImplLoc = ItemLoc<ast::ImplDef>;
|
type ImplLoc = ItemLoc<Impl>;
|
||||||
impl_intern!(ImplId, ImplLoc, intern_impl, lookup_intern_impl);
|
impl_intern!(ImplId, ImplLoc, intern_impl, lookup_intern_impl);
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||||
|
@ -365,7 +416,7 @@ impl HasModule for AssocContainerId {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<N: AstNode> HasModule for AssocItemLoc<N> {
|
impl<N: ItemTreeNode> HasModule for AssocItemLoc<N> {
|
||||||
fn module(&self, db: &dyn db::DefDatabase) -> ModuleId {
|
fn module(&self, db: &dyn db::DefDatabase) -> ModuleId {
|
||||||
self.container.module(db)
|
self.container.module(db)
|
||||||
}
|
}
|
||||||
|
@ -392,6 +443,16 @@ impl HasModule for DefWithBodyId {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl DefWithBodyId {
|
||||||
|
pub fn as_mod_item(self, db: &dyn db::DefDatabase) -> ModItem {
|
||||||
|
match self {
|
||||||
|
DefWithBodyId::FunctionId(it) => it.lookup(db).id.value.into(),
|
||||||
|
DefWithBodyId::StaticId(it) => it.lookup(db).id.value.into(),
|
||||||
|
DefWithBodyId::ConstId(it) => it.lookup(db).id.value.into(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl HasModule for GenericDefId {
|
impl HasModule for GenericDefId {
|
||||||
fn module(&self, db: &dyn db::DefDatabase) -> ModuleId {
|
fn module(&self, db: &dyn db::DefDatabase) -> ModuleId {
|
||||||
match self {
|
match self {
|
||||||
|
|
|
@ -47,7 +47,6 @@
|
||||||
//! path and, upon success, we run macro expansion and "collect module" phase on
|
//! path and, upon success, we run macro expansion and "collect module" phase on
|
||||||
//! the result
|
//! the result
|
||||||
|
|
||||||
pub(crate) mod raw;
|
|
||||||
mod collector;
|
mod collector;
|
||||||
mod mod_resolution;
|
mod mod_resolution;
|
||||||
mod path_resolution;
|
mod path_resolution;
|
||||||
|
|
|
@ -4,6 +4,7 @@
|
||||||
//! resolves imports and expands macros.
|
//! resolves imports and expands macros.
|
||||||
|
|
||||||
use hir_expand::{
|
use hir_expand::{
|
||||||
|
ast_id_map::FileAstId,
|
||||||
builtin_derive::find_builtin_derive,
|
builtin_derive::find_builtin_derive,
|
||||||
builtin_macro::find_builtin_macro,
|
builtin_macro::find_builtin_macro,
|
||||||
name::{name, AsName, Name},
|
name::{name, AsName, Name},
|
||||||
|
@ -19,13 +20,16 @@ use test_utils::mark;
|
||||||
use crate::{
|
use crate::{
|
||||||
attr::Attrs,
|
attr::Attrs,
|
||||||
db::DefDatabase,
|
db::DefDatabase,
|
||||||
|
item_tree::{
|
||||||
|
self, FileItemTreeId, ItemTree, ItemTreeId, MacroCall, Mod, ModItem, ModKind, StructDefKind,
|
||||||
|
},
|
||||||
nameres::{
|
nameres::{
|
||||||
diagnostics::DefDiagnostic, mod_resolution::ModDir, path_resolution::ReachedFixedPoint,
|
diagnostics::DefDiagnostic, mod_resolution::ModDir, path_resolution::ReachedFixedPoint,
|
||||||
raw, BuiltinShadowMode, CrateDefMap, ModuleData, ModuleOrigin, ResolveMode,
|
BuiltinShadowMode, CrateDefMap, ModuleData, ModuleOrigin, ResolveMode,
|
||||||
},
|
},
|
||||||
path::{ImportAlias, ModPath, PathKind},
|
path::{ImportAlias, ModPath, PathKind},
|
||||||
per_ns::PerNs,
|
per_ns::PerNs,
|
||||||
visibility::Visibility,
|
visibility::{RawVisibility, Visibility},
|
||||||
AdtId, AsMacroCall, AstId, AstIdWithPath, ConstLoc, ContainerId, EnumLoc, EnumVariantId,
|
AdtId, AsMacroCall, AstId, AstIdWithPath, ConstLoc, ContainerId, EnumLoc, EnumVariantId,
|
||||||
FunctionLoc, ImplLoc, Intern, LocalModuleId, ModuleDefId, ModuleId, StaticLoc, StructLoc,
|
FunctionLoc, ImplLoc, Intern, LocalModuleId, ModuleDefId, ModuleId, StaticLoc, StructLoc,
|
||||||
TraitLoc, TypeAliasLoc, UnionLoc,
|
TraitLoc, TypeAliasLoc, UnionLoc,
|
||||||
|
@ -101,11 +105,51 @@ impl PartialResolvedImport {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, Eq, PartialEq)]
|
||||||
|
struct Import {
|
||||||
|
pub path: ModPath,
|
||||||
|
pub alias: Option<ImportAlias>,
|
||||||
|
pub visibility: RawVisibility,
|
||||||
|
pub is_glob: bool,
|
||||||
|
pub is_prelude: bool,
|
||||||
|
pub is_extern_crate: bool,
|
||||||
|
pub is_macro_use: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Import {
|
||||||
|
fn from_use(tree: &ItemTree, id: FileItemTreeId<item_tree::Import>) -> Self {
|
||||||
|
let it = &tree[id];
|
||||||
|
let visibility = &tree[it.visibility];
|
||||||
|
Self {
|
||||||
|
path: it.path.clone(),
|
||||||
|
alias: it.alias.clone(),
|
||||||
|
visibility: visibility.clone(),
|
||||||
|
is_glob: it.is_glob,
|
||||||
|
is_prelude: it.is_prelude,
|
||||||
|
is_extern_crate: false,
|
||||||
|
is_macro_use: false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn from_extern_crate(tree: &ItemTree, id: FileItemTreeId<item_tree::ExternCrate>) -> Self {
|
||||||
|
let it = &tree[id];
|
||||||
|
let visibility = &tree[it.visibility];
|
||||||
|
Self {
|
||||||
|
path: it.path.clone(),
|
||||||
|
alias: it.alias.clone(),
|
||||||
|
visibility: visibility.clone(),
|
||||||
|
is_glob: false,
|
||||||
|
is_prelude: false,
|
||||||
|
is_extern_crate: true,
|
||||||
|
is_macro_use: it.is_macro_use,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, Eq, PartialEq)]
|
#[derive(Clone, Debug, Eq, PartialEq)]
|
||||||
struct ImportDirective {
|
struct ImportDirective {
|
||||||
module_id: LocalModuleId,
|
module_id: LocalModuleId,
|
||||||
import_id: raw::Import,
|
import: Import,
|
||||||
import: raw::ImportData,
|
|
||||||
status: PartialResolvedImport,
|
status: PartialResolvedImport,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -123,6 +167,13 @@ struct DeriveDirective {
|
||||||
ast_id: AstIdWithPath<ast::ModuleItem>,
|
ast_id: AstIdWithPath<ast::ModuleItem>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct DefData<'a> {
|
||||||
|
id: ModuleDefId,
|
||||||
|
name: &'a Name,
|
||||||
|
visibility: &'a RawVisibility,
|
||||||
|
has_constructor: bool,
|
||||||
|
}
|
||||||
|
|
||||||
/// Walks the tree of module recursively
|
/// Walks the tree of module recursively
|
||||||
struct DefCollector<'a> {
|
struct DefCollector<'a> {
|
||||||
db: &'a dyn DefDatabase,
|
db: &'a dyn DefDatabase,
|
||||||
|
@ -140,7 +191,7 @@ struct DefCollector<'a> {
|
||||||
impl DefCollector<'_> {
|
impl DefCollector<'_> {
|
||||||
fn collect(&mut self) {
|
fn collect(&mut self) {
|
||||||
let file_id = self.db.crate_graph()[self.def_map.krate].root_file_id;
|
let file_id = self.db.crate_graph()[self.def_map.krate].root_file_id;
|
||||||
let raw_items = self.db.raw_items(file_id.into());
|
let item_tree = self.db.item_tree(file_id.into());
|
||||||
let module_id = self.def_map.root;
|
let module_id = self.def_map.root;
|
||||||
self.def_map.modules[module_id].origin = ModuleOrigin::CrateRoot { definition: file_id };
|
self.def_map.modules[module_id].origin = ModuleOrigin::CrateRoot { definition: file_id };
|
||||||
ModCollector {
|
ModCollector {
|
||||||
|
@ -148,10 +199,10 @@ impl DefCollector<'_> {
|
||||||
macro_depth: 0,
|
macro_depth: 0,
|
||||||
module_id,
|
module_id,
|
||||||
file_id: file_id.into(),
|
file_id: file_id.into(),
|
||||||
raw_items: &raw_items,
|
item_tree: &item_tree,
|
||||||
mod_dir: ModDir::root(),
|
mod_dir: ModDir::root(),
|
||||||
}
|
}
|
||||||
.collect(raw_items.items());
|
.collect(item_tree.top_level_items());
|
||||||
|
|
||||||
// main name resolution fixed-point loop.
|
// main name resolution fixed-point loop.
|
||||||
let mut i = 0;
|
let mut i = 0;
|
||||||
|
@ -286,7 +337,7 @@ impl DefCollector<'_> {
|
||||||
fn import_macros_from_extern_crate(
|
fn import_macros_from_extern_crate(
|
||||||
&mut self,
|
&mut self,
|
||||||
current_module_id: LocalModuleId,
|
current_module_id: LocalModuleId,
|
||||||
import: &raw::ImportData,
|
import: &item_tree::ExternCrate,
|
||||||
) {
|
) {
|
||||||
log::debug!(
|
log::debug!(
|
||||||
"importing macros from extern crate: {:?} ({:?})",
|
"importing macros from extern crate: {:?} ({:?})",
|
||||||
|
@ -352,11 +403,7 @@ impl DefCollector<'_> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn resolve_import(
|
fn resolve_import(&self, module_id: LocalModuleId, import: &Import) -> PartialResolvedImport {
|
||||||
&self,
|
|
||||||
module_id: LocalModuleId,
|
|
||||||
import: &raw::ImportData,
|
|
||||||
) -> PartialResolvedImport {
|
|
||||||
log::debug!("resolving import: {:?} ({:?})", import, self.def_map.edition);
|
log::debug!("resolving import: {:?} ({:?})", import, self.def_map.edition);
|
||||||
if import.is_extern_crate {
|
if import.is_extern_crate {
|
||||||
let res = self.def_map.resolve_name_in_extern_prelude(
|
let res = self.def_map.resolve_name_in_extern_prelude(
|
||||||
|
@ -649,17 +696,17 @@ impl DefCollector<'_> {
|
||||||
depth: usize,
|
depth: usize,
|
||||||
) {
|
) {
|
||||||
let file_id: HirFileId = macro_call_id.as_file();
|
let file_id: HirFileId = macro_call_id.as_file();
|
||||||
let raw_items = self.db.raw_items(file_id);
|
let item_tree = self.db.item_tree(file_id);
|
||||||
let mod_dir = self.mod_dirs[&module_id].clone();
|
let mod_dir = self.mod_dirs[&module_id].clone();
|
||||||
ModCollector {
|
ModCollector {
|
||||||
def_collector: &mut *self,
|
def_collector: &mut *self,
|
||||||
macro_depth: depth,
|
macro_depth: depth,
|
||||||
file_id,
|
file_id,
|
||||||
module_id,
|
module_id,
|
||||||
raw_items: &raw_items,
|
item_tree: &item_tree,
|
||||||
mod_dir,
|
mod_dir,
|
||||||
}
|
}
|
||||||
.collect(raw_items.items());
|
.collect(item_tree.top_level_items());
|
||||||
}
|
}
|
||||||
|
|
||||||
fn finish(self) -> CrateDefMap {
|
fn finish(self) -> CrateDefMap {
|
||||||
|
@ -673,12 +720,12 @@ struct ModCollector<'a, 'b> {
|
||||||
macro_depth: usize,
|
macro_depth: usize,
|
||||||
module_id: LocalModuleId,
|
module_id: LocalModuleId,
|
||||||
file_id: HirFileId,
|
file_id: HirFileId,
|
||||||
raw_items: &'a raw::RawItems,
|
item_tree: &'a ItemTree,
|
||||||
mod_dir: ModDir,
|
mod_dir: ModDir,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ModCollector<'_, '_> {
|
impl ModCollector<'_, '_> {
|
||||||
fn collect(&mut self, items: &[raw::RawItem]) {
|
fn collect(&mut self, items: &[ModItem]) {
|
||||||
// Note: don't assert that inserted value is fresh: it's simply not true
|
// Note: don't assert that inserted value is fresh: it's simply not true
|
||||||
// for macros.
|
// for macros.
|
||||||
self.def_collector.mod_dirs.insert(self.module_id, self.mod_dir.clone());
|
self.def_collector.mod_dirs.insert(self.module_id, self.mod_dir.clone());
|
||||||
|
@ -695,64 +742,204 @@ impl ModCollector<'_, '_> {
|
||||||
// `#[macro_use] extern crate` is hoisted to imports macros before collecting
|
// `#[macro_use] extern crate` is hoisted to imports macros before collecting
|
||||||
// any other items.
|
// any other items.
|
||||||
for item in items {
|
for item in items {
|
||||||
if self.is_cfg_enabled(&item.attrs) {
|
if self.is_cfg_enabled(self.item_tree.attrs(*item)) {
|
||||||
if let raw::RawItemKind::Import(import_id) = item.kind {
|
if let ModItem::ExternCrate(id) = item {
|
||||||
let import = self.raw_items[import_id].clone();
|
let import = self.item_tree[*id].clone();
|
||||||
if import.is_extern_crate && import.is_macro_use {
|
if import.is_macro_use {
|
||||||
self.def_collector.import_macros_from_extern_crate(self.module_id, &import);
|
self.def_collector.import_macros_from_extern_crate(self.module_id, &import);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for item in items {
|
for &item in items {
|
||||||
if self.is_cfg_enabled(&item.attrs) {
|
let attrs = self.item_tree.attrs(item);
|
||||||
match item.kind {
|
if self.is_cfg_enabled(attrs) {
|
||||||
raw::RawItemKind::Module(m) => {
|
let module =
|
||||||
self.collect_module(&self.raw_items[m], &item.attrs)
|
ModuleId { krate: self.def_collector.def_map.krate, local_id: self.module_id };
|
||||||
}
|
let container = ContainerId::ModuleId(module);
|
||||||
raw::RawItemKind::Import(import_id) => {
|
|
||||||
|
let mut def = None;
|
||||||
|
match item {
|
||||||
|
ModItem::Mod(m) => self.collect_module(&self.item_tree[m], attrs),
|
||||||
|
ModItem::Import(import_id) => {
|
||||||
self.def_collector.unresolved_imports.push(ImportDirective {
|
self.def_collector.unresolved_imports.push(ImportDirective {
|
||||||
module_id: self.module_id,
|
module_id: self.module_id,
|
||||||
import_id,
|
import: Import::from_use(&self.item_tree, import_id),
|
||||||
import: self.raw_items[import_id].clone(),
|
|
||||||
status: PartialResolvedImport::Unresolved,
|
status: PartialResolvedImport::Unresolved,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
raw::RawItemKind::Def(def) => {
|
ModItem::ExternCrate(import_id) => {
|
||||||
self.define_def(&self.raw_items[def], &item.attrs)
|
self.def_collector.unresolved_imports.push(ImportDirective {
|
||||||
|
module_id: self.module_id,
|
||||||
|
import: Import::from_extern_crate(&self.item_tree, import_id),
|
||||||
|
status: PartialResolvedImport::Unresolved,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
raw::RawItemKind::Macro(mac) => self.collect_macro(&self.raw_items[mac]),
|
ModItem::MacroCall(mac) => self.collect_macro(&self.item_tree[mac]),
|
||||||
raw::RawItemKind::Impl(imp) => {
|
ModItem::Impl(imp) => {
|
||||||
let module = ModuleId {
|
let module = ModuleId {
|
||||||
krate: self.def_collector.def_map.krate,
|
krate: self.def_collector.def_map.krate,
|
||||||
local_id: self.module_id,
|
local_id: self.module_id,
|
||||||
};
|
};
|
||||||
let container = ContainerId::ModuleId(module);
|
let container = ContainerId::ModuleId(module);
|
||||||
let ast_id = self.raw_items[imp].ast_id;
|
let impl_id = ImplLoc { container, id: ItemTreeId::new(self.file_id, imp) }
|
||||||
let impl_id =
|
.intern(self.def_collector.db);
|
||||||
ImplLoc { container, ast_id: AstId::new(self.file_id, ast_id) }
|
|
||||||
.intern(self.def_collector.db);
|
|
||||||
self.def_collector.def_map.modules[self.module_id]
|
self.def_collector.def_map.modules[self.module_id]
|
||||||
.scope
|
.scope
|
||||||
.define_impl(impl_id)
|
.define_impl(impl_id)
|
||||||
}
|
}
|
||||||
|
ModItem::Function(id) => {
|
||||||
|
let func = &self.item_tree[id];
|
||||||
|
def = Some(DefData {
|
||||||
|
id: FunctionLoc {
|
||||||
|
container: container.into(),
|
||||||
|
id: ItemTreeId::new(self.file_id, id),
|
||||||
|
}
|
||||||
|
.intern(self.def_collector.db)
|
||||||
|
.into(),
|
||||||
|
name: &func.name,
|
||||||
|
visibility: &self.item_tree[func.visibility],
|
||||||
|
has_constructor: false,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
ModItem::Struct(id) => {
|
||||||
|
let it = &self.item_tree[id];
|
||||||
|
|
||||||
|
// FIXME: check attrs to see if this is an attribute macro invocation;
|
||||||
|
// in which case we don't add the invocation, just a single attribute
|
||||||
|
// macro invocation
|
||||||
|
self.collect_derives(attrs, it.ast_id.upcast());
|
||||||
|
|
||||||
|
def = Some(DefData {
|
||||||
|
id: StructLoc { container, id: ItemTreeId::new(self.file_id, id) }
|
||||||
|
.intern(self.def_collector.db)
|
||||||
|
.into(),
|
||||||
|
name: &it.name,
|
||||||
|
visibility: &self.item_tree[it.visibility],
|
||||||
|
has_constructor: it.kind != StructDefKind::Record,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
ModItem::Union(id) => {
|
||||||
|
let it = &self.item_tree[id];
|
||||||
|
|
||||||
|
// FIXME: check attrs to see if this is an attribute macro invocation;
|
||||||
|
// in which case we don't add the invocation, just a single attribute
|
||||||
|
// macro invocation
|
||||||
|
self.collect_derives(attrs, it.ast_id.upcast());
|
||||||
|
|
||||||
|
def = Some(DefData {
|
||||||
|
id: UnionLoc { container, id: ItemTreeId::new(self.file_id, id) }
|
||||||
|
.intern(self.def_collector.db)
|
||||||
|
.into(),
|
||||||
|
name: &it.name,
|
||||||
|
visibility: &self.item_tree[it.visibility],
|
||||||
|
has_constructor: false,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
ModItem::Enum(id) => {
|
||||||
|
let it = &self.item_tree[id];
|
||||||
|
|
||||||
|
// FIXME: check attrs to see if this is an attribute macro invocation;
|
||||||
|
// in which case we don't add the invocation, just a single attribute
|
||||||
|
// macro invocation
|
||||||
|
self.collect_derives(attrs, it.ast_id.upcast());
|
||||||
|
|
||||||
|
def = Some(DefData {
|
||||||
|
id: EnumLoc { container, id: ItemTreeId::new(self.file_id, id) }
|
||||||
|
.intern(self.def_collector.db)
|
||||||
|
.into(),
|
||||||
|
name: &it.name,
|
||||||
|
visibility: &self.item_tree[it.visibility],
|
||||||
|
has_constructor: false,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
ModItem::Const(id) => {
|
||||||
|
let it = &self.item_tree[id];
|
||||||
|
|
||||||
|
if let Some(name) = &it.name {
|
||||||
|
def = Some(DefData {
|
||||||
|
id: ConstLoc {
|
||||||
|
container: container.into(),
|
||||||
|
id: ItemTreeId::new(self.file_id, id),
|
||||||
|
}
|
||||||
|
.intern(self.def_collector.db)
|
||||||
|
.into(),
|
||||||
|
name,
|
||||||
|
visibility: &self.item_tree[it.visibility],
|
||||||
|
has_constructor: false,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ModItem::Static(id) => {
|
||||||
|
let it = &self.item_tree[id];
|
||||||
|
|
||||||
|
def = Some(DefData {
|
||||||
|
id: StaticLoc { container, id: ItemTreeId::new(self.file_id, id) }
|
||||||
|
.intern(self.def_collector.db)
|
||||||
|
.into(),
|
||||||
|
name: &it.name,
|
||||||
|
visibility: &self.item_tree[it.visibility],
|
||||||
|
has_constructor: false,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
ModItem::Trait(id) => {
|
||||||
|
let it = &self.item_tree[id];
|
||||||
|
|
||||||
|
def = Some(DefData {
|
||||||
|
id: TraitLoc { container, id: ItemTreeId::new(self.file_id, id) }
|
||||||
|
.intern(self.def_collector.db)
|
||||||
|
.into(),
|
||||||
|
name: &it.name,
|
||||||
|
visibility: &self.item_tree[it.visibility],
|
||||||
|
has_constructor: false,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
ModItem::TypeAlias(id) => {
|
||||||
|
let it = &self.item_tree[id];
|
||||||
|
|
||||||
|
def = Some(DefData {
|
||||||
|
id: TypeAliasLoc {
|
||||||
|
container: container.into(),
|
||||||
|
id: ItemTreeId::new(self.file_id, id),
|
||||||
|
}
|
||||||
|
.intern(self.def_collector.db)
|
||||||
|
.into(),
|
||||||
|
name: &it.name,
|
||||||
|
visibility: &self.item_tree[it.visibility],
|
||||||
|
has_constructor: false,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(DefData { id, name, visibility, has_constructor }) = def {
|
||||||
|
self.def_collector.def_map.modules[self.module_id].scope.define_def(id);
|
||||||
|
let vis = self
|
||||||
|
.def_collector
|
||||||
|
.def_map
|
||||||
|
.resolve_visibility(self.def_collector.db, self.module_id, visibility)
|
||||||
|
.unwrap_or(Visibility::Public);
|
||||||
|
self.def_collector.update(
|
||||||
|
self.module_id,
|
||||||
|
&[(name.clone(), PerNs::from_def(id, vis, has_constructor))],
|
||||||
|
vis,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn collect_module(&mut self, module: &raw::ModuleData, attrs: &Attrs) {
|
fn collect_module(&mut self, module: &Mod, attrs: &Attrs) {
|
||||||
let path_attr = attrs.by_key("path").string_value();
|
let path_attr = attrs.by_key("path").string_value();
|
||||||
let is_macro_use = attrs.by_key("macro_use").exists();
|
let is_macro_use = attrs.by_key("macro_use").exists();
|
||||||
match module {
|
match &module.kind {
|
||||||
// inline module, just recurse
|
// inline module, just recurse
|
||||||
raw::ModuleData::Definition { name, visibility, items, ast_id } => {
|
ModKind::Inline { items } => {
|
||||||
let module_id = self.push_child_module(
|
let module_id = self.push_child_module(
|
||||||
name.clone(),
|
module.name.clone(),
|
||||||
AstId::new(self.file_id, *ast_id),
|
AstId::new(self.file_id, module.ast_id),
|
||||||
None,
|
None,
|
||||||
&visibility,
|
&self.item_tree[module.visibility],
|
||||||
);
|
);
|
||||||
|
|
||||||
ModCollector {
|
ModCollector {
|
||||||
|
@ -760,8 +947,8 @@ impl ModCollector<'_, '_> {
|
||||||
macro_depth: self.macro_depth,
|
macro_depth: self.macro_depth,
|
||||||
module_id,
|
module_id,
|
||||||
file_id: self.file_id,
|
file_id: self.file_id,
|
||||||
raw_items: self.raw_items,
|
item_tree: self.item_tree,
|
||||||
mod_dir: self.mod_dir.descend_into_definition(name, path_attr),
|
mod_dir: self.mod_dir.descend_into_definition(&module.name, path_attr),
|
||||||
}
|
}
|
||||||
.collect(&*items);
|
.collect(&*items);
|
||||||
if is_macro_use {
|
if is_macro_use {
|
||||||
|
@ -769,31 +956,31 @@ impl ModCollector<'_, '_> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// out of line module, resolve, parse and recurse
|
// out of line module, resolve, parse and recurse
|
||||||
raw::ModuleData::Declaration { name, visibility, ast_id } => {
|
ModKind::Outline {} => {
|
||||||
let ast_id = AstId::new(self.file_id, *ast_id);
|
let ast_id = AstId::new(self.file_id, module.ast_id);
|
||||||
match self.mod_dir.resolve_declaration(
|
match self.mod_dir.resolve_declaration(
|
||||||
self.def_collector.db,
|
self.def_collector.db,
|
||||||
self.file_id,
|
self.file_id,
|
||||||
name,
|
&module.name,
|
||||||
path_attr,
|
path_attr,
|
||||||
) {
|
) {
|
||||||
Ok((file_id, is_mod_rs, mod_dir)) => {
|
Ok((file_id, is_mod_rs, mod_dir)) => {
|
||||||
let module_id = self.push_child_module(
|
let module_id = self.push_child_module(
|
||||||
name.clone(),
|
module.name.clone(),
|
||||||
ast_id,
|
ast_id,
|
||||||
Some((file_id, is_mod_rs)),
|
Some((file_id, is_mod_rs)),
|
||||||
&visibility,
|
&self.item_tree[module.visibility],
|
||||||
);
|
);
|
||||||
let raw_items = self.def_collector.db.raw_items(file_id.into());
|
let item_tree = self.def_collector.db.item_tree(file_id.into());
|
||||||
ModCollector {
|
ModCollector {
|
||||||
def_collector: &mut *self.def_collector,
|
def_collector: &mut *self.def_collector,
|
||||||
macro_depth: self.macro_depth,
|
macro_depth: self.macro_depth,
|
||||||
module_id,
|
module_id,
|
||||||
file_id: file_id.into(),
|
file_id: file_id.into(),
|
||||||
raw_items: &raw_items,
|
item_tree: &item_tree,
|
||||||
mod_dir,
|
mod_dir,
|
||||||
}
|
}
|
||||||
.collect(raw_items.items());
|
.collect(item_tree.top_level_items());
|
||||||
if is_macro_use {
|
if is_macro_use {
|
||||||
self.import_all_legacy_macros(module_id);
|
self.import_all_legacy_macros(module_id);
|
||||||
}
|
}
|
||||||
|
@ -842,77 +1029,7 @@ impl ModCollector<'_, '_> {
|
||||||
res
|
res
|
||||||
}
|
}
|
||||||
|
|
||||||
fn define_def(&mut self, def: &raw::DefData, attrs: &Attrs) {
|
fn collect_derives(&mut self, attrs: &Attrs, ast_id: FileAstId<ast::ModuleItem>) {
|
||||||
let module = ModuleId { krate: self.def_collector.def_map.krate, local_id: self.module_id };
|
|
||||||
// FIXME: check attrs to see if this is an attribute macro invocation;
|
|
||||||
// in which case we don't add the invocation, just a single attribute
|
|
||||||
// macro invocation
|
|
||||||
self.collect_derives(attrs, def);
|
|
||||||
|
|
||||||
let name = def.name.clone();
|
|
||||||
let container = ContainerId::ModuleId(module);
|
|
||||||
let vis = &def.visibility;
|
|
||||||
let mut has_constructor = false;
|
|
||||||
|
|
||||||
let def: ModuleDefId = match def.kind {
|
|
||||||
raw::DefKind::Function(ast_id) => FunctionLoc {
|
|
||||||
container: container.into(),
|
|
||||||
ast_id: AstId::new(self.file_id, ast_id),
|
|
||||||
}
|
|
||||||
.intern(self.def_collector.db)
|
|
||||||
.into(),
|
|
||||||
raw::DefKind::Struct(ast_id, mode) => {
|
|
||||||
has_constructor = mode != raw::StructDefKind::Record;
|
|
||||||
StructLoc { container, ast_id: AstId::new(self.file_id, ast_id) }
|
|
||||||
.intern(self.def_collector.db)
|
|
||||||
.into()
|
|
||||||
}
|
|
||||||
raw::DefKind::Union(ast_id) => {
|
|
||||||
UnionLoc { container, ast_id: AstId::new(self.file_id, ast_id) }
|
|
||||||
.intern(self.def_collector.db)
|
|
||||||
.into()
|
|
||||||
}
|
|
||||||
raw::DefKind::Enum(ast_id) => {
|
|
||||||
EnumLoc { container, ast_id: AstId::new(self.file_id, ast_id) }
|
|
||||||
.intern(self.def_collector.db)
|
|
||||||
.into()
|
|
||||||
}
|
|
||||||
raw::DefKind::Const(ast_id) => {
|
|
||||||
ConstLoc { container: container.into(), ast_id: AstId::new(self.file_id, ast_id) }
|
|
||||||
.intern(self.def_collector.db)
|
|
||||||
.into()
|
|
||||||
}
|
|
||||||
raw::DefKind::Static(ast_id) => {
|
|
||||||
StaticLoc { container, ast_id: AstId::new(self.file_id, ast_id) }
|
|
||||||
.intern(self.def_collector.db)
|
|
||||||
.into()
|
|
||||||
}
|
|
||||||
raw::DefKind::Trait(ast_id) => {
|
|
||||||
TraitLoc { container, ast_id: AstId::new(self.file_id, ast_id) }
|
|
||||||
.intern(self.def_collector.db)
|
|
||||||
.into()
|
|
||||||
}
|
|
||||||
raw::DefKind::TypeAlias(ast_id) => TypeAliasLoc {
|
|
||||||
container: container.into(),
|
|
||||||
ast_id: AstId::new(self.file_id, ast_id),
|
|
||||||
}
|
|
||||||
.intern(self.def_collector.db)
|
|
||||||
.into(),
|
|
||||||
};
|
|
||||||
self.def_collector.def_map.modules[self.module_id].scope.define_def(def);
|
|
||||||
let vis = self
|
|
||||||
.def_collector
|
|
||||||
.def_map
|
|
||||||
.resolve_visibility(self.def_collector.db, self.module_id, vis)
|
|
||||||
.unwrap_or(Visibility::Public);
|
|
||||||
self.def_collector.update(
|
|
||||||
self.module_id,
|
|
||||||
&[(name, PerNs::from_def(def, vis, has_constructor))],
|
|
||||||
vis,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn collect_derives(&mut self, attrs: &Attrs, def: &raw::DefData) {
|
|
||||||
for derive_subtree in attrs.by_key("derive").tt_values() {
|
for derive_subtree in attrs.by_key("derive").tt_values() {
|
||||||
// for #[derive(Copy, Clone)], `derive_subtree` is the `(Copy, Clone)` subtree
|
// for #[derive(Copy, Clone)], `derive_subtree` is the `(Copy, Clone)` subtree
|
||||||
for tt in &derive_subtree.token_trees {
|
for tt in &derive_subtree.token_trees {
|
||||||
|
@ -923,7 +1040,7 @@ impl ModCollector<'_, '_> {
|
||||||
};
|
};
|
||||||
let path = ModPath::from_tt_ident(ident);
|
let path = ModPath::from_tt_ident(ident);
|
||||||
|
|
||||||
let ast_id = AstIdWithPath::new(self.file_id, def.kind.ast_id(), path);
|
let ast_id = AstIdWithPath::new(self.file_id, ast_id, path);
|
||||||
self.def_collector
|
self.def_collector
|
||||||
.unexpanded_attribute_macros
|
.unexpanded_attribute_macros
|
||||||
.push(DeriveDirective { module_id: self.module_id, ast_id });
|
.push(DeriveDirective { module_id: self.module_id, ast_id });
|
||||||
|
@ -931,11 +1048,11 @@ impl ModCollector<'_, '_> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn collect_macro(&mut self, mac: &raw::MacroData) {
|
fn collect_macro(&mut self, mac: &MacroCall) {
|
||||||
let mut ast_id = AstIdWithPath::new(self.file_id, mac.ast_id, mac.path.clone());
|
let mut ast_id = AstIdWithPath::new(self.file_id, mac.ast_id, mac.path.clone());
|
||||||
|
|
||||||
// Case 0: builtin macros
|
// Case 0: builtin macros
|
||||||
if mac.builtin {
|
if mac.is_builtin {
|
||||||
if let Some(name) = &mac.name {
|
if let Some(name) = &mac.name {
|
||||||
let krate = self.def_collector.def_map.krate;
|
let krate = self.def_collector.def_map.krate;
|
||||||
if let Some(macro_id) = find_builtin_macro(name, krate, ast_id.ast_id) {
|
if let Some(macro_id) = find_builtin_macro(name, krate, ast_id.ast_id) {
|
||||||
|
@ -943,7 +1060,7 @@ impl ModCollector<'_, '_> {
|
||||||
self.module_id,
|
self.module_id,
|
||||||
name.clone(),
|
name.clone(),
|
||||||
macro_id,
|
macro_id,
|
||||||
mac.export,
|
mac.is_export,
|
||||||
);
|
);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -957,9 +1074,14 @@ impl ModCollector<'_, '_> {
|
||||||
ast_id: Some(ast_id.ast_id),
|
ast_id: Some(ast_id.ast_id),
|
||||||
krate: Some(self.def_collector.def_map.krate),
|
krate: Some(self.def_collector.def_map.krate),
|
||||||
kind: MacroDefKind::Declarative,
|
kind: MacroDefKind::Declarative,
|
||||||
local_inner: mac.local_inner,
|
local_inner: mac.is_local_inner,
|
||||||
};
|
};
|
||||||
self.def_collector.define_macro(self.module_id, name.clone(), macro_id, mac.export);
|
self.def_collector.define_macro(
|
||||||
|
self.module_id,
|
||||||
|
name.clone(),
|
||||||
|
macro_id,
|
||||||
|
mac.is_export,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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]
|
#[test]
|
||||||
fn typing_inside_a_macro_should_not_invalidate_def_map() {
|
fn typing_inside_a_macro_should_not_invalidate_def_map() {
|
||||||
let (mut db, pos) = TestDB::with_position(
|
let (mut db, pos) = TestDB::with_position(
|
||||||
|
|
|
@ -20,8 +20,11 @@ fn name_res_works_for_broken_modules() {
|
||||||
",
|
",
|
||||||
);
|
);
|
||||||
assert_snapshot!(map, @r###"
|
assert_snapshot!(map, @r###"
|
||||||
⋮crate
|
crate
|
||||||
⋮Baz: _
|
Baz: _
|
||||||
|
foo: t
|
||||||
|
|
||||||
|
crate::foo
|
||||||
"###);
|
"###);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -719,10 +722,7 @@ fn unresolved_module_diagnostics() {
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
value: FileAstId {
|
value: FileAstId::<ra_syntax::ast::generated::nodes::Module>(1),
|
||||||
raw: Idx::<SyntaxNodePtr>(1),
|
|
||||||
_ty: PhantomData,
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
candidate: "bar.rs",
|
candidate: "bar.rs",
|
||||||
},
|
},
|
||||||
|
|
|
@ -2,30 +2,37 @@
|
||||||
|
|
||||||
use hir_expand::InFile;
|
use hir_expand::InFile;
|
||||||
use ra_arena::map::ArenaMap;
|
use ra_arena::map::ArenaMap;
|
||||||
use ra_syntax::AstNode;
|
|
||||||
|
|
||||||
use crate::{db::DefDatabase, AssocItemLoc, ItemLoc};
|
use crate::{db::DefDatabase, item_tree::ItemTreeNode, AssocItemLoc, ItemLoc};
|
||||||
|
|
||||||
pub trait HasSource {
|
pub trait HasSource {
|
||||||
type Value;
|
type Value;
|
||||||
fn source(&self, db: &dyn DefDatabase) -> InFile<Self::Value>;
|
fn source(&self, db: &dyn DefDatabase) -> InFile<Self::Value>;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<N: AstNode> HasSource for AssocItemLoc<N> {
|
impl<N: ItemTreeNode> HasSource for AssocItemLoc<N> {
|
||||||
type Value = N;
|
type Value = N::Source;
|
||||||
|
|
||||||
fn source(&self, db: &dyn DefDatabase) -> InFile<N> {
|
fn source(&self, db: &dyn DefDatabase) -> InFile<N::Source> {
|
||||||
let node = self.ast_id.to_node(db.upcast());
|
let tree = db.item_tree(self.id.file_id);
|
||||||
InFile::new(self.ast_id.file_id, node)
|
let ast_id_map = db.ast_id_map(self.id.file_id);
|
||||||
|
let root = db.parse_or_expand(self.id.file_id).unwrap();
|
||||||
|
let node = &tree[self.id.value];
|
||||||
|
|
||||||
|
InFile::new(self.id.file_id, ast_id_map.get(node.ast_id()).to_node(&root))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<N: AstNode> HasSource for ItemLoc<N> {
|
impl<N: ItemTreeNode> HasSource for ItemLoc<N> {
|
||||||
type Value = N;
|
type Value = N::Source;
|
||||||
|
|
||||||
fn source(&self, db: &dyn DefDatabase) -> InFile<N> {
|
fn source(&self, db: &dyn DefDatabase) -> InFile<N::Source> {
|
||||||
let node = self.ast_id.to_node(db.upcast());
|
let tree = db.item_tree(self.id.file_id);
|
||||||
InFile::new(self.ast_id.file_id, node)
|
let ast_id_map = db.ast_id_map(self.id.file_id);
|
||||||
|
let root = db.parse_or_expand(self.id.file_id).unwrap();
|
||||||
|
let node = &tree[self.id.value];
|
||||||
|
|
||||||
|
InFile::new(self.id.file_id, ast_id_map.get(node.ast_id()).to_node(&root))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -6,7 +6,7 @@ use ra_syntax::ast;
|
||||||
use crate::{
|
use crate::{
|
||||||
db::DefDatabase,
|
db::DefDatabase,
|
||||||
path::{ModPath, PathKind},
|
path::{ModPath, PathKind},
|
||||||
AssocContainerId, ModuleId,
|
ModuleId,
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Visibility of an item, not yet resolved.
|
/// Visibility of an item, not yet resolved.
|
||||||
|
@ -25,25 +25,6 @@ impl RawVisibility {
|
||||||
RawVisibility::Module(path)
|
RawVisibility::Module(path)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn default_for_container(container_id: AssocContainerId) -> Self {
|
|
||||||
match container_id {
|
|
||||||
AssocContainerId::TraitId(_) => RawVisibility::Public,
|
|
||||||
_ => RawVisibility::private(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn from_ast_with_default(
|
|
||||||
db: &dyn DefDatabase,
|
|
||||||
default: RawVisibility,
|
|
||||||
node: InFile<Option<ast::Visibility>>,
|
|
||||||
) -> RawVisibility {
|
|
||||||
Self::from_ast_with_hygiene_and_default(
|
|
||||||
node.value,
|
|
||||||
default,
|
|
||||||
&Hygiene::new(db.upcast(), node.file_id),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn from_ast(
|
pub(crate) fn from_ast(
|
||||||
db: &dyn DefDatabase,
|
db: &dyn DefDatabase,
|
||||||
node: InFile<Option<ast::Visibility>>,
|
node: InFile<Option<ast::Visibility>>,
|
||||||
|
|
|
@ -6,6 +6,8 @@
|
||||||
//! changes.
|
//! changes.
|
||||||
|
|
||||||
use std::{
|
use std::{
|
||||||
|
any::type_name,
|
||||||
|
fmt,
|
||||||
hash::{Hash, Hasher},
|
hash::{Hash, Hasher},
|
||||||
marker::PhantomData,
|
marker::PhantomData,
|
||||||
};
|
};
|
||||||
|
@ -14,7 +16,6 @@ use ra_arena::{Arena, Idx};
|
||||||
use ra_syntax::{ast, AstNode, AstPtr, SyntaxNode, SyntaxNodePtr};
|
use ra_syntax::{ast, AstNode, AstPtr, SyntaxNode, SyntaxNodePtr};
|
||||||
|
|
||||||
/// `AstId` points to an AST node in a specific file.
|
/// `AstId` points to an AST node in a specific file.
|
||||||
#[derive(Debug)]
|
|
||||||
pub struct FileAstId<N: AstNode> {
|
pub struct FileAstId<N: AstNode> {
|
||||||
raw: ErasedFileAstId,
|
raw: ErasedFileAstId,
|
||||||
_ty: PhantomData<fn() -> N>,
|
_ty: PhantomData<fn() -> N>,
|
||||||
|
@ -39,11 +40,17 @@ impl<N: AstNode> Hash for FileAstId<N> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<N: AstNode> fmt::Debug for FileAstId<N> {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
write!(f, "FileAstId::<{}>({})", type_name::<N>(), self.raw.into_raw())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl<N: AstNode> FileAstId<N> {
|
impl<N: AstNode> FileAstId<N> {
|
||||||
// Can't make this a From implementation because of coherence
|
// Can't make this a From implementation because of coherence
|
||||||
pub fn upcast<M: AstNode>(self) -> FileAstId<M>
|
pub fn upcast<M: AstNode>(self) -> FileAstId<M>
|
||||||
where
|
where
|
||||||
M: From<N>,
|
N: Into<M>,
|
||||||
{
|
{
|
||||||
FileAstId { raw: self.raw, _ty: PhantomData }
|
FileAstId { raw: self.raw, _ty: PhantomData }
|
||||||
}
|
}
|
||||||
|
@ -89,7 +96,7 @@ impl AstIdMap {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn get<N: AstNode>(&self, id: FileAstId<N>) -> AstPtr<N> {
|
pub fn get<N: AstNode>(&self, id: FileAstId<N>) -> AstPtr<N> {
|
||||||
self.arena[id.raw].clone().cast::<N>().unwrap()
|
self.arena[id.raw].clone().cast::<N>().unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -67,8 +67,8 @@ fn type_at_pos_displayed(
|
||||||
panic!("Can't find expression")
|
panic!("Can't find expression")
|
||||||
}
|
}
|
||||||
|
|
||||||
fn type_at(content: &str) -> String {
|
fn type_at(ra_fixture: &str) -> String {
|
||||||
let (db, file_pos) = TestDB::with_position(content);
|
let (db, file_pos) = TestDB::with_position(ra_fixture);
|
||||||
type_at_pos(&db, file_pos)
|
type_at_pos(&db, file_pos)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -164,13 +164,19 @@ fn infer_with_mismatches(content: &str, include_mismatches: bool) -> String {
|
||||||
visit_module(&db, &crate_def_map, module.local_id, &mut |it| defs.push(it));
|
visit_module(&db, &crate_def_map, module.local_id, &mut |it| defs.push(it));
|
||||||
defs.sort_by_key(|def| match def {
|
defs.sort_by_key(|def| match def {
|
||||||
DefWithBodyId::FunctionId(it) => {
|
DefWithBodyId::FunctionId(it) => {
|
||||||
it.lookup(&db).ast_id.to_node(&db).syntax().text_range().start()
|
let loc = it.lookup(&db);
|
||||||
|
let tree = db.item_tree(loc.id.file_id);
|
||||||
|
tree.source(&db, loc.id).syntax().text_range().start()
|
||||||
}
|
}
|
||||||
DefWithBodyId::ConstId(it) => {
|
DefWithBodyId::ConstId(it) => {
|
||||||
it.lookup(&db).ast_id.to_node(&db).syntax().text_range().start()
|
let loc = it.lookup(&db);
|
||||||
|
let tree = db.item_tree(loc.id.file_id);
|
||||||
|
tree.source(&db, loc.id).syntax().text_range().start()
|
||||||
}
|
}
|
||||||
DefWithBodyId::StaticId(it) => {
|
DefWithBodyId::StaticId(it) => {
|
||||||
it.lookup(&db).ast_id.to_node(&db).syntax().text_range().start()
|
let loc = it.lookup(&db);
|
||||||
|
let tree = db.item_tree(loc.id.file_id);
|
||||||
|
tree.source(&db, loc.id).syntax().text_range().start()
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
for def in defs {
|
for def in defs {
|
||||||
|
|
|
@ -199,7 +199,7 @@ impl RootDatabase {
|
||||||
hir::db::InternEagerExpansionQuery
|
hir::db::InternEagerExpansionQuery
|
||||||
|
|
||||||
// DefDatabase
|
// DefDatabase
|
||||||
hir::db::RawItemsQuery
|
hir::db::ItemTreeQuery
|
||||||
hir::db::CrateDefMapQueryQuery
|
hir::db::CrateDefMapQueryQuery
|
||||||
hir::db::StructDataQuery
|
hir::db::StructDataQuery
|
||||||
hir::db::UnionDataQuery
|
hir::db::UnionDataQuery
|
||||||
|
|
Loading…
Reference in a new issue