From 134cadf989ba6f63c8ee4082900f37439cd42e44 Mon Sep 17 00:00:00 2001 From: Simone Date: Mon, 18 Mar 2024 14:32:31 +0100 Subject: [PATCH] Fix parsing of events --- slither/core/scope/scope.py | 6 +- slither/solc_parsing/declarations/contract.py | 4 +- .../{event.py => event_contract.py} | 20 ++--- .../declarations/event_top_level.py | 75 ++++++++++++++++++ .../solc_parsing/expressions/find_variable.py | 10 ++- .../slither_compilation_unit_solc.py | 17 +++- .../solidity_types/type_parsing.py | 11 ++- .../event-top-level.sol-0.8.22-compact.zip | Bin 2022 -> 3325 bytes .../test_data/event-top-level.sol | 10 +++ .../event-top-level.sol-0.8.22-compact.json | 3 +- 10 files changed, 133 insertions(+), 23 deletions(-) rename slither/solc_parsing/declarations/{event.py => event_contract.py} (73%) create mode 100644 slither/solc_parsing/declarations/event_top_level.py diff --git a/slither/core/scope/scope.py b/slither/core/scope/scope.py index 784b17cb20..99fe00b253 100644 --- a/slither/core/scope/scope.py +++ b/slither/core/scope/scope.py @@ -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 diff --git a/slither/solc_parsing/declarations/contract.py b/slither/solc_parsing/declarations/contract.py index f0696d557b..b27cce7988 100644 --- a/slither/solc_parsing/declarations/contract.py +++ b/slither/solc_parsing/declarations/contract.py @@ -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: diff --git a/slither/solc_parsing/declarations/event.py b/slither/solc_parsing/declarations/event_contract.py similarity index 73% rename from slither/solc_parsing/declarations/event.py rename to slither/solc_parsing/declarations/event_contract.py index 4a7d62389d..6af45ba527 100644 --- a/slither/solc_parsing/declarations/event.py +++ b/slither/solc_parsing/declarations/event_contract.py @@ -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) diff --git a/slither/solc_parsing/declarations/event_top_level.py b/slither/solc_parsing/declarations/event_top_level.py new file mode 100644 index 0000000000..9b6b676c79 --- /dev/null +++ b/slither/solc_parsing/declarations/event_top_level.py @@ -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 diff --git a/slither/solc_parsing/expressions/find_variable.py b/slither/solc_parsing/expressions/find_variable.py index e7fa995215..404bcdee5f 100644 --- a/slither/solc_parsing/expressions/find_variable.py +++ b/slither/solc_parsing/expressions/find_variable.py @@ -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] diff --git a/slither/solc_parsing/slither_compilation_unit_solc.py b/slither/solc_parsing/slither_compilation_unit_solc.py index ee28a7bf5d..02d8307024 100644 --- a/slither/solc_parsing/slither_compilation_unit_solc.py +++ b/slither/solc_parsing/slither_compilation_unit_solc.py @@ -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() diff --git a/slither/solc_parsing/solidity_types/type_parsing.py b/slither/solc_parsing/solidity_types/type_parsing.py index 82c1ac3926..06a91f9114 100644 --- a/slither/solc_parsing/solidity_types/type_parsing.py +++ b/slither/solc_parsing/solidity_types/type_parsing.py @@ -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 diff --git a/tests/e2e/solc_parsing/test_data/compile/event-top-level.sol-0.8.22-compact.zip b/tests/e2e/solc_parsing/test_data/compile/event-top-level.sol-0.8.22-compact.zip index ed82f32b17350e7c70788a8d84e0d02dcbdd0bba..4845562904d655f7a0e5f976f0700974d92f66ea 100644 GIT binary patch delta 3040 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 diff --git a/tests/e2e/solc_parsing/test_data/event-top-level.sol b/tests/e2e/solc_parsing/test_data/event-top-level.sol index fa64e1bf7c..81ac94f7b4 100644 --- a/tests/e2e/solc_parsing/test_data/event-top-level.sol +++ b/tests/e2e/solc_parsing/test_data/event-top-level.sol @@ -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]); + } } diff --git a/tests/e2e/solc_parsing/test_data/expected/event-top-level.sol-0.8.22-compact.json b/tests/e2e/solc_parsing/test_data/expected/event-top-level.sol-0.8.22-compact.json index 58c6a3ab60..68dff297fb 100644 --- a/tests/e2e/solc_parsing/test_data/expected/event-top-level.sol-0.8.22-compact.json +++ b/tests/e2e/solc_parsing/test_data/expected/event-top-level.sol-0.8.22-compact.json @@ -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" } } \ No newline at end of file