1use 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 const RUSTC_HAS_INCOHERENT_INHERENT_IMPLS = 1 << 1;
59 const FUNDAMENTAL = 1 << 2;
61 const IS_PHANTOM_DATA = 1 << 3;
63 const IS_BOX = 1 << 4;
65 const IS_MANUALLY_DROP = 1 << 5;
67 const IS_UNSAFE_CELL = 1 << 6;
69 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 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 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 UNSAFE = 1 << 3;
353 }
354}
355
356#[derive(Debug, PartialEq, Eq)]
357pub struct ImplSignature {
358 pub generic_params: Arc<GenericParams>,
359 pub store: Arc<ExpressionStore>,
360 pub self_ty: TypeRefId,
361 pub target_trait: Option<TraitRef>,
362 pub flags: ImplFlags,
363}
364
365impl ImplSignature {
366 pub fn query(db: &dyn DefDatabase, id: ImplId) -> (Arc<Self>, Arc<ExpressionStoreSourceMap>) {
367 let loc = id.lookup(db);
368
369 let mut flags = ImplFlags::empty();
370 let src = loc.source(db);
371 if src.value.unsafe_token().is_some() {
372 flags.insert(ImplFlags::UNSAFE);
373 }
374 if src.value.excl_token().is_some() {
375 flags.insert(ImplFlags::NEGATIVE);
376 }
377
378 let (store, source_map, self_ty, target_trait, generic_params) =
379 crate::expr_store::lower::lower_impl(db, loc.container, src, id);
380
381 (
382 Arc::new(ImplSignature {
383 store: Arc::new(store),
384 generic_params,
385 self_ty,
386 target_trait,
387 flags,
388 }),
389 Arc::new(source_map),
390 )
391 }
392}
393
394bitflags::bitflags! {
395 #[derive(Debug, Clone, Copy, Eq, PartialEq, Default)]
396 pub struct TraitFlags: u16 {
397 const RUSTC_HAS_INCOHERENT_INHERENT_IMPLS = 1 << 1;
398 const FUNDAMENTAL = 1 << 2;
399 const UNSAFE = 1 << 3;
400 const AUTO = 1 << 4;
401 const SKIP_ARRAY_DURING_METHOD_DISPATCH = 1 << 5;
402 const SKIP_BOXED_SLICE_DURING_METHOD_DISPATCH = 1 << 6;
403 const RUSTC_PAREN_SUGAR = 1 << 7;
404 const COINDUCTIVE = 1 << 8;
405 const ALIAS = 1 << 9;
406 }
407}
408
409#[derive(Debug, PartialEq, Eq)]
410pub struct TraitSignature {
411 pub name: Name,
412 pub generic_params: Arc<GenericParams>,
413 pub store: Arc<ExpressionStore>,
414 pub flags: TraitFlags,
415}
416
417impl TraitSignature {
418 pub fn query(db: &dyn DefDatabase, id: TraitId) -> (Arc<Self>, Arc<ExpressionStoreSourceMap>) {
419 let loc = id.lookup(db);
420
421 let mut flags = TraitFlags::empty();
422 let attrs = db.attrs(id.into());
423 let source = loc.source(db);
424 if source.value.auto_token().is_some() {
425 flags.insert(TraitFlags::AUTO);
426 }
427 if source.value.unsafe_token().is_some() {
428 flags.insert(TraitFlags::UNSAFE);
429 }
430 if source.value.eq_token().is_some() {
431 flags.insert(TraitFlags::ALIAS);
432 }
433 if attrs.by_key(sym::fundamental).exists() {
434 flags |= TraitFlags::FUNDAMENTAL;
435 }
436 if attrs.by_key(sym::rustc_has_incoherent_inherent_impls).exists() {
437 flags |= TraitFlags::RUSTC_HAS_INCOHERENT_INHERENT_IMPLS;
438 }
439 if attrs.by_key(sym::rustc_paren_sugar).exists() {
440 flags |= TraitFlags::RUSTC_PAREN_SUGAR;
441 }
442 if attrs.by_key(sym::rustc_coinductive).exists() {
443 flags |= TraitFlags::COINDUCTIVE;
444 }
445 let mut skip_array_during_method_dispatch =
446 attrs.by_key(sym::rustc_skip_array_during_method_dispatch).exists();
447 let mut skip_boxed_slice_during_method_dispatch = false;
448 for tt in attrs.by_key(sym::rustc_skip_during_method_dispatch).tt_values() {
449 for tt in tt.iter() {
450 if let tt::iter::TtElement::Leaf(tt::Leaf::Ident(ident)) = tt {
451 skip_array_during_method_dispatch |= ident.sym == sym::array;
452 skip_boxed_slice_during_method_dispatch |= ident.sym == sym::boxed_slice;
453 }
454 }
455 }
456
457 if skip_array_during_method_dispatch {
458 flags |= TraitFlags::SKIP_ARRAY_DURING_METHOD_DISPATCH;
459 }
460 if skip_boxed_slice_during_method_dispatch {
461 flags |= TraitFlags::SKIP_BOXED_SLICE_DURING_METHOD_DISPATCH;
462 }
463
464 let name = as_name_opt(source.value.name());
465 let (store, source_map, generic_params) = lower_trait(db, loc.container, source, id);
466
467 (
468 Arc::new(TraitSignature { store: Arc::new(store), generic_params, flags, name }),
469 Arc::new(source_map),
470 )
471 }
472}
473
474bitflags! {
475 #[derive(Debug, Clone, Copy, Eq, PartialEq, Default)]
476 pub struct FnFlags: u16 {
477 const HAS_BODY = 1 << 1;
478 const DEFAULT = 1 << 2;
479 const CONST = 1 << 3;
480 const ASYNC = 1 << 4;
481 const UNSAFE = 1 << 5;
482 const HAS_VARARGS = 1 << 6;
483 const RUSTC_ALLOW_INCOHERENT_IMPL = 1 << 7;
484 const HAS_SELF_PARAM = 1 << 8;
485 const HAS_TARGET_FEATURE = 1 << 9;
490 const DEPRECATED_SAFE_2024 = 1 << 10;
491 const EXPLICIT_SAFE = 1 << 11;
492 }
493}
494
495#[derive(Debug, PartialEq, Eq)]
496pub struct FunctionSignature {
497 pub name: Name,
498 pub generic_params: Arc<GenericParams>,
499 pub store: Arc<ExpressionStore>,
500 pub params: Box<[TypeRefId]>,
501 pub ret_type: Option<TypeRefId>,
502 pub abi: Option<Symbol>,
503 pub flags: FnFlags,
504 pub legacy_const_generics_indices: Option<Box<Box<[u32]>>>,
506}
507
508impl FunctionSignature {
509 pub fn query(
510 db: &dyn DefDatabase,
511 id: FunctionId,
512 ) -> (Arc<Self>, Arc<ExpressionStoreSourceMap>) {
513 let loc = id.lookup(db);
514 let module = loc.container.module(db);
515
516 let mut flags = FnFlags::empty();
517 let attrs = db.attrs(id.into());
518 if attrs.by_key(sym::rustc_allow_incoherent_impl).exists() {
519 flags.insert(FnFlags::RUSTC_ALLOW_INCOHERENT_IMPL);
520 }
521
522 if attrs.by_key(sym::target_feature).exists() {
523 flags.insert(FnFlags::HAS_TARGET_FEATURE);
524 }
525 let legacy_const_generics_indices = attrs.rustc_legacy_const_generics();
526
527 let source = loc.source(db);
528
529 if source.value.unsafe_token().is_some() {
530 if attrs.by_key(sym::rustc_deprecated_safe_2024).exists() {
531 flags.insert(FnFlags::DEPRECATED_SAFE_2024);
532 } else {
533 flags.insert(FnFlags::UNSAFE);
534 }
535 }
536 if source.value.async_token().is_some() {
537 flags.insert(FnFlags::ASYNC);
538 }
539 if source.value.const_token().is_some() {
540 flags.insert(FnFlags::CONST);
541 }
542 if source.value.default_token().is_some() {
543 flags.insert(FnFlags::DEFAULT);
544 }
545 if source.value.safe_token().is_some() {
546 flags.insert(FnFlags::EXPLICIT_SAFE);
547 }
548 if source.value.body().is_some() {
549 flags.insert(FnFlags::HAS_BODY);
550 }
551
552 let name = as_name_opt(source.value.name());
553 let abi = source.value.abi().map(|abi| {
554 abi.abi_string().map_or_else(|| sym::C, |it| Symbol::intern(it.text_without_quotes()))
555 });
556 let (store, source_map, generic_params, params, ret_type, self_param, variadic) =
557 lower_function(db, module, source, id);
558 if self_param {
559 flags.insert(FnFlags::HAS_SELF_PARAM);
560 }
561 if variadic {
562 flags.insert(FnFlags::HAS_VARARGS);
563 }
564 (
565 Arc::new(FunctionSignature {
566 generic_params,
567 store: Arc::new(store),
568 params,
569 ret_type,
570 abi,
571 flags,
572 legacy_const_generics_indices,
573 name,
574 }),
575 Arc::new(source_map),
576 )
577 }
578
579 pub fn has_body(&self) -> bool {
580 self.flags.contains(FnFlags::HAS_BODY)
581 }
582
583 pub fn has_self_param(&self) -> bool {
586 self.flags.contains(FnFlags::HAS_SELF_PARAM)
587 }
588
589 pub fn is_default(&self) -> bool {
590 self.flags.contains(FnFlags::DEFAULT)
591 }
592
593 pub fn is_const(&self) -> bool {
594 self.flags.contains(FnFlags::CONST)
595 }
596
597 pub fn is_async(&self) -> bool {
598 self.flags.contains(FnFlags::ASYNC)
599 }
600
601 pub fn is_unsafe(&self) -> bool {
602 self.flags.contains(FnFlags::UNSAFE)
603 }
604
605 pub fn is_deprecated_safe_2024(&self) -> bool {
606 self.flags.contains(FnFlags::DEPRECATED_SAFE_2024)
607 }
608
609 pub fn is_safe(&self) -> bool {
610 self.flags.contains(FnFlags::EXPLICIT_SAFE)
611 }
612
613 pub fn is_varargs(&self) -> bool {
614 self.flags.contains(FnFlags::HAS_VARARGS)
615 }
616
617 pub fn has_target_feature(&self) -> bool {
618 self.flags.contains(FnFlags::HAS_TARGET_FEATURE)
619 }
620}
621
622bitflags! {
623 #[derive(Debug, Clone, Copy, Eq, PartialEq, Default)]
624 pub struct TypeAliasFlags: u8 {
625 const RUSTC_HAS_INCOHERENT_INHERENT_IMPL = 1 << 1;
626 const IS_EXTERN = 1 << 6;
627 const RUSTC_ALLOW_INCOHERENT_IMPL = 1 << 7;
628 }
629}
630
631#[derive(Debug, PartialEq, Eq)]
632pub struct TypeAliasSignature {
633 pub name: Name,
634 pub generic_params: Arc<GenericParams>,
635 pub store: Arc<ExpressionStore>,
636 pub bounds: Box<[TypeBound]>,
637 pub ty: Option<TypeRefId>,
638 pub flags: TypeAliasFlags,
639}
640
641impl TypeAliasSignature {
642 pub fn query(
643 db: &dyn DefDatabase,
644 id: TypeAliasId,
645 ) -> (Arc<Self>, Arc<ExpressionStoreSourceMap>) {
646 let loc = id.lookup(db);
647
648 let mut flags = TypeAliasFlags::empty();
649 let attrs = db.attrs(id.into());
650 if attrs.by_key(sym::rustc_has_incoherent_inherent_impls).exists() {
651 flags.insert(TypeAliasFlags::RUSTC_HAS_INCOHERENT_INHERENT_IMPL);
652 }
653 if attrs.by_key(sym::rustc_allow_incoherent_impl).exists() {
654 flags.insert(TypeAliasFlags::RUSTC_ALLOW_INCOHERENT_IMPL);
655 }
656 if matches!(loc.container, ItemContainerId::ExternBlockId(_)) {
657 flags.insert(TypeAliasFlags::IS_EXTERN);
658 }
659 let source = loc.source(db);
660 let name = as_name_opt(source.value.name());
661 let (store, source_map, generic_params, bounds, ty) =
662 lower_type_alias(db, loc.container.module(db), source, id);
663
664 (
665 Arc::new(TypeAliasSignature {
666 store: Arc::new(store),
667 generic_params,
668 flags,
669 bounds,
670 name,
671 ty,
672 }),
673 Arc::new(source_map),
674 )
675 }
676}
677
678#[derive(Debug, PartialEq, Eq)]
679pub struct FunctionBody {
680 pub store: Arc<ExpressionStore>,
681 pub parameters: Box<[PatId]>,
682}
683
684#[derive(Debug, PartialEq, Eq)]
685pub struct SimpleBody {
686 pub store: Arc<ExpressionStore>,
687}
688pub type StaticBody = SimpleBody;
689pub type ConstBody = SimpleBody;
690pub type EnumVariantBody = SimpleBody;
691
692#[derive(Debug, PartialEq, Eq)]
693pub struct VariantFieldsBody {
694 pub store: Arc<ExpressionStore>,
695 pub fields: Box<[Option<ExprId>]>,
696}
697
698#[derive(Debug, Clone, PartialEq, Eq)]
700pub struct FieldData {
701 pub name: Name,
702 pub type_ref: TypeRefId,
703 pub visibility: RawVisibility,
704 pub is_unsafe: bool,
705}
706
707pub type LocalFieldId = Idx<FieldData>;
708
709#[derive(Debug, Clone, PartialEq, Eq)]
710pub struct VariantFields {
711 fields: Arena<FieldData>,
712 pub store: Arc<ExpressionStore>,
713 pub shape: FieldsShape,
714}
715
716#[salsa::tracked]
717impl VariantFields {
718 #[salsa::tracked(returns(clone))]
719 pub(crate) fn query(
720 db: &dyn DefDatabase,
721 id: VariantId,
722 ) -> (Arc<Self>, Arc<ExpressionStoreSourceMap>) {
723 let (shape, result) = match id {
724 VariantId::EnumVariantId(id) => {
725 let loc = id.lookup(db);
726 let parent = loc.parent.lookup(db);
727 let source = loc.source(db);
728 let shape = adt_shape(source.value.kind());
729 let enum_vis = Some(source.value.parent_enum().visibility());
730 let fields = lower_field_list(
731 db,
732 parent.container,
733 source.map(|src| src.field_list()),
734 enum_vis,
735 );
736 (shape, fields)
737 }
738 VariantId::StructId(id) => {
739 let loc = id.lookup(db);
740 let source = loc.source(db);
741 let shape = adt_shape(source.value.kind());
742 let fields =
743 lower_field_list(db, loc.container, source.map(|src| src.field_list()), None);
744 (shape, fields)
745 }
746 VariantId::UnionId(id) => {
747 let loc = id.lookup(db);
748 let source = loc.source(db);
749 let fields = lower_field_list(
750 db,
751 loc.container,
752 source.map(|src| src.record_field_list().map(ast::FieldList::RecordFieldList)),
753 None,
754 );
755 (FieldsShape::Record, fields)
756 }
757 };
758 match result {
759 Some((fields, store, source_map)) => (
760 Arc::new(VariantFields { fields, store: Arc::new(store), shape }),
761 Arc::new(source_map),
762 ),
763 None => {
764 let (store, source_map) = ExpressionStore::empty_singleton();
765 (Arc::new(VariantFields { fields: Arena::default(), store, shape }), source_map)
766 }
767 }
768 }
769
770 #[salsa::tracked(returns(deref))]
771 pub(crate) fn firewall(db: &dyn DefDatabase, id: VariantId) -> Arc<Self> {
772 Self::query(db, id).0
773 }
774}
775
776impl VariantFields {
777 pub fn len(&self) -> usize {
778 self.fields.len()
779 }
780
781 pub fn fields(&self) -> &Arena<FieldData> {
782 &self.fields
783 }
784
785 pub fn field(&self, name: &Name) -> Option<LocalFieldId> {
786 self.fields().iter().find_map(|(id, data)| if &data.name == name { Some(id) } else { None })
787 }
788}
789
790fn lower_field_list(
791 db: &dyn DefDatabase,
792 module: ModuleId,
793 fields: InFile<Option<ast::FieldList>>,
794 override_visibility: Option<Option<ast::Visibility>>,
795) -> Option<(Arena<FieldData>, ExpressionStore, ExpressionStoreSourceMap)> {
796 let file_id = fields.file_id;
797 match fields.value? {
798 ast::FieldList::RecordFieldList(fields) => lower_fields(
799 db,
800 module,
801 InFile::new(file_id, fields.fields().map(|field| (field.ty(), field))),
802 |_, field| as_name_opt(field.name()),
803 override_visibility,
804 ),
805 ast::FieldList::TupleFieldList(fields) => lower_fields(
806 db,
807 module,
808 InFile::new(file_id, fields.fields().map(|field| (field.ty(), field))),
809 |idx, _| Name::new_tuple_field(idx),
810 override_visibility,
811 ),
812 }
813}
814
815fn lower_fields<Field: ast::HasAttrs + ast::HasVisibility>(
816 db: &dyn DefDatabase,
817 module: ModuleId,
818 fields: InFile<impl Iterator<Item = (Option<ast::Type>, Field)>>,
819 mut field_name: impl FnMut(usize, &Field) -> Name,
820 override_visibility: Option<Option<ast::Visibility>>,
821) -> Option<(Arena<FieldData>, ExpressionStore, ExpressionStoreSourceMap)> {
822 let cfg_options = module.krate.cfg_options(db);
823 let mut col = ExprCollector::new(db, module, fields.file_id);
824 let override_visibility = override_visibility.map(|vis| {
825 LazyCell::new(|| {
826 let span_map = db.span_map(fields.file_id);
827 visibility_from_ast(db, vis, &mut |range| span_map.span_for_range(range).ctx)
828 })
829 });
830
831 let mut arena = Arena::new();
832 let mut idx = 0;
833 let mut has_fields = false;
834 for (ty, field) in fields.value {
835 has_fields = true;
836 match Attrs::is_cfg_enabled_for(db, &field, col.span_map(), cfg_options) {
837 Ok(()) => {
838 let type_ref =
839 col.lower_type_ref_opt(ty, &mut ExprCollector::impl_trait_error_allocator);
840 let visibility = override_visibility.as_ref().map_or_else(
841 || {
842 visibility_from_ast(db, field.visibility(), &mut |range| {
843 col.span_map().span_for_range(range).ctx
844 })
845 },
846 |it| RawVisibility::clone(it),
847 );
848 let is_unsafe = field
849 .syntax()
850 .children_with_tokens()
851 .filter_map(NodeOrToken::into_token)
852 .any(|token| token.kind() == T![unsafe]);
853 let name = field_name(idx, &field);
854 arena.alloc(FieldData { name, type_ref, visibility, is_unsafe });
855 idx += 1;
856 }
857 Err(cfg) => {
858 col.store.diagnostics.push(
859 crate::expr_store::ExpressionStoreDiagnostics::InactiveCode {
860 node: InFile::new(fields.file_id, SyntaxNodePtr::new(field.syntax())),
861 cfg,
862 opts: cfg_options.clone(),
863 },
864 );
865 }
866 }
867 }
868 if !has_fields {
869 return None;
870 }
871 let (store, source_map) = col.store.finish();
872 arena.shrink_to_fit();
873 Some((arena, store, source_map))
874}
875
876#[derive(Debug, PartialEq, Eq)]
877pub struct InactiveEnumVariantCode {
878 pub cfg: CfgExpr,
879 pub opts: CfgOptions,
880 pub ast_id: span::FileAstId<ast::Variant>,
881}
882
883#[derive(Debug, Clone, PartialEq, Eq)]
884pub struct EnumVariants {
885 pub variants: Box<[(EnumVariantId, Name, FieldsShape)]>,
886}
887
888#[salsa::tracked]
889impl EnumVariants {
890 #[salsa::tracked(returns(ref))]
891 pub(crate) fn of(
892 db: &dyn DefDatabase,
893 e: EnumId,
894 ) -> (EnumVariants, Option<ThinVec<InactiveEnumVariantCode>>) {
895 let loc = e.lookup(db);
896 let source = loc.source(db);
897 let ast_id_map = db.ast_id_map(source.file_id);
898 let span_map = db.span_map(source.file_id);
899
900 let mut diagnostics = ThinVec::new();
901 let cfg_options = loc.container.krate.cfg_options(db);
902 let mut index = 0;
903 let Some(variants) = source.value.variant_list() else {
904 return (EnumVariants { variants: Box::default() }, None);
905 };
906 let variants = variants
907 .variants()
908 .filter_map(|variant| {
909 let ast_id = ast_id_map.ast_id(&variant);
910 match Attrs::is_cfg_enabled_for(db, &variant, span_map.as_ref(), cfg_options) {
911 Ok(()) => {
912 let enum_variant =
913 EnumVariantLoc { id: source.with_value(ast_id), parent: e, index }
914 .intern(db);
915 index += 1;
916 let name = as_name_opt(variant.name());
917 let shape = adt_shape(variant.kind());
918 Some((enum_variant, name, shape))
919 }
920 Err(cfg) => {
921 diagnostics.push(InactiveEnumVariantCode {
922 ast_id,
923 cfg,
924 opts: cfg_options.clone(),
925 });
926 None
927 }
928 }
929 })
930 .collect();
931
932 (EnumVariants { variants }, diagnostics.is_empty().not().then_some(diagnostics))
933 }
934}
935
936impl EnumVariants {
937 pub fn variant(&self, name: &Name) -> Option<EnumVariantId> {
938 self.variants.iter().find_map(|(v, n, _)| if n == name { Some(*v) } else { None })
939 }
940
941 pub fn variant_name_by_id(&self, variant_id: EnumVariantId) -> Option<Name> {
942 self.variants
943 .iter()
944 .find_map(|(id, name, _)| if *id == variant_id { Some(name.clone()) } else { None })
945 }
946
947 pub fn is_payload_free(&self, db: &dyn DefDatabase) -> bool {
949 self.variants.iter().all(|&(v, _, _)| {
950 let variant = v.fields(db);
953 if !variant.fields().is_empty() {
954 return false;
955 }
956 if !matches!(variant.shape, FieldsShape::Unit) {
958 let body = db.body(v.into());
959 if !matches!(body[body.body_expr], crate::hir::Expr::Missing) {
961 return false;
962 }
963 }
964 true
965 })
966 }
967}
968
969pub(crate) fn extern_block_abi(
970 db: &dyn DefDatabase,
971 extern_block: ExternBlockId,
972) -> Option<Symbol> {
973 let source = extern_block.lookup(db).source(db);
974 source.value.abi().map(|abi| {
975 match abi.abi_string() {
976 Some(tok) => Symbol::intern(tok.text_without_quotes()),
977 _ => sym::C,
979 }
980 })
981}