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