//! A simplified AST that only contains items. //! //! This is the primary IR used throughout `hir_def`. It is the input to the name resolution //! algorithm, as well as to the queries defined in `adt.rs`, `data.rs`, and most things in //! `attr.rs`. //! //! `ItemTree`s are built per `HirFileId`, from the syntax tree of the parsed file. This means that //! they are crate-independent: they don't know which `#[cfg]`s are active or which module they //! belong to, since those concepts don't exist at this level (a single `ItemTree` might be part of //! multiple crates, or might be included into the same crate twice via `#[path]`). //! //! One important purpose of this layer is to provide an "invalidation barrier" for incremental //! computations: when typing inside an item body, the `ItemTree` of the modified file is typically //! unaffected, so we don't have to recompute name resolution results or item data (see `data.rs`). //! //! The `ItemTree` for the currently open file can be displayed by using the VS Code command //! "rust-analyzer: Debug ItemTree". //! //! Compared to rustc's architecture, `ItemTree` has properties from both rustc's AST and HIR: many //! syntax-level Rust features are already desugared to simpler forms in the `ItemTree`, but name //! resolution has not yet been performed. `ItemTree`s are per-file, while rustc's AST and HIR are //! per-crate, because we are interested in incrementally computing it. //! //! The representation of items in the `ItemTree` should generally mirror the surface syntax: it is //! usually a bad idea to desugar a syntax-level construct to something that is structurally //! different here. Name resolution needs to be able to process attributes and expand macros //! (including attribute macros), and having a 1-to-1 mapping between syntax and the `ItemTree` //! avoids introducing subtle bugs. //! //! In general, any item in the `ItemTree` stores its `AstId`, which allows mapping it back to its //! surface syntax. mod lower; mod pretty; #[cfg(test)] mod tests; use std::{ fmt::{self, Debug}, hash::{Hash, Hasher}, marker::PhantomData, ops::Index, sync::Arc, }; use ast::{AstNode, HasName, StructKind}; use base_db::CrateId; use either::Either; use hir_expand::{ ast_id_map::FileAstId, attrs::RawAttrs, hygiene::Hygiene, name::{name, AsName, Name}, ExpandTo, HirFileId, InFile, }; use intern::Interned; use la_arena::{Arena, Idx, IdxRange, RawIdx}; use profile::Count; use rustc_hash::FxHashMap; use smallvec::SmallVec; use stdx::never; use syntax::{ast, match_ast, SyntaxKind}; use crate::{ attr::Attrs, db::DefDatabase, generics::GenericParams, path::{path, AssociatedTypeBinding, GenericArgs, ImportAlias, ModPath, Path, PathKind}, type_ref::{Mutability, TraitRef, TypeBound, TypeRef}, visibility::RawVisibility, BlockId, }; #[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() } } /// The item tree of a source file. #[derive(Debug, Default, Eq, PartialEq)] pub struct ItemTree { _c: Count, top_level: SmallVec<[ModItem; 1]>, attrs: FxHashMap, data: Option>, } impl ItemTree { pub(crate) fn file_item_tree_query(db: &dyn DefDatabase, file_id: HirFileId) -> Arc { let _p = profile::span("file_item_tree_query").detail(|| format!("{file_id:?}")); let syntax = match db.parse_or_expand(file_id) { Some(node) => node, None => return Default::default(), }; if never!(syntax.kind() == SyntaxKind::ERROR, "{:?} from {:?} {}", file_id, syntax, syntax) { // FIXME: not 100% sure why these crop up, but return an empty tree to avoid a panic return Default::default(); } let ctx = lower::Ctx::new(db, file_id); let mut top_attrs = None; let mut item_tree = match_ast! { match syntax { ast::SourceFile(file) => { top_attrs = Some(RawAttrs::new(db.upcast(), &file, ctx.hygiene())); ctx.lower_module_items(&file) }, ast::MacroItems(items) => { ctx.lower_module_items(&items) }, ast::MacroStmts(stmts) => { // The produced statements can include items, which should be added as top-level // items. ctx.lower_macro_stmts(stmts) }, _ => { panic!("cannot create item tree for file {file_id:?} from {syntax:?} {syntax}"); }, } }; if let Some(attrs) = top_attrs { item_tree.attrs.insert(AttrOwner::TopLevel, attrs); } item_tree.shrink_to_fit(); Arc::new(item_tree) } /// 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, db: &dyn DefDatabase, krate: CrateId) -> Attrs { Attrs::filter( db, krate, self.attrs.get(&AttrOwner::TopLevel).unwrap_or(&RawAttrs::EMPTY).clone(), ) } pub(crate) fn raw_attrs(&self, of: AttrOwner) -> &RawAttrs { self.attrs.get(&of).unwrap_or(&RawAttrs::EMPTY) } pub(crate) fn attrs(&self, db: &dyn DefDatabase, krate: CrateId, of: AttrOwner) -> Attrs { Attrs::filter(db, krate, self.raw_attrs(of).clone()) } pub fn pretty_print(&self) -> String { pretty::print_item_tree(self) } 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) } fn block_item_tree(db: &dyn DefDatabase, block: BlockId) -> Arc { let loc = db.lookup_intern_block(block); let block = loc.ast_id.to_node(db.upcast()); let ctx = lower::Ctx::new(db, loc.ast_id.file_id); Arc::new(ctx.lower_block(&block)) } fn shrink_to_fit(&mut self) { if let Some(data) = &mut self.data { let ItemTreeData { imports, extern_crates, extern_blocks, functions, params, structs, fields, unions, enums, variants, consts, statics, traits, impls, type_aliases, mods, macro_calls, macro_rules, macro_defs, vis, } = &mut **data; imports.shrink_to_fit(); extern_crates.shrink_to_fit(); extern_blocks.shrink_to_fit(); functions.shrink_to_fit(); params.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(); macro_rules.shrink_to_fit(); macro_defs.shrink_to_fit(); vis.arena.shrink_to_fit(); } } } #[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::from_kind(PathKind::Super(0))); static VIS_PUB_CRATE: RawVisibility = RawVisibility::Module(ModPath::from_kind(PathKind::Crate)); #[derive(Default, Debug, Eq, PartialEq)] struct ItemTreeData { imports: Arena, extern_crates: Arena, extern_blocks: Arena, functions: Arena, params: 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, macro_rules: Arena, macro_defs: Arena, vis: ItemVisibilities, } #[derive(Debug, Eq, PartialEq, Hash)] pub enum AttrOwner { /// Attributes on an item. ModItem(ModItem), /// Inner attributes of the source file. TopLevel, Variant(Idx), Field(Idx), Param(Idx), } macro_rules! from_attrs { ( $( $var:ident($t:ty) ),+ ) => { $( impl From<$t> for AttrOwner { fn from(t: $t) -> AttrOwner { AttrOwner::$var(t) } } )+ }; } from_attrs!(ModItem(ModItem), Variant(Idx), Field(Idx), Param(Idx)); /// Trait implemented by all item 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) } } /// Identifies a particular [`ItemTree`]. #[derive(Debug, PartialEq, Eq, Clone, Copy, Hash)] pub struct TreeId { file: HirFileId, block: Option, } impl TreeId { pub(crate) fn new(file: HirFileId, block: Option) -> Self { Self { file, block } } pub(crate) fn item_tree(&self, db: &dyn DefDatabase) -> Arc { match self.block { Some(block) => ItemTree::block_item_tree(db, block), None => db.file_item_tree(self.file), } } pub(crate) fn file_id(self) -> HirFileId { self.file } pub(crate) fn is_block(self) -> bool { self.block.is_some() } } #[derive(Debug)] pub struct ItemTreeId { tree: TreeId, pub value: FileItemTreeId, } impl ItemTreeId { pub fn new(tree: TreeId, idx: FileItemTreeId) -> Self { Self { tree, value: idx } } pub fn file_id(self) -> HirFileId { self.tree.file } pub fn tree_id(self) -> TreeId { self.tree } pub fn item_tree(self, db: &dyn DefDatabase) -> Arc { self.tree.item_tree(db) } } impl Copy for ItemTreeId {} impl Clone for ItemTreeId { fn clone(&self) -> Self { *self } } impl PartialEq for ItemTreeId { fn eq(&self, other: &Self) -> bool { self.tree == other.tree && self.value == other.value } } impl Eq for ItemTreeId {} impl Hash for ItemTreeId { fn hash(&self, state: &mut H) { self.tree.hash(state); self.value.hash(state); } } 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> { match mod_item { ModItem::$typ(id) => Some(id), _ => 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::Use, ExternCrate in extern_crates -> ast::ExternCrate, ExternBlock in extern_blocks -> ast::ExternBlock, Function in functions -> ast::Fn, Struct in structs -> ast::Struct, Union in unions -> ast::Union, Enum in enums -> ast::Enum, Const in consts -> ast::Const, Static in statics -> ast::Static, Trait in traits -> ast::Trait, Impl in impls -> ast::Impl, TypeAlias in type_aliases -> ast::TypeAlias, Mod in mods -> ast::Module, MacroCall in macro_calls -> ast::MacroCall, MacroRules in macro_rules -> ast::MacroRules, MacroDef in macro_defs -> ast::MacroDef, } 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, params: Param); 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 = N; fn index(&self, id: FileItemTreeId) -> &N { N::lookup(self, id.index) } } #[derive(Debug, Clone, Eq, PartialEq)] pub struct Import { pub visibility: RawVisibilityId, pub ast_id: FileAstId, pub use_tree: UseTree, } #[derive(Debug, Clone, Eq, PartialEq)] pub struct UseTree { pub index: Idx, kind: UseTreeKind, } #[derive(Debug, Clone, Eq, PartialEq)] pub enum UseTreeKind { /// ``` /// use path::to::Item; /// use path::to::Item as Renamed; /// use path::to::Trait as _; /// ``` Single { path: Interned, alias: Option }, /// ``` /// use *; // (invalid, but can occur in nested tree) /// use path::*; /// ``` Glob { path: Option> }, /// ``` /// use prefix::{self, Item, ...}; /// ``` Prefixed { prefix: Option>, list: Box<[UseTree]> }, } #[derive(Debug, Clone, Eq, PartialEq)] pub struct ExternCrate { pub name: Name, pub alias: Option, pub visibility: RawVisibilityId, pub ast_id: FileAstId, } #[derive(Debug, Clone, Eq, PartialEq)] pub struct ExternBlock { pub abi: Option>, pub ast_id: FileAstId, pub children: Box<[ModItem]>, } #[derive(Debug, Clone, Eq, PartialEq)] pub struct Function { pub name: Name, pub visibility: RawVisibilityId, pub explicit_generic_params: Interned, pub abi: Option>, pub params: IdxRange, pub ret_type: Interned, pub async_ret_type: Option>, pub ast_id: FileAstId, pub(crate) flags: FnFlags, } #[derive(Debug, Clone, Eq, PartialEq)] pub enum Param { Normal(Option, Interned), Varargs, } bitflags::bitflags! { #[derive(Default)] pub(crate) struct FnFlags: u8 { const HAS_SELF_PARAM = 1 << 0; const HAS_BODY = 1 << 1; const HAS_DEFAULT_KW = 1 << 2; const HAS_CONST_KW = 1 << 3; const HAS_ASYNC_KW = 1 << 4; const HAS_UNSAFE_KW = 1 << 5; const IS_VARARGS = 1 << 6; } } #[derive(Debug, Clone, Eq, PartialEq)] pub struct Struct { pub name: Name, pub visibility: RawVisibilityId, pub generic_params: Interned, pub fields: Fields, pub ast_id: FileAstId, } #[derive(Debug, Clone, Eq, PartialEq)] pub struct Union { pub name: Name, pub visibility: RawVisibilityId, pub generic_params: Interned, pub fields: Fields, pub ast_id: FileAstId, } #[derive(Debug, Clone, Eq, PartialEq)] pub struct Enum { pub name: Name, pub visibility: RawVisibilityId, pub generic_params: Interned, pub variants: IdxRange, pub ast_id: FileAstId, } #[derive(Debug, Clone, Eq, PartialEq)] pub struct Const { /// `None` for `const _: () = ();` pub name: Option, pub visibility: RawVisibilityId, pub type_ref: Interned, pub ast_id: FileAstId, } #[derive(Debug, Clone, Eq, PartialEq)] pub struct Static { pub name: Name, pub visibility: RawVisibilityId, pub mutable: bool, pub type_ref: Interned, pub ast_id: FileAstId, } #[derive(Debug, Clone, Eq, PartialEq)] pub struct Trait { pub name: Name, pub visibility: RawVisibilityId, pub generic_params: Interned, pub is_auto: bool, pub is_unsafe: bool, /// This is [`None`] if this Trait is a trait alias. pub items: Option>, pub ast_id: FileAstId, } #[derive(Debug, Clone, Eq, PartialEq)] pub struct Impl { pub generic_params: Interned, pub target_trait: Option>, pub self_ty: Interned, 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<[Interned]>, pub generic_params: Interned, 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 { /// Path to the called macro. pub path: Interned, pub ast_id: FileAstId, pub expand_to: ExpandTo, } #[derive(Debug, Clone, Eq, PartialEq)] pub struct MacroRules { /// The name of the declared macro. pub name: Name, pub ast_id: FileAstId, } /// "Macros 2.0" macro definition. #[derive(Debug, Clone, Eq, PartialEq)] pub struct MacroDef { pub name: Name, pub visibility: RawVisibilityId, pub ast_id: FileAstId, } impl Import { /// Maps a `UseTree` contained in this import back to its AST node. pub fn use_tree_to_ast( &self, db: &dyn DefDatabase, file_id: HirFileId, index: Idx, ) -> ast::UseTree { // Re-lower the AST item and get the source map. // Note: The AST unwraps are fine, since if they fail we should have never obtained `index`. let ast = InFile::new(file_id, self.ast_id).to_node(db.upcast()); let ast_use_tree = ast.use_tree().expect("missing `use_tree`"); let hygiene = Hygiene::new(db.upcast(), file_id); let (_, source_map) = lower::lower_use_tree(db, &hygiene, ast_use_tree).expect("failed to lower use tree"); source_map[index].clone() } } #[derive(Clone, Copy, Debug, Eq, PartialEq)] pub enum ImportKind { /// The `ModPath` is imported normally. Plain, /// This is a glob-import of all names in the `ModPath`. Glob, /// This is a `some::path::self` import, which imports `some::path` only in type namespace. TypeOnly, } impl UseTree { /// Expands the `UseTree` into individually imported `ModPath`s. pub fn expand( &self, mut cb: impl FnMut(Idx, ModPath, ImportKind, Option), ) { self.expand_impl(None, &mut cb) } fn expand_impl( &self, prefix: Option, cb: &mut dyn FnMut(Idx, ModPath, ImportKind, Option), ) { fn concat_mod_paths( prefix: Option, path: &ModPath, ) -> Option<(ModPath, ImportKind)> { match (prefix, &path.kind) { (None, _) => Some((path.clone(), ImportKind::Plain)), (Some(mut prefix), PathKind::Plain) => { for segment in path.segments() { prefix.push_segment(segment.clone()); } Some((prefix, ImportKind::Plain)) } (Some(mut prefix), PathKind::Super(n)) if *n > 0 && prefix.segments().is_empty() => { // `super::super` + `super::rest` match &mut prefix.kind { PathKind::Super(m) => { cov_mark::hit!(concat_super_mod_paths); *m += *n; for segment in path.segments() { prefix.push_segment(segment.clone()); } Some((prefix, ImportKind::Plain)) } _ => None, } } (Some(prefix), PathKind::Super(0)) if path.segments().is_empty() => { // `some::path::self` == `some::path` Some((prefix, ImportKind::TypeOnly)) } (Some(_), _) => None, } } match &self.kind { UseTreeKind::Single { path, alias } => { if let Some((path, kind)) = concat_mod_paths(prefix, path) { cb(self.index, path, kind, alias.clone()); } } UseTreeKind::Glob { path: Some(path) } => { if let Some((path, _)) = concat_mod_paths(prefix, path) { cb(self.index, path, ImportKind::Glob, None); } } UseTreeKind::Glob { path: None } => { if let Some(prefix) = prefix { cb(self.index, prefix, ImportKind::Glob, None); } } UseTreeKind::Prefixed { prefix: additional_prefix, list } => { let prefix = match additional_prefix { Some(path) => match concat_mod_paths(prefix, path) { Some((path, ImportKind::Plain)) => Some(path), _ => return, }, None => prefix, }; for tree in &**list { tree.expand_impl(prefix.clone(), cb); } } } } } 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::ExternBlock(_) | ModItem::Struct(_) | ModItem::Union(_) | ModItem::Enum(_) | ModItem::Static(_) | ModItem::Trait(_) | ModItem::Impl(_) | ModItem::Mod(_) | ModItem::MacroRules(_) | ModItem::MacroDef(_) => 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) } pub fn ast_id(&self, tree: &ItemTree) -> FileAstId { match self { ModItem::Import(it) => tree[it.index].ast_id().upcast(), ModItem::ExternCrate(it) => tree[it.index].ast_id().upcast(), ModItem::ExternBlock(it) => tree[it.index].ast_id().upcast(), ModItem::Function(it) => tree[it.index].ast_id().upcast(), ModItem::Struct(it) => tree[it.index].ast_id().upcast(), ModItem::Union(it) => tree[it.index].ast_id().upcast(), ModItem::Enum(it) => tree[it.index].ast_id().upcast(), ModItem::Const(it) => tree[it.index].ast_id().upcast(), ModItem::Static(it) => tree[it.index].ast_id().upcast(), ModItem::Trait(it) => tree[it.index].ast_id().upcast(), ModItem::Impl(it) => tree[it.index].ast_id().upcast(), ModItem::TypeAlias(it) => tree[it.index].ast_id().upcast(), ModItem::Mod(it) => tree[it.index].ast_id().upcast(), ModItem::MacroCall(it) => tree[it.index].ast_id().upcast(), ModItem::MacroRules(it) => tree[it.index].ast_id().upcast(), ModItem::MacroDef(it) => tree[it.index].ast_id().upcast(), } } } #[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(), } } } impl AssocItem { pub fn ast_id(self, tree: &ItemTree) -> FileAstId { match self { AssocItem::Function(id) => tree[id].ast_id.upcast(), AssocItem::TypeAlias(id) => tree[id].ast_id.upcast(), AssocItem::Const(id) => tree[id].ast_id.upcast(), AssocItem::MacroCall(id) => tree[id].ast_id.upcast(), } } } #[derive(Debug, Eq, PartialEq)] pub struct Variant { pub name: Name, pub fields: Fields, pub ast_id: FileAstId, } #[derive(Debug, Clone, PartialEq, Eq)] pub enum Fields { Record(IdxRange), Tuple(IdxRange), Unit, } #[derive(Debug, Clone, PartialEq, Eq)] pub enum FieldAstId { Record(FileAstId), Tuple(FileAstId), } /// A single field of an enum variant or struct #[derive(Debug, Clone, PartialEq, Eq)] pub struct Field { pub name: Name, pub type_ref: Interned, pub visibility: RawVisibilityId, pub ast_id: FieldAstId, }