slither.core.declarations.function

Function module

   1"""
   2Function module
   3"""
   4
   5import logging
   6from abc import abstractmethod, ABCMeta
   7from collections import namedtuple
   8from enum import Enum
   9from itertools import groupby
  10from typing import Any, TYPE_CHECKING, Optional, Union
  11from collections.abc import Callable
  12
  13from slither.core.cfg.scope import Scope
  14from slither.core.declarations.solidity_variables import (
  15    SolidityFunction,
  16    SolidityVariable,
  17    SolidityVariableComposed,
  18)
  19from slither.core.expressions import (
  20    Identifier,
  21    IndexAccess,
  22    MemberAccess,
  23    UnaryOperation,
  24)
  25from slither.core.solidity_types.type import Type
  26from slither.core.source_mapping.source_mapping import SourceMapping
  27from slither.core.variables.local_variable import LocalVariable
  28from slither.core.variables.state_variable import StateVariable
  29from slither.utils.type import convert_type_for_solidity_signature_to_string
  30from slither.utils.utils import unroll
  31
  32
  33if TYPE_CHECKING:
  34    from slither.core.declarations import Contract, FunctionContract
  35    from slither.core.cfg.node import Node, NodeType
  36    from slither.core.variables.variable import Variable
  37    from slither.slithir.variables.variable import SlithIRVariable
  38    from slither.slithir.variables import LocalIRVariable
  39    from slither.core.expressions.expression import Expression
  40    from slither.slithir.operations import (
  41        HighLevelCall,
  42        InternalCall,
  43        LibraryCall,
  44        LowLevelCall,
  45        SolidityCall,
  46        Operation,
  47    )
  48    from slither.core.compilation_unit import SlitherCompilationUnit
  49    from slither.core.scope.scope import FileScope
  50
  51LOGGER = logging.getLogger("Function")
  52ReacheableNode = namedtuple("ReacheableNode", ["node", "ir"])
  53
  54
  55class ModifierStatements:
  56    def __init__(
  57        self,
  58        modifier: Union["Contract", "Function"],
  59        entry_point: "Node",
  60        nodes: list["Node"],
  61    ) -> None:
  62        self._modifier = modifier
  63        self._entry_point = entry_point
  64        self._nodes = nodes
  65
  66    @property
  67    def modifier(self) -> Union["Contract", "Function"]:
  68        return self._modifier
  69
  70    @property
  71    def entry_point(self) -> "Node":
  72        return self._entry_point
  73
  74    @entry_point.setter
  75    def entry_point(self, entry_point: "Node"):
  76        self._entry_point = entry_point
  77
  78    @property
  79    def nodes(self) -> list["Node"]:
  80        return self._nodes
  81
  82    @nodes.setter
  83    def nodes(self, nodes: list["Node"]):
  84        self._nodes = nodes
  85
  86
  87class FunctionType(Enum):
  88    NORMAL = 0
  89    CONSTRUCTOR = 1
  90    FALLBACK = 2
  91    RECEIVE = 3
  92    CONSTRUCTOR_VARIABLES = 10  # Fake function to hold variable declaration statements
  93    CONSTRUCTOR_CONSTANT_VARIABLES = 11  # Fake function to hold variable declaration statements
  94
  95
  96def _filter_state_variables_written(expressions: list["Expression"]):
  97    ret = []
  98
  99    for expression in expressions:
 100        if isinstance(expression, (Identifier, UnaryOperation, MemberAccess)):
 101            ret.append(expression.expression)
 102        elif isinstance(expression, IndexAccess):
 103            ret.append(expression.expression_left)
 104    return ret
 105
 106
 107class FunctionLanguage(Enum):
 108    Solidity = 0
 109    Yul = 1
 110    Vyper = 2
 111
 112
 113class Function(SourceMapping, metaclass=ABCMeta):
 114    """
 115    Function class
 116    """
 117
 118    def __init__(self, compilation_unit: "SlitherCompilationUnit") -> None:
 119        super().__init__()
 120        self._internal_scope: list[str] = []
 121        self._name: str | None = None
 122        self._view: bool = False
 123        self._pure: bool = False
 124        self._payable: bool = False
 125        self._visibility: str | None = None
 126        self._virtual: bool = False
 127        self._overrides: list[FunctionContract] = []
 128        self._overridden_by: list[FunctionContract] = []
 129
 130        self._is_implemented: bool | None = None
 131        self._is_empty: bool | None = None
 132        self._entry_point: Node | None = None
 133        self._nodes: list[Node] = []
 134        self._variables: dict[str, LocalVariable] = {}
 135        # slithir Temporary and references variables (but not SSA)
 136        self._slithir_variables: set[SlithIRVariable] = set()
 137        self._parameters: list[LocalVariable] = []
 138        self._parameters_ssa: list[LocalIRVariable] = []
 139        self._parameters_src: SourceMapping = SourceMapping()
 140        # This is used for vyper calls with default arguments
 141        self._default_args_as_expressions: list[Expression] = []
 142        self._returns: list[LocalVariable] = []
 143        self._returns_ssa: list[LocalIRVariable] = []
 144        self._returns_src: SourceMapping = SourceMapping()
 145        self._return_values: list[SlithIRVariable] | None = None
 146        self._return_values_ssa: list[SlithIRVariable] | None = None
 147        self._vars_read: list[Variable] = []
 148        self._vars_written: list[Variable] = []
 149        self._state_vars_read: list[StateVariable] = []
 150        self._vars_read_or_written: list[Variable] = []
 151        self._solidity_vars_read: list[SolidityVariable] = []
 152        self._state_vars_written: list[StateVariable] = []
 153        self._internal_calls: list[InternalCall] = []
 154        self._solidity_calls: list[SolidityCall] = []
 155        self._low_level_calls: list[LowLevelCall] = []
 156        self._high_level_calls: list[tuple[Contract, HighLevelCall]] = []
 157        self._library_calls: list[LibraryCall] = []
 158        self._external_calls_as_expressions: list[Expression] = []
 159        self._expression_vars_read: list[Expression] = []
 160        self._expression_vars_written: list[Expression] = []
 161        self._expression_calls: list[Expression] = []
 162        # self._expression_modifiers: List["Expression"] = []
 163        self._modifiers: list[ModifierStatements] = []
 164        self._explicit_base_constructor_calls: list[ModifierStatements] = []
 165        self._contains_assembly: bool = False
 166
 167        self._expressions: list[Expression] | None = None
 168        self._slithir_operations: list[Operation] | None = None
 169        self._slithir_ssa_operations: list[Operation] | None = None
 170
 171        self._all_expressions: list[Expression] | None = None
 172        self._all_slithir_operations: list[Operation] | None = None
 173        self._all_internals_calls: list[InternalCall] | None = None
 174        self._all_high_level_calls: list[tuple[Contract, HighLevelCall]] | None = None
 175        self._all_library_calls: list[LibraryCall] | None = None
 176        self._all_low_level_calls: list[LowLevelCall] | None = None
 177        self._all_solidity_calls: list[SolidityCall] | None = None
 178        self._all_variables_read: list[Variable] | None = None
 179        self._all_variables_written: list[Variable] | None = None
 180        self._all_state_variables_read: list[StateVariable] | None = None
 181        self._all_solidity_variables_read: list[SolidityVariable] | None = None
 182        self._all_state_variables_written: list[StateVariable] | None = None
 183        self._all_slithir_variables: list[SlithIRVariable] | None = None
 184        self._all_nodes: list[Node] | None = None
 185        self._all_conditional_state_variables_read: list[StateVariable] | None = None
 186        self._all_conditional_state_variables_read_with_loop: list[StateVariable] | None = None
 187        self._all_conditional_solidity_variables_read: list[SolidityVariable] | None = None
 188        self._all_conditional_solidity_variables_read_with_loop: list[SolidityVariable] | None = (
 189            None
 190        )
 191        self._all_solidity_variables_used_as_args: list[SolidityVariable] | None = None
 192
 193        self._is_shadowed: bool = False
 194        self._shadows: bool = False
 195
 196        # set(ReacheableNode)
 197        self._reachable_from_nodes: set[ReacheableNode] = set()
 198        self._reachable_from_functions: set[Function] = set()
 199        self._all_reachable_from_functions: set[Function] | None = None
 200
 201        # Constructor, fallback, State variable constructor
 202        self._function_type: FunctionType | None = None
 203        self._is_constructor: bool | None = None
 204
 205        # Computed on the fly, can be True of False
 206        self._can_reenter: bool | None = None
 207        self._can_send_eth: bool | None = None
 208
 209        self._nodes_ordered_dominators: list[Node] | None = None
 210
 211        self._counter_nodes = 0
 212
 213        # Memoize parameters:
 214        # TODO: identify all the memoize parameters and add a way to undo the memoization
 215        self._full_name: str | None = None
 216        self._signature: tuple[str, list[str], list[str]] | None = None
 217        self._solidity_signature: str | None = None
 218        self._signature_str: str | None = None
 219        self._canonical_name: str | None = None
 220        self._is_protected: bool | None = None
 221
 222        self.compilation_unit: SlitherCompilationUnit = compilation_unit
 223
 224        self.function_language: FunctionLanguage = (
 225            FunctionLanguage.Solidity if compilation_unit.is_solidity else FunctionLanguage.Vyper
 226        )
 227
 228        self._id: str | None = None
 229
 230        # To be improved with a parsing of the documentation
 231        self.has_documentation: bool = False
 232
 233    ###################################################################################
 234    ###################################################################################
 235    # region General properties
 236    ###################################################################################
 237    ###################################################################################
 238
 239    @property
 240    def name(self) -> str:
 241        """
 242        str: function name
 243        """
 244        if self._name == "" and self._function_type == FunctionType.CONSTRUCTOR:
 245            return "constructor"
 246        if self._name == "" and self._function_type == FunctionType.FALLBACK:
 247            return "fallback"
 248        if self._function_type == FunctionType.RECEIVE:
 249            return "receive"
 250        if self._function_type == FunctionType.CONSTRUCTOR_VARIABLES:
 251            return "slitherConstructorVariables"
 252        if self._function_type == FunctionType.CONSTRUCTOR_CONSTANT_VARIABLES:
 253            return "slitherConstructorConstantVariables"
 254        return self._name
 255
 256    @name.setter
 257    def name(self, new_name: str):
 258        self._name = new_name
 259
 260    @property
 261    def internal_scope(self) -> list[str]:
 262        """
 263        Return a list of name representing the scope of the function
 264        This is used to model nested functions declared in YUL
 265
 266        :return:
 267        """
 268        return self._internal_scope
 269
 270    @internal_scope.setter
 271    def internal_scope(self, new_scope: list[str]):
 272        self._internal_scope = new_scope
 273
 274    @property
 275    def full_name(self) -> str:
 276        """
 277        str: func_name(type1,type2)
 278        Return the function signature without the return values
 279        The difference between this function and solidity_function is that full_name does not translate the underlying
 280        type (ex: structure, contract to address, ...)
 281        """
 282        if self._full_name is None:
 283            name, parameters, _ = self.signature
 284            full_name = ".".join(self._internal_scope + [name]) + "(" + ",".join(parameters) + ")"
 285            self._full_name = full_name
 286        return self._full_name
 287
 288    @property
 289    @abstractmethod
 290    def canonical_name(self) -> str:
 291        """
 292        str: contract.func_name(type1,type2)
 293        Return the function signature without the return values
 294        """
 295        return ""
 296
 297    @property
 298    def contains_assembly(self) -> bool:
 299        return self._contains_assembly
 300
 301    @contains_assembly.setter
 302    def contains_assembly(self, c: bool):
 303        self._contains_assembly = c
 304
 305    def can_reenter(self, callstack: list[Union["Function", "Variable"]] | None = None) -> bool:
 306        """
 307        Check if the function can re-enter
 308        Follow internal calls.
 309        Do not consider CREATE as potential re-enter, but check if the
 310        destination's constructor can contain a call (recurs. follow nested CREATE)
 311        For Solidity > 0.5, filter access to public variables and constant/pure/view
 312        For call to this. check if the destination can re-enter
 313        Do not consider Send/Transfer as there is not enough gas
 314        :param callstack: used internally to check for recursion
 315        :return bool:
 316        """
 317        from slither.slithir.operations import Call
 318
 319        if self._can_reenter is None:
 320            self._can_reenter = False
 321            for ir in self.all_slithir_operations():
 322                if isinstance(ir, Call) and ir.can_reenter(callstack):
 323                    self._can_reenter = True
 324                    return True
 325        return self._can_reenter
 326
 327    def can_send_eth(self) -> bool:
 328        """
 329        Check if the function or any internal (not external) functions called by it can send eth
 330        :return bool:
 331        """
 332        from slither.slithir.operations import Call
 333
 334        if self._can_send_eth is None:
 335            self._can_send_eth = False
 336            for ir in self.all_slithir_operations():
 337                if isinstance(ir, Call) and ir.can_send_eth():
 338                    self._can_send_eth = True
 339                    return True
 340        return self._can_send_eth
 341
 342    @property
 343    def is_checked(self) -> bool:
 344        """
 345        Return true if the overflow are enabled by default
 346
 347
 348        :return:
 349        """
 350
 351        return self.compilation_unit.solc_version >= "0.8.0"
 352
 353    @property
 354    def id(self) -> str | None:
 355        """
 356        Return the reference ID of the function, if available.
 357
 358        :return:
 359        :rtype:
 360        """
 361        return self._id
 362
 363    @id.setter
 364    def id(self, new_id: str):
 365        self._id = new_id
 366
 367    @property
 368    @abstractmethod
 369    def file_scope(self) -> "FileScope":
 370        pass
 371
 372    # endregion
 373    ###################################################################################
 374    ###################################################################################
 375    # region Type (FunctionType)
 376    ###################################################################################
 377    ###################################################################################
 378
 379    def set_function_type(self, t: FunctionType) -> None:
 380        assert isinstance(t, FunctionType)
 381        self._function_type = t
 382
 383    @property
 384    def function_type(self) -> FunctionType | None:
 385        return self._function_type
 386
 387    @function_type.setter
 388    def function_type(self, t: FunctionType):
 389        self._function_type = t
 390
 391    @property
 392    def is_constructor(self) -> bool:
 393        """
 394        bool: True if the function is the constructor
 395        """
 396        return self._function_type == FunctionType.CONSTRUCTOR
 397
 398    @property
 399    def is_constructor_variables(self) -> bool:
 400        """
 401        bool: True if the function is the constructor of the variables
 402        Slither has inbuilt functions to hold the state variables initialization
 403        """
 404        return self._function_type in [
 405            FunctionType.CONSTRUCTOR_VARIABLES,
 406            FunctionType.CONSTRUCTOR_CONSTANT_VARIABLES,
 407        ]
 408
 409    @property
 410    def is_fallback(self) -> bool:
 411        """
 412            Determine if the function is the fallback function for the contract
 413        Returns
 414            (bool)
 415        """
 416        return self._function_type == FunctionType.FALLBACK
 417
 418    @property
 419    def is_receive(self) -> bool:
 420        """
 421            Determine if the function is the receive function for the contract
 422        Returns
 423            (bool)
 424        """
 425        return self._function_type == FunctionType.RECEIVE
 426
 427    # endregion
 428    ###################################################################################
 429    ###################################################################################
 430    # region Payable
 431    ###################################################################################
 432    ###################################################################################
 433
 434    @property
 435    def payable(self) -> bool:
 436        """
 437        bool: True if the function is payable
 438        """
 439        return self._payable
 440
 441    @payable.setter
 442    def payable(self, p: bool):
 443        self._payable = p
 444
 445    # endregion
 446    ###################################################################################
 447    ###################################################################################
 448    # region Virtual
 449    ###################################################################################
 450    ###################################################################################
 451
 452    @property
 453    def is_virtual(self) -> bool:
 454        """
 455        Note for Solidity < 0.6.0 it will always be false
 456        bool: True if the function is virtual
 457        """
 458        return self._virtual
 459
 460    @is_virtual.setter
 461    def is_virtual(self, v: bool):
 462        self._virtual = v
 463
 464    @property
 465    def is_override(self) -> bool:
 466        """
 467        Note for Solidity < 0.6.0 it will always be false
 468        bool: True if the function overrides a base function
 469        """
 470        return len(self._overrides) > 0
 471
 472    @property
 473    def overridden_by(self) -> list["FunctionContract"]:
 474        """
 475        List["FunctionContract"]: List of functions in child contracts that override this function
 476        This may include distinct instances of the same function due to inheritance
 477        """
 478        return self._overridden_by
 479
 480    @property
 481    def overrides(self) -> list["FunctionContract"]:
 482        """
 483        List["FunctionContract"]: List of functions in parent contracts that this function overrides
 484        This may include distinct instances of the same function due to inheritance
 485        """
 486        return self._overrides
 487
 488    # endregion
 489    ###################################################################################
 490    ###################################################################################
 491    # region Visibility
 492    ###################################################################################
 493    ###################################################################################
 494
 495    @property
 496    def visibility(self) -> str:
 497        """
 498        str: Function visibility
 499        """
 500        assert self._visibility is not None
 501        return self._visibility
 502
 503    @visibility.setter
 504    def visibility(self, v: str):
 505        self._visibility = v
 506
 507    def set_visibility(self, v: str) -> None:
 508        self._visibility = v
 509
 510    @property
 511    def view(self) -> bool:
 512        """
 513        bool: True if the function is declared as view
 514        """
 515        return self._view
 516
 517    @view.setter
 518    def view(self, v: bool):
 519        self._view = v
 520
 521    @property
 522    def pure(self) -> bool:
 523        """
 524        bool: True if the function is declared as pure
 525        """
 526        return self._pure
 527
 528    @pure.setter
 529    def pure(self, p: bool):
 530        self._pure = p
 531
 532    @property
 533    def is_shadowed(self) -> bool:
 534        return self._is_shadowed
 535
 536    @is_shadowed.setter
 537    def is_shadowed(self, is_shadowed):
 538        self._is_shadowed = is_shadowed
 539
 540    @property
 541    def shadows(self) -> bool:
 542        return self._shadows
 543
 544    @shadows.setter
 545    def shadows(self, _shadows: bool):
 546        self._shadows = _shadows
 547
 548    # endregion
 549    ###################################################################################
 550    ###################################################################################
 551    # region Function's body
 552    ###################################################################################
 553    ###################################################################################
 554
 555    @property
 556    def is_implemented(self) -> bool:
 557        """
 558        bool: True if the function is implemented
 559        """
 560        return self._is_implemented
 561
 562    @is_implemented.setter
 563    def is_implemented(self, is_impl: bool):
 564        self._is_implemented = is_impl
 565
 566    @property
 567    def is_empty(self) -> bool:
 568        """
 569        bool: True if the function is empty, None if the function is an interface
 570        """
 571        return self._is_empty
 572
 573    @is_empty.setter
 574    def is_empty(self, empty: bool):
 575        self._is_empty = empty
 576
 577    # endregion
 578    ###################################################################################
 579    ###################################################################################
 580    # region Nodes
 581    ###################################################################################
 582    ###################################################################################
 583
 584    @property
 585    def nodes(self) -> list["Node"]:
 586        """
 587        list(Node): List of the nodes
 588        """
 589        return list(self._nodes)
 590
 591    @nodes.setter
 592    def nodes(self, nodes: list["Node"]):
 593        self._nodes = nodes
 594
 595    @property
 596    def entry_point(self) -> Optional["Node"]:
 597        """
 598        Node: Entry point of the function
 599        """
 600        return self._entry_point
 601
 602    @entry_point.setter
 603    def entry_point(self, node: "Node"):
 604        self._entry_point = node
 605
 606    def add_node(self, node: "Node") -> None:
 607        if not self._entry_point:
 608            self._entry_point = node
 609        self._nodes.append(node)
 610
 611    @property
 612    def nodes_ordered_dominators(self) -> list["Node"]:
 613        # TODO: does not work properly; most likely due to modifier call
 614        # This will not work for modifier call that lead to multiple nodes
 615        # from slither.core.cfg.node import NodeType
 616        if self._nodes_ordered_dominators is None:
 617            self._nodes_ordered_dominators = []
 618            if self.entry_point:
 619                self._compute_nodes_ordered_dominators(self.entry_point)
 620
 621            for node in self.nodes:
 622                # if node.type == NodeType.OTHER_ENTRYPOINT:
 623                if node not in self._nodes_ordered_dominators:
 624                    self._compute_nodes_ordered_dominators(node)
 625
 626        return self._nodes_ordered_dominators
 627
 628    def _compute_nodes_ordered_dominators(self, node: "Node"):
 629        assert self._nodes_ordered_dominators is not None
 630        if node in self._nodes_ordered_dominators:
 631            return
 632        self._nodes_ordered_dominators.append(node)
 633        for dom in node.dominance_exploration_ordered:
 634            self._compute_nodes_ordered_dominators(dom)
 635
 636    # endregion
 637    ###################################################################################
 638    ###################################################################################
 639    # region Parameters
 640    ###################################################################################
 641    ###################################################################################
 642
 643    @property
 644    def parameters(self) -> list["LocalVariable"]:
 645        """
 646        list(LocalVariable): List of the parameters
 647        """
 648        return list(self._parameters)
 649
 650    def add_parameters(self, p: "LocalVariable") -> None:
 651        self._parameters.append(p)
 652
 653    @property
 654    def parameters_ssa(self) -> list["LocalIRVariable"]:
 655        """
 656        list(LocalIRVariable): List of the parameters (SSA form)
 657        """
 658        return list(self._parameters_ssa)
 659
 660    def add_parameter_ssa(self, var: "LocalIRVariable") -> None:
 661        self._parameters_ssa.append(var)
 662
 663    def parameters_src(self) -> SourceMapping:
 664        return self._parameters_src
 665
 666    # endregion
 667    ###################################################################################
 668    ###################################################################################
 669    # region Return values
 670    ###################################################################################
 671    ###################################################################################
 672
 673    @property
 674    def return_type(self) -> list[Type] | None:
 675        """
 676        Return the list of return type
 677        If no return, return None
 678        """
 679        returns = self.returns
 680        if returns:
 681            return [r.type for r in returns]
 682        return None
 683
 684    def returns_src(self) -> SourceMapping:
 685        return self._returns_src
 686
 687    @property
 688    def type(self) -> list[Type] | None:
 689        """
 690        Return the list of return type
 691        If no return, return None
 692        Alias of return_type
 693        """
 694        return self.return_type
 695
 696    @property
 697    def returns(self) -> list["LocalVariable"]:
 698        """
 699        list(LocalVariable): List of the return variables
 700        """
 701        return list(self._returns)
 702
 703    def add_return(self, r: "LocalVariable") -> None:
 704        self._returns.append(r)
 705
 706    @property
 707    def returns_ssa(self) -> list["LocalIRVariable"]:
 708        """
 709        list(LocalIRVariable): List of the return variables (SSA form)
 710        """
 711        return list(self._returns_ssa)
 712
 713    def add_return_ssa(self, var: "LocalIRVariable") -> None:
 714        self._returns_ssa.append(var)
 715
 716    # endregion
 717    ###################################################################################
 718    ###################################################################################
 719    # region Modifiers
 720    ###################################################################################
 721    ###################################################################################
 722
 723    @property
 724    def modifiers(self) -> list[Union["Contract", "Function"]]:
 725        """
 726        list(Modifier): List of the modifiers
 727        Can be contract for constructor's calls
 728
 729        """
 730        return [c.modifier for c in self._modifiers]
 731
 732    def add_modifier(self, modif: "ModifierStatements") -> None:
 733        self._modifiers.append(modif)
 734
 735    @property
 736    def modifiers_statements(self) -> list[ModifierStatements]:
 737        """
 738        list(ModifierCall): List of the modifiers call (include expression and irs)
 739        """
 740        return list(self._modifiers)
 741
 742    @property
 743    def explicit_base_constructor_calls(self) -> list["Function"]:
 744        """
 745        list(Function): List of the base constructors called explicitly by this presumed constructor definition.
 746
 747                        Base constructors implicitly or explicitly called by the contract definition will not be
 748                        included.
 749        """
 750        # This is a list of contracts internally, so we convert it to a list of constructor functions.
 751        return [
 752            c.modifier.constructors_declared
 753            for c in self._explicit_base_constructor_calls
 754            if c.modifier.constructors_declared
 755        ]
 756
 757    @property
 758    def explicit_base_constructor_calls_statements(self) -> list[ModifierStatements]:
 759        """
 760        list(ModifierCall): List of the base constructors called explicitly by this presumed constructor definition.
 761
 762        """
 763        # This is a list of contracts internally, so we convert it to a list of constructor functions.
 764        return list(self._explicit_base_constructor_calls)
 765
 766    def add_explicit_base_constructor_calls_statements(self, modif: ModifierStatements) -> None:
 767        self._explicit_base_constructor_calls.append(modif)
 768
 769    # endregion
 770    ###################################################################################
 771    ###################################################################################
 772    # region Variables
 773    ###################################################################################
 774    ###################################################################################
 775
 776    @property
 777    def variables(self) -> list[LocalVariable]:
 778        """
 779        Return all local variables
 780        Include parameters and return values
 781        """
 782        return list(self._variables.values())
 783
 784    @property
 785    def local_variables(self) -> list[LocalVariable]:
 786        """
 787        Return all local variables (dont include parameters and return values)
 788        """
 789        return list(set(self.variables) - set(self.returns) - set(self.parameters))
 790
 791    @property
 792    def variables_as_dict(self) -> dict[str, LocalVariable]:
 793        return self._variables
 794
 795    @property
 796    def variables_read(self) -> list["Variable"]:
 797        """
 798        list(Variable): Variables read (local/state/solidity)
 799        """
 800        return list(self._vars_read)
 801
 802    @property
 803    def variables_written(self) -> list["Variable"]:
 804        """
 805        list(Variable): Variables written (local/state/solidity)
 806        """
 807        return list(self._vars_written)
 808
 809    @property
 810    def state_variables_read(self) -> list["StateVariable"]:
 811        """
 812        list(StateVariable): State variables read
 813        """
 814        return list(self._state_vars_read)
 815
 816    @property
 817    def solidity_variables_read(self) -> list["SolidityVariable"]:
 818        """
 819        list(SolidityVariable): Solidity variables read
 820        """
 821        return list(self._solidity_vars_read)
 822
 823    @property
 824    def state_variables_written(self) -> list["StateVariable"]:
 825        """
 826        list(StateVariable): State variables written
 827        """
 828        return list(self._state_vars_written)
 829
 830    @property
 831    def variables_read_or_written(self) -> list["Variable"]:
 832        """
 833        list(Variable): Variables read or written (local/state/solidity)
 834        """
 835        return list(self._vars_read_or_written)
 836
 837    @property
 838    def variables_read_as_expression(self) -> list["Expression"]:
 839        return self._expression_vars_read
 840
 841    @property
 842    def variables_written_as_expression(self) -> list["Expression"]:
 843        return self._expression_vars_written
 844
 845    @property
 846    def slithir_variables(self) -> list["SlithIRVariable"]:
 847        """
 848        Temporary and Reference Variables (not SSA form)
 849        """
 850
 851        return list(self._slithir_variables)
 852
 853    # endregion
 854    ###################################################################################
 855    ###################################################################################
 856    # region Calls
 857    ###################################################################################
 858    ###################################################################################
 859
 860    @property
 861    def internal_calls(self) -> list["InternalCall"]:
 862        """
 863        list(InternalCall): List of IR operations for internal calls
 864        """
 865        return list(self._internal_calls)
 866
 867    @property
 868    def solidity_calls(self) -> list["SolidityCall"]:
 869        """
 870        list(SolidityCall): List of IR operations for Solidity calls
 871        """
 872        return list(self._solidity_calls)
 873
 874    @property
 875    def high_level_calls(self) -> list[tuple["Contract", "HighLevelCall"]]:
 876        """
 877        list(Tuple(Contract, "HighLevelCall")): List of call target contract and IR of the high level call
 878        A variable is called in case of call to a public state variable
 879        Include library calls
 880        """
 881        return list(self._high_level_calls)
 882
 883    @property
 884    def library_calls(self) -> list["LibraryCall"]:
 885        """
 886        list(LibraryCall): List of IR operations for library calls
 887        """
 888        return list(self._library_calls)
 889
 890    @property
 891    def low_level_calls(self) -> list["LowLevelCall"]:
 892        """
 893        list(LowLevelCall): List of IR operations for low level calls
 894        A low level call is defined by
 895        - the variable called
 896        - the name of the function (call/delegatecall/callcode)
 897        """
 898        return list(self._low_level_calls)
 899
 900    @property
 901    def external_calls_as_expressions(self) -> list["Expression"]:
 902        """
 903        list(ExpressionCall): List of message calls (that creates a transaction)
 904        """
 905        return list(self._external_calls_as_expressions)
 906
 907    # endregion
 908    ###################################################################################
 909    ###################################################################################
 910    # region Expressions
 911    ###################################################################################
 912    ###################################################################################
 913
 914    @property
 915    def calls_as_expressions(self) -> list["Expression"]:
 916        return self._expression_calls
 917
 918    @property
 919    def expressions(self) -> list["Expression"]:
 920        """
 921        list(Expression): List of the expressions
 922        """
 923        if self._expressions is None:
 924            expressionss = [n.expression for n in self.nodes]
 925            expressions = [e for e in expressionss if e]
 926            self._expressions = expressions
 927        return self._expressions
 928
 929    @property
 930    def return_values(self) -> list["SlithIRVariable"]:
 931        """
 932        list(Return Values): List of the return values
 933        """
 934        from slither.core.cfg.node import NodeType
 935        from slither.slithir.operations import Return
 936        from slither.slithir.variables import Constant
 937
 938        if self._return_values is None:
 939            return_values = []
 940            returns = [n for n in self.nodes if n.type == NodeType.RETURN]
 941            [
 942                return_values.extend(ir.values)
 943                for node in returns
 944                for ir in node.irs
 945                if isinstance(ir, Return)
 946            ]
 947            self._return_values = list({x for x in return_values if not isinstance(x, Constant)})
 948        return self._return_values
 949
 950    @property
 951    def return_values_ssa(self) -> list["SlithIRVariable"]:
 952        """
 953        list(Return Values in SSA form): List of the return values in ssa form
 954        """
 955        from slither.core.cfg.node import NodeType
 956        from slither.slithir.operations import Return
 957        from slither.slithir.variables import Constant
 958
 959        if self._return_values_ssa is None:
 960            return_values_ssa = []
 961            returns = [n for n in self.nodes if n.type == NodeType.RETURN]
 962            [
 963                return_values_ssa.extend(ir.values)
 964                for node in returns
 965                for ir in node.irs_ssa
 966                if isinstance(ir, Return)
 967            ]
 968            self._return_values_ssa = list(
 969                {x for x in return_values_ssa if not isinstance(x, Constant)}
 970            )
 971        return self._return_values_ssa
 972
 973    # endregion
 974    ###################################################################################
 975    ###################################################################################
 976    # region SlithIR
 977    ###################################################################################
 978    ###################################################################################
 979
 980    @property
 981    def slithir_operations(self) -> list["Operation"]:
 982        """
 983        list(Operation): List of the slithir operations
 984        """
 985        if self._slithir_operations is None:
 986            operationss = [n.irs for n in self.nodes]
 987            operations = [item for sublist in operationss for item in sublist if item]
 988            self._slithir_operations = operations
 989        return self._slithir_operations
 990
 991    @property
 992    def slithir_ssa_operations(self) -> list["Operation"]:
 993        """
 994        list(Operation): List of the slithir operations (SSA)
 995        """
 996        if self._slithir_ssa_operations is None:
 997            operationss = [n.irs_ssa for n in self.nodes]
 998            operations = [item for sublist in operationss for item in sublist if item]
 999            self._slithir_ssa_operations = operations
1000        return self._slithir_ssa_operations
1001
1002    # endregion
1003    ###################################################################################
1004    ###################################################################################
1005    # region Signature
1006    ###################################################################################
1007    ###################################################################################
1008
1009    @property
1010    def solidity_signature(self) -> str:
1011        """
1012        Return a signature following the Solidity Standard
1013        Contract and converted into address
1014
1015        It might still keep internal types (ex: structure name) for internal functions.
1016        The reason is that internal functions allows recursive structure definition, which
1017        can't be converted following the Solidity stand ard
1018
1019        :return: the solidity signature
1020        """
1021        if self._solidity_signature is None:
1022            parameters = [
1023                convert_type_for_solidity_signature_to_string(x.type) for x in self.parameters
1024            ]
1025            self._solidity_signature = self.name + "(" + ",".join(parameters) + ")"
1026        return self._solidity_signature
1027
1028    @property
1029    def signature(self) -> tuple[str, list[str], list[str]]:
1030        """
1031        (str, list(str), list(str)): Function signature as
1032        (name, list parameters type, list return values type)
1033        """
1034        # FIXME memoizing this function is not working properly for vyper
1035        # if self._signature is None:
1036        return (
1037            self.name,
1038            [str(x.type) for x in self.parameters],
1039            [str(x.type) for x in self.returns],
1040        )
1041        #     self._signature = signature
1042        # return self._signature
1043
1044    @property
1045    def signature_str(self) -> str:
1046        """
1047        str: func_name(type1,type2) returns (type3)
1048        Return the function signature as a str (contains the return values)
1049        """
1050        if self._signature_str is None:
1051            name, parameters, returnVars = self.signature
1052            self._signature_str = (
1053                name + "(" + ",".join(parameters) + ") returns(" + ",".join(returnVars) + ")"
1054            )
1055        return self._signature_str
1056
1057    # endregion
1058    ###################################################################################
1059    ###################################################################################
1060    # region Functions
1061    ###################################################################################
1062    ###################################################################################
1063
1064    @property
1065    @abstractmethod
1066    def functions_shadowed(self) -> list["Function"]:
1067        pass
1068
1069    # endregion
1070    ###################################################################################
1071    ###################################################################################
1072    # region Reachable
1073    ###################################################################################
1074    ###################################################################################
1075
1076    @property
1077    def reachable_from_nodes(self) -> set[ReacheableNode]:
1078        """
1079        Return
1080            ReacheableNode
1081        """
1082        return self._reachable_from_nodes
1083
1084    @property
1085    def reachable_from_functions(self) -> set["Function"]:
1086        return self._reachable_from_functions
1087
1088    @property
1089    def all_reachable_from_functions(self) -> set["Function"]:
1090        """
1091        Give the recursive version of reachable_from_functions (all the functions that lead to call self in the CFG)
1092        """
1093        if self._all_reachable_from_functions is None:
1094            functions: set[Function] = set()
1095
1096            new_functions = self.reachable_from_functions
1097            # iterate until we have are finding new functions
1098            while new_functions and not new_functions.issubset(functions):
1099                functions = functions.union(new_functions)
1100                # Use a temporary set, because we iterate over new_functions
1101                new_functionss: set[Function] = set()
1102                for f in new_functions:
1103                    new_functionss = new_functionss.union(f.reachable_from_functions)
1104                new_functions = new_functionss - functions
1105
1106            self._all_reachable_from_functions = functions
1107        return self._all_reachable_from_functions
1108
1109    def add_reachable_from_node(self, n: "Node", ir: "Operation") -> None:
1110        self._reachable_from_nodes.add(ReacheableNode(n, ir))
1111        self._reachable_from_functions.add(n.function)
1112
1113    # endregion
1114    ###################################################################################
1115    ###################################################################################
1116    # region Recursive getters
1117    ###################################################################################
1118    ###################################################################################
1119
1120    def _explore_functions(self, f_new_values: Callable[["Function"], list]) -> list[Any]:
1121        values = f_new_values(self)
1122        explored: set[Function] = {self}
1123        to_explore_set: set[Function] = set()
1124
1125        for ir in self.internal_calls:
1126            if isinstance(ir.function, Function) and ir.function not in explored:
1127                to_explore_set.add(ir.function)
1128        for ir in self.library_calls:
1129            if isinstance(ir.function, Function) and ir.function not in explored:
1130                to_explore_set.add(ir.function)
1131        for m in self.modifiers:
1132            if m not in explored:
1133                to_explore_set.add(m)
1134
1135        while to_explore_set:
1136            f = to_explore_set.pop()
1137            if f in explored:
1138                continue
1139            explored.add(f)
1140
1141            values += f_new_values(f)
1142
1143            for ir in f.internal_calls:
1144                if isinstance(ir.function, Function) and ir.function not in explored:
1145                    to_explore_set.add(ir.function)
1146            for ir in f.library_calls:
1147                if isinstance(ir.function, Function) and ir.function not in explored:
1148                    to_explore_set.add(ir.function)
1149            for m in f.modifiers:
1150                if m not in explored:
1151                    to_explore_set.add(m)
1152
1153        return list(set(values))
1154
1155    def all_variables_read(self) -> list["Variable"]:
1156        """recursive version of variables_read"""
1157        if self._all_variables_read is None:
1158            self._all_variables_read = self._explore_functions(lambda x: x.variables_read)
1159        return self._all_variables_read
1160
1161    def all_variables_written(self) -> list["Variable"]:
1162        """recursive version of variables_written"""
1163        if self._all_variables_written is None:
1164            self._all_variables_written = self._explore_functions(lambda x: x.variables_written)
1165        return self._all_variables_written
1166
1167    def all_state_variables_read(self) -> list["StateVariable"]:
1168        """recursive version of variables_read"""
1169        if self._all_state_variables_read is None:
1170            self._all_state_variables_read = self._explore_functions(
1171                lambda x: x.state_variables_read
1172            )
1173        return self._all_state_variables_read
1174
1175    def all_solidity_variables_read(self) -> list[SolidityVariable]:
1176        """recursive version of solidity_read"""
1177        if self._all_solidity_variables_read is None:
1178            self._all_solidity_variables_read = self._explore_functions(
1179                lambda x: x.solidity_variables_read
1180            )
1181        return self._all_solidity_variables_read
1182
1183    def all_slithir_variables(self) -> list["SlithIRVariable"]:
1184        """recursive version of slithir_variables"""
1185        if self._all_slithir_variables is None:
1186            self._all_slithir_variables = self._explore_functions(lambda x: x.slithir_variables)
1187        return self._all_slithir_variables
1188
1189    def all_nodes(self) -> list["Node"]:
1190        """recursive version of nodes"""
1191        if self._all_nodes is None:
1192            self._all_nodes = self._explore_functions(lambda x: x.nodes)
1193        return self._all_nodes
1194
1195    def all_expressions(self) -> list["Expression"]:
1196        """recursive version of variables_read"""
1197        if self._all_expressions is None:
1198            self._all_expressions = self._explore_functions(lambda x: x.expressions)
1199        return self._all_expressions
1200
1201    def all_slithir_operations(self) -> list["Operation"]:
1202        if self._all_slithir_operations is None:
1203            self._all_slithir_operations = self._explore_functions(lambda x: x.slithir_operations)
1204        return self._all_slithir_operations
1205
1206    def all_state_variables_written(self) -> list[StateVariable]:
1207        """recursive version of variables_written"""
1208        if self._all_state_variables_written is None:
1209            self._all_state_variables_written = self._explore_functions(
1210                lambda x: x.state_variables_written
1211            )
1212        return self._all_state_variables_written
1213
1214    def all_internal_calls(self) -> list["InternalCall"]:
1215        """recursive version of internal_calls"""
1216        if self._all_internals_calls is None:
1217            self._all_internals_calls = self._explore_functions(lambda x: x.internal_calls)
1218        return self._all_internals_calls
1219
1220    def all_low_level_calls(self) -> list["LowLevelCall"]:
1221        """recursive version of low_level calls"""
1222        if self._all_low_level_calls is None:
1223            self._all_low_level_calls = self._explore_functions(lambda x: x.low_level_calls)
1224        return self._all_low_level_calls
1225
1226    def all_high_level_calls(self) -> list[tuple["Contract", "HighLevelCall"]]:
1227        """recursive version of high_level calls"""
1228        if self._all_high_level_calls is None:
1229            self._all_high_level_calls = self._explore_functions(lambda x: x.high_level_calls)
1230        return self._all_high_level_calls
1231
1232    def all_library_calls(self) -> list["LibraryCall"]:
1233        """recursive version of library calls"""
1234        if self._all_library_calls is None:
1235            self._all_library_calls = self._explore_functions(lambda x: x.library_calls)
1236        return self._all_library_calls
1237
1238    def all_solidity_calls(self) -> list["SolidityCall"]:
1239        """recursive version of solidity calls"""
1240        if self._all_solidity_calls is None:
1241            self._all_solidity_calls = self._explore_functions(lambda x: x.solidity_calls)
1242        return self._all_solidity_calls
1243
1244    @staticmethod
1245    def _explore_func_cond_read(func: "Function", include_loop: bool) -> list["StateVariable"]:
1246        ret = [n.state_variables_read for n in func.nodes if n.is_conditional(include_loop)]
1247        return [item for sublist in ret for item in sublist]
1248
1249    def all_conditional_state_variables_read(self, include_loop=True) -> list["StateVariable"]:
1250        """
1251        Return the state variable used in a condition
1252
1253        Over approximate and also return index access
1254        It won't work if the variable is assigned to a temp variable
1255        """
1256        if include_loop:
1257            if self._all_conditional_state_variables_read_with_loop is None:
1258                self._all_conditional_state_variables_read_with_loop = self._explore_functions(
1259                    lambda x: self._explore_func_cond_read(x, include_loop)
1260                )
1261            return self._all_conditional_state_variables_read_with_loop
1262        if self._all_conditional_state_variables_read is None:
1263            self._all_conditional_state_variables_read = self._explore_functions(
1264                lambda x: self._explore_func_cond_read(x, include_loop)
1265            )
1266        return self._all_conditional_state_variables_read
1267
1268    @staticmethod
1269    def _solidity_variable_in_binary(node: "Node") -> list[SolidityVariable]:
1270        from slither.slithir.operations.binary import Binary
1271
1272        ret = []
1273        for ir in node.irs:
1274            if isinstance(ir, Binary):
1275                ret += ir.read
1276        return [var for var in ret if isinstance(var, SolidityVariable)]
1277
1278    @staticmethod
1279    def _explore_func_conditional(
1280        func: "Function",
1281        f: Callable[["Node"], list[SolidityVariable]],
1282        include_loop: bool,
1283    ) -> list[Any]:
1284        ret = [f(n) for n in func.nodes if n.is_conditional(include_loop)]
1285        return [item for sublist in ret for item in sublist]
1286
1287    def all_conditional_solidity_variables_read(
1288        self, include_loop: bool = True
1289    ) -> list[SolidityVariable]:
1290        """
1291        Return the Solidity variables directly used in a condition
1292
1293        Use of the IR to filter index access
1294        Assumption: the solidity vars are used directly in the conditional node
1295        It won't work if the variable is assigned to a temp variable
1296        """
1297        if include_loop:
1298            if self._all_conditional_solidity_variables_read_with_loop is None:
1299                self._all_conditional_solidity_variables_read_with_loop = self._explore_functions(
1300                    lambda x: self._explore_func_conditional(
1301                        x, self._solidity_variable_in_binary, include_loop
1302                    )
1303                )
1304            return self._all_conditional_solidity_variables_read_with_loop
1305
1306        if self._all_conditional_solidity_variables_read is None:
1307            self._all_conditional_solidity_variables_read = self._explore_functions(
1308                lambda x: self._explore_func_conditional(
1309                    x, self._solidity_variable_in_binary, include_loop
1310                )
1311            )
1312        return self._all_conditional_solidity_variables_read
1313
1314    @staticmethod
1315    def _solidity_variable_in_internal_calls(node: "Node") -> list[SolidityVariable]:
1316        from slither.slithir.operations.internal_call import InternalCall
1317
1318        ret = []
1319        for ir in node.irs:
1320            if isinstance(ir, InternalCall):
1321                ret += ir.read
1322        return [var for var in ret if isinstance(var, SolidityVariable)]
1323
1324    @staticmethod
1325    def _explore_func_nodes(
1326        func: "Function", f: Callable[["Node"], list[SolidityVariable]]
1327    ) -> list[Any | SolidityVariableComposed]:
1328        ret = [f(n) for n in func.nodes]
1329        return [item for sublist in ret for item in sublist]
1330
1331    def all_solidity_variables_used_as_args(self) -> list[SolidityVariable]:
1332        """
1333        Return the Solidity variables directly used in a call
1334
1335        Use of the IR to filter index access
1336        Used to catch check(msg.sender)
1337        """
1338        if self._all_solidity_variables_used_as_args is None:
1339            self._all_solidity_variables_used_as_args = self._explore_functions(
1340                lambda x: self._explore_func_nodes(x, self._solidity_variable_in_internal_calls)
1341            )
1342        return self._all_solidity_variables_used_as_args
1343
1344    # endregion
1345    ###################################################################################
1346    ###################################################################################
1347    # region Visitor
1348    ###################################################################################
1349    ###################################################################################
1350
1351    def apply_visitor(self, Visitor: Callable) -> list:
1352        """
1353            Apply a visitor to all the function expressions
1354        Args:
1355            Visitor: slither.visitors
1356        Returns
1357            list(): results of the visit
1358        """
1359        expressions = self.expressions
1360        v = [Visitor(e).result() for e in expressions]
1361        return [item for sublist in v for item in sublist]
1362
1363    # endregion
1364    ###################################################################################
1365    ###################################################################################
1366    # region Getters from/to object
1367    ###################################################################################
1368    ###################################################################################
1369
1370    def get_local_variable_from_name(self, variable_name: str) -> LocalVariable | None:
1371        """
1372            Return a local variable from a name
1373
1374        Args:
1375            variable_name (str): name of the variable
1376        Returns:
1377            LocalVariable
1378        """
1379        return next((v for v in self.variables if v.name == variable_name), None)
1380
1381    # endregion
1382    ###################################################################################
1383    ###################################################################################
1384    # region Export
1385    ###################################################################################
1386    ###################################################################################
1387
1388    def cfg_to_dot(self, filename: str):
1389        """
1390            Export the function to a dot file
1391        Args:
1392            filename (str)
1393        """
1394        with open(filename, "w", encoding="utf8") as f:
1395            f.write("digraph{\n")
1396            for node in self.nodes:
1397                f.write(f'{node.node_id}[label="{node!s}"];\n')
1398                for son in node.sons:
1399                    f.write(f"{node.node_id}->{son.node_id};\n")
1400
1401            f.write("}\n")
1402
1403    def dominator_tree_to_dot(self, filename: str):
1404        """
1405            Export the dominator tree of the function to a dot file
1406        Args:
1407            filename (str)
1408        """
1409
1410        def description(node):
1411            desc = f"{node}\n"
1412            desc += f"id: {node.node_id}"
1413            if node.dominance_frontier:
1414                desc += f"\ndominance frontier: {[n.node_id for n in node.dominance_frontier]}"
1415            return desc
1416
1417        with open(filename, "w", encoding="utf8") as f:
1418            f.write("digraph{\n")
1419            for node in self.nodes:
1420                f.write(f'{node.node_id}[label="{description(node)}"];\n')
1421                if node.immediate_dominator:
1422                    f.write(f"{node.immediate_dominator.node_id}->{node.node_id};\n")
1423
1424            f.write("}\n")
1425
1426    def slithir_cfg_to_dot(self, filename: str):
1427        """
1428        Export the CFG to a DOT file. The nodes includes the Solidity expressions and the IRs
1429        :param filename:
1430        :return:
1431        """
1432        content = self.slithir_cfg_to_dot_str()
1433        with open(filename, "w", encoding="utf8") as f:
1434            f.write(content)
1435
1436    def slithir_cfg_to_dot_str(self, skip_expressions: bool = False) -> str:
1437        """
1438        Export the CFG to a DOT format. The nodes includes the Solidity expressions and the IRs
1439        :return: the DOT content
1440        :rtype: str
1441        """
1442        from slither.core.cfg.node import NodeType
1443
1444        content = ""
1445        content += "digraph{\n"
1446        for node in self.nodes:
1447            label = f"Node Type: {node.type.value} {node.node_id}\n"
1448            if node.expression and not skip_expressions:
1449                label += f"\nEXPRESSION:\n{node.expression}\n"
1450            if node.irs and not skip_expressions:
1451                label += "\nIRs:\n" + "\n".join([str(ir) for ir in node.irs])
1452            content += f'{node.node_id}[label="{label}"];\n'
1453            if node.type in [NodeType.IF, NodeType.IFLOOP]:
1454                true_node = node.son_true
1455                if true_node:
1456                    content += f'{node.node_id}->{true_node.node_id}[label="True"];\n'
1457                false_node = node.son_false
1458                if false_node:
1459                    content += f'{node.node_id}->{false_node.node_id}[label="False"];\n'
1460            else:
1461                for son in node.sons:
1462                    content += f"{node.node_id}->{son.node_id};\n"
1463
1464        content += "}\n"
1465        return content
1466
1467    # endregion
1468    ###################################################################################
1469    ###################################################################################
1470    # region Summary information
1471    ###################################################################################
1472    ###################################################################################
1473
1474    def is_reading(self, variable: "Variable") -> bool:
1475        """
1476            Check if the function reads the variable
1477        Args:
1478            variable (Variable):
1479        Returns:
1480            bool: True if the variable is read
1481        """
1482        return variable in self.variables_read
1483
1484    def is_reading_in_conditional_node(self, variable: "Variable") -> bool:
1485        """
1486            Check if the function reads the variable in a IF node
1487        Args:
1488            variable (Variable):
1489        Returns:
1490            bool: True if the variable is read
1491        """
1492        variables_reads = [n.variables_read for n in self.nodes if n.contains_if()]
1493        variables_read = [item for sublist in variables_reads for item in sublist]
1494        return variable in variables_read
1495
1496    def is_reading_in_require_or_assert(self, variable: "Variable") -> bool:
1497        """
1498            Check if the function reads the variable in an require or assert
1499        Args:
1500            variable (Variable):
1501        Returns:
1502            bool: True if the variable is read
1503        """
1504        variables_reads = [n.variables_read for n in self.nodes if n.contains_require_or_assert()]
1505        variables_read = [item for sublist in variables_reads for item in sublist]
1506        return variable in variables_read
1507
1508    def is_writing(self, variable: "Variable") -> bool:
1509        """
1510            Check if the function writes the variable
1511        Args:
1512            variable (Variable):
1513        Returns:
1514            bool: True if the variable is written
1515        """
1516        return variable in self.variables_written
1517
1518    @abstractmethod
1519    def get_summary(
1520        self,
1521    ) -> tuple[str, str, str, list[str], list[str], list[str], list[str], list[str]]:
1522        pass
1523
1524    def is_protected(self) -> bool:
1525        """
1526            Determine if the function is protected using a check on msg.sender
1527
1528            Consider onlyOwner as a safe modifier.
1529            If the owner functionality is incorrectly implemented, this will lead to incorrectly
1530            classify the function as protected
1531
1532            Otherwise only detects if msg.sender is directly used in a condition
1533            For example, it wont work for:
1534                address a = msg.sender
1535                require(a == owner)
1536        Returns
1537            (bool)
1538        """
1539
1540        if self._is_protected is None:
1541            if self.is_constructor:
1542                self._is_protected = True
1543                return True
1544            if "onlyOwner" in [m.name for m in self.modifiers]:
1545                self._is_protected = True
1546                return True
1547            conditional_vars = self.all_conditional_solidity_variables_read(include_loop=False)
1548            args_vars = self.all_solidity_variables_used_as_args()
1549            self._is_protected = (
1550                SolidityVariableComposed("msg.sender") in conditional_vars + args_vars
1551            )
1552        return self._is_protected
1553
1554    @property
1555    def is_reentrant(self) -> bool:
1556        """
1557        Determine if the function can be re-entered
1558        """
1559        reentrancy_modifier = "nonReentrant"
1560
1561        if self.function_language == FunctionLanguage.Vyper:
1562            reentrancy_modifier = "nonreentrant(lock)"
1563
1564        # TODO: compare with hash of known nonReentrant modifier instead of the name
1565        if reentrancy_modifier in [m.name for m in self.modifiers]:
1566            return False
1567
1568        if self.visibility in ["public", "external"]:
1569            return True
1570
1571        # If it's an internal function, check if all its entry points have the nonReentrant modifier
1572        all_entry_points = [
1573            f for f in self.all_reachable_from_functions if f.visibility in ["public", "external"]
1574        ]
1575        if not all_entry_points:
1576            return True
1577        return not all(
1578            reentrancy_modifier in [m.name for m in f.modifiers] for f in all_entry_points
1579        )
1580
1581    # endregion
1582    ###################################################################################
1583    ###################################################################################
1584    # region Analyses
1585    ###################################################################################
1586    ###################################################################################
1587
1588    def _analyze_read_write(self) -> None:
1589        """Compute variables read/written/..."""
1590        write_var = [x.variables_written_as_expression for x in self.nodes]
1591        write_var = [x for x in write_var if x]
1592        write_var = [item for sublist in write_var for item in sublist]
1593        write_var = list(set(write_var))
1594        # Remove duplicate if they share the same string representation
1595        write_var = [
1596            next(obj)
1597            for i, obj in groupby(sorted(write_var, key=lambda x: str(x)), lambda x: str(x))
1598        ]
1599        self._expression_vars_written = write_var
1600
1601        write_var = [x.variables_written for x in self.nodes]
1602        write_var = [x for x in write_var if x]
1603        write_var = [item for sublist in write_var for item in sublist]
1604        write_var = list(set(write_var))
1605        # Remove duplicate if they share the same string representation
1606        write_var = [
1607            next(obj)
1608            for i, obj in groupby(sorted(write_var, key=lambda x: str(x)), lambda x: str(x))
1609        ]
1610        self._vars_written = write_var
1611
1612        read_var = [x.variables_read_as_expression for x in self.nodes]
1613        read_var = [x for x in read_var if x]
1614        read_var = [item for sublist in read_var for item in sublist]
1615        # Remove duplicate if they share the same string representation
1616        read_var = [
1617            next(obj)
1618            for i, obj in groupby(sorted(read_var, key=lambda x: str(x)), lambda x: str(x))
1619        ]
1620        self._expression_vars_read = read_var
1621
1622        read_var = [x.variables_read for x in self.nodes]
1623        read_var = [x for x in read_var if x]
1624        read_var = [item for sublist in read_var for item in sublist]
1625        # Remove duplicate if they share the same string representation
1626        read_var = [
1627            next(obj)
1628            for i, obj in groupby(sorted(read_var, key=lambda x: str(x)), lambda x: str(x))
1629        ]
1630        self._vars_read = read_var
1631
1632        self._state_vars_written = [
1633            x for x in self.variables_written if isinstance(x, StateVariable)
1634        ]
1635        self._state_vars_read = [x for x in self.variables_read if isinstance(x, StateVariable)]
1636        self._solidity_vars_read = [
1637            x for x in self.variables_read if isinstance(x, SolidityVariable)
1638        ]
1639
1640        self._vars_read_or_written = self._vars_written + self._vars_read
1641
1642        slithir_variables = [x.slithir_variables for x in self.nodes]
1643        slithir_variables = [x for x in slithir_variables if x]
1644        self._slithir_variables = [item for sublist in slithir_variables for item in sublist]
1645
1646    def _analyze_calls(self) -> None:
1647        calls = [x.calls_as_expression for x in self.nodes]
1648        calls = [x for x in calls if x]
1649        calls = [item for sublist in calls for item in sublist]
1650        self._expression_calls = list(set(calls))
1651
1652        internal_calls = [x.internal_calls for x in self.nodes]
1653        internal_calls = [x for x in internal_calls if x]
1654        internal_calls = [item for sublist in internal_calls for item in sublist]
1655        self._internal_calls = list(set(internal_calls))
1656
1657        self._solidity_calls = [
1658            ir for ir in internal_calls if isinstance(ir.function, SolidityFunction)
1659        ]
1660
1661        low_level_calls = [x.low_level_calls for x in self.nodes]
1662        low_level_calls = [x for x in low_level_calls if x]
1663        low_level_calls = [item for sublist in low_level_calls for item in sublist]
1664        self._low_level_calls = list(set(low_level_calls))
1665
1666        high_level_calls = [x.high_level_calls for x in self.nodes]
1667        high_level_calls = [x for x in high_level_calls if x]
1668        high_level_calls = [item for sublist in high_level_calls for item in sublist]
1669        self._high_level_calls = list(set(high_level_calls))
1670
1671        library_calls = [x.library_calls for x in self.nodes]
1672        library_calls = [x for x in library_calls if x]
1673        library_calls = [item for sublist in library_calls for item in sublist]
1674        self._library_calls = list(set(library_calls))
1675
1676        external_calls_as_expressions = [x.external_calls_as_expressions for x in self.nodes]
1677        external_calls_as_expressions = [x for x in external_calls_as_expressions if x]
1678        external_calls_as_expressions = [
1679            item for sublist in external_calls_as_expressions for item in sublist
1680        ]
1681        self._external_calls_as_expressions = list(set(external_calls_as_expressions))
1682
1683    # endregion
1684    ###################################################################################
1685    ###################################################################################
1686    # region Nodes
1687    ###################################################################################
1688    ###################################################################################
1689
1690    def new_node(
1691        self, node_type: "NodeType", src: str | dict, scope: Union[Scope, "Function"]
1692    ) -> "Node":
1693        from slither.core.cfg.node import Node
1694
1695        node = Node(node_type, self._counter_nodes, scope, self.file_scope)
1696        node.set_offset(src, self.compilation_unit)
1697        self._counter_nodes += 1
1698        node.set_function(self)
1699        self._nodes.append(node)
1700
1701        return node
1702
1703    # endregion
1704    ###################################################################################
1705    ###################################################################################
1706    # region SlithIr and SSA
1707    ###################################################################################
1708    ###################################################################################
1709
1710    def _get_last_ssa_variable_instances(
1711        self, target_state: bool, target_local: bool
1712    ) -> dict[str, set["SlithIRVariable"]]:
1713        from slither.slithir.variables import ReferenceVariable
1714        from slither.slithir.operations import OperationWithLValue
1715        from slither.core.cfg.node import NodeType
1716
1717        if not self.is_implemented:
1718            return {}
1719
1720        if self._entry_point is None:
1721            return {}
1722        # node, values
1723        to_explore: list[tuple[Node, dict]] = [(self._entry_point, {})]
1724        # node -> values
1725        explored: dict = {}
1726        # name -> instances
1727        ret: dict = {}
1728
1729        while to_explore:
1730            node, values = to_explore[0]
1731            to_explore = to_explore[1::]
1732
1733            if node.type != NodeType.ENTRYPOINT:
1734                for ir_ssa in node.irs_ssa:
1735                    if isinstance(ir_ssa, OperationWithLValue):
1736                        lvalue = ir_ssa.lvalue
1737                        if isinstance(lvalue, ReferenceVariable):
1738                            lvalue = lvalue.points_to_origin
1739                        if isinstance(lvalue, StateVariable) and target_state:
1740                            values[lvalue.canonical_name] = {lvalue}
1741                        if isinstance(lvalue, LocalVariable) and target_local:
1742                            values[lvalue.canonical_name] = {lvalue}
1743
1744            # Check for fixpoint
1745            if node in explored:
1746                if values == explored[node]:
1747                    continue
1748                for k, instances in values.items():
1749                    if k not in explored[node]:
1750                        explored[node][k] = set()
1751                    explored[node][k] |= instances
1752                values = explored[node]
1753            else:
1754                explored[node] = values
1755
1756            # Return condition
1757            if node.will_return:
1758                for name, instances in values.items():
1759                    if name not in ret:
1760                        ret[name] = set()
1761                    ret[name] |= instances
1762
1763            for son in node.sons:
1764                to_explore.append((son, dict(values)))
1765
1766        return ret
1767
1768    def get_last_ssa_state_variables_instances(
1769        self,
1770    ) -> dict[str, set["SlithIRVariable"]]:
1771        return self._get_last_ssa_variable_instances(target_state=True, target_local=False)
1772
1773    def get_last_ssa_local_variables_instances(
1774        self,
1775    ) -> dict[str, set["SlithIRVariable"]]:
1776        return self._get_last_ssa_variable_instances(target_state=False, target_local=True)
1777
1778    @staticmethod
1779    def _unchange_phi(ir: "Operation") -> bool:
1780        from slither.slithir.operations import Phi, PhiCallback
1781
1782        if not isinstance(ir, (Phi, PhiCallback)) or len(ir.rvalues) > 1:
1783            return False
1784        if not ir.rvalues:
1785            return True
1786        return ir.rvalues[0] == ir.lvalue
1787
1788    def _fix_phi_entry(
1789        self,
1790        node: "Node",
1791        last_state_variables_instances: dict[str, list["StateVariable"]],
1792        initial_state_variables_instances: dict[str, "StateVariable"],
1793    ) -> None:
1794        from slither.slithir.variables import Constant, StateIRVariable, LocalIRVariable
1795
1796        for ir in node.irs_ssa:
1797            if isinstance(ir.lvalue, StateIRVariable):
1798                additional = [initial_state_variables_instances[ir.lvalue.canonical_name]]
1799                additional += last_state_variables_instances[ir.lvalue.canonical_name]
1800                ir.rvalues = list(set(additional + ir.rvalues))
1801            # function parameter that are storage pointer
1802            else:
1803                # find index of the parameter
1804                idx = self.parameters.index(ir.lvalue.non_ssa_version)
1805                # find non ssa version of that index
1806                additional = [n.ir.arguments[idx] for n in self.reachable_from_nodes]
1807                additional = unroll(additional)
1808                additional = [a for a in additional if not isinstance(a, Constant)]
1809                ir.rvalues = list(set(additional + ir.rvalues))
1810
1811                if isinstance(ir.lvalue, LocalIRVariable) and ir.lvalue.is_storage:
1812                    # Update the refers_to to point to the phi rvalues
1813                    # This basically means that the local variable is a storage that point to any
1814                    # state variable that the storage pointer alias analysis found
1815                    ir.lvalue.refers_to = [
1816                        rvalue for rvalue in ir.rvalues if isinstance(rvalue, StateIRVariable)
1817                    ]
1818
1819    def fix_phi(
1820        self,
1821        last_state_variables_instances: dict[str, list["StateVariable"]],
1822        initial_state_variables_instances: dict[str, "StateVariable"],
1823    ) -> None:
1824        from slither.slithir.operations import InternalCall, PhiCallback, Phi
1825        from slither.slithir.variables import StateIRVariable, LocalIRVariable
1826
1827        for node in self.nodes:
1828            if node == self.entry_point:
1829                self._fix_phi_entry(
1830                    node, last_state_variables_instances, initial_state_variables_instances
1831                )
1832            for ir in node.irs_ssa:
1833                if isinstance(ir, PhiCallback):
1834                    callee_ir = ir.callee_ir
1835                    if isinstance(callee_ir, InternalCall):
1836                        last_ssa = callee_ir.function.get_last_ssa_state_variables_instances()
1837                        if ir.lvalue.canonical_name in last_ssa:
1838                            ir.rvalues = list(last_ssa[ir.lvalue.canonical_name])
1839                        else:
1840                            ir.rvalues = [ir.lvalue]
1841                    else:
1842                        additional = last_state_variables_instances[ir.lvalue.canonical_name]
1843                        ir.rvalues = list(set(additional + ir.rvalues))
1844
1845                # Propage storage ref information if it does not exist
1846                # This can happen if the refers_to variable was discovered through the phi operator on function parameter
1847                # aka you have storage pointer as function parameter
1848                # instead of having a storage pointer for which the aliases belong to the function body
1849                if (
1850                    isinstance(ir, Phi)
1851                    and isinstance(ir.lvalue, LocalIRVariable)
1852                    and ir.lvalue.is_storage
1853                    and not ir.lvalue.refers_to
1854                ):
1855                    refers_to = []
1856                    for candidate in ir.rvalues:
1857                        if isinstance(candidate, StateIRVariable):
1858                            refers_to.append(candidate)
1859                        if isinstance(candidate, LocalIRVariable) and candidate.is_storage:
1860                            refers_to += candidate.refers_to
1861
1862                    ir.lvalue.refers_to = refers_to
1863
1864            node.irs_ssa = [ir for ir in node.irs_ssa if not self._unchange_phi(ir)]
1865
1866    def generate_slithir_and_analyze(self) -> None:
1867        for node in self.nodes:
1868            node.slithir_generation()
1869
1870        self._analyze_read_write()
1871        self._analyze_calls()
1872
1873    @abstractmethod
1874    def generate_slithir_ssa(self, all_ssa_state_variables_instances):
1875        pass
1876
1877    def update_read_write_using_ssa(self) -> None:
1878        for node in self.nodes:
1879            node.update_read_write_using_ssa()
1880        self._analyze_read_write()
1881
1882    ###################################################################################
1883    ###################################################################################
1884    # region Built in definitions
1885    ###################################################################################
1886    ###################################################################################
1887
1888    def __str__(self) -> str:
1889        return self.name
1890
1891    # endregion
LOGGER = <Logger Function (WARNING)>
class ReacheableNode(builtins.tuple):

ReacheableNode(node, ir)

ReacheableNode(node, ir)

Create new instance of ReacheableNode(node, ir)

node

Alias for field number 0

ir

Alias for field number 1

class ModifierStatements:
56class ModifierStatements:
57    def __init__(
58        self,
59        modifier: Union["Contract", "Function"],
60        entry_point: "Node",
61        nodes: list["Node"],
62    ) -> None:
63        self._modifier = modifier
64        self._entry_point = entry_point
65        self._nodes = nodes
66
67    @property
68    def modifier(self) -> Union["Contract", "Function"]:
69        return self._modifier
70
71    @property
72    def entry_point(self) -> "Node":
73        return self._entry_point
74
75    @entry_point.setter
76    def entry_point(self, entry_point: "Node"):
77        self._entry_point = entry_point
78
79    @property
80    def nodes(self) -> list["Node"]:
81        return self._nodes
82
83    @nodes.setter
84    def nodes(self, nodes: list["Node"]):
85        self._nodes = nodes
ModifierStatements( modifier: Union[slither.core.declarations.contract.Contract, Function], entry_point: slither.core.cfg.node.Node, nodes: list[slither.core.cfg.node.Node])
57    def __init__(
58        self,
59        modifier: Union["Contract", "Function"],
60        entry_point: "Node",
61        nodes: list["Node"],
62    ) -> None:
63        self._modifier = modifier
64        self._entry_point = entry_point
65        self._nodes = nodes
67    @property
68    def modifier(self) -> Union["Contract", "Function"]:
69        return self._modifier
entry_point: slither.core.cfg.node.Node
71    @property
72    def entry_point(self) -> "Node":
73        return self._entry_point
nodes: list[slither.core.cfg.node.Node]
79    @property
80    def nodes(self) -> list["Node"]:
81        return self._nodes
class FunctionType(enum.Enum):
88class FunctionType(Enum):
89    NORMAL = 0
90    CONSTRUCTOR = 1
91    FALLBACK = 2
92    RECEIVE = 3
93    CONSTRUCTOR_VARIABLES = 10  # Fake function to hold variable declaration statements
94    CONSTRUCTOR_CONSTANT_VARIABLES = 11  # Fake function to hold variable declaration statements

An enumeration.

NORMAL = <FunctionType.NORMAL: 0>
CONSTRUCTOR = <FunctionType.CONSTRUCTOR: 1>
FALLBACK = <FunctionType.FALLBACK: 2>
RECEIVE = <FunctionType.RECEIVE: 3>
CONSTRUCTOR_VARIABLES = <FunctionType.CONSTRUCTOR_VARIABLES: 10>
CONSTRUCTOR_CONSTANT_VARIABLES = <FunctionType.CONSTRUCTOR_CONSTANT_VARIABLES: 11>
class FunctionLanguage(enum.Enum):
108class FunctionLanguage(Enum):
109    Solidity = 0
110    Yul = 1
111    Vyper = 2

An enumeration.

Solidity = <FunctionLanguage.Solidity: 0>
Vyper = <FunctionLanguage.Vyper: 2>
 114class Function(SourceMapping, metaclass=ABCMeta):
 115    """
 116    Function class
 117    """
 118
 119    def __init__(self, compilation_unit: "SlitherCompilationUnit") -> None:
 120        super().__init__()
 121        self._internal_scope: list[str] = []
 122        self._name: str | None = None
 123        self._view: bool = False
 124        self._pure: bool = False
 125        self._payable: bool = False
 126        self._visibility: str | None = None
 127        self._virtual: bool = False
 128        self._overrides: list[FunctionContract] = []
 129        self._overridden_by: list[FunctionContract] = []
 130
 131        self._is_implemented: bool | None = None
 132        self._is_empty: bool | None = None
 133        self._entry_point: Node | None = None
 134        self._nodes: list[Node] = []
 135        self._variables: dict[str, LocalVariable] = {}
 136        # slithir Temporary and references variables (but not SSA)
 137        self._slithir_variables: set[SlithIRVariable] = set()
 138        self._parameters: list[LocalVariable] = []
 139        self._parameters_ssa: list[LocalIRVariable] = []
 140        self._parameters_src: SourceMapping = SourceMapping()
 141        # This is used for vyper calls with default arguments
 142        self._default_args_as_expressions: list[Expression] = []
 143        self._returns: list[LocalVariable] = []
 144        self._returns_ssa: list[LocalIRVariable] = []
 145        self._returns_src: SourceMapping = SourceMapping()
 146        self._return_values: list[SlithIRVariable] | None = None
 147        self._return_values_ssa: list[SlithIRVariable] | None = None
 148        self._vars_read: list[Variable] = []
 149        self._vars_written: list[Variable] = []
 150        self._state_vars_read: list[StateVariable] = []
 151        self._vars_read_or_written: list[Variable] = []
 152        self._solidity_vars_read: list[SolidityVariable] = []
 153        self._state_vars_written: list[StateVariable] = []
 154        self._internal_calls: list[InternalCall] = []
 155        self._solidity_calls: list[SolidityCall] = []
 156        self._low_level_calls: list[LowLevelCall] = []
 157        self._high_level_calls: list[tuple[Contract, HighLevelCall]] = []
 158        self._library_calls: list[LibraryCall] = []
 159        self._external_calls_as_expressions: list[Expression] = []
 160        self._expression_vars_read: list[Expression] = []
 161        self._expression_vars_written: list[Expression] = []
 162        self._expression_calls: list[Expression] = []
 163        # self._expression_modifiers: List["Expression"] = []
 164        self._modifiers: list[ModifierStatements] = []
 165        self._explicit_base_constructor_calls: list[ModifierStatements] = []
 166        self._contains_assembly: bool = False
 167
 168        self._expressions: list[Expression] | None = None
 169        self._slithir_operations: list[Operation] | None = None
 170        self._slithir_ssa_operations: list[Operation] | None = None
 171
 172        self._all_expressions: list[Expression] | None = None
 173        self._all_slithir_operations: list[Operation] | None = None
 174        self._all_internals_calls: list[InternalCall] | None = None
 175        self._all_high_level_calls: list[tuple[Contract, HighLevelCall]] | None = None
 176        self._all_library_calls: list[LibraryCall] | None = None
 177        self._all_low_level_calls: list[LowLevelCall] | None = None
 178        self._all_solidity_calls: list[SolidityCall] | None = None
 179        self._all_variables_read: list[Variable] | None = None
 180        self._all_variables_written: list[Variable] | None = None
 181        self._all_state_variables_read: list[StateVariable] | None = None
 182        self._all_solidity_variables_read: list[SolidityVariable] | None = None
 183        self._all_state_variables_written: list[StateVariable] | None = None
 184        self._all_slithir_variables: list[SlithIRVariable] | None = None
 185        self._all_nodes: list[Node] | None = None
 186        self._all_conditional_state_variables_read: list[StateVariable] | None = None
 187        self._all_conditional_state_variables_read_with_loop: list[StateVariable] | None = None
 188        self._all_conditional_solidity_variables_read: list[SolidityVariable] | None = None
 189        self._all_conditional_solidity_variables_read_with_loop: list[SolidityVariable] | None = (
 190            None
 191        )
 192        self._all_solidity_variables_used_as_args: list[SolidityVariable] | None = None
 193
 194        self._is_shadowed: bool = False
 195        self._shadows: bool = False
 196
 197        # set(ReacheableNode)
 198        self._reachable_from_nodes: set[ReacheableNode] = set()
 199        self._reachable_from_functions: set[Function] = set()
 200        self._all_reachable_from_functions: set[Function] | None = None
 201
 202        # Constructor, fallback, State variable constructor
 203        self._function_type: FunctionType | None = None
 204        self._is_constructor: bool | None = None
 205
 206        # Computed on the fly, can be True of False
 207        self._can_reenter: bool | None = None
 208        self._can_send_eth: bool | None = None
 209
 210        self._nodes_ordered_dominators: list[Node] | None = None
 211
 212        self._counter_nodes = 0
 213
 214        # Memoize parameters:
 215        # TODO: identify all the memoize parameters and add a way to undo the memoization
 216        self._full_name: str | None = None
 217        self._signature: tuple[str, list[str], list[str]] | None = None
 218        self._solidity_signature: str | None = None
 219        self._signature_str: str | None = None
 220        self._canonical_name: str | None = None
 221        self._is_protected: bool | None = None
 222
 223        self.compilation_unit: SlitherCompilationUnit = compilation_unit
 224
 225        self.function_language: FunctionLanguage = (
 226            FunctionLanguage.Solidity if compilation_unit.is_solidity else FunctionLanguage.Vyper
 227        )
 228
 229        self._id: str | None = None
 230
 231        # To be improved with a parsing of the documentation
 232        self.has_documentation: bool = False
 233
 234    ###################################################################################
 235    ###################################################################################
 236    # region General properties
 237    ###################################################################################
 238    ###################################################################################
 239
 240    @property
 241    def name(self) -> str:
 242        """
 243        str: function name
 244        """
 245        if self._name == "" and self._function_type == FunctionType.CONSTRUCTOR:
 246            return "constructor"
 247        if self._name == "" and self._function_type == FunctionType.FALLBACK:
 248            return "fallback"
 249        if self._function_type == FunctionType.RECEIVE:
 250            return "receive"
 251        if self._function_type == FunctionType.CONSTRUCTOR_VARIABLES:
 252            return "slitherConstructorVariables"
 253        if self._function_type == FunctionType.CONSTRUCTOR_CONSTANT_VARIABLES:
 254            return "slitherConstructorConstantVariables"
 255        return self._name
 256
 257    @name.setter
 258    def name(self, new_name: str):
 259        self._name = new_name
 260
 261    @property
 262    def internal_scope(self) -> list[str]:
 263        """
 264        Return a list of name representing the scope of the function
 265        This is used to model nested functions declared in YUL
 266
 267        :return:
 268        """
 269        return self._internal_scope
 270
 271    @internal_scope.setter
 272    def internal_scope(self, new_scope: list[str]):
 273        self._internal_scope = new_scope
 274
 275    @property
 276    def full_name(self) -> str:
 277        """
 278        str: func_name(type1,type2)
 279        Return the function signature without the return values
 280        The difference between this function and solidity_function is that full_name does not translate the underlying
 281        type (ex: structure, contract to address, ...)
 282        """
 283        if self._full_name is None:
 284            name, parameters, _ = self.signature
 285            full_name = ".".join(self._internal_scope + [name]) + "(" + ",".join(parameters) + ")"
 286            self._full_name = full_name
 287        return self._full_name
 288
 289    @property
 290    @abstractmethod
 291    def canonical_name(self) -> str:
 292        """
 293        str: contract.func_name(type1,type2)
 294        Return the function signature without the return values
 295        """
 296        return ""
 297
 298    @property
 299    def contains_assembly(self) -> bool:
 300        return self._contains_assembly
 301
 302    @contains_assembly.setter
 303    def contains_assembly(self, c: bool):
 304        self._contains_assembly = c
 305
 306    def can_reenter(self, callstack: list[Union["Function", "Variable"]] | None = None) -> bool:
 307        """
 308        Check if the function can re-enter
 309        Follow internal calls.
 310        Do not consider CREATE as potential re-enter, but check if the
 311        destination's constructor can contain a call (recurs. follow nested CREATE)
 312        For Solidity > 0.5, filter access to public variables and constant/pure/view
 313        For call to this. check if the destination can re-enter
 314        Do not consider Send/Transfer as there is not enough gas
 315        :param callstack: used internally to check for recursion
 316        :return bool:
 317        """
 318        from slither.slithir.operations import Call
 319
 320        if self._can_reenter is None:
 321            self._can_reenter = False
 322            for ir in self.all_slithir_operations():
 323                if isinstance(ir, Call) and ir.can_reenter(callstack):
 324                    self._can_reenter = True
 325                    return True
 326        return self._can_reenter
 327
 328    def can_send_eth(self) -> bool:
 329        """
 330        Check if the function or any internal (not external) functions called by it can send eth
 331        :return bool:
 332        """
 333        from slither.slithir.operations import Call
 334
 335        if self._can_send_eth is None:
 336            self._can_send_eth = False
 337            for ir in self.all_slithir_operations():
 338                if isinstance(ir, Call) and ir.can_send_eth():
 339                    self._can_send_eth = True
 340                    return True
 341        return self._can_send_eth
 342
 343    @property
 344    def is_checked(self) -> bool:
 345        """
 346        Return true if the overflow are enabled by default
 347
 348
 349        :return:
 350        """
 351
 352        return self.compilation_unit.solc_version >= "0.8.0"
 353
 354    @property
 355    def id(self) -> str | None:
 356        """
 357        Return the reference ID of the function, if available.
 358
 359        :return:
 360        :rtype:
 361        """
 362        return self._id
 363
 364    @id.setter
 365    def id(self, new_id: str):
 366        self._id = new_id
 367
 368    @property
 369    @abstractmethod
 370    def file_scope(self) -> "FileScope":
 371        pass
 372
 373    # endregion
 374    ###################################################################################
 375    ###################################################################################
 376    # region Type (FunctionType)
 377    ###################################################################################
 378    ###################################################################################
 379
 380    def set_function_type(self, t: FunctionType) -> None:
 381        assert isinstance(t, FunctionType)
 382        self._function_type = t
 383
 384    @property
 385    def function_type(self) -> FunctionType | None:
 386        return self._function_type
 387
 388    @function_type.setter
 389    def function_type(self, t: FunctionType):
 390        self._function_type = t
 391
 392    @property
 393    def is_constructor(self) -> bool:
 394        """
 395        bool: True if the function is the constructor
 396        """
 397        return self._function_type == FunctionType.CONSTRUCTOR
 398
 399    @property
 400    def is_constructor_variables(self) -> bool:
 401        """
 402        bool: True if the function is the constructor of the variables
 403        Slither has inbuilt functions to hold the state variables initialization
 404        """
 405        return self._function_type in [
 406            FunctionType.CONSTRUCTOR_VARIABLES,
 407            FunctionType.CONSTRUCTOR_CONSTANT_VARIABLES,
 408        ]
 409
 410    @property
 411    def is_fallback(self) -> bool:
 412        """
 413            Determine if the function is the fallback function for the contract
 414        Returns
 415            (bool)
 416        """
 417        return self._function_type == FunctionType.FALLBACK
 418
 419    @property
 420    def is_receive(self) -> bool:
 421        """
 422            Determine if the function is the receive function for the contract
 423        Returns
 424            (bool)
 425        """
 426        return self._function_type == FunctionType.RECEIVE
 427
 428    # endregion
 429    ###################################################################################
 430    ###################################################################################
 431    # region Payable
 432    ###################################################################################
 433    ###################################################################################
 434
 435    @property
 436    def payable(self) -> bool:
 437        """
 438        bool: True if the function is payable
 439        """
 440        return self._payable
 441
 442    @payable.setter
 443    def payable(self, p: bool):
 444        self._payable = p
 445
 446    # endregion
 447    ###################################################################################
 448    ###################################################################################
 449    # region Virtual
 450    ###################################################################################
 451    ###################################################################################
 452
 453    @property
 454    def is_virtual(self) -> bool:
 455        """
 456        Note for Solidity < 0.6.0 it will always be false
 457        bool: True if the function is virtual
 458        """
 459        return self._virtual
 460
 461    @is_virtual.setter
 462    def is_virtual(self, v: bool):
 463        self._virtual = v
 464
 465    @property
 466    def is_override(self) -> bool:
 467        """
 468        Note for Solidity < 0.6.0 it will always be false
 469        bool: True if the function overrides a base function
 470        """
 471        return len(self._overrides) > 0
 472
 473    @property
 474    def overridden_by(self) -> list["FunctionContract"]:
 475        """
 476        List["FunctionContract"]: List of functions in child contracts that override this function
 477        This may include distinct instances of the same function due to inheritance
 478        """
 479        return self._overridden_by
 480
 481    @property
 482    def overrides(self) -> list["FunctionContract"]:
 483        """
 484        List["FunctionContract"]: List of functions in parent contracts that this function overrides
 485        This may include distinct instances of the same function due to inheritance
 486        """
 487        return self._overrides
 488
 489    # endregion
 490    ###################################################################################
 491    ###################################################################################
 492    # region Visibility
 493    ###################################################################################
 494    ###################################################################################
 495
 496    @property
 497    def visibility(self) -> str:
 498        """
 499        str: Function visibility
 500        """
 501        assert self._visibility is not None
 502        return self._visibility
 503
 504    @visibility.setter
 505    def visibility(self, v: str):
 506        self._visibility = v
 507
 508    def set_visibility(self, v: str) -> None:
 509        self._visibility = v
 510
 511    @property
 512    def view(self) -> bool:
 513        """
 514        bool: True if the function is declared as view
 515        """
 516        return self._view
 517
 518    @view.setter
 519    def view(self, v: bool):
 520        self._view = v
 521
 522    @property
 523    def pure(self) -> bool:
 524        """
 525        bool: True if the function is declared as pure
 526        """
 527        return self._pure
 528
 529    @pure.setter
 530    def pure(self, p: bool):
 531        self._pure = p
 532
 533    @property
 534    def is_shadowed(self) -> bool:
 535        return self._is_shadowed
 536
 537    @is_shadowed.setter
 538    def is_shadowed(self, is_shadowed):
 539        self._is_shadowed = is_shadowed
 540
 541    @property
 542    def shadows(self) -> bool:
 543        return self._shadows
 544
 545    @shadows.setter
 546    def shadows(self, _shadows: bool):
 547        self._shadows = _shadows
 548
 549    # endregion
 550    ###################################################################################
 551    ###################################################################################
 552    # region Function's body
 553    ###################################################################################
 554    ###################################################################################
 555
 556    @property
 557    def is_implemented(self) -> bool:
 558        """
 559        bool: True if the function is implemented
 560        """
 561        return self._is_implemented
 562
 563    @is_implemented.setter
 564    def is_implemented(self, is_impl: bool):
 565        self._is_implemented = is_impl
 566
 567    @property
 568    def is_empty(self) -> bool:
 569        """
 570        bool: True if the function is empty, None if the function is an interface
 571        """
 572        return self._is_empty
 573
 574    @is_empty.setter
 575    def is_empty(self, empty: bool):
 576        self._is_empty = empty
 577
 578    # endregion
 579    ###################################################################################
 580    ###################################################################################
 581    # region Nodes
 582    ###################################################################################
 583    ###################################################################################
 584
 585    @property
 586    def nodes(self) -> list["Node"]:
 587        """
 588        list(Node): List of the nodes
 589        """
 590        return list(self._nodes)
 591
 592    @nodes.setter
 593    def nodes(self, nodes: list["Node"]):
 594        self._nodes = nodes
 595
 596    @property
 597    def entry_point(self) -> Optional["Node"]:
 598        """
 599        Node: Entry point of the function
 600        """
 601        return self._entry_point
 602
 603    @entry_point.setter
 604    def entry_point(self, node: "Node"):
 605        self._entry_point = node
 606
 607    def add_node(self, node: "Node") -> None:
 608        if not self._entry_point:
 609            self._entry_point = node
 610        self._nodes.append(node)
 611
 612    @property
 613    def nodes_ordered_dominators(self) -> list["Node"]:
 614        # TODO: does not work properly; most likely due to modifier call
 615        # This will not work for modifier call that lead to multiple nodes
 616        # from slither.core.cfg.node import NodeType
 617        if self._nodes_ordered_dominators is None:
 618            self._nodes_ordered_dominators = []
 619            if self.entry_point:
 620                self._compute_nodes_ordered_dominators(self.entry_point)
 621
 622            for node in self.nodes:
 623                # if node.type == NodeType.OTHER_ENTRYPOINT:
 624                if node not in self._nodes_ordered_dominators:
 625                    self._compute_nodes_ordered_dominators(node)
 626
 627        return self._nodes_ordered_dominators
 628
 629    def _compute_nodes_ordered_dominators(self, node: "Node"):
 630        assert self._nodes_ordered_dominators is not None
 631        if node in self._nodes_ordered_dominators:
 632            return
 633        self._nodes_ordered_dominators.append(node)
 634        for dom in node.dominance_exploration_ordered:
 635            self._compute_nodes_ordered_dominators(dom)
 636
 637    # endregion
 638    ###################################################################################
 639    ###################################################################################
 640    # region Parameters
 641    ###################################################################################
 642    ###################################################################################
 643
 644    @property
 645    def parameters(self) -> list["LocalVariable"]:
 646        """
 647        list(LocalVariable): List of the parameters
 648        """
 649        return list(self._parameters)
 650
 651    def add_parameters(self, p: "LocalVariable") -> None:
 652        self._parameters.append(p)
 653
 654    @property
 655    def parameters_ssa(self) -> list["LocalIRVariable"]:
 656        """
 657        list(LocalIRVariable): List of the parameters (SSA form)
 658        """
 659        return list(self._parameters_ssa)
 660
 661    def add_parameter_ssa(self, var: "LocalIRVariable") -> None:
 662        self._parameters_ssa.append(var)
 663
 664    def parameters_src(self) -> SourceMapping:
 665        return self._parameters_src
 666
 667    # endregion
 668    ###################################################################################
 669    ###################################################################################
 670    # region Return values
 671    ###################################################################################
 672    ###################################################################################
 673
 674    @property
 675    def return_type(self) -> list[Type] | None:
 676        """
 677        Return the list of return type
 678        If no return, return None
 679        """
 680        returns = self.returns
 681        if returns:
 682            return [r.type for r in returns]
 683        return None
 684
 685    def returns_src(self) -> SourceMapping:
 686        return self._returns_src
 687
 688    @property
 689    def type(self) -> list[Type] | None:
 690        """
 691        Return the list of return type
 692        If no return, return None
 693        Alias of return_type
 694        """
 695        return self.return_type
 696
 697    @property
 698    def returns(self) -> list["LocalVariable"]:
 699        """
 700        list(LocalVariable): List of the return variables
 701        """
 702        return list(self._returns)
 703
 704    def add_return(self, r: "LocalVariable") -> None:
 705        self._returns.append(r)
 706
 707    @property
 708    def returns_ssa(self) -> list["LocalIRVariable"]:
 709        """
 710        list(LocalIRVariable): List of the return variables (SSA form)
 711        """
 712        return list(self._returns_ssa)
 713
 714    def add_return_ssa(self, var: "LocalIRVariable") -> None:
 715        self._returns_ssa.append(var)
 716
 717    # endregion
 718    ###################################################################################
 719    ###################################################################################
 720    # region Modifiers
 721    ###################################################################################
 722    ###################################################################################
 723
 724    @property
 725    def modifiers(self) -> list[Union["Contract", "Function"]]:
 726        """
 727        list(Modifier): List of the modifiers
 728        Can be contract for constructor's calls
 729
 730        """
 731        return [c.modifier for c in self._modifiers]
 732
 733    def add_modifier(self, modif: "ModifierStatements") -> None:
 734        self._modifiers.append(modif)
 735
 736    @property
 737    def modifiers_statements(self) -> list[ModifierStatements]:
 738        """
 739        list(ModifierCall): List of the modifiers call (include expression and irs)
 740        """
 741        return list(self._modifiers)
 742
 743    @property
 744    def explicit_base_constructor_calls(self) -> list["Function"]:
 745        """
 746        list(Function): List of the base constructors called explicitly by this presumed constructor definition.
 747
 748                        Base constructors implicitly or explicitly called by the contract definition will not be
 749                        included.
 750        """
 751        # This is a list of contracts internally, so we convert it to a list of constructor functions.
 752        return [
 753            c.modifier.constructors_declared
 754            for c in self._explicit_base_constructor_calls
 755            if c.modifier.constructors_declared
 756        ]
 757
 758    @property
 759    def explicit_base_constructor_calls_statements(self) -> list[ModifierStatements]:
 760        """
 761        list(ModifierCall): List of the base constructors called explicitly by this presumed constructor definition.
 762
 763        """
 764        # This is a list of contracts internally, so we convert it to a list of constructor functions.
 765        return list(self._explicit_base_constructor_calls)
 766
 767    def add_explicit_base_constructor_calls_statements(self, modif: ModifierStatements) -> None:
 768        self._explicit_base_constructor_calls.append(modif)
 769
 770    # endregion
 771    ###################################################################################
 772    ###################################################################################
 773    # region Variables
 774    ###################################################################################
 775    ###################################################################################
 776
 777    @property
 778    def variables(self) -> list[LocalVariable]:
 779        """
 780        Return all local variables
 781        Include parameters and return values
 782        """
 783        return list(self._variables.values())
 784
 785    @property
 786    def local_variables(self) -> list[LocalVariable]:
 787        """
 788        Return all local variables (dont include parameters and return values)
 789        """
 790        return list(set(self.variables) - set(self.returns) - set(self.parameters))
 791
 792    @property
 793    def variables_as_dict(self) -> dict[str, LocalVariable]:
 794        return self._variables
 795
 796    @property
 797    def variables_read(self) -> list["Variable"]:
 798        """
 799        list(Variable): Variables read (local/state/solidity)
 800        """
 801        return list(self._vars_read)
 802
 803    @property
 804    def variables_written(self) -> list["Variable"]:
 805        """
 806        list(Variable): Variables written (local/state/solidity)
 807        """
 808        return list(self._vars_written)
 809
 810    @property
 811    def state_variables_read(self) -> list["StateVariable"]:
 812        """
 813        list(StateVariable): State variables read
 814        """
 815        return list(self._state_vars_read)
 816
 817    @property
 818    def solidity_variables_read(self) -> list["SolidityVariable"]:
 819        """
 820        list(SolidityVariable): Solidity variables read
 821        """
 822        return list(self._solidity_vars_read)
 823
 824    @property
 825    def state_variables_written(self) -> list["StateVariable"]:
 826        """
 827        list(StateVariable): State variables written
 828        """
 829        return list(self._state_vars_written)
 830
 831    @property
 832    def variables_read_or_written(self) -> list["Variable"]:
 833        """
 834        list(Variable): Variables read or written (local/state/solidity)
 835        """
 836        return list(self._vars_read_or_written)
 837
 838    @property
 839    def variables_read_as_expression(self) -> list["Expression"]:
 840        return self._expression_vars_read
 841
 842    @property
 843    def variables_written_as_expression(self) -> list["Expression"]:
 844        return self._expression_vars_written
 845
 846    @property
 847    def slithir_variables(self) -> list["SlithIRVariable"]:
 848        """
 849        Temporary and Reference Variables (not SSA form)
 850        """
 851
 852        return list(self._slithir_variables)
 853
 854    # endregion
 855    ###################################################################################
 856    ###################################################################################
 857    # region Calls
 858    ###################################################################################
 859    ###################################################################################
 860
 861    @property
 862    def internal_calls(self) -> list["InternalCall"]:
 863        """
 864        list(InternalCall): List of IR operations for internal calls
 865        """
 866        return list(self._internal_calls)
 867
 868    @property
 869    def solidity_calls(self) -> list["SolidityCall"]:
 870        """
 871        list(SolidityCall): List of IR operations for Solidity calls
 872        """
 873        return list(self._solidity_calls)
 874
 875    @property
 876    def high_level_calls(self) -> list[tuple["Contract", "HighLevelCall"]]:
 877        """
 878        list(Tuple(Contract, "HighLevelCall")): List of call target contract and IR of the high level call
 879        A variable is called in case of call to a public state variable
 880        Include library calls
 881        """
 882        return list(self._high_level_calls)
 883
 884    @property
 885    def library_calls(self) -> list["LibraryCall"]:
 886        """
 887        list(LibraryCall): List of IR operations for library calls
 888        """
 889        return list(self._library_calls)
 890
 891    @property
 892    def low_level_calls(self) -> list["LowLevelCall"]:
 893        """
 894        list(LowLevelCall): List of IR operations for low level calls
 895        A low level call is defined by
 896        - the variable called
 897        - the name of the function (call/delegatecall/callcode)
 898        """
 899        return list(self._low_level_calls)
 900
 901    @property
 902    def external_calls_as_expressions(self) -> list["Expression"]:
 903        """
 904        list(ExpressionCall): List of message calls (that creates a transaction)
 905        """
 906        return list(self._external_calls_as_expressions)
 907
 908    # endregion
 909    ###################################################################################
 910    ###################################################################################
 911    # region Expressions
 912    ###################################################################################
 913    ###################################################################################
 914
 915    @property
 916    def calls_as_expressions(self) -> list["Expression"]:
 917        return self._expression_calls
 918
 919    @property
 920    def expressions(self) -> list["Expression"]:
 921        """
 922        list(Expression): List of the expressions
 923        """
 924        if self._expressions is None:
 925            expressionss = [n.expression for n in self.nodes]
 926            expressions = [e for e in expressionss if e]
 927            self._expressions = expressions
 928        return self._expressions
 929
 930    @property
 931    def return_values(self) -> list["SlithIRVariable"]:
 932        """
 933        list(Return Values): List of the return values
 934        """
 935        from slither.core.cfg.node import NodeType
 936        from slither.slithir.operations import Return
 937        from slither.slithir.variables import Constant
 938
 939        if self._return_values is None:
 940            return_values = []
 941            returns = [n for n in self.nodes if n.type == NodeType.RETURN]
 942            [
 943                return_values.extend(ir.values)
 944                for node in returns
 945                for ir in node.irs
 946                if isinstance(ir, Return)
 947            ]
 948            self._return_values = list({x for x in return_values if not isinstance(x, Constant)})
 949        return self._return_values
 950
 951    @property
 952    def return_values_ssa(self) -> list["SlithIRVariable"]:
 953        """
 954        list(Return Values in SSA form): List of the return values in ssa form
 955        """
 956        from slither.core.cfg.node import NodeType
 957        from slither.slithir.operations import Return
 958        from slither.slithir.variables import Constant
 959
 960        if self._return_values_ssa is None:
 961            return_values_ssa = []
 962            returns = [n for n in self.nodes if n.type == NodeType.RETURN]
 963            [
 964                return_values_ssa.extend(ir.values)
 965                for node in returns
 966                for ir in node.irs_ssa
 967                if isinstance(ir, Return)
 968            ]
 969            self._return_values_ssa = list(
 970                {x for x in return_values_ssa if not isinstance(x, Constant)}
 971            )
 972        return self._return_values_ssa
 973
 974    # endregion
 975    ###################################################################################
 976    ###################################################################################
 977    # region SlithIR
 978    ###################################################################################
 979    ###################################################################################
 980
 981    @property
 982    def slithir_operations(self) -> list["Operation"]:
 983        """
 984        list(Operation): List of the slithir operations
 985        """
 986        if self._slithir_operations is None:
 987            operationss = [n.irs for n in self.nodes]
 988            operations = [item for sublist in operationss for item in sublist if item]
 989            self._slithir_operations = operations
 990        return self._slithir_operations
 991
 992    @property
 993    def slithir_ssa_operations(self) -> list["Operation"]:
 994        """
 995        list(Operation): List of the slithir operations (SSA)
 996        """
 997        if self._slithir_ssa_operations is None:
 998            operationss = [n.irs_ssa for n in self.nodes]
 999            operations = [item for sublist in operationss for item in sublist if item]
1000            self._slithir_ssa_operations = operations
1001        return self._slithir_ssa_operations
1002
1003    # endregion
1004    ###################################################################################
1005    ###################################################################################
1006    # region Signature
1007    ###################################################################################
1008    ###################################################################################
1009
1010    @property
1011    def solidity_signature(self) -> str:
1012        """
1013        Return a signature following the Solidity Standard
1014        Contract and converted into address
1015
1016        It might still keep internal types (ex: structure name) for internal functions.
1017        The reason is that internal functions allows recursive structure definition, which
1018        can't be converted following the Solidity stand ard
1019
1020        :return: the solidity signature
1021        """
1022        if self._solidity_signature is None:
1023            parameters = [
1024                convert_type_for_solidity_signature_to_string(x.type) for x in self.parameters
1025            ]
1026            self._solidity_signature = self.name + "(" + ",".join(parameters) + ")"
1027        return self._solidity_signature
1028
1029    @property
1030    def signature(self) -> tuple[str, list[str], list[str]]:
1031        """
1032        (str, list(str), list(str)): Function signature as
1033        (name, list parameters type, list return values type)
1034        """
1035        # FIXME memoizing this function is not working properly for vyper
1036        # if self._signature is None:
1037        return (
1038            self.name,
1039            [str(x.type) for x in self.parameters],
1040            [str(x.type) for x in self.returns],
1041        )
1042        #     self._signature = signature
1043        # return self._signature
1044
1045    @property
1046    def signature_str(self) -> str:
1047        """
1048        str: func_name(type1,type2) returns (type3)
1049        Return the function signature as a str (contains the return values)
1050        """
1051        if self._signature_str is None:
1052            name, parameters, returnVars = self.signature
1053            self._signature_str = (
1054                name + "(" + ",".join(parameters) + ") returns(" + ",".join(returnVars) + ")"
1055            )
1056        return self._signature_str
1057
1058    # endregion
1059    ###################################################################################
1060    ###################################################################################
1061    # region Functions
1062    ###################################################################################
1063    ###################################################################################
1064
1065    @property
1066    @abstractmethod
1067    def functions_shadowed(self) -> list["Function"]:
1068        pass
1069
1070    # endregion
1071    ###################################################################################
1072    ###################################################################################
1073    # region Reachable
1074    ###################################################################################
1075    ###################################################################################
1076
1077    @property
1078    def reachable_from_nodes(self) -> set[ReacheableNode]:
1079        """
1080        Return
1081            ReacheableNode
1082        """
1083        return self._reachable_from_nodes
1084
1085    @property
1086    def reachable_from_functions(self) -> set["Function"]:
1087        return self._reachable_from_functions
1088
1089    @property
1090    def all_reachable_from_functions(self) -> set["Function"]:
1091        """
1092        Give the recursive version of reachable_from_functions (all the functions that lead to call self in the CFG)
1093        """
1094        if self._all_reachable_from_functions is None:
1095            functions: set[Function] = set()
1096
1097            new_functions = self.reachable_from_functions
1098            # iterate until we have are finding new functions
1099            while new_functions and not new_functions.issubset(functions):
1100                functions = functions.union(new_functions)
1101                # Use a temporary set, because we iterate over new_functions
1102                new_functionss: set[Function] = set()
1103                for f in new_functions:
1104                    new_functionss = new_functionss.union(f.reachable_from_functions)
1105                new_functions = new_functionss - functions
1106
1107            self._all_reachable_from_functions = functions
1108        return self._all_reachable_from_functions
1109
1110    def add_reachable_from_node(self, n: "Node", ir: "Operation") -> None:
1111        self._reachable_from_nodes.add(ReacheableNode(n, ir))
1112        self._reachable_from_functions.add(n.function)
1113
1114    # endregion
1115    ###################################################################################
1116    ###################################################################################
1117    # region Recursive getters
1118    ###################################################################################
1119    ###################################################################################
1120
1121    def _explore_functions(self, f_new_values: Callable[["Function"], list]) -> list[Any]:
1122        values = f_new_values(self)
1123        explored: set[Function] = {self}
1124        to_explore_set: set[Function] = set()
1125
1126        for ir in self.internal_calls:
1127            if isinstance(ir.function, Function) and ir.function not in explored:
1128                to_explore_set.add(ir.function)
1129        for ir in self.library_calls:
1130            if isinstance(ir.function, Function) and ir.function not in explored:
1131                to_explore_set.add(ir.function)
1132        for m in self.modifiers:
1133            if m not in explored:
1134                to_explore_set.add(m)
1135
1136        while to_explore_set:
1137            f = to_explore_set.pop()
1138            if f in explored:
1139                continue
1140            explored.add(f)
1141
1142            values += f_new_values(f)
1143
1144            for ir in f.internal_calls:
1145                if isinstance(ir.function, Function) and ir.function not in explored:
1146                    to_explore_set.add(ir.function)
1147            for ir in f.library_calls:
1148                if isinstance(ir.function, Function) and ir.function not in explored:
1149                    to_explore_set.add(ir.function)
1150            for m in f.modifiers:
1151                if m not in explored:
1152                    to_explore_set.add(m)
1153
1154        return list(set(values))
1155
1156    def all_variables_read(self) -> list["Variable"]:
1157        """recursive version of variables_read"""
1158        if self._all_variables_read is None:
1159            self._all_variables_read = self._explore_functions(lambda x: x.variables_read)
1160        return self._all_variables_read
1161
1162    def all_variables_written(self) -> list["Variable"]:
1163        """recursive version of variables_written"""
1164        if self._all_variables_written is None:
1165            self._all_variables_written = self._explore_functions(lambda x: x.variables_written)
1166        return self._all_variables_written
1167
1168    def all_state_variables_read(self) -> list["StateVariable"]:
1169        """recursive version of variables_read"""
1170        if self._all_state_variables_read is None:
1171            self._all_state_variables_read = self._explore_functions(
1172                lambda x: x.state_variables_read
1173            )
1174        return self._all_state_variables_read
1175
1176    def all_solidity_variables_read(self) -> list[SolidityVariable]:
1177        """recursive version of solidity_read"""
1178        if self._all_solidity_variables_read is None:
1179            self._all_solidity_variables_read = self._explore_functions(
1180                lambda x: x.solidity_variables_read
1181            )
1182        return self._all_solidity_variables_read
1183
1184    def all_slithir_variables(self) -> list["SlithIRVariable"]:
1185        """recursive version of slithir_variables"""
1186        if self._all_slithir_variables is None:
1187            self._all_slithir_variables = self._explore_functions(lambda x: x.slithir_variables)
1188        return self._all_slithir_variables
1189
1190    def all_nodes(self) -> list["Node"]:
1191        """recursive version of nodes"""
1192        if self._all_nodes is None:
1193            self._all_nodes = self._explore_functions(lambda x: x.nodes)
1194        return self._all_nodes
1195
1196    def all_expressions(self) -> list["Expression"]:
1197        """recursive version of variables_read"""
1198        if self._all_expressions is None:
1199            self._all_expressions = self._explore_functions(lambda x: x.expressions)
1200        return self._all_expressions
1201
1202    def all_slithir_operations(self) -> list["Operation"]:
1203        if self._all_slithir_operations is None:
1204            self._all_slithir_operations = self._explore_functions(lambda x: x.slithir_operations)
1205        return self._all_slithir_operations
1206
1207    def all_state_variables_written(self) -> list[StateVariable]:
1208        """recursive version of variables_written"""
1209        if self._all_state_variables_written is None:
1210            self._all_state_variables_written = self._explore_functions(
1211                lambda x: x.state_variables_written
1212            )
1213        return self._all_state_variables_written
1214
1215    def all_internal_calls(self) -> list["InternalCall"]:
1216        """recursive version of internal_calls"""
1217        if self._all_internals_calls is None:
1218            self._all_internals_calls = self._explore_functions(lambda x: x.internal_calls)
1219        return self._all_internals_calls
1220
1221    def all_low_level_calls(self) -> list["LowLevelCall"]:
1222        """recursive version of low_level calls"""
1223        if self._all_low_level_calls is None:
1224            self._all_low_level_calls = self._explore_functions(lambda x: x.low_level_calls)
1225        return self._all_low_level_calls
1226
1227    def all_high_level_calls(self) -> list[tuple["Contract", "HighLevelCall"]]:
1228        """recursive version of high_level calls"""
1229        if self._all_high_level_calls is None:
1230            self._all_high_level_calls = self._explore_functions(lambda x: x.high_level_calls)
1231        return self._all_high_level_calls
1232
1233    def all_library_calls(self) -> list["LibraryCall"]:
1234        """recursive version of library calls"""
1235        if self._all_library_calls is None:
1236            self._all_library_calls = self._explore_functions(lambda x: x.library_calls)
1237        return self._all_library_calls
1238
1239    def all_solidity_calls(self) -> list["SolidityCall"]:
1240        """recursive version of solidity calls"""
1241        if self._all_solidity_calls is None:
1242            self._all_solidity_calls = self._explore_functions(lambda x: x.solidity_calls)
1243        return self._all_solidity_calls
1244
1245    @staticmethod
1246    def _explore_func_cond_read(func: "Function", include_loop: bool) -> list["StateVariable"]:
1247        ret = [n.state_variables_read for n in func.nodes if n.is_conditional(include_loop)]
1248        return [item for sublist in ret for item in sublist]
1249
1250    def all_conditional_state_variables_read(self, include_loop=True) -> list["StateVariable"]:
1251        """
1252        Return the state variable used in a condition
1253
1254        Over approximate and also return index access
1255        It won't work if the variable is assigned to a temp variable
1256        """
1257        if include_loop:
1258            if self._all_conditional_state_variables_read_with_loop is None:
1259                self._all_conditional_state_variables_read_with_loop = self._explore_functions(
1260                    lambda x: self._explore_func_cond_read(x, include_loop)
1261                )
1262            return self._all_conditional_state_variables_read_with_loop
1263        if self._all_conditional_state_variables_read is None:
1264            self._all_conditional_state_variables_read = self._explore_functions(
1265                lambda x: self._explore_func_cond_read(x, include_loop)
1266            )
1267        return self._all_conditional_state_variables_read
1268
1269    @staticmethod
1270    def _solidity_variable_in_binary(node: "Node") -> list[SolidityVariable]:
1271        from slither.slithir.operations.binary import Binary
1272
1273        ret = []
1274        for ir in node.irs:
1275            if isinstance(ir, Binary):
1276                ret += ir.read
1277        return [var for var in ret if isinstance(var, SolidityVariable)]
1278
1279    @staticmethod
1280    def _explore_func_conditional(
1281        func: "Function",
1282        f: Callable[["Node"], list[SolidityVariable]],
1283        include_loop: bool,
1284    ) -> list[Any]:
1285        ret = [f(n) for n in func.nodes if n.is_conditional(include_loop)]
1286        return [item for sublist in ret for item in sublist]
1287
1288    def all_conditional_solidity_variables_read(
1289        self, include_loop: bool = True
1290    ) -> list[SolidityVariable]:
1291        """
1292        Return the Solidity variables directly used in a condition
1293
1294        Use of the IR to filter index access
1295        Assumption: the solidity vars are used directly in the conditional node
1296        It won't work if the variable is assigned to a temp variable
1297        """
1298        if include_loop:
1299            if self._all_conditional_solidity_variables_read_with_loop is None:
1300                self._all_conditional_solidity_variables_read_with_loop = self._explore_functions(
1301                    lambda x: self._explore_func_conditional(
1302                        x, self._solidity_variable_in_binary, include_loop
1303                    )
1304                )
1305            return self._all_conditional_solidity_variables_read_with_loop
1306
1307        if self._all_conditional_solidity_variables_read is None:
1308            self._all_conditional_solidity_variables_read = self._explore_functions(
1309                lambda x: self._explore_func_conditional(
1310                    x, self._solidity_variable_in_binary, include_loop
1311                )
1312            )
1313        return self._all_conditional_solidity_variables_read
1314
1315    @staticmethod
1316    def _solidity_variable_in_internal_calls(node: "Node") -> list[SolidityVariable]:
1317        from slither.slithir.operations.internal_call import InternalCall
1318
1319        ret = []
1320        for ir in node.irs:
1321            if isinstance(ir, InternalCall):
1322                ret += ir.read
1323        return [var for var in ret if isinstance(var, SolidityVariable)]
1324
1325    @staticmethod
1326    def _explore_func_nodes(
1327        func: "Function", f: Callable[["Node"], list[SolidityVariable]]
1328    ) -> list[Any | SolidityVariableComposed]:
1329        ret = [f(n) for n in func.nodes]
1330        return [item for sublist in ret for item in sublist]
1331
1332    def all_solidity_variables_used_as_args(self) -> list[SolidityVariable]:
1333        """
1334        Return the Solidity variables directly used in a call
1335
1336        Use of the IR to filter index access
1337        Used to catch check(msg.sender)
1338        """
1339        if self._all_solidity_variables_used_as_args is None:
1340            self._all_solidity_variables_used_as_args = self._explore_functions(
1341                lambda x: self._explore_func_nodes(x, self._solidity_variable_in_internal_calls)
1342            )
1343        return self._all_solidity_variables_used_as_args
1344
1345    # endregion
1346    ###################################################################################
1347    ###################################################################################
1348    # region Visitor
1349    ###################################################################################
1350    ###################################################################################
1351
1352    def apply_visitor(self, Visitor: Callable) -> list:
1353        """
1354            Apply a visitor to all the function expressions
1355        Args:
1356            Visitor: slither.visitors
1357        Returns
1358            list(): results of the visit
1359        """
1360        expressions = self.expressions
1361        v = [Visitor(e).result() for e in expressions]
1362        return [item for sublist in v for item in sublist]
1363
1364    # endregion
1365    ###################################################################################
1366    ###################################################################################
1367    # region Getters from/to object
1368    ###################################################################################
1369    ###################################################################################
1370
1371    def get_local_variable_from_name(self, variable_name: str) -> LocalVariable | None:
1372        """
1373            Return a local variable from a name
1374
1375        Args:
1376            variable_name (str): name of the variable
1377        Returns:
1378            LocalVariable
1379        """
1380        return next((v for v in self.variables if v.name == variable_name), None)
1381
1382    # endregion
1383    ###################################################################################
1384    ###################################################################################
1385    # region Export
1386    ###################################################################################
1387    ###################################################################################
1388
1389    def cfg_to_dot(self, filename: str):
1390        """
1391            Export the function to a dot file
1392        Args:
1393            filename (str)
1394        """
1395        with open(filename, "w", encoding="utf8") as f:
1396            f.write("digraph{\n")
1397            for node in self.nodes:
1398                f.write(f'{node.node_id}[label="{node!s}"];\n')
1399                for son in node.sons:
1400                    f.write(f"{node.node_id}->{son.node_id};\n")
1401
1402            f.write("}\n")
1403
1404    def dominator_tree_to_dot(self, filename: str):
1405        """
1406            Export the dominator tree of the function to a dot file
1407        Args:
1408            filename (str)
1409        """
1410
1411        def description(node):
1412            desc = f"{node}\n"
1413            desc += f"id: {node.node_id}"
1414            if node.dominance_frontier:
1415                desc += f"\ndominance frontier: {[n.node_id for n in node.dominance_frontier]}"
1416            return desc
1417
1418        with open(filename, "w", encoding="utf8") as f:
1419            f.write("digraph{\n")
1420            for node in self.nodes:
1421                f.write(f'{node.node_id}[label="{description(node)}"];\n')
1422                if node.immediate_dominator:
1423                    f.write(f"{node.immediate_dominator.node_id}->{node.node_id};\n")
1424
1425            f.write("}\n")
1426
1427    def slithir_cfg_to_dot(self, filename: str):
1428        """
1429        Export the CFG to a DOT file. The nodes includes the Solidity expressions and the IRs
1430        :param filename:
1431        :return:
1432        """
1433        content = self.slithir_cfg_to_dot_str()
1434        with open(filename, "w", encoding="utf8") as f:
1435            f.write(content)
1436
1437    def slithir_cfg_to_dot_str(self, skip_expressions: bool = False) -> str:
1438        """
1439        Export the CFG to a DOT format. The nodes includes the Solidity expressions and the IRs
1440        :return: the DOT content
1441        :rtype: str
1442        """
1443        from slither.core.cfg.node import NodeType
1444
1445        content = ""
1446        content += "digraph{\n"
1447        for node in self.nodes:
1448            label = f"Node Type: {node.type.value} {node.node_id}\n"
1449            if node.expression and not skip_expressions:
1450                label += f"\nEXPRESSION:\n{node.expression}\n"
1451            if node.irs and not skip_expressions:
1452                label += "\nIRs:\n" + "\n".join([str(ir) for ir in node.irs])
1453            content += f'{node.node_id}[label="{label}"];\n'
1454            if node.type in [NodeType.IF, NodeType.IFLOOP]:
1455                true_node = node.son_true
1456                if true_node:
1457                    content += f'{node.node_id}->{true_node.node_id}[label="True"];\n'
1458                false_node = node.son_false
1459                if false_node:
1460                    content += f'{node.node_id}->{false_node.node_id}[label="False"];\n'
1461            else:
1462                for son in node.sons:
1463                    content += f"{node.node_id}->{son.node_id};\n"
1464
1465        content += "}\n"
1466        return content
1467
1468    # endregion
1469    ###################################################################################
1470    ###################################################################################
1471    # region Summary information
1472    ###################################################################################
1473    ###################################################################################
1474
1475    def is_reading(self, variable: "Variable") -> bool:
1476        """
1477            Check if the function reads the variable
1478        Args:
1479            variable (Variable):
1480        Returns:
1481            bool: True if the variable is read
1482        """
1483        return variable in self.variables_read
1484
1485    def is_reading_in_conditional_node(self, variable: "Variable") -> bool:
1486        """
1487            Check if the function reads the variable in a IF node
1488        Args:
1489            variable (Variable):
1490        Returns:
1491            bool: True if the variable is read
1492        """
1493        variables_reads = [n.variables_read for n in self.nodes if n.contains_if()]
1494        variables_read = [item for sublist in variables_reads for item in sublist]
1495        return variable in variables_read
1496
1497    def is_reading_in_require_or_assert(self, variable: "Variable") -> bool:
1498        """
1499            Check if the function reads the variable in an require or assert
1500        Args:
1501            variable (Variable):
1502        Returns:
1503            bool: True if the variable is read
1504        """
1505        variables_reads = [n.variables_read for n in self.nodes if n.contains_require_or_assert()]
1506        variables_read = [item for sublist in variables_reads for item in sublist]
1507        return variable in variables_read
1508
1509    def is_writing(self, variable: "Variable") -> bool:
1510        """
1511            Check if the function writes the variable
1512        Args:
1513            variable (Variable):
1514        Returns:
1515            bool: True if the variable is written
1516        """
1517        return variable in self.variables_written
1518
1519    @abstractmethod
1520    def get_summary(
1521        self,
1522    ) -> tuple[str, str, str, list[str], list[str], list[str], list[str], list[str]]:
1523        pass
1524
1525    def is_protected(self) -> bool:
1526        """
1527            Determine if the function is protected using a check on msg.sender
1528
1529            Consider onlyOwner as a safe modifier.
1530            If the owner functionality is incorrectly implemented, this will lead to incorrectly
1531            classify the function as protected
1532
1533            Otherwise only detects if msg.sender is directly used in a condition
1534            For example, it wont work for:
1535                address a = msg.sender
1536                require(a == owner)
1537        Returns
1538            (bool)
1539        """
1540
1541        if self._is_protected is None:
1542            if self.is_constructor:
1543                self._is_protected = True
1544                return True
1545            if "onlyOwner" in [m.name for m in self.modifiers]:
1546                self._is_protected = True
1547                return True
1548            conditional_vars = self.all_conditional_solidity_variables_read(include_loop=False)
1549            args_vars = self.all_solidity_variables_used_as_args()
1550            self._is_protected = (
1551                SolidityVariableComposed("msg.sender") in conditional_vars + args_vars
1552            )
1553        return self._is_protected
1554
1555    @property
1556    def is_reentrant(self) -> bool:
1557        """
1558        Determine if the function can be re-entered
1559        """
1560        reentrancy_modifier = "nonReentrant"
1561
1562        if self.function_language == FunctionLanguage.Vyper:
1563            reentrancy_modifier = "nonreentrant(lock)"
1564
1565        # TODO: compare with hash of known nonReentrant modifier instead of the name
1566        if reentrancy_modifier in [m.name for m in self.modifiers]:
1567            return False
1568
1569        if self.visibility in ["public", "external"]:
1570            return True
1571
1572        # If it's an internal function, check if all its entry points have the nonReentrant modifier
1573        all_entry_points = [
1574            f for f in self.all_reachable_from_functions if f.visibility in ["public", "external"]
1575        ]
1576        if not all_entry_points:
1577            return True
1578        return not all(
1579            reentrancy_modifier in [m.name for m in f.modifiers] for f in all_entry_points
1580        )
1581
1582    # endregion
1583    ###################################################################################
1584    ###################################################################################
1585    # region Analyses
1586    ###################################################################################
1587    ###################################################################################
1588
1589    def _analyze_read_write(self) -> None:
1590        """Compute variables read/written/..."""
1591        write_var = [x.variables_written_as_expression for x in self.nodes]
1592        write_var = [x for x in write_var if x]
1593        write_var = [item for sublist in write_var for item in sublist]
1594        write_var = list(set(write_var))
1595        # Remove duplicate if they share the same string representation
1596        write_var = [
1597            next(obj)
1598            for i, obj in groupby(sorted(write_var, key=lambda x: str(x)), lambda x: str(x))
1599        ]
1600        self._expression_vars_written = write_var
1601
1602        write_var = [x.variables_written for x in self.nodes]
1603        write_var = [x for x in write_var if x]
1604        write_var = [item for sublist in write_var for item in sublist]
1605        write_var = list(set(write_var))
1606        # Remove duplicate if they share the same string representation
1607        write_var = [
1608            next(obj)
1609            for i, obj in groupby(sorted(write_var, key=lambda x: str(x)), lambda x: str(x))
1610        ]
1611        self._vars_written = write_var
1612
1613        read_var = [x.variables_read_as_expression for x in self.nodes]
1614        read_var = [x for x in read_var if x]
1615        read_var = [item for sublist in read_var for item in sublist]
1616        # Remove duplicate if they share the same string representation
1617        read_var = [
1618            next(obj)
1619            for i, obj in groupby(sorted(read_var, key=lambda x: str(x)), lambda x: str(x))
1620        ]
1621        self._expression_vars_read = read_var
1622
1623        read_var = [x.variables_read for x in self.nodes]
1624        read_var = [x for x in read_var if x]
1625        read_var = [item for sublist in read_var for item in sublist]
1626        # Remove duplicate if they share the same string representation
1627        read_var = [
1628            next(obj)
1629            for i, obj in groupby(sorted(read_var, key=lambda x: str(x)), lambda x: str(x))
1630        ]
1631        self._vars_read = read_var
1632
1633        self._state_vars_written = [
1634            x for x in self.variables_written if isinstance(x, StateVariable)
1635        ]
1636        self._state_vars_read = [x for x in self.variables_read if isinstance(x, StateVariable)]
1637        self._solidity_vars_read = [
1638            x for x in self.variables_read if isinstance(x, SolidityVariable)
1639        ]
1640
1641        self._vars_read_or_written = self._vars_written + self._vars_read
1642
1643        slithir_variables = [x.slithir_variables for x in self.nodes]
1644        slithir_variables = [x for x in slithir_variables if x]
1645        self._slithir_variables = [item for sublist in slithir_variables for item in sublist]
1646
1647    def _analyze_calls(self) -> None:
1648        calls = [x.calls_as_expression for x in self.nodes]
1649        calls = [x for x in calls if x]
1650        calls = [item for sublist in calls for item in sublist]
1651        self._expression_calls = list(set(calls))
1652
1653        internal_calls = [x.internal_calls for x in self.nodes]
1654        internal_calls = [x for x in internal_calls if x]
1655        internal_calls = [item for sublist in internal_calls for item in sublist]
1656        self._internal_calls = list(set(internal_calls))
1657
1658        self._solidity_calls = [
1659            ir for ir in internal_calls if isinstance(ir.function, SolidityFunction)
1660        ]
1661
1662        low_level_calls = [x.low_level_calls for x in self.nodes]
1663        low_level_calls = [x for x in low_level_calls if x]
1664        low_level_calls = [item for sublist in low_level_calls for item in sublist]
1665        self._low_level_calls = list(set(low_level_calls))
1666
1667        high_level_calls = [x.high_level_calls for x in self.nodes]
1668        high_level_calls = [x for x in high_level_calls if x]
1669        high_level_calls = [item for sublist in high_level_calls for item in sublist]
1670        self._high_level_calls = list(set(high_level_calls))
1671
1672        library_calls = [x.library_calls for x in self.nodes]
1673        library_calls = [x for x in library_calls if x]
1674        library_calls = [item for sublist in library_calls for item in sublist]
1675        self._library_calls = list(set(library_calls))
1676
1677        external_calls_as_expressions = [x.external_calls_as_expressions for x in self.nodes]
1678        external_calls_as_expressions = [x for x in external_calls_as_expressions if x]
1679        external_calls_as_expressions = [
1680            item for sublist in external_calls_as_expressions for item in sublist
1681        ]
1682        self._external_calls_as_expressions = list(set(external_calls_as_expressions))
1683
1684    # endregion
1685    ###################################################################################
1686    ###################################################################################
1687    # region Nodes
1688    ###################################################################################
1689    ###################################################################################
1690
1691    def new_node(
1692        self, node_type: "NodeType", src: str | dict, scope: Union[Scope, "Function"]
1693    ) -> "Node":
1694        from slither.core.cfg.node import Node
1695
1696        node = Node(node_type, self._counter_nodes, scope, self.file_scope)
1697        node.set_offset(src, self.compilation_unit)
1698        self._counter_nodes += 1
1699        node.set_function(self)
1700        self._nodes.append(node)
1701
1702        return node
1703
1704    # endregion
1705    ###################################################################################
1706    ###################################################################################
1707    # region SlithIr and SSA
1708    ###################################################################################
1709    ###################################################################################
1710
1711    def _get_last_ssa_variable_instances(
1712        self, target_state: bool, target_local: bool
1713    ) -> dict[str, set["SlithIRVariable"]]:
1714        from slither.slithir.variables import ReferenceVariable
1715        from slither.slithir.operations import OperationWithLValue
1716        from slither.core.cfg.node import NodeType
1717
1718        if not self.is_implemented:
1719            return {}
1720
1721        if self._entry_point is None:
1722            return {}
1723        # node, values
1724        to_explore: list[tuple[Node, dict]] = [(self._entry_point, {})]
1725        # node -> values
1726        explored: dict = {}
1727        # name -> instances
1728        ret: dict = {}
1729
1730        while to_explore:
1731            node, values = to_explore[0]
1732            to_explore = to_explore[1::]
1733
1734            if node.type != NodeType.ENTRYPOINT:
1735                for ir_ssa in node.irs_ssa:
1736                    if isinstance(ir_ssa, OperationWithLValue):
1737                        lvalue = ir_ssa.lvalue
1738                        if isinstance(lvalue, ReferenceVariable):
1739                            lvalue = lvalue.points_to_origin
1740                        if isinstance(lvalue, StateVariable) and target_state:
1741                            values[lvalue.canonical_name] = {lvalue}
1742                        if isinstance(lvalue, LocalVariable) and target_local:
1743                            values[lvalue.canonical_name] = {lvalue}
1744
1745            # Check for fixpoint
1746            if node in explored:
1747                if values == explored[node]:
1748                    continue
1749                for k, instances in values.items():
1750                    if k not in explored[node]:
1751                        explored[node][k] = set()
1752                    explored[node][k] |= instances
1753                values = explored[node]
1754            else:
1755                explored[node] = values
1756
1757            # Return condition
1758            if node.will_return:
1759                for name, instances in values.items():
1760                    if name not in ret:
1761                        ret[name] = set()
1762                    ret[name] |= instances
1763
1764            for son in node.sons:
1765                to_explore.append((son, dict(values)))
1766
1767        return ret
1768
1769    def get_last_ssa_state_variables_instances(
1770        self,
1771    ) -> dict[str, set["SlithIRVariable"]]:
1772        return self._get_last_ssa_variable_instances(target_state=True, target_local=False)
1773
1774    def get_last_ssa_local_variables_instances(
1775        self,
1776    ) -> dict[str, set["SlithIRVariable"]]:
1777        return self._get_last_ssa_variable_instances(target_state=False, target_local=True)
1778
1779    @staticmethod
1780    def _unchange_phi(ir: "Operation") -> bool:
1781        from slither.slithir.operations import Phi, PhiCallback
1782
1783        if not isinstance(ir, (Phi, PhiCallback)) or len(ir.rvalues) > 1:
1784            return False
1785        if not ir.rvalues:
1786            return True
1787        return ir.rvalues[0] == ir.lvalue
1788
1789    def _fix_phi_entry(
1790        self,
1791        node: "Node",
1792        last_state_variables_instances: dict[str, list["StateVariable"]],
1793        initial_state_variables_instances: dict[str, "StateVariable"],
1794    ) -> None:
1795        from slither.slithir.variables import Constant, StateIRVariable, LocalIRVariable
1796
1797        for ir in node.irs_ssa:
1798            if isinstance(ir.lvalue, StateIRVariable):
1799                additional = [initial_state_variables_instances[ir.lvalue.canonical_name]]
1800                additional += last_state_variables_instances[ir.lvalue.canonical_name]
1801                ir.rvalues = list(set(additional + ir.rvalues))
1802            # function parameter that are storage pointer
1803            else:
1804                # find index of the parameter
1805                idx = self.parameters.index(ir.lvalue.non_ssa_version)
1806                # find non ssa version of that index
1807                additional = [n.ir.arguments[idx] for n in self.reachable_from_nodes]
1808                additional = unroll(additional)
1809                additional = [a for a in additional if not isinstance(a, Constant)]
1810                ir.rvalues = list(set(additional + ir.rvalues))
1811
1812                if isinstance(ir.lvalue, LocalIRVariable) and ir.lvalue.is_storage:
1813                    # Update the refers_to to point to the phi rvalues
1814                    # This basically means that the local variable is a storage that point to any
1815                    # state variable that the storage pointer alias analysis found
1816                    ir.lvalue.refers_to = [
1817                        rvalue for rvalue in ir.rvalues if isinstance(rvalue, StateIRVariable)
1818                    ]
1819
1820    def fix_phi(
1821        self,
1822        last_state_variables_instances: dict[str, list["StateVariable"]],
1823        initial_state_variables_instances: dict[str, "StateVariable"],
1824    ) -> None:
1825        from slither.slithir.operations import InternalCall, PhiCallback, Phi
1826        from slither.slithir.variables import StateIRVariable, LocalIRVariable
1827
1828        for node in self.nodes:
1829            if node == self.entry_point:
1830                self._fix_phi_entry(
1831                    node, last_state_variables_instances, initial_state_variables_instances
1832                )
1833            for ir in node.irs_ssa:
1834                if isinstance(ir, PhiCallback):
1835                    callee_ir = ir.callee_ir
1836                    if isinstance(callee_ir, InternalCall):
1837                        last_ssa = callee_ir.function.get_last_ssa_state_variables_instances()
1838                        if ir.lvalue.canonical_name in last_ssa:
1839                            ir.rvalues = list(last_ssa[ir.lvalue.canonical_name])
1840                        else:
1841                            ir.rvalues = [ir.lvalue]
1842                    else:
1843                        additional = last_state_variables_instances[ir.lvalue.canonical_name]
1844                        ir.rvalues = list(set(additional + ir.rvalues))
1845
1846                # Propage storage ref information if it does not exist
1847                # This can happen if the refers_to variable was discovered through the phi operator on function parameter
1848                # aka you have storage pointer as function parameter
1849                # instead of having a storage pointer for which the aliases belong to the function body
1850                if (
1851                    isinstance(ir, Phi)
1852                    and isinstance(ir.lvalue, LocalIRVariable)
1853                    and ir.lvalue.is_storage
1854                    and not ir.lvalue.refers_to
1855                ):
1856                    refers_to = []
1857                    for candidate in ir.rvalues:
1858                        if isinstance(candidate, StateIRVariable):
1859                            refers_to.append(candidate)
1860                        if isinstance(candidate, LocalIRVariable) and candidate.is_storage:
1861                            refers_to += candidate.refers_to
1862
1863                    ir.lvalue.refers_to = refers_to
1864
1865            node.irs_ssa = [ir for ir in node.irs_ssa if not self._unchange_phi(ir)]
1866
1867    def generate_slithir_and_analyze(self) -> None:
1868        for node in self.nodes:
1869            node.slithir_generation()
1870
1871        self._analyze_read_write()
1872        self._analyze_calls()
1873
1874    @abstractmethod
1875    def generate_slithir_ssa(self, all_ssa_state_variables_instances):
1876        pass
1877
1878    def update_read_write_using_ssa(self) -> None:
1879        for node in self.nodes:
1880            node.update_read_write_using_ssa()
1881        self._analyze_read_write()
1882
1883    ###################################################################################
1884    ###################################################################################
1885    # region Built in definitions
1886    ###################################################################################
1887    ###################################################################################
1888
1889    def __str__(self) -> str:
1890        return self.name
1891
1892    # endregion

Function class

function_language: FunctionLanguage
has_documentation: bool
name: str
240    @property
241    def name(self) -> str:
242        """
243        str: function name
244        """
245        if self._name == "" and self._function_type == FunctionType.CONSTRUCTOR:
246            return "constructor"
247        if self._name == "" and self._function_type == FunctionType.FALLBACK:
248            return "fallback"
249        if self._function_type == FunctionType.RECEIVE:
250            return "receive"
251        if self._function_type == FunctionType.CONSTRUCTOR_VARIABLES:
252            return "slitherConstructorVariables"
253        if self._function_type == FunctionType.CONSTRUCTOR_CONSTANT_VARIABLES:
254            return "slitherConstructorConstantVariables"
255        return self._name

str: function name

internal_scope: list[str]
261    @property
262    def internal_scope(self) -> list[str]:
263        """
264        Return a list of name representing the scope of the function
265        This is used to model nested functions declared in YUL
266
267        :return:
268        """
269        return self._internal_scope

Return a list of name representing the scope of the function This is used to model nested functions declared in YUL

Returns
full_name: str
275    @property
276    def full_name(self) -> str:
277        """
278        str: func_name(type1,type2)
279        Return the function signature without the return values
280        The difference between this function and solidity_function is that full_name does not translate the underlying
281        type (ex: structure, contract to address, ...)
282        """
283        if self._full_name is None:
284            name, parameters, _ = self.signature
285            full_name = ".".join(self._internal_scope + [name]) + "(" + ",".join(parameters) + ")"
286            self._full_name = full_name
287        return self._full_name

str: func_name(type1,type2) Return the function signature without the return values The difference between this function and solidity_function is that full_name does not translate the underlying type (ex: structure, contract to address, ...)

canonical_name: str
289    @property
290    @abstractmethod
291    def canonical_name(self) -> str:
292        """
293        str: contract.func_name(type1,type2)
294        Return the function signature without the return values
295        """
296        return ""

str: contract.func_name(type1,type2) Return the function signature without the return values

contains_assembly: bool
298    @property
299    def contains_assembly(self) -> bool:
300        return self._contains_assembly
def can_reenter( self, callstack: list[typing.Union[Function, slither.core.variables.variable.Variable]] | None = None) -> bool:
306    def can_reenter(self, callstack: list[Union["Function", "Variable"]] | None = None) -> bool:
307        """
308        Check if the function can re-enter
309        Follow internal calls.
310        Do not consider CREATE as potential re-enter, but check if the
311        destination's constructor can contain a call (recurs. follow nested CREATE)
312        For Solidity > 0.5, filter access to public variables and constant/pure/view
313        For call to this. check if the destination can re-enter
314        Do not consider Send/Transfer as there is not enough gas
315        :param callstack: used internally to check for recursion
316        :return bool:
317        """
318        from slither.slithir.operations import Call
319
320        if self._can_reenter is None:
321            self._can_reenter = False
322            for ir in self.all_slithir_operations():
323                if isinstance(ir, Call) and ir.can_reenter(callstack):
324                    self._can_reenter = True
325                    return True
326        return self._can_reenter

Check if the function can re-enter Follow internal calls. Do not consider CREATE as potential re-enter, but check if the destination's constructor can contain a call (recurs. follow nested CREATE) For Solidity > 0.5, filter access to public variables and constant/pure/view For call to this. check if the destination can re-enter Do not consider Send/Transfer as there is not enough gas

Parameters
  • callstack: used internally to check for recursion
Returns
def can_send_eth(self) -> bool:
328    def can_send_eth(self) -> bool:
329        """
330        Check if the function or any internal (not external) functions called by it can send eth
331        :return bool:
332        """
333        from slither.slithir.operations import Call
334
335        if self._can_send_eth is None:
336            self._can_send_eth = False
337            for ir in self.all_slithir_operations():
338                if isinstance(ir, Call) and ir.can_send_eth():
339                    self._can_send_eth = True
340                    return True
341        return self._can_send_eth

Check if the function or any internal (not external) functions called by it can send eth

Returns
is_checked: bool
343    @property
344    def is_checked(self) -> bool:
345        """
346        Return true if the overflow are enabled by default
347
348
349        :return:
350        """
351
352        return self.compilation_unit.solc_version >= "0.8.0"

Return true if the overflow are enabled by default

Returns
id: str | None
354    @property
355    def id(self) -> str | None:
356        """
357        Return the reference ID of the function, if available.
358
359        :return:
360        :rtype:
361        """
362        return self._id

Return the reference ID of the function, if available.

Returns

file_scope: slither.core.scope.scope.FileScope
368    @property
369    @abstractmethod
370    def file_scope(self) -> "FileScope":
371        pass
def set_function_type(self, t: FunctionType) -> None:
380    def set_function_type(self, t: FunctionType) -> None:
381        assert isinstance(t, FunctionType)
382        self._function_type = t
function_type: FunctionType | None
384    @property
385    def function_type(self) -> FunctionType | None:
386        return self._function_type
is_constructor: bool
392    @property
393    def is_constructor(self) -> bool:
394        """
395        bool: True if the function is the constructor
396        """
397        return self._function_type == FunctionType.CONSTRUCTOR

bool: True if the function is the constructor

is_constructor_variables: bool
399    @property
400    def is_constructor_variables(self) -> bool:
401        """
402        bool: True if the function is the constructor of the variables
403        Slither has inbuilt functions to hold the state variables initialization
404        """
405        return self._function_type in [
406            FunctionType.CONSTRUCTOR_VARIABLES,
407            FunctionType.CONSTRUCTOR_CONSTANT_VARIABLES,
408        ]

bool: True if the function is the constructor of the variables Slither has inbuilt functions to hold the state variables initialization

is_fallback: bool
410    @property
411    def is_fallback(self) -> bool:
412        """
413            Determine if the function is the fallback function for the contract
414        Returns
415            (bool)
416        """
417        return self._function_type == FunctionType.FALLBACK

Determine if the function is the fallback function for the contract Returns (bool)

is_receive: bool
419    @property
420    def is_receive(self) -> bool:
421        """
422            Determine if the function is the receive function for the contract
423        Returns
424            (bool)
425        """
426        return self._function_type == FunctionType.RECEIVE

Determine if the function is the receive function for the contract Returns (bool)

payable: bool
435    @property
436    def payable(self) -> bool:
437        """
438        bool: True if the function is payable
439        """
440        return self._payable

bool: True if the function is payable

is_virtual: bool
453    @property
454    def is_virtual(self) -> bool:
455        """
456        Note for Solidity < 0.6.0 it will always be false
457        bool: True if the function is virtual
458        """
459        return self._virtual

Note for Solidity < 0.6.0 it will always be false bool: True if the function is virtual

is_override: bool
465    @property
466    def is_override(self) -> bool:
467        """
468        Note for Solidity < 0.6.0 it will always be false
469        bool: True if the function overrides a base function
470        """
471        return len(self._overrides) > 0

Note for Solidity < 0.6.0 it will always be false bool: True if the function overrides a base function

473    @property
474    def overridden_by(self) -> list["FunctionContract"]:
475        """
476        List["FunctionContract"]: List of functions in child contracts that override this function
477        This may include distinct instances of the same function due to inheritance
478        """
479        return self._overridden_by

List["FunctionContract"]: List of functions in child contracts that override this function This may include distinct instances of the same function due to inheritance

481    @property
482    def overrides(self) -> list["FunctionContract"]:
483        """
484        List["FunctionContract"]: List of functions in parent contracts that this function overrides
485        This may include distinct instances of the same function due to inheritance
486        """
487        return self._overrides

List["FunctionContract"]: List of functions in parent contracts that this function overrides This may include distinct instances of the same function due to inheritance

visibility: str
496    @property
497    def visibility(self) -> str:
498        """
499        str: Function visibility
500        """
501        assert self._visibility is not None
502        return self._visibility

str: Function visibility

def set_visibility(self, v: str) -> None:
508    def set_visibility(self, v: str) -> None:
509        self._visibility = v
view: bool
511    @property
512    def view(self) -> bool:
513        """
514        bool: True if the function is declared as view
515        """
516        return self._view

bool: True if the function is declared as view

pure: bool
522    @property
523    def pure(self) -> bool:
524        """
525        bool: True if the function is declared as pure
526        """
527        return self._pure

bool: True if the function is declared as pure

is_shadowed: bool
533    @property
534    def is_shadowed(self) -> bool:
535        return self._is_shadowed
shadows: bool
541    @property
542    def shadows(self) -> bool:
543        return self._shadows
is_implemented: bool
556    @property
557    def is_implemented(self) -> bool:
558        """
559        bool: True if the function is implemented
560        """
561        return self._is_implemented

bool: True if the function is implemented

is_empty: bool
567    @property
568    def is_empty(self) -> bool:
569        """
570        bool: True if the function is empty, None if the function is an interface
571        """
572        return self._is_empty

bool: True if the function is empty, None if the function is an interface

nodes: list[slither.core.cfg.node.Node]
585    @property
586    def nodes(self) -> list["Node"]:
587        """
588        list(Node): List of the nodes
589        """
590        return list(self._nodes)

list(Node): List of the nodes

entry_point: Optional[slither.core.cfg.node.Node]
596    @property
597    def entry_point(self) -> Optional["Node"]:
598        """
599        Node: Entry point of the function
600        """
601        return self._entry_point

Node: Entry point of the function

def add_node(self, node: slither.core.cfg.node.Node) -> None:
607    def add_node(self, node: "Node") -> None:
608        if not self._entry_point:
609            self._entry_point = node
610        self._nodes.append(node)
nodes_ordered_dominators: list[slither.core.cfg.node.Node]
612    @property
613    def nodes_ordered_dominators(self) -> list["Node"]:
614        # TODO: does not work properly; most likely due to modifier call
615        # This will not work for modifier call that lead to multiple nodes
616        # from slither.core.cfg.node import NodeType
617        if self._nodes_ordered_dominators is None:
618            self._nodes_ordered_dominators = []
619            if self.entry_point:
620                self._compute_nodes_ordered_dominators(self.entry_point)
621
622            for node in self.nodes:
623                # if node.type == NodeType.OTHER_ENTRYPOINT:
624                if node not in self._nodes_ordered_dominators:
625                    self._compute_nodes_ordered_dominators(node)
626
627        return self._nodes_ordered_dominators
644    @property
645    def parameters(self) -> list["LocalVariable"]:
646        """
647        list(LocalVariable): List of the parameters
648        """
649        return list(self._parameters)

list(LocalVariable): List of the parameters

def add_parameters(self, p: slither.core.variables.local_variable.LocalVariable) -> None:
651    def add_parameters(self, p: "LocalVariable") -> None:
652        self._parameters.append(p)
654    @property
655    def parameters_ssa(self) -> list["LocalIRVariable"]:
656        """
657        list(LocalIRVariable): List of the parameters (SSA form)
658        """
659        return list(self._parameters_ssa)

list(LocalIRVariable): List of the parameters (SSA form)

def add_parameter_ssa( self, var: slither.slithir.variables.local_variable.LocalIRVariable) -> None:
661    def add_parameter_ssa(self, var: "LocalIRVariable") -> None:
662        self._parameters_ssa.append(var)
def parameters_src(self) -> slither.core.source_mapping.source_mapping.SourceMapping:
664    def parameters_src(self) -> SourceMapping:
665        return self._parameters_src
return_type: list[slither.core.solidity_types.type.Type] | None
674    @property
675    def return_type(self) -> list[Type] | None:
676        """
677        Return the list of return type
678        If no return, return None
679        """
680        returns = self.returns
681        if returns:
682            return [r.type for r in returns]
683        return None

Return the list of return type If no return, return None

def returns_src(self) -> slither.core.source_mapping.source_mapping.SourceMapping:
685    def returns_src(self) -> SourceMapping:
686        return self._returns_src
type: list[slither.core.solidity_types.type.Type] | None
688    @property
689    def type(self) -> list[Type] | None:
690        """
691        Return the list of return type
692        If no return, return None
693        Alias of return_type
694        """
695        return self.return_type

Return the list of return type If no return, return None Alias of return_type

697    @property
698    def returns(self) -> list["LocalVariable"]:
699        """
700        list(LocalVariable): List of the return variables
701        """
702        return list(self._returns)

list(LocalVariable): List of the return variables

def add_return(self, r: slither.core.variables.local_variable.LocalVariable) -> None:
704    def add_return(self, r: "LocalVariable") -> None:
705        self._returns.append(r)
707    @property
708    def returns_ssa(self) -> list["LocalIRVariable"]:
709        """
710        list(LocalIRVariable): List of the return variables (SSA form)
711        """
712        return list(self._returns_ssa)

list(LocalIRVariable): List of the return variables (SSA form)

def add_return_ssa( self, var: slither.slithir.variables.local_variable.LocalIRVariable) -> None:
714    def add_return_ssa(self, var: "LocalIRVariable") -> None:
715        self._returns_ssa.append(var)
modifiers: list[typing.Union[slither.core.declarations.contract.Contract, Function]]
724    @property
725    def modifiers(self) -> list[Union["Contract", "Function"]]:
726        """
727        list(Modifier): List of the modifiers
728        Can be contract for constructor's calls
729
730        """
731        return [c.modifier for c in self._modifiers]

list(Modifier): List of the modifiers Can be contract for constructor's calls

def add_modifier( self, modif: ModifierStatements) -> None:
733    def add_modifier(self, modif: "ModifierStatements") -> None:
734        self._modifiers.append(modif)
modifiers_statements: list[ModifierStatements]
736    @property
737    def modifiers_statements(self) -> list[ModifierStatements]:
738        """
739        list(ModifierCall): List of the modifiers call (include expression and irs)
740        """
741        return list(self._modifiers)

list(ModifierCall): List of the modifiers call (include expression and irs)

explicit_base_constructor_calls: list[Function]
743    @property
744    def explicit_base_constructor_calls(self) -> list["Function"]:
745        """
746        list(Function): List of the base constructors called explicitly by this presumed constructor definition.
747
748                        Base constructors implicitly or explicitly called by the contract definition will not be
749                        included.
750        """
751        # This is a list of contracts internally, so we convert it to a list of constructor functions.
752        return [
753            c.modifier.constructors_declared
754            for c in self._explicit_base_constructor_calls
755            if c.modifier.constructors_declared
756        ]

list(Function): List of the base constructors called explicitly by this presumed constructor definition.

Base constructors implicitly or explicitly called by the contract definition will not be included.

explicit_base_constructor_calls_statements: list[ModifierStatements]
758    @property
759    def explicit_base_constructor_calls_statements(self) -> list[ModifierStatements]:
760        """
761        list(ModifierCall): List of the base constructors called explicitly by this presumed constructor definition.
762
763        """
764        # This is a list of contracts internally, so we convert it to a list of constructor functions.
765        return list(self._explicit_base_constructor_calls)

list(ModifierCall): List of the base constructors called explicitly by this presumed constructor definition.

def add_explicit_base_constructor_calls_statements( self, modif: ModifierStatements) -> None:
767    def add_explicit_base_constructor_calls_statements(self, modif: ModifierStatements) -> None:
768        self._explicit_base_constructor_calls.append(modif)
777    @property
778    def variables(self) -> list[LocalVariable]:
779        """
780        Return all local variables
781        Include parameters and return values
782        """
783        return list(self._variables.values())

Return all local variables Include parameters and return values

local_variables: list[slither.core.variables.local_variable.LocalVariable]
785    @property
786    def local_variables(self) -> list[LocalVariable]:
787        """
788        Return all local variables (dont include parameters and return values)
789        """
790        return list(set(self.variables) - set(self.returns) - set(self.parameters))

Return all local variables (dont include parameters and return values)

variables_as_dict: dict[str, slither.core.variables.local_variable.LocalVariable]
792    @property
793    def variables_as_dict(self) -> dict[str, LocalVariable]:
794        return self._variables
variables_read: list[slither.core.variables.variable.Variable]
796    @property
797    def variables_read(self) -> list["Variable"]:
798        """
799        list(Variable): Variables read (local/state/solidity)
800        """
801        return list(self._vars_read)

list(Variable): Variables read (local/state/solidity)

variables_written: list[slither.core.variables.variable.Variable]
803    @property
804    def variables_written(self) -> list["Variable"]:
805        """
806        list(Variable): Variables written (local/state/solidity)
807        """
808        return list(self._vars_written)

list(Variable): Variables written (local/state/solidity)

state_variables_read: list[slither.core.variables.state_variable.StateVariable]
810    @property
811    def state_variables_read(self) -> list["StateVariable"]:
812        """
813        list(StateVariable): State variables read
814        """
815        return list(self._state_vars_read)

list(StateVariable): State variables read

solidity_variables_read: list[slither.core.declarations.solidity_variables.SolidityVariable]
817    @property
818    def solidity_variables_read(self) -> list["SolidityVariable"]:
819        """
820        list(SolidityVariable): Solidity variables read
821        """
822        return list(self._solidity_vars_read)

list(SolidityVariable): Solidity variables read

state_variables_written: list[slither.core.variables.state_variable.StateVariable]
824    @property
825    def state_variables_written(self) -> list["StateVariable"]:
826        """
827        list(StateVariable): State variables written
828        """
829        return list(self._state_vars_written)

list(StateVariable): State variables written

variables_read_or_written: list[slither.core.variables.variable.Variable]
831    @property
832    def variables_read_or_written(self) -> list["Variable"]:
833        """
834        list(Variable): Variables read or written (local/state/solidity)
835        """
836        return list(self._vars_read_or_written)

list(Variable): Variables read or written (local/state/solidity)

variables_read_as_expression: list[slither.core.expressions.expression.Expression]
838    @property
839    def variables_read_as_expression(self) -> list["Expression"]:
840        return self._expression_vars_read
variables_written_as_expression: list[slither.core.expressions.expression.Expression]
842    @property
843    def variables_written_as_expression(self) -> list["Expression"]:
844        return self._expression_vars_written
slithir_variables: list[slither.slithir.variables.variable.SlithIRVariable]
846    @property
847    def slithir_variables(self) -> list["SlithIRVariable"]:
848        """
849        Temporary and Reference Variables (not SSA form)
850        """
851
852        return list(self._slithir_variables)

Temporary and Reference Variables (not SSA form)

internal_calls: list[slither.slithir.operations.internal_call.InternalCall]
861    @property
862    def internal_calls(self) -> list["InternalCall"]:
863        """
864        list(InternalCall): List of IR operations for internal calls
865        """
866        return list(self._internal_calls)

list(InternalCall): List of IR operations for internal calls

solidity_calls: list[slither.slithir.operations.solidity_call.SolidityCall]
868    @property
869    def solidity_calls(self) -> list["SolidityCall"]:
870        """
871        list(SolidityCall): List of IR operations for Solidity calls
872        """
873        return list(self._solidity_calls)

list(SolidityCall): List of IR operations for Solidity calls

875    @property
876    def high_level_calls(self) -> list[tuple["Contract", "HighLevelCall"]]:
877        """
878        list(Tuple(Contract, "HighLevelCall")): List of call target contract and IR of the high level call
879        A variable is called in case of call to a public state variable
880        Include library calls
881        """
882        return list(self._high_level_calls)

list(Tuple(Contract, "HighLevelCall")): List of call target contract and IR of the high level call A variable is called in case of call to a public state variable Include library calls

library_calls: list[slither.slithir.operations.library_call.LibraryCall]
884    @property
885    def library_calls(self) -> list["LibraryCall"]:
886        """
887        list(LibraryCall): List of IR operations for library calls
888        """
889        return list(self._library_calls)

list(LibraryCall): List of IR operations for library calls

low_level_calls: list[slither.slithir.operations.low_level_call.LowLevelCall]
891    @property
892    def low_level_calls(self) -> list["LowLevelCall"]:
893        """
894        list(LowLevelCall): List of IR operations for low level calls
895        A low level call is defined by
896        - the variable called
897        - the name of the function (call/delegatecall/callcode)
898        """
899        return list(self._low_level_calls)

list(LowLevelCall): List of IR operations for low level calls A low level call is defined by

  • the variable called
  • the name of the function (call/delegatecall/callcode)
external_calls_as_expressions: list[slither.core.expressions.expression.Expression]
901    @property
902    def external_calls_as_expressions(self) -> list["Expression"]:
903        """
904        list(ExpressionCall): List of message calls (that creates a transaction)
905        """
906        return list(self._external_calls_as_expressions)

list(ExpressionCall): List of message calls (that creates a transaction)

calls_as_expressions: list[slither.core.expressions.expression.Expression]
915    @property
916    def calls_as_expressions(self) -> list["Expression"]:
917        return self._expression_calls
expressions: list[slither.core.expressions.expression.Expression]
919    @property
920    def expressions(self) -> list["Expression"]:
921        """
922        list(Expression): List of the expressions
923        """
924        if self._expressions is None:
925            expressionss = [n.expression for n in self.nodes]
926            expressions = [e for e in expressionss if e]
927            self._expressions = expressions
928        return self._expressions

list(Expression): List of the expressions

return_values: list[slither.slithir.variables.variable.SlithIRVariable]
930    @property
931    def return_values(self) -> list["SlithIRVariable"]:
932        """
933        list(Return Values): List of the return values
934        """
935        from slither.core.cfg.node import NodeType
936        from slither.slithir.operations import Return
937        from slither.slithir.variables import Constant
938
939        if self._return_values is None:
940            return_values = []
941            returns = [n for n in self.nodes if n.type == NodeType.RETURN]
942            [
943                return_values.extend(ir.values)
944                for node in returns
945                for ir in node.irs
946                if isinstance(ir, Return)
947            ]
948            self._return_values = list({x for x in return_values if not isinstance(x, Constant)})
949        return self._return_values

list(Return Values): List of the return values

return_values_ssa: list[slither.slithir.variables.variable.SlithIRVariable]
951    @property
952    def return_values_ssa(self) -> list["SlithIRVariable"]:
953        """
954        list(Return Values in SSA form): List of the return values in ssa form
955        """
956        from slither.core.cfg.node import NodeType
957        from slither.slithir.operations import Return
958        from slither.slithir.variables import Constant
959
960        if self._return_values_ssa is None:
961            return_values_ssa = []
962            returns = [n for n in self.nodes if n.type == NodeType.RETURN]
963            [
964                return_values_ssa.extend(ir.values)
965                for node in returns
966                for ir in node.irs_ssa
967                if isinstance(ir, Return)
968            ]
969            self._return_values_ssa = list(
970                {x for x in return_values_ssa if not isinstance(x, Constant)}
971            )
972        return self._return_values_ssa

list(Return Values in SSA form): List of the return values in ssa form

slithir_operations: list[slither.slithir.operations.operation.Operation]
981    @property
982    def slithir_operations(self) -> list["Operation"]:
983        """
984        list(Operation): List of the slithir operations
985        """
986        if self._slithir_operations is None:
987            operationss = [n.irs for n in self.nodes]
988            operations = [item for sublist in operationss for item in sublist if item]
989            self._slithir_operations = operations
990        return self._slithir_operations

list(Operation): List of the slithir operations

slithir_ssa_operations: list[slither.slithir.operations.operation.Operation]
 992    @property
 993    def slithir_ssa_operations(self) -> list["Operation"]:
 994        """
 995        list(Operation): List of the slithir operations (SSA)
 996        """
 997        if self._slithir_ssa_operations is None:
 998            operationss = [n.irs_ssa for n in self.nodes]
 999            operations = [item for sublist in operationss for item in sublist if item]
1000            self._slithir_ssa_operations = operations
1001        return self._slithir_ssa_operations

list(Operation): List of the slithir operations (SSA)

solidity_signature: str
1010    @property
1011    def solidity_signature(self) -> str:
1012        """
1013        Return a signature following the Solidity Standard
1014        Contract and converted into address
1015
1016        It might still keep internal types (ex: structure name) for internal functions.
1017        The reason is that internal functions allows recursive structure definition, which
1018        can't be converted following the Solidity stand ard
1019
1020        :return: the solidity signature
1021        """
1022        if self._solidity_signature is None:
1023            parameters = [
1024                convert_type_for_solidity_signature_to_string(x.type) for x in self.parameters
1025            ]
1026            self._solidity_signature = self.name + "(" + ",".join(parameters) + ")"
1027        return self._solidity_signature

Return a signature following the Solidity Standard Contract and converted into address

It might still keep internal types (ex: structure name) for internal functions. The reason is that internal functions allows recursive structure definition, which can't be converted following the Solidity stand ard

Returns

the solidity signature

signature: tuple[str, list[str], list[str]]
1029    @property
1030    def signature(self) -> tuple[str, list[str], list[str]]:
1031        """
1032        (str, list(str), list(str)): Function signature as
1033        (name, list parameters type, list return values type)
1034        """
1035        # FIXME memoizing this function is not working properly for vyper
1036        # if self._signature is None:
1037        return (
1038            self.name,
1039            [str(x.type) for x in self.parameters],
1040            [str(x.type) for x in self.returns],
1041        )
1042        #     self._signature = signature
1043        # return self._signature

(str, list(str), list(str)): Function signature as (name, list parameters type, list return values type)

signature_str: str
1045    @property
1046    def signature_str(self) -> str:
1047        """
1048        str: func_name(type1,type2) returns (type3)
1049        Return the function signature as a str (contains the return values)
1050        """
1051        if self._signature_str is None:
1052            name, parameters, returnVars = self.signature
1053            self._signature_str = (
1054                name + "(" + ",".join(parameters) + ") returns(" + ",".join(returnVars) + ")"
1055            )
1056        return self._signature_str

str: func_name(type1,type2) returns (type3) Return the function signature as a str (contains the return values)

functions_shadowed: list[Function]
1065    @property
1066    @abstractmethod
1067    def functions_shadowed(self) -> list["Function"]:
1068        pass
reachable_from_nodes: set[ReacheableNode]
1077    @property
1078    def reachable_from_nodes(self) -> set[ReacheableNode]:
1079        """
1080        Return
1081            ReacheableNode
1082        """
1083        return self._reachable_from_nodes

Return ReacheableNode

reachable_from_functions: set[Function]
1085    @property
1086    def reachable_from_functions(self) -> set["Function"]:
1087        return self._reachable_from_functions
all_reachable_from_functions: set[Function]
1089    @property
1090    def all_reachable_from_functions(self) -> set["Function"]:
1091        """
1092        Give the recursive version of reachable_from_functions (all the functions that lead to call self in the CFG)
1093        """
1094        if self._all_reachable_from_functions is None:
1095            functions: set[Function] = set()
1096
1097            new_functions = self.reachable_from_functions
1098            # iterate until we have are finding new functions
1099            while new_functions and not new_functions.issubset(functions):
1100                functions = functions.union(new_functions)
1101                # Use a temporary set, because we iterate over new_functions
1102                new_functionss: set[Function] = set()
1103                for f in new_functions:
1104                    new_functionss = new_functionss.union(f.reachable_from_functions)
1105                new_functions = new_functionss - functions
1106
1107            self._all_reachable_from_functions = functions
1108        return self._all_reachable_from_functions

Give the recursive version of reachable_from_functions (all the functions that lead to call self in the CFG)

def add_reachable_from_node( self, n: slither.core.cfg.node.Node, ir: slither.slithir.operations.operation.Operation) -> None:
1110    def add_reachable_from_node(self, n: "Node", ir: "Operation") -> None:
1111        self._reachable_from_nodes.add(ReacheableNode(n, ir))
1112        self._reachable_from_functions.add(n.function)
def all_variables_read(self) -> list[slither.core.variables.variable.Variable]:
1156    def all_variables_read(self) -> list["Variable"]:
1157        """recursive version of variables_read"""
1158        if self._all_variables_read is None:
1159            self._all_variables_read = self._explore_functions(lambda x: x.variables_read)
1160        return self._all_variables_read

recursive version of variables_read

def all_variables_written(self) -> list[slither.core.variables.variable.Variable]:
1162    def all_variables_written(self) -> list["Variable"]:
1163        """recursive version of variables_written"""
1164        if self._all_variables_written is None:
1165            self._all_variables_written = self._explore_functions(lambda x: x.variables_written)
1166        return self._all_variables_written

recursive version of variables_written

def all_state_variables_read(self) -> list[slither.core.variables.state_variable.StateVariable]:
1168    def all_state_variables_read(self) -> list["StateVariable"]:
1169        """recursive version of variables_read"""
1170        if self._all_state_variables_read is None:
1171            self._all_state_variables_read = self._explore_functions(
1172                lambda x: x.state_variables_read
1173            )
1174        return self._all_state_variables_read

recursive version of variables_read

def all_solidity_variables_read( self) -> list[slither.core.declarations.solidity_variables.SolidityVariable]:
1176    def all_solidity_variables_read(self) -> list[SolidityVariable]:
1177        """recursive version of solidity_read"""
1178        if self._all_solidity_variables_read is None:
1179            self._all_solidity_variables_read = self._explore_functions(
1180                lambda x: x.solidity_variables_read
1181            )
1182        return self._all_solidity_variables_read

recursive version of solidity_read

def all_slithir_variables(self) -> list[slither.slithir.variables.variable.SlithIRVariable]:
1184    def all_slithir_variables(self) -> list["SlithIRVariable"]:
1185        """recursive version of slithir_variables"""
1186        if self._all_slithir_variables is None:
1187            self._all_slithir_variables = self._explore_functions(lambda x: x.slithir_variables)
1188        return self._all_slithir_variables

recursive version of slithir_variables

def all_nodes(self) -> list[slither.core.cfg.node.Node]:
1190    def all_nodes(self) -> list["Node"]:
1191        """recursive version of nodes"""
1192        if self._all_nodes is None:
1193            self._all_nodes = self._explore_functions(lambda x: x.nodes)
1194        return self._all_nodes

recursive version of nodes

def all_expressions(self) -> list[slither.core.expressions.expression.Expression]:
1196    def all_expressions(self) -> list["Expression"]:
1197        """recursive version of variables_read"""
1198        if self._all_expressions is None:
1199            self._all_expressions = self._explore_functions(lambda x: x.expressions)
1200        return self._all_expressions

recursive version of variables_read

def all_slithir_operations(self) -> list[slither.slithir.operations.operation.Operation]:
1202    def all_slithir_operations(self) -> list["Operation"]:
1203        if self._all_slithir_operations is None:
1204            self._all_slithir_operations = self._explore_functions(lambda x: x.slithir_operations)
1205        return self._all_slithir_operations
def all_state_variables_written(self) -> list[slither.core.variables.state_variable.StateVariable]:
1207    def all_state_variables_written(self) -> list[StateVariable]:
1208        """recursive version of variables_written"""
1209        if self._all_state_variables_written is None:
1210            self._all_state_variables_written = self._explore_functions(
1211                lambda x: x.state_variables_written
1212            )
1213        return self._all_state_variables_written

recursive version of variables_written

def all_internal_calls(self) -> list[slither.slithir.operations.internal_call.InternalCall]:
1215    def all_internal_calls(self) -> list["InternalCall"]:
1216        """recursive version of internal_calls"""
1217        if self._all_internals_calls is None:
1218            self._all_internals_calls = self._explore_functions(lambda x: x.internal_calls)
1219        return self._all_internals_calls

recursive version of internal_calls

def all_low_level_calls(self) -> list[slither.slithir.operations.low_level_call.LowLevelCall]:
1221    def all_low_level_calls(self) -> list["LowLevelCall"]:
1222        """recursive version of low_level calls"""
1223        if self._all_low_level_calls is None:
1224            self._all_low_level_calls = self._explore_functions(lambda x: x.low_level_calls)
1225        return self._all_low_level_calls

recursive version of low_level calls

1227    def all_high_level_calls(self) -> list[tuple["Contract", "HighLevelCall"]]:
1228        """recursive version of high_level calls"""
1229        if self._all_high_level_calls is None:
1230            self._all_high_level_calls = self._explore_functions(lambda x: x.high_level_calls)
1231        return self._all_high_level_calls

recursive version of high_level calls

def all_library_calls(self) -> list[slither.slithir.operations.library_call.LibraryCall]:
1233    def all_library_calls(self) -> list["LibraryCall"]:
1234        """recursive version of library calls"""
1235        if self._all_library_calls is None:
1236            self._all_library_calls = self._explore_functions(lambda x: x.library_calls)
1237        return self._all_library_calls

recursive version of library calls

def all_solidity_calls(self) -> list[slither.slithir.operations.solidity_call.SolidityCall]:
1239    def all_solidity_calls(self) -> list["SolidityCall"]:
1240        """recursive version of solidity calls"""
1241        if self._all_solidity_calls is None:
1242            self._all_solidity_calls = self._explore_functions(lambda x: x.solidity_calls)
1243        return self._all_solidity_calls

recursive version of solidity calls

def all_conditional_state_variables_read( self, include_loop=True) -> list[slither.core.variables.state_variable.StateVariable]:
1250    def all_conditional_state_variables_read(self, include_loop=True) -> list["StateVariable"]:
1251        """
1252        Return the state variable used in a condition
1253
1254        Over approximate and also return index access
1255        It won't work if the variable is assigned to a temp variable
1256        """
1257        if include_loop:
1258            if self._all_conditional_state_variables_read_with_loop is None:
1259                self._all_conditional_state_variables_read_with_loop = self._explore_functions(
1260                    lambda x: self._explore_func_cond_read(x, include_loop)
1261                )
1262            return self._all_conditional_state_variables_read_with_loop
1263        if self._all_conditional_state_variables_read is None:
1264            self._all_conditional_state_variables_read = self._explore_functions(
1265                lambda x: self._explore_func_cond_read(x, include_loop)
1266            )
1267        return self._all_conditional_state_variables_read

Return the state variable used in a condition

Over approximate and also return index access It won't work if the variable is assigned to a temp variable

def all_conditional_solidity_variables_read( self, include_loop: bool = True) -> list[slither.core.declarations.solidity_variables.SolidityVariable]:
1288    def all_conditional_solidity_variables_read(
1289        self, include_loop: bool = True
1290    ) -> list[SolidityVariable]:
1291        """
1292        Return the Solidity variables directly used in a condition
1293
1294        Use of the IR to filter index access
1295        Assumption: the solidity vars are used directly in the conditional node
1296        It won't work if the variable is assigned to a temp variable
1297        """
1298        if include_loop:
1299            if self._all_conditional_solidity_variables_read_with_loop is None:
1300                self._all_conditional_solidity_variables_read_with_loop = self._explore_functions(
1301                    lambda x: self._explore_func_conditional(
1302                        x, self._solidity_variable_in_binary, include_loop
1303                    )
1304                )
1305            return self._all_conditional_solidity_variables_read_with_loop
1306
1307        if self._all_conditional_solidity_variables_read is None:
1308            self._all_conditional_solidity_variables_read = self._explore_functions(
1309                lambda x: self._explore_func_conditional(
1310                    x, self._solidity_variable_in_binary, include_loop
1311                )
1312            )
1313        return self._all_conditional_solidity_variables_read

Return the Solidity variables directly used in a condition

Use of the IR to filter index access Assumption: the solidity vars are used directly in the conditional node It won't work if the variable is assigned to a temp variable

def all_solidity_variables_used_as_args( self) -> list[slither.core.declarations.solidity_variables.SolidityVariable]:
1332    def all_solidity_variables_used_as_args(self) -> list[SolidityVariable]:
1333        """
1334        Return the Solidity variables directly used in a call
1335
1336        Use of the IR to filter index access
1337        Used to catch check(msg.sender)
1338        """
1339        if self._all_solidity_variables_used_as_args is None:
1340            self._all_solidity_variables_used_as_args = self._explore_functions(
1341                lambda x: self._explore_func_nodes(x, self._solidity_variable_in_internal_calls)
1342            )
1343        return self._all_solidity_variables_used_as_args

Return the Solidity variables directly used in a call

Use of the IR to filter index access Used to catch check(msg.sender)

def apply_visitor(self, Visitor: Callable) -> list:
1352    def apply_visitor(self, Visitor: Callable) -> list:
1353        """
1354            Apply a visitor to all the function expressions
1355        Args:
1356            Visitor: slither.visitors
1357        Returns
1358            list(): results of the visit
1359        """
1360        expressions = self.expressions
1361        v = [Visitor(e).result() for e in expressions]
1362        return [item for sublist in v for item in sublist]

Apply a visitor to all the function expressions Args: Visitor: slither.visitors Returns list(): results of the visit

def get_local_variable_from_name( self, variable_name: str) -> slither.core.variables.local_variable.LocalVariable | None:
1371    def get_local_variable_from_name(self, variable_name: str) -> LocalVariable | None:
1372        """
1373            Return a local variable from a name
1374
1375        Args:
1376            variable_name (str): name of the variable
1377        Returns:
1378            LocalVariable
1379        """
1380        return next((v for v in self.variables if v.name == variable_name), None)

Return a local variable from a name

Args: variable_name (str): name of the variable Returns: LocalVariable

def cfg_to_dot(self, filename: str):
1389    def cfg_to_dot(self, filename: str):
1390        """
1391            Export the function to a dot file
1392        Args:
1393            filename (str)
1394        """
1395        with open(filename, "w", encoding="utf8") as f:
1396            f.write("digraph{\n")
1397            for node in self.nodes:
1398                f.write(f'{node.node_id}[label="{node!s}"];\n')
1399                for son in node.sons:
1400                    f.write(f"{node.node_id}->{son.node_id};\n")
1401
1402            f.write("}\n")

Export the function to a dot file Args: filename (str)

def dominator_tree_to_dot(self, filename: str):
1404    def dominator_tree_to_dot(self, filename: str):
1405        """
1406            Export the dominator tree of the function to a dot file
1407        Args:
1408            filename (str)
1409        """
1410
1411        def description(node):
1412            desc = f"{node}\n"
1413            desc += f"id: {node.node_id}"
1414            if node.dominance_frontier:
1415                desc += f"\ndominance frontier: {[n.node_id for n in node.dominance_frontier]}"
1416            return desc
1417
1418        with open(filename, "w", encoding="utf8") as f:
1419            f.write("digraph{\n")
1420            for node in self.nodes:
1421                f.write(f'{node.node_id}[label="{description(node)}"];\n')
1422                if node.immediate_dominator:
1423                    f.write(f"{node.immediate_dominator.node_id}->{node.node_id};\n")
1424
1425            f.write("}\n")

Export the dominator tree of the function to a dot file Args: filename (str)

def slithir_cfg_to_dot(self, filename: str):
1427    def slithir_cfg_to_dot(self, filename: str):
1428        """
1429        Export the CFG to a DOT file. The nodes includes the Solidity expressions and the IRs
1430        :param filename:
1431        :return:
1432        """
1433        content = self.slithir_cfg_to_dot_str()
1434        with open(filename, "w", encoding="utf8") as f:
1435            f.write(content)

Export the CFG to a DOT file. The nodes includes the Solidity expressions and the IRs

Parameters
  • filename:
Returns
def slithir_cfg_to_dot_str(self, skip_expressions: bool = False) -> str:
1437    def slithir_cfg_to_dot_str(self, skip_expressions: bool = False) -> str:
1438        """
1439        Export the CFG to a DOT format. The nodes includes the Solidity expressions and the IRs
1440        :return: the DOT content
1441        :rtype: str
1442        """
1443        from slither.core.cfg.node import NodeType
1444
1445        content = ""
1446        content += "digraph{\n"
1447        for node in self.nodes:
1448            label = f"Node Type: {node.type.value} {node.node_id}\n"
1449            if node.expression and not skip_expressions:
1450                label += f"\nEXPRESSION:\n{node.expression}\n"
1451            if node.irs and not skip_expressions:
1452                label += "\nIRs:\n" + "\n".join([str(ir) for ir in node.irs])
1453            content += f'{node.node_id}[label="{label}"];\n'
1454            if node.type in [NodeType.IF, NodeType.IFLOOP]:
1455                true_node = node.son_true
1456                if true_node:
1457                    content += f'{node.node_id}->{true_node.node_id}[label="True"];\n'
1458                false_node = node.son_false
1459                if false_node:
1460                    content += f'{node.node_id}->{false_node.node_id}[label="False"];\n'
1461            else:
1462                for son in node.sons:
1463                    content += f"{node.node_id}->{son.node_id};\n"
1464
1465        content += "}\n"
1466        return content

Export the CFG to a DOT format. The nodes includes the Solidity expressions and the IRs

Returns

the DOT content

def is_reading(self, variable: slither.core.variables.variable.Variable) -> bool:
1475    def is_reading(self, variable: "Variable") -> bool:
1476        """
1477            Check if the function reads the variable
1478        Args:
1479            variable (Variable):
1480        Returns:
1481            bool: True if the variable is read
1482        """
1483        return variable in self.variables_read

Check if the function reads the variable Args: variable (Variable): Returns: bool: True if the variable is read

def is_reading_in_conditional_node(self, variable: slither.core.variables.variable.Variable) -> bool:
1485    def is_reading_in_conditional_node(self, variable: "Variable") -> bool:
1486        """
1487            Check if the function reads the variable in a IF node
1488        Args:
1489            variable (Variable):
1490        Returns:
1491            bool: True if the variable is read
1492        """
1493        variables_reads = [n.variables_read for n in self.nodes if n.contains_if()]
1494        variables_read = [item for sublist in variables_reads for item in sublist]
1495        return variable in variables_read

Check if the function reads the variable in a IF node Args: variable (Variable): Returns: bool: True if the variable is read

def is_reading_in_require_or_assert(self, variable: slither.core.variables.variable.Variable) -> bool:
1497    def is_reading_in_require_or_assert(self, variable: "Variable") -> bool:
1498        """
1499            Check if the function reads the variable in an require or assert
1500        Args:
1501            variable (Variable):
1502        Returns:
1503            bool: True if the variable is read
1504        """
1505        variables_reads = [n.variables_read for n in self.nodes if n.contains_require_or_assert()]
1506        variables_read = [item for sublist in variables_reads for item in sublist]
1507        return variable in variables_read

Check if the function reads the variable in an require or assert Args: variable (Variable): Returns: bool: True if the variable is read

def is_writing(self, variable: slither.core.variables.variable.Variable) -> bool:
1509    def is_writing(self, variable: "Variable") -> bool:
1510        """
1511            Check if the function writes the variable
1512        Args:
1513            variable (Variable):
1514        Returns:
1515            bool: True if the variable is written
1516        """
1517        return variable in self.variables_written

Check if the function writes the variable Args: variable (Variable): Returns: bool: True if the variable is written

@abstractmethod
def get_summary( self) -> tuple[str, str, str, list[str], list[str], list[str], list[str], list[str]]:
1519    @abstractmethod
1520    def get_summary(
1521        self,
1522    ) -> tuple[str, str, str, list[str], list[str], list[str], list[str], list[str]]:
1523        pass
def is_protected(self) -> bool:
1525    def is_protected(self) -> bool:
1526        """
1527            Determine if the function is protected using a check on msg.sender
1528
1529            Consider onlyOwner as a safe modifier.
1530            If the owner functionality is incorrectly implemented, this will lead to incorrectly
1531            classify the function as protected
1532
1533            Otherwise only detects if msg.sender is directly used in a condition
1534            For example, it wont work for:
1535                address a = msg.sender
1536                require(a == owner)
1537        Returns
1538            (bool)
1539        """
1540
1541        if self._is_protected is None:
1542            if self.is_constructor:
1543                self._is_protected = True
1544                return True
1545            if "onlyOwner" in [m.name for m in self.modifiers]:
1546                self._is_protected = True
1547                return True
1548            conditional_vars = self.all_conditional_solidity_variables_read(include_loop=False)
1549            args_vars = self.all_solidity_variables_used_as_args()
1550            self._is_protected = (
1551                SolidityVariableComposed("msg.sender") in conditional_vars + args_vars
1552            )
1553        return self._is_protected

Determine if the function is protected using a check on msg.sender

Consider onlyOwner as a safe modifier.
If the owner functionality is incorrectly implemented, this will lead to incorrectly
classify the function as protected

Otherwise only detects if msg.sender is directly used in a condition
For example, it wont work for:
    address a = msg.sender
    require(a == owner)

Returns (bool)

is_reentrant: bool
1555    @property
1556    def is_reentrant(self) -> bool:
1557        """
1558        Determine if the function can be re-entered
1559        """
1560        reentrancy_modifier = "nonReentrant"
1561
1562        if self.function_language == FunctionLanguage.Vyper:
1563            reentrancy_modifier = "nonreentrant(lock)"
1564
1565        # TODO: compare with hash of known nonReentrant modifier instead of the name
1566        if reentrancy_modifier in [m.name for m in self.modifiers]:
1567            return False
1568
1569        if self.visibility in ["public", "external"]:
1570            return True
1571
1572        # If it's an internal function, check if all its entry points have the nonReentrant modifier
1573        all_entry_points = [
1574            f for f in self.all_reachable_from_functions if f.visibility in ["public", "external"]
1575        ]
1576        if not all_entry_points:
1577            return True
1578        return not all(
1579            reentrancy_modifier in [m.name for m in f.modifiers] for f in all_entry_points
1580        )

Determine if the function can be re-entered

def new_node( self, node_type: slither.core.cfg.node.NodeType, src: str | dict, scope: Union[slither.core.cfg.scope.Scope, Function]) -> slither.core.cfg.node.Node:
1691    def new_node(
1692        self, node_type: "NodeType", src: str | dict, scope: Union[Scope, "Function"]
1693    ) -> "Node":
1694        from slither.core.cfg.node import Node
1695
1696        node = Node(node_type, self._counter_nodes, scope, self.file_scope)
1697        node.set_offset(src, self.compilation_unit)
1698        self._counter_nodes += 1
1699        node.set_function(self)
1700        self._nodes.append(node)
1701
1702        return node
def get_last_ssa_state_variables_instances( self) -> dict[str, set[slither.slithir.variables.variable.SlithIRVariable]]:
1769    def get_last_ssa_state_variables_instances(
1770        self,
1771    ) -> dict[str, set["SlithIRVariable"]]:
1772        return self._get_last_ssa_variable_instances(target_state=True, target_local=False)
def get_last_ssa_local_variables_instances( self) -> dict[str, set[slither.slithir.variables.variable.SlithIRVariable]]:
1774    def get_last_ssa_local_variables_instances(
1775        self,
1776    ) -> dict[str, set["SlithIRVariable"]]:
1777        return self._get_last_ssa_variable_instances(target_state=False, target_local=True)
def fix_phi( self, last_state_variables_instances: dict[str, list[slither.core.variables.state_variable.StateVariable]], initial_state_variables_instances: dict[str, slither.core.variables.state_variable.StateVariable]) -> None:
1820    def fix_phi(
1821        self,
1822        last_state_variables_instances: dict[str, list["StateVariable"]],
1823        initial_state_variables_instances: dict[str, "StateVariable"],
1824    ) -> None:
1825        from slither.slithir.operations import InternalCall, PhiCallback, Phi
1826        from slither.slithir.variables import StateIRVariable, LocalIRVariable
1827
1828        for node in self.nodes:
1829            if node == self.entry_point:
1830                self._fix_phi_entry(
1831                    node, last_state_variables_instances, initial_state_variables_instances
1832                )
1833            for ir in node.irs_ssa:
1834                if isinstance(ir, PhiCallback):
1835                    callee_ir = ir.callee_ir
1836                    if isinstance(callee_ir, InternalCall):
1837                        last_ssa = callee_ir.function.get_last_ssa_state_variables_instances()
1838                        if ir.lvalue.canonical_name in last_ssa:
1839                            ir.rvalues = list(last_ssa[ir.lvalue.canonical_name])
1840                        else:
1841                            ir.rvalues = [ir.lvalue]
1842                    else:
1843                        additional = last_state_variables_instances[ir.lvalue.canonical_name]
1844                        ir.rvalues = list(set(additional + ir.rvalues))
1845
1846                # Propage storage ref information if it does not exist
1847                # This can happen if the refers_to variable was discovered through the phi operator on function parameter
1848                # aka you have storage pointer as function parameter
1849                # instead of having a storage pointer for which the aliases belong to the function body
1850                if (
1851                    isinstance(ir, Phi)
1852                    and isinstance(ir.lvalue, LocalIRVariable)
1853                    and ir.lvalue.is_storage
1854                    and not ir.lvalue.refers_to
1855                ):
1856                    refers_to = []
1857                    for candidate in ir.rvalues:
1858                        if isinstance(candidate, StateIRVariable):
1859                            refers_to.append(candidate)
1860                        if isinstance(candidate, LocalIRVariable) and candidate.is_storage:
1861                            refers_to += candidate.refers_to
1862
1863                    ir.lvalue.refers_to = refers_to
1864
1865            node.irs_ssa = [ir for ir in node.irs_ssa if not self._unchange_phi(ir)]
def generate_slithir_and_analyze(self) -> None:
1867    def generate_slithir_and_analyze(self) -> None:
1868        for node in self.nodes:
1869            node.slithir_generation()
1870
1871        self._analyze_read_write()
1872        self._analyze_calls()
@abstractmethod
def generate_slithir_ssa(self, all_ssa_state_variables_instances):
1874    @abstractmethod
1875    def generate_slithir_ssa(self, all_ssa_state_variables_instances):
1876        pass
def update_read_write_using_ssa(self) -> None:
1878    def update_read_write_using_ssa(self) -> None:
1879        for node in self.nodes:
1880            node.update_read_write_using_ssa()
1881        self._analyze_read_write()