swamp_analyzer/
variable.rs1use crate::Analyzer;
6use crate::err::{Error, ErrorKind};
7use source_map_node::Node;
8use std::rc::Rc;
9use swamp_semantic::{
10 BlockScopeMode, Expression, ExpressionKind, MutOrImmutableExpression, Variable, VariableRef,
11};
12use swamp_types::prelude::*;
13
14impl Analyzer<'_> {
15 fn try_find_local_variable(&self, node: &Node) -> Option<&VariableRef> {
16 let current_scope = self
17 .scope
18 .block_scope_stack
19 .iter()
20 .last()
21 .expect("no scope stack available");
22
23 let variable_text = self.get_text_resolved(node).to_string();
24
25 current_scope.variables.get(&variable_text)
26 }
27
28 #[allow(unused)]
29 pub(crate) fn find_variable(
30 &self,
31 variable: &swamp_ast::Variable,
32 ) -> Result<VariableRef, Error> {
33 self.try_find_variable(&variable.name).map_or_else(
34 || Err(self.create_err(ErrorKind::UnknownVariable, &variable.name)),
35 Ok,
36 )
37 }
38
39 pub(crate) fn try_find_variable(&self, node: &swamp_ast::Node) -> Option<VariableRef> {
40 let variable_text = self.get_text(node);
41
42 for scope in self.scope.block_scope_stack.iter().rev() {
43 if let Some(value) = scope.variables.get(&variable_text.to_string()) {
44 return Some(value.clone());
45 }
46 if scope.mode == BlockScopeMode::Closed {
47 break;
48 }
49 }
50
51 None
52 }
53
54 pub(crate) fn create_local_variable(
55 &mut self,
56 variable: &swamp_ast::Node,
57 is_mutable: Option<&swamp_ast::Node>,
58 variable_type_ref: &Type,
59 ) -> Result<VariableRef, Error> {
60 debug_assert!(variable_type_ref.is_concrete());
61 self.create_local_variable_resolved(
62 &self.to_node(variable),
63 Option::from(&self.to_node_option(is_mutable)),
64 variable_type_ref,
65 )
66 }
67
68 pub(crate) fn create_variable(
69 &mut self,
70 variable: &swamp_ast::Variable,
71 variable_type_ref: &Type,
72 ) -> Result<VariableRef, Error> {
73 self.create_local_variable(
74 &variable.name,
75 Option::from(&variable.is_mutable),
76 variable_type_ref,
77 )
78 }
79
80 pub(crate) fn create_local_variable_resolved(
81 &mut self,
82 variable: &Node,
83 is_mutable: Option<&Node>,
84 variable_type_ref: &Type,
85 ) -> Result<VariableRef, Error> {
86 if let Some(_existing_variable) = self.try_find_local_variable(variable) {
87 return Err(
88 self.create_err_resolved(ErrorKind::OverwriteVariableNotAllowedHere, variable)
89 );
90 }
91 let variable_str = self.get_text_resolved(variable).to_string();
92
93 let scope_index = self.scope.block_scope_stack.len() - 1;
94
95 let index = { self.scope.gen_variable_index() };
96
97 let should_insert_in_scope = !variable_str.starts_with('_');
98 let variables = &mut self
99 .scope
100 .block_scope_stack
101 .last_mut()
102 .expect("block scope should have at least one scope")
103 .variables;
104
105 let resolved_variable = Variable {
106 name: variable.clone(),
107 assigned_name: variable_str.clone(),
108
109 resolved_type: variable_type_ref.clone(),
110 mutable_node: is_mutable.cloned(),
111 scope_index,
112 variable_index: variables.len(),
113 unique_id_within_function: index,
114 is_unused: !should_insert_in_scope,
115 };
116
117 let variable_ref = Rc::new(resolved_variable);
118
119 if !should_insert_in_scope && is_mutable.is_some() {
120 return Err(self.create_err_resolved(ErrorKind::UnusedVariablesCanNotBeMut, variable));
121 }
122 if should_insert_in_scope {
123 variables
124 .insert(variable_str, variable_ref.clone())
125 .expect("should have checked earlier for variable");
126 self.function_variables.push(variable_ref.clone());
127 }
128
129 Ok(variable_ref)
130 }
131
132 #[allow(clippy::unnecessary_wraps)]
133 pub(crate) fn create_local_variable_generated(
134 &mut self,
135 variable_str: &str,
136 is_mutable: bool,
137 variable_type_ref: &Type,
138 ) -> Result<VariableRef, Error> {
139 let scope_index = self.scope.block_scope_stack.len() - 1;
140
141 let index_within_function = self.scope.gen_variable_index();
142
143 let variables = &mut self
144 .scope
145 .block_scope_stack
146 .last_mut()
147 .expect("block scope should have at least one scope")
148 .variables;
149
150 let should_insert_in_scope = !variable_str.starts_with('_');
151
152 let resolved_variable = Variable {
153 name: Node::default(),
154 assigned_name: variable_str.to_string(),
155 resolved_type: variable_type_ref.clone(),
156 mutable_node: if is_mutable {
157 Some(Node::default())
158 } else {
159 None
160 },
161 scope_index,
162 variable_index: variables.len(),
163 unique_id_within_function: index_within_function,
164 is_unused: !should_insert_in_scope,
165 };
166
167 let variable_ref = Rc::new(resolved_variable);
168
169 if should_insert_in_scope {
170 variables
171 .insert(variable_str.to_string(), variable_ref.clone())
172 .expect("should have checked earlier for variable");
173 }
174
175 Ok(variable_ref)
176 }
177
178 pub(crate) fn create_variable_binding_for_with(
179 &mut self,
180 ast_variable: &swamp_ast::Variable,
181 converted_expression: MutOrImmutableExpression,
182 ) -> Result<Expression, Error> {
183 let expression_type = converted_expression.ty().clone();
184 let variable_ref = self.create_local_variable(
185 &ast_variable.name,
186 ast_variable.is_mutable.as_ref(),
187 &expression_type,
188 )?;
189 let expr_kind =
190 ExpressionKind::VariableDefinition(variable_ref, Box::from(converted_expression));
191
192 let expr = self.create_expr(expr_kind, expression_type, &ast_variable.name);
193
194 Ok(expr)
195 }
196}