Merge branch 'dev' of https://github.com/dokzai/slither into issue-2083

pull/2125/head
Judy Wu 1 year ago
commit f00e520625
  1. 2
      .github/workflows/black.yml
  2. 4
      .github/workflows/ci.yml
  3. 2
      .github/workflows/docker.yml
  4. 2
      .github/workflows/docs.yml
  5. 2
      .github/workflows/doctor.yml
  6. 2
      .github/workflows/linter.yml
  7. 2
      .github/workflows/pip-audit.yml
  8. 2
      .github/workflows/publish.yml
  9. 2
      .github/workflows/pylint.yml
  10. 4
      .github/workflows/test.yml
  11. 37
      slither/core/expressions/call_expression.py
  12. 23
      slither/printers/guidance/echidna.py
  13. 93
      slither/slithir/convert.py
  14. 18
      slither/slithir/operations/call.py
  15. 9
      slither/slithir/operations/high_level_call.py
  16. 2
      slither/slithir/operations/init_array.py
  17. 10
      slither/slithir/operations/internal_call.py
  18. 2
      slither/slithir/operations/new_array.py
  19. 13
      slither/slithir/operations/new_contract.py
  20. 11
      slither/slithir/operations/new_structure.py
  21. 5
      slither/slithir/operations/unary.py
  22. 19
      slither/slithir/tmp_operations/tmp_call.py
  23. 15
      slither/slithir/utils/ssa.py
  24. 2
      slither/solc_parsing/declarations/contract.py
  25. 9
      slither/solc_parsing/declarations/custom_error.py
  26. 3
      slither/solc_parsing/expressions/expression_parsing.py
  27. 32
      slither/solc_parsing/expressions/find_variable.py
  28. 2
      slither/solc_parsing/slither_compilation_unit_solc.py
  29. 18
      slither/visitors/slithir/expression_to_slithir.py
  30. BIN
      tests/e2e/solc_parsing/test_data/compile/custom_error-0.8.4.sol-0.8.10-compact.zip
  31. BIN
      tests/e2e/solc_parsing/test_data/compile/custom_error-0.8.4.sol-0.8.11-compact.zip
  32. BIN
      tests/e2e/solc_parsing/test_data/compile/custom_error-0.8.4.sol-0.8.12-compact.zip
  33. BIN
      tests/e2e/solc_parsing/test_data/compile/custom_error-0.8.4.sol-0.8.13-compact.zip
  34. BIN
      tests/e2e/solc_parsing/test_data/compile/custom_error-0.8.4.sol-0.8.14-compact.zip
  35. BIN
      tests/e2e/solc_parsing/test_data/compile/custom_error-0.8.4.sol-0.8.15-compact.zip
  36. BIN
      tests/e2e/solc_parsing/test_data/compile/custom_error-0.8.4.sol-0.8.4-compact.zip
  37. BIN
      tests/e2e/solc_parsing/test_data/compile/custom_error-0.8.4.sol-0.8.5-compact.zip
  38. BIN
      tests/e2e/solc_parsing/test_data/compile/custom_error-0.8.4.sol-0.8.6-compact.zip
  39. BIN
      tests/e2e/solc_parsing/test_data/compile/custom_error-0.8.4.sol-0.8.7-compact.zip
  40. BIN
      tests/e2e/solc_parsing/test_data/compile/custom_error-0.8.4.sol-0.8.8-compact.zip
  41. BIN
      tests/e2e/solc_parsing/test_data/compile/custom_error-0.8.4.sol-0.8.9-compact.zip
  42. 14
      tests/e2e/solc_parsing/test_data/custom_error-0.8.4.sol
  43. 4
      tests/e2e/solc_parsing/test_data/expected/custom_error-0.8.4.sol-0.8.10-compact.json
  44. 4
      tests/e2e/solc_parsing/test_data/expected/custom_error-0.8.4.sol-0.8.11-compact.json
  45. 4
      tests/e2e/solc_parsing/test_data/expected/custom_error-0.8.4.sol-0.8.12-compact.json
  46. 4
      tests/e2e/solc_parsing/test_data/expected/custom_error-0.8.4.sol-0.8.13-compact.json
  47. 4
      tests/e2e/solc_parsing/test_data/expected/custom_error-0.8.4.sol-0.8.14-compact.json
  48. 4
      tests/e2e/solc_parsing/test_data/expected/custom_error-0.8.4.sol-0.8.15-compact.json
  49. 4
      tests/e2e/solc_parsing/test_data/expected/custom_error-0.8.4.sol-0.8.4-compact.json
  50. 4
      tests/e2e/solc_parsing/test_data/expected/custom_error-0.8.4.sol-0.8.5-compact.json
  51. 4
      tests/e2e/solc_parsing/test_data/expected/custom_error-0.8.4.sol-0.8.6-compact.json
  52. 4
      tests/e2e/solc_parsing/test_data/expected/custom_error-0.8.4.sol-0.8.7-compact.json
  53. 4
      tests/e2e/solc_parsing/test_data/expected/custom_error-0.8.4.sol-0.8.8-compact.json
  54. 4
      tests/e2e/solc_parsing/test_data/expected/custom_error-0.8.4.sol-0.8.9-compact.json
  55. 65
      tests/unit/slithir/test_argument_reorder.py
  56. 8
      tests/unit/slithir/test_data/argument_reorder/test_internal_call_reorder.sol
  57. 11
      tests/unit/slithir/test_data/argument_reorder/test_struct_constructor.sol
  58. 22
      tests/unit/slithir/test_ssa_generation.py

@ -26,7 +26,7 @@ jobs:
steps:
- name: Checkout Code
uses: actions/checkout@v3
uses: actions/checkout@v4
with:
# Full git history is needed to get a proper list of changed files within `super-linter`
fetch-depth: 0

@ -53,7 +53,7 @@ jobs:
- os: windows-2022
type: truffle
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
- name: Set up Python ${{ matrix.python }}
uses: actions/setup-python@v4
with:
@ -67,7 +67,7 @@ jobs:
- name: Set up nix
if: matrix.type == 'dapp'
uses: cachix/install-nix-action@v22
uses: cachix/install-nix-action@v23
- name: Set up cachix
if: matrix.type == 'dapp'

@ -17,7 +17,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v3
uses: actions/checkout@v4
- name: Set up QEMU
uses: docker/setup-qemu-action@v2

@ -28,7 +28,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v3
uses: actions/checkout@v4
- name: Setup Pages
uses: actions/configure-pages@v3
- uses: actions/setup-python@v4

@ -29,7 +29,7 @@ jobs:
- os: windows-2022
python: 3.8
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
- name: Set up Python ${{ matrix.python }}
uses: actions/setup-python@v4

@ -25,7 +25,7 @@ jobs:
steps:
- name: Checkout Code
uses: actions/checkout@v3
uses: actions/checkout@v4
with:
# Full git history is needed to get a proper list of changed files within `super-linter`
fetch-depth: 0

@ -18,7 +18,7 @@ jobs:
steps:
- name: Checkout repository
uses: actions/checkout@v3
uses: actions/checkout@v4
- name: Install Python
uses: actions/setup-python@v4

@ -10,7 +10,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
- name: Set up Python
uses: actions/setup-python@v4

@ -23,7 +23,7 @@ jobs:
steps:
- name: Checkout Code
uses: actions/checkout@v3
uses: actions/checkout@v4
with:
# Full git history is needed to get a proper list of changed files within `super-linter`
fetch-depth: 0

@ -27,7 +27,7 @@ jobs:
type: ["unit", "integration", "tool"]
python: ${{ (github.event_name == 'pull_request' && fromJSON('["3.8", "3.11"]')) || fromJSON('["3.8", "3.9", "3.10", "3.11"]') }}
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
- name: Set up Python ${{ matrix.python }}
uses: actions/setup-python@v4
with:
@ -84,7 +84,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
- name: Set up Python 3.8
uses: actions/setup-python@v4
with:

@ -4,12 +4,32 @@ from slither.core.expressions.expression import Expression
class CallExpression(Expression): # pylint: disable=too-many-instance-attributes
def __init__(self, called: Expression, arguments: List[Any], type_call: str) -> None:
def __init__(
self,
called: Expression,
arguments: List[Any],
type_call: str,
names: Optional[List[str]] = None,
) -> None:
"""
#### Parameters
called -
The expression denoting the function to be called
arguments -
List of argument expressions
type_call -
A string formatting of the called function's return type
names -
For calls with named fields, list fields in call order.
For calls without named fields, None.
"""
assert isinstance(called, Expression)
assert (names is None) or isinstance(names, list)
super().__init__()
self._called: Expression = called
self._arguments: List[Expression] = arguments
self._type_call: str = type_call
self._names: Optional[List[str]] = names
# gas and value are only available if the syntax is {gas: , value: }
# For the .gas().value(), the member are considered as function call
# And converted later to the correct info (convert.py)
@ -17,6 +37,14 @@ class CallExpression(Expression): # pylint: disable=too-many-instance-attribute
self._value: Optional[Expression] = None
self._salt: Optional[Expression] = None
@property
def names(self) -> Optional[List[str]]:
"""
For calls with named fields, list fields in call order.
For calls without named fields, None.
"""
return self._names
@property
def call_value(self) -> Optional[Expression]:
return self._value
@ -62,4 +90,9 @@ class CallExpression(Expression): # pylint: disable=too-many-instance-attribute
if gas or value or salt:
options = [gas, value, salt]
txt += "{" + ",".join([o for o in options if o != ""]) + "}"
return txt + "(" + ",".join([str(a) for a in self._arguments]) + ")"
args = (
"{" + ",".join([f"{n}:{str(a)}" for (a, n) in zip(self._arguments, self._names)]) + "}"
if self._names is not None
else ",".join([str(a) for a in self._arguments])
)
return txt + "(" + args + ")"

@ -126,15 +126,24 @@ def _extract_constant_functions(slither: SlitherCore) -> Dict[str, List[str]]:
return ret
def _extract_assert(slither: SlitherCore) -> Dict[str, List[str]]:
ret: Dict[str, List[str]] = {}
def _extract_assert(slither: SlitherCore) -> Dict[str, Dict[str, List[Dict]]]:
"""
Return the list of contract -> function name -> List(source mapping of the assert))
Args:
slither:
Returns:
"""
ret: Dict[str, Dict[str, List[Dict]]] = {}
for contract in slither.contracts:
functions_using_assert = []
functions_using_assert: Dict[str, List[Dict]] = defaultdict(list)
for f in contract.functions_entry_points:
for v in f.all_solidity_calls():
if v == SolidityFunction("assert(bool)"):
functions_using_assert.append(_get_name(f))
break
for node in f.all_nodes():
if SolidityFunction("assert(bool)") in node.solidity_calls and node.source_mapping:
func_name = _get_name(f)
functions_using_assert[func_name].append(node.source_mapping.to_json())
if functions_using_assert:
ret[contract.name] = functions_using_assert
return ret

@ -385,6 +385,70 @@ def integrate_value_gas(result: List[Operation]) -> List[Operation]:
###################################################################################
def get_declared_param_names(
ins: Union[
NewStructure,
NewContract,
InternalCall,
LibraryCall,
HighLevelCall,
InternalDynamicCall,
EventCall,
]
) -> Optional[List[str]]:
"""
Given a call operation, return the list of parameter names, in the order
listed in the function declaration.
#### Parameters
ins -
The call instruction
#### Possible Returns
List[str] -
A list of the parameters in declaration order
None -
Workaround: Unable to obtain list of parameters in declaration order
"""
if isinstance(ins, NewStructure):
return [x.name for x in ins.structure.elems_ordered if not isinstance(x.type, MappingType)]
if isinstance(ins, (InternalCall, LibraryCall, HighLevelCall)):
if isinstance(ins.function, Function):
return [p.name for p in ins.function.parameters]
return None
if isinstance(ins, InternalDynamicCall):
return [p.name for p in ins.function_type.params]
assert isinstance(ins, (EventCall, NewContract))
return None
def reorder_arguments(
args: List[Variable], call_names: List[str], decl_names: List[str]
) -> List[Variable]:
"""
Reorder named struct constructor arguments so that they match struct declaration ordering rather
than call ordering
E.g. for `struct S { int x; int y; }` we reorder `S({y : 2, x : 3})` to `S(3, 2)`
#### Parameters
args -
Arguments to constructor call, in call order
names -
Parameter names in call order
decl_names -
Parameter names in declaration order
#### Returns
Reordered arguments to constructor call, now in declaration order
"""
assert len(args) == len(call_names)
assert len(call_names) == len(decl_names)
args_ret = []
for n in decl_names:
ind = call_names.index(n)
args_ret.append(args[ind])
return args_ret
def propagate_type_and_convert_call(result: List[Operation], node: "Node") -> List[Operation]:
"""
Propagate the types variables and convert tmp call to real call operation
@ -434,6 +498,23 @@ def propagate_type_and_convert_call(result: List[Operation], node: "Node") -> Li
if ins.call_id in calls_gas and isinstance(ins, (HighLevelCall, InternalDynamicCall)):
ins.call_gas = calls_gas[ins.call_id]
if isinstance(ins, Call) and (ins.names is not None):
assert isinstance(
ins,
(
NewStructure,
NewContract,
InternalCall,
LibraryCall,
HighLevelCall,
InternalDynamicCall,
EventCall,
),
)
decl_param_names = get_declared_param_names(ins)
if decl_param_names is not None:
call_data = reorder_arguments(call_data, ins.names, decl_param_names)
if isinstance(ins, (Call, NewContract, NewStructure)):
# We might have stored some arguments for libraries
if ins.arguments:
@ -855,7 +936,7 @@ def extract_tmp_call(ins: TmpCall, contract: Optional[Contract]) -> Union[Call,
if isinstance(ins.ori.variable_left, Contract):
st = ins.ori.variable_left.get_structure_from_name(ins.ori.variable_right)
if st:
op = NewStructure(st, ins.lvalue)
op = NewStructure(st, ins.lvalue, names=ins.names)
op.set_expression(ins.expression)
op.call_id = ins.call_id
return op
@ -892,6 +973,7 @@ def extract_tmp_call(ins: TmpCall, contract: Optional[Contract]) -> Union[Call,
ins.nbr_arguments,
ins.lvalue,
ins.type_call,
names=ins.names,
)
libcall.set_expression(ins.expression)
libcall.call_id = ins.call_id
@ -950,6 +1032,7 @@ def extract_tmp_call(ins: TmpCall, contract: Optional[Contract]) -> Union[Call,
len(lib_func.parameters),
ins.lvalue,
"d",
names=ins.names,
)
lib_call.set_expression(ins.expression)
lib_call.set_node(ins.node)
@ -1031,6 +1114,7 @@ def extract_tmp_call(ins: TmpCall, contract: Optional[Contract]) -> Union[Call,
ins.nbr_arguments,
ins.lvalue,
ins.type_call,
names=ins.names,
)
msgcall.call_id = ins.call_id
@ -1082,7 +1166,7 @@ def extract_tmp_call(ins: TmpCall, contract: Optional[Contract]) -> Union[Call,
return n
if isinstance(ins.called, Structure):
op = NewStructure(ins.called, ins.lvalue)
op = NewStructure(ins.called, ins.lvalue, names=ins.names)
op.set_expression(ins.expression)
op.call_id = ins.call_id
op.set_expression(ins.expression)
@ -1106,7 +1190,7 @@ def extract_tmp_call(ins: TmpCall, contract: Optional[Contract]) -> Union[Call,
if len(ins.called.constructor.parameters) != ins.nbr_arguments:
return Nop()
internalcall = InternalCall(
ins.called.constructor, ins.nbr_arguments, ins.lvalue, ins.type_call
ins.called.constructor, ins.nbr_arguments, ins.lvalue, ins.type_call, ins.names
)
internalcall.call_id = ins.call_id
internalcall.set_expression(ins.expression)
@ -1440,6 +1524,7 @@ def look_for_library_or_top_level(
ir.nbr_arguments,
ir.lvalue,
ir.type_call,
names=ir.names,
)
lib_call.set_expression(ir.expression)
lib_call.set_node(ir.node)
@ -1857,7 +1942,7 @@ def convert_constant_types(irs: List[Operation]) -> None:
if isinstance(ir.lvalue.type.type, ElementaryType):
if ir.lvalue.type.type.type in ElementaryTypeInt:
for r in ir.read:
if r.type.type not in ElementaryTypeInt:
if r.type.type.type not in ElementaryTypeInt:
r.set_type(ElementaryType(ir.lvalue.type.type.type))
was_changed = True

@ -6,9 +6,25 @@ from slither.slithir.operations.operation import Operation
class Call(Operation):
def __init__(self) -> None:
def __init__(self, names: Optional[List[str]] = None) -> None:
"""
#### Parameters
names -
For calls of the form f({argName1 : arg1, ...}), the names of parameters listed in call order.
Otherwise, None.
"""
assert (names is None) or isinstance(names, list)
super().__init__()
self._arguments: List[Variable] = []
self._names = names
@property
def names(self) -> Optional[List[str]]:
"""
For calls of the form f({argName1 : arg1, ...}), the names of parameters listed in call order.
Otherwise, None.
"""
return self._names
@property
def arguments(self) -> List[Variable]:

@ -28,11 +28,18 @@ class HighLevelCall(Call, OperationWithLValue):
nbr_arguments: int,
result: Optional[Union[TemporaryVariable, TupleVariable, TemporaryVariableSSA]],
type_call: str,
names: Optional[List[str]] = None,
) -> None:
"""
#### Parameters
names -
For calls of the form f({argName1 : arg1, ...}), the names of parameters listed in call order.
Otherwise, None.
"""
assert isinstance(function_name, Constant)
assert is_valid_lvalue(result) or result is None
self._check_destination(destination)
super().__init__()
super().__init__(names=names)
# Contract is only possible for library call, which inherits from highlevelcall
self._destination: Union[Variable, SolidityVariable, Contract] = destination # type: ignore
self._function_name = function_name

@ -44,4 +44,4 @@ class InitArray(OperationWithLValue):
return f"{elem}({elem.type})"
init_values = convert(self.init_values)
return f"{self.lvalue}({self.lvalue.type}) = {init_values}"
return f"{self.lvalue}({self.lvalue.type}) = {init_values}"

@ -20,8 +20,16 @@ class InternalCall(Call, OperationWithLValue): # pylint: disable=too-many-insta
Union[TupleVariableSSA, TemporaryVariableSSA, TupleVariable, TemporaryVariable]
],
type_call: str,
names: Optional[List[str]] = None,
) -> None:
super().__init__()
# pylint: disable=too-many-arguments
"""
#### Parameters
names -
For calls of the form f({argName1 : arg1, ...}), the names of parameters listed in call order.
Otherwise, None.
"""
super().__init__(names=names)
self._contract_name = ""
if isinstance(function, Function):
self._function: Optional[Function] = function

@ -33,4 +33,4 @@ class NewArray(Call, OperationWithLValue):
def __str__(self):
args = [str(a) for a in self.arguments]
lvalue = self.lvalue
return f"{lvalue}{lvalue.type}) = new {self.array_type}({','.join(args)})"
return f"{lvalue}({lvalue.type}) = new {self.array_type}({','.join(args)})"

@ -12,11 +12,20 @@ from slither.slithir.variables.temporary_ssa import TemporaryVariableSSA
class NewContract(Call, OperationWithLValue): # pylint: disable=too-many-instance-attributes
def __init__(
self, contract_name: Constant, lvalue: Union[TemporaryVariableSSA, TemporaryVariable]
self,
contract_name: Constant,
lvalue: Union[TemporaryVariableSSA, TemporaryVariable],
names: Optional[List[str]] = None,
) -> None:
"""
#### Parameters
names -
For calls of the form f({argName1 : arg1, ...}), the names of parameters listed in call order.
Otherwise, None.
"""
assert isinstance(contract_name, Constant)
assert is_valid_lvalue(lvalue)
super().__init__()
super().__init__(names=names)
self._contract_name = contract_name
# todo create analyze to add the contract instance
self._lvalue = lvalue

@ -1,4 +1,4 @@
from typing import List, Union
from typing import List, Optional, Union
from slither.slithir.operations.call import Call
from slither.slithir.operations.lvalue import OperationWithLValue
@ -17,8 +17,15 @@ class NewStructure(Call, OperationWithLValue):
self,
structure: StructureContract,
lvalue: Union[TemporaryVariableSSA, TemporaryVariable],
names: Optional[List[str]] = None,
) -> None:
super().__init__()
"""
#### Parameters
names -
For calls of the form f({argName1 : arg1, ...}), the names of parameters listed in call order.
Otherwise, None.
"""
super().__init__(names=names)
assert isinstance(structure, Structure)
assert is_valid_lvalue(lvalue)
self._structure = structure

@ -5,7 +5,6 @@ from enum import Enum
from slither.slithir.operations.lvalue import OperationWithLValue
from slither.slithir.utils.utils import is_valid_lvalue, is_valid_rvalue
from slither.slithir.exceptions import SlithIRError
from slither.core.expressions.unary_operation import UnaryOperationType
from slither.core.variables.local_variable import LocalVariable
from slither.slithir.variables.constant import Constant
from slither.slithir.variables.local_variable import LocalIRVariable
@ -35,7 +34,7 @@ class Unary(OperationWithLValue):
self,
result: Union[TemporaryVariableSSA, TemporaryVariable],
variable: Union[Constant, LocalIRVariable, LocalVariable],
operation_type: UnaryOperationType,
operation_type: UnaryType,
) -> None:
assert is_valid_rvalue(variable)
assert is_valid_lvalue(result)
@ -53,7 +52,7 @@ class Unary(OperationWithLValue):
return self._variable
@property
def type(self) -> UnaryOperationType:
def type(self) -> UnaryType:
return self._type
@property

@ -1,4 +1,4 @@
from typing import Optional, Union
from typing import List, Optional, Union
from slither.core.declarations import (
Event,
@ -25,7 +25,15 @@ class TmpCall(OperationWithLValue): # pylint: disable=too-many-instance-attribu
nbr_arguments: int,
result: Union[TupleVariable, TemporaryVariable],
type_call: str,
names: Optional[List[str]] = None,
) -> None:
# pylint: disable=too-many-arguments
"""
#### Parameters
names -
For calls of the form f({argName1 : arg1, ...}), the names of parameters listed in call order.
Otherwise, None.
"""
assert isinstance(
called,
(
@ -42,6 +50,7 @@ class TmpCall(OperationWithLValue): # pylint: disable=too-many-instance-attribu
self._called = called
self._nbr_arguments = nbr_arguments
self._type_call = type_call
self._names = names
self._lvalue = result
self._ori = None #
self._callid = None
@ -49,6 +58,14 @@ class TmpCall(OperationWithLValue): # pylint: disable=too-many-instance-attribu
self._value = None
self._salt = None
@property
def names(self) -> Optional[List[str]]:
"""
For calls of the form f({argName1 : arg1, ...}), the names of parameters listed in call order.
Otherwise, None.
"""
return self._names
@property
def call_value(self):
return self._value

@ -735,12 +735,17 @@ def copy_ir(ir: Operation, *instances) -> Operation:
destination = get_variable(ir, lambda x: x.destination, *instances)
function_name = ir.function_name
nbr_arguments = ir.nbr_arguments
names = ir.names
lvalue = get_variable(ir, lambda x: x.lvalue, *instances)
type_call = ir.type_call
if isinstance(ir, LibraryCall):
new_ir = LibraryCall(destination, function_name, nbr_arguments, lvalue, type_call)
new_ir = LibraryCall(
destination, function_name, nbr_arguments, lvalue, type_call, names=names
)
else:
new_ir = HighLevelCall(destination, function_name, nbr_arguments, lvalue, type_call)
new_ir = HighLevelCall(
destination, function_name, nbr_arguments, lvalue, type_call, names=names
)
new_ir.call_id = ir.call_id
new_ir.call_value = get_variable(ir, lambda x: x.call_value, *instances)
new_ir.call_gas = get_variable(ir, lambda x: x.call_gas, *instances)
@ -761,7 +766,8 @@ def copy_ir(ir: Operation, *instances) -> Operation:
nbr_arguments = ir.nbr_arguments
lvalue = get_variable(ir, lambda x: x.lvalue, *instances)
type_call = ir.type_call
new_ir = InternalCall(function, nbr_arguments, lvalue, type_call)
names = ir.names
new_ir = InternalCall(function, nbr_arguments, lvalue, type_call, names=names)
new_ir.arguments = get_arguments(ir, *instances)
return new_ir
if isinstance(ir, InternalDynamicCall):
@ -811,7 +817,8 @@ def copy_ir(ir: Operation, *instances) -> Operation:
if isinstance(ir, NewStructure):
structure = ir.structure
lvalue = get_variable(ir, lambda x: x.lvalue, *instances)
new_ir = NewStructure(structure, lvalue)
names = ir.names
new_ir = NewStructure(structure, lvalue, names=names)
new_ir.arguments = get_arguments(ir, *instances)
return new_ir
if isinstance(ir, Nop):

@ -319,7 +319,7 @@ class ContractSolc(CallerContextExpression):
ce.set_contract(self._contract)
ce.set_offset(custom_error["src"], self.compilation_unit)
ce_parser = CustomErrorSolc(ce, custom_error, self._slither_parser)
ce_parser = CustomErrorSolc(ce, custom_error, self, self._slither_parser)
self._contract.custom_errors_as_dict[ce.name] = ce
self._custom_errors_parser.append(ce_parser)

@ -1,4 +1,4 @@
from typing import TYPE_CHECKING, Dict
from typing import TYPE_CHECKING, Dict, Optional
from slither.core.declarations.custom_error import CustomError
from slither.core.declarations.custom_error_contract import CustomErrorContract
@ -10,6 +10,7 @@ from slither.solc_parsing.variables.local_variable import LocalVariableSolc
if TYPE_CHECKING:
from slither.solc_parsing.slither_compilation_unit_solc import SlitherCompilationUnitSolc
from slither.core.compilation_unit import SlitherCompilationUnit
from slither.solc_parsing.declarations.contract import ContractSolc
# Part of the code was copied from the function parsing
@ -21,11 +22,13 @@ class CustomErrorSolc(CallerContextExpression):
self,
custom_error: CustomError,
custom_error_data: dict,
contract_parser: Optional["ContractSolc"],
slither_parser: "SlitherCompilationUnitSolc",
) -> None:
self._slither_parser: "SlitherCompilationUnitSolc" = slither_parser
self._custom_error = custom_error
custom_error.name = custom_error_data["name"]
self._contract_parser = contract_parser
self._params_was_analyzed = False
if not self._slither_parser.is_compact_ast:
@ -56,6 +59,10 @@ class CustomErrorSolc(CallerContextExpression):
if params:
self._parse_params(params)
@property
def contract_parser(self) -> Optional["ContractSolc"]:
return self._contract_parser
@property
def is_compact_ast(self) -> bool:
return self._slither_parser.is_compact_ast

@ -179,7 +179,8 @@ def parse_call(
sp = SuperCallExpression(called, arguments, type_return)
sp.set_offset(expression["src"], caller_context.compilation_unit)
return sp
call_expression = CallExpression(called, arguments, type_return)
names = expression["names"] if "names" in expression and len(expression["names"]) > 0 else None
call_expression = CallExpression(called, arguments, type_return, names=names)
call_expression.set_offset(src, caller_context.compilation_unit)
# Only available if the syntax {gas:, value:} was used

@ -3,6 +3,8 @@ from typing import TYPE_CHECKING, Optional, Union, List, Tuple
from slither.core.declarations import Event, Enum, Structure
from slither.core.declarations.contract import Contract
from slither.core.declarations.custom_error import CustomError
from slither.core.declarations.custom_error_contract import CustomErrorContract
from slither.core.declarations.custom_error_top_level import CustomErrorTopLevel
from slither.core.declarations.function import Function
from slither.core.declarations.function_contract import FunctionContract
from slither.core.declarations.function_top_level import FunctionTopLevel
@ -246,6 +248,7 @@ def _find_in_contract(
return None
# pylint: disable=too-many-statements
def _find_variable_init(
caller_context: CallerContextExpression,
) -> Tuple[List[Contract], List["Function"], FileScope,]:
@ -253,6 +256,7 @@ def _find_variable_init(
from slither.solc_parsing.declarations.function import FunctionSolc
from slither.solc_parsing.declarations.structure_top_level import StructureTopLevelSolc
from slither.solc_parsing.variables.top_level_variable import TopLevelVariableSolc
from slither.solc_parsing.declarations.custom_error import CustomErrorSolc
direct_contracts: List[Contract]
direct_functions_parser: List[Function]
@ -295,6 +299,24 @@ def _find_variable_init(
direct_contracts = []
direct_functions_parser = []
scope = caller_context.underlying_variable.file_scope
elif isinstance(caller_context, CustomErrorSolc):
if caller_context.contract_parser:
direct_contracts = [caller_context.contract_parser.underlying_contract]
direct_functions_parser = [
f.underlying_function
for f in caller_context.contract_parser.functions_parser
+ caller_context.contract_parser.modifiers_parser
]
else:
# Top level custom error
direct_contracts = []
direct_functions_parser = []
underlying_custom_error = caller_context.underlying_custom_error
if isinstance(underlying_custom_error, CustomErrorTopLevel):
scope = underlying_custom_error.file_scope
else:
assert isinstance(underlying_custom_error, CustomErrorContract)
scope = underlying_custom_error.contract.file_scope
else:
raise SlitherError(
f"{type(caller_context)} ({caller_context} is not valid for find_variable"
@ -343,6 +365,7 @@ def find_variable(
"""
from slither.solc_parsing.declarations.function import FunctionSolc
from slither.solc_parsing.declarations.contract import ContractSolc
from slither.solc_parsing.declarations.custom_error import CustomErrorSolc
# variable are looked from the contract declarer
# functions can be shadowed, but are looked from the contract instance, rather than the contract declarer
@ -394,6 +417,15 @@ def find_variable(
contract_declarer = underlying_func.contract_declarer
else:
assert isinstance(underlying_func, FunctionTopLevel)
elif isinstance(caller_context, CustomErrorSolc):
underlying_custom_error = caller_context.underlying_custom_error
if isinstance(underlying_custom_error, CustomErrorContract):
contract = underlying_custom_error.contract
# We check for contract variables here because _find_in_contract
# will return since in this case the contract_declarer is None
for var in contract.variables:
if var_name == var.name:
return var, False
ret = _find_in_contract(var_name, contract, contract_declarer, is_super, is_identifier_path)
if ret:

@ -326,7 +326,7 @@ class SlitherCompilationUnitSolc(CallerContextExpression):
custom_error = CustomErrorTopLevel(self._compilation_unit, scope)
custom_error.set_offset(top_level_data["src"], self._compilation_unit)
custom_error_parser = CustomErrorSolc(custom_error, top_level_data, self)
custom_error_parser = CustomErrorSolc(custom_error, top_level_data, None, self)
scope.custom_errors.add(custom_error)
self._compilation_unit.custom_errors.append(custom_error)
self._custom_error_parser.append(custom_error_parser)

@ -50,6 +50,7 @@ from slither.slithir.operations import (
Member,
TypeConversion,
Unary,
UnaryType,
Unpack,
Return,
SolidityCall,
@ -109,6 +110,13 @@ _binary_to_binary = {
BinaryOperationType.OROR: BinaryType.OROR,
}
_unary_to_unary = {
UnaryOperationType.BANG: UnaryType.BANG,
UnaryOperationType.TILD: UnaryType.TILD,
}
_signed_to_unsigned = {
BinaryOperationType.DIVISION_SIGNED: BinaryType.DIVISION,
BinaryOperationType.MODULO_SIGNED: BinaryType.MODULO,
@ -304,7 +312,9 @@ class ExpressionToSlithIR(ExpressionVisitor):
val = TupleVariable(self._node)
else:
val = TemporaryVariable(self._node)
internal_call = InternalCall(called, len(args), val, expression.type_call)
internal_call = InternalCall(
called, len(args), val, expression.type_call, names=expression.names
)
internal_call.set_expression(expression)
self._result.append(internal_call)
set_val(expression, val)
@ -373,7 +383,9 @@ class ExpressionToSlithIR(ExpressionVisitor):
else:
val = TemporaryVariable(self._node)
message_call = TmpCall(called, len(args), val, expression.type_call)
message_call = TmpCall(
called, len(args), val, expression.type_call, names=expression.names
)
message_call.set_expression(expression)
# Gas/value are only accessible here if the syntax {gas: , value: }
# Is used over .gas().value()
@ -585,7 +597,7 @@ class ExpressionToSlithIR(ExpressionVisitor):
operation: Operation
if expression.type in [UnaryOperationType.BANG, UnaryOperationType.TILD]:
lvalue = TemporaryVariable(self._node)
operation = Unary(lvalue, value, expression.type)
operation = Unary(lvalue, value, _unary_to_unary[expression.type])
operation.set_expression(expression)
self._result.append(operation)
set_val(expression, lvalue)

@ -8,13 +8,19 @@ struct St{
uint v;
}
uint256 constant MAX = 5;
error ErrorSimple();
error ErrorWithArgs(uint, uint);
error ErrorWithStruct(St s);
error ErrorWithConst(uint256[MAX]);
contract VendingMachine is I {
uint256 constant CMAX = 10;
error CErrorWithConst(uint256[CMAX]);
function err0() public {
revert ErrorSimple();
}
@ -32,6 +38,14 @@ contract VendingMachine is I {
function err4() public {
revert ErrorWithEnum(SomeEnum.ONE);
}
function err5(uint256[MAX] calldata a) public {
revert ErrorWithConst(a);
}
function err6(uint256[CMAX] calldata a) public {
revert CErrorWithConst(a);
}
}
contract A{

@ -5,7 +5,9 @@
"err1()": "digraph{\n0[label=\"Node Type: ENTRY_POINT 0\n\"];\n0->1;\n1[label=\"Node Type: NEW VARIABLE 1\n\"];\n1->2;\n2[label=\"Node Type: EXPRESSION 2\n\"];\n}\n",
"err2()": "digraph{\n0[label=\"Node Type: ENTRY_POINT 0\n\"];\n0->1;\n1[label=\"Node Type: EXPRESSION 1\n\"];\n1->2;\n2[label=\"Node Type: EXPRESSION 2\n\"];\n}\n",
"err3()": "digraph{\n0[label=\"Node Type: ENTRY_POINT 0\n\"];\n0->1;\n1[label=\"Node Type: EXPRESSION 1\n\"];\n}\n",
"err4()": "digraph{\n0[label=\"Node Type: ENTRY_POINT 0\n\"];\n0->1;\n1[label=\"Node Type: EXPRESSION 1\n\"];\n}\n"
"err4()": "digraph{\n0[label=\"Node Type: ENTRY_POINT 0\n\"];\n0->1;\n1[label=\"Node Type: EXPRESSION 1\n\"];\n}\n",
"err5(uint256[5])": "digraph{\n0[label=\"Node Type: ENTRY_POINT 0\n\"];\n0->1;\n1[label=\"Node Type: EXPRESSION 1\n\"];\n}\n",
"err6(uint256[10])": "digraph{\n0[label=\"Node Type: ENTRY_POINT 0\n\"];\n0->1;\n1[label=\"Node Type: EXPRESSION 1\n\"];\n}\n"
},
"A": {
"f()": "digraph{\n0[label=\"Node Type: ENTRY_POINT 0\n\"];\n0->1;\n1[label=\"Node Type: EXPRESSION 1\n\"];\n}\n"

@ -5,7 +5,9 @@
"err1()": "digraph{\n0[label=\"Node Type: ENTRY_POINT 0\n\"];\n0->1;\n1[label=\"Node Type: NEW VARIABLE 1\n\"];\n1->2;\n2[label=\"Node Type: EXPRESSION 2\n\"];\n}\n",
"err2()": "digraph{\n0[label=\"Node Type: ENTRY_POINT 0\n\"];\n0->1;\n1[label=\"Node Type: EXPRESSION 1\n\"];\n1->2;\n2[label=\"Node Type: EXPRESSION 2\n\"];\n}\n",
"err3()": "digraph{\n0[label=\"Node Type: ENTRY_POINT 0\n\"];\n0->1;\n1[label=\"Node Type: EXPRESSION 1\n\"];\n}\n",
"err4()": "digraph{\n0[label=\"Node Type: ENTRY_POINT 0\n\"];\n0->1;\n1[label=\"Node Type: EXPRESSION 1\n\"];\n}\n"
"err4()": "digraph{\n0[label=\"Node Type: ENTRY_POINT 0\n\"];\n0->1;\n1[label=\"Node Type: EXPRESSION 1\n\"];\n}\n",
"err5(uint256[5])": "digraph{\n0[label=\"Node Type: ENTRY_POINT 0\n\"];\n0->1;\n1[label=\"Node Type: EXPRESSION 1\n\"];\n}\n",
"err6(uint256[10])": "digraph{\n0[label=\"Node Type: ENTRY_POINT 0\n\"];\n0->1;\n1[label=\"Node Type: EXPRESSION 1\n\"];\n}\n"
},
"A": {
"f()": "digraph{\n0[label=\"Node Type: ENTRY_POINT 0\n\"];\n0->1;\n1[label=\"Node Type: EXPRESSION 1\n\"];\n}\n"

@ -5,7 +5,9 @@
"err1()": "digraph{\n0[label=\"Node Type: ENTRY_POINT 0\n\"];\n0->1;\n1[label=\"Node Type: NEW VARIABLE 1\n\"];\n1->2;\n2[label=\"Node Type: EXPRESSION 2\n\"];\n}\n",
"err2()": "digraph{\n0[label=\"Node Type: ENTRY_POINT 0\n\"];\n0->1;\n1[label=\"Node Type: EXPRESSION 1\n\"];\n1->2;\n2[label=\"Node Type: EXPRESSION 2\n\"];\n}\n",
"err3()": "digraph{\n0[label=\"Node Type: ENTRY_POINT 0\n\"];\n0->1;\n1[label=\"Node Type: EXPRESSION 1\n\"];\n}\n",
"err4()": "digraph{\n0[label=\"Node Type: ENTRY_POINT 0\n\"];\n0->1;\n1[label=\"Node Type: EXPRESSION 1\n\"];\n}\n"
"err4()": "digraph{\n0[label=\"Node Type: ENTRY_POINT 0\n\"];\n0->1;\n1[label=\"Node Type: EXPRESSION 1\n\"];\n}\n",
"err5(uint256[5])": "digraph{\n0[label=\"Node Type: ENTRY_POINT 0\n\"];\n0->1;\n1[label=\"Node Type: EXPRESSION 1\n\"];\n}\n",
"err6(uint256[10])": "digraph{\n0[label=\"Node Type: ENTRY_POINT 0\n\"];\n0->1;\n1[label=\"Node Type: EXPRESSION 1\n\"];\n}\n"
},
"A": {
"f()": "digraph{\n0[label=\"Node Type: ENTRY_POINT 0\n\"];\n0->1;\n1[label=\"Node Type: EXPRESSION 1\n\"];\n}\n"

@ -5,7 +5,9 @@
"err1()": "digraph{\n0[label=\"Node Type: ENTRY_POINT 0\n\"];\n0->1;\n1[label=\"Node Type: NEW VARIABLE 1\n\"];\n1->2;\n2[label=\"Node Type: EXPRESSION 2\n\"];\n}\n",
"err2()": "digraph{\n0[label=\"Node Type: ENTRY_POINT 0\n\"];\n0->1;\n1[label=\"Node Type: EXPRESSION 1\n\"];\n1->2;\n2[label=\"Node Type: EXPRESSION 2\n\"];\n}\n",
"err3()": "digraph{\n0[label=\"Node Type: ENTRY_POINT 0\n\"];\n0->1;\n1[label=\"Node Type: EXPRESSION 1\n\"];\n}\n",
"err4()": "digraph{\n0[label=\"Node Type: ENTRY_POINT 0\n\"];\n0->1;\n1[label=\"Node Type: EXPRESSION 1\n\"];\n}\n"
"err4()": "digraph{\n0[label=\"Node Type: ENTRY_POINT 0\n\"];\n0->1;\n1[label=\"Node Type: EXPRESSION 1\n\"];\n}\n",
"err5(uint256[5])": "digraph{\n0[label=\"Node Type: ENTRY_POINT 0\n\"];\n0->1;\n1[label=\"Node Type: EXPRESSION 1\n\"];\n}\n",
"err6(uint256[10])": "digraph{\n0[label=\"Node Type: ENTRY_POINT 0\n\"];\n0->1;\n1[label=\"Node Type: EXPRESSION 1\n\"];\n}\n"
},
"A": {
"f()": "digraph{\n0[label=\"Node Type: ENTRY_POINT 0\n\"];\n0->1;\n1[label=\"Node Type: EXPRESSION 1\n\"];\n}\n"

@ -5,7 +5,9 @@
"err1()": "digraph{\n0[label=\"Node Type: ENTRY_POINT 0\n\"];\n0->1;\n1[label=\"Node Type: NEW VARIABLE 1\n\"];\n1->2;\n2[label=\"Node Type: EXPRESSION 2\n\"];\n}\n",
"err2()": "digraph{\n0[label=\"Node Type: ENTRY_POINT 0\n\"];\n0->1;\n1[label=\"Node Type: EXPRESSION 1\n\"];\n1->2;\n2[label=\"Node Type: EXPRESSION 2\n\"];\n}\n",
"err3()": "digraph{\n0[label=\"Node Type: ENTRY_POINT 0\n\"];\n0->1;\n1[label=\"Node Type: EXPRESSION 1\n\"];\n}\n",
"err4()": "digraph{\n0[label=\"Node Type: ENTRY_POINT 0\n\"];\n0->1;\n1[label=\"Node Type: EXPRESSION 1\n\"];\n}\n"
"err4()": "digraph{\n0[label=\"Node Type: ENTRY_POINT 0\n\"];\n0->1;\n1[label=\"Node Type: EXPRESSION 1\n\"];\n}\n",
"err5(uint256[5])": "digraph{\n0[label=\"Node Type: ENTRY_POINT 0\n\"];\n0->1;\n1[label=\"Node Type: EXPRESSION 1\n\"];\n}\n",
"err6(uint256[10])": "digraph{\n0[label=\"Node Type: ENTRY_POINT 0\n\"];\n0->1;\n1[label=\"Node Type: EXPRESSION 1\n\"];\n}\n"
},
"A": {
"f()": "digraph{\n0[label=\"Node Type: ENTRY_POINT 0\n\"];\n0->1;\n1[label=\"Node Type: EXPRESSION 1\n\"];\n}\n"

@ -5,7 +5,9 @@
"err1()": "digraph{\n0[label=\"Node Type: ENTRY_POINT 0\n\"];\n0->1;\n1[label=\"Node Type: NEW VARIABLE 1\n\"];\n1->2;\n2[label=\"Node Type: EXPRESSION 2\n\"];\n}\n",
"err2()": "digraph{\n0[label=\"Node Type: ENTRY_POINT 0\n\"];\n0->1;\n1[label=\"Node Type: EXPRESSION 1\n\"];\n1->2;\n2[label=\"Node Type: EXPRESSION 2\n\"];\n}\n",
"err3()": "digraph{\n0[label=\"Node Type: ENTRY_POINT 0\n\"];\n0->1;\n1[label=\"Node Type: EXPRESSION 1\n\"];\n}\n",
"err4()": "digraph{\n0[label=\"Node Type: ENTRY_POINT 0\n\"];\n0->1;\n1[label=\"Node Type: EXPRESSION 1\n\"];\n}\n"
"err4()": "digraph{\n0[label=\"Node Type: ENTRY_POINT 0\n\"];\n0->1;\n1[label=\"Node Type: EXPRESSION 1\n\"];\n}\n",
"err5(uint256[5])": "digraph{\n0[label=\"Node Type: ENTRY_POINT 0\n\"];\n0->1;\n1[label=\"Node Type: EXPRESSION 1\n\"];\n}\n",
"err6(uint256[10])": "digraph{\n0[label=\"Node Type: ENTRY_POINT 0\n\"];\n0->1;\n1[label=\"Node Type: EXPRESSION 1\n\"];\n}\n"
},
"A": {
"f()": "digraph{\n0[label=\"Node Type: ENTRY_POINT 0\n\"];\n0->1;\n1[label=\"Node Type: EXPRESSION 1\n\"];\n}\n"

@ -5,7 +5,9 @@
"err1()": "digraph{\n0[label=\"Node Type: ENTRY_POINT 0\n\"];\n0->1;\n1[label=\"Node Type: NEW VARIABLE 1\n\"];\n1->2;\n2[label=\"Node Type: EXPRESSION 2\n\"];\n}\n",
"err2()": "digraph{\n0[label=\"Node Type: ENTRY_POINT 0\n\"];\n0->1;\n1[label=\"Node Type: EXPRESSION 1\n\"];\n1->2;\n2[label=\"Node Type: EXPRESSION 2\n\"];\n}\n",
"err3()": "digraph{\n0[label=\"Node Type: ENTRY_POINT 0\n\"];\n0->1;\n1[label=\"Node Type: EXPRESSION 1\n\"];\n}\n",
"err4()": "digraph{\n0[label=\"Node Type: ENTRY_POINT 0\n\"];\n0->1;\n1[label=\"Node Type: EXPRESSION 1\n\"];\n}\n"
"err4()": "digraph{\n0[label=\"Node Type: ENTRY_POINT 0\n\"];\n0->1;\n1[label=\"Node Type: EXPRESSION 1\n\"];\n}\n",
"err5(uint256[5])": "digraph{\n0[label=\"Node Type: ENTRY_POINT 0\n\"];\n0->1;\n1[label=\"Node Type: EXPRESSION 1\n\"];\n}\n",
"err6(uint256[10])": "digraph{\n0[label=\"Node Type: ENTRY_POINT 0\n\"];\n0->1;\n1[label=\"Node Type: EXPRESSION 1\n\"];\n}\n"
},
"A": {
"f()": "digraph{\n0[label=\"Node Type: ENTRY_POINT 0\n\"];\n0->1;\n1[label=\"Node Type: EXPRESSION 1\n\"];\n}\n"

@ -5,7 +5,9 @@
"err1()": "digraph{\n0[label=\"Node Type: ENTRY_POINT 0\n\"];\n0->1;\n1[label=\"Node Type: NEW VARIABLE 1\n\"];\n1->2;\n2[label=\"Node Type: EXPRESSION 2\n\"];\n}\n",
"err2()": "digraph{\n0[label=\"Node Type: ENTRY_POINT 0\n\"];\n0->1;\n1[label=\"Node Type: EXPRESSION 1\n\"];\n1->2;\n2[label=\"Node Type: EXPRESSION 2\n\"];\n}\n",
"err3()": "digraph{\n0[label=\"Node Type: ENTRY_POINT 0\n\"];\n0->1;\n1[label=\"Node Type: EXPRESSION 1\n\"];\n}\n",
"err4()": "digraph{\n0[label=\"Node Type: ENTRY_POINT 0\n\"];\n0->1;\n1[label=\"Node Type: EXPRESSION 1\n\"];\n}\n"
"err4()": "digraph{\n0[label=\"Node Type: ENTRY_POINT 0\n\"];\n0->1;\n1[label=\"Node Type: EXPRESSION 1\n\"];\n}\n",
"err5(uint256[5])": "digraph{\n0[label=\"Node Type: ENTRY_POINT 0\n\"];\n0->1;\n1[label=\"Node Type: EXPRESSION 1\n\"];\n}\n",
"err6(uint256[10])": "digraph{\n0[label=\"Node Type: ENTRY_POINT 0\n\"];\n0->1;\n1[label=\"Node Type: EXPRESSION 1\n\"];\n}\n"
},
"A": {
"f()": "digraph{\n0[label=\"Node Type: ENTRY_POINT 0\n\"];\n0->1;\n1[label=\"Node Type: EXPRESSION 1\n\"];\n}\n"

@ -5,7 +5,9 @@
"err1()": "digraph{\n0[label=\"Node Type: ENTRY_POINT 0\n\"];\n0->1;\n1[label=\"Node Type: NEW VARIABLE 1\n\"];\n1->2;\n2[label=\"Node Type: EXPRESSION 2\n\"];\n}\n",
"err2()": "digraph{\n0[label=\"Node Type: ENTRY_POINT 0\n\"];\n0->1;\n1[label=\"Node Type: EXPRESSION 1\n\"];\n1->2;\n2[label=\"Node Type: EXPRESSION 2\n\"];\n}\n",
"err3()": "digraph{\n0[label=\"Node Type: ENTRY_POINT 0\n\"];\n0->1;\n1[label=\"Node Type: EXPRESSION 1\n\"];\n}\n",
"err4()": "digraph{\n0[label=\"Node Type: ENTRY_POINT 0\n\"];\n0->1;\n1[label=\"Node Type: EXPRESSION 1\n\"];\n}\n"
"err4()": "digraph{\n0[label=\"Node Type: ENTRY_POINT 0\n\"];\n0->1;\n1[label=\"Node Type: EXPRESSION 1\n\"];\n}\n",
"err5(uint256[5])": "digraph{\n0[label=\"Node Type: ENTRY_POINT 0\n\"];\n0->1;\n1[label=\"Node Type: EXPRESSION 1\n\"];\n}\n",
"err6(uint256[10])": "digraph{\n0[label=\"Node Type: ENTRY_POINT 0\n\"];\n0->1;\n1[label=\"Node Type: EXPRESSION 1\n\"];\n}\n"
},
"A": {
"f()": "digraph{\n0[label=\"Node Type: ENTRY_POINT 0\n\"];\n0->1;\n1[label=\"Node Type: EXPRESSION 1\n\"];\n}\n"

@ -5,7 +5,9 @@
"err1()": "digraph{\n0[label=\"Node Type: ENTRY_POINT 0\n\"];\n0->1;\n1[label=\"Node Type: NEW VARIABLE 1\n\"];\n1->2;\n2[label=\"Node Type: EXPRESSION 2\n\"];\n}\n",
"err2()": "digraph{\n0[label=\"Node Type: ENTRY_POINT 0\n\"];\n0->1;\n1[label=\"Node Type: EXPRESSION 1\n\"];\n1->2;\n2[label=\"Node Type: EXPRESSION 2\n\"];\n}\n",
"err3()": "digraph{\n0[label=\"Node Type: ENTRY_POINT 0\n\"];\n0->1;\n1[label=\"Node Type: EXPRESSION 1\n\"];\n}\n",
"err4()": "digraph{\n0[label=\"Node Type: ENTRY_POINT 0\n\"];\n0->1;\n1[label=\"Node Type: EXPRESSION 1\n\"];\n}\n"
"err4()": "digraph{\n0[label=\"Node Type: ENTRY_POINT 0\n\"];\n0->1;\n1[label=\"Node Type: EXPRESSION 1\n\"];\n}\n",
"err5(uint256[5])": "digraph{\n0[label=\"Node Type: ENTRY_POINT 0\n\"];\n0->1;\n1[label=\"Node Type: EXPRESSION 1\n\"];\n}\n",
"err6(uint256[10])": "digraph{\n0[label=\"Node Type: ENTRY_POINT 0\n\"];\n0->1;\n1[label=\"Node Type: EXPRESSION 1\n\"];\n}\n"
},
"A": {
"f()": "digraph{\n0[label=\"Node Type: ENTRY_POINT 0\n\"];\n0->1;\n1[label=\"Node Type: EXPRESSION 1\n\"];\n}\n"

@ -5,7 +5,9 @@
"err1()": "digraph{\n0[label=\"Node Type: ENTRY_POINT 0\n\"];\n0->1;\n1[label=\"Node Type: NEW VARIABLE 1\n\"];\n1->2;\n2[label=\"Node Type: EXPRESSION 2\n\"];\n}\n",
"err2()": "digraph{\n0[label=\"Node Type: ENTRY_POINT 0\n\"];\n0->1;\n1[label=\"Node Type: EXPRESSION 1\n\"];\n1->2;\n2[label=\"Node Type: EXPRESSION 2\n\"];\n}\n",
"err3()": "digraph{\n0[label=\"Node Type: ENTRY_POINT 0\n\"];\n0->1;\n1[label=\"Node Type: EXPRESSION 1\n\"];\n}\n",
"err4()": "digraph{\n0[label=\"Node Type: ENTRY_POINT 0\n\"];\n0->1;\n1[label=\"Node Type: EXPRESSION 1\n\"];\n}\n"
"err4()": "digraph{\n0[label=\"Node Type: ENTRY_POINT 0\n\"];\n0->1;\n1[label=\"Node Type: EXPRESSION 1\n\"];\n}\n",
"err5(uint256[5])": "digraph{\n0[label=\"Node Type: ENTRY_POINT 0\n\"];\n0->1;\n1[label=\"Node Type: EXPRESSION 1\n\"];\n}\n",
"err6(uint256[10])": "digraph{\n0[label=\"Node Type: ENTRY_POINT 0\n\"];\n0->1;\n1[label=\"Node Type: EXPRESSION 1\n\"];\n}\n"
},
"A": {
"f()": "digraph{\n0[label=\"Node Type: ENTRY_POINT 0\n\"];\n0->1;\n1[label=\"Node Type: EXPRESSION 1\n\"];\n}\n"

@ -5,7 +5,9 @@
"err1()": "digraph{\n0[label=\"Node Type: ENTRY_POINT 0\n\"];\n0->1;\n1[label=\"Node Type: NEW VARIABLE 1\n\"];\n1->2;\n2[label=\"Node Type: EXPRESSION 2\n\"];\n}\n",
"err2()": "digraph{\n0[label=\"Node Type: ENTRY_POINT 0\n\"];\n0->1;\n1[label=\"Node Type: EXPRESSION 1\n\"];\n1->2;\n2[label=\"Node Type: EXPRESSION 2\n\"];\n}\n",
"err3()": "digraph{\n0[label=\"Node Type: ENTRY_POINT 0\n\"];\n0->1;\n1[label=\"Node Type: EXPRESSION 1\n\"];\n}\n",
"err4()": "digraph{\n0[label=\"Node Type: ENTRY_POINT 0\n\"];\n0->1;\n1[label=\"Node Type: EXPRESSION 1\n\"];\n}\n"
"err4()": "digraph{\n0[label=\"Node Type: ENTRY_POINT 0\n\"];\n0->1;\n1[label=\"Node Type: EXPRESSION 1\n\"];\n}\n",
"err5(uint256[5])": "digraph{\n0[label=\"Node Type: ENTRY_POINT 0\n\"];\n0->1;\n1[label=\"Node Type: EXPRESSION 1\n\"];\n}\n",
"err6(uint256[10])": "digraph{\n0[label=\"Node Type: ENTRY_POINT 0\n\"];\n0->1;\n1[label=\"Node Type: EXPRESSION 1\n\"];\n}\n"
},
"A": {
"f()": "digraph{\n0[label=\"Node Type: ENTRY_POINT 0\n\"];\n0->1;\n1[label=\"Node Type: EXPRESSION 1\n\"];\n}\n"

@ -0,0 +1,65 @@
from pathlib import Path
from slither import Slither
from slither.slithir.operations.internal_call import InternalCall
from slither.slithir.operations.new_structure import NewStructure
from slither.slithir.variables.constant import Constant
TEST_DATA_DIR = Path(__file__).resolve().parent / "test_data"
ARG_REORDER_TEST_ROOT = Path(TEST_DATA_DIR, "argument_reorder")
def test_struct_constructor_reorder(solc_binary_path) -> None:
solc_path = solc_binary_path("0.8.15")
slither = Slither(
Path(ARG_REORDER_TEST_ROOT, "test_struct_constructor.sol").as_posix(), solc=solc_path
)
operations = slither.contracts[0].functions[0].slithir_operations
constructor_calls = [x for x in operations if isinstance(x, NewStructure)]
assert len(constructor_calls) == 2
# Arguments to first call are 2, 3
assert (
isinstance(constructor_calls[0].arguments[0], Constant)
and constructor_calls[0].arguments[0].value == 2
)
assert (
isinstance(constructor_calls[0].arguments[1], Constant)
and constructor_calls[0].arguments[1].value == 3
)
# Arguments to second call are 5, 4 (note the reversed order)
assert (
isinstance(constructor_calls[1].arguments[0], Constant)
and constructor_calls[1].arguments[0].value == 5
)
assert (
isinstance(constructor_calls[1].arguments[1], Constant)
and constructor_calls[1].arguments[1].value == 4
)
def test_internal_call_reorder(solc_binary_path) -> None:
solc_path = solc_binary_path("0.8.15")
slither = Slither(
Path(ARG_REORDER_TEST_ROOT, "test_internal_call_reorder.sol").as_posix(), solc=solc_path
)
operations = slither.contracts[0].functions[1].slithir_operations
internal_calls = [x for x in operations if isinstance(x, InternalCall)]
assert len(internal_calls) == 1
# Arguments to call are 3, true, 5
assert (
isinstance(internal_calls[0].arguments[0], Constant)
and internal_calls[0].arguments[0].value == 3
)
assert (
isinstance(internal_calls[0].arguments[1], Constant)
and internal_calls[0].arguments[1].value is True
)
assert (
isinstance(internal_calls[0].arguments[2], Constant)
and internal_calls[0].arguments[2].value == 5
)

@ -0,0 +1,8 @@
contract InternalCallReorderTest {
function internal_func(uint256 a, bool b, uint256 c) internal {
}
function caller() external {
internal_func({a: 3, c: 5, b: true});
}
}

@ -0,0 +1,11 @@
contract StructConstructorTest {
struct S {
int x;
int y;
}
function test() external {
S memory p = S({x: 2, y: 3});
S memory q = S({y: 4, x: 5});
}
}

@ -11,7 +11,7 @@ from solc_select.solc_select import valid_version as solc_valid_version
from slither import Slither
from slither.core.cfg.node import Node, NodeType
from slither.core.declarations import Function, Contract
from slither.core.solidity_types import ArrayType
from slither.core.solidity_types import ArrayType, ElementaryType
from slither.core.variables.local_variable import LocalVariable
from slither.core.variables.state_variable import StateVariable
from slither.slithir.operations import (
@ -1116,3 +1116,23 @@ def test_issue_1846_ternary_in_ternary(slither_from_source):
assert node.type == NodeType.IF
assert node.son_true.type == NodeType.IF
assert node.son_false.type == NodeType.EXPRESSION
def test_issue_2016(slither_from_source):
source = """
contract Contract {
function test() external {
int[] memory a = new int[](5);
}
}
"""
with slither_from_source(source) as slither:
c = slither.get_contract_from_name("Contract")[0]
f = c.functions[0]
operations = f.slithir_operations
new_op = operations[0]
lvalue = new_op.lvalue
lvalue_type = lvalue.type
assert isinstance(lvalue_type, ArrayType)
assert lvalue_type.type == ElementaryType("int256")
assert lvalue_type.is_dynamic

Loading…
Cancel
Save