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