Skip to content

Commit

Permalink
Merge pull request #2365 from crytic/dev-fix-event-parsing
Browse files Browse the repository at this point in the history
Fix parsing of events
  • Loading branch information
0xalpharush authored Mar 18, 2024
2 parents 66ae9d2 + 134cadf commit de268d3
Show file tree
Hide file tree
Showing 10 changed files with 133 additions and 23 deletions.
6 changes: 3 additions & 3 deletions slither/core/scope/scope.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,11 +36,11 @@ def __init__(self, filename: Filename) -> None:
# 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()
Expand Down Expand Up @@ -76,8 +76,8 @@ def add_accesible_scopes(self) -> bool: # pylint: disable=too-many-branches
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
Expand Down
4 changes: 2 additions & 2 deletions slither/solc_parsing/declarations/contract.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
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
Expand Down Expand Up @@ -751,7 +751,7 @@ def analyze_events(self) -> None:
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:
Expand Down
Original file line number Diff line number Diff line change
@@ -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"]
Expand All @@ -42,16 +42,16 @@ def __init__(

@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)

Expand Down
75 changes: 75 additions & 0 deletions slither/solc_parsing/declarations/event_top_level.py
Original file line number Diff line number Diff line change
@@ -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
10 changes: 8 additions & 2 deletions slither/solc_parsing/expressions/find_variable.py
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down Expand Up @@ -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]
Expand Down Expand Up @@ -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]
Expand Down
17 changes: 13 additions & 4 deletions slither/solc_parsing/slither_compilation_unit_solc.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@
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
Expand Down Expand Up @@ -90,6 +90,7 @@ def __init__(self, compilation_unit: SlitherCompilationUnit) -> None:
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
Expand Down Expand Up @@ -353,9 +354,9 @@ def parse_top_level_items(self, data_loaded: Dict, filename: str) -> None:
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:
Expand Down Expand Up @@ -612,6 +613,7 @@ def _analyze_second_part(

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
Expand Down Expand Up @@ -722,6 +724,13 @@ def _analyze_top_level_variables(self) -> None:
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()
Expand Down
11 changes: 10 additions & 1 deletion slither/solc_parsing/solidity_types/type_parsing.py
Original file line number Diff line number Diff line change
Expand Up @@ -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]
Expand Down Expand Up @@ -266,14 +267,22 @@ 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
elif isinstance(caller_context, TopLevelVariableSolc):
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
Expand Down
Binary file not shown.
10 changes: 10 additions & 0 deletions tests/e2e/solc_parsing/test_data/event-top-level.sol
Original file line number Diff line number Diff line change
@@ -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]);
}
}
Original file line number Diff line number Diff line change
@@ -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"
}
}

0 comments on commit de268d3

Please sign in to comment.