Merge branch 'dev' into fix/guard-implicit-conversion-of-literals

pull/2383/head
alpharush 7 months ago committed by GitHub
commit 6bda75df3b
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
  1. 4
      .github/workflows/ci.yml
  2. 2
      .github/workflows/doctor.yml
  3. 40
      .github/workflows/issue-metrics.yml
  4. 2
      .github/workflows/publish.yml
  5. 2
      .github/workflows/test.yml
  6. 23
      slither/__main__.py
  7. 1
      slither/core/declarations/solidity_variables.py
  8. 6
      slither/core/scope/scope.py
  9. 7
      slither/slithir/convert.py
  10. 14
      slither/slithir/operations/new_contract.py
  11. 4
      slither/solc_parsing/declarations/contract.py
  12. 20
      slither/solc_parsing/declarations/event_contract.py
  13. 75
      slither/solc_parsing/declarations/event_top_level.py
  14. 17
      slither/solc_parsing/expressions/expression_parsing.py
  15. 10
      slither/solc_parsing/expressions/find_variable.py
  16. 17
      slither/solc_parsing/slither_compilation_unit_solc.py
  17. 11
      slither/solc_parsing/solidity_types/type_parsing.py
  18. 4
      slither/utils/output.py
  19. 15
      slither/vyper_parsing/expressions/expression_parsing.py
  20. 2
      tests/e2e/solc_parsing/test_ast_parsing.py
  21. 1
      tests/e2e/solc_parsing/test_data/aliasing/MyContract.sol
  22. 8
      tests/e2e/solc_parsing/test_data/aliasing/alias-symbol-NewContract.sol
  23. 9
      tests/e2e/solc_parsing/test_data/aliasing/alias-unit-NewContract.sol
  24. BIN
      tests/e2e/solc_parsing/test_data/compile/aliasing/alias-symbol-NewContract.sol-0.8.19-compact.zip
  25. BIN
      tests/e2e/solc_parsing/test_data/compile/aliasing/alias-unit-NewContract.sol-0.8.19-compact.zip
  26. BIN
      tests/e2e/solc_parsing/test_data/compile/event-top-level.sol-0.8.22-compact.zip
  27. 10
      tests/e2e/solc_parsing/test_data/event-top-level.sol
  28. 6
      tests/e2e/solc_parsing/test_data/expected/aliasing/alias-symbol-NewContract.sol-0.8.19-compact.json
  29. 6
      tests/e2e/solc_parsing/test_data/expected/aliasing/alias-unit-NewContract.sol-0.8.19-compact.json
  30. 3
      tests/e2e/solc_parsing/test_data/expected/event-top-level.sol-0.8.22-compact.json
  31. 24
      tests/e2e/vyper_parsing/snapshots/ast_parsing__vyper_cfgir_builtins_c__0.txt
  32. 8
      tests/e2e/vyper_parsing/snapshots/ast_parsing__vyper_cfgir_builtins_test_builtins__0.txt
  33. 1
      tests/e2e/vyper_parsing/test_data/builtins.vy
  34. 124
      tests/unit/slithir/test_implicit_returns.py

@ -26,7 +26,7 @@ jobs:
fail-fast: false
matrix:
os: ["ubuntu-latest", "windows-2022"]
python: ${{ (github.event_name == 'pull_request' && fromJSON('["3.8", "3.11"]')) || fromJSON('["3.8", "3.9", "3.10", "3.11"]') }}
python: ${{ (github.event_name == 'pull_request' && fromJSON('["3.8", "3.12"]')) || fromJSON('["3.8", "3.9", "3.10", "3.11", "3.12"]') }}
type: ["cli",
"dapp",
"data_dependency",
@ -67,7 +67,7 @@ jobs:
- name: Set up nix
if: matrix.type == 'dapp'
uses: cachix/install-nix-action@v25
uses: cachix/install-nix-action@v26
- name: Set up cachix
if: matrix.type == 'dapp'

@ -23,7 +23,7 @@ jobs:
fail-fast: false
matrix:
os: ["ubuntu-latest", "windows-2022"]
python: ["3.8", "3.9", "3.10", "3.11"]
python: ["3.8", "3.9", "3.10", "3.11", "3.12"]
exclude:
# strange failure
- os: windows-2022

@ -0,0 +1,40 @@
name: Monthly issue metrics
on:
workflow_dispatch:
schedule:
- cron: '3 2 1 * *'
permissions:
issues: write
pull-requests: read
jobs:
build:
name: issue metrics
runs-on: ubuntu-latest
steps:
- name: Get dates for last month
shell: bash
run: |
# Calculate the first day of the previous month
first_day=$(date -d "last month" +%Y-%m-01)
# Calculate the last day of the previous month
last_day=$(date -d "$first_day +1 month -1 day" +%Y-%m-%d)
#Set an environment variable with the date range
echo "$first_day..$last_day"
echo "last_month=$first_day..$last_day" >> "$GITHUB_ENV"
- name: Run issue-metrics tool
uses: github/issue-metrics@v2
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
SEARCH_QUERY: 'repo:crytic/slither is:issue created:${{ env.last_month }} -reason:"not planned" -reason:"duplicate"'
- name: Create issue
uses: peter-evans/create-issue-from-file@v5
with:
title: Monthly issue metrics report
token: ${{ secrets.GITHUB_TOKEN }}
content-filepath: ./issue_metrics.md

@ -44,7 +44,7 @@ jobs:
path: dist/
- name: publish
uses: pypa/gh-action-pypi-publish@v1.8.11
uses: pypa/gh-action-pypi-publish@v1.8.14
- name: sign
uses: sigstore/gh-action-sigstore-python@v2.1.1

@ -25,7 +25,7 @@ jobs:
matrix:
os: ["ubuntu-latest", "windows-2022"]
type: ["unit", "integration", "tool"]
python: ${{ (github.event_name == 'pull_request' && fromJSON('["3.8", "3.11"]')) || fromJSON('["3.8", "3.9", "3.10", "3.11"]') }}
python: ${{ (github.event_name == 'pull_request' && fromJSON('["3.8", "3.12"]')) || fromJSON('["3.8", "3.9", "3.10", "3.11", "3.12"]') }}
steps:
- uses: actions/checkout@v4
- name: Set up Python ${{ matrix.python }}

@ -10,9 +10,9 @@ import os
import pstats
import sys
import traceback
from importlib import metadata
from typing import Tuple, Optional, List, Dict, Type, Union, Any, Sequence
from pkg_resources import iter_entry_points, require
from crytic_compile import cryticparser, CryticCompile
from crytic_compile.platform.standard import generate_standard_export
@ -166,19 +166,26 @@ def get_detectors_and_printers() -> Tuple[
printers = [p for p in printers_ if inspect.isclass(p) and issubclass(p, AbstractPrinter)]
# Handle plugins!
for entry_point in iter_entry_points(group="slither_analyzer.plugin", name=None):
if sys.version_info >= (3, 10):
entry_points = metadata.entry_points(group="slither_analyzer.plugin")
else:
from pkg_resources import iter_entry_points # pylint: disable=import-outside-toplevel
entry_points = iter_entry_points(group="slither_analyzer.plugin", name=None)
for entry_point in entry_points:
make_plugin = entry_point.load()
plugin_detectors, plugin_printers = make_plugin()
detector = None
if not all(issubclass(detector, AbstractDetector) for detector in plugin_detectors):
raise Exception(
raise ValueError(
f"Error when loading plugin {entry_point}, {detector} is not a detector"
)
printer = None
if not all(issubclass(printer, AbstractPrinter) for printer in plugin_printers):
raise Exception(f"Error when loading plugin {entry_point}, {printer} is not a printer")
raise ValueError(f"Error when loading plugin {entry_point}, {printer} is not a printer")
# We convert those to lists in case someone returns a tuple
detectors += list(plugin_detectors)
@ -208,7 +215,7 @@ def choose_detectors(
if detector in detectors:
detectors_to_run.append(detectors[detector])
else:
raise Exception(f"Error: {detector} is not a detector")
raise ValueError(f"Error: {detector} is not a detector")
detectors_to_run = sorted(detectors_to_run, key=lambda x: x.IMPACT)
return detectors_to_run
@ -256,7 +263,7 @@ def choose_printers(
if printer in printers:
printers_to_run.append(printers[printer])
else:
raise Exception(f"Error: {printer} is not a printer")
raise ValueError(f"Error: {printer} is not a printer")
return printers_to_run
@ -298,7 +305,7 @@ def parse_args(
parser.add_argument(
"--version",
help="displays the current version",
version=require("slither-analyzer")[0].version,
version=metadata.version("slither-analyzer"),
action="version",
)
@ -648,7 +655,7 @@ def parse_args(
args.json_types = set(args.json_types.split(",")) # type:ignore
for json_type in args.json_types:
if json_type not in JSON_OUTPUT_TYPES:
raise Exception(f'Error: "{json_type}" is not a valid JSON result output type.')
raise ValueError(f'Error: "{json_type}" is not a valid JSON result output type.')
return args

@ -116,6 +116,7 @@ SOLIDITY_FUNCTIONS: Dict[str, List[str]] = {
"_abi_encode()": [],
"slice()": [],
"uint2str()": ["string"],
"send()": [],
}

@ -36,11 +36,11 @@ class FileScope:
# So we simplify the logic and have the scope fields all populated
self.custom_errors: Set[CustomErrorTopLevel] = set()
self.enums: Dict[str, EnumTopLevel] = {}
self.events: Dict[str, EventTopLevel] = {}
# Functions is a list instead of a dict
# Because we parse the function signature later on
# So we simplify the logic and have the scope fields all populated
self.functions: Set[FunctionTopLevel] = set()
self.events: Set[EventTopLevel] = set()
self.using_for_directives: Set[UsingForTopLevel] = set()
self.imports: Set[Import] = set()
self.pragmas: Set[Pragma] = set()
@ -76,8 +76,8 @@ class FileScope:
if not _dict_contain(new_scope.enums, self.enums):
self.enums.update(new_scope.enums)
learn_something = True
if not _dict_contain(new_scope.events, self.events):
self.events.update(new_scope.events)
if not new_scope.events.issubset(self.events):
self.events |= new_scope.events
learn_something = True
if not new_scope.functions.issubset(self.functions):
self.functions |= new_scope.functions

@ -871,9 +871,7 @@ def propagate_types(ir: Operation, node: "Node"): # pylint: disable=too-many-lo
elif isinstance(ir, NewArray):
ir.lvalue.set_type(ir.array_type)
elif isinstance(ir, NewContract):
contract = node.file_scope.get_contract_from_name(ir.contract_name)
assert contract
ir.lvalue.set_type(UserDefinedType(contract))
ir.lvalue.set_type(ir.contract_name)
elif isinstance(ir, NewElementaryType):
ir.lvalue.set_type(ir.type)
elif isinstance(ir, NewStructure):
@ -1164,7 +1162,7 @@ def extract_tmp_call(ins: TmpCall, contract: Optional[Contract]) -> Union[Call,
return n
if isinstance(ins.ori, TmpNewContract):
op = NewContract(Constant(ins.ori.contract_name), ins.lvalue)
op = NewContract(ins.ori.contract_name, ins.lvalue)
op.set_expression(ins.expression)
op.call_id = ins.call_id
if ins.call_value:
@ -1719,6 +1717,7 @@ def convert_type_of_high_and_internal_level_call(
Returns:
Potential new IR
"""
func = None
if isinstance(ir, InternalCall):
candidates: List[Function]

@ -3,6 +3,7 @@ from typing import Optional, Any, List, Union
from slither.core.declarations import Function
from slither.core.declarations.contract import Contract
from slither.core.variables import Variable
from slither.core.solidity_types import UserDefinedType
from slither.slithir.operations import Call, OperationWithLValue
from slither.slithir.utils.utils import is_valid_lvalue
from slither.slithir.variables.constant import Constant
@ -13,7 +14,7 @@ from slither.slithir.variables.temporary_ssa import TemporaryVariableSSA
class NewContract(Call, OperationWithLValue): # pylint: disable=too-many-instance-attributes
def __init__(
self,
contract_name: Constant,
contract_name: UserDefinedType,
lvalue: Union[TemporaryVariableSSA, TemporaryVariable],
names: Optional[List[str]] = None,
) -> None:
@ -23,7 +24,9 @@ class NewContract(Call, OperationWithLValue): # pylint: disable=too-many-instan
For calls of the form f({argName1 : arg1, ...}), the names of parameters listed in call order.
Otherwise, None.
"""
assert isinstance(contract_name, Constant)
assert isinstance(
contract_name.type, Contract
), f"contract_name is {contract_name} of type {type(contract_name)}"
assert is_valid_lvalue(lvalue)
super().__init__(names=names)
self._contract_name = contract_name
@ -58,7 +61,7 @@ class NewContract(Call, OperationWithLValue): # pylint: disable=too-many-instan
self._call_salt = s
@property
def contract_name(self) -> Constant:
def contract_name(self) -> UserDefinedType:
return self._contract_name
@property
@ -69,10 +72,7 @@ class NewContract(Call, OperationWithLValue): # pylint: disable=too-many-instan
@property
def contract_created(self) -> Contract:
contract_name = self.contract_name
contract_instance = self.node.file_scope.get_contract_from_name(contract_name)
assert contract_instance
return contract_instance
return self.contract_name.type
###################################################################################
###################################################################################

@ -16,7 +16,7 @@ from slither.core.solidity_types import ElementaryType, TypeAliasContract
from slither.core.variables.state_variable import StateVariable
from slither.solc_parsing.declarations.caller_context import CallerContextExpression
from slither.solc_parsing.declarations.custom_error import CustomErrorSolc
from slither.solc_parsing.declarations.event import EventSolc
from slither.solc_parsing.declarations.event_contract import EventContractSolc
from slither.solc_parsing.declarations.function import FunctionSolc
from slither.solc_parsing.declarations.modifier import ModifierSolc
from slither.solc_parsing.declarations.structure_contract import StructureContractSolc
@ -760,7 +760,7 @@ class ContractSolc(CallerContextExpression):
event.set_contract(self._contract)
event.set_offset(event_to_parse["src"], self._contract.compilation_unit)
event_parser = EventSolc(event, event_to_parse, self._slither_parser) # type: ignore
event_parser = EventContractSolc(event, event_to_parse, self) # type: ignore
event_parser.analyze() # type: ignore
self._contract.events_as_dict[event.full_name] = event
except (VariableNotFound, KeyError) as e:

@ -1,27 +1,27 @@
"""
Event module
EventContract module
"""
from typing import TYPE_CHECKING, Dict
from slither.core.variables.event_variable import EventVariable
from slither.solc_parsing.variables.event_variable import EventVariableSolc
from slither.core.declarations.event import Event
from slither.core.declarations.event_contract import EventContract
if TYPE_CHECKING:
from slither.solc_parsing.slither_compilation_unit_solc import SlitherCompilationUnitSolc
from slither.solc_parsing.declarations.contract import ContractSolc
class EventSolc:
class EventContractSolc:
"""
Event class
EventContract class
"""
def __init__(
self, event: Event, event_data: Dict, slither_parser: "SlitherCompilationUnitSolc"
self, event: EventContract, event_data: Dict, contract_parser: "ContractSolc"
) -> None:
self._event = event
self._slither_parser = slither_parser
self._contract_parser = contract_parser
if self.is_compact_ast:
self._event.name = event_data["name"]
@ -42,16 +42,16 @@ class EventSolc:
@property
def is_compact_ast(self) -> bool:
return self._slither_parser.is_compact_ast
return self._contract_parser.is_compact_ast
def analyze(self) -> None:
for elem_to_parse in self._elemsNotParsed:
elem = EventVariable()
# Todo: check if the source offset is always here
if "src" in elem_to_parse:
elem.set_offset(elem_to_parse["src"], self._slither_parser.compilation_unit)
elem.set_offset(elem_to_parse["src"], self._contract_parser.compilation_unit)
elem_parser = EventVariableSolc(elem, elem_to_parse)
elem_parser.analyze(self._slither_parser)
elem_parser.analyze(self._contract_parser)
self._event.elems.append(elem)

@ -0,0 +1,75 @@
"""
EventTopLevel module
"""
from typing import TYPE_CHECKING, Dict
from slither.core.declarations.event_top_level import EventTopLevel
from slither.core.variables.event_variable import EventVariable
from slither.core.compilation_unit import SlitherCompilationUnit
from slither.solc_parsing.variables.event_variable import EventVariableSolc
from slither.solc_parsing.declarations.caller_context import CallerContextExpression
if TYPE_CHECKING:
from slither.solc_parsing.slither_compilation_unit_solc import SlitherCompilationUnitSolc
class EventTopLevelSolc(CallerContextExpression):
"""
EventTopLevel class
"""
def __init__(
self, event: EventTopLevel, event_data: Dict, slither_parser: "SlitherCompilationUnitSolc"
) -> None:
self._event = event
self._slither_parser = slither_parser
if self.is_compact_ast:
self._event.name = event_data["name"]
elems = event_data["parameters"]
assert elems["nodeType"] == "ParameterList"
self._elemsNotParsed = elems["parameters"]
else:
self._event.name = event_data["attributes"]["name"]
for elem in event_data["children"]:
# From Solidity 0.6.3 to 0.6.10 (included)
# Comment above a event might be added in the children
# of an event for the legacy ast
if elem["name"] == "ParameterList":
if "children" in elem:
self._elemsNotParsed = elem["children"]
else:
self._elemsNotParsed = []
def analyze(self) -> None:
for elem_to_parse in self._elemsNotParsed:
elem = EventVariable()
# Todo: check if the source offset is always here
if "src" in elem_to_parse:
elem.set_offset(elem_to_parse["src"], self._slither_parser.compilation_unit)
elem_parser = EventVariableSolc(elem, elem_to_parse)
elem_parser.analyze(self)
self._event.elems.append(elem)
self._elemsNotParsed = []
@property
def is_compact_ast(self) -> bool:
return self._slither_parser.is_compact_ast
@property
def compilation_unit(self) -> SlitherCompilationUnit:
return self._slither_parser.compilation_unit
def get_key(self) -> str:
return self._slither_parser.get_key()
@property
def slither_parser(self) -> "SlitherCompilationUnitSolc":
return self._slither_parser
@property
def underlying_event(self) -> EventTopLevel:
return self._event

@ -614,20 +614,9 @@ def parse_expression(expression: Dict, caller_context: CallerContextExpression)
assert type_name[caller_context.get_key()] == "UserDefinedTypeName"
if is_compact_ast:
# Changed introduced in Solidity 0.8
# see https://github.com/crytic/slither/issues/794
# TODO explore more the changes introduced in 0.8 and the usage of pathNode/IdentifierPath
if "name" not in type_name:
assert "pathNode" in type_name and "name" in type_name["pathNode"]
contract_name = type_name["pathNode"]["name"]
else:
contract_name = type_name["name"]
else:
contract_name = type_name["attributes"]["name"]
new = NewContract(contract_name)
contract_type = parse_type(type_name, caller_context)
assert isinstance(contract_type, UserDefinedType)
new = NewContract(contract_type)
new.set_offset(src, caller_context.compilation_unit)
return new

@ -134,8 +134,9 @@ def find_top_level(
if var_name in scope.enums:
return scope.enums[var_name], False
if var_name in scope.events:
return scope.events[var_name], False
for event in scope.events:
if var_name == event.full_name:
return event, False
for import_directive in scope.imports:
if import_directive.alias == var_name:
@ -268,6 +269,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.event_top_level import EventTopLevelSolc
from slither.solc_parsing.declarations.custom_error import CustomErrorSolc
direct_contracts: List[Contract]
@ -311,6 +313,10 @@ def _find_variable_init(
direct_contracts = []
direct_functions_parser = []
scope = caller_context.underlying_variable.file_scope
elif isinstance(caller_context, EventTopLevelSolc):
direct_contracts = []
direct_functions_parser = []
scope = caller_context.underlying_event.file_scope
elif isinstance(caller_context, CustomErrorSolc):
if caller_context.contract_parser:
direct_contracts = [caller_context.contract_parser.underlying_contract]

@ -25,7 +25,7 @@ from slither.solc_parsing.declarations.caller_context import CallerContextExpres
from slither.solc_parsing.declarations.contract import ContractSolc
from slither.solc_parsing.declarations.custom_error import CustomErrorSolc
from slither.solc_parsing.declarations.function import FunctionSolc
from slither.solc_parsing.declarations.event import EventSolc
from slither.solc_parsing.declarations.event_top_level import EventTopLevelSolc
from slither.solc_parsing.declarations.structure_top_level import StructureTopLevelSolc
from slither.solc_parsing.declarations.using_for_top_level import UsingForTopLevelSolc
from slither.solc_parsing.exceptions import VariableNotFound
@ -92,6 +92,7 @@ class SlitherCompilationUnitSolc(CallerContextExpression):
self._variables_top_level_parser: List[TopLevelVariableSolc] = []
self._functions_top_level_parser: List[FunctionSolc] = []
self._using_for_top_level_parser: List[UsingForTopLevelSolc] = []
self._events_top_level_parser: List[EventTopLevelSolc] = []
self._all_functions_and_modifier_parser: List[FunctionSolc] = []
self._top_level_contracts_counter = 0
@ -364,9 +365,9 @@ class SlitherCompilationUnitSolc(CallerContextExpression):
event = EventTopLevel(scope)
event.set_offset(top_level_data["src"], self._compilation_unit)
event_parser = EventSolc(event, top_level_data, self) # type: ignore
event_parser.analyze() # type: ignore
scope.events[event.full_name] = event
event_parser = EventTopLevelSolc(event, top_level_data, self) # type: ignore
self._events_top_level_parser.append(event_parser)
scope.events.add(event)
self._compilation_unit.events_top_level.append(event)
else:
@ -626,6 +627,7 @@ Please rename it, this name is reserved for Slither's internals"""
self._analyze_top_level_variables()
self._analyze_top_level_structures()
self._analyze_top_level_events()
# Start with the contracts without inheritance
# Analyze a contract only if all its fathers
@ -736,6 +738,13 @@ Please rename it, this name is reserved for Slither's internals"""
except (VariableNotFound, KeyError) as e:
raise SlitherException(f"Missing {e} during variable analyze") from e
def _analyze_top_level_events(self) -> None:
try:
for event in self._events_top_level_parser:
event.analyze()
except (VariableNotFound, KeyError) as e:
raise SlitherException(f"Missing event {e} during top level event analyze") from e
def _analyze_params_top_level_function(self) -> None:
for func_parser in self._functions_top_level_parser:
func_parser.analyze_params()

@ -232,6 +232,7 @@ def parse_type(
from slither.solc_parsing.declarations.structure_top_level import StructureTopLevelSolc
from slither.solc_parsing.slither_compilation_unit_solc import SlitherCompilationUnitSolc
from slither.solc_parsing.variables.top_level_variable import TopLevelVariableSolc
from slither.solc_parsing.declarations.event_top_level import EventTopLevelSolc
sl: "SlitherCompilationUnit"
renaming: Dict[str, str]
@ -266,7 +267,13 @@ def parse_type(
functions = []
elif isinstance(
caller_context,
(StructureTopLevelSolc, CustomErrorSolc, TopLevelVariableSolc, UsingForTopLevelSolc),
(
StructureTopLevelSolc,
CustomErrorSolc,
TopLevelVariableSolc,
UsingForTopLevelSolc,
EventTopLevelSolc,
),
):
if isinstance(caller_context, StructureTopLevelSolc):
scope = caller_context.underlying_structure.file_scope
@ -274,6 +281,8 @@ def parse_type(
scope = caller_context.underlying_variable.file_scope
elif isinstance(caller_context, UsingForTopLevelSolc):
scope = caller_context.underlying_using_for.file_scope
elif isinstance(caller_context, EventTopLevelSolc):
scope = caller_context.underlying_event.file_scope
else:
assert isinstance(caller_context, CustomErrorSolc)
custom_error = caller_context.underlying_custom_error

@ -4,10 +4,10 @@ import logging
import os
import zipfile
from collections import OrderedDict
from importlib import metadata
from typing import Tuple, Optional, Dict, List, Union, Any, TYPE_CHECKING, Type
from zipfile import ZipFile
from pkg_resources import require
from slither.core.cfg.node import Node
from slither.core.declarations import (
@ -161,7 +161,7 @@ def output_to_sarif(
"driver": {
"name": "Slither",
"informationUri": "https://github.com/crytic/slither",
"version": require("slither-analyzer")[0].version,
"version": metadata.version("slither-analyzer"),
"rules": [],
}
},

@ -153,10 +153,10 @@ def parse_expression(
parsed_expr.set_offset(expression.src, caller_context.compilation_unit)
return parsed_expr
# `raw_call` and `send` are treated specially in order to force `extract_tmp_call` to treat this as a `HighLevelCall` which will be converted
# to a `LowLevelCall` by `convert_to_low_level`. This is an artifact of the late conversion of Solidity...
if called.value.name == "raw_call()":
args = [parse_expression(a, caller_context) for a in expression.args]
# This is treated specially in order to force `extract_tmp_call` to treat this as a `HighLevelCall` which will be converted
# to a `LowLevelCall` by `convert_to_low_level`. This is an artifact of the late conversion of Solidity...
call = CallExpression(
MemberAccess("raw_call", "tuple(bool,bytes32)", args[0]),
args[1:],
@ -183,6 +183,17 @@ def parse_expression(
return call
if called.value.name == "send()":
args = [parse_expression(a, caller_context) for a in expression.args]
call = CallExpression(
MemberAccess("send", "tuple()", args[0]),
args[1:],
"tuple()",
)
call.set_offset(expression.src, caller_context.compilation_unit)
return call
if expression.args and isinstance(expression.args[0], VyDict):
arguments = []
for val in expression.args[0].values:

@ -467,6 +467,8 @@ ALL_TESTS = [
),
Test("user_defined_operators-0.8.19.sol", ["0.8.19"]),
Test("aliasing/main.sol", ["0.8.19"]),
Test("aliasing/alias-unit-NewContract.sol", ["0.8.19"]),
Test("aliasing/alias-symbol-NewContract.sol", ["0.8.19"]),
Test("type-aliases.sol", ["0.8.19"]),
Test("enum-max-min.sol", ["0.8.19"]),
Test("event-top-level.sol", ["0.8.22"]),

@ -0,0 +1,8 @@
import {MyContract as MyAliasedContract} from "./MyContract.sol";
contract Test {
MyAliasedContract c;
constructor() {
c = new MyAliasedContract();
}
}

@ -0,0 +1,9 @@
import "./MyContract.sol" as MyAliasedContract;
contract Test {
MyAliasedContract.MyContract c;
constructor() {
c = new MyAliasedContract.MyContract();
}
}

@ -1,7 +1,17 @@
event MyEvent(uint256 a);
uint256 constant A = 3;
event MyEvent2(uint8[A]);
contract T {
type MyType is uint256;
event MyCustomEvent(MyType mytype);
function a() public {
emit MyEvent(2);
}
function b() public {
emit MyEvent2([1,2,3]);
}
}

@ -0,0 +1,6 @@
{
"MyContract": {},
"Test": {
"constructor()": "digraph{\n0[label=\"Node Type: ENTRY_POINT 0\n\"];\n0->1;\n1[label=\"Node Type: EXPRESSION 1\n\"];\n}\n"
}
}

@ -0,0 +1,6 @@
{
"MyContract": {},
"Test": {
"constructor()": "digraph{\n0[label=\"Node Type: ENTRY_POINT 0\n\"];\n0->1;\n1[label=\"Node Type: EXPRESSION 1\n\"];\n}\n"
}
}

@ -1,5 +1,6 @@
{
"T": {
"a()": "digraph{\n0[label=\"Node Type: ENTRY_POINT 0\n\"];\n0->1;\n1[label=\"Node Type: EXPRESSION 1\n\"];\n}\n"
"a()": "digraph{\n0[label=\"Node Type: ENTRY_POINT 0\n\"];\n0->1;\n1[label=\"Node Type: EXPRESSION 1\n\"];\n}\n",
"b()": "digraph{\n0[label=\"Node Type: ENTRY_POINT 0\n\"];\n0->1;\n1[label=\"Node Type: EXPRESSION 1\n\"];\n}\n"
}
}

@ -16,12 +16,12 @@ EXPRESSION:
user_shares.append(1)
IRs:
REF_1 -> LENGTH user_shares
TMP_3(uint256) := REF_1(uint256)
TMP_4(uint256) = TMP_3 (c)+ 1
REF_1(uint256) (->user_shares) := TMP_4(uint256)
REF_2(uint256) -> user_shares[TMP_3]
REF_2(uint256) (->user_shares) := 1(uint256)"];
REF_2 -> LENGTH user_shares
TMP_4(uint256) := REF_2(uint256)
TMP_5(uint256) = TMP_4 (c)+ 1
REF_2(uint256) (->user_shares) := TMP_5(uint256)
REF_3(uint256) -> user_shares[TMP_4]
REF_3(uint256) (->user_shares) := 1(uint256)"];
2->3;
3[label="Node Type: EXPRESSION 3
@ -29,10 +29,10 @@ EXPRESSION:
user_shares.pop()
IRs:
REF_4 -> LENGTH user_shares
TMP_6(uint256) = REF_4 (c)- 1
REF_5(uint256) -> user_shares[TMP_6]
REF_5 = delete REF_5
REF_6 -> LENGTH user_shares
REF_6(uint256) (->user_shares) := TMP_6(uint256)"];
REF_5 -> LENGTH user_shares
TMP_7(uint256) = REF_5 (c)- 1
REF_6(uint256) -> user_shares[TMP_7]
REF_6 = delete REF_6
REF_7 -> LENGTH user_shares
REF_7(uint256) (->user_shares) := TMP_7(uint256)"];
}

@ -115,4 +115,12 @@ x = self.balance
IRs:
x(uint256) := self.balance(uint256)"];
14->15;
15[label="Node Type: EXPRESSION 15
EXPRESSION:
msg.sender.send(x)
IRs:
TMP_2 = SEND dest:msg.sender value:x"];
}

@ -16,6 +16,7 @@ def test_builtins():
m: address = tx.origin
n: uint256 = tx.gasprice
x: uint256 = self.balance
send(msg.sender, x)
@external
def c(x: uint256):

@ -10,7 +10,8 @@ from slither.slithir.operations import (
)
def test_with_explicit_return(slither_from_solidity_source) -> None:
@pytest.mark.parametrize("legacy", [True, False])
def test_with_explicit_return(slither_from_solidity_source, legacy) -> None:
source = """
contract Contract {
function foo(int x) public returns (int y) {
@ -22,31 +23,31 @@ def test_with_explicit_return(slither_from_solidity_source) -> None:
}
}
"""
for legacy in [True, False]:
with slither_from_solidity_source(source, legacy=legacy) as slither:
c: Contract = slither.get_contract_from_name("Contract")[0]
f: Function = c.functions[0]
node_if: Node = f.nodes[1]
node_true = node_if.son_true
node_false = node_if.son_false
assert node_true.type == NodeType.RETURN
assert isinstance(node_true.irs[0], Return)
assert node_true.irs[0].values[0] == f.get_local_variable_from_name("x")
assert len(node_true.sons) == 0
node_end_if = node_false.sons[0]
assert node_end_if.type == NodeType.ENDIF
assert node_end_if.sons[0].type == NodeType.RETURN
node_ret = node_end_if.sons[0]
assert isinstance(node_ret.irs[0], Return)
assert node_ret.irs[0].values[0] == f.get_local_variable_from_name("y")
with slither_from_solidity_source(source, legacy=legacy) as slither:
c: Contract = slither.get_contract_from_name("Contract")[0]
f: Function = c.functions[0]
node_if: Node = f.nodes[1]
node_true = node_if.son_true
node_false = node_if.son_false
assert node_true.type == NodeType.RETURN
assert isinstance(node_true.irs[0], Return)
assert node_true.irs[0].values[0] == f.get_local_variable_from_name("x")
assert len(node_true.sons) == 0
node_end_if = node_false.sons[0]
assert node_end_if.type == NodeType.ENDIF
assert node_end_if.sons[0].type == NodeType.RETURN
node_ret = node_end_if.sons[0]
assert isinstance(node_ret.irs[0], Return)
assert node_ret.irs[0].values[0] == f.get_local_variable_from_name("y")
def test_return_multiple_with_struct(slither_from_solidity_source) -> None:
@pytest.mark.parametrize("legacy", [True, False])
def test_return_multiple_with_struct(slither_from_solidity_source, legacy) -> None:
source = """
struct St {
uint256 value;
}
contract Contract {
function foo(St memory x) public returns (St memory y, uint256 z) {
z = x.value;
@ -54,16 +55,15 @@ def test_return_multiple_with_struct(slither_from_solidity_source) -> None:
}
}
"""
for legacy in [True, False]:
with slither_from_solidity_source(source, legacy=legacy) as slither:
c: Contract = slither.get_contract_from_name("Contract")[0]
f: Function = c.functions[0]
assert len(f.nodes) == 4
node = f.nodes[3]
assert node.type == NodeType.RETURN
assert isinstance(node.irs[0], Return)
assert node.irs[0].values[0] == f.get_local_variable_from_name("y")
assert node.irs[0].values[1] == f.get_local_variable_from_name("z")
with slither_from_solidity_source(source, legacy=legacy) as slither:
c: Contract = slither.get_contract_from_name("Contract")[0]
f: Function = c.functions[0]
assert len(f.nodes) == 4
node = f.nodes[3]
assert node.type == NodeType.RETURN
assert isinstance(node.irs[0], Return)
assert node.irs[0].values[0] == f.get_local_variable_from_name("y")
assert node.irs[0].values[1] == f.get_local_variable_from_name("z")
def test_nested_ifs_with_loop_legacy(slither_from_solidity_source) -> None:
@ -149,7 +149,8 @@ def test_nested_ifs_with_loop_compact(slither_from_solidity_source) -> None:
@pytest.mark.xfail # Explicit returns inside assembly are currently not parsed as return nodes
def test_assembly_switch_cases(slither_from_solidity_source):
@pytest.mark.parametrize("legacy", [True, False])
def test_assembly_switch_cases(slither_from_solidity_source, legacy):
source = """
contract Contract {
function foo(uint a) public returns (uint x) {
@ -164,28 +165,28 @@ def test_assembly_switch_cases(slither_from_solidity_source):
}
}
"""
for legacy in [True, False]:
with slither_from_solidity_source(source, solc_version="0.8.0", legacy=legacy) as slither:
c: Contract = slither.get_contract_from_name("Contract")[0]
f = c.functions[0]
if legacy:
node = f.nodes[2]
assert node.type == NodeType.RETURN
assert isinstance(node.irs[0], Return)
assert node.irs[0].values[0] == f.get_local_variable_from_name("x")
else:
node_end_if = f.nodes[5]
assert node_end_if.sons[0].type == NodeType.RETURN
node_implicit = node_end_if.sons[0]
assert isinstance(node_implicit.irs[0], Return)
assert node_implicit.irs[0].values[0] == f.get_local_variable_from_name("x")
# This part will fail until issue #1927 is fixed
node_explicit = f.nodes[10]
assert node_explicit.type == NodeType.RETURN
assert len(node_explicit.sons) == 0
with slither_from_solidity_source(source, solc_version="0.8.0", legacy=legacy) as slither:
c: Contract = slither.get_contract_from_name("Contract")[0]
f = c.functions[0]
if legacy:
node = f.nodes[2]
assert node.type == NodeType.RETURN
assert isinstance(node.irs[0], Return)
assert node.irs[0].values[0] == f.get_local_variable_from_name("x")
else:
node_end_if = f.nodes[5]
assert node_end_if.sons[0].type == NodeType.RETURN
node_implicit = node_end_if.sons[0]
assert isinstance(node_implicit.irs[0], Return)
assert node_implicit.irs[0].values[0] == f.get_local_variable_from_name("x")
# This part will fail until issue #1927 is fixed
node_explicit = f.nodes[10]
assert node_explicit.type == NodeType.RETURN
assert len(node_explicit.sons) == 0
def test_issue_1846_ternary_in_ternary(slither_from_solidity_source):
@pytest.mark.parametrize("legacy", [True, False])
def test_issue_1846_ternary_in_ternary(slither_from_solidity_source, legacy):
source = """
contract Contract {
function foo(uint x) public returns (uint y) {
@ -193,14 +194,13 @@ def test_issue_1846_ternary_in_ternary(slither_from_solidity_source):
}
}
"""
for legacy in [True, False]:
with slither_from_solidity_source(source, legacy=legacy) as slither:
c: Contract = slither.get_contract_from_name("Contract")[0]
f = c.functions[0]
node_end_if = f.nodes[3]
assert node_end_if.type == NodeType.ENDIF
assert len(node_end_if.sons) == 1
node_ret = node_end_if.sons[0]
assert node_ret.type == NodeType.RETURN
assert isinstance(node_ret.irs[0], Return)
assert node_ret.irs[0].values[0] == f.get_local_variable_from_name("y")
with slither_from_solidity_source(source, legacy=legacy) as slither:
c: Contract = slither.get_contract_from_name("Contract")[0]
f = c.functions[0]
node_end_if = f.nodes[3]
assert node_end_if.type == NodeType.ENDIF
assert len(node_end_if.sons) == 1
node_ret = node_end_if.sons[0]
assert node_ret.type == NodeType.RETURN
assert isinstance(node_ret.irs[0], Return)
assert node_ret.irs[0].values[0] == f.get_local_variable_from_name("y")

Loading…
Cancel
Save