Unnamed repository; edit this file 'description' to name the repository.
Diffstat (limited to 'crates/hir-def/src/signatures.rs')
-rw-r--r--crates/hir-def/src/signatures.rs975
1 files changed, 975 insertions, 0 deletions
diff --git a/crates/hir-def/src/signatures.rs b/crates/hir-def/src/signatures.rs
new file mode 100644
index 0000000000..44cfd72c48
--- /dev/null
+++ b/crates/hir-def/src/signatures.rs
@@ -0,0 +1,975 @@
+//! 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, 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 ExprCollector::impl_trait_error_allocator),
+ 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
+ })
+ }
+}