mirror of https://github.com/crytic/slither
Add support for CustomError (#947)
* Add support for CustomError - Create a new CustomError/CustomErrorTopLevel/CustomErrorContract core objects, and associated parsing classes - Create a specific solidity function to handle the revert CustomError call - Fix #919, Fix #893pull/984/head
parent
154dd9f260
commit
df1062902f
@ -0,0 +1,71 @@ |
||||
from typing import List, TYPE_CHECKING, Optional, Type, Union |
||||
|
||||
from slither.core.solidity_types import UserDefinedType |
||||
from slither.core.source_mapping.source_mapping import SourceMapping |
||||
from slither.core.variables.local_variable import LocalVariable |
||||
|
||||
if TYPE_CHECKING: |
||||
from slither.core.compilation_unit import SlitherCompilationUnit |
||||
|
||||
|
||||
class CustomError(SourceMapping): |
||||
def __init__(self, compilation_unit: "SlitherCompilationUnit"): |
||||
super().__init__() |
||||
self._name: str = "" |
||||
self._parameters: List[LocalVariable] = [] |
||||
self._compilation_unit = compilation_unit |
||||
|
||||
self._solidity_signature: Optional[str] = None |
||||
|
||||
@property |
||||
def name(self) -> str: |
||||
return self._name |
||||
|
||||
@name.setter |
||||
def name(self, new_name: str) -> None: |
||||
self._name = new_name |
||||
|
||||
@property |
||||
def parameters(self) -> List[LocalVariable]: |
||||
return self._parameters |
||||
|
||||
def add_parameters(self, p: "LocalVariable"): |
||||
self._parameters.append(p) |
||||
|
||||
@property |
||||
def compilation_unit(self) -> "SlitherCompilationUnit": |
||||
return self._compilation_unit |
||||
|
||||
# region Signature |
||||
################################################################################### |
||||
################################################################################### |
||||
|
||||
@staticmethod |
||||
def _convert_type_for_solidity_signature(t: Optional[Union[Type, List[Type]]]): |
||||
# pylint: disable=import-outside-toplevel |
||||
from slither.core.declarations import Contract |
||||
|
||||
if isinstance(t, UserDefinedType) and isinstance(t.type, Contract): |
||||
return "address" |
||||
return str(t) |
||||
|
||||
@property |
||||
def solidity_signature(self) -> str: |
||||
""" |
||||
Return a signature following the Solidity Standard |
||||
Contract and converted into address |
||||
:return: the solidity signature |
||||
""" |
||||
if self._solidity_signature is None: |
||||
parameters = [ |
||||
self._convert_type_for_solidity_signature(x.type) for x in self.parameters |
||||
] |
||||
self._solidity_signature = self.name + "(" + ",".join(parameters) + ")" |
||||
return self._solidity_signature |
||||
|
||||
# endregion |
||||
################################################################################### |
||||
################################################################################### |
||||
|
||||
def __str__(self): |
||||
return "revert " + self.solidity_signature |
@ -0,0 +1,12 @@ |
||||
from slither.core.children.child_contract import ChildContract |
||||
from slither.core.declarations.custom_error import CustomError |
||||
|
||||
|
||||
class CustomErrorContract(CustomError, ChildContract): |
||||
def is_declared_by(self, contract): |
||||
""" |
||||
Check if the element is declared by the contract |
||||
:param contract: |
||||
:return: |
||||
""" |
||||
return self.contract == contract |
@ -0,0 +1,6 @@ |
||||
from slither.core.declarations.custom_error import CustomError |
||||
from slither.core.declarations.top_level import TopLevel |
||||
|
||||
|
||||
class CustomErrorTopLevel(CustomError, TopLevel): |
||||
pass |
@ -0,0 +1,101 @@ |
||||
from typing import TYPE_CHECKING, Dict |
||||
|
||||
from slither.core.declarations.custom_error import CustomError |
||||
from slither.core.variables.local_variable import LocalVariable |
||||
from slither.solc_parsing.variables.local_variable import LocalVariableSolc |
||||
|
||||
if TYPE_CHECKING: |
||||
from slither.solc_parsing.slither_compilation_unit_solc import SlitherCompilationUnitSolc |
||||
|
||||
|
||||
# Part of the code was copied from the function parsing |
||||
# In the long term we should refactor these two classes to merge the duplicated code |
||||
|
||||
|
||||
class CustomErrorSolc: |
||||
def __init__( |
||||
self, |
||||
custom_error: CustomError, |
||||
custom_error_data: dict, |
||||
slither_parser: "SlitherCompilationUnitSolc", |
||||
): |
||||
self._slither_parser: "SlitherCompilationUnitSolc" = slither_parser |
||||
self._custom_error = custom_error |
||||
custom_error.name = custom_error_data["name"] |
||||
self._params_was_analyzed = False |
||||
|
||||
if not self._slither_parser.is_compact_ast: |
||||
custom_error_data = custom_error_data["attributes"] |
||||
self._custom_error_data = custom_error_data |
||||
|
||||
def analyze_params(self): |
||||
# Can be re-analyzed due to inheritance |
||||
if self._params_was_analyzed: |
||||
return |
||||
|
||||
self._params_was_analyzed = True |
||||
|
||||
if self._slither_parser.is_compact_ast: |
||||
params = self._custom_error_data["parameters"] |
||||
else: |
||||
children = self._custom_error_data[self.get_children("children")] |
||||
# It uses to be |
||||
# params = children[0] |
||||
# returns = children[1] |
||||
# But from Solidity 0.6.3 to 0.6.10 (included) |
||||
# Comment above a function might be added in the children |
||||
child_iter = iter( |
||||
[child for child in children if child[self.get_key()] == "ParameterList"] |
||||
) |
||||
params = next(child_iter) |
||||
|
||||
if params: |
||||
self._parse_params(params) |
||||
|
||||
@property |
||||
def is_compact_ast(self) -> bool: |
||||
return self._slither_parser.is_compact_ast |
||||
|
||||
def get_key(self) -> str: |
||||
return self._slither_parser.get_key() |
||||
|
||||
def get_children(self, key: str) -> str: |
||||
if self._slither_parser.is_compact_ast: |
||||
return key |
||||
return "children" |
||||
|
||||
def _parse_params(self, params: Dict): |
||||
assert params[self.get_key()] == "ParameterList" |
||||
|
||||
if self._slither_parser.is_compact_ast: |
||||
params = params["parameters"] |
||||
else: |
||||
params = params[self.get_children("children")] |
||||
|
||||
for param in params: |
||||
assert param[self.get_key()] == "VariableDeclaration" |
||||
local_var = self._add_param(param) |
||||
self._custom_error.add_parameters(local_var.underlying_variable) |
||||
|
||||
def _add_param(self, param: Dict) -> LocalVariableSolc: |
||||
|
||||
local_var = LocalVariable() |
||||
local_var.set_offset(param["src"], self._slither_parser.compilation_unit) |
||||
|
||||
local_var_parser = LocalVariableSolc(local_var, param) |
||||
|
||||
local_var_parser.analyze(self) |
||||
|
||||
# see https://solidity.readthedocs.io/en/v0.4.24/types.html?highlight=storage%20location#data-location |
||||
if local_var.location == "default": |
||||
local_var.set_location("memory") |
||||
|
||||
return local_var_parser |
||||
|
||||
@property |
||||
def underlying_custom_error(self) -> CustomError: |
||||
return self._custom_error |
||||
|
||||
@property |
||||
def slither_parser(self) -> "SlitherCompilationUnitSolc": |
||||
return self._slither_parser |
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in new issue