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