Merge pull request #2365 from crytic/dev-fix-event-parsing

Fix parsing of events
pull/2368/head
alpharush 8 months ago committed by GitHub
commit de268d34df
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
  1. 6
      slither/core/scope/scope.py
  2. 4
      slither/solc_parsing/declarations/contract.py
  3. 20
      slither/solc_parsing/declarations/event_contract.py
  4. 75
      slither/solc_parsing/declarations/event_top_level.py
  5. 10
      slither/solc_parsing/expressions/find_variable.py
  6. 17
      slither/solc_parsing/slither_compilation_unit_solc.py
  7. 11
      slither/solc_parsing/solidity_types/type_parsing.py
  8. BIN
      tests/e2e/solc_parsing/test_data/compile/event-top-level.sol-0.8.22-compact.zip
  9. 10
      tests/e2e/solc_parsing/test_data/event-top-level.sol
  10. 3
      tests/e2e/solc_parsing/test_data/expected/event-top-level.sol-0.8.22-compact.json

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

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

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

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

@ -24,7 +24,7 @@ from slither.solc_parsing.declarations.caller_context import CallerContextExpres
from slither.solc_parsing.declarations.contract import ContractSolc from slither.solc_parsing.declarations.contract import ContractSolc
from slither.solc_parsing.declarations.custom_error import CustomErrorSolc from slither.solc_parsing.declarations.custom_error import CustomErrorSolc
from slither.solc_parsing.declarations.function import FunctionSolc 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.structure_top_level import StructureTopLevelSolc
from slither.solc_parsing.declarations.using_for_top_level import UsingForTopLevelSolc from slither.solc_parsing.declarations.using_for_top_level import UsingForTopLevelSolc
from slither.solc_parsing.exceptions import VariableNotFound from slither.solc_parsing.exceptions import VariableNotFound
@ -90,6 +90,7 @@ class SlitherCompilationUnitSolc(CallerContextExpression):
self._variables_top_level_parser: List[TopLevelVariableSolc] = [] self._variables_top_level_parser: List[TopLevelVariableSolc] = []
self._functions_top_level_parser: List[FunctionSolc] = [] self._functions_top_level_parser: List[FunctionSolc] = []
self._using_for_top_level_parser: List[UsingForTopLevelSolc] = [] self._using_for_top_level_parser: List[UsingForTopLevelSolc] = []
self._events_top_level_parser: List[EventTopLevelSolc] = []
self._all_functions_and_modifier_parser: List[FunctionSolc] = [] self._all_functions_and_modifier_parser: List[FunctionSolc] = []
self._top_level_contracts_counter = 0 self._top_level_contracts_counter = 0
@ -353,9 +354,9 @@ class SlitherCompilationUnitSolc(CallerContextExpression):
event = EventTopLevel(scope) event = EventTopLevel(scope)
event.set_offset(top_level_data["src"], self._compilation_unit) event.set_offset(top_level_data["src"], self._compilation_unit)
event_parser = EventSolc(event, top_level_data, self) # type: ignore event_parser = EventTopLevelSolc(event, top_level_data, self) # type: ignore
event_parser.analyze() # type: ignore self._events_top_level_parser.append(event_parser)
scope.events[event.full_name] = event scope.events.add(event)
self._compilation_unit.events_top_level.append(event) self._compilation_unit.events_top_level.append(event)
else: else:
@ -612,6 +613,7 @@ Please rename it, this name is reserved for Slither's internals"""
self._analyze_top_level_variables() self._analyze_top_level_variables()
self._analyze_top_level_structures() self._analyze_top_level_structures()
self._analyze_top_level_events()
# Start with the contracts without inheritance # Start with the contracts without inheritance
# Analyze a contract only if all its fathers # Analyze a contract only if all its fathers
@ -722,6 +724,13 @@ Please rename it, this name is reserved for Slither's internals"""
except (VariableNotFound, KeyError) as e: except (VariableNotFound, KeyError) as e:
raise SlitherException(f"Missing {e} during variable analyze") from 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: def _analyze_params_top_level_function(self) -> None:
for func_parser in self._functions_top_level_parser: for func_parser in self._functions_top_level_parser:
func_parser.analyze_params() 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.declarations.structure_top_level import StructureTopLevelSolc
from slither.solc_parsing.slither_compilation_unit_solc import SlitherCompilationUnitSolc from slither.solc_parsing.slither_compilation_unit_solc import SlitherCompilationUnitSolc
from slither.solc_parsing.variables.top_level_variable import TopLevelVariableSolc from slither.solc_parsing.variables.top_level_variable import TopLevelVariableSolc
from slither.solc_parsing.declarations.event_top_level import EventTopLevelSolc
sl: "SlitherCompilationUnit" sl: "SlitherCompilationUnit"
renaming: Dict[str, str] renaming: Dict[str, str]
@ -266,7 +267,13 @@ def parse_type(
functions = [] functions = []
elif isinstance( elif isinstance(
caller_context, caller_context,
(StructureTopLevelSolc, CustomErrorSolc, TopLevelVariableSolc, UsingForTopLevelSolc), (
StructureTopLevelSolc,
CustomErrorSolc,
TopLevelVariableSolc,
UsingForTopLevelSolc,
EventTopLevelSolc,
),
): ):
if isinstance(caller_context, StructureTopLevelSolc): if isinstance(caller_context, StructureTopLevelSolc):
scope = caller_context.underlying_structure.file_scope scope = caller_context.underlying_structure.file_scope
@ -274,6 +281,8 @@ def parse_type(
scope = caller_context.underlying_variable.file_scope scope = caller_context.underlying_variable.file_scope
elif isinstance(caller_context, UsingForTopLevelSolc): elif isinstance(caller_context, UsingForTopLevelSolc):
scope = caller_context.underlying_using_for.file_scope scope = caller_context.underlying_using_for.file_scope
elif isinstance(caller_context, EventTopLevelSolc):
scope = caller_context.underlying_event.file_scope
else: else:
assert isinstance(caller_context, CustomErrorSolc) assert isinstance(caller_context, CustomErrorSolc)
custom_error = caller_context.underlying_custom_error custom_error = caller_context.underlying_custom_error

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

@ -1,5 +1,6 @@
{ {
"T": { "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"
} }
} }
Loading…
Cancel
Save