Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix parsing of events #2365

Merged
merged 1 commit into from
Mar 18, 2024
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions slither/core/scope/scope.py
Original file line number Diff line number Diff line change
@@ -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()
@@ -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
4 changes: 2 additions & 2 deletions slither/solc_parsing/declarations/contract.py
Original file line number Diff line number Diff line change
@@ -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
@@ -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:
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"]
@@ -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)

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
@@ -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]
17 changes: 13 additions & 4 deletions slither/solc_parsing/slither_compilation_unit_solc.py
Original file line number Diff line number Diff line change
@@ -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
@@ -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
@@ -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:
@@ -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
@@ -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()
11 changes: 10 additions & 1 deletion slither/solc_parsing/solidity_types/type_parsing.py
Original file line number Diff line number Diff line change
@@ -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,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
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"
}
}