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