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