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(
99 enum_variant_type_ref.common.symbol_id.into(),
100 name_node.clone(),
101 );
102 self.shared
103 .definition_table
104 .refs
105 .add(enum_variant_type_ref.common.symbol_id.into(), name_node);
106
107 match destructuring_pattern {
108 swamp_ast::DestructuringPattern::Struct { fields } => {
109 if !scope_was_pushed {
110 self.push_block_scope("enum struct");
111 scope_was_pushed = true;
112 }
113
114 let mut resolved_elements = Vec::new();
115
116 match &*enum_variant_type_ref.payload_type.kind {
117 TypeKind::AnonymousStruct(anon_struct_type) => {
118 for var in fields {
120 let var_name_str = self.get_text(&var.name).to_string();
121 let Some(field_index) = anon_struct_type
123 .field_name_sorted_fields
124 .get_index(&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 let Some(field_type) = anon_struct_type
136 .field_name_sorted_fields
137 .get(&var_name_str)
138 else {
139 return (
140 NormalPattern::Literal(
141 self.create_err(ErrorKind::UnknownField, &var.name),
142 ),
143 false,
144 false,
145 );
146 };
147
148 if var.is_mutable.is_some() {
149 anyone_wants_mutable = true;
150 }
151
152 let variable_ref = self.create_local_variable_parameter_like(
153 &var.name,
154 Option::from(&var.is_mutable),
155 &field_type.field_type,
156 false,
157 );
158
159 resolved_elements.push(PatternElement::VariableWithFieldIndex(
160 variable_ref,
161 field_index,
162 ));
163 }
164 }
165 _ => {
166 return (
167 NormalPattern::Literal(
168 self.create_err(ErrorKind::CanNotDestructure, variant_name),
169 ),
170 false,
171 false,
172 );
173 }
174 }
175
176 (
177 NormalPattern::EnumPattern(
178 enum_variant_type_ref,
179 Some(resolved_elements),
180 ),
181 scope_was_pushed,
182 anyone_wants_mutable,
183 )
184 }
185 swamp_ast::DestructuringPattern::Tuple { elements } => {
186 if !scope_was_pushed {
187 self.push_block_scope("enum tuple");
188 scope_was_pushed = true;
189 }
190 assert!(elements.len() > 1, "internal error with tuple");
191
192 let mut resolved_elements = Vec::new();
193
194 if let TypeKind::Tuple(fields_in_order) =
195 &*enum_variant_type_ref.payload_type.kind
196 {
197 if elements.len() > fields_in_order.len() {
199 return (
200 NormalPattern::Literal(self.create_err(
201 ErrorKind::TooManyTupleFields {
202 max: fields_in_order.len(),
203 got: elements.len(),
204 },
205 variant_name,
206 )),
207 false,
208 false,
209 );
210 }
211
212 for (tuple_element_index, (element, field_type)) in
214 elements.iter().zip(fields_in_order).enumerate()
215 {
216 match element {
217 swamp_ast::PatternVariableOrWildcard::Variable(var) => {
218 if var.is_mutable.is_some() {
219 anyone_wants_mutable = true;
220 }
221
222 let variable_ref = self
223 .create_local_variable_parameter_like(
224 &var.name,
225 var.is_mutable.as_ref(),
226 field_type,
227 false,
228 );
229
230 resolved_elements.push(
231 PatternElement::VariableWithFieldIndex(
232 variable_ref,
233 tuple_element_index,
234 ),
235 );
236 }
237 swamp_ast::PatternVariableOrWildcard::Wildcard(node) => {
238 resolved_elements
239 .push(PatternElement::Wildcard(self.to_node(node)));
240 }
241 }
242 }
243 (
244 NormalPattern::EnumPattern(
245 enum_variant_type_ref,
246 Some(resolved_elements),
247 ),
248 scope_was_pushed,
249 anyone_wants_mutable,
250 )
251 } else {
252 self.add_err(ErrorKind::ExpectedTupleType, node);
253 (NormalPattern::PatternList(vec![]), false, false)
254 }
255 }
256 swamp_ast::DestructuringPattern::None { variable } => {
257 if !scope_was_pushed {
259 self.push_block_scope(&format!(
260 "enum destructure to single variable {variable:?}"
261 ));
262 scope_was_pushed = true;
263 }
264
265 if variable.is_mutable.is_some() {
266 anyone_wants_mutable = true;
267 }
268
269 let variable_ref = self.create_local_variable(
270 &variable.name,
271 variable.is_mutable.as_ref(),
272 &enum_variant_type_ref.payload_type,
273 false,
274 );
275
276 let payload_pattern_element = PatternElement::Variable(variable_ref);
277
278 (
279 NormalPattern::EnumPattern(
280 enum_variant_type_ref,
281 Some(vec![payload_pattern_element]),
282 ),
283 scope_was_pushed,
284 anyone_wants_mutable,
285 )
286 }
287 swamp_ast::DestructuringPattern::Unit => {
288 (
290 NormalPattern::EnumPattern(enum_variant_type_ref, None),
291 scope_was_pushed,
292 anyone_wants_mutable,
293 )
294 }
295 }
296 }
297
298 swamp_ast::ConcretePattern::Literal(ast_literal) => (
299 self.analyze_pattern_literal(node, ast_literal, expected_condition_type),
300 false,
301 anyone_wants_mutable,
302 ),
303 }
304 }
305}