Skip to main content

lisette_syntax/
ast.rs

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