diff --git a/slither/solc_parsing/declarations/function.py b/slither/solc_parsing/declarations/function.py index 379d20528..a8167c666 100644 --- a/slither/solc_parsing/declarations/function.py +++ b/slither/solc_parsing/declarations/function.py @@ -48,6 +48,15 @@ class FunctionSolc(Function): self._content_was_analyzed = False self._counter_nodes = 0 + self._counter_scope_local_variables = 0 + # variable renamed will map the solc id + # to the variable. It only works for compact format + # Later if an expression provides the referencedDeclaration attr + # we can retrieve the variable + # It only matters if two variables have the same name in the function + # which is only possible with solc > 0.5 + self._variables_renamed = {} + def get_key(self): return self.slither.get_key() @@ -60,6 +69,24 @@ class FunctionSolc(Function): def is_compact_ast(self): return self.slither.is_compact_ast + @property + def variables_renamed(self): + return self._variables_renamed + + def _add_local_variable(self, local_var): + # If two local variables have the same name + # We add a suffix to the new variable + # This is done to prevent collision during SSA translation + # Use of while in case of collision + # In the worst case, the name will be really long + while local_var.name in self._variables: + local_var.name += "_scope_{}".format(self._counter_scope_local_variables) + self._counter_scope_local_variables += 1 + if not local_var.reference_id is None: + self._variables_renamed[local_var.reference_id] = local_var + self._variables[local_var.name] = local_var + + def _analyze_attributes(self): if self.is_compact_ast: attributes = self._functionNotParsed @@ -329,7 +356,7 @@ class FunctionSolc(Function): local_var.set_function(self) local_var.set_offset(statement['src'], self.contract.slither) - self._variables[local_var.name] = local_var + self._add_local_variable(local_var) #local_var.analyze(self) new_node = self._new_node(NodeType.VARIABLE, statement['src']) @@ -491,7 +518,7 @@ class FunctionSolc(Function): local_var.set_function(self) local_var.set_offset(statement['src'], self.contract.slither) - self._variables[local_var.name] = local_var + self._add_local_variable(local_var) # local_var.analyze(self) new_node = self._new_node(NodeType.VARIABLE, statement['src']) @@ -741,7 +768,7 @@ class FunctionSolc(Function): if local_var.location == 'default': local_var.set_location('memory') - self._variables[local_var.name] = local_var + self._add_local_variable(local_var) self._parameters.append(local_var) def _parse_returns(self, returns): @@ -766,7 +793,7 @@ class FunctionSolc(Function): if local_var.location == 'default': local_var.set_location('memory') - self._variables[local_var.name] = local_var + self._add_local_variable(local_var) self._returns.append(local_var) diff --git a/slither/solc_parsing/expressions/expression_parsing.py b/slither/solc_parsing/expressions/expression_parsing.py index 1fe3a5322..14bb73e03 100644 --- a/slither/solc_parsing/expressions/expression_parsing.py +++ b/slither/solc_parsing/expressions/expression_parsing.py @@ -60,6 +60,11 @@ def find_variable(var_name, caller_context, referenced_declaration=None): exit(-1) if function: + # We look for variable declared with the referencedDeclaration attr + func_variables = function.variables_renamed + if referenced_declaration and referenced_declaration in func_variables: + return func_variables[referenced_declaration] + # If not found, check for name func_variables = function.variables_as_dict() if var_name in func_variables: return func_variables[var_name] diff --git a/slither/solc_parsing/variables/variable_declaration.py b/slither/solc_parsing/variables/variable_declaration.py index 17b1e9c37..0f5f08ee3 100644 --- a/slither/solc_parsing/variables/variable_declaration.py +++ b/slither/solc_parsing/variables/variable_declaration.py @@ -35,6 +35,9 @@ class VariableDeclarationSolc(Variable): self._is_compact_ast = False + self._reference_id = None + + if 'nodeType' in var: self._is_compact_ast = True nodeType = var['nodeType'] @@ -80,6 +83,14 @@ class VariableDeclarationSolc(Variable): def uninitialized(self): return not self._initialized + @property + def reference_id(self): + ''' + Return the solc id. It can be compared with the referencedDeclaration attr + Returns None if it was not parsed (legacy AST) + ''' + return self._reference_id + def _analyze_variable_attributes(self, attributes): if 'visibility' in attributes: self._visibility = attributes['visibility'] @@ -105,6 +116,12 @@ class VariableDeclarationSolc(Variable): self._initial_expression = None self._type = None + # Only for comapct ast format + # the id can be used later if referencedDeclaration + # is provided + if 'id' in var: + self._reference_id = var['id'] + if 'constant' in attributes: self._is_constant = attributes['constant']