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