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

Add support top level events #2219

Merged
merged 1 commit into from
Feb 16, 2024
Merged
Show file tree
Hide file tree
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: 6 additions & 0 deletions slither/core/compilation_unit.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
)
from slither.core.declarations.custom_error_top_level import CustomErrorTopLevel
from slither.core.declarations.enum_top_level import EnumTopLevel
from slither.core.declarations.event_top_level import EventTopLevel
from slither.core.declarations.function_top_level import FunctionTopLevel
from slither.core.declarations.structure_top_level import StructureTopLevel
from slither.core.declarations.using_for_top_level import UsingForTopLevel
Expand Down Expand Up @@ -57,6 +58,7 @@ def __init__(self, core: "SlitherCore", crytic_compilation_unit: CompilationUnit
self.contracts: List[Contract] = []
self._structures_top_level: List[StructureTopLevel] = []
self._enums_top_level: List[EnumTopLevel] = []
self._events_top_level: List[EventTopLevel] = []
self._variables_top_level: List[TopLevelVariable] = []
self._functions_top_level: List[FunctionTopLevel] = []
self._using_for_top_level: List[UsingForTopLevel] = []
Expand Down Expand Up @@ -234,6 +236,10 @@ def structures_top_level(self) -> List[StructureTopLevel]:
def enums_top_level(self) -> List[EnumTopLevel]:
return self._enums_top_level

@property
def events_top_level(self) -> List[EventTopLevel]:
return self._events_top_level

@property
def variables_top_level(self) -> List[TopLevelVariable]:
return self._variables_top_level
Expand Down
2 changes: 2 additions & 0 deletions slither/core/declarations/__init__.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
from .contract import Contract
from .enum import Enum
from .event import Event
from .event_contract import EventContract
from .event_top_level import EventTopLevel
from .function import Function
from .import_directive import Import
from .modifier import Modifier
Expand Down
12 changes: 6 additions & 6 deletions slither/core/declarations/contract.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@
from slither.utils.type_helpers import LibraryCallType, HighLevelCallType, InternalCallType
from slither.core.declarations import (
Enum,
Event,
EventContract,
Modifier,
EnumContract,
StructureContract,
Expand Down Expand Up @@ -73,7 +73,7 @@ def __init__(self, compilation_unit: "SlitherCompilationUnit", scope: "FileScope

self._enums: Dict[str, "EnumContract"] = {}
self._structures: Dict[str, "StructureContract"] = {}
self._events: Dict[str, "Event"] = {}
self._events: Dict[str, "EventContract"] = {}
# map accessible variable from name -> variable
# do not contain private variables inherited from contract
self._variables: Dict[str, "StateVariable"] = {}
Expand Down Expand Up @@ -278,28 +278,28 @@ def enums_as_dict(self) -> Dict[str, "EnumContract"]:
###################################################################################

@property
def events(self) -> List["Event"]:
def events(self) -> List["EventContract"]:
"""
list(Event): List of the events
"""
return list(self._events.values())

@property
def events_inherited(self) -> List["Event"]:
def events_inherited(self) -> List["EventContract"]:
"""
list(Event): List of the inherited events
"""
return [e for e in self.events if e.contract != self]

@property
def events_declared(self) -> List["Event"]:
def events_declared(self) -> List["EventContract"]:
"""
list(Event): List of the events declared within the contract (not inherited)
"""
return [e for e in self.events if e.contract == self]

@property
def events_as_dict(self) -> Dict[str, "Event"]:
def events_as_dict(self) -> Dict[str, "EventContract"]:
return self._events

# endregion
Expand Down
24 changes: 2 additions & 22 deletions slither/core/declarations/event.py
Original file line number Diff line number Diff line change
@@ -1,14 +1,10 @@
from typing import List, Tuple, TYPE_CHECKING
from typing import List, Tuple

from slither.core.declarations.contract_level import ContractLevel
from slither.core.source_mapping.source_mapping import SourceMapping
from slither.core.variables.event_variable import EventVariable

if TYPE_CHECKING:
from slither.core.declarations import Contract


class Event(ContractLevel, SourceMapping):
class Event(SourceMapping):
def __init__(self) -> None:
super().__init__()
self._name = None
Expand Down Expand Up @@ -39,25 +35,9 @@ def full_name(self) -> str:
name, parameters = self.signature
return name + "(" + ",".join(parameters) + ")"

@property
def canonical_name(self) -> str:
"""Return the function signature as a str
Returns:
str: contract.func_name(type1,type2)
"""
return self.contract.name + "." + self.full_name

@property
def elems(self) -> List["EventVariable"]:
return self._elems

def is_declared_by(self, contract: "Contract") -> bool:
"""
Check if the element is declared by the contract
:param contract:
:return:
"""
return self.contract == contract

def __str__(self) -> str:
return self.name
25 changes: 25 additions & 0 deletions slither/core/declarations/event_contract.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
from typing import TYPE_CHECKING

from slither.core.declarations.contract_level import ContractLevel
from slither.core.declarations import Event

if TYPE_CHECKING:
from slither.core.declarations import Contract


class EventContract(Event, ContractLevel):
def is_declared_by(self, contract: "Contract") -> bool:
"""
Check if the element is declared by the contract
:param contract:
:return:
"""
return self.contract == contract

@property
def canonical_name(self) -> str:
"""Return the function signature as a str
Returns:
str: contract.func_name(type1,type2)
"""
return self.contract.name + "." + self.full_name
13 changes: 13 additions & 0 deletions slither/core/declarations/event_top_level.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
from typing import TYPE_CHECKING

from slither.core.declarations import Event
from slither.core.declarations.top_level import TopLevel

if TYPE_CHECKING:
from slither.core.scope.scope import FileScope


class EventTopLevel(Event, TopLevel):
def __init__(self, scope: "FileScope") -> None:
super().__init__()
self.file_scope: "FileScope" = scope
7 changes: 6 additions & 1 deletion slither/core/scope/scope.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
from slither.core.declarations import Contract, Import, Pragma
from slither.core.declarations.custom_error_top_level import CustomErrorTopLevel
from slither.core.declarations.enum_top_level import EnumTopLevel
from slither.core.declarations.event_top_level import EventTopLevel
from slither.core.declarations.function_top_level import FunctionTopLevel
from slither.core.declarations.using_for_top_level import UsingForTopLevel
from slither.core.declarations.structure_top_level import StructureTopLevel
Expand Down Expand Up @@ -35,6 +36,7 @@ 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
Expand All @@ -54,7 +56,7 @@ def __init__(self, filename: Filename) -> None:
# Name -> type alias
self.type_aliases: Dict[str, TypeAlias] = {}

def add_accesible_scopes(self) -> bool:
def add_accesible_scopes(self) -> bool: # pylint: disable=too-many-branches
"""
Add information from accessible scopes. Return true if new information was obtained

Expand All @@ -74,6 +76,9 @@ def add_accesible_scopes(self) -> bool:
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)
learn_something = True
if not new_scope.functions.issubset(self.functions):
self.functions |= new_scope.functions
learn_something = True
Expand Down
2 changes: 2 additions & 0 deletions slither/core/slither_core.py
Original file line number Diff line number Diff line change
Expand Up @@ -269,6 +269,8 @@ def _compute_offsets_to_ref_impl_decl(self): # pylint: disable=too-many-branche
self._compute_offsets_from_thing(event)
for enum in compilation_unit.enums_top_level:
self._compute_offsets_from_thing(enum)
for event in compilation_unit.events_top_level:
self._compute_offsets_from_thing(event)
for function in compilation_unit.functions_top_level:
self._compute_offsets_from_thing(function)
for st in compilation_unit.structures_top_level:
Expand Down
8 changes: 4 additions & 4 deletions slither/solc_parsing/declarations/contract.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

from slither.core.declarations import (
Modifier,
Event,
EventContract,
EnumContract,
StructureContract,
Function,
Expand Down Expand Up @@ -747,12 +747,12 @@ def analyze_events(self) -> None:
self._contract.events_as_dict.update(father.events_as_dict)

for event_to_parse in self._eventsNotParsed:
event = Event()
event = EventContract()
event.set_contract(self._contract)
event.set_offset(event_to_parse["src"], self._contract.compilation_unit)

event_parser = EventSolc(event, event_to_parse, self) # type: ignore
event_parser.analyze(self) # type: ignore
event_parser = EventSolc(event, event_to_parse, self._slither_parser) # type: ignore
event_parser.analyze() # type: ignore
self._contract.events_as_dict[event.full_name] = event
except (VariableNotFound, KeyError) as e:
self.log_incorrect_parsing(f"Missing event {e}")
Expand Down
19 changes: 9 additions & 10 deletions slither/solc_parsing/declarations/event.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,19 +8,20 @@
from slither.core.declarations.event import Event

if TYPE_CHECKING:
from slither.solc_parsing.declarations.contract import ContractSolc
from slither.solc_parsing.slither_compilation_unit_solc import SlitherCompilationUnitSolc


class EventSolc:
"""
Event class
"""

def __init__(self, event: Event, event_data: Dict, contract_parser: "ContractSolc") -> None:
def __init__(
self, event: Event, event_data: Dict, slither_parser: "SlitherCompilationUnitSolc"
) -> None:

self._event = event
event.set_contract(contract_parser.underlying_contract)
self._parser_contract = contract_parser
self._slither_parser = slither_parser

if self.is_compact_ast:
self._event.name = event_data["name"]
Expand All @@ -41,18 +42,16 @@ def __init__(self, event: Event, event_data: Dict, contract_parser: "ContractSol

@property
def is_compact_ast(self) -> bool:
return self._parser_contract.is_compact_ast
return self._slither_parser.is_compact_ast

def analyze(self, contract: "ContractSolc") -> None:
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._parser_contract.underlying_contract.compilation_unit
)
elem.set_offset(elem_to_parse["src"], self._slither_parser.compilation_unit)
elem_parser = EventVariableSolc(elem, elem_to_parse)
elem_parser.analyze(contract)
elem_parser.analyze(self._slither_parser)

self._event.elems.append(elem)

Expand Down
3 changes: 3 additions & 0 deletions slither/solc_parsing/expressions/find_variable.py
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +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 import_directive in scope.imports:
if import_directive.alias == var_name:
new_val = SolidityImportPlaceHolder(import_directive)
Expand Down
11 changes: 11 additions & 0 deletions slither/solc_parsing/slither_compilation_unit_solc.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
from slither.core.declarations import Contract
from slither.core.declarations.custom_error_top_level import CustomErrorTopLevel
from slither.core.declarations.enum_top_level import EnumTopLevel
from slither.core.declarations.event_top_level import EventTopLevel
from slither.core.declarations.function_top_level import FunctionTopLevel
from slither.core.declarations.import_directive import Import
from slither.core.declarations.pragma_directive import Pragma
Expand All @@ -23,6 +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.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 @@ -347,6 +349,15 @@ def parse_top_level_items(self, data_loaded: Dict, filename: str) -> None:
self._compilation_unit.type_aliases[alias] = type_alias
scope.type_aliases[alias] = type_alias

elif top_level_data[self.get_key()] == "EventDefinition":
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
self._compilation_unit.events_top_level.append(event)

else:
raise SlitherException(f"Top level {top_level_data[self.get_key()]} not supported")

Expand Down
1 change: 1 addition & 0 deletions slither/tools/flattening/flattening.py
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@ def __init__(

self._get_source_code_top_level(compilation_unit.structures_top_level)
self._get_source_code_top_level(compilation_unit.enums_top_level)
self._get_source_code_top_level(compilation_unit.events_top_level)
self._get_source_code_top_level(compilation_unit.custom_errors)
self._get_source_code_top_level(compilation_unit.variables_top_level)
self._get_source_code_top_level(compilation_unit.functions_top_level)
Expand Down
4 changes: 2 additions & 2 deletions slither/vyper_parsing/declarations/contract.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@
from slither.vyper_parsing.variables.state_variable import StateVariableVyper
from slither.vyper_parsing.declarations.function import FunctionVyper
from slither.core.declarations.function_contract import FunctionContract
from slither.core.declarations import Contract, StructureContract, EnumContract, Event
from slither.core.declarations import Contract, StructureContract, EnumContract, EventContract

from slither.core.variables.state_variable import StateVariable

Expand Down Expand Up @@ -478,7 +478,7 @@ def parse_state_variables(self) -> None:

def parse_events(self) -> None:
for event_to_parse in self._eventsNotParsed:
event = Event()
event = EventContract()
event.set_contract(self._contract)
event.set_offset(event_to_parse.src, self._contract.compilation_unit)

Expand Down
1 change: 1 addition & 0 deletions tests/e2e/solc_parsing/test_ast_parsing.py
Original file line number Diff line number Diff line change
Expand Up @@ -462,6 +462,7 @@ def make_version(minor: int, patch_min: int, patch_max: int) -> List[str]:
Test("aliasing/main.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"]),
]
# create the output folder if needed
try:
Expand Down
Binary file not shown.
7 changes: 7 additions & 0 deletions tests/e2e/solc_parsing/test_data/event-top-level.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
event MyEvent(uint256 a);

contract T {
function a() public {
emit MyEvent(2);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"T": {
"a()": "digraph{\n0[label=\"Node Type: ENTRY_POINT 0\n\"];\n0->1;\n1[label=\"Node Type: EXPRESSION 1\n\"];\n}\n"
}
}