sway_core/language/parsed/expression/
scrutinee.rs

1use crate::{
2    engine_threading::{EqWithEngines, PartialEqWithEngines, PartialEqWithEnginesContext},
3    language::{CallPath, Literal},
4    TypeInfo,
5};
6
7use sway_error::handler::ErrorEmitted;
8use sway_types::{ident::Ident, span::Span, Spanned};
9
10/// A [Scrutinee] is on the left-hand-side of a pattern, and dictates whether or
11/// not a pattern will succeed at pattern matching and what, if any, elements will
12/// need to be implemented in a desugared if expression.
13#[allow(clippy::enum_variant_names)]
14#[derive(Debug, Clone)]
15pub enum Scrutinee {
16    Or {
17        elems: Vec<Scrutinee>,
18        span: Span,
19    },
20    CatchAll {
21        span: Span,
22    },
23    Literal {
24        value: Literal,
25        span: Span,
26    },
27    Variable {
28        name: Ident,
29        span: Span,
30    },
31    AmbiguousSingleIdent(Ident),
32    StructScrutinee {
33        struct_name: CallPath,
34        fields: Vec<StructScrutineeField>,
35        span: Span,
36    },
37    EnumScrutinee {
38        call_path: CallPath,
39        value: Box<Scrutinee>,
40        span: Span,
41    },
42    Tuple {
43        elems: Vec<Scrutinee>,
44        span: Span,
45    },
46    // this is to handle parser recovery
47    Error {
48        spans: Box<[Span]>,
49        err: ErrorEmitted,
50    },
51}
52
53impl EqWithEngines for Scrutinee {}
54impl PartialEqWithEngines for Scrutinee {
55    fn eq(&self, other: &Self, ctx: &PartialEqWithEnginesContext) -> bool {
56        match (self, other) {
57            (
58                Scrutinee::Or { elems, span },
59                Scrutinee::Or {
60                    elems: r_elems,
61                    span: r_span,
62                },
63            ) => elems.eq(r_elems, ctx) && span.eq(r_span),
64            (
65                Scrutinee::Literal { value, span },
66                Scrutinee::Literal {
67                    value: r_value,
68                    span: r_span,
69                },
70            ) => value.eq(r_value) && span.eq(r_span),
71            (
72                Scrutinee::Variable { name, span },
73                Scrutinee::Variable {
74                    name: r_name,
75                    span: r_span,
76                },
77            ) => name.eq(r_name) && span.eq(r_span),
78            (Scrutinee::AmbiguousSingleIdent(ident), Scrutinee::AmbiguousSingleIdent(r_ident)) => {
79                ident.eq(r_ident)
80            }
81            (
82                Scrutinee::StructScrutinee {
83                    struct_name,
84                    fields,
85                    span,
86                },
87                Scrutinee::StructScrutinee {
88                    struct_name: r_struct_name,
89                    fields: r_fields,
90                    span: r_span,
91                },
92            ) => {
93                PartialEqWithEngines::eq(struct_name, r_struct_name, ctx)
94                    && fields.eq(r_fields, ctx)
95                    && span.eq(r_span)
96            }
97            (
98                Scrutinee::EnumScrutinee {
99                    call_path,
100                    value,
101                    span,
102                },
103                Scrutinee::EnumScrutinee {
104                    call_path: r_call_path,
105                    value: r_value,
106                    span: r_span,
107                },
108            ) => {
109                PartialEqWithEngines::eq(call_path, r_call_path, ctx)
110                    && value.eq(r_value, ctx)
111                    && span.eq(r_span)
112            }
113            (
114                Scrutinee::Tuple { elems, span },
115                Scrutinee::Tuple {
116                    elems: r_elems,
117                    span: r_span,
118                },
119            ) => elems.eq(r_elems, ctx) && span.eq(r_span),
120            (
121                Scrutinee::Error { spans, err },
122                Scrutinee::Error {
123                    spans: r_spans,
124                    err: r_err,
125                },
126            ) => spans.eq(r_spans) && err.eq(r_err),
127            _ => false,
128        }
129    }
130}
131
132#[derive(Debug, Clone)]
133#[allow(clippy::large_enum_variant)]
134pub enum StructScrutineeField {
135    Rest {
136        span: Span,
137    },
138    Field {
139        field: Ident,
140        scrutinee: Option<Scrutinee>,
141        span: Span,
142    },
143}
144
145impl EqWithEngines for StructScrutineeField {}
146impl PartialEqWithEngines for StructScrutineeField {
147    fn eq(&self, other: &Self, ctx: &PartialEqWithEnginesContext) -> bool {
148        match (self, other) {
149            (StructScrutineeField::Rest { span }, StructScrutineeField::Rest { span: r_span }) => {
150                span == r_span
151            }
152            (
153                StructScrutineeField::Field {
154                    field,
155                    scrutinee,
156                    span,
157                },
158                StructScrutineeField::Field {
159                    field: r_field,
160                    scrutinee: r_scrutinee,
161                    span: r_span,
162                },
163            ) => field.eq(r_field) && scrutinee.eq(r_scrutinee, ctx) && span.eq(r_span),
164            _ => false,
165        }
166    }
167}
168
169impl Spanned for Scrutinee {
170    fn span(&self) -> Span {
171        match self {
172            Scrutinee::Or { span, .. } => span.clone(),
173            Scrutinee::CatchAll { span } => span.clone(),
174            Scrutinee::Literal { span, .. } => span.clone(),
175            Scrutinee::Variable { span, .. } => span.clone(),
176            Scrutinee::AmbiguousSingleIdent(ident) => ident.span(),
177            Scrutinee::StructScrutinee { span, .. } => span.clone(),
178            Scrutinee::EnumScrutinee { span, .. } => span.clone(),
179            Scrutinee::Tuple { span, .. } => span.clone(),
180            Scrutinee::Error { spans, .. } => spans
181                .iter()
182                .cloned()
183                .reduce(|s1: Span, s2: Span| Span::join(s1, &s2))
184                .unwrap(),
185        }
186    }
187}
188
189impl Scrutinee {
190    /// Given some `Scrutinee` `self`, analyze `self` and return all instances
191    /// of possible dependencies. A "possible dependency" is a `Scrutinee` that
192    /// resolves to one or more `TypeInfo::Custom`.
193    ///
194    /// For example, this `Scrutinee`:
195    ///
196    /// ```ignore
197    /// Scrutinee::EnumScrutinee {
198    ///   call_path: CallPath {
199    ///     prefixes: ["Data"]
200    ///     suffix: "A"
201    ///   },
202    ///   value: Scrutinee::StructScrutinee {
203    ///     struct_name: "Foo",
204    ///     fields: [
205    ///         StructScrutineeField {
206    ///             scrutinee: Scrutinee::StructScrutinee {
207    ///                 struct_name: "Bar",
208    ///                 fields: [
209    ///                     StructScrutineeField {
210    ///                         scrutinee: Scrutinee::Literal { .. },
211    ///                         ..
212    ///                     }
213    ///                 ],
214    ///                 ..
215    ///             },
216    ///             ..
217    ///         }
218    ///     ],
219    ///     ..
220    ///   }
221    ///   ..
222    /// }
223    /// ```
224    ///
225    /// would resolve to this list of approximate `TypeInfo` dependencies:
226    ///
227    /// ```ignore
228    /// [
229    ///     TypeInfo::Custom {
230    ///         name: "Data",
231    ///         ..
232    ///     },
233    ///     TypeInfo::Custom {
234    ///         name: "Foo",
235    ///         ..
236    ///     },
237    ///     TypeInfo::Custom {
238    ///         name: "Bar",
239    ///         ..
240    ///     },
241    /// ]
242    /// ```
243    pub(crate) fn gather_approximate_typeinfo_dependencies(&self) -> Vec<TypeInfo> {
244        match self {
245            Scrutinee::StructScrutinee {
246                struct_name,
247                fields,
248                ..
249            } => {
250                let name = vec![TypeInfo::Custom {
251                    qualified_call_path: struct_name.clone().into(),
252                    type_arguments: None,
253                }];
254                let fields = fields
255                    .iter()
256                    .flat_map(|f| match f {
257                        StructScrutineeField::Field {
258                            scrutinee: Some(scrutinee),
259                            ..
260                        } => scrutinee.gather_approximate_typeinfo_dependencies(),
261                        _ => vec![],
262                    })
263                    .collect::<Vec<TypeInfo>>();
264                [name, fields].concat()
265            }
266            Scrutinee::EnumScrutinee {
267                call_path, value, ..
268            } => {
269                let enum_name = call_path.prefixes.last().unwrap_or(&call_path.suffix);
270                let name = vec![TypeInfo::Custom {
271                    qualified_call_path: enum_name.clone().into(),
272                    type_arguments: None,
273                }];
274                let value = value.gather_approximate_typeinfo_dependencies();
275                [name, value].concat()
276            }
277            Scrutinee::Tuple { elems, .. } | Scrutinee::Or { elems, .. } => elems
278                .iter()
279                .flat_map(|scrutinee| scrutinee.gather_approximate_typeinfo_dependencies())
280                .collect::<Vec<TypeInfo>>(),
281            Scrutinee::Literal { .. }
282            | Scrutinee::CatchAll { .. }
283            | Scrutinee::AmbiguousSingleIdent(..)
284            | Scrutinee::Variable { .. }
285            | Scrutinee::Error { .. } => {
286                vec![]
287            }
288        }
289    }
290}