address lints and improve typing

pull/2099/head
alpharush 1 year ago
parent e8fa8b85fa
commit 961db4563f
  1. 5
      slither/visitors/slithir/expression_to_slithir.py
  2. 46
      slither/vyper_parsing/ast/ast.py
  3. 3
      slither/vyper_parsing/declarations/contract.py
  4. 2
      slither/vyper_parsing/declarations/event.py
  5. 39
      slither/vyper_parsing/declarations/function.py
  6. 2
      slither/vyper_parsing/declarations/struct.py
  7. 161
      slither/vyper_parsing/expressions/expression_parsing.py
  8. 4
      slither/vyper_parsing/expressions/find_variable.py
  9. 13
      slither/vyper_parsing/type_parsing.py

@ -171,9 +171,8 @@ class ExpressionToSlithIR(ExpressionVisitor):
def result(self) -> List[Operation]:
return self._result
def _post_assignement_operation(
self, expression: AssignmentOperation
) -> None: # pylint: disable=too-many-branches,too-many-statements
# pylint: disable=too-many-branches,too-many-statements
def _post_assignement_operation(self, expression: AssignmentOperation) -> None:
left = get(expression.expression_left)
right = get(expression.expression_right)
operation: Operation

@ -1,6 +1,48 @@
from typing import Dict, Callable, List
from slither.vyper_parsing.ast.types import ASTNode
from slither.vyper_parsing.ast.types import *
from slither.vyper_parsing.ast.types import (
ASTNode,
Module,
ImportFrom,
EventDef,
AnnAssign,
Name,
Call,
StructDef,
VariableDecl,
Subscript,
Index,
Hex,
Int,
Str,
Tuple,
FunctionDef,
Assign,
Raise,
Attribute,
Assert,
Keyword,
Arguments,
Arg,
UnaryOp,
BinOp,
Expr,
Log,
Return,
VyDict,
VyList,
NameConstant,
If,
Compare,
For,
Break,
Continue,
Pass,
InterfaceDef,
EnumDef,
Bytes,
AugAssign,
BoolOp,
)
class ParsingError(Exception):

@ -32,7 +32,7 @@ if TYPE_CHECKING:
from slither.vyper_parsing.vyper_compilation_unit import VyperCompilationUnit
class ContractVyper:
class ContractVyper: # pylint: disable=too-many-instance-attributes
def __init__(
self, slither_parser: "VyperCompilationUnit", contract: Contract, module: Module
) -> None:
@ -426,6 +426,7 @@ class ContractVyper:
contract_parser = ContractVyper(self._slither_parser, contract, node)
self._contract.file_scope.contracts[contract.name] = contract
# pylint: disable=protected-access
self._slither_parser._underlying_contract_to_parser[contract] = contract_parser
elif isinstance(node, AnnAssign): # implements: ERC20

@ -11,7 +11,7 @@ from slither.vyper_parsing.ast.types import AnnAssign, Pass
from slither.vyper_parsing.ast.types import EventDef
class EventVyper:
class EventVyper: # pylint: disable=too-few-public-methods
"""
Event class
"""

@ -1,4 +1,4 @@
from typing import Dict, Union, List, TYPE_CHECKING, Tuple
from typing import Dict, Union, List, TYPE_CHECKING
from slither.core.cfg.node import NodeType, link_nodes, Node
from slither.core.cfg.scope import Scope
@ -13,7 +13,33 @@ from slither.core.variables.local_variable import LocalVariable
from slither.vyper_parsing.cfg.node import NodeVyper
from slither.solc_parsing.exceptions import ParsingError
from slither.vyper_parsing.variables.local_variable import LocalVariableVyper
from slither.vyper_parsing.ast.types import *
from slither.vyper_parsing.ast.types import (
Int,
Call,
Attribute,
Name,
Tuple as TupleVyper,
ASTNode,
AnnAssign,
FunctionDef,
Return,
Assert,
Compare,
Log,
Subscript,
If,
Pass,
Assign,
AugAssign,
Raise,
Expr,
For,
Index,
Arg,
Arguments,
Continue,
Break,
)
if TYPE_CHECKING:
from slither.core.compilation_unit import SlitherCompilationUnit
@ -24,7 +50,7 @@ def link_underlying_nodes(node1: NodeVyper, node2: NodeVyper):
link_nodes(node1.underlying_node, node2.underlying_node)
class FunctionVyper:
class FunctionVyper: # pylint: disable=too-many-instance-attributes
def __init__(
self,
function: Function,
@ -183,7 +209,7 @@ class FunctionVyper:
contract = self._contract_parser.underlying_contract
compilation_unit = self._contract_parser.underlying_contract.compilation_unit
modifier = Modifier(compilation_unit)
modifier._name = name
modifier.name = name
modifier.set_offset(decorator.src, compilation_unit)
modifier.set_contract(contract)
modifier.set_contract_declarer(contract)
@ -218,6 +244,7 @@ class FunctionVyper:
###################################################################################
###################################################################################
# pylint: disable=too-many-branches,too-many-statements,protected-access,too-many-locals
def _parse_cfg(self, cfg: List[ASTNode]) -> None:
entry_node = self._new_node(NodeType.ENTRYPOINT, "-1:-1:-1", self.underlying_function)
@ -517,7 +544,7 @@ class FunctionVyper:
local_var = self._add_param(param)
self._function.add_parameters(local_var.underlying_variable)
def _parse_returns(self, returns: Union[Name, Tuple, Subscript]):
def _parse_returns(self, returns: Union[Name, TupleVyper, Subscript]):
self._function.returns_src().set_offset(returns.src, self._function.compilation_unit)
# Only the type of the arg is given, not a name. We create an an `Arg` with an empty name
@ -527,7 +554,7 @@ class FunctionVyper:
local_var = self._add_param(Arg(returns.src, returns.node_id, "", annotation=returns))
self._function.add_return(local_var.underlying_variable)
else:
assert isinstance(returns, Tuple)
assert isinstance(returns, TupleVyper)
for ret in returns.elements:
local_var = self._add_param(Arg(ret.src, ret.node_id, "", annotation=ret))
self._function.add_return(local_var.underlying_variable)

@ -6,7 +6,7 @@ from slither.vyper_parsing.variables.structure_variable import StructureVariable
from slither.vyper_parsing.ast.types import StructDef, AnnAssign
class StructVyper:
class StructVyper: # pylint: disable=too-few-public-methods
def __init__(
self,
st: Structure,

@ -1,10 +1,10 @@
from typing import Dict, TYPE_CHECKING
from typing import Optional, List, Union, TYPE_CHECKING
from collections import deque
from slither.core.declarations.solidity_variables import (
SOLIDITY_VARIABLES_COMPOSED,
SolidityVariableComposed,
)
from slither.core.declarations import SolidityFunction, Function
from slither.core.declarations import SolidityFunction, FunctionContract
from slither.core.variables.state_variable import StateVariable
from slither.core.expressions import (
CallExpression,
@ -57,14 +57,25 @@ from slither.vyper_parsing.ast.types import (
AugAssign,
VyList,
Raise,
ASTNode,
)
if TYPE_CHECKING:
from slither.core.expressions.expression import Expression
def parse_expression(expression: Dict, caller_context) -> "Expression": # pylint
print("parse_expression", expression)
def vars_to_typestr(rets: Optional[List["Expression"]]) -> str:
if rets is None:
return "tuple()"
if len(rets) == 1:
return str(rets[0].type)
return f"tuple({','.join(str(ret.type) for ret in rets)})"
# pylint: disable=too-many-branches,too-many-statements,too-many-locals
def parse_expression(
expression: ASTNode, caller_context: Union[FunctionContract, Contract]
) -> "Expression":
if isinstance(expression, Int):
literal = Literal(str(expression.value), ElementaryType("uint256"))
@ -103,14 +114,14 @@ def parse_expression(expression: Dict, caller_context) -> "Expression": # pylin
parsed_expr.set_offset(expression.src, caller_context.compilation_unit)
return parsed_expr
elif called.value.name == "convert()":
if called.value.name == "convert()":
arg = parse_expression(expression.args[0], caller_context)
type_to = parse_type(expression.args[1], caller_context)
parsed_expr = TypeConversion(arg, type_to)
parsed_expr.set_offset(expression.src, caller_context.compilation_unit)
return parsed_expr
elif called.value.name == "min_value()":
if called.value.name == "min_value()":
type_to = parse_type(expression.args[0], caller_context)
member_type = str(type_to)
# TODO return Literal
@ -126,7 +137,7 @@ def parse_expression(expression: Dict, caller_context) -> "Expression": # pylin
parsed_expr.set_offset(expression.src, caller_context.compilation_unit)
return parsed_expr
elif called.value.name == "max_value()":
if called.value.name == "max_value()":
type_to = parse_type(expression.args[0], caller_context)
member_type = str(type_to)
# TODO return Literal
@ -142,7 +153,7 @@ def parse_expression(expression: Dict, caller_context) -> "Expression": # pylin
parsed_expr.set_offset(expression.src, caller_context.compilation_unit)
return parsed_expr
elif called.value.name == "raw_call()":
if called.value.name == "raw_call()":
args = [parse_expression(a, caller_context) for a in expression.args]
# This is treated specially in order to force `extract_tmp_call` to treat this as a `HighLevelCall` which will be converted
# to a `LowLevelCall` by `convert_to_low_level`. This is an artifact of the late conversion of Solidity...
@ -182,9 +193,10 @@ def parse_expression(expression: Dict, caller_context) -> "Expression": # pylin
rets = None
# Since the AST lacks the type of the return values, we recover it.
if isinstance(called, Identifier):
if isinstance(called.value, Function):
if isinstance(called.value, FunctionContract):
rets = called.value.returns
# Default arguments are not represented in the AST, so we recover them as well.
# pylint: disable=protected-access
if called.value._default_args_as_expressions and len(arguments) < len(
called.value.parameters
):
@ -209,28 +221,9 @@ def parse_expression(expression: Dict, caller_context) -> "Expression": # pylin
elif isinstance(called, MemberAccess) and called.type is not None:
# (recover_type_2) Propagate the type collected to the `CallExpression`
# see recover_type_1
rets = [called.type]
if rets is None:
rets = ["tuple()"]
def get_type_str(x):
if isinstance(x, str):
return x
return str(x.type)
# def vars_to_typestr(rets: List[Expression]) -> str:
# if len(rets) == 0:
# return ""
# if len(rets) == 1:
# return str(rets[0].type)
# return f"tuple({','.join(str(ret.type) for ret in rets)})"
type_str = (
get_type_str(rets[0])
if len(rets) == 1
else f"tuple({','.join(map(get_type_str, rets))})"
)
rets = [called]
type_str = vars_to_typestr(rets)
parsed_expr = CallExpression(called, arguments, type_str)
parsed_expr.set_offset(expression.src, caller_context.compilation_unit)
return parsed_expr
@ -266,41 +259,28 @@ def parse_expression(expression: Dict, caller_context) -> "Expression": # pylin
# (recover_type_1) This may be a call to an interface and we don't have the return types,
# so we see if there's a function identifier with `member_name` and propagate the type to
# its enclosing `CallExpression`
if isinstance(expr, Identifier) and isinstance(expr.value, StateVariable) and isinstance(expr.value.type, UserDefinedType) and isinstance(expr.value.type.type, Contract):
if (
isinstance(expr, Identifier)
and isinstance(expr.value, StateVariable)
and isinstance(expr.value.type, UserDefinedType)
and isinstance(expr.value.type.type, Contract)
):
# If we access a member of an interface, needs to be interface instead of self namespace
var = find_variable(member_name, expr.value.type.type)
if isinstance(var, Function):
if isinstance(var, FunctionContract):
rets = var.returns
member_name_ret_type = vars_to_typestr(rets)
def get_type_str(x):
if isinstance(x, str):
return x
return str(x.type)
type_str = (
get_type_str(rets[0])
if len(rets) == 1
else f"tuple({','.join(map(get_type_str, rets))})"
)
member_name_ret_type = type_str
if isinstance(expr, TypeConversion) and isinstance(expr.type, UserDefinedType) and isinstance(expr.type.type, Contract):
if (
isinstance(expr, TypeConversion)
and isinstance(expr.type, UserDefinedType)
and isinstance(expr.type.type, Contract)
):
# If we access a member of an interface, needs to be interface instead of self namespace
var = find_variable(member_name, expr.type.type)
if isinstance(var, Function):
if isinstance(var, FunctionContract):
rets = var.returns
def get_type_str(x):
if isinstance(x, str):
return x
return str(x.type)
type_str = (
get_type_str(rets[0])
if len(rets) == 1
else f"tuple({','.join(map(get_type_str, rets))})"
)
member_name_ret_type = type_str
member_name_ret_type = vars_to_typestr(rets)
member_access = MemberAccess(member_name, member_name_ret_type, expr)
@ -359,7 +339,9 @@ def parse_expression(expression: Dict, caller_context) -> "Expression": # pylin
is_tuple = isinstance(rhs, TupleExpression)
is_array = isinstance(rhs, Identifier) and isinstance(rhs.value.type, ArrayType)
if is_array:
assert rhs.value.type.is_fixed_array
assert (
rhs.value.type.is_fixed_array
), "Dynamic arrays are not supported in comparison operators"
if is_tuple or is_array:
length = len(rhs.expressions) if is_tuple else rhs.value.type.length_value.value
inner_op = (
@ -391,37 +373,36 @@ def parse_expression(expression: Dict, caller_context) -> "Expression": # pylin
return conditions.pop()
else: # enum type membership check https://docs.vyperlang.org/en/stable/types.html?h#id18
is_member_op = (
BinaryOperationType.get_type("==")
if expression.op == "NotIn"
else BinaryOperationType.get_type("!=")
)
# If all bits are cleared, then the lhs is not a member of the enum
# This allows representing membership in multiple enum members
# For example, if enum Foo has members A (1), B (2), and C (4), then
# (x in [Foo.A, Foo.B]) is equivalent to (x & (Foo.A | Foo.B) != 0),
# where (Foo.A | Foo.B) evaluates to 3.
# Thus, when x is 3, (x & (Foo.A | Foo.B) != 0) is true.
enum_bit_mask = BinaryOperation(
TypeConversion(lhs, ElementaryType("uint256")),
TypeConversion(rhs, ElementaryType("uint256")),
BinaryOperationType.get_type("&"),
)
membership_check = BinaryOperation(
enum_bit_mask, Literal("0", ElementaryType("uint256")), is_member_op
)
membership_check.set_offset(lhs.source_mapping, caller_context.compilation_unit)
return membership_check
else: # a regular logical operator
rhs = parse_expression(expression.right, caller_context)
op = BinaryOperationType.get_type(expression.op)
# enum type membership check https://docs.vyperlang.org/en/stable/types.html?h#id18
is_member_op = (
BinaryOperationType.get_type("==")
if expression.op == "NotIn"
else BinaryOperationType.get_type("!=")
)
# If all bits are cleared, then the lhs is not a member of the enum
# This allows representing membership in multiple enum members
# For example, if enum Foo has members A (1), B (2), and C (4), then
# (x in [Foo.A, Foo.B]) is equivalent to (x & (Foo.A | Foo.B) != 0),
# where (Foo.A | Foo.B) evaluates to 3.
# Thus, when x is 3, (x & (Foo.A | Foo.B) != 0) is true.
enum_bit_mask = BinaryOperation(
TypeConversion(lhs, ElementaryType("uint256")),
TypeConversion(rhs, ElementaryType("uint256")),
BinaryOperationType.get_type("&"),
)
membership_check = BinaryOperation(
enum_bit_mask, Literal("0", ElementaryType("uint256")), is_member_op
)
membership_check.set_offset(lhs.source_mapping, caller_context.compilation_unit)
return membership_check
# a regular logical operator
rhs = parse_expression(expression.right, caller_context)
op = BinaryOperationType.get_type(expression.op)
parsed_expr = BinaryOperation(lhs, rhs, op)
parsed_expr.set_offset(expression.src, caller_context.compilation_unit)
return parsed_expr
parsed_expr = BinaryOperation(lhs, rhs, op)
parsed_expr.set_offset(expression.src, caller_context.compilation_unit)
return parsed_expr
if isinstance(expression, BinOp):
lhs = parse_expression(expression.left, caller_context)

@ -98,10 +98,10 @@ def find_variable(
:return:
:rtype:
"""
# pylint: disable=import-outside-toplevel
from slither.vyper_parsing.declarations.function import (
FunctionVyper,
) # pylint: disable=import-outside-toplevel
)
if isinstance(caller_context, Contract):
current_scope = caller_context.file_scope

@ -1,20 +1,19 @@
from typing import Union
from slither.core.solidity_types.elementary_type import (
ElementaryType,
ElementaryTypeName,
) # TODO rename solidity type
from slither.core.solidity_types.array_type import ArrayType
from slither.core.solidity_types.mapping_type import MappingType
from slither.vyper_parsing.ast.types import Name, Subscript, Call, Index, Tuple
from typing import Union
from slither.core.solidity_types.user_defined_type import UserDefinedType
from slither.core.declarations import FunctionContract, Contract
from slither.core.declarations.function_contract import FunctionContract
# pylint: disable=too-many-branches,too-many-return-statements,import-outside-toplevel,too-many-locals
def parse_type(
annotation: Union[Name, Subscript, Call, Tuple], caller_context
): # pylint disable=too-many-branches,too-many-return-statements,import-outside-toplevel
annotation: Union[Name, Subscript, Call, Tuple],
caller_context: Union[FunctionContract, Contract],
):
from slither.vyper_parsing.expressions.expression_parsing import parse_expression
if isinstance(caller_context, FunctionContract):

Loading…
Cancel
Save