1use indexmap::IndexMap;
4use solang_parser::diagnostics::{ErrorType, Level, Note};
5use std::collections::HashMap;
6use std::str;
7use std::sync::Arc;
8
9use super::ast::{Diagnostic, Namespace, Type};
10use super::expression::ExprContext;
11use crate::sema::ast::Expression;
12use solang_parser::pt;
13
14#[derive(Clone, Debug)]
15pub struct Variable {
16 pub id: pt::Identifier,
17 pub ty: Type,
18 pub pos: usize,
19 pub slice: bool,
20 pub assigned: bool,
21 pub read: bool,
22 pub usage_type: VariableUsage,
23 pub initializer: VariableInitializer,
24 pub storage_location: Option<pt::StorageLocation>,
25}
26
27#[derive(Clone, Debug)]
28pub enum VariableInitializer {
29 Solidity(Option<Arc<Expression>>),
30 Yul(bool),
31}
32
33impl VariableInitializer {
34 pub fn has_initializer(&self) -> bool {
35 match self {
36 VariableInitializer::Solidity(expr) => expr.is_some(),
37 VariableInitializer::Yul(initialized) => *initialized,
38 }
39 }
40}
41
42impl Variable {
43 pub fn is_reference(&self, ns: &Namespace) -> bool {
44 if matches!(
47 self.storage_location,
48 Some(pt::StorageLocation::Memory(_)) | Some(pt::StorageLocation::Storage(_)) | None
49 ) && self.ty.is_reference_type(ns)
50 {
51 if let VariableInitializer::Solidity(Some(expr)) = &self.initializer {
52 return !matches!(
55 **expr,
56 Expression::AllocDynamicBytes { .. }
57 | Expression::ArrayLiteral { .. }
58 | Expression::Constructor { .. }
59 | Expression::StructLiteral { .. }
60 );
61 }
62 }
63
64 false
65 }
66}
67
68#[derive(Clone, Debug)]
69pub enum VariableUsage {
70 Parameter,
71 ReturnVariable,
72 AnonymousReturnVariable,
73 LocalVariable,
74 DestructureVariable,
75 TryCatchReturns,
76 TryCatchErrorString,
77 TryCatchErrorBytes,
78 YulLocalVariable,
79}
80
81#[derive(Debug, Clone)]
82pub struct VarScope {
83 pub loc: Option<pt::Loc>,
84 pub names: HashMap<String, usize>,
85}
86
87#[derive(Default, Debug, Clone)]
88pub struct Symtable {
89 pub vars: IndexMap<usize, Variable>,
90 pub arguments: Vec<Option<usize>>,
91 pub returns: Vec<usize>,
92 pub scopes: Vec<VarScope>,
93}
94
95impl Symtable {
96 pub fn add(
97 &mut self,
98 id: &pt::Identifier,
99 ty: Type,
100 ns: &mut Namespace,
101 initializer: VariableInitializer,
102 usage_type: VariableUsage,
103 storage_location: Option<pt::StorageLocation>,
104 context: &mut ExprContext,
105 ) -> Option<usize> {
106 let pos = ns.next_id;
107 ns.next_id += 1;
108
109 self.vars.insert(
110 pos,
111 Variable {
112 id: id.clone(),
113 ty,
114 pos,
115 slice: false,
116 initializer,
117 assigned: false,
118 usage_type,
119 read: false,
120 storage_location,
121 },
122 );
123
124 if !id.name.is_empty() {
126 if let Some(prev) = self.find(context, &id.name) {
127 ns.diagnostics.push(Diagnostic::error_with_note(
128 id.loc,
129 format!("{} is already declared", id.name),
130 prev.id.loc,
131 "location of previous declaration".to_string(),
132 ));
133 return None;
134 }
135
136 context
137 .active_scopes
138 .last_mut()
139 .unwrap()
140 .names
141 .insert(id.name.to_string(), pos);
142 }
143
144 Some(pos)
145 }
146
147 pub fn exclusive_add(
148 &mut self,
149 id: &pt::Identifier,
150 ty: Type,
151 ns: &mut Namespace,
152 initializer: VariableInitializer,
153 usage_type: VariableUsage,
154 storage_location: Option<pt::StorageLocation>,
155 context: &mut ExprContext,
156 ) -> Option<usize> {
157 if let Some(var) = self.find(context, &id.name) {
158 ns.diagnostics.push(Diagnostic {
159 level: Level::Error,
160 ty: ErrorType::DeclarationError,
161 loc: id.loc,
162 message: format!("variable name '{}' already used in this scope", id.name),
163 notes: vec![Note {
164 loc: var.id.loc,
165 message: "found previous declaration here".to_string(),
166 }],
167 });
168 return None;
169 }
170
171 self.add(
172 id,
173 ty,
174 ns,
175 initializer,
176 usage_type,
177 storage_location,
178 context,
179 )
180 }
181
182 pub fn find(&self, context: &mut ExprContext, name: &str) -> Option<&Variable> {
183 for scope in context.active_scopes.iter().rev() {
184 if let Some(n) = scope.names.get(name) {
185 return self.vars.get(n);
186 }
187 }
188
189 None
190 }
191
192 pub fn get_name(&self, pos: usize) -> &str {
193 &self.vars[&pos].id.name
194 }
195}
196
197pub struct LoopScope {
198 pub no_breaks: usize,
199 pub no_continues: usize,
200}
201
202pub struct LoopScopes(Vec<LoopScope>);
203
204impl Default for LoopScopes {
205 fn default() -> Self {
206 LoopScopes::new()
207 }
208}
209
210impl LoopScopes {
211 pub fn new() -> Self {
212 LoopScopes(Vec::new())
213 }
214
215 pub fn enter_scope(&mut self) {
216 self.0.push(LoopScope {
217 no_breaks: 0,
218 no_continues: 0,
219 })
220 }
221
222 pub fn leave_scope(&mut self) -> LoopScope {
223 self.0.pop().unwrap()
224 }
225
226 pub fn do_break(&mut self) -> bool {
227 match self.0.last_mut() {
228 Some(scope) => {
229 scope.no_breaks += 1;
230 true
231 }
232 None => false,
233 }
234 }
235
236 pub fn in_a_loop(&self) -> bool {
237 !self.0.is_empty()
238 }
239
240 pub fn do_continue(&mut self) -> bool {
241 match self.0.last_mut() {
242 Some(scope) => {
243 scope.no_continues += 1;
244 true
245 }
246 None => false,
247 }
248 }
249}