1use ecow::EcoString;
2
3use crate::types::Type;
4
5#[derive(Debug, Clone, Copy, PartialEq, Eq)]
6pub enum DeadCodeCause {
7 Return,
8 Break,
9 Continue,
10 DivergingIf,
11 DivergingMatch,
12 InfiniteLoop,
13 DivergingCall,
14}
15
16#[derive(Clone, PartialEq)]
17pub struct Binding {
18 pub pattern: Pattern,
19 pub annotation: Option<Annotation>,
20 pub typed_pattern: Option<TypedPattern>,
21 pub ty: Type,
22 pub mutable: bool,
23}
24
25impl std::fmt::Debug for Binding {
26 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
27 let mut s = f.debug_struct("Binding");
28 s.field("pattern", &self.pattern);
29 s.field("annotation", &self.annotation);
30 s.field("typed_pattern", &self.typed_pattern);
31 s.field("ty", &self.ty);
32 if self.mutable {
33 s.field("mutable", &self.mutable);
34 }
35 s.finish()
36 }
37}
38
39pub type BindingId = u32;
40
41#[derive(Debug, Clone, Copy, PartialEq, Eq)]
42pub enum BindingKind {
43 Let { mutable: bool },
44 Parameter { mutable: bool },
45 MatchArm,
46}
47
48impl BindingKind {
49 pub fn is_mutable(&self) -> bool {
50 matches!(
51 self,
52 BindingKind::Let { mutable: true } | BindingKind::Parameter { mutable: true }
53 )
54 }
55
56 pub fn is_param(&self) -> bool {
57 matches!(self, BindingKind::Parameter { .. })
58 }
59
60 pub fn is_match_arm(&self) -> bool {
61 matches!(self, BindingKind::MatchArm)
62 }
63}
64
65#[derive(Clone, PartialEq)]
66pub struct MatchArm {
67 pub pattern: Pattern,
68 pub guard: Option<Box<Expression>>,
69 pub typed_pattern: Option<TypedPattern>,
70 pub expression: Box<Expression>,
71}
72
73impl MatchArm {
74 pub fn has_guard(&self) -> bool {
75 self.guard.is_some()
76 }
77}
78
79impl std::fmt::Debug for MatchArm {
80 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
81 let mut s = f.debug_struct("MatchArm");
82 s.field("pattern", &self.pattern);
83 if self.guard.is_some() {
84 s.field("guard", &self.guard);
85 }
86 s.field("expression", &self.expression);
87 s.finish()
88 }
89}
90
91#[derive(Debug, Clone, Copy, PartialEq, Eq)]
92pub enum MatchOrigin {
93 Explicit,
94 IfLet { else_span: Option<Span> },
95}
96
97#[derive(Debug, Clone, PartialEq)]
98pub struct SelectArm {
99 pub pattern: SelectArmPattern,
100}
101
102#[derive(Debug, Clone, PartialEq)]
103pub enum SelectArmPattern {
104 Receive {
105 binding: Box<Pattern>,
106 typed_pattern: Option<TypedPattern>,
107 receive_expression: Box<Expression>,
108 body: Box<Expression>,
109 },
110 Send {
111 send_expression: Box<Expression>,
112 body: Box<Expression>,
113 },
114 MatchReceive {
115 receive_expression: Box<Expression>,
116 arms: Vec<MatchArm>,
117 },
118 WildCard {
119 body: Box<Expression>,
120 },
121}
122
123#[derive(Debug, Clone, PartialEq)]
124pub enum RestPattern {
125 Absent,
126 Discard(Span),
127 Bind { name: EcoString, span: Span },
128}
129
130impl RestPattern {
131 pub fn is_present(&self) -> bool {
132 !matches!(self, RestPattern::Absent)
133 }
134}
135
136#[derive(Debug, Clone, PartialEq)]
137pub enum Pattern {
138 Literal {
139 literal: Literal,
140 ty: Type,
141 span: Span,
142 },
143 Unit {
144 ty: Type,
145 span: Span,
146 },
147 EnumVariant {
148 identifier: EcoString,
149 fields: Vec<Self>,
150 rest: bool,
151 ty: Type,
152 span: Span,
153 },
154 Struct {
155 identifier: EcoString,
156 fields: Vec<StructFieldPattern>,
157 rest: bool,
158 ty: Type,
159 span: Span,
160 },
161 Tuple {
162 elements: Vec<Self>,
163 span: Span,
164 },
165 WildCard {
166 span: Span,
167 },
168 Identifier {
169 identifier: EcoString,
170 span: Span,
171 },
172 Slice {
173 prefix: Vec<Self>,
174 rest: RestPattern,
175 element_ty: Type,
176 span: Span,
177 },
178 Or {
179 patterns: Vec<Self>,
180 span: Span,
181 },
182}
183
184impl Pattern {
185 pub fn get_span(&self) -> Span {
186 match self {
187 Pattern::Identifier { span, .. } => *span,
188 Pattern::Literal { span, .. } => *span,
189 Pattern::EnumVariant { span, .. } => *span,
190 Pattern::Struct { span, .. } => *span,
191 Pattern::WildCard { span } => *span,
192 Pattern::Unit { span, .. } => *span,
193 Pattern::Tuple { span, .. } => *span,
194 Pattern::Slice { span, .. } => *span,
195 Pattern::Or { span, .. } => *span,
196 }
197 }
198
199 pub fn get_type(&self) -> Option<Type> {
200 match self {
201 Pattern::Identifier { .. } => None,
202 Pattern::Literal { ty, .. } => Some(ty.clone()),
203 Pattern::EnumVariant { ty, .. } => Some(ty.clone()),
204 Pattern::Struct { ty, .. } => Some(ty.clone()),
205 Pattern::WildCard { .. } => None,
206 Pattern::Unit { ty, .. } => Some(ty.clone()),
207 Pattern::Tuple { .. } => None,
208 Pattern::Slice { .. } => None,
209 Pattern::Or { .. } => None,
210 }
211 }
212
213 pub fn is_identifier(&self) -> bool {
214 matches!(self, Pattern::Identifier { .. })
215 }
216
217 pub fn get_identifier(&self) -> Option<EcoString> {
218 match self {
219 Pattern::Identifier { identifier, .. } => Some(identifier.clone()),
220 _ => None,
221 }
222 }
223}
224
225#[derive(Debug, Clone, PartialEq)]
226pub struct StructFieldPattern {
227 pub name: EcoString,
228 pub value: Pattern,
229}
230
231#[derive(Debug, Clone, PartialEq)]
232pub enum TypedPattern {
233 Wildcard,
234 Literal(Literal),
235 EnumVariant {
236 enum_name: EcoString,
237 variant_name: EcoString,
238 variant_fields: Vec<EnumFieldDefinition>,
239 fields: Vec<TypedPattern>,
240 type_args: Vec<Type>,
241 field_types: Box<[Type]>,
242 },
243 EnumStructVariant {
244 enum_name: EcoString,
245 variant_name: EcoString,
246 variant_fields: Vec<EnumFieldDefinition>,
247 pattern_fields: Vec<(EcoString, TypedPattern)>,
248 type_args: Vec<Type>,
249 },
250 Struct {
251 struct_name: EcoString,
252 struct_fields: Vec<StructFieldDefinition>,
253 pattern_fields: Vec<(EcoString, TypedPattern)>,
254 type_args: Vec<Type>,
255 },
256 Slice {
257 prefix: Vec<TypedPattern>,
258 has_rest: bool,
259 element_type: Type,
260 },
261 Tuple {
262 arity: usize,
263 elements: Vec<TypedPattern>,
264 },
265 Or {
266 alternatives: Vec<TypedPattern>,
267 },
268}
269
270#[derive(Debug, Clone, PartialEq)]
271pub struct FunctionDefinition {
272 pub name: EcoString,
273 pub name_span: Span,
274 pub generics: Vec<Generic>,
275 pub params: Vec<Binding>,
276 pub body: Box<Expression>,
277 pub return_type: Type,
278 pub annotation: Annotation,
279 pub ty: Type,
280}
281
282#[derive(Debug, Clone, PartialEq)]
283pub enum VariantFields {
284 Unit,
285 Tuple(Vec<EnumFieldDefinition>),
286 Struct(Vec<EnumFieldDefinition>),
287}
288
289impl VariantFields {
290 pub fn is_empty(&self) -> bool {
291 match self {
292 VariantFields::Unit => true,
293 VariantFields::Tuple(fields) | VariantFields::Struct(fields) => fields.is_empty(),
294 }
295 }
296
297 pub fn len(&self) -> usize {
298 match self {
299 VariantFields::Unit => 0,
300 VariantFields::Tuple(fields) | VariantFields::Struct(fields) => fields.len(),
301 }
302 }
303
304 pub fn iter(&self) -> std::slice::Iter<'_, EnumFieldDefinition> {
305 match self {
306 VariantFields::Unit => [].iter(),
307 VariantFields::Tuple(fields) | VariantFields::Struct(fields) => fields.iter(),
308 }
309 }
310
311 pub fn is_struct(&self) -> bool {
312 matches!(self, VariantFields::Struct(_))
313 }
314}
315
316impl<'a> IntoIterator for &'a VariantFields {
317 type Item = &'a EnumFieldDefinition;
318 type IntoIter = std::slice::Iter<'a, EnumFieldDefinition>;
319
320 fn into_iter(self) -> Self::IntoIter {
321 self.iter()
322 }
323}
324
325#[derive(Debug, Clone, PartialEq)]
326pub struct EnumVariant {
327 pub doc: Option<String>,
328 pub name: EcoString,
329 pub name_span: Span,
330 pub fields: VariantFields,
331}
332
333#[derive(Debug, Clone, PartialEq)]
334pub struct ValueEnumVariant {
335 pub doc: Option<String>,
336 pub name: EcoString,
337 pub name_span: Span,
338 pub value: Literal,
339 pub value_span: Span,
340}
341
342#[derive(Debug, Clone, PartialEq)]
343pub struct EnumFieldDefinition {
344 pub name: EcoString,
345 pub name_span: Span,
346 pub annotation: Annotation,
347 pub ty: Type,
348}
349
350#[derive(Debug, Clone, PartialEq)]
351pub struct Attribute {
352 pub name: String,
353 pub args: Vec<AttributeArg>,
354 pub span: Span,
355}
356
357#[derive(Debug, Clone, PartialEq)]
358#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
359pub enum AttributeArg {
360 Flag(String),
362 NegatedFlag(String),
364 String(String),
366 Raw(String),
368}
369
370#[derive(Debug, Clone, Copy, PartialEq)]
371#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
372pub enum StructKind {
373 Record,
374 Tuple,
375}
376
377#[derive(Debug, Clone, PartialEq)]
378pub struct StructFieldDefinition {
379 pub doc: Option<String>,
380 pub attributes: Vec<Attribute>,
381 pub name: EcoString,
382 pub name_span: Span,
383 pub annotation: Annotation,
384 pub visibility: Visibility,
385 pub ty: Type,
386}
387
388#[derive(Debug, Clone, PartialEq)]
389pub struct StructFieldAssignment {
390 pub name: EcoString,
391 pub name_span: Span,
392 pub value: Box<Expression>,
393}
394
395#[derive(Debug, Clone, PartialEq)]
396#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
397pub enum Annotation {
398 Constructor {
399 name: EcoString,
400 params: Vec<Self>,
401 span: Span,
402 },
403 Function {
404 params: Vec<Self>,
405 return_type: Box<Self>,
406 span: Span,
407 },
408 Tuple {
409 elements: Vec<Self>,
410 span: Span,
411 },
412 Unknown,
413 Opaque {
414 span: Span,
415 },
416}
417
418impl Annotation {
419 pub fn unit() -> Self {
420 Self::Constructor {
421 name: "Unit".into(),
422 params: vec![],
423 span: Span::dummy(),
424 }
425 }
426
427 pub fn get_span(&self) -> Span {
428 match self {
429 Self::Constructor { span, .. } => *span,
430 Self::Function { span, .. } => *span,
431 Self::Tuple { span, .. } => *span,
432 Self::Opaque { span } => *span,
433 Self::Unknown => Span::dummy(),
434 }
435 }
436
437 pub fn get_name(&self) -> Option<String> {
438 match self {
439 Self::Constructor { name, .. } => Some(name.to_string()),
440 _ => None,
441 }
442 }
443
444 pub fn is_unit(&self) -> bool {
445 matches!(self, Self::Constructor { name, params, .. } if name == "Unit" && params.is_empty())
446 }
447
448 pub fn is_unknown(&self) -> bool {
449 matches!(self, Self::Unknown)
450 }
451
452 pub fn is_opaque(&self) -> bool {
453 matches!(self, Self::Opaque { .. })
454 }
455}
456
457#[derive(Debug, Clone, PartialEq)]
458pub struct Generic {
459 pub name: EcoString,
460 pub bounds: Vec<Annotation>,
461 pub span: Span,
462}
463
464#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
465#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
466pub struct Span {
467 pub file_id: u32,
468 pub byte_offset: u32,
469 pub byte_length: u32,
470}
471
472impl Span {
473 pub fn new(file_id: u32, byte_offset: u32, byte_length: u32) -> Self {
474 Span {
475 file_id,
476 byte_offset,
477 byte_length,
478 }
479 }
480
481 pub fn dummy() -> Self {
482 Span {
483 file_id: 0,
484 byte_offset: 0,
485 byte_length: 0,
486 }
487 }
488
489 pub fn is_dummy(&self) -> bool {
490 self.byte_length == 0
491 }
492
493 pub fn end(&self) -> u32 {
494 self.byte_offset + self.byte_length
495 }
496}
497
498#[derive(Debug, Clone, PartialEq)]
499#[allow(clippy::large_enum_variant)]
500pub enum Expression {
501 Literal {
502 literal: Literal,
503 ty: Type,
504 span: Span,
505 },
506 Function {
507 doc: Option<String>,
508 attributes: Vec<Attribute>,
509 name: EcoString,
510 name_span: Span,
511 generics: Vec<Generic>,
512 params: Vec<Binding>,
513 return_annotation: Annotation,
514 return_type: Type,
515 visibility: Visibility,
516 body: Box<Expression>,
517 ty: Type,
518 span: Span,
519 },
520 Lambda {
521 params: Vec<Binding>,
522 return_annotation: Annotation,
523 body: Box<Expression>,
524 ty: Type,
525 span: Span,
526 },
527 Block {
528 items: Vec<Expression>,
529 ty: Type,
530 span: Span,
531 },
532 Let {
533 binding: Box<Binding>,
534 value: Box<Expression>,
535 mutable: bool,
536 mut_span: Option<Span>,
537 else_block: Option<Box<Expression>>,
538 else_span: Option<Span>,
539 typed_pattern: Option<TypedPattern>,
540 ty: Type,
541 span: Span,
542 },
543 Identifier {
544 value: EcoString,
545 ty: Type,
546 span: Span,
547 binding_id: Option<BindingId>,
548 qualified: Option<EcoString>,
549 },
550 Call {
551 expression: Box<Expression>,
552 args: Vec<Expression>,
553 type_args: Vec<Annotation>,
554 ty: Type,
555 span: Span,
556 },
557 If {
558 condition: Box<Expression>,
559 consequence: Box<Expression>,
560 alternative: Box<Expression>,
561 ty: Type,
562 span: Span,
563 },
564 IfLet {
565 pattern: Pattern,
566 scrutinee: Box<Expression>,
567 consequence: Box<Expression>,
568 alternative: Box<Expression>,
569 typed_pattern: Option<TypedPattern>,
570 else_span: Option<Span>,
571 ty: Type,
572 span: Span,
573 },
574 Match {
575 subject: Box<Expression>,
576 arms: Vec<MatchArm>,
577 origin: MatchOrigin,
578 ty: Type,
579 span: Span,
580 },
581 Tuple {
582 elements: Vec<Expression>,
583 ty: Type,
584 span: Span,
585 },
586 StructCall {
587 name: EcoString,
588 field_assignments: Vec<StructFieldAssignment>,
589 spread: Box<Option<Expression>>,
590 ty: Type,
591 span: Span,
592 },
593 DotAccess {
594 expression: Box<Expression>,
595 member: EcoString,
596 ty: Type,
597 span: Span,
598 },
599 Assignment {
600 target: Box<Expression>,
601 value: Box<Expression>,
602 compound_operator: Option<BinaryOperator>,
603 span: Span,
604 },
605 Return {
606 expression: Box<Expression>,
607 ty: Type,
608 span: Span,
609 },
610 Propagate {
611 expression: Box<Expression>,
612 ty: Type,
613 span: Span,
614 },
615 TryBlock {
616 items: Vec<Expression>,
617 ty: Type,
618 try_keyword_span: Span,
619 span: Span,
620 },
621 RecoverBlock {
622 items: Vec<Expression>,
623 ty: Type,
624 recover_keyword_span: Span,
625 span: Span,
626 },
627 ImplBlock {
628 annotation: Annotation,
629 receiver_name: EcoString,
630 methods: Vec<Expression>,
631 generics: Vec<Generic>,
632 ty: Type,
633 span: Span,
634 },
635 Binary {
636 operator: BinaryOperator,
637 left: Box<Expression>,
638 right: Box<Expression>,
639 ty: Type,
640 span: Span,
641 },
642 Unary {
643 operator: UnaryOperator,
644 expression: Box<Expression>,
645 ty: Type,
646 span: Span,
647 },
648 Paren {
649 expression: Box<Expression>,
650 ty: Type,
651 span: Span,
652 },
653 Const {
654 doc: Option<String>,
655 identifier: EcoString,
656 identifier_span: Span,
657 annotation: Option<Annotation>,
658 expression: Box<Expression>,
659 visibility: Visibility,
660 ty: Type,
661 span: Span,
662 },
663 VariableDeclaration {
664 doc: Option<String>,
665 name: EcoString,
666 name_span: Span,
667 annotation: Annotation,
668 visibility: Visibility,
669 ty: Type,
670 span: Span,
671 },
672 RawGo {
673 text: String,
674 },
675 Loop {
676 body: Box<Expression>,
677 ty: Type,
678 span: Span,
679 needs_label: bool,
680 },
681 While {
682 condition: Box<Expression>,
683 body: Box<Expression>,
684 span: Span,
685 needs_label: bool,
686 },
687 WhileLet {
688 pattern: Pattern,
689 scrutinee: Box<Expression>,
690 body: Box<Expression>,
691 typed_pattern: Option<TypedPattern>,
692 span: Span,
693 needs_label: bool,
694 },
695 For {
696 binding: Box<Binding>,
697 iterable: Box<Expression>,
698 body: Box<Expression>,
699 span: Span,
700 needs_label: bool,
701 },
702 Break {
703 value: Option<Box<Expression>>,
704 span: Span,
705 },
706 Continue {
707 span: Span,
708 },
709 Enum {
710 doc: Option<String>,
711 attributes: Vec<Attribute>,
712 name: EcoString,
713 name_span: Span,
714 generics: Vec<Generic>,
715 variants: Vec<EnumVariant>,
716 visibility: Visibility,
717 span: Span,
718 },
719 ValueEnum {
720 doc: Option<String>,
721 name: EcoString,
722 name_span: Span,
723 underlying_ty: Option<Annotation>,
724 variants: Vec<ValueEnumVariant>,
725 visibility: Visibility,
726 span: Span,
727 },
728 Struct {
729 doc: Option<String>,
730 attributes: Vec<Attribute>,
731 name: EcoString,
732 name_span: Span,
733 generics: Vec<Generic>,
734 fields: Vec<StructFieldDefinition>,
735 kind: StructKind,
736 visibility: Visibility,
737 span: Span,
738 },
739 TypeAlias {
740 doc: Option<String>,
741 name: EcoString,
742 name_span: Span,
743 generics: Vec<Generic>,
744 annotation: Annotation,
745 ty: Type,
746 visibility: Visibility,
747 span: Span,
748 },
749 ModuleImport {
750 name: EcoString,
751 name_span: Span,
752 alias: Option<ImportAlias>,
753 span: Span,
754 },
755 Reference {
756 expression: Box<Expression>,
757 ty: Type,
758 span: Span,
759 },
760 Interface {
761 doc: Option<String>,
762 name: EcoString,
763 name_span: Span,
764 generics: Vec<Generic>,
765 parents: Vec<ParentInterface>,
766 method_signatures: Vec<Expression>,
767 visibility: Visibility,
768 span: Span,
769 },
770 IndexedAccess {
771 expression: Box<Expression>,
772 index: Box<Expression>,
773 ty: Type,
774 span: Span,
775 },
776 Task {
777 expression: Box<Expression>,
778 ty: Type,
779 span: Span,
780 },
781 Defer {
782 expression: Box<Expression>,
783 ty: Type,
784 span: Span,
785 },
786 Select {
787 arms: Vec<SelectArm>,
788 ty: Type,
789 span: Span,
790 },
791 Unit {
792 ty: Type,
793 span: Span,
794 },
795 Range {
796 start: Option<Box<Expression>>,
797 end: Option<Box<Expression>>,
798 inclusive: bool,
799 ty: Type,
800 span: Span,
801 },
802 Cast {
803 expression: Box<Expression>,
804 target_type: Annotation,
805 ty: Type,
806 span: Span,
807 },
808 NoOp,
809}
810
811impl Expression {
812 pub fn is_noop(&self) -> bool {
813 matches!(self, Expression::NoOp)
814 }
815
816 pub fn is_range(&self) -> bool {
817 matches!(self, Expression::Range { .. })
818 }
819
820 pub fn is_conditional(&self) -> bool {
821 matches!(
822 self,
823 Expression::If { .. }
824 | Expression::IfLet { .. }
825 | Expression::Match {
826 origin: MatchOrigin::IfLet { .. },
827 ..
828 }
829 )
830 }
831
832 pub fn is_control_flow(&self) -> bool {
833 matches!(
834 self,
835 Expression::If { .. }
836 | Expression::Match { .. }
837 | Expression::Select { .. }
838 | Expression::For { .. }
839 | Expression::While { .. }
840 | Expression::WhileLet { .. }
841 | Expression::Loop { .. }
842 )
843 }
844
845 pub fn callee_name(&self) -> Option<String> {
846 let Expression::Call { expression, .. } = self else {
847 return None;
848 };
849 match expression.as_ref() {
850 Expression::Identifier { value, .. } => Some(value.to_string()),
851 Expression::DotAccess {
852 expression: base,
853 member,
854 ..
855 } => {
856 if let Expression::Identifier { value, .. } = base.as_ref() {
857 Some(format!("{}.{}", value, member))
858 } else {
859 None
860 }
861 }
862 _ => None,
863 }
864 }
865
866 pub fn to_function_signature(&self) -> FunctionDefinition {
867 match self {
868 Expression::Function {
869 name,
870 name_span,
871 generics,
872 params,
873 return_annotation,
874 return_type,
875 ty,
876 ..
877 } => FunctionDefinition {
878 name: name.clone(),
879 name_span: *name_span,
880 generics: generics.clone(),
881 params: params.clone(),
882 body: Box::new(Expression::NoOp),
883 return_type: return_type.clone(),
884 annotation: return_annotation.clone(),
885 ty: ty.clone(),
886 },
887 _ => panic!("to_function_signature called on non-Function expression"),
888 }
889 }
890
891 pub fn to_function_definition(&self) -> FunctionDefinition {
892 match self {
893 Expression::Function {
894 name,
895 name_span,
896 generics,
897 params,
898 return_annotation,
899 return_type,
900 body,
901 ty,
902 ..
903 } => FunctionDefinition {
904 name: name.clone(),
905 name_span: *name_span,
906 generics: generics.clone(),
907 params: params.clone(),
908 body: body.clone(),
909 return_type: return_type.clone(),
910 annotation: return_annotation.clone(),
911 ty: ty.clone(),
912 },
913 _ => panic!("to_function_definition called on non-Function expression"),
914 }
915 }
916
917 pub fn as_option_constructor(&self) -> Option<std::result::Result<(), ()>> {
918 let variant = match self {
919 Expression::Identifier { value, .. } => Some(value.as_str()),
920 _ => None,
921 }?;
922
923 match variant {
924 "Option.Some" | "Some" => Some(Ok(())),
925 "Option.None" | "None" => Some(Err(())),
926 _ => None,
927 }
928 }
929
930 pub fn as_result_constructor(&self) -> Option<std::result::Result<(), ()>> {
931 let variant = match self {
932 Expression::Identifier { value, .. } => Some(value.as_str()),
933 _ => None,
934 }?;
935
936 match variant {
937 "Result.Ok" | "Ok" => Some(Ok(())),
938 "Result.Err" | "Err" => Some(Err(())),
939 _ => None,
940 }
941 }
942
943 pub fn get_type(&self) -> Type {
944 match self {
945 Self::Literal { ty, .. }
946 | Self::Function { ty, .. }
947 | Self::Lambda { ty, .. }
948 | Self::Block { ty, .. }
949 | Self::Let { ty, .. }
950 | Self::Identifier { ty, .. }
951 | Self::Call { ty, .. }
952 | Self::If { ty, .. }
953 | Self::IfLet { ty, .. }
954 | Self::Match { ty, .. }
955 | Self::Tuple { ty, .. }
956 | Self::StructCall { ty, .. }
957 | Self::DotAccess { ty, .. }
958 | Self::Return { ty, .. }
959 | Self::Propagate { ty, .. }
960 | Self::TryBlock { ty, .. }
961 | Self::RecoverBlock { ty, .. }
962 | Self::Binary { ty, .. }
963 | Self::Paren { ty, .. }
964 | Self::Unary { ty, .. }
965 | Self::Const { ty, .. }
966 | Self::VariableDeclaration { ty, .. }
967 | Self::Defer { ty, .. }
968 | Self::Reference { ty, .. }
969 | Self::IndexedAccess { ty, .. }
970 | Self::Task { ty, .. }
971 | Self::Select { ty, .. }
972 | Self::Unit { ty, .. }
973 | Self::Loop { ty, .. }
974 | Self::Range { ty, .. }
975 | Self::Cast { ty, .. } => ty.clone(),
976 Self::Enum { .. }
977 | Self::ValueEnum { .. }
978 | Self::Struct { .. }
979 | Self::Assignment { .. }
980 | Self::ImplBlock { .. }
981 | Self::TypeAlias { .. }
982 | Self::ModuleImport { .. }
983 | Self::Interface { .. }
984 | Self::NoOp
985 | Self::RawGo { .. }
986 | Self::While { .. }
987 | Self::WhileLet { .. }
988 | Self::For { .. } => Type::ignored(),
989 Self::Break { .. } | Self::Continue { .. } => Type::Never,
990 }
991 }
992
993 pub fn get_span(&self) -> Span {
994 match self {
995 Self::Literal { span, .. }
996 | Self::Function { span, .. }
997 | Self::Lambda { span, .. }
998 | Self::Block { span, .. }
999 | Self::Let { span, .. }
1000 | Self::Identifier { span, .. }
1001 | Self::Call { span, .. }
1002 | Self::If { span, .. }
1003 | Self::IfLet { span, .. }
1004 | Self::Match { span, .. }
1005 | Self::Tuple { span, .. }
1006 | Self::Enum { span, .. }
1007 | Self::ValueEnum { span, .. }
1008 | Self::Struct { span, .. }
1009 | Self::StructCall { span, .. }
1010 | Self::DotAccess { span, .. }
1011 | Self::Assignment { span, .. }
1012 | Self::Return { span, .. }
1013 | Self::Propagate { span, .. }
1014 | Self::TryBlock { span, .. }
1015 | Self::RecoverBlock { span, .. }
1016 | Self::ImplBlock { span, .. }
1017 | Self::Binary { span, .. }
1018 | Self::Paren { span, .. }
1019 | Self::Unary { span, .. }
1020 | Self::Const { span, .. }
1021 | Self::VariableDeclaration { span, .. }
1022 | Self::Defer { span, .. }
1023 | Self::Reference { span, .. }
1024 | Self::IndexedAccess { span, .. }
1025 | Self::Task { span, .. }
1026 | Self::Select { span, .. }
1027 | Self::Loop { span, .. }
1028 | Self::TypeAlias { span, .. }
1029 | Self::ModuleImport { span, .. }
1030 | Self::Interface { span, .. }
1031 | Self::Unit { span, .. }
1032 | Self::While { span, .. }
1033 | Self::WhileLet { span, .. }
1034 | Self::For { span, .. }
1035 | Self::Break { span, .. }
1036 | Self::Continue { span, .. }
1037 | Self::Range { span, .. }
1038 | Self::Cast { span, .. } => *span,
1039 Self::NoOp | Self::RawGo { .. } => Span::dummy(),
1040 }
1041 }
1042
1043 pub fn contains_break(&self) -> bool {
1044 match self {
1045 Expression::Break { .. } => true,
1046
1047 Expression::Loop { .. }
1048 | Expression::While { .. }
1049 | Expression::WhileLet { .. }
1050 | Expression::For { .. } => false,
1051
1052 Expression::Block { items, .. } => items.iter().any(Self::contains_break),
1053
1054 Expression::TryBlock { items, .. } => items.iter().any(Self::contains_break),
1055 Expression::RecoverBlock { items, .. } => items.iter().any(Self::contains_break),
1056
1057 Expression::If {
1058 condition,
1059 consequence,
1060 alternative,
1061 ..
1062 } => {
1063 condition.contains_break()
1064 || consequence.contains_break()
1065 || alternative.contains_break()
1066 }
1067
1068 Expression::IfLet {
1069 scrutinee,
1070 consequence,
1071 alternative,
1072 ..
1073 } => {
1074 scrutinee.contains_break()
1075 || consequence.contains_break()
1076 || alternative.contains_break()
1077 }
1078
1079 Expression::Match { subject, arms, .. } => {
1080 subject.contains_break() || arms.iter().any(|arm| arm.expression.contains_break())
1081 }
1082
1083 Expression::Paren { expression, .. } => expression.contains_break(),
1084
1085 Expression::Binary { left, right, .. } => {
1086 left.contains_break() || right.contains_break()
1087 }
1088
1089 Expression::Unary { expression, .. } => expression.contains_break(),
1090
1091 Expression::Call {
1092 expression, args, ..
1093 } => expression.contains_break() || args.iter().any(Self::contains_break),
1094
1095 Expression::Function { .. } | Expression::Lambda { .. } => false,
1096
1097 Expression::Select { arms, .. } => arms.iter().any(|arm| match &arm.pattern {
1098 SelectArmPattern::Receive { body, .. } => body.contains_break(),
1099 SelectArmPattern::Send { body, .. } => body.contains_break(),
1100 SelectArmPattern::MatchReceive { arms, .. } => {
1101 arms.iter().any(|a| a.expression.contains_break())
1102 }
1103 SelectArmPattern::WildCard { body } => body.contains_break(),
1104 }),
1105
1106 Expression::Cast { expression, .. } => expression.contains_break(),
1107
1108 Expression::Let {
1109 value, else_block, ..
1110 } => value.contains_break() || else_block.as_ref().is_some_and(|e| e.contains_break()),
1111
1112 Expression::Assignment { value, .. } => value.contains_break(),
1113
1114 _ => false,
1115 }
1116 }
1117
1118 pub fn diverges(&self) -> Option<DeadCodeCause> {
1119 match self {
1120 Expression::Return { .. } => Some(DeadCodeCause::Return),
1121 Expression::Break { .. } => Some(DeadCodeCause::Break),
1122 Expression::Continue { .. } => Some(DeadCodeCause::Continue),
1123
1124 Expression::If {
1125 consequence,
1126 alternative,
1127 ..
1128 } => {
1129 if consequence.diverges().is_some() && alternative.diverges().is_some() {
1130 Some(DeadCodeCause::DivergingIf)
1131 } else {
1132 None
1133 }
1134 }
1135
1136 Expression::IfLet {
1137 consequence,
1138 alternative,
1139 ..
1140 } => {
1141 if consequence.diverges().is_some() && alternative.diverges().is_some() {
1142 Some(DeadCodeCause::DivergingIf)
1143 } else {
1144 None
1145 }
1146 }
1147
1148 Expression::Match { arms, .. } => {
1149 if !arms.is_empty() && arms.iter().all(|arm| arm.expression.diverges().is_some()) {
1150 Some(DeadCodeCause::DivergingMatch)
1151 } else {
1152 None
1153 }
1154 }
1155
1156 Expression::Block { items, .. } => {
1157 for item in items {
1158 if let Some(cause) = item.diverges() {
1159 return Some(cause);
1160 }
1161 }
1162 None
1163 }
1164
1165 Expression::TryBlock { items, .. } | Expression::RecoverBlock { items, .. } => {
1166 for item in items {
1167 if let Some(cause) = item.diverges() {
1168 return Some(cause);
1169 }
1170 }
1171 None
1172 }
1173
1174 Expression::Paren { expression, .. } | Expression::Cast { expression, .. } => {
1175 expression.diverges()
1176 }
1177
1178 Expression::Loop { body, .. } => {
1179 if !body.contains_break() {
1180 Some(DeadCodeCause::InfiniteLoop)
1181 } else {
1182 None
1183 }
1184 }
1185
1186 Expression::Call { ty, .. } if ty.is_never() => Some(DeadCodeCause::DivergingCall),
1187
1188 _ => None,
1189 }
1190 }
1191
1192 pub fn children(&self) -> Vec<&Expression> {
1197 match self {
1198 Expression::Literal { literal, .. } => match literal {
1199 Literal::Slice(elements) => elements.iter().collect(),
1200 Literal::FormatString(parts) => parts
1201 .iter()
1202 .filter_map(|p| match p {
1203 FormatStringPart::Expression(e) => Some(e.as_ref()),
1204 FormatStringPart::Text(_) => None,
1205 })
1206 .collect(),
1207 _ => vec![],
1208 },
1209 Expression::Function { body, .. } => vec![body],
1210 Expression::Lambda { body, .. } => vec![body],
1211 Expression::Block { items, .. } => items.iter().collect(),
1212 Expression::Let {
1213 value, else_block, ..
1214 } => {
1215 let mut c = vec![value.as_ref()];
1216 if let Some(eb) = else_block {
1217 c.push(eb);
1218 }
1219 c
1220 }
1221 Expression::Identifier { .. } => vec![],
1222 Expression::Call {
1223 expression, args, ..
1224 } => {
1225 let mut c = vec![expression.as_ref()];
1226 c.extend(args);
1227 c
1228 }
1229 Expression::If {
1230 condition,
1231 consequence,
1232 alternative,
1233 ..
1234 } => vec![condition, consequence, alternative],
1235 Expression::IfLet {
1236 scrutinee,
1237 consequence,
1238 alternative,
1239 ..
1240 } => vec![scrutinee, consequence, alternative],
1241 Expression::Match { subject, arms, .. } => {
1242 let mut c = vec![subject.as_ref()];
1243 for arm in arms {
1244 if let Some(guard) = &arm.guard {
1245 c.push(guard);
1246 }
1247 c.push(&arm.expression);
1248 }
1249 c
1250 }
1251 Expression::Tuple { elements, .. } => elements.iter().collect(),
1252 Expression::StructCall {
1253 field_assignments,
1254 spread,
1255 ..
1256 } => {
1257 let mut c: Vec<&Expression> =
1258 field_assignments.iter().map(|f| f.value.as_ref()).collect();
1259 if let Some(s) = spread.as_ref() {
1260 c.push(s);
1261 }
1262 c
1263 }
1264 Expression::DotAccess { expression, .. } => vec![expression],
1265 Expression::Assignment { target, value, .. } => vec![target, value],
1266 Expression::Return { expression, .. } => vec![expression],
1267 Expression::Propagate { expression, .. } => vec![expression],
1268 Expression::TryBlock { items, .. } | Expression::RecoverBlock { items, .. } => {
1269 items.iter().collect()
1270 }
1271 Expression::ImplBlock { methods, .. } => methods.iter().collect(),
1272 Expression::Binary { left, right, .. } => vec![left, right],
1273 Expression::Unary { expression, .. } => vec![expression],
1274 Expression::Paren { expression, .. } => vec![expression],
1275 Expression::Const { expression, .. } => vec![expression],
1276 Expression::Loop { body, .. } => vec![body],
1277 Expression::While {
1278 condition, body, ..
1279 } => vec![condition, body],
1280 Expression::WhileLet {
1281 scrutinee, body, ..
1282 } => vec![scrutinee, body],
1283 Expression::For { iterable, body, .. } => vec![iterable, body],
1284 Expression::Break { value, .. } => {
1285 value.as_ref().map(|v| vec![v.as_ref()]).unwrap_or_default()
1286 }
1287 Expression::Reference { expression, .. } => vec![expression],
1288 Expression::IndexedAccess {
1289 expression, index, ..
1290 } => vec![expression, index],
1291 Expression::Task { expression, .. } => vec![expression],
1292 Expression::Defer { expression, .. } => vec![expression],
1293 Expression::Select { arms, .. } => {
1294 let mut c = vec![];
1295 for arm in arms {
1296 match &arm.pattern {
1297 SelectArmPattern::Receive {
1298 receive_expression,
1299 body,
1300 ..
1301 } => {
1302 c.push(receive_expression.as_ref());
1303 c.push(body.as_ref());
1304 }
1305 SelectArmPattern::Send {
1306 send_expression,
1307 body,
1308 } => {
1309 c.push(send_expression.as_ref());
1310 c.push(body.as_ref());
1311 }
1312 SelectArmPattern::MatchReceive {
1313 receive_expression,
1314 arms: match_arms,
1315 } => {
1316 c.push(receive_expression.as_ref());
1317 for ma in match_arms {
1318 if let Some(guard) = &ma.guard {
1319 c.push(guard);
1320 }
1321 c.push(&ma.expression);
1322 }
1323 }
1324 SelectArmPattern::WildCard { body } => {
1325 c.push(body.as_ref());
1326 }
1327 }
1328 }
1329 c
1330 }
1331 Expression::Range { start, end, .. } => {
1332 let mut c = vec![];
1333 if let Some(s) = start {
1334 c.push(s.as_ref());
1335 }
1336 if let Some(e) = end {
1337 c.push(e.as_ref());
1338 }
1339 c
1340 }
1341 Expression::Cast { expression, .. } => vec![expression],
1342 Expression::Interface {
1343 method_signatures, ..
1344 } => method_signatures.iter().collect(),
1345 Expression::Unit { .. }
1346 | Expression::Continue { .. }
1347 | Expression::Enum { .. }
1348 | Expression::ValueEnum { .. }
1349 | Expression::Struct { .. }
1350 | Expression::TypeAlias { .. }
1351 | Expression::VariableDeclaration { .. }
1352 | Expression::ModuleImport { .. }
1353 | Expression::RawGo { .. }
1354 | Expression::NoOp => vec![],
1355 }
1356 }
1357
1358 pub fn unwrap_parens(&self) -> &Expression {
1359 match self {
1360 Expression::Paren { expression, .. } => expression.unwrap_parens(),
1361 other => other,
1362 }
1363 }
1364
1365 pub fn as_dotted_path(&self) -> Option<String> {
1366 match self {
1367 Expression::Identifier { value, .. } => Some(value.to_string()),
1368 Expression::DotAccess {
1369 expression, member, ..
1370 } => Some(format!("{}.{}", expression.as_dotted_path()?, member)),
1371 _ => None,
1372 }
1373 }
1374
1375 pub fn root_identifier(&self) -> Option<&str> {
1376 match self {
1377 Expression::Identifier { value, .. } => Some(value),
1378 Expression::DotAccess { expression, .. } => expression.root_identifier(),
1379 _ => None,
1380 }
1381 }
1382
1383 pub fn is_empty_collection(&self) -> bool {
1384 matches!(
1385 self,
1386 Expression::Literal {
1387 literal: Literal::Slice(elements),
1388 ..
1389 } if elements.is_empty()
1390 )
1391 }
1392
1393 pub fn is_all_literals(&self) -> bool {
1394 match self.unwrap_parens() {
1395 Expression::Literal { literal, .. } => match literal {
1396 Literal::Slice(elements) => elements.iter().all(|e| e.is_all_literals()),
1397 Literal::FormatString(parts) => parts.iter().all(|p| match p {
1398 FormatStringPart::Text(_) => true,
1399 FormatStringPart::Expression(e) => e.is_all_literals(),
1400 }),
1401 _ => true,
1402 },
1403 Expression::Tuple { elements, .. } => elements.iter().all(|e| e.is_all_literals()),
1404 Expression::Unit { .. } => true,
1405 _ => false,
1406 }
1407 }
1408
1409 pub fn get_var_name(&self) -> Option<String> {
1410 match self {
1411 Expression::Identifier { value, .. } => Some(value.to_string()),
1412 Expression::DotAccess { expression, .. } => expression.get_var_name(),
1413 Expression::Assignment { target, .. } => target.get_var_name(),
1414 Expression::IndexedAccess { expression, .. } => expression.get_var_name(),
1415 Expression::Paren { expression, .. } => expression.get_var_name(),
1416 Expression::Reference { expression, .. } => expression.get_var_name(),
1417 Expression::Unary {
1418 operator,
1419 expression,
1420 ..
1421 } => {
1422 if operator == &UnaryOperator::Deref {
1423 expression.get_var_name()
1424 } else {
1425 None
1426 }
1427 }
1428 _ => None,
1429 }
1430 }
1431
1432 pub fn is_function(&self) -> bool {
1433 matches!(self, Expression::Function { .. })
1434 }
1435
1436 pub fn set_public(self) -> Self {
1437 match self {
1438 Expression::Enum {
1439 doc,
1440 attributes,
1441 name,
1442 name_span,
1443 generics,
1444 variants,
1445 span,
1446 ..
1447 } => Expression::Enum {
1448 doc,
1449 attributes,
1450 name,
1451 name_span,
1452 generics,
1453 variants,
1454 visibility: Visibility::Public,
1455 span,
1456 },
1457 Expression::ValueEnum {
1458 doc,
1459 name,
1460 name_span,
1461 underlying_ty,
1462 variants,
1463 span,
1464 ..
1465 } => Expression::ValueEnum {
1466 doc,
1467 name,
1468 name_span,
1469 underlying_ty,
1470 variants,
1471 visibility: Visibility::Public,
1472 span,
1473 },
1474 Expression::Struct {
1475 doc,
1476 attributes,
1477 name,
1478 name_span,
1479 generics,
1480 fields,
1481 kind,
1482 span,
1483 ..
1484 } => {
1485 let fields = if kind == StructKind::Tuple {
1486 fields
1487 .into_iter()
1488 .map(|f| StructFieldDefinition {
1489 visibility: Visibility::Public,
1490 ..f
1491 })
1492 .collect()
1493 } else {
1494 fields
1495 };
1496 Expression::Struct {
1497 doc,
1498 attributes,
1499 name,
1500 name_span,
1501 generics,
1502 fields,
1503 kind,
1504 visibility: Visibility::Public,
1505 span,
1506 }
1507 }
1508 Expression::Function {
1509 doc,
1510 attributes,
1511 name,
1512 name_span,
1513 generics,
1514 params,
1515 return_annotation,
1516 return_type,
1517 body,
1518 ty,
1519 span,
1520 ..
1521 } => Expression::Function {
1522 doc,
1523 attributes,
1524 name,
1525 name_span,
1526 generics,
1527 params,
1528 return_annotation,
1529 return_type,
1530 visibility: Visibility::Public,
1531 body,
1532 ty,
1533 span,
1534 },
1535 Expression::Const {
1536 doc,
1537 identifier,
1538 identifier_span,
1539 annotation,
1540 expression,
1541 ty,
1542 span,
1543 ..
1544 } => Expression::Const {
1545 doc,
1546 identifier,
1547 identifier_span,
1548 annotation,
1549 expression,
1550 visibility: Visibility::Public,
1551 ty,
1552 span,
1553 },
1554 Expression::VariableDeclaration {
1555 doc,
1556 name,
1557 name_span,
1558 annotation,
1559 ty,
1560 span,
1561 ..
1562 } => Expression::VariableDeclaration {
1563 doc,
1564 name,
1565 name_span,
1566 annotation,
1567 visibility: Visibility::Public,
1568 ty,
1569 span,
1570 },
1571 Expression::TypeAlias {
1572 doc,
1573 name,
1574 name_span,
1575 generics,
1576 annotation,
1577 ty,
1578 span,
1579 ..
1580 } => Expression::TypeAlias {
1581 doc,
1582 name,
1583 name_span,
1584 generics,
1585 annotation,
1586 ty,
1587 visibility: Visibility::Public,
1588 span,
1589 },
1590 Expression::Interface {
1591 doc,
1592 name,
1593 name_span,
1594 generics,
1595 parents,
1596 method_signatures,
1597 span,
1598 ..
1599 } => Expression::Interface {
1600 doc,
1601 name,
1602 name_span,
1603 generics,
1604 parents,
1605 method_signatures,
1606 visibility: Visibility::Public,
1607 span,
1608 },
1609 expression => expression,
1610 }
1611 }
1612
1613 pub fn has_else(&self) -> bool {
1614 match self {
1615 Self::Block { items, .. } if items.is_empty() => false,
1616 Self::Unit { .. } => false,
1617 Self::If { alternative, .. } | Self::IfLet { alternative, .. } => {
1618 alternative.has_else()
1619 }
1620 _ => true,
1621 }
1622 }
1623}
1624
1625#[derive(Debug, Clone, PartialEq)]
1626pub enum Literal {
1627 Integer {
1628 value: u64,
1629 text: Option<String>,
1630 },
1631 Float {
1632 value: f64,
1633 text: Option<String>,
1634 },
1635 Imaginary(f64),
1637 Boolean(bool),
1638 String(String),
1639 FormatString(Vec<FormatStringPart>),
1640 Char(String),
1641 Slice(Vec<Expression>),
1642}
1643
1644#[derive(Debug, Clone, PartialEq)]
1645pub enum FormatStringPart {
1646 Text(String),
1647 Expression(Box<Expression>),
1648}
1649
1650#[derive(Debug, Clone, PartialEq)]
1651pub enum UnaryOperator {
1652 Negative,
1653 Not,
1654 Deref,
1655}
1656
1657#[derive(Debug, Clone, Copy, PartialEq)]
1658pub enum BinaryOperator {
1659 Addition,
1660 Subtraction,
1661 Multiplication,
1662 Division,
1663 LessThan,
1664 LessThanOrEqual,
1665 GreaterThan,
1666 GreaterThanOrEqual,
1667 Remainder,
1668 Equal,
1669 NotEqual,
1670 And,
1671 Or,
1672 Pipeline,
1673}
1674
1675impl std::fmt::Display for BinaryOperator {
1676 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1677 let symbol = match self {
1678 BinaryOperator::Addition => "+",
1679 BinaryOperator::Subtraction => "-",
1680 BinaryOperator::Multiplication => "*",
1681 BinaryOperator::Division => "/",
1682 BinaryOperator::Remainder => "%",
1683 BinaryOperator::Equal => "==",
1684 BinaryOperator::NotEqual => "!=",
1685 BinaryOperator::LessThan => "<",
1686 BinaryOperator::LessThanOrEqual => "<=",
1687 BinaryOperator::GreaterThan => ">",
1688 BinaryOperator::GreaterThanOrEqual => ">=",
1689 BinaryOperator::And => "&&",
1690 BinaryOperator::Or => "||",
1691 BinaryOperator::Pipeline => "|>",
1692 };
1693 write!(f, "{}", symbol)
1694 }
1695}
1696
1697#[derive(Debug, Clone, PartialEq)]
1698pub struct ParentInterface {
1699 pub annotation: Annotation,
1700 pub ty: Type,
1701 pub span: Span,
1702}
1703
1704#[derive(Debug, Clone, Copy, PartialEq)]
1705#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
1706pub enum Visibility {
1707 Public,
1708 Private,
1709}
1710
1711impl Visibility {
1712 pub fn is_public(&self) -> bool {
1713 matches!(self, Visibility::Public)
1714 }
1715}
1716
1717#[derive(Debug, Clone, PartialEq)]
1718pub enum ImportAlias {
1719 Named(EcoString, Span),
1720 Blank(Span),
1721}