hir_def/
signatures.rs

1//! Item signature IR definitions
2
3use std::{cell::LazyCell, ops::Not as _};
4
5use bitflags::bitflags;
6use cfg::{CfgExpr, CfgOptions};
7use hir_expand::{
8    InFile, Intern, Lookup,
9    name::{AsName, Name},
10};
11use intern::{Symbol, sym};
12use la_arena::{Arena, Idx};
13use rustc_abi::{IntegerType, ReprOptions};
14use syntax::{
15    NodeOrToken, SyntaxNodePtr, T,
16    ast::{self, HasGenericParams, HasName, HasVisibility, IsString},
17};
18use thin_vec::ThinVec;
19use triomphe::Arc;
20
21use crate::{
22    ConstId, EnumId, EnumVariantId, EnumVariantLoc, ExternBlockId, FunctionId, HasModule, ImplId,
23    ItemContainerId, ModuleId, StaticId, StructId, TraitId, TypeAliasId, UnionId, VariantId,
24    attrs::AttrFlags,
25    db::DefDatabase,
26    expr_store::{
27        ExpressionStore, ExpressionStoreSourceMap,
28        lower::{
29            ExprCollector, lower_function, lower_generic_params, lower_trait, lower_type_alias,
30        },
31    },
32    hir::{ExprId, PatId, generics::GenericParams},
33    item_tree::{FieldsShape, RawVisibility, visibility_from_ast},
34    src::HasSource,
35    type_ref::{TraitRef, TypeBound, TypeRefId},
36};
37
38#[inline]
39fn as_name_opt(name: Option<ast::Name>) -> Name {
40    name.map_or_else(Name::missing, |it| it.as_name())
41}
42
43#[derive(Debug, PartialEq, Eq)]
44pub struct StructSignature {
45    pub name: Name,
46    pub generic_params: Arc<GenericParams>,
47    pub store: Arc<ExpressionStore>,
48    pub flags: StructFlags,
49    pub shape: FieldsShape,
50}
51
52bitflags! {
53    #[derive(Debug, Copy, Clone, PartialEq, Eq)]
54    pub struct StructFlags: u8 {
55        /// Indicates whether this struct has `#[repr]`.
56        const HAS_REPR = 1 << 0;
57        /// Indicates whether the struct has a `#[rustc_has_incoherent_inherent_impls]` attribute.
58        const RUSTC_HAS_INCOHERENT_INHERENT_IMPLS = 1 << 1;
59        /// Indicates whether the struct has a `#[fundamental]` attribute.
60        const FUNDAMENTAL      = 1 << 2;
61        /// Indicates whether the struct is `PhantomData`.
62        const IS_PHANTOM_DATA  = 1 << 3;
63        /// Indicates whether this struct is `Box`.
64        const IS_BOX           = 1 << 4;
65        /// Indicates whether this struct is `ManuallyDrop`.
66        const IS_MANUALLY_DROP = 1 << 5;
67        /// Indicates whether this struct is `UnsafeCell`.
68        const IS_UNSAFE_CELL   = 1 << 6;
69        /// Indicates whether this struct is `UnsafePinned`.
70        const IS_UNSAFE_PINNED = 1 << 7;
71    }
72}
73
74impl StructSignature {
75    pub fn query(db: &dyn DefDatabase, id: StructId) -> (Arc<Self>, Arc<ExpressionStoreSourceMap>) {
76        let loc = id.lookup(db);
77        let InFile { file_id, value: source } = loc.source(db);
78        let attrs = AttrFlags::query(db, id.into());
79
80        let mut flags = StructFlags::empty();
81        if attrs.contains(AttrFlags::RUSTC_HAS_INCOHERENT_INHERENT_IMPLS) {
82            flags |= StructFlags::RUSTC_HAS_INCOHERENT_INHERENT_IMPLS;
83        }
84        if attrs.contains(AttrFlags::FUNDAMENTAL) {
85            flags |= StructFlags::FUNDAMENTAL;
86        }
87        if attrs.contains(AttrFlags::HAS_REPR) {
88            flags |= StructFlags::HAS_REPR;
89        }
90        if let Some(lang) = attrs.lang_item_with_attrs(db, id.into()) {
91            match lang {
92                _ if lang == sym::phantom_data => flags |= StructFlags::IS_PHANTOM_DATA,
93                _ if lang == sym::owned_box => flags |= StructFlags::IS_BOX,
94                _ if lang == sym::manually_drop => flags |= StructFlags::IS_MANUALLY_DROP,
95                _ if lang == sym::unsafe_cell => flags |= StructFlags::IS_UNSAFE_CELL,
96                _ if lang == sym::unsafe_pinned => flags |= StructFlags::IS_UNSAFE_PINNED,
97                _ => (),
98            }
99        }
100        let shape = adt_shape(source.kind());
101
102        let (store, generic_params, source_map) = lower_generic_params(
103            db,
104            loc.container,
105            id.into(),
106            file_id,
107            source.generic_param_list(),
108            source.where_clause(),
109        );
110        (
111            Arc::new(StructSignature {
112                generic_params,
113                store,
114                flags,
115                shape,
116                name: as_name_opt(source.name()),
117            }),
118            Arc::new(source_map),
119        )
120    }
121
122    #[inline]
123    pub fn repr(&self, db: &dyn DefDatabase, id: StructId) -> Option<ReprOptions> {
124        if self.flags.contains(StructFlags::HAS_REPR) {
125            AttrFlags::repr(db, id.into())
126        } else {
127            None
128        }
129    }
130}
131
132#[inline]
133fn adt_shape(adt_kind: ast::StructKind) -> FieldsShape {
134    match adt_kind {
135        ast::StructKind::Record(_) => FieldsShape::Record,
136        ast::StructKind::Tuple(_) => FieldsShape::Tuple,
137        ast::StructKind::Unit => FieldsShape::Unit,
138    }
139}
140
141#[derive(Debug, PartialEq, Eq)]
142pub struct UnionSignature {
143    pub name: Name,
144    pub generic_params: Arc<GenericParams>,
145    pub store: Arc<ExpressionStore>,
146    pub flags: StructFlags,
147}
148
149impl UnionSignature {
150    pub fn query(db: &dyn DefDatabase, id: UnionId) -> (Arc<Self>, Arc<ExpressionStoreSourceMap>) {
151        let loc = id.lookup(db);
152        let attrs = AttrFlags::query(db, id.into());
153        let mut flags = StructFlags::empty();
154        if attrs.contains(AttrFlags::RUSTC_HAS_INCOHERENT_INHERENT_IMPLS) {
155            flags |= StructFlags::RUSTC_HAS_INCOHERENT_INHERENT_IMPLS;
156        }
157        if attrs.contains(AttrFlags::FUNDAMENTAL) {
158            flags |= StructFlags::FUNDAMENTAL;
159        }
160        if attrs.contains(AttrFlags::HAS_REPR) {
161            flags |= StructFlags::HAS_REPR;
162        }
163
164        let InFile { file_id, value: source } = loc.source(db);
165        let (store, generic_params, source_map) = lower_generic_params(
166            db,
167            loc.container,
168            id.into(),
169            file_id,
170            source.generic_param_list(),
171            source.where_clause(),
172        );
173        (
174            Arc::new(UnionSignature {
175                generic_params,
176                store,
177                flags,
178                name: as_name_opt(source.name()),
179            }),
180            Arc::new(source_map),
181        )
182    }
183}
184
185bitflags! {
186    #[derive(Debug, Copy, Clone, PartialEq, Eq)]
187    pub struct EnumFlags: u8 {
188        const RUSTC_HAS_INCOHERENT_INHERENT_IMPLS  = 1 << 1;
189    }
190}
191
192#[derive(Debug, PartialEq, Eq)]
193pub struct EnumSignature {
194    pub name: Name,
195    pub generic_params: Arc<GenericParams>,
196    pub store: Arc<ExpressionStore>,
197    pub flags: EnumFlags,
198}
199
200impl EnumSignature {
201    pub fn query(db: &dyn DefDatabase, id: EnumId) -> (Arc<Self>, Arc<ExpressionStoreSourceMap>) {
202        let loc = id.lookup(db);
203        let attrs = AttrFlags::query(db, id.into());
204        let mut flags = EnumFlags::empty();
205        if attrs.contains(AttrFlags::RUSTC_HAS_INCOHERENT_INHERENT_IMPLS) {
206            flags |= EnumFlags::RUSTC_HAS_INCOHERENT_INHERENT_IMPLS;
207        }
208
209        let InFile { file_id, value: source } = loc.source(db);
210        let (store, generic_params, source_map) = lower_generic_params(
211            db,
212            loc.container,
213            id.into(),
214            file_id,
215            source.generic_param_list(),
216            source.where_clause(),
217        );
218
219        (
220            Arc::new(EnumSignature {
221                generic_params,
222                store,
223                flags,
224                name: as_name_opt(source.name()),
225            }),
226            Arc::new(source_map),
227        )
228    }
229
230    pub fn variant_body_type(db: &dyn DefDatabase, id: EnumId) -> IntegerType {
231        match AttrFlags::repr(db, id.into()) {
232            Some(ReprOptions { int: Some(builtin), .. }) => builtin,
233            _ => IntegerType::Pointer(true),
234        }
235    }
236}
237bitflags::bitflags! {
238    #[derive(Debug, Clone, Copy, Eq, PartialEq, Default)]
239    pub struct ConstFlags: u8 {
240        const HAS_BODY = 1 << 1;
241        const RUSTC_ALLOW_INCOHERENT_IMPL = 1 << 7;
242    }
243}
244
245#[derive(Debug, PartialEq, Eq)]
246pub struct ConstSignature {
247    pub name: Option<Name>,
248    // generic_params: Arc<GenericParams>,
249    pub store: Arc<ExpressionStore>,
250    pub type_ref: TypeRefId,
251    pub flags: ConstFlags,
252}
253
254impl ConstSignature {
255    pub fn query(db: &dyn DefDatabase, id: ConstId) -> (Arc<Self>, Arc<ExpressionStoreSourceMap>) {
256        let loc = id.lookup(db);
257
258        let module = loc.container.module(db);
259        let attrs = AttrFlags::query(db, id.into());
260        let mut flags = ConstFlags::empty();
261        if attrs.contains(AttrFlags::RUSTC_ALLOW_INCOHERENT_IMPL) {
262            flags |= ConstFlags::RUSTC_ALLOW_INCOHERENT_IMPL;
263        }
264        let source = loc.source(db);
265        if source.value.body().is_some() {
266            flags.insert(ConstFlags::HAS_BODY);
267        }
268
269        let (store, source_map, type_ref) =
270            crate::expr_store::lower::lower_type_ref(db, module, source.as_ref().map(|it| it.ty()));
271
272        (
273            Arc::new(ConstSignature {
274                store: Arc::new(store),
275                type_ref,
276                flags,
277                name: source.value.name().map(|it| it.as_name()),
278            }),
279            Arc::new(source_map),
280        )
281    }
282
283    pub fn has_body(&self) -> bool {
284        self.flags.contains(ConstFlags::HAS_BODY)
285    }
286}
287
288bitflags::bitflags! {
289    #[derive(Debug, Clone, Copy, Eq, PartialEq, Default)]
290    pub struct StaticFlags: u8 {
291        const HAS_BODY = 1 << 1;
292        const MUTABLE    = 1 << 3;
293        const UNSAFE     = 1 << 4;
294        const EXPLICIT_SAFE = 1 << 5;
295        const EXTERN     = 1 << 6;
296        const RUSTC_ALLOW_INCOHERENT_IMPL = 1 << 7;
297    }
298}
299
300#[derive(Debug, PartialEq, Eq)]
301pub struct StaticSignature {
302    pub name: Name,
303
304    // generic_params: Arc<GenericParams>,
305    pub store: Arc<ExpressionStore>,
306    pub type_ref: TypeRefId,
307    pub flags: StaticFlags,
308}
309impl StaticSignature {
310    pub fn query(db: &dyn DefDatabase, id: StaticId) -> (Arc<Self>, Arc<ExpressionStoreSourceMap>) {
311        let loc = id.lookup(db);
312
313        let module = loc.container.module(db);
314        let attrs = AttrFlags::query(db, id.into());
315        let mut flags = StaticFlags::empty();
316        if attrs.contains(AttrFlags::RUSTC_ALLOW_INCOHERENT_IMPL) {
317            flags |= StaticFlags::RUSTC_ALLOW_INCOHERENT_IMPL;
318        }
319
320        if matches!(loc.container, ItemContainerId::ExternBlockId(_)) {
321            flags.insert(StaticFlags::EXTERN);
322        }
323
324        let source = loc.source(db);
325        if source.value.body().is_some() {
326            flags.insert(StaticFlags::HAS_BODY);
327        }
328        if source.value.mut_token().is_some() {
329            flags.insert(StaticFlags::MUTABLE);
330        }
331        if source.value.unsafe_token().is_some() {
332            flags.insert(StaticFlags::UNSAFE);
333        }
334        if source.value.safe_token().is_some() {
335            flags.insert(StaticFlags::EXPLICIT_SAFE);
336        }
337
338        let (store, source_map, type_ref) =
339            crate::expr_store::lower::lower_type_ref(db, module, source.as_ref().map(|it| it.ty()));
340
341        (
342            Arc::new(StaticSignature {
343                store: Arc::new(store),
344                type_ref,
345                flags,
346                name: as_name_opt(source.value.name()),
347            }),
348            Arc::new(source_map),
349        )
350    }
351}
352
353bitflags::bitflags! {
354    #[derive(Debug, Clone, Copy, Eq, PartialEq, Default)]
355    pub struct ImplFlags: u8 {
356        const NEGATIVE = 1 << 1;
357        const DEFAULT = 1 << 2;
358        const UNSAFE = 1 << 3;
359    }
360}
361
362#[derive(Debug, PartialEq, Eq)]
363pub struct ImplSignature {
364    pub generic_params: Arc<GenericParams>,
365    pub store: Arc<ExpressionStore>,
366    pub self_ty: TypeRefId,
367    pub target_trait: Option<TraitRef>,
368    pub flags: ImplFlags,
369}
370
371impl ImplSignature {
372    pub fn query(db: &dyn DefDatabase, id: ImplId) -> (Arc<Self>, Arc<ExpressionStoreSourceMap>) {
373        let loc = id.lookup(db);
374
375        let mut flags = ImplFlags::empty();
376        let src = loc.source(db);
377        if src.value.unsafe_token().is_some() {
378            flags.insert(ImplFlags::UNSAFE);
379        }
380        if src.value.excl_token().is_some() {
381            flags.insert(ImplFlags::NEGATIVE);
382        }
383        if src.value.default_token().is_some() {
384            flags.insert(ImplFlags::DEFAULT);
385        }
386
387        let (store, source_map, self_ty, target_trait, generic_params) =
388            crate::expr_store::lower::lower_impl(db, loc.container, src, id);
389
390        (
391            Arc::new(ImplSignature {
392                store: Arc::new(store),
393                generic_params,
394                self_ty,
395                target_trait,
396                flags,
397            }),
398            Arc::new(source_map),
399        )
400    }
401
402    #[inline]
403    pub fn is_negative(&self) -> bool {
404        self.flags.contains(ImplFlags::NEGATIVE)
405    }
406
407    #[inline]
408    pub fn is_default(&self) -> bool {
409        self.flags.contains(ImplFlags::DEFAULT)
410    }
411}
412
413bitflags::bitflags! {
414    #[derive(Debug, Clone, Copy, Eq, PartialEq, Default)]
415    pub struct TraitFlags: u16 {
416        const RUSTC_HAS_INCOHERENT_INHERENT_IMPLS = 1 << 1;
417        const FUNDAMENTAL = 1 << 2;
418        const UNSAFE = 1 << 3;
419        const AUTO = 1 << 4;
420        const SKIP_ARRAY_DURING_METHOD_DISPATCH = 1 << 5;
421        const SKIP_BOXED_SLICE_DURING_METHOD_DISPATCH = 1 << 6;
422        const RUSTC_PAREN_SUGAR = 1 << 7;
423        const COINDUCTIVE = 1 << 8;
424        const ALIAS = 1 << 9;
425    }
426}
427
428#[derive(Debug, PartialEq, Eq)]
429pub struct TraitSignature {
430    pub name: Name,
431    pub generic_params: Arc<GenericParams>,
432    pub store: Arc<ExpressionStore>,
433    pub flags: TraitFlags,
434}
435
436impl TraitSignature {
437    pub fn query(db: &dyn DefDatabase, id: TraitId) -> (Arc<Self>, Arc<ExpressionStoreSourceMap>) {
438        let loc = id.lookup(db);
439
440        let mut flags = TraitFlags::empty();
441        let attrs = AttrFlags::query(db, id.into());
442        let source = loc.source(db);
443        if source.value.auto_token().is_some() {
444            flags.insert(TraitFlags::AUTO);
445        }
446        if source.value.unsafe_token().is_some() {
447            flags.insert(TraitFlags::UNSAFE);
448        }
449        if source.value.eq_token().is_some() {
450            flags.insert(TraitFlags::ALIAS);
451        }
452        if attrs.contains(AttrFlags::FUNDAMENTAL) {
453            flags |= TraitFlags::FUNDAMENTAL;
454        }
455        if attrs.contains(AttrFlags::RUSTC_HAS_INCOHERENT_INHERENT_IMPLS) {
456            flags |= TraitFlags::RUSTC_HAS_INCOHERENT_INHERENT_IMPLS;
457        }
458        if attrs.contains(AttrFlags::RUSTC_PAREN_SUGAR) {
459            flags |= TraitFlags::RUSTC_PAREN_SUGAR;
460        }
461        if attrs.contains(AttrFlags::RUSTC_COINDUCTIVE) {
462            flags |= TraitFlags::COINDUCTIVE;
463        }
464
465        if attrs.contains(AttrFlags::RUSTC_SKIP_ARRAY_DURING_METHOD_DISPATCH) {
466            flags |= TraitFlags::SKIP_ARRAY_DURING_METHOD_DISPATCH;
467        }
468        if attrs.contains(AttrFlags::RUSTC_SKIP_BOXED_SLICE_DURING_METHOD_DISPATCH) {
469            flags |= TraitFlags::SKIP_BOXED_SLICE_DURING_METHOD_DISPATCH;
470        }
471
472        let name = as_name_opt(source.value.name());
473        let (store, source_map, generic_params) = lower_trait(db, loc.container, source, id);
474
475        (
476            Arc::new(TraitSignature { store: Arc::new(store), generic_params, flags, name }),
477            Arc::new(source_map),
478        )
479    }
480}
481
482bitflags! {
483    #[derive(Debug, Clone, Copy, Eq, PartialEq, Default)]
484    pub struct FnFlags: u16 {
485        const HAS_BODY = 1 << 1;
486        const DEFAULT = 1 << 2;
487        const CONST = 1 << 3;
488        const ASYNC = 1 << 4;
489        const UNSAFE = 1 << 5;
490        const HAS_VARARGS = 1 << 6;
491        const RUSTC_ALLOW_INCOHERENT_IMPL = 1 << 7;
492        const HAS_SELF_PARAM = 1 << 8;
493        /// The `#[target_feature]` attribute is necessary to check safety (with RFC 2396),
494        /// but keeping it for all functions will consume a lot of memory when there are
495        /// only very few functions with it. So we only encode its existence here, and lookup
496        /// it if needed.
497        const HAS_TARGET_FEATURE = 1 << 9;
498        const DEPRECATED_SAFE_2024 = 1 << 10;
499        const EXPLICIT_SAFE = 1 << 11;
500        const HAS_LEGACY_CONST_GENERICS = 1 << 12;
501        const RUSTC_INTRINSIC = 1 << 13;
502    }
503}
504
505#[derive(Debug, PartialEq, Eq)]
506pub struct FunctionSignature {
507    pub name: Name,
508    pub generic_params: Arc<GenericParams>,
509    pub store: Arc<ExpressionStore>,
510    pub params: Box<[TypeRefId]>,
511    pub ret_type: Option<TypeRefId>,
512    pub abi: Option<Symbol>,
513    pub flags: FnFlags,
514}
515
516impl FunctionSignature {
517    pub fn query(
518        db: &dyn DefDatabase,
519        id: FunctionId,
520    ) -> (Arc<Self>, Arc<ExpressionStoreSourceMap>) {
521        let loc = id.lookup(db);
522        let module = loc.container.module(db);
523
524        let mut flags = FnFlags::empty();
525        let attrs = AttrFlags::query(db, id.into());
526        if attrs.contains(AttrFlags::RUSTC_ALLOW_INCOHERENT_IMPL) {
527            flags.insert(FnFlags::RUSTC_ALLOW_INCOHERENT_IMPL);
528        }
529
530        if attrs.contains(AttrFlags::HAS_TARGET_FEATURE) {
531            flags.insert(FnFlags::HAS_TARGET_FEATURE);
532        }
533
534        if attrs.contains(AttrFlags::RUSTC_INTRINSIC) {
535            flags.insert(FnFlags::RUSTC_INTRINSIC);
536        }
537        if attrs.contains(AttrFlags::HAS_LEGACY_CONST_GENERICS) {
538            flags.insert(FnFlags::HAS_LEGACY_CONST_GENERICS);
539        }
540
541        let source = loc.source(db);
542
543        if source.value.unsafe_token().is_some() {
544            if attrs.contains(AttrFlags::RUSTC_DEPRECATED_SAFE_2024) {
545                flags.insert(FnFlags::DEPRECATED_SAFE_2024);
546            } else {
547                flags.insert(FnFlags::UNSAFE);
548            }
549        }
550        if source.value.async_token().is_some() {
551            flags.insert(FnFlags::ASYNC);
552        }
553        if source.value.const_token().is_some() {
554            flags.insert(FnFlags::CONST);
555        }
556        if source.value.default_token().is_some() {
557            flags.insert(FnFlags::DEFAULT);
558        }
559        if source.value.safe_token().is_some() {
560            flags.insert(FnFlags::EXPLICIT_SAFE);
561        }
562        if source.value.body().is_some() {
563            flags.insert(FnFlags::HAS_BODY);
564        }
565
566        let name = as_name_opt(source.value.name());
567        let abi = source.value.abi().map(|abi| {
568            abi.abi_string().map_or_else(|| sym::C, |it| Symbol::intern(it.text_without_quotes()))
569        });
570        let (store, source_map, generic_params, params, ret_type, self_param, variadic) =
571            lower_function(db, module, source, id);
572        if self_param {
573            flags.insert(FnFlags::HAS_SELF_PARAM);
574        }
575        if variadic {
576            flags.insert(FnFlags::HAS_VARARGS);
577        }
578        (
579            Arc::new(FunctionSignature {
580                generic_params,
581                store: Arc::new(store),
582                params,
583                ret_type,
584                abi,
585                flags,
586                name,
587            }),
588            Arc::new(source_map),
589        )
590    }
591
592    pub fn has_body(&self) -> bool {
593        self.flags.contains(FnFlags::HAS_BODY)
594    }
595
596    /// True if the first param is `self`. This is relevant to decide whether this
597    /// can be called as a method.
598    pub fn has_self_param(&self) -> bool {
599        self.flags.contains(FnFlags::HAS_SELF_PARAM)
600    }
601
602    pub fn is_default(&self) -> bool {
603        self.flags.contains(FnFlags::DEFAULT)
604    }
605
606    pub fn is_const(&self) -> bool {
607        self.flags.contains(FnFlags::CONST)
608    }
609
610    pub fn is_async(&self) -> bool {
611        self.flags.contains(FnFlags::ASYNC)
612    }
613
614    pub fn is_unsafe(&self) -> bool {
615        self.flags.contains(FnFlags::UNSAFE)
616    }
617
618    pub fn is_deprecated_safe_2024(&self) -> bool {
619        self.flags.contains(FnFlags::DEPRECATED_SAFE_2024)
620    }
621
622    pub fn is_safe(&self) -> bool {
623        self.flags.contains(FnFlags::EXPLICIT_SAFE)
624    }
625
626    pub fn is_varargs(&self) -> bool {
627        self.flags.contains(FnFlags::HAS_VARARGS)
628    }
629
630    pub fn has_target_feature(&self) -> bool {
631        self.flags.contains(FnFlags::HAS_TARGET_FEATURE)
632    }
633
634    #[inline]
635    pub fn legacy_const_generics_indices<'db>(
636        &self,
637        db: &'db dyn DefDatabase,
638        id: FunctionId,
639    ) -> Option<&'db [u32]> {
640        if !self.flags.contains(FnFlags::HAS_LEGACY_CONST_GENERICS) {
641            return None;
642        }
643
644        AttrFlags::legacy_const_generic_indices(db, id).as_deref()
645    }
646
647    pub fn is_intrinsic(db: &dyn DefDatabase, id: FunctionId) -> bool {
648        let data = db.function_signature(id);
649        data.flags.contains(FnFlags::RUSTC_INTRINSIC)
650            // Keep this around for a bit until extern "rustc-intrinsic" abis are no longer used
651            || match &data.abi {
652                Some(abi) => *abi == sym::rust_dash_intrinsic,
653                None => match id.lookup(db).container {
654                    ItemContainerId::ExternBlockId(block) => {
655                        block.abi(db) == Some(sym::rust_dash_intrinsic)
656                    }
657                    _ => false,
658                },
659            }
660    }
661}
662
663bitflags! {
664    #[derive(Debug, Clone, Copy, Eq, PartialEq, Default)]
665    pub struct TypeAliasFlags: u8 {
666        const RUSTC_HAS_INCOHERENT_INHERENT_IMPL = 1 << 1;
667        const IS_EXTERN = 1 << 6;
668        const RUSTC_ALLOW_INCOHERENT_IMPL = 1 << 7;
669    }
670}
671
672#[derive(Debug, PartialEq, Eq)]
673pub struct TypeAliasSignature {
674    pub name: Name,
675    pub generic_params: Arc<GenericParams>,
676    pub store: Arc<ExpressionStore>,
677    pub bounds: Box<[TypeBound]>,
678    pub ty: Option<TypeRefId>,
679    pub flags: TypeAliasFlags,
680}
681
682impl TypeAliasSignature {
683    pub fn query(
684        db: &dyn DefDatabase,
685        id: TypeAliasId,
686    ) -> (Arc<Self>, Arc<ExpressionStoreSourceMap>) {
687        let loc = id.lookup(db);
688
689        let mut flags = TypeAliasFlags::empty();
690        let attrs = AttrFlags::query(db, id.into());
691        if attrs.contains(AttrFlags::RUSTC_HAS_INCOHERENT_INHERENT_IMPLS) {
692            flags.insert(TypeAliasFlags::RUSTC_HAS_INCOHERENT_INHERENT_IMPL);
693        }
694        if attrs.contains(AttrFlags::RUSTC_ALLOW_INCOHERENT_IMPL) {
695            flags.insert(TypeAliasFlags::RUSTC_ALLOW_INCOHERENT_IMPL);
696        }
697        if matches!(loc.container, ItemContainerId::ExternBlockId(_)) {
698            flags.insert(TypeAliasFlags::IS_EXTERN);
699        }
700        let source = loc.source(db);
701        let name = as_name_opt(source.value.name());
702        let (store, source_map, generic_params, bounds, ty) =
703            lower_type_alias(db, loc.container.module(db), source, id);
704
705        (
706            Arc::new(TypeAliasSignature {
707                store: Arc::new(store),
708                generic_params,
709                flags,
710                bounds,
711                name,
712                ty,
713            }),
714            Arc::new(source_map),
715        )
716    }
717}
718
719#[derive(Debug, PartialEq, Eq)]
720pub struct FunctionBody {
721    pub store: Arc<ExpressionStore>,
722    pub parameters: Box<[PatId]>,
723}
724
725#[derive(Debug, PartialEq, Eq)]
726pub struct SimpleBody {
727    pub store: Arc<ExpressionStore>,
728}
729pub type StaticBody = SimpleBody;
730pub type ConstBody = SimpleBody;
731pub type EnumVariantBody = SimpleBody;
732
733#[derive(Debug, PartialEq, Eq)]
734pub struct VariantFieldsBody {
735    pub store: Arc<ExpressionStore>,
736    pub fields: Box<[Option<ExprId>]>,
737}
738
739/// A single field of an enum variant or struct
740#[derive(Debug, Clone, PartialEq, Eq)]
741pub struct FieldData {
742    pub name: Name,
743    pub type_ref: TypeRefId,
744    pub visibility: RawVisibility,
745    pub is_unsafe: bool,
746}
747
748pub type LocalFieldId = Idx<FieldData>;
749
750#[derive(Debug, Clone, PartialEq, Eq)]
751pub struct VariantFields {
752    fields: Arena<FieldData>,
753    pub store: Arc<ExpressionStore>,
754    pub shape: FieldsShape,
755}
756
757#[salsa::tracked]
758impl VariantFields {
759    #[salsa::tracked(returns(clone))]
760    pub(crate) fn query(
761        db: &dyn DefDatabase,
762        id: VariantId,
763    ) -> (Arc<Self>, Arc<ExpressionStoreSourceMap>) {
764        let (shape, result) = match id {
765            VariantId::EnumVariantId(id) => {
766                let loc = id.lookup(db);
767                let parent = loc.parent.lookup(db);
768                let source = loc.source(db);
769                let shape = adt_shape(source.value.kind());
770                let enum_vis = Some(source.value.parent_enum().visibility());
771                let fields = lower_field_list(
772                    db,
773                    parent.container,
774                    source.map(|src| src.field_list()),
775                    enum_vis,
776                );
777                (shape, fields)
778            }
779            VariantId::StructId(id) => {
780                let loc = id.lookup(db);
781                let source = loc.source(db);
782                let shape = adt_shape(source.value.kind());
783                let fields =
784                    lower_field_list(db, loc.container, source.map(|src| src.field_list()), None);
785                (shape, fields)
786            }
787            VariantId::UnionId(id) => {
788                let loc = id.lookup(db);
789                let source = loc.source(db);
790                let fields = lower_field_list(
791                    db,
792                    loc.container,
793                    source.map(|src| src.record_field_list().map(ast::FieldList::RecordFieldList)),
794                    None,
795                );
796                (FieldsShape::Record, fields)
797            }
798        };
799        match result {
800            Some((fields, store, source_map)) => (
801                Arc::new(VariantFields { fields, store: Arc::new(store), shape }),
802                Arc::new(source_map),
803            ),
804            None => {
805                let (store, source_map) = ExpressionStore::empty_singleton();
806                (Arc::new(VariantFields { fields: Arena::default(), store, shape }), source_map)
807            }
808        }
809    }
810
811    #[salsa::tracked(returns(deref))]
812    pub(crate) fn firewall(db: &dyn DefDatabase, id: VariantId) -> Arc<Self> {
813        Self::query(db, id).0
814    }
815}
816
817impl VariantFields {
818    pub fn len(&self) -> usize {
819        self.fields.len()
820    }
821
822    pub fn fields(&self) -> &Arena<FieldData> {
823        &self.fields
824    }
825
826    pub fn field(&self, name: &Name) -> Option<LocalFieldId> {
827        self.fields().iter().find_map(|(id, data)| if &data.name == name { Some(id) } else { None })
828    }
829}
830
831fn lower_field_list(
832    db: &dyn DefDatabase,
833    module: ModuleId,
834    fields: InFile<Option<ast::FieldList>>,
835    override_visibility: Option<Option<ast::Visibility>>,
836) -> Option<(Arena<FieldData>, ExpressionStore, ExpressionStoreSourceMap)> {
837    let file_id = fields.file_id;
838    match fields.value? {
839        ast::FieldList::RecordFieldList(fields) => lower_fields(
840            db,
841            module,
842            InFile::new(file_id, fields.fields().map(|field| (field.ty(), field))),
843            |_, field| as_name_opt(field.name()),
844            override_visibility,
845        ),
846        ast::FieldList::TupleFieldList(fields) => lower_fields(
847            db,
848            module,
849            InFile::new(file_id, fields.fields().map(|field| (field.ty(), field))),
850            |idx, _| Name::new_tuple_field(idx),
851            override_visibility,
852        ),
853    }
854}
855
856fn lower_fields<Field: ast::HasAttrs + ast::HasVisibility>(
857    db: &dyn DefDatabase,
858    module: ModuleId,
859    fields: InFile<impl Iterator<Item = (Option<ast::Type>, Field)>>,
860    mut field_name: impl FnMut(usize, &Field) -> Name,
861    override_visibility: Option<Option<ast::Visibility>>,
862) -> Option<(Arena<FieldData>, ExpressionStore, ExpressionStoreSourceMap)> {
863    let cfg_options = module.krate(db).cfg_options(db);
864    let mut col = ExprCollector::new(db, module, fields.file_id);
865    let override_visibility = override_visibility.map(|vis| {
866        LazyCell::new(|| {
867            let span_map = db.span_map(fields.file_id);
868            visibility_from_ast(db, vis, &mut |range| span_map.span_for_range(range).ctx)
869        })
870    });
871
872    let mut arena = Arena::new();
873    let mut idx = 0;
874    let mut has_fields = false;
875    for (ty, field) in fields.value {
876        has_fields = true;
877        match AttrFlags::is_cfg_enabled_for(&field, cfg_options) {
878            Ok(()) => {
879                let type_ref =
880                    col.lower_type_ref_opt(ty, &mut ExprCollector::impl_trait_error_allocator);
881                let visibility = override_visibility.as_ref().map_or_else(
882                    || {
883                        visibility_from_ast(db, field.visibility(), &mut |range| {
884                            col.span_map().span_for_range(range).ctx
885                        })
886                    },
887                    |it| RawVisibility::clone(it),
888                );
889                let is_unsafe = field
890                    .syntax()
891                    .children_with_tokens()
892                    .filter_map(NodeOrToken::into_token)
893                    .any(|token| token.kind() == T![unsafe]);
894                let name = field_name(idx, &field);
895                arena.alloc(FieldData { name, type_ref, visibility, is_unsafe });
896                idx += 1;
897            }
898            Err(cfg) => {
899                col.store.diagnostics.push(
900                    crate::expr_store::ExpressionStoreDiagnostics::InactiveCode {
901                        node: InFile::new(fields.file_id, SyntaxNodePtr::new(field.syntax())),
902                        cfg,
903                        opts: cfg_options.clone(),
904                    },
905                );
906            }
907        }
908    }
909    if !has_fields {
910        return None;
911    }
912    let (store, source_map) = col.store.finish();
913    arena.shrink_to_fit();
914    Some((arena, store, source_map))
915}
916
917#[derive(Debug, PartialEq, Eq)]
918pub struct InactiveEnumVariantCode {
919    pub cfg: CfgExpr,
920    pub opts: CfgOptions,
921    pub ast_id: span::FileAstId<ast::Variant>,
922}
923
924#[derive(Debug, Clone, PartialEq, Eq)]
925pub struct EnumVariants {
926    pub variants: Box<[(EnumVariantId, Name, FieldsShape)]>,
927}
928
929#[salsa::tracked]
930impl EnumVariants {
931    #[salsa::tracked(returns(ref))]
932    pub(crate) fn of(
933        db: &dyn DefDatabase,
934        e: EnumId,
935    ) -> (EnumVariants, Option<ThinVec<InactiveEnumVariantCode>>) {
936        let loc = e.lookup(db);
937        let source = loc.source(db);
938        let ast_id_map = db.ast_id_map(source.file_id);
939
940        let mut diagnostics = ThinVec::new();
941        let cfg_options = loc.container.krate(db).cfg_options(db);
942        let mut index = 0;
943        let Some(variants) = source.value.variant_list() else {
944            return (EnumVariants { variants: Box::default() }, None);
945        };
946        let variants = variants
947            .variants()
948            .filter_map(|variant| {
949                let ast_id = ast_id_map.ast_id(&variant);
950                match AttrFlags::is_cfg_enabled_for(&variant, cfg_options) {
951                    Ok(()) => {
952                        let enum_variant =
953                            EnumVariantLoc { id: source.with_value(ast_id), parent: e, index }
954                                .intern(db);
955                        index += 1;
956                        let name = as_name_opt(variant.name());
957                        let shape = adt_shape(variant.kind());
958                        Some((enum_variant, name, shape))
959                    }
960                    Err(cfg) => {
961                        diagnostics.push(InactiveEnumVariantCode {
962                            ast_id,
963                            cfg,
964                            opts: cfg_options.clone(),
965                        });
966                        None
967                    }
968                }
969            })
970            .collect();
971
972        (EnumVariants { variants }, diagnostics.is_empty().not().then_some(diagnostics))
973    }
974}
975
976impl EnumVariants {
977    pub fn variant(&self, name: &Name) -> Option<EnumVariantId> {
978        self.variants.iter().find_map(|(v, n, _)| if n == name { Some(*v) } else { None })
979    }
980
981    pub fn variant_name_by_id(&self, variant_id: EnumVariantId) -> Option<Name> {
982        self.variants
983            .iter()
984            .find_map(|(id, name, _)| if *id == variant_id { Some(name.clone()) } else { None })
985    }
986
987    // [Adopted from rustc](https://github.com/rust-lang/rust/blob/bd53aa3bf7a24a70d763182303bd75e5fc51a9af/compiler/rustc_middle/src/ty/adt.rs#L446-L448)
988    pub fn is_payload_free(&self, db: &dyn DefDatabase) -> bool {
989        self.variants.iter().all(|&(v, _, _)| {
990            // The condition check order is slightly modified from rustc
991            // to improve performance by early returning with relatively fast checks
992            let variant = v.fields(db);
993            if !variant.fields().is_empty() {
994                return false;
995            }
996            // The outer if condition is whether this variant has const ctor or not
997            if !matches!(variant.shape, FieldsShape::Unit) {
998                let body = db.body(v.into());
999                // A variant with explicit discriminant
1000                if !matches!(body[body.body_expr], crate::hir::Expr::Missing) {
1001                    return false;
1002                }
1003            }
1004            true
1005        })
1006    }
1007}
1008
1009pub(crate) fn extern_block_abi(
1010    db: &dyn DefDatabase,
1011    extern_block: ExternBlockId,
1012) -> Option<Symbol> {
1013    let source = extern_block.lookup(db).source(db);
1014    source.value.abi().map(|abi| {
1015        match abi.abi_string() {
1016            Some(tok) => Symbol::intern(tok.text_without_quotes()),
1017            // `extern` default to be `extern "C"`.
1018            _ => sym::C,
1019        }
1020    })
1021}