| //! Item signature IR definitions |
| |
| use std::ops::Not as _; |
| |
| use bitflags::bitflags; |
| use cfg::{CfgExpr, CfgOptions}; |
| use either::Either; |
| use hir_expand::{InFile, Intern, Lookup, name::Name}; |
| use intern::{Symbol, sym}; |
| use la_arena::{Arena, Idx}; |
| use rustc_abi::{IntegerType, ReprOptions}; |
| use syntax::{ |
| AstNode, SyntaxNodePtr, |
| ast::{self, HasGenericParams, IsString}, |
| }; |
| use thin_vec::ThinVec; |
| use triomphe::Arc; |
| |
| use crate::{ |
| ConstId, EnumId, EnumVariantId, EnumVariantLoc, FunctionId, HasModule, ImplId, ItemContainerId, |
| ModuleId, StaticId, StructId, TraitAliasId, TraitId, TypeAliasId, UnionId, VariantId, |
| db::DefDatabase, |
| expr_store::{ |
| ExpressionStore, ExpressionStoreSourceMap, |
| lower::{ |
| ExprCollector, lower_function, lower_generic_params, lower_trait, lower_trait_alias, |
| lower_type_alias, |
| }, |
| }, |
| hir::{ExprId, PatId, generics::GenericParams}, |
| item_tree::{ |
| AttrOwner, Field, FieldParent, FieldsShape, FileItemTreeId, ItemTree, ItemTreeId, ModItem, |
| RawVisibility, RawVisibilityId, |
| }, |
| lang_item::LangItem, |
| src::HasSource, |
| type_ref::{TraitRef, TypeBound, TypeRef, TypeRefId}, |
| }; |
| |
| #[derive(Debug, PartialEq, Eq)] |
| pub struct StructSignature { |
| pub name: Name, |
| pub generic_params: Arc<GenericParams>, |
| pub store: Arc<ExpressionStore>, |
| pub flags: StructFlags, |
| pub shape: FieldsShape, |
| pub repr: Option<ReprOptions>, |
| } |
| |
| bitflags! { |
| #[derive(Debug, Copy, Clone, PartialEq, Eq)] |
| pub struct StructFlags: u8 { |
| /// Indicates whether the struct has a `#[rustc_has_incoherent_inherent_impls]` attribute. |
| const RUSTC_HAS_INCOHERENT_INHERENT_IMPLS = 1 << 1; |
| /// Indicates whether the struct has a `#[fundamental]` attribute. |
| const FUNDAMENTAL = 1 << 2; |
| /// Indicates whether the struct is `PhantomData`. |
| const IS_PHANTOM_DATA = 1 << 3; |
| /// Indicates whether this struct is `Box`. |
| const IS_BOX = 1 << 4; |
| /// Indicates whether this struct is `ManuallyDrop`. |
| const IS_MANUALLY_DROP = 1 << 5; |
| /// Indicates whether this struct is `UnsafeCell`. |
| const IS_UNSAFE_CELL = 1 << 6; |
| /// Indicates whether this struct is `UnsafePinned`. |
| const IS_UNSAFE_PINNED = 1 << 7; |
| } |
| } |
| |
| impl StructSignature { |
| pub fn query(db: &dyn DefDatabase, id: StructId) -> (Arc<Self>, Arc<ExpressionStoreSourceMap>) { |
| let loc = id.lookup(db); |
| let item_tree = loc.id.item_tree(db); |
| let attrs = item_tree.attrs(db, loc.container.krate, ModItem::from(loc.id.value).into()); |
| |
| let mut flags = StructFlags::empty(); |
| if attrs.by_key(sym::rustc_has_incoherent_inherent_impls).exists() { |
| flags |= StructFlags::RUSTC_HAS_INCOHERENT_INHERENT_IMPLS; |
| } |
| if attrs.by_key(sym::fundamental).exists() { |
| flags |= StructFlags::FUNDAMENTAL; |
| } |
| if let Some(lang) = attrs.lang_item() { |
| match lang { |
| LangItem::PhantomData => flags |= StructFlags::IS_PHANTOM_DATA, |
| LangItem::OwnedBox => flags |= StructFlags::IS_BOX, |
| LangItem::ManuallyDrop => flags |= StructFlags::IS_MANUALLY_DROP, |
| LangItem::UnsafeCell => flags |= StructFlags::IS_UNSAFE_CELL, |
| LangItem::UnsafePinned => flags |= StructFlags::IS_UNSAFE_PINNED, |
| _ => (), |
| } |
| } |
| let repr = attrs.repr(); |
| |
| let hir_expand::files::InFileWrapper { file_id, value } = loc.source(db); |
| let (store, generic_params, source_map) = lower_generic_params( |
| db, |
| loc.container, |
| id.into(), |
| file_id, |
| value.generic_param_list(), |
| value.where_clause(), |
| ); |
| ( |
| Arc::new(StructSignature { |
| generic_params, |
| store, |
| flags, |
| shape: item_tree[loc.id.value].shape, |
| name: item_tree[loc.id.value].name.clone(), |
| repr, |
| }), |
| Arc::new(source_map), |
| ) |
| } |
| } |
| |
| #[derive(Debug, PartialEq, Eq)] |
| pub struct UnionSignature { |
| pub name: Name, |
| pub generic_params: Arc<GenericParams>, |
| pub store: Arc<ExpressionStore>, |
| pub flags: StructFlags, |
| pub repr: Option<ReprOptions>, |
| } |
| |
| impl UnionSignature { |
| pub fn query(db: &dyn DefDatabase, id: UnionId) -> (Arc<Self>, Arc<ExpressionStoreSourceMap>) { |
| let loc = id.lookup(db); |
| let krate = loc.container.krate; |
| let item_tree = loc.id.item_tree(db); |
| let attrs = item_tree.attrs(db, krate, ModItem::from(loc.id.value).into()); |
| let mut flags = StructFlags::empty(); |
| if attrs.by_key(sym::rustc_has_incoherent_inherent_impls).exists() { |
| flags |= StructFlags::RUSTC_HAS_INCOHERENT_INHERENT_IMPLS; |
| } |
| if attrs.by_key(sym::fundamental).exists() { |
| flags |= StructFlags::FUNDAMENTAL; |
| } |
| |
| let repr = attrs.repr(); |
| |
| let hir_expand::files::InFileWrapper { file_id, value } = loc.source(db); |
| let (store, generic_params, source_map) = lower_generic_params( |
| db, |
| loc.container, |
| id.into(), |
| file_id, |
| value.generic_param_list(), |
| value.where_clause(), |
| ); |
| ( |
| Arc::new(UnionSignature { |
| generic_params, |
| store, |
| flags, |
| repr, |
| name: item_tree[loc.id.value].name.clone(), |
| }), |
| Arc::new(source_map), |
| ) |
| } |
| } |
| |
| bitflags! { |
| #[derive(Debug, Copy, Clone, PartialEq, Eq)] |
| pub struct EnumFlags: u8 { |
| const RUSTC_HAS_INCOHERENT_INHERENT_IMPLS = 1 << 1; |
| } |
| } |
| |
| #[derive(Debug, PartialEq, Eq)] |
| pub struct EnumSignature { |
| pub name: Name, |
| pub generic_params: Arc<GenericParams>, |
| pub store: Arc<ExpressionStore>, |
| pub flags: EnumFlags, |
| pub repr: Option<ReprOptions>, |
| } |
| |
| impl EnumSignature { |
| pub fn query(db: &dyn DefDatabase, id: EnumId) -> (Arc<Self>, Arc<ExpressionStoreSourceMap>) { |
| let loc = id.lookup(db); |
| let item_tree = loc.id.item_tree(db); |
| let attrs = item_tree.attrs(db, loc.container.krate, ModItem::from(loc.id.value).into()); |
| let mut flags = EnumFlags::empty(); |
| if attrs.by_key(sym::rustc_has_incoherent_inherent_impls).exists() { |
| flags |= EnumFlags::RUSTC_HAS_INCOHERENT_INHERENT_IMPLS; |
| } |
| |
| let repr = attrs.repr(); |
| |
| let hir_expand::files::InFileWrapper { file_id, value } = loc.source(db); |
| let (store, generic_params, source_map) = lower_generic_params( |
| db, |
| loc.container, |
| id.into(), |
| file_id, |
| value.generic_param_list(), |
| value.where_clause(), |
| ); |
| |
| ( |
| Arc::new(EnumSignature { |
| generic_params, |
| store, |
| flags, |
| repr, |
| name: item_tree[loc.id.value].name.clone(), |
| }), |
| Arc::new(source_map), |
| ) |
| } |
| |
| pub fn variant_body_type(&self) -> IntegerType { |
| match self.repr { |
| Some(ReprOptions { int: Some(builtin), .. }) => builtin, |
| _ => IntegerType::Pointer(true), |
| } |
| } |
| } |
| bitflags::bitflags! { |
| #[derive(Debug, Clone, Copy, Eq, PartialEq, Default)] |
| pub struct ConstFlags: u8 { |
| const HAS_BODY = 1 << 1; |
| const RUSTC_ALLOW_INCOHERENT_IMPL = 1 << 7; |
| } |
| } |
| |
| #[derive(Debug, PartialEq, Eq)] |
| pub struct ConstSignature { |
| pub name: Option<Name>, |
| // generic_params: Arc<GenericParams>, |
| pub store: Arc<ExpressionStore>, |
| pub type_ref: TypeRefId, |
| pub flags: ConstFlags, |
| } |
| |
| impl ConstSignature { |
| pub fn query(db: &dyn DefDatabase, id: ConstId) -> (Arc<Self>, Arc<ExpressionStoreSourceMap>) { |
| let loc = id.lookup(db); |
| let item_tree = loc.id.item_tree(db); |
| |
| let module = loc.container.module(db); |
| let attrs = item_tree.attrs(db, module.krate, ModItem::from(loc.id.value).into()); |
| let mut flags = ConstFlags::empty(); |
| if attrs.by_key(sym::rustc_allow_incoherent_impl).exists() { |
| flags |= ConstFlags::RUSTC_ALLOW_INCOHERENT_IMPL; |
| } |
| let source = loc.source(db); |
| if source.value.body().is_some() { |
| flags.insert(ConstFlags::HAS_BODY); |
| } |
| |
| let (store, source_map, type_ref) = |
| crate::expr_store::lower::lower_type_ref(db, module, source.map(|it| it.ty())); |
| |
| ( |
| Arc::new(ConstSignature { |
| store: Arc::new(store), |
| type_ref, |
| flags, |
| name: item_tree[loc.id.value].name.clone(), |
| }), |
| Arc::new(source_map), |
| ) |
| } |
| |
| pub fn has_body(&self) -> bool { |
| self.flags.contains(ConstFlags::HAS_BODY) |
| } |
| } |
| |
| bitflags::bitflags! { |
| #[derive(Debug, Clone, Copy, Eq, PartialEq, Default)] |
| pub struct StaticFlags: u8 { |
| const HAS_BODY = 1 << 1; |
| const MUTABLE = 1 << 3; |
| const UNSAFE = 1 << 4; |
| const EXPLICIT_SAFE = 1 << 5; |
| const EXTERN = 1 << 6; |
| const RUSTC_ALLOW_INCOHERENT_IMPL = 1 << 7; |
| } |
| } |
| |
| #[derive(Debug, PartialEq, Eq)] |
| pub struct StaticSignature { |
| pub name: Name, |
| |
| // generic_params: Arc<GenericParams>, |
| pub store: Arc<ExpressionStore>, |
| pub type_ref: TypeRefId, |
| pub flags: StaticFlags, |
| } |
| impl StaticSignature { |
| pub fn query(db: &dyn DefDatabase, id: StaticId) -> (Arc<Self>, Arc<ExpressionStoreSourceMap>) { |
| let loc = id.lookup(db); |
| let item_tree = loc.id.item_tree(db); |
| |
| let module = loc.container.module(db); |
| let attrs = item_tree.attrs(db, module.krate, ModItem::from(loc.id.value).into()); |
| let mut flags = StaticFlags::empty(); |
| if attrs.by_key(sym::rustc_allow_incoherent_impl).exists() { |
| flags |= StaticFlags::RUSTC_ALLOW_INCOHERENT_IMPL; |
| } |
| |
| if matches!(loc.container, ItemContainerId::ExternBlockId(_)) { |
| flags.insert(StaticFlags::EXTERN); |
| } |
| |
| let source = loc.source(db); |
| if source.value.body().is_some() { |
| flags.insert(StaticFlags::HAS_BODY); |
| } |
| if source.value.mut_token().is_some() { |
| flags.insert(StaticFlags::MUTABLE); |
| } |
| if source.value.unsafe_token().is_some() { |
| flags.insert(StaticFlags::UNSAFE); |
| } |
| if source.value.safe_token().is_some() { |
| flags.insert(StaticFlags::EXPLICIT_SAFE); |
| } |
| |
| let (store, source_map, type_ref) = |
| crate::expr_store::lower::lower_type_ref(db, module, source.map(|it| it.ty())); |
| |
| ( |
| Arc::new(StaticSignature { |
| store: Arc::new(store), |
| type_ref, |
| flags, |
| name: item_tree[loc.id.value].name.clone(), |
| }), |
| Arc::new(source_map), |
| ) |
| } |
| } |
| |
| bitflags::bitflags! { |
| #[derive(Debug, Clone, Copy, Eq, PartialEq, Default)] |
| pub struct ImplFlags: u8 { |
| const NEGATIVE = 1 << 1; |
| const UNSAFE = 1 << 3; |
| } |
| } |
| |
| #[derive(Debug, PartialEq, Eq)] |
| pub struct ImplSignature { |
| pub generic_params: Arc<GenericParams>, |
| pub store: Arc<ExpressionStore>, |
| pub self_ty: TypeRefId, |
| pub target_trait: Option<TraitRef>, |
| pub flags: ImplFlags, |
| } |
| |
| impl ImplSignature { |
| pub fn query(db: &dyn DefDatabase, id: ImplId) -> (Arc<Self>, Arc<ExpressionStoreSourceMap>) { |
| let loc = id.lookup(db); |
| |
| let mut flags = ImplFlags::empty(); |
| let src = loc.source(db); |
| if src.value.unsafe_token().is_some() { |
| flags.insert(ImplFlags::UNSAFE); |
| } |
| if src.value.excl_token().is_some() { |
| flags.insert(ImplFlags::NEGATIVE); |
| } |
| |
| let (store, source_map, self_ty, target_trait, generic_params) = |
| crate::expr_store::lower::lower_impl(db, loc.container, src, id); |
| |
| ( |
| Arc::new(ImplSignature { |
| store: Arc::new(store), |
| generic_params, |
| self_ty, |
| target_trait, |
| flags, |
| }), |
| Arc::new(source_map), |
| ) |
| } |
| } |
| |
| bitflags::bitflags! { |
| #[derive(Debug, Clone, Copy, Eq, PartialEq, Default)] |
| pub struct TraitFlags: u8 { |
| const RUSTC_HAS_INCOHERENT_INHERENT_IMPLS = 1 << 1; |
| const FUNDAMENTAL = 1 << 2; |
| const UNSAFE = 1 << 3; |
| const AUTO = 1 << 4; |
| const SKIP_ARRAY_DURING_METHOD_DISPATCH = 1 << 5; |
| const SKIP_BOXED_SLICE_DURING_METHOD_DISPATCH = 1 << 6; |
| const RUSTC_PAREN_SUGAR = 1 << 7; |
| } |
| } |
| |
| #[derive(Debug, PartialEq, Eq)] |
| pub struct TraitSignature { |
| pub name: Name, |
| pub generic_params: Arc<GenericParams>, |
| pub store: Arc<ExpressionStore>, |
| pub flags: TraitFlags, |
| } |
| |
| impl TraitSignature { |
| pub fn query(db: &dyn DefDatabase, id: TraitId) -> (Arc<Self>, Arc<ExpressionStoreSourceMap>) { |
| let loc = id.lookup(db); |
| let item_tree = loc.id.item_tree(db); |
| |
| let mut flags = TraitFlags::empty(); |
| let attrs = item_tree.attrs(db, loc.container.krate, ModItem::from(loc.id.value).into()); |
| let source = loc.source(db); |
| if source.value.auto_token().is_some() { |
| flags.insert(TraitFlags::AUTO); |
| } |
| if source.value.unsafe_token().is_some() { |
| flags.insert(TraitFlags::UNSAFE); |
| } |
| if attrs.by_key(sym::fundamental).exists() { |
| flags |= TraitFlags::FUNDAMENTAL; |
| } |
| if attrs.by_key(sym::rustc_has_incoherent_inherent_impls).exists() { |
| flags |= TraitFlags::RUSTC_HAS_INCOHERENT_INHERENT_IMPLS; |
| } |
| if attrs.by_key(sym::rustc_paren_sugar).exists() { |
| flags |= TraitFlags::RUSTC_PAREN_SUGAR; |
| } |
| let mut skip_array_during_method_dispatch = |
| attrs.by_key(sym::rustc_skip_array_during_method_dispatch).exists(); |
| let mut skip_boxed_slice_during_method_dispatch = false; |
| for tt in attrs.by_key(sym::rustc_skip_during_method_dispatch).tt_values() { |
| for tt in tt.iter() { |
| if let tt::iter::TtElement::Leaf(tt::Leaf::Ident(ident)) = tt { |
| skip_array_during_method_dispatch |= ident.sym == sym::array; |
| skip_boxed_slice_during_method_dispatch |= ident.sym == sym::boxed_slice; |
| } |
| } |
| } |
| |
| if skip_array_during_method_dispatch { |
| flags |= TraitFlags::SKIP_ARRAY_DURING_METHOD_DISPATCH; |
| } |
| if skip_boxed_slice_during_method_dispatch { |
| flags |= TraitFlags::SKIP_BOXED_SLICE_DURING_METHOD_DISPATCH; |
| } |
| |
| let (store, source_map, generic_params) = lower_trait(db, loc.container, source, id); |
| |
| ( |
| Arc::new(TraitSignature { |
| store: Arc::new(store), |
| generic_params, |
| flags, |
| name: item_tree[loc.id.value].name.clone(), |
| }), |
| Arc::new(source_map), |
| ) |
| } |
| } |
| |
| #[derive(Debug, PartialEq, Eq)] |
| pub struct TraitAliasSignature { |
| pub name: Name, |
| pub generic_params: Arc<GenericParams>, |
| pub store: Arc<ExpressionStore>, |
| } |
| |
| impl TraitAliasSignature { |
| pub fn query( |
| db: &dyn DefDatabase, |
| id: TraitAliasId, |
| ) -> (Arc<Self>, Arc<ExpressionStoreSourceMap>) { |
| let loc = id.lookup(db); |
| let item_tree = loc.id.item_tree(db); |
| |
| let source = loc.source(db); |
| let (store, source_map, generic_params) = lower_trait_alias(db, loc.container, source, id); |
| |
| ( |
| Arc::new(TraitAliasSignature { |
| generic_params, |
| store: Arc::new(store), |
| name: item_tree[loc.id.value].name.clone(), |
| }), |
| Arc::new(source_map), |
| ) |
| } |
| } |
| |
| bitflags! { |
| #[derive(Debug, Clone, Copy, Eq, PartialEq, Default)] |
| pub struct FnFlags: u16 { |
| const HAS_BODY = 1 << 1; |
| const DEFAULT = 1 << 2; |
| const CONST = 1 << 3; |
| const ASYNC = 1 << 4; |
| const UNSAFE = 1 << 5; |
| const HAS_VARARGS = 1 << 6; |
| const RUSTC_ALLOW_INCOHERENT_IMPL = 1 << 7; |
| const HAS_SELF_PARAM = 1 << 8; |
| /// The `#[target_feature]` attribute is necessary to check safety (with RFC 2396), |
| /// but keeping it for all functions will consume a lot of memory when there are |
| /// only very few functions with it. So we only encode its existence here, and lookup |
| /// it if needed. |
| const HAS_TARGET_FEATURE = 1 << 9; |
| const DEPRECATED_SAFE_2024 = 1 << 10; |
| const EXPLICIT_SAFE = 1 << 11; |
| } |
| } |
| |
| #[derive(Debug, PartialEq, Eq)] |
| pub struct FunctionSignature { |
| pub name: Name, |
| pub generic_params: Arc<GenericParams>, |
| pub store: Arc<ExpressionStore>, |
| pub params: Box<[TypeRefId]>, |
| pub ret_type: Option<TypeRefId>, |
| pub abi: Option<Symbol>, |
| pub flags: FnFlags, |
| // FIXME: we should put this behind a fn flags + query to avoid bloating the struct |
| pub legacy_const_generics_indices: Option<Box<Box<[u32]>>>, |
| } |
| |
| impl FunctionSignature { |
| pub fn query( |
| db: &dyn DefDatabase, |
| id: FunctionId, |
| ) -> (Arc<Self>, Arc<ExpressionStoreSourceMap>) { |
| let loc = id.lookup(db); |
| let module = loc.container.module(db); |
| let item_tree = loc.id.item_tree(db); |
| |
| let mut flags = FnFlags::empty(); |
| let attrs = item_tree.attrs(db, module.krate, ModItem::from(loc.id.value).into()); |
| if attrs.by_key(sym::rustc_allow_incoherent_impl).exists() { |
| flags.insert(FnFlags::RUSTC_ALLOW_INCOHERENT_IMPL); |
| } |
| |
| if attrs.by_key(sym::target_feature).exists() { |
| flags.insert(FnFlags::HAS_TARGET_FEATURE); |
| } |
| let legacy_const_generics_indices = attrs.rustc_legacy_const_generics(); |
| |
| let source = loc.source(db); |
| |
| if source.value.unsafe_token().is_some() { |
| if attrs.by_key(sym::rustc_deprecated_safe_2024).exists() { |
| flags.insert(FnFlags::DEPRECATED_SAFE_2024); |
| } else { |
| flags.insert(FnFlags::UNSAFE); |
| } |
| } |
| if source.value.async_token().is_some() { |
| flags.insert(FnFlags::ASYNC); |
| } |
| if source.value.const_token().is_some() { |
| flags.insert(FnFlags::CONST); |
| } |
| if source.value.default_token().is_some() { |
| flags.insert(FnFlags::DEFAULT); |
| } |
| if source.value.safe_token().is_some() { |
| flags.insert(FnFlags::EXPLICIT_SAFE); |
| } |
| if source.value.body().is_some() { |
| flags.insert(FnFlags::HAS_BODY); |
| } |
| |
| let abi = source.value.abi().map(|abi| { |
| abi.abi_string().map_or_else(|| sym::C, |it| Symbol::intern(it.text_without_quotes())) |
| }); |
| let (store, source_map, generic_params, params, ret_type, self_param, variadic) = |
| lower_function(db, module, source, id); |
| if self_param { |
| flags.insert(FnFlags::HAS_SELF_PARAM); |
| } |
| if variadic { |
| flags.insert(FnFlags::HAS_VARARGS); |
| } |
| ( |
| Arc::new(FunctionSignature { |
| generic_params, |
| store: Arc::new(store), |
| params, |
| ret_type, |
| abi, |
| flags, |
| legacy_const_generics_indices, |
| name: item_tree[loc.id.value].name.clone(), |
| }), |
| Arc::new(source_map), |
| ) |
| } |
| |
| pub fn has_body(&self) -> bool { |
| self.flags.contains(FnFlags::HAS_BODY) |
| } |
| |
| /// True if the first param is `self`. This is relevant to decide whether this |
| /// can be called as a method. |
| pub fn has_self_param(&self) -> bool { |
| self.flags.contains(FnFlags::HAS_SELF_PARAM) |
| } |
| |
| pub fn is_default(&self) -> bool { |
| self.flags.contains(FnFlags::DEFAULT) |
| } |
| |
| pub fn is_const(&self) -> bool { |
| self.flags.contains(FnFlags::CONST) |
| } |
| |
| pub fn is_async(&self) -> bool { |
| self.flags.contains(FnFlags::ASYNC) |
| } |
| |
| pub fn is_unsafe(&self) -> bool { |
| self.flags.contains(FnFlags::UNSAFE) |
| } |
| |
| pub fn is_deprecated_safe_2024(&self) -> bool { |
| self.flags.contains(FnFlags::DEPRECATED_SAFE_2024) |
| } |
| |
| pub fn is_safe(&self) -> bool { |
| self.flags.contains(FnFlags::EXPLICIT_SAFE) |
| } |
| |
| pub fn is_varargs(&self) -> bool { |
| self.flags.contains(FnFlags::HAS_VARARGS) |
| } |
| |
| pub fn has_target_feature(&self) -> bool { |
| self.flags.contains(FnFlags::HAS_TARGET_FEATURE) |
| } |
| } |
| |
| bitflags! { |
| #[derive(Debug, Clone, Copy, Eq, PartialEq, Default)] |
| pub struct TypeAliasFlags: u8 { |
| const RUSTC_HAS_INCOHERENT_INHERENT_IMPL = 1 << 1; |
| const IS_EXTERN = 1 << 6; |
| const RUSTC_ALLOW_INCOHERENT_IMPL = 1 << 7; |
| } |
| } |
| |
| #[derive(Debug, PartialEq, Eq)] |
| pub struct TypeAliasSignature { |
| pub name: Name, |
| pub generic_params: Arc<GenericParams>, |
| pub store: Arc<ExpressionStore>, |
| pub bounds: Box<[TypeBound]>, |
| pub ty: Option<TypeRefId>, |
| pub flags: TypeAliasFlags, |
| } |
| |
| impl TypeAliasSignature { |
| pub fn query( |
| db: &dyn DefDatabase, |
| id: TypeAliasId, |
| ) -> (Arc<Self>, Arc<ExpressionStoreSourceMap>) { |
| let loc = id.lookup(db); |
| let item_tree = loc.id.item_tree(db); |
| |
| let mut flags = TypeAliasFlags::empty(); |
| let attrs = item_tree.attrs( |
| db, |
| loc.container.module(db).krate(), |
| ModItem::from(loc.id.value).into(), |
| ); |
| if attrs.by_key(sym::rustc_has_incoherent_inherent_impls).exists() { |
| flags.insert(TypeAliasFlags::RUSTC_HAS_INCOHERENT_INHERENT_IMPL); |
| } |
| if attrs.by_key(sym::rustc_allow_incoherent_impl).exists() { |
| flags.insert(TypeAliasFlags::RUSTC_ALLOW_INCOHERENT_IMPL); |
| } |
| if matches!(loc.container, ItemContainerId::ExternBlockId(_)) { |
| flags.insert(TypeAliasFlags::IS_EXTERN); |
| } |
| let source = loc.source(db); |
| let (store, source_map, generic_params, bounds, ty) = |
| lower_type_alias(db, loc.container.module(db), source, id); |
| |
| ( |
| Arc::new(TypeAliasSignature { |
| store: Arc::new(store), |
| generic_params, |
| flags, |
| bounds, |
| name: item_tree[loc.id.value].name.clone(), |
| ty, |
| }), |
| Arc::new(source_map), |
| ) |
| } |
| } |
| |
| #[derive(Debug, PartialEq, Eq)] |
| pub struct FunctionBody { |
| pub store: Arc<ExpressionStore>, |
| pub parameters: Box<[PatId]>, |
| } |
| |
| #[derive(Debug, PartialEq, Eq)] |
| pub struct SimpleBody { |
| pub store: Arc<ExpressionStore>, |
| } |
| pub type StaticBody = SimpleBody; |
| pub type ConstBody = SimpleBody; |
| pub type EnumVariantBody = SimpleBody; |
| |
| #[derive(Debug, PartialEq, Eq)] |
| pub struct VariantFieldsBody { |
| pub store: Arc<ExpressionStore>, |
| pub fields: Box<[Option<ExprId>]>, |
| } |
| |
| /// A single field of an enum variant or struct |
| #[derive(Debug, Clone, PartialEq, Eq)] |
| pub struct FieldData { |
| pub name: Name, |
| pub type_ref: TypeRefId, |
| pub visibility: RawVisibility, |
| pub is_unsafe: bool, |
| } |
| |
| pub type LocalFieldId = Idx<FieldData>; |
| |
| #[derive(Debug, Clone, PartialEq, Eq)] |
| pub struct VariantFields { |
| fields: Arena<FieldData>, |
| pub store: Arc<ExpressionStore>, |
| pub shape: FieldsShape, |
| } |
| impl VariantFields { |
| #[inline] |
| pub(crate) fn query( |
| db: &dyn DefDatabase, |
| id: VariantId, |
| ) -> (Arc<Self>, Arc<ExpressionStoreSourceMap>) { |
| let (shape, (fields, store, source_map)) = match id { |
| VariantId::EnumVariantId(id) => { |
| let loc = id.lookup(db); |
| let item_tree = loc.id.item_tree(db); |
| let parent = loc.parent.lookup(db); |
| let variant = &item_tree[loc.id.value]; |
| ( |
| variant.shape, |
| lower_fields( |
| db, |
| parent.container, |
| &item_tree, |
| FieldParent::EnumVariant(loc.id.value), |
| loc.source(db).map(|src| { |
| variant.fields.iter().zip( |
| src.field_list() |
| .map(|it| { |
| match it { |
| ast::FieldList::RecordFieldList(record_field_list) => { |
| Either::Left(record_field_list.fields().map(|it| { |
| (SyntaxNodePtr::new(it.syntax()), it.ty()) |
| })) |
| } |
| ast::FieldList::TupleFieldList(field_list) => { |
| Either::Right(field_list.fields().map(|it| { |
| (SyntaxNodePtr::new(it.syntax()), it.ty()) |
| })) |
| } |
| } |
| .into_iter() |
| }) |
| .into_iter() |
| .flatten(), |
| ) |
| }), |
| Some(item_tree[parent.id.value].visibility), |
| ), |
| ) |
| } |
| VariantId::StructId(id) => { |
| let loc = id.lookup(db); |
| let item_tree = loc.id.item_tree(db); |
| let strukt = &item_tree[loc.id.value]; |
| ( |
| strukt.shape, |
| lower_fields( |
| db, |
| loc.container, |
| &item_tree, |
| FieldParent::Struct(loc.id.value), |
| loc.source(db).map(|src| { |
| strukt.fields.iter().zip( |
| src.field_list() |
| .map(|it| { |
| match it { |
| ast::FieldList::RecordFieldList(record_field_list) => { |
| Either::Left(record_field_list.fields().map(|it| { |
| (SyntaxNodePtr::new(it.syntax()), it.ty()) |
| })) |
| } |
| ast::FieldList::TupleFieldList(field_list) => { |
| Either::Right(field_list.fields().map(|it| { |
| (SyntaxNodePtr::new(it.syntax()), it.ty()) |
| })) |
| } |
| } |
| .into_iter() |
| }) |
| .into_iter() |
| .flatten(), |
| ) |
| }), |
| None, |
| ), |
| ) |
| } |
| VariantId::UnionId(id) => { |
| let loc = id.lookup(db); |
| let item_tree = loc.id.item_tree(db); |
| let union = &item_tree[loc.id.value]; |
| ( |
| FieldsShape::Record, |
| lower_fields( |
| db, |
| loc.container, |
| &item_tree, |
| FieldParent::Union(loc.id.value), |
| loc.source(db).map(|src| { |
| union.fields.iter().zip( |
| src.record_field_list() |
| .map(|it| { |
| it.fields() |
| .map(|it| (SyntaxNodePtr::new(it.syntax()), it.ty())) |
| }) |
| .into_iter() |
| .flatten(), |
| ) |
| }), |
| None, |
| ), |
| ) |
| } |
| }; |
| |
| (Arc::new(VariantFields { fields, store: Arc::new(store), shape }), Arc::new(source_map)) |
| } |
| |
| pub fn len(&self) -> usize { |
| self.fields.len() |
| } |
| |
| pub fn fields(&self) -> &Arena<FieldData> { |
| &self.fields |
| } |
| |
| pub fn field(&self, name: &Name) -> Option<LocalFieldId> { |
| self.fields().iter().find_map(|(id, data)| if &data.name == name { Some(id) } else { None }) |
| } |
| } |
| |
| fn lower_fields<'a>( |
| db: &dyn DefDatabase, |
| module: ModuleId, |
| item_tree: &ItemTree, |
| parent: FieldParent, |
| fields: InFile<impl Iterator<Item = (&'a Field, (SyntaxNodePtr, Option<ast::Type>))>>, |
| override_visibility: Option<RawVisibilityId>, |
| ) -> (Arena<FieldData>, ExpressionStore, ExpressionStoreSourceMap) { |
| let mut arena = Arena::new(); |
| let cfg_options = module.krate.cfg_options(db); |
| let mut col = ExprCollector::new(db, module, fields.file_id); |
| for (idx, (field, (ptr, ty))) in fields.value.enumerate() { |
| let attr_owner = AttrOwner::make_field_indexed(parent, idx); |
| let attrs = item_tree.attrs(db, module.krate, attr_owner); |
| if attrs.is_cfg_enabled(cfg_options) { |
| arena.alloc(FieldData { |
| name: field.name.clone(), |
| type_ref: col.lower_type_ref_opt(ty, &mut |_| TypeRef::Error), |
| visibility: item_tree[override_visibility.unwrap_or(field.visibility)].clone(), |
| is_unsafe: field.is_unsafe, |
| }); |
| } else { |
| col.source_map.diagnostics.push( |
| crate::expr_store::ExpressionStoreDiagnostics::InactiveCode { |
| node: InFile::new(fields.file_id, ptr), |
| cfg: attrs.cfg().unwrap(), |
| opts: cfg_options.clone(), |
| }, |
| ); |
| } |
| } |
| let store = col.store.finish(); |
| (arena, store, col.source_map) |
| } |
| |
| #[derive(Debug, PartialEq, Eq)] |
| pub struct InactiveEnumVariantCode { |
| pub cfg: CfgExpr, |
| pub opts: CfgOptions, |
| pub ast_id: span::FileAstId<ast::Variant>, |
| } |
| |
| #[derive(Debug, Clone, PartialEq, Eq)] |
| pub struct EnumVariants { |
| pub variants: Box<[(EnumVariantId, Name)]>, |
| } |
| |
| impl EnumVariants { |
| pub(crate) fn enum_variants_query( |
| db: &dyn DefDatabase, |
| e: EnumId, |
| ) -> (Arc<EnumVariants>, Option<Arc<ThinVec<InactiveEnumVariantCode>>>) { |
| let loc = e.lookup(db); |
| let item_tree = loc.id.item_tree(db); |
| |
| let mut diagnostics = ThinVec::new(); |
| let cfg_options = loc.container.krate.cfg_options(db); |
| let mut index = 0; |
| let variants = FileItemTreeId::range_iter(item_tree[loc.id.value].variants.clone()) |
| .filter_map(|variant| { |
| let attrs = item_tree.attrs(db, loc.container.krate, variant.into()); |
| if attrs.is_cfg_enabled(cfg_options) { |
| let enum_variant = EnumVariantLoc { |
| id: ItemTreeId::new(loc.id.tree_id(), variant), |
| parent: e, |
| index, |
| } |
| .intern(db); |
| index += 1; |
| Some((enum_variant, item_tree[variant].name.clone())) |
| } else { |
| diagnostics.push(InactiveEnumVariantCode { |
| ast_id: item_tree[variant].ast_id, |
| cfg: attrs.cfg().unwrap(), |
| opts: cfg_options.clone(), |
| }); |
| None |
| } |
| }) |
| .collect(); |
| |
| ( |
| Arc::new(EnumVariants { variants }), |
| diagnostics.is_empty().not().then(|| Arc::new(diagnostics)), |
| ) |
| } |
| |
| pub fn variant(&self, name: &Name) -> Option<EnumVariantId> { |
| self.variants.iter().find_map(|(v, n)| if n == name { Some(*v) } else { None }) |
| } |
| |
| // [Adopted from rustc](https://github.com/rust-lang/rust/blob/bd53aa3bf7a24a70d763182303bd75e5fc51a9af/compiler/rustc_middle/src/ty/adt.rs#L446-L448) |
| pub fn is_payload_free(&self, db: &dyn DefDatabase) -> bool { |
| self.variants.iter().all(|&(v, _)| { |
| // The condition check order is slightly modified from rustc |
| // to improve performance by early returning with relatively fast checks |
| let variant = &db.variant_fields(v.into()); |
| if !variant.fields().is_empty() { |
| return false; |
| } |
| // The outer if condition is whether this variant has const ctor or not |
| if !matches!(variant.shape, FieldsShape::Unit) { |
| let body = db.body(v.into()); |
| // A variant with explicit discriminant |
| if body.exprs[body.body_expr] != crate::hir::Expr::Missing { |
| return false; |
| } |
| } |
| true |
| }) |
| } |
| } |