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