Skip to main content

lisette_syntax/
ast.rs

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