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