Improve find_variable

Fix dupplicate parsers
Minor code improvements
Prevent the CI to run on solc 0.8
pull/728/head
Josselin 4 years ago
parent 330325a3b9
commit d778ed6a02
  1. 12
      slither/slithir/convert.py
  2. 74
      slither/solc_parsing/declarations/contract.py
  3. 86
      slither/solc_parsing/expressions/expression_parsing.py
  4. 12
      slither/solc_parsing/slitherSolc.py
  5. 16
      slither/solc_parsing/solidity_types/type_parsing.py
  6. 1
      slither/visitors/slithir/expression_to_slithir.py
  7. 11
      tests/test_ast_parsing.py

@ -13,7 +13,6 @@ from slither.core.declarations import (
Structure, Structure,
) )
from slither.core.declarations.function_contract import FunctionContract from slither.core.declarations.function_contract import FunctionContract
from slither.core.declarations.solidity_variables import SolidityImportPlaceHolder
from slither.core.expressions import Identifier, Literal from slither.core.expressions import Identifier, Literal
from slither.core.solidity_types import ( from slither.core.solidity_types import (
ArrayType, ArrayType,
@ -26,10 +25,9 @@ from slither.core.solidity_types import (
from slither.core.solidity_types.elementary_type import Int as ElementaryTypeInt from slither.core.solidity_types.elementary_type import Int as ElementaryTypeInt
from slither.core.solidity_types.type import Type from slither.core.solidity_types.type import Type
from slither.core.variables.function_type_variable import FunctionTypeVariable from slither.core.variables.function_type_variable import FunctionTypeVariable
from slither.core.variables.variable import Variable
from slither.core.variables.state_variable import StateVariable from slither.core.variables.state_variable import StateVariable
from slither.slithir.operations.codesize import CodeSize from slither.core.variables.variable import Variable
from slither.slithir.variables import TupleVariable from slither.slithir.exceptions import SlithIRError
from slither.slithir.operations import ( from slither.slithir.operations import (
Assignment, Assignment,
Balance, Balance,
@ -63,6 +61,7 @@ from slither.slithir.operations import (
Unpack, Unpack,
Nop, Nop,
) )
from slither.slithir.operations.codesize import CodeSize
from slither.slithir.tmp_operations.argument import Argument, ArgumentType from slither.slithir.tmp_operations.argument import Argument, ArgumentType
from slither.slithir.tmp_operations.tmp_call import TmpCall from slither.slithir.tmp_operations.tmp_call import TmpCall
from slither.slithir.tmp_operations.tmp_new_array import TmpNewArray from slither.slithir.tmp_operations.tmp_new_array import TmpNewArray
@ -70,10 +69,10 @@ from slither.slithir.tmp_operations.tmp_new_contract import TmpNewContract
from slither.slithir.tmp_operations.tmp_new_elementary_type import TmpNewElementaryType from slither.slithir.tmp_operations.tmp_new_elementary_type import TmpNewElementaryType
from slither.slithir.tmp_operations.tmp_new_structure import TmpNewStructure from slither.slithir.tmp_operations.tmp_new_structure import TmpNewStructure
from slither.slithir.variables import Constant, ReferenceVariable, TemporaryVariable from slither.slithir.variables import Constant, ReferenceVariable, TemporaryVariable
from slither.visitors.slithir.expression_to_slithir import ExpressionToSlithIR from slither.slithir.variables import TupleVariable
from slither.utils.function import get_function_id from slither.utils.function import get_function_id
from slither.utils.type import export_nested_types_from_variable from slither.utils.type import export_nested_types_from_variable
from slither.slithir.exceptions import SlithIRError from slither.visitors.slithir.expression_to_slithir import ExpressionToSlithIR
if TYPE_CHECKING: if TYPE_CHECKING:
from slither.core.cfg.node import Node from slither.core.cfg.node import Node
@ -433,7 +432,6 @@ def _convert_type_contract(ir, slither):
assignment.lvalue.set_type(ElementaryType("string")) assignment.lvalue.set_type(ElementaryType("string"))
return assignment return assignment
raise SlithIRError(f"type({contract.name}).{ir.variable_right} is unknown") raise SlithIRError(f"type({contract.name}).{ir.variable_right} is unknown")

@ -1,7 +1,7 @@
import logging import logging
from typing import List, Dict, Callable, TYPE_CHECKING, Union from typing import List, Dict, Callable, TYPE_CHECKING, Union, Set
from slither.core.declarations import Modifier, Event, EnumContract, StructureContract from slither.core.declarations import Modifier, Event, EnumContract, StructureContract, Function
from slither.core.declarations.contract import Contract from slither.core.declarations.contract import Contract
from slither.core.declarations.function_contract import FunctionContract from slither.core.declarations.function_contract import FunctionContract
from slither.core.variables.state_variable import StateVariable from slither.core.variables.state_variable import StateVariable
@ -301,7 +301,7 @@ class ContractSolc:
self._modifiers_no_params.append(modif_parser) self._modifiers_no_params.append(modif_parser)
self._modifiers_parser.append(modif_parser) self._modifiers_parser.append(modif_parser)
self._slither_parser.add_functions_parser(modif_parser) self._slither_parser.add_function_or_modifier_parser(modif_parser)
def parse_modifiers(self): def parse_modifiers(self):
for modifier in self._modifiersNotParsed: for modifier in self._modifiersNotParsed:
@ -319,7 +319,7 @@ class ContractSolc:
self._functions_no_params.append(func_parser) self._functions_no_params.append(func_parser)
self._functions_parser.append(func_parser) self._functions_parser.append(func_parser)
self._slither_parser.add_functions_parser(func_parser) self._slither_parser.add_function_or_modifier_parser(func_parser)
def parse_functions(self): def parse_functions(self):
@ -395,6 +395,47 @@ class ContractSolc:
self.log_incorrect_parsing(f"Missing params {e}") self.log_incorrect_parsing(f"Missing params {e}")
self._functions_no_params = [] self._functions_no_params = []
def _analyze_params_element( # pylint: disable=too-many-arguments
self,
Cls: Callable,
Cls_parser: Callable,
element_parser: FunctionSolc,
explored_reference_id: Set[int],
parser: List[FunctionSolc],
all_elements: Dict[str, Function],
):
elem = Cls(self.slither)
elem.set_contract(self._contract)
underlying_function = element_parser.underlying_function
# TopLevel function are not analyzed here
assert isinstance(underlying_function, FunctionContract)
elem.set_contract_declarer(underlying_function.contract_declarer)
elem.set_offset(
element_parser.function_not_parsed["src"], self._contract.slither,
)
elem_parser = Cls_parser(
elem, element_parser.function_not_parsed, self, self.slither_parser
)
if (
element_parser.referenced_declaration
and element_parser.referenced_declaration in explored_reference_id
):
# Already added from other fathers
return
if element_parser.referenced_declaration:
explored_reference_id.add(element_parser.referenced_declaration)
elem_parser.analyze_params()
if isinstance(elem, Modifier):
self._contract.slither.add_modifier(elem)
else:
self._contract.slither.add_function(elem)
self._slither_parser.add_function_or_modifier_parser(elem_parser)
all_elements[elem.canonical_name] = elem
parser.append(elem_parser)
def _analyze_params_elements( # pylint: disable=too-many-arguments,too-many-locals def _analyze_params_elements( # pylint: disable=too-many-arguments,too-many-locals
self, self,
elements_no_params: List[FunctionSolc], elements_no_params: List[FunctionSolc],
@ -417,33 +458,14 @@ class ContractSolc:
""" """
all_elements = {} all_elements = {}
explored_reference_id = set()
try: try:
for father in self._contract.inheritance: for father in self._contract.inheritance:
father_parser = self._slither_parser.underlying_contract_to_parser[father] father_parser = self._slither_parser.underlying_contract_to_parser[father]
for element_parser in getter(father_parser): for element_parser in getter(father_parser):
elem = Cls(self.slither) self._analyze_params_element(
elem.set_contract(self._contract) Cls, Cls_parser, element_parser, explored_reference_id, parser, all_elements
underlying_function = element_parser.underlying_function
# TopLevel function are not analyzed here
assert isinstance(underlying_function, FunctionContract)
elem.set_contract_declarer(underlying_function.contract_declarer)
elem.set_offset(
element_parser.function_not_parsed["src"], self._contract.slither,
)
elem_parser = Cls_parser(
elem, element_parser.function_not_parsed, self, self.slither_parser
) )
elem_parser.analyze_params()
if isinstance(elem, Modifier):
self._contract.slither.add_modifier(elem)
else:
self._contract.slither.add_function(elem)
self._slither_parser.add_functions_parser(elem_parser)
all_elements[elem.canonical_name] = elem
parser.append(elem_parser)
accessible_elements = self._contract.available_elements_from_inheritances( accessible_elements = self._contract.available_elements_from_inheritances(
all_elements, getter_available all_elements, getter_available

@ -1,6 +1,6 @@
import logging import logging
import re import re
from typing import Dict, TYPE_CHECKING, Optional, Union, List from typing import Dict, TYPE_CHECKING, Optional, Union, List, Tuple
from slither.core.declarations import Event, Enum, Structure from slither.core.declarations import Event, Enum, Structure
from slither.core.declarations.contract import Contract from slither.core.declarations.contract import Contract
@ -38,7 +38,6 @@ from slither.core.expressions.super_identifier import SuperIdentifier
from slither.core.expressions.tuple_expression import TupleExpression from slither.core.expressions.tuple_expression import TupleExpression
from slither.core.expressions.type_conversion import TypeConversion from slither.core.expressions.type_conversion import TypeConversion
from slither.core.expressions.unary_operation import UnaryOperation, UnaryOperationType from slither.core.expressions.unary_operation import UnaryOperation, UnaryOperationType
from slither.core.slither_core import SlitherCore
from slither.core.solidity_types import ( from slither.core.solidity_types import (
ArrayType, ArrayType,
ElementaryType, ElementaryType,
@ -46,6 +45,7 @@ from slither.core.solidity_types import (
MappingType, MappingType,
) )
from slither.core.variables.variable import Variable from slither.core.variables.variable import Variable
from slither.exceptions import SlitherError
from slither.solc_parsing.exceptions import ParsingError, VariableNotFound from slither.solc_parsing.exceptions import ParsingError, VariableNotFound
from slither.solc_parsing.solidity_types.type_parsing import UnknownType, parse_type from slither.solc_parsing.solidity_types.type_parsing import UnknownType, parse_type
@ -53,6 +53,8 @@ if TYPE_CHECKING:
from slither.core.expressions.expression import Expression from slither.core.expressions.expression import Expression
from slither.solc_parsing.declarations.function import FunctionSolc from slither.solc_parsing.declarations.function import FunctionSolc
from slither.solc_parsing.declarations.contract import ContractSolc from slither.solc_parsing.declarations.contract import ContractSolc
from slither.core.slither_core import SlitherCore
from slither.solc_parsing.slitherSolc import SlitherSolc
logger = logging.getLogger("ExpressionParsing") logger = logging.getLogger("ExpressionParsing")
@ -95,7 +97,10 @@ def _find_variable_from_ref_declaration(
if contract_candidate.id == referenced_declaration: if contract_candidate.id == referenced_declaration:
return contract_candidate return contract_candidate
for function_candidate in all_functions_parser: for function_candidate in all_functions_parser:
if function_candidate.referenced_declaration == referenced_declaration: if (
function_candidate.referenced_declaration == referenced_declaration
and not function_candidate.underlying_function.is_shadowed
):
return function_candidate.underlying_function return function_candidate.underlying_function
return None return None
@ -217,6 +222,47 @@ def _find_in_contract(
return None return None
def _find_variable_init(
caller_context: CallerContext,
) -> Tuple[List[Contract], Union[List["FunctionSolc"]], "SlitherCore", "SlitherSolc"]:
from slither.solc_parsing.slitherSolc import SlitherSolc
from slither.solc_parsing.declarations.contract import ContractSolc
from slither.solc_parsing.declarations.function import FunctionSolc
direct_contracts: List[Contract]
direct_functions_parser: List[FunctionSolc]
if isinstance(caller_context, SlitherSolc):
direct_contracts = []
direct_functions_parser = []
sl = caller_context.core
sl_parser = caller_context
elif isinstance(caller_context, ContractSolc):
direct_contracts = [caller_context.underlying_contract]
direct_functions_parser = caller_context.functions_parser + caller_context.modifiers_parser
sl = caller_context.slither
sl_parser = caller_context.slither_parser
elif isinstance(caller_context, FunctionSolc):
if caller_context.contract_parser:
direct_contracts = [caller_context.contract_parser.underlying_contract]
direct_functions_parser = (
caller_context.contract_parser.functions_parser
+ caller_context.contract_parser.modifiers_parser
)
else:
# Top level functions
direct_contracts = []
direct_functions_parser = []
sl = caller_context.slither
sl_parser = caller_context.slither_parser
else:
raise SlitherError(
f"{type(caller_context)} ({caller_context} is not valid for find_variable"
)
return direct_contracts, direct_functions_parser, sl, sl_parser
def find_variable( def find_variable(
var_name: str, var_name: str,
caller_context: CallerContext, caller_context: CallerContext,
@ -225,9 +271,8 @@ def find_variable(
) -> Union[ ) -> Union[
Variable, Function, Contract, SolidityVariable, SolidityFunction, Event, Enum, Structure, Variable, Function, Contract, SolidityVariable, SolidityFunction, Event, Enum, Structure,
]: ]:
from slither.solc_parsing.declarations.contract import ContractSolc
from slither.solc_parsing.declarations.function import FunctionSolc from slither.solc_parsing.declarations.function import FunctionSolc
from slither.solc_parsing.slitherSolc import SlitherSolc from slither.solc_parsing.declarations.contract import ContractSolc
# variable are looked from the contract declarer # variable are looked from the contract declarer
# functions can be shadowed, but are looked from the contract instance, rather than the contract declarer # functions can be shadowed, but are looked from the contract instance, rather than the contract declarer
@ -244,14 +289,22 @@ def find_variable(
# for events it's unclear what should be the behavior, as they can be shadowed, but there is not impact # for events it's unclear what should be the behavior, as they can be shadowed, but there is not impact
# structure/enums cannot be shadowed # structure/enums cannot be shadowed
sl: SlitherCore = caller_context.core if isinstance( direct_contracts, direct_functions_parser, sl, sl_parser = _find_variable_init(caller_context)
caller_context, SlitherSolc
) else caller_context.slither
sl_parser: SlitherSolc = caller_context if isinstance(
caller_context, SlitherSolc
) else caller_context.slither_parser
all_contracts = sl.contracts all_contracts = sl.contracts
all_functions_parser = sl_parser.all_functions_parser all_functions_parser = sl_parser.all_functions_and_modifiers_parser
# Look for all references delcaration
# First look only in the context of function/contract
# Then look everywhere
# Because functions are copied between contracts, two functions can have the same ref
# So we need to first look with respect to the direct context
ret = _find_variable_from_ref_declaration(
referenced_declaration, direct_contracts, direct_functions_parser
)
if ret:
return ret
ret = _find_variable_from_ref_declaration( ret = _find_variable_from_ref_declaration(
referenced_declaration, all_contracts, all_functions_parser referenced_declaration, all_contracts, all_functions_parser
@ -266,10 +319,6 @@ def find_variable(
if ret: if ret:
return ret return ret
ret = _find_top_level(var_name, sl)
if ret:
return ret
contract: Optional[Contract] = None contract: Optional[Contract] = None
contract_declarer: Optional[Contract] = None contract_declarer: Optional[Contract] = None
if isinstance(caller_context, ContractSolc): if isinstance(caller_context, ContractSolc):
@ -302,6 +351,11 @@ def find_variable(
if var_name in SOLIDITY_FUNCTIONS: if var_name in SOLIDITY_FUNCTIONS:
return SolidityFunction(var_name) return SolidityFunction(var_name)
# Top level must be at the end, if nothing else was found
ret = _find_top_level(var_name, sl)
if ret:
return ret
raise VariableNotFound("Variable not found: {} (context {})".format(var_name, caller_context)) raise VariableNotFound("Variable not found: {} (context {})".format(var_name, caller_context))

@ -43,7 +43,7 @@ class SlitherSolc:
self._is_compact_ast = False self._is_compact_ast = False
self._core: SlitherCore = core self._core: SlitherCore = core
self._all_functions_parser: List[FunctionSolc] = [] self._all_functions_and_modifier_parser: List[FunctionSolc] = []
self._top_level_contracts_counter = 0 self._top_level_contracts_counter = 0
@ -52,11 +52,11 @@ class SlitherSolc:
return self._core return self._core
@property @property
def all_functions_parser(self) -> List[FunctionSolc]: def all_functions_and_modifiers_parser(self) -> List[FunctionSolc]:
return self._all_functions_parser return self._all_functions_and_modifier_parser
def add_functions_parser(self, f: FunctionSolc): def add_function_or_modifier_parser(self, f: FunctionSolc):
self._all_functions_parser.append(f) self._all_functions_and_modifier_parser.append(f)
@property @property
def underlying_contract_to_parser(self) -> Dict[Contract, ContractSolc]: def underlying_contract_to_parser(self) -> Dict[Contract, ContractSolc]:
@ -217,7 +217,7 @@ class SlitherSolc:
self._core.functions_top_level.append(func) self._core.functions_top_level.append(func)
self._functions_top_level_parser.append(func_parser) self._functions_top_level_parser.append(func_parser)
self.add_functions_parser(func_parser) self.add_function_or_modifier_parser(func_parser)
else: else:
raise SlitherException(f"Top level {top_level_data[self.get_key()]} not supported") raise SlitherException(f"Top level {top_level_data[self.get_key()]} not supported")

@ -1,23 +1,19 @@
import logging import logging
import re import re
from typing import List, TYPE_CHECKING, Union, Dict, Optional from typing import List, TYPE_CHECKING, Union, Dict
from slither.core.declarations.function_contract import FunctionContract from slither.core.declarations.function_contract import FunctionContract
from slither.core.expressions.literal import Literal
from slither.core.solidity_types.array_type import ArrayType
from slither.core.solidity_types.elementary_type import ( from slither.core.solidity_types.elementary_type import (
ElementaryType, ElementaryType,
ElementaryTypeName, ElementaryTypeName,
) )
from slither.core.solidity_types.function_type import FunctionType
from slither.core.solidity_types.mapping_type import MappingType
from slither.core.solidity_types.type import Type from slither.core.solidity_types.type import Type
from slither.core.solidity_types.user_defined_type import UserDefinedType from slither.core.solidity_types.user_defined_type import UserDefinedType
from slither.core.solidity_types.array_type import ArrayType
from slither.core.solidity_types.mapping_type import MappingType
from slither.core.solidity_types.function_type import FunctionType
from slither.core.variables.function_type_variable import FunctionTypeVariable from slither.core.variables.function_type_variable import FunctionTypeVariable
from slither.core.expressions.literal import Literal
from slither.solc_parsing.exceptions import ParsingError from slither.solc_parsing.exceptions import ParsingError
if TYPE_CHECKING: if TYPE_CHECKING:
@ -241,7 +237,7 @@ def parse_type(t: Union[Dict, UnknownType], caller_context):
all_enums = [item for sublist in all_enumss for item in sublist] all_enums = [item for sublist in all_enumss for item in sublist]
all_enums += contract.slither.enums_top_level all_enums += contract.slither.enums_top_level
contracts = contract.slither.contracts contracts = contract.slither.contracts
functions = contract.functions functions = contract.functions + contract.modifiers
else: else:
raise ParsingError(f"Incorrect caller context: {type(caller_context)}") raise ParsingError(f"Incorrect caller context: {type(caller_context)}")

@ -6,7 +6,6 @@ from slither.core.declarations import (
SolidityVariableComposed, SolidityVariableComposed,
SolidityFunction, SolidityFunction,
) )
from slither.core.declarations.solidity_variables import SolidityImportPlaceHolder
from slither.core.expressions import ( from slither.core.expressions import (
AssignmentOperationType, AssignmentOperationType,
UnaryOperationType, UnaryOperationType,

@ -404,6 +404,8 @@ XFAIL = [
] ]
TESTED_SOLC_07 = ["0.7.0", "0.7.1", "0.7.2", "0.7.3", "0.7.4", "0.7.5"] TESTED_SOLC_07 = ["0.7.0", "0.7.1", "0.7.2", "0.7.3", "0.7.4", "0.7.5"]
def get_solc_versions() -> List[str]: def get_solc_versions() -> List[str]:
""" """
get a list of all the supported versions of solidity, sorted from earliest to latest get a list of all the supported versions of solidity, sorted from earliest to latest
@ -415,7 +417,14 @@ def get_solc_versions() -> List[str]:
# there's an extra newline so just remove all empty strings # there's an extra newline so just remove all empty strings
solc_versions = [version for version in solc_versions if version != ""] solc_versions = [version for version in solc_versions if version != ""]
solc_versions = [version for version in solc_versions if (not version.startswith('0.7.')) or (version in TESTED_SOLC_07)] # Dont test for newer 0.7 versions until explicity updated
# Dont test for 0.8 yet
solc_versions = [
version
for version in solc_versions
if ((not version.startswith("0.7.") or version.startswith("0.8.")))
or (version in TESTED_SOLC_07)
]
solc_versions.reverse() solc_versions.reverse()
return solc_versions return solc_versions

Loading…
Cancel
Save