1use crate::Analyzer;
6use swamp_semantic::err::ErrorKind;
7use swamp_semantic::{NormalPattern, Pattern, PatternElement};
8use swamp_types::prelude::EnumVariantType as TypesEnumVariantType;
9use swamp_types::prelude::{EnumVariantCommon, TypeKind, TypeRef};
10
11impl Analyzer<'_> {
12 fn find_variant_in_pattern(
13 &mut self,
14 expression_type: &TypeRef,
15 ast_name: &swamp_ast::Node,
16 ) -> TypesEnumVariantType {
17 let enum_type_ref = if let TypeKind::Enum(enum_type_ref) = &*expression_type.kind {
18 enum_type_ref
19 } else {
20 self.add_err(ErrorKind::ExpectedEnumInPattern, ast_name);
21
22 return TypesEnumVariantType {
23 common: EnumVariantCommon {
24 name: Default::default(),
25 assigned_name: String::new(),
26 container_index: 0,
27 },
28 payload_type: self.types().unit(),
29 };
30 };
31
32 let variant_name = self.get_text(ast_name).to_string();
33
34 if let Some(variant) = enum_type_ref.get_variant(&variant_name) {
35 variant.clone()
36 } else {
37 self.add_err(ErrorKind::UnknownEnumVariantTypeInPattern, ast_name);
38 TypesEnumVariantType {
39 common: EnumVariantCommon {
40 name: Default::default(),
41 assigned_name: String::new(),
42 container_index: 0,
43 },
44 payload_type: self.types().unit(),
45 }
46 }
47 }
48
49 pub(crate) fn analyze_pattern(
50 &mut self,
51 ast_pattern: &swamp_ast::Pattern,
52 expected_condition_type: &TypeRef,
53 ) -> (Pattern, bool, bool) {
54 match ast_pattern {
55 swamp_ast::Pattern::Wildcard(node) => {
56 (Pattern::Wildcard(self.to_node(node)), false, false)
57 }
58 swamp_ast::Pattern::ConcretePattern(node, concrete_pattern, maybe_guard) => {
59 let (normal_pattern, was_pushed, wanted_mutable) =
60 self.analyze_normal_pattern(node, concrete_pattern, expected_condition_type);
61
62 let resolved_guard =
63 maybe_guard
64 .as_ref()
65 .and_then(|guard_clause| match guard_clause {
66 swamp_ast::GuardClause::Wildcard(_) => None,
67 swamp_ast::GuardClause::Expression(clause_expr) => {
68 Some(self.analyze_bool_argument_expression(clause_expr))
69 }
70 });
71 (
72 Pattern::Normal(normal_pattern, resolved_guard),
73 was_pushed,
74 wanted_mutable,
75 )
76 }
77 }
78 }
79
80 #[allow(clippy::too_many_lines)]
81 pub(crate) fn analyze_normal_pattern(
82 &mut self,
83 node: &swamp_ast::Node,
84 ast_concrete_pattern: &swamp_ast::ConcretePattern,
85 expected_condition_type: &TypeRef,
86 ) -> (NormalPattern, bool, bool) {
87 let mut anyone_wants_mutable = false;
88 match ast_concrete_pattern {
89 swamp_ast::ConcretePattern::EnumPattern(variant_name, destructuring_pattern) => {
90 let mut scope_was_pushed = false;
91 let enum_variant_type_ref =
92 self.find_variant_in_pattern(expected_condition_type, variant_name);
93
94 match destructuring_pattern {
95 swamp_ast::DestructuringPattern::Struct { fields } => {
96 if !scope_was_pushed {
97 self.push_block_scope("enum struct");
98 scope_was_pushed = true;
99 }
100
101 let mut resolved_elements = Vec::new();
102
103 match &*enum_variant_type_ref.payload_type.kind {
104 TypeKind::AnonymousStruct(anon_struct_type) => {
105 for var in fields {
107 let var_name_str = self.get_text(&var.name).to_string();
108 let Some(field_index) = anon_struct_type
110 .field_name_sorted_fields
111 .get_index(&var_name_str)
112 else {
113 return (
114 NormalPattern::Literal(
115 self.create_err(ErrorKind::UnknownField, &var.name),
116 ),
117 false,
118 false,
119 );
120 };
121
122 let Some(field_type) = anon_struct_type
123 .field_name_sorted_fields
124 .get(&var_name_str)
125 else {
126 return (
127 NormalPattern::Literal(
128 self.create_err(ErrorKind::UnknownField, &var.name),
129 ),
130 false,
131 false,
132 );
133 };
134
135 if var.is_mutable.is_some() {
136 anyone_wants_mutable = true;
137 }
138
139 let variable_ref = self.create_local_variable(
140 &var.name,
141 Option::from(&var.is_mutable),
142 &field_type.field_type,
143 false,
144 );
145
146 resolved_elements.push(PatternElement::VariableWithFieldIndex(
147 variable_ref,
148 field_index,
149 ));
150 }
151 }
152 _ => {
153 return (
154 NormalPattern::Literal(
155 self.create_err(ErrorKind::CanNotDestructure, variant_name),
156 ),
157 false,
158 false,
159 );
160 }
161 }
162
163 (
164 NormalPattern::EnumPattern(
165 enum_variant_type_ref,
166 Some(resolved_elements),
167 ),
168 scope_was_pushed,
169 anyone_wants_mutable,
170 )
171 }
172 swamp_ast::DestructuringPattern::Tuple { elements } => {
173 if !scope_was_pushed {
174 self.push_block_scope("enum tuple");
175 scope_was_pushed = true;
176 }
177 assert!(elements.len() > 1, "internal error with tuple");
178
179 let mut resolved_elements = Vec::new();
180
181 if let TypeKind::Tuple(fields_in_order) =
182 &*enum_variant_type_ref.payload_type.kind
183 {
184 if elements.len() > fields_in_order.len() {
186 return (
187 NormalPattern::Literal(self.create_err(
188 ErrorKind::TooManyTupleFields {
189 max: fields_in_order.len(),
190 got: elements.len(),
191 },
192 variant_name,
193 )),
194 false,
195 false,
196 );
197 }
198
199 for (tuple_element_index, (element, field_type)) in
201 elements.iter().zip(fields_in_order).enumerate()
202 {
203 match element {
204 swamp_ast::PatternVariableOrWildcard::Variable(var) => {
205 if var.is_mutable.is_some() {
206 anyone_wants_mutable = true;
207 }
208
209 let variable_ref = self.create_local_variable(
210 &var.name,
211 var.is_mutable.as_ref(),
212 field_type,
213 false,
214 );
215
216 resolved_elements.push(
217 PatternElement::VariableWithFieldIndex(
218 variable_ref,
219 tuple_element_index,
220 ),
221 );
222 }
223 swamp_ast::PatternVariableOrWildcard::Wildcard(node) => {
224 resolved_elements
225 .push(PatternElement::Wildcard(self.to_node(node)));
226 }
227 }
228 }
229 (
230 NormalPattern::EnumPattern(
231 enum_variant_type_ref,
232 Some(resolved_elements),
233 ),
234 scope_was_pushed,
235 anyone_wants_mutable,
236 )
237 } else {
238 self.add_err(ErrorKind::ExpectedTupleType, node);
239 (NormalPattern::PatternList(vec![]), false, false)
240 }
241 }
242 swamp_ast::DestructuringPattern::None { variable } => {
243 if !scope_was_pushed {
245 self.push_block_scope("enum payload");
246 scope_was_pushed = true;
247 }
248
249 if variable.is_mutable.is_some() {
250 anyone_wants_mutable = true;
251 }
252
253 let variable_ref = self.create_local_variable(
254 &variable.name,
255 variable.is_mutable.as_ref(),
256 &enum_variant_type_ref.payload_type,
257 false,
258 );
259
260 let payload_pattern_element = PatternElement::Variable(variable_ref);
261
262 (
263 NormalPattern::EnumPattern(
264 enum_variant_type_ref,
265 Some(vec![payload_pattern_element]),
266 ),
267 scope_was_pushed,
268 anyone_wants_mutable,
269 )
270 }
271 swamp_ast::DestructuringPattern::Unit => {
272 (
274 NormalPattern::EnumPattern(enum_variant_type_ref, None),
275 scope_was_pushed,
276 anyone_wants_mutable,
277 )
278 }
279 }
280 }
281
282 swamp_ast::ConcretePattern::Literal(ast_literal) => (
283 self.analyze_pattern_literal(node, ast_literal, expected_condition_type),
284 false,
285 anyone_wants_mutable,
286 ),
287 }
288 }
289}