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