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 spread: Box<Option<Expression>>,
554 type_args: Vec<Annotation>,
555 ty: Type,
556 span: Span,
557 },
558 If {
559 condition: Box<Expression>,
560 consequence: Box<Expression>,
561 alternative: Box<Expression>,
562 ty: Type,
563 span: Span,
564 },
565 IfLet {
566 pattern: Pattern,
567 scrutinee: Box<Expression>,
568 consequence: Box<Expression>,
569 alternative: Box<Expression>,
570 typed_pattern: Option<TypedPattern>,
571 else_span: Option<Span>,
572 ty: Type,
573 span: Span,
574 },
575 Match {
576 subject: Box<Expression>,
577 arms: Vec<MatchArm>,
578 origin: MatchOrigin,
579 ty: Type,
580 span: Span,
581 },
582 Tuple {
583 elements: Vec<Expression>,
584 ty: Type,
585 span: Span,
586 },
587 StructCall {
588 name: EcoString,
589 field_assignments: Vec<StructFieldAssignment>,
590 spread: Box<Option<Expression>>,
591 ty: Type,
592 span: Span,
593 },
594 DotAccess {
595 expression: Box<Expression>,
596 member: EcoString,
597 ty: Type,
598 span: Span,
599 },
600 Assignment {
601 target: Box<Expression>,
602 value: Box<Expression>,
603 compound_operator: Option<BinaryOperator>,
604 span: Span,
605 },
606 Return {
607 expression: Box<Expression>,
608 ty: Type,
609 span: Span,
610 },
611 Propagate {
612 expression: Box<Expression>,
613 ty: Type,
614 span: Span,
615 },
616 TryBlock {
617 items: Vec<Expression>,
618 ty: Type,
619 try_keyword_span: Span,
620 span: Span,
621 },
622 RecoverBlock {
623 items: Vec<Expression>,
624 ty: Type,
625 recover_keyword_span: Span,
626 span: Span,
627 },
628 ImplBlock {
629 annotation: Annotation,
630 receiver_name: EcoString,
631 methods: Vec<Expression>,
632 generics: Vec<Generic>,
633 ty: Type,
634 span: Span,
635 },
636 Binary {
637 operator: BinaryOperator,
638 left: Box<Expression>,
639 right: Box<Expression>,
640 ty: Type,
641 span: Span,
642 },
643 Unary {
644 operator: UnaryOperator,
645 expression: Box<Expression>,
646 ty: Type,
647 span: Span,
648 },
649 Paren {
650 expression: Box<Expression>,
651 ty: Type,
652 span: Span,
653 },
654 Const {
655 doc: Option<String>,
656 identifier: EcoString,
657 identifier_span: Span,
658 annotation: Option<Annotation>,
659 expression: Box<Expression>,
660 visibility: Visibility,
661 ty: Type,
662 span: Span,
663 },
664 VariableDeclaration {
665 doc: Option<String>,
666 name: EcoString,
667 name_span: Span,
668 annotation: Annotation,
669 visibility: Visibility,
670 ty: Type,
671 span: Span,
672 },
673 RawGo {
674 text: String,
675 },
676 Loop {
677 body: Box<Expression>,
678 ty: Type,
679 span: Span,
680 needs_label: bool,
681 },
682 While {
683 condition: Box<Expression>,
684 body: Box<Expression>,
685 span: Span,
686 needs_label: bool,
687 },
688 WhileLet {
689 pattern: Pattern,
690 scrutinee: Box<Expression>,
691 body: Box<Expression>,
692 typed_pattern: Option<TypedPattern>,
693 span: Span,
694 needs_label: bool,
695 },
696 For {
697 binding: Box<Binding>,
698 iterable: Box<Expression>,
699 body: Box<Expression>,
700 span: Span,
701 needs_label: bool,
702 },
703 Break {
704 value: Option<Box<Expression>>,
705 span: Span,
706 },
707 Continue {
708 span: Span,
709 },
710 Enum {
711 doc: Option<String>,
712 attributes: Vec<Attribute>,
713 name: EcoString,
714 name_span: Span,
715 generics: Vec<Generic>,
716 variants: Vec<EnumVariant>,
717 visibility: Visibility,
718 span: Span,
719 },
720 ValueEnum {
721 doc: Option<String>,
722 name: EcoString,
723 name_span: Span,
724 underlying_ty: Option<Annotation>,
725 variants: Vec<ValueEnumVariant>,
726 visibility: Visibility,
727 span: Span,
728 },
729 Struct {
730 doc: Option<String>,
731 attributes: Vec<Attribute>,
732 name: EcoString,
733 name_span: Span,
734 generics: Vec<Generic>,
735 fields: Vec<StructFieldDefinition>,
736 kind: StructKind,
737 visibility: Visibility,
738 span: Span,
739 },
740 TypeAlias {
741 doc: Option<String>,
742 name: EcoString,
743 name_span: Span,
744 generics: Vec<Generic>,
745 annotation: Annotation,
746 ty: Type,
747 visibility: Visibility,
748 span: Span,
749 },
750 ModuleImport {
751 name: EcoString,
752 name_span: Span,
753 alias: Option<ImportAlias>,
754 span: Span,
755 },
756 Reference {
757 expression: Box<Expression>,
758 ty: Type,
759 span: Span,
760 },
761 Interface {
762 doc: Option<String>,
763 name: EcoString,
764 name_span: Span,
765 generics: Vec<Generic>,
766 parents: Vec<ParentInterface>,
767 method_signatures: Vec<Expression>,
768 visibility: Visibility,
769 span: Span,
770 },
771 IndexedAccess {
772 expression: Box<Expression>,
773 index: Box<Expression>,
774 ty: Type,
775 span: Span,
776 },
777 Task {
778 expression: Box<Expression>,
779 ty: Type,
780 span: Span,
781 },
782 Defer {
783 expression: Box<Expression>,
784 ty: Type,
785 span: Span,
786 },
787 Select {
788 arms: Vec<SelectArm>,
789 ty: Type,
790 span: Span,
791 },
792 Unit {
793 ty: Type,
794 span: Span,
795 },
796 Range {
797 start: Option<Box<Expression>>,
798 end: Option<Box<Expression>>,
799 inclusive: bool,
800 ty: Type,
801 span: Span,
802 },
803 Cast {
804 expression: Box<Expression>,
805 target_type: Annotation,
806 ty: Type,
807 span: Span,
808 },
809 NoOp,
810}
811
812impl Expression {
813 pub fn is_noop(&self) -> bool {
814 matches!(self, Expression::NoOp)
815 }
816
817 pub fn is_range(&self) -> bool {
818 matches!(self, Expression::Range { .. })
819 }
820
821 pub fn is_conditional(&self) -> bool {
822 matches!(
823 self,
824 Expression::If { .. }
825 | Expression::IfLet { .. }
826 | Expression::Match {
827 origin: MatchOrigin::IfLet { .. },
828 ..
829 }
830 )
831 }
832
833 pub fn is_control_flow(&self) -> bool {
834 matches!(
835 self,
836 Expression::If { .. }
837 | Expression::Match { .. }
838 | Expression::Select { .. }
839 | Expression::For { .. }
840 | Expression::While { .. }
841 | Expression::WhileLet { .. }
842 | Expression::Loop { .. }
843 )
844 }
845
846 pub fn callee_name(&self) -> Option<String> {
847 let Expression::Call { expression, .. } = self else {
848 return None;
849 };
850 match expression.as_ref() {
851 Expression::Identifier { value, .. } => Some(value.to_string()),
852 Expression::DotAccess {
853 expression: base,
854 member,
855 ..
856 } => {
857 if let Expression::Identifier { value, .. } = base.as_ref() {
858 Some(format!("{}.{}", value, member))
859 } else {
860 None
861 }
862 }
863 _ => None,
864 }
865 }
866
867 pub fn to_function_signature(&self) -> FunctionDefinition {
868 match self {
869 Expression::Function {
870 name,
871 name_span,
872 generics,
873 params,
874 return_annotation,
875 return_type,
876 ty,
877 ..
878 } => FunctionDefinition {
879 name: name.clone(),
880 name_span: *name_span,
881 generics: generics.clone(),
882 params: params.clone(),
883 body: Box::new(Expression::NoOp),
884 return_type: return_type.clone(),
885 annotation: return_annotation.clone(),
886 ty: ty.clone(),
887 },
888 _ => panic!("to_function_signature called on non-Function expression"),
889 }
890 }
891
892 pub fn to_function_definition(&self) -> FunctionDefinition {
893 match self {
894 Expression::Function {
895 name,
896 name_span,
897 generics,
898 params,
899 return_annotation,
900 return_type,
901 body,
902 ty,
903 ..
904 } => FunctionDefinition {
905 name: name.clone(),
906 name_span: *name_span,
907 generics: generics.clone(),
908 params: params.clone(),
909 body: body.clone(),
910 return_type: return_type.clone(),
911 annotation: return_annotation.clone(),
912 ty: ty.clone(),
913 },
914 _ => panic!("to_function_definition called on non-Function expression"),
915 }
916 }
917
918 pub fn as_option_constructor(&self) -> Option<std::result::Result<(), ()>> {
919 let variant = match self {
920 Expression::Identifier { value, .. } => Some(value.as_str()),
921 _ => None,
922 }?;
923
924 match variant {
925 "Option.Some" | "Some" => Some(Ok(())),
926 "Option.None" | "None" => Some(Err(())),
927 _ => None,
928 }
929 }
930
931 pub fn as_result_constructor(&self) -> Option<std::result::Result<(), ()>> {
932 let variant = match self {
933 Expression::Identifier { value, .. } => Some(value.as_str()),
934 _ => None,
935 }?;
936
937 match variant {
938 "Result.Ok" | "Ok" => Some(Ok(())),
939 "Result.Err" | "Err" => Some(Err(())),
940 _ => None,
941 }
942 }
943
944 pub fn get_type(&self) -> Type {
945 match self {
946 Self::Literal { ty, .. }
947 | Self::Function { ty, .. }
948 | Self::Lambda { ty, .. }
949 | Self::Block { ty, .. }
950 | Self::Let { ty, .. }
951 | Self::Identifier { ty, .. }
952 | Self::Call { ty, .. }
953 | Self::If { ty, .. }
954 | Self::IfLet { ty, .. }
955 | Self::Match { ty, .. }
956 | Self::Tuple { ty, .. }
957 | Self::StructCall { ty, .. }
958 | Self::DotAccess { ty, .. }
959 | Self::Return { ty, .. }
960 | Self::Propagate { ty, .. }
961 | Self::TryBlock { ty, .. }
962 | Self::RecoverBlock { ty, .. }
963 | Self::Binary { ty, .. }
964 | Self::Paren { ty, .. }
965 | Self::Unary { ty, .. }
966 | Self::Const { ty, .. }
967 | Self::VariableDeclaration { ty, .. }
968 | Self::Defer { ty, .. }
969 | Self::Reference { ty, .. }
970 | Self::IndexedAccess { ty, .. }
971 | Self::Task { ty, .. }
972 | Self::Select { ty, .. }
973 | Self::Unit { ty, .. }
974 | Self::Loop { ty, .. }
975 | Self::Range { ty, .. }
976 | Self::Cast { ty, .. } => ty.clone(),
977 Self::Enum { .. }
978 | Self::ValueEnum { .. }
979 | Self::Struct { .. }
980 | Self::Assignment { .. }
981 | Self::ImplBlock { .. }
982 | Self::TypeAlias { .. }
983 | Self::ModuleImport { .. }
984 | Self::Interface { .. }
985 | Self::NoOp
986 | Self::RawGo { .. }
987 | Self::While { .. }
988 | Self::WhileLet { .. }
989 | Self::For { .. } => Type::ignored(),
990 Self::Break { .. } | Self::Continue { .. } => Type::Never,
991 }
992 }
993
994 pub fn get_span(&self) -> Span {
995 match self {
996 Self::Literal { span, .. }
997 | Self::Function { span, .. }
998 | Self::Lambda { span, .. }
999 | Self::Block { span, .. }
1000 | Self::Let { span, .. }
1001 | Self::Identifier { span, .. }
1002 | Self::Call { span, .. }
1003 | Self::If { span, .. }
1004 | Self::IfLet { span, .. }
1005 | Self::Match { span, .. }
1006 | Self::Tuple { span, .. }
1007 | Self::Enum { span, .. }
1008 | Self::ValueEnum { span, .. }
1009 | Self::Struct { span, .. }
1010 | Self::StructCall { span, .. }
1011 | Self::DotAccess { span, .. }
1012 | Self::Assignment { span, .. }
1013 | Self::Return { span, .. }
1014 | Self::Propagate { span, .. }
1015 | Self::TryBlock { span, .. }
1016 | Self::RecoverBlock { span, .. }
1017 | Self::ImplBlock { span, .. }
1018 | Self::Binary { span, .. }
1019 | Self::Paren { span, .. }
1020 | Self::Unary { span, .. }
1021 | Self::Const { span, .. }
1022 | Self::VariableDeclaration { span, .. }
1023 | Self::Defer { span, .. }
1024 | Self::Reference { span, .. }
1025 | Self::IndexedAccess { span, .. }
1026 | Self::Task { span, .. }
1027 | Self::Select { span, .. }
1028 | Self::Loop { span, .. }
1029 | Self::TypeAlias { span, .. }
1030 | Self::ModuleImport { span, .. }
1031 | Self::Interface { span, .. }
1032 | Self::Unit { span, .. }
1033 | Self::While { span, .. }
1034 | Self::WhileLet { span, .. }
1035 | Self::For { span, .. }
1036 | Self::Break { span, .. }
1037 | Self::Continue { span, .. }
1038 | Self::Range { span, .. }
1039 | Self::Cast { span, .. } => *span,
1040 Self::NoOp | Self::RawGo { .. } => Span::dummy(),
1041 }
1042 }
1043
1044 pub fn contains_break(&self) -> bool {
1045 match self {
1046 Expression::Break { .. } => true,
1047
1048 Expression::Loop { .. }
1049 | Expression::While { .. }
1050 | Expression::WhileLet { .. }
1051 | Expression::For { .. } => false,
1052
1053 Expression::Block { items, .. } => items.iter().any(Self::contains_break),
1054
1055 Expression::TryBlock { items, .. } => items.iter().any(Self::contains_break),
1056 Expression::RecoverBlock { items, .. } => items.iter().any(Self::contains_break),
1057
1058 Expression::If {
1059 condition,
1060 consequence,
1061 alternative,
1062 ..
1063 } => {
1064 condition.contains_break()
1065 || consequence.contains_break()
1066 || alternative.contains_break()
1067 }
1068
1069 Expression::IfLet {
1070 scrutinee,
1071 consequence,
1072 alternative,
1073 ..
1074 } => {
1075 scrutinee.contains_break()
1076 || consequence.contains_break()
1077 || alternative.contains_break()
1078 }
1079
1080 Expression::Match { subject, arms, .. } => {
1081 subject.contains_break() || arms.iter().any(|arm| arm.expression.contains_break())
1082 }
1083
1084 Expression::Paren { expression, .. } => expression.contains_break(),
1085
1086 Expression::Binary { left, right, .. } => {
1087 left.contains_break() || right.contains_break()
1088 }
1089
1090 Expression::Unary { expression, .. } => expression.contains_break(),
1091
1092 Expression::Call {
1093 expression,
1094 args,
1095 spread,
1096 ..
1097 } => {
1098 expression.contains_break()
1099 || args.iter().any(Self::contains_break)
1100 || spread.as_ref().as_ref().is_some_and(Self::contains_break)
1101 }
1102
1103 Expression::Function { .. } | Expression::Lambda { .. } => false,
1104
1105 Expression::Select { arms, .. } => arms.iter().any(|arm| match &arm.pattern {
1106 SelectArmPattern::Receive { body, .. } => body.contains_break(),
1107 SelectArmPattern::Send { body, .. } => body.contains_break(),
1108 SelectArmPattern::MatchReceive { arms, .. } => {
1109 arms.iter().any(|a| a.expression.contains_break())
1110 }
1111 SelectArmPattern::WildCard { body } => body.contains_break(),
1112 }),
1113
1114 Expression::Cast { expression, .. } => expression.contains_break(),
1115
1116 Expression::Let {
1117 value, else_block, ..
1118 } => value.contains_break() || else_block.as_ref().is_some_and(|e| e.contains_break()),
1119
1120 Expression::Assignment { value, .. } => value.contains_break(),
1121
1122 _ => false,
1123 }
1124 }
1125
1126 pub fn diverges(&self) -> Option<DeadCodeCause> {
1127 match self {
1128 Expression::Return { .. } => Some(DeadCodeCause::Return),
1129 Expression::Break { .. } => Some(DeadCodeCause::Break),
1130 Expression::Continue { .. } => Some(DeadCodeCause::Continue),
1131
1132 Expression::If {
1133 consequence,
1134 alternative,
1135 ..
1136 } => {
1137 if consequence.diverges().is_some() && alternative.diverges().is_some() {
1138 Some(DeadCodeCause::DivergingIf)
1139 } else {
1140 None
1141 }
1142 }
1143
1144 Expression::IfLet {
1145 consequence,
1146 alternative,
1147 ..
1148 } => {
1149 if consequence.diverges().is_some() && alternative.diverges().is_some() {
1150 Some(DeadCodeCause::DivergingIf)
1151 } else {
1152 None
1153 }
1154 }
1155
1156 Expression::Match { arms, .. } => {
1157 if !arms.is_empty() && arms.iter().all(|arm| arm.expression.diverges().is_some()) {
1158 Some(DeadCodeCause::DivergingMatch)
1159 } else {
1160 None
1161 }
1162 }
1163
1164 Expression::Block { items, .. } => {
1165 for item in items {
1166 if let Some(cause) = item.diverges() {
1167 return Some(cause);
1168 }
1169 }
1170 None
1171 }
1172
1173 Expression::TryBlock { items, .. } | Expression::RecoverBlock { items, .. } => {
1174 for item in items {
1175 if let Some(cause) = item.diverges() {
1176 return Some(cause);
1177 }
1178 }
1179 None
1180 }
1181
1182 Expression::Paren { expression, .. } | Expression::Cast { expression, .. } => {
1183 expression.diverges()
1184 }
1185
1186 Expression::Loop { body, .. } => {
1187 if !body.contains_break() {
1188 Some(DeadCodeCause::InfiniteLoop)
1189 } else {
1190 None
1191 }
1192 }
1193
1194 Expression::Call { ty, .. } if ty.is_never() => Some(DeadCodeCause::DivergingCall),
1195
1196 _ => None,
1197 }
1198 }
1199
1200 pub fn children(&self) -> Vec<&Expression> {
1205 match self {
1206 Expression::Literal { literal, .. } => match literal {
1207 Literal::Slice(elements) => elements.iter().collect(),
1208 Literal::FormatString(parts) => parts
1209 .iter()
1210 .filter_map(|p| match p {
1211 FormatStringPart::Expression(e) => Some(e.as_ref()),
1212 FormatStringPart::Text(_) => None,
1213 })
1214 .collect(),
1215 _ => vec![],
1216 },
1217 Expression::Function { body, .. } => vec![body],
1218 Expression::Lambda { body, .. } => vec![body],
1219 Expression::Block { items, .. } => items.iter().collect(),
1220 Expression::Let {
1221 value, else_block, ..
1222 } => {
1223 let mut c = vec![value.as_ref()];
1224 if let Some(eb) = else_block {
1225 c.push(eb);
1226 }
1227 c
1228 }
1229 Expression::Identifier { .. } => vec![],
1230 Expression::Call {
1231 expression,
1232 args,
1233 spread,
1234 ..
1235 } => {
1236 let mut c = vec![expression.as_ref()];
1237 c.extend(args);
1238 if let Some(s) = spread.as_ref() {
1239 c.push(s);
1240 }
1241 c
1242 }
1243 Expression::If {
1244 condition,
1245 consequence,
1246 alternative,
1247 ..
1248 } => vec![condition, consequence, alternative],
1249 Expression::IfLet {
1250 scrutinee,
1251 consequence,
1252 alternative,
1253 ..
1254 } => vec![scrutinee, consequence, alternative],
1255 Expression::Match { subject, arms, .. } => {
1256 let mut c = vec![subject.as_ref()];
1257 for arm in arms {
1258 if let Some(guard) = &arm.guard {
1259 c.push(guard);
1260 }
1261 c.push(&arm.expression);
1262 }
1263 c
1264 }
1265 Expression::Tuple { elements, .. } => elements.iter().collect(),
1266 Expression::StructCall {
1267 field_assignments,
1268 spread,
1269 ..
1270 } => {
1271 let mut c: Vec<&Expression> =
1272 field_assignments.iter().map(|f| f.value.as_ref()).collect();
1273 if let Some(s) = spread.as_ref() {
1274 c.push(s);
1275 }
1276 c
1277 }
1278 Expression::DotAccess { expression, .. } => vec![expression],
1279 Expression::Assignment { target, value, .. } => vec![target, value],
1280 Expression::Return { expression, .. } => vec![expression],
1281 Expression::Propagate { expression, .. } => vec![expression],
1282 Expression::TryBlock { items, .. } | Expression::RecoverBlock { items, .. } => {
1283 items.iter().collect()
1284 }
1285 Expression::ImplBlock { methods, .. } => methods.iter().collect(),
1286 Expression::Binary { left, right, .. } => vec![left, right],
1287 Expression::Unary { expression, .. } => vec![expression],
1288 Expression::Paren { expression, .. } => vec![expression],
1289 Expression::Const { expression, .. } => vec![expression],
1290 Expression::Loop { body, .. } => vec![body],
1291 Expression::While {
1292 condition, body, ..
1293 } => vec![condition, body],
1294 Expression::WhileLet {
1295 scrutinee, body, ..
1296 } => vec![scrutinee, body],
1297 Expression::For { iterable, body, .. } => vec![iterable, body],
1298 Expression::Break { value, .. } => {
1299 value.as_ref().map(|v| vec![v.as_ref()]).unwrap_or_default()
1300 }
1301 Expression::Reference { expression, .. } => vec![expression],
1302 Expression::IndexedAccess {
1303 expression, index, ..
1304 } => vec![expression, index],
1305 Expression::Task { expression, .. } => vec![expression],
1306 Expression::Defer { expression, .. } => vec![expression],
1307 Expression::Select { arms, .. } => {
1308 let mut c = vec![];
1309 for arm in arms {
1310 match &arm.pattern {
1311 SelectArmPattern::Receive {
1312 receive_expression,
1313 body,
1314 ..
1315 } => {
1316 c.push(receive_expression.as_ref());
1317 c.push(body.as_ref());
1318 }
1319 SelectArmPattern::Send {
1320 send_expression,
1321 body,
1322 } => {
1323 c.push(send_expression.as_ref());
1324 c.push(body.as_ref());
1325 }
1326 SelectArmPattern::MatchReceive {
1327 receive_expression,
1328 arms: match_arms,
1329 } => {
1330 c.push(receive_expression.as_ref());
1331 for ma in match_arms {
1332 if let Some(guard) = &ma.guard {
1333 c.push(guard);
1334 }
1335 c.push(&ma.expression);
1336 }
1337 }
1338 SelectArmPattern::WildCard { body } => {
1339 c.push(body.as_ref());
1340 }
1341 }
1342 }
1343 c
1344 }
1345 Expression::Range { start, end, .. } => {
1346 let mut c = vec![];
1347 if let Some(s) = start {
1348 c.push(s.as_ref());
1349 }
1350 if let Some(e) = end {
1351 c.push(e.as_ref());
1352 }
1353 c
1354 }
1355 Expression::Cast { expression, .. } => vec![expression],
1356 Expression::Interface {
1357 method_signatures, ..
1358 } => method_signatures.iter().collect(),
1359 Expression::Unit { .. }
1360 | Expression::Continue { .. }
1361 | Expression::Enum { .. }
1362 | Expression::ValueEnum { .. }
1363 | Expression::Struct { .. }
1364 | Expression::TypeAlias { .. }
1365 | Expression::VariableDeclaration { .. }
1366 | Expression::ModuleImport { .. }
1367 | Expression::RawGo { .. }
1368 | Expression::NoOp => vec![],
1369 }
1370 }
1371
1372 pub fn unwrap_parens(&self) -> &Expression {
1373 match self {
1374 Expression::Paren { expression, .. } => expression.unwrap_parens(),
1375 other => other,
1376 }
1377 }
1378
1379 pub fn as_dotted_path(&self) -> Option<String> {
1380 match self {
1381 Expression::Identifier { value, .. } => Some(value.to_string()),
1382 Expression::DotAccess {
1383 expression, member, ..
1384 } => Some(format!("{}.{}", expression.as_dotted_path()?, member)),
1385 _ => None,
1386 }
1387 }
1388
1389 pub fn root_identifier(&self) -> Option<&str> {
1390 match self {
1391 Expression::Identifier { value, .. } => Some(value),
1392 Expression::DotAccess { expression, .. } => expression.root_identifier(),
1393 _ => None,
1394 }
1395 }
1396
1397 pub fn is_empty_collection(&self) -> bool {
1398 matches!(
1399 self,
1400 Expression::Literal {
1401 literal: Literal::Slice(elements),
1402 ..
1403 } if elements.is_empty()
1404 )
1405 }
1406
1407 pub fn is_all_literals(&self) -> bool {
1408 match self.unwrap_parens() {
1409 Expression::Literal { literal, .. } => match literal {
1410 Literal::Slice(elements) => elements.iter().all(|e| e.is_all_literals()),
1411 Literal::FormatString(parts) => parts.iter().all(|p| match p {
1412 FormatStringPart::Text(_) => true,
1413 FormatStringPart::Expression(e) => e.is_all_literals(),
1414 }),
1415 _ => true,
1416 },
1417 Expression::Tuple { elements, .. } => elements.iter().all(|e| e.is_all_literals()),
1418 Expression::Unit { .. } => true,
1419 _ => false,
1420 }
1421 }
1422
1423 pub fn get_var_name(&self) -> Option<String> {
1424 match self {
1425 Expression::Identifier { value, .. } => Some(value.to_string()),
1426 Expression::DotAccess { expression, .. } => expression.get_var_name(),
1427 Expression::Assignment { target, .. } => target.get_var_name(),
1428 Expression::IndexedAccess { expression, .. } => expression.get_var_name(),
1429 Expression::Paren { expression, .. } => expression.get_var_name(),
1430 Expression::Reference { expression, .. } => expression.get_var_name(),
1431 Expression::Unary {
1432 operator,
1433 expression,
1434 ..
1435 } => {
1436 if operator == &UnaryOperator::Deref {
1437 expression.get_var_name()
1438 } else {
1439 None
1440 }
1441 }
1442 _ => None,
1443 }
1444 }
1445
1446 pub fn is_function(&self) -> bool {
1447 matches!(self, Expression::Function { .. })
1448 }
1449
1450 pub fn set_public(self) -> Self {
1451 match self {
1452 Expression::Enum {
1453 doc,
1454 attributes,
1455 name,
1456 name_span,
1457 generics,
1458 variants,
1459 span,
1460 ..
1461 } => Expression::Enum {
1462 doc,
1463 attributes,
1464 name,
1465 name_span,
1466 generics,
1467 variants,
1468 visibility: Visibility::Public,
1469 span,
1470 },
1471 Expression::ValueEnum {
1472 doc,
1473 name,
1474 name_span,
1475 underlying_ty,
1476 variants,
1477 span,
1478 ..
1479 } => Expression::ValueEnum {
1480 doc,
1481 name,
1482 name_span,
1483 underlying_ty,
1484 variants,
1485 visibility: Visibility::Public,
1486 span,
1487 },
1488 Expression::Struct {
1489 doc,
1490 attributes,
1491 name,
1492 name_span,
1493 generics,
1494 fields,
1495 kind,
1496 span,
1497 ..
1498 } => {
1499 let fields = if kind == StructKind::Tuple {
1500 fields
1501 .into_iter()
1502 .map(|f| StructFieldDefinition {
1503 visibility: Visibility::Public,
1504 ..f
1505 })
1506 .collect()
1507 } else {
1508 fields
1509 };
1510 Expression::Struct {
1511 doc,
1512 attributes,
1513 name,
1514 name_span,
1515 generics,
1516 fields,
1517 kind,
1518 visibility: Visibility::Public,
1519 span,
1520 }
1521 }
1522 Expression::Function {
1523 doc,
1524 attributes,
1525 name,
1526 name_span,
1527 generics,
1528 params,
1529 return_annotation,
1530 return_type,
1531 body,
1532 ty,
1533 span,
1534 ..
1535 } => Expression::Function {
1536 doc,
1537 attributes,
1538 name,
1539 name_span,
1540 generics,
1541 params,
1542 return_annotation,
1543 return_type,
1544 visibility: Visibility::Public,
1545 body,
1546 ty,
1547 span,
1548 },
1549 Expression::Const {
1550 doc,
1551 identifier,
1552 identifier_span,
1553 annotation,
1554 expression,
1555 ty,
1556 span,
1557 ..
1558 } => Expression::Const {
1559 doc,
1560 identifier,
1561 identifier_span,
1562 annotation,
1563 expression,
1564 visibility: Visibility::Public,
1565 ty,
1566 span,
1567 },
1568 Expression::VariableDeclaration {
1569 doc,
1570 name,
1571 name_span,
1572 annotation,
1573 ty,
1574 span,
1575 ..
1576 } => Expression::VariableDeclaration {
1577 doc,
1578 name,
1579 name_span,
1580 annotation,
1581 visibility: Visibility::Public,
1582 ty,
1583 span,
1584 },
1585 Expression::TypeAlias {
1586 doc,
1587 name,
1588 name_span,
1589 generics,
1590 annotation,
1591 ty,
1592 span,
1593 ..
1594 } => Expression::TypeAlias {
1595 doc,
1596 name,
1597 name_span,
1598 generics,
1599 annotation,
1600 ty,
1601 visibility: Visibility::Public,
1602 span,
1603 },
1604 Expression::Interface {
1605 doc,
1606 name,
1607 name_span,
1608 generics,
1609 parents,
1610 method_signatures,
1611 span,
1612 ..
1613 } => Expression::Interface {
1614 doc,
1615 name,
1616 name_span,
1617 generics,
1618 parents,
1619 method_signatures,
1620 visibility: Visibility::Public,
1621 span,
1622 },
1623 expression => expression,
1624 }
1625 }
1626
1627 pub fn has_else(&self) -> bool {
1628 match self {
1629 Self::Block { items, .. } if items.is_empty() => false,
1630 Self::Unit { .. } => false,
1631 Self::If { alternative, .. } | Self::IfLet { alternative, .. } => {
1632 alternative.has_else()
1633 }
1634 _ => true,
1635 }
1636 }
1637}
1638
1639#[derive(Debug, Clone, PartialEq)]
1640pub enum Literal {
1641 Integer {
1642 value: u64,
1643 text: Option<String>,
1644 },
1645 Float {
1646 value: f64,
1647 text: Option<String>,
1648 },
1649 Imaginary(f64),
1651 Boolean(bool),
1652 String(String),
1653 FormatString(Vec<FormatStringPart>),
1654 Char(String),
1655 Slice(Vec<Expression>),
1656}
1657
1658#[derive(Debug, Clone, PartialEq)]
1659pub enum FormatStringPart {
1660 Text(String),
1661 Expression(Box<Expression>),
1662}
1663
1664#[derive(Debug, Clone, PartialEq)]
1665pub enum UnaryOperator {
1666 Negative,
1667 Not,
1668 Deref,
1669}
1670
1671#[derive(Debug, Clone, Copy, PartialEq)]
1672pub enum BinaryOperator {
1673 Addition,
1674 Subtraction,
1675 Multiplication,
1676 Division,
1677 LessThan,
1678 LessThanOrEqual,
1679 GreaterThan,
1680 GreaterThanOrEqual,
1681 Remainder,
1682 Equal,
1683 NotEqual,
1684 And,
1685 Or,
1686 Pipeline,
1687}
1688
1689impl std::fmt::Display for BinaryOperator {
1690 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1691 let symbol = match self {
1692 BinaryOperator::Addition => "+",
1693 BinaryOperator::Subtraction => "-",
1694 BinaryOperator::Multiplication => "*",
1695 BinaryOperator::Division => "/",
1696 BinaryOperator::Remainder => "%",
1697 BinaryOperator::Equal => "==",
1698 BinaryOperator::NotEqual => "!=",
1699 BinaryOperator::LessThan => "<",
1700 BinaryOperator::LessThanOrEqual => "<=",
1701 BinaryOperator::GreaterThan => ">",
1702 BinaryOperator::GreaterThanOrEqual => ">=",
1703 BinaryOperator::And => "&&",
1704 BinaryOperator::Or => "||",
1705 BinaryOperator::Pipeline => "|>",
1706 };
1707 write!(f, "{}", symbol)
1708 }
1709}
1710
1711#[derive(Debug, Clone, PartialEq)]
1712pub struct ParentInterface {
1713 pub annotation: Annotation,
1714 pub ty: Type,
1715 pub span: Span,
1716}
1717
1718#[derive(Debug, Clone, Copy, PartialEq)]
1719#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
1720pub enum Visibility {
1721 Public,
1722 Private,
1723}
1724
1725impl Visibility {
1726 pub fn is_public(&self) -> bool {
1727 matches!(self, Visibility::Public)
1728 }
1729}
1730
1731#[derive(Debug, Clone, PartialEq)]
1732pub enum ImportAlias {
1733 Named(EcoString, Span),
1734 Blank(Span),
1735}