Skip to content

Commit

Permalink
link issues for TODO comments, lint
Browse files Browse the repository at this point in the history
  • Loading branch information
0xalpharush committed Sep 5, 2023
1 parent 961db45 commit 404914c
Show file tree
Hide file tree
Showing 5 changed files with 37 additions and 36 deletions.
2 changes: 1 addition & 1 deletion slither/vyper_parsing/declarations/function.py
Original file line number Diff line number Diff line change
Expand Up @@ -282,7 +282,7 @@ def parse_statement(
curr_node = new_node

elif isinstance(expr, Expr):
# TODO This is a workaround to handle Vyper putting payable/view in the function body...
# TODO This is a workaround to handle Vyper putting payable/view in the function body... https://github.com/vyperlang/vyper/issues/3578
if not isinstance(expr.value, Name):
new_node = self._new_node(NodeType.EXPRESSION, expr.src, scope)
new_node.add_unparsed_expression(expr.value)
Expand Down
11 changes: 6 additions & 5 deletions slither/vyper_parsing/expressions/expression_parsing.py
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ def parse_expression(
return literal

if isinstance(expression, Hex):
# TODO this is an implicit conversion and could potentially be bytes20 or other?
# TODO this is an implicit conversion and could potentially be bytes20 or other? https://github.com/vyperlang/vyper/issues/3580
literal = Literal(str(expression.value), ElementaryType("address"))
literal.set_offset(expression.src, caller_context.compilation_unit)
return literal
Expand Down Expand Up @@ -191,7 +191,7 @@ def parse_expression(
arguments = [parse_expression(a, caller_context) for a in expression.args]

rets = None
# Since the AST lacks the type of the return values, we recover it.
# Since the AST lacks the type of the return values, we recover it. https://github.com/vyperlang/vyper/issues/3581
if isinstance(called, Identifier):
if isinstance(called.value, FunctionContract):
rets = called.value.returns
Expand All @@ -212,7 +212,7 @@ def parse_expression(

elif isinstance(called.value, Contract):
# Type conversions are not explicitly represented in the AST e.g. converting address to contract/ interface,
# so we infer that a type conversion is occurring if `called` is a `Contract` type.
# so we infer that a type conversion is occurring if `called` is a `Contract` type. https://github.com/vyperlang/vyper/issues/3580
type_to = parse_type(expression.func, caller_context)
parsed_expr = TypeConversion(arguments[0], type_to)
parsed_expr.set_offset(expression.src, caller_context.compilation_unit)
Expand All @@ -231,7 +231,7 @@ def parse_expression(
if isinstance(expression, Attribute):
member_name = expression.attr
if isinstance(expression.value, Name):
# TODO this is ambiguous because it could be a state variable or a call to balance
# TODO this is ambiguous because it could be a state variable or a call to balance https://github.com/vyperlang/vyper/issues/3582
if expression.value.id == "self" and member_name != "balance":
var = find_variable(member_name, caller_context, is_self=True)
parsed_expr = SelfIdentifier(var)
Expand All @@ -241,6 +241,7 @@ def parse_expression(

expr = parse_expression(expression.value, caller_context)
# TODO this is ambiguous because it could be a type conversion of an interface or a member access
# see https://github.com/vyperlang/vyper/issues/3580 and ttps://github.com/vyperlang/vyper/issues/3582
if expression.attr == "address":
parsed_expr = TypeConversion(expr, ElementaryType("address"))
parsed_expr.set_offset(expression.src, caller_context.compilation_unit)
Expand All @@ -258,7 +259,7 @@ def parse_expression(
member_name_ret_type = None
# (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`
# its enclosing `CallExpression`. https://github.com/vyperlang/vyper/issues/3581
if (
isinstance(expr, Identifier)
and isinstance(expr.value, StateVariable)
Expand Down
46 changes: 22 additions & 24 deletions slither/vyper_parsing/type_parsing.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,10 @@
) # 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 slither.core.solidity_types.user_defined_type import UserDefinedType
from slither.core.declarations import FunctionContract, Contract
from slither.vyper_parsing.ast.types import Name, Subscript, Call, Index, Tuple
from slither.solc_parsing.exceptions import ParsingError

# pylint: disable=too-many-branches,too-many-return-statements,import-outside-toplevel,too-many-locals
def parse_type(
Expand All @@ -25,9 +26,24 @@ def parse_type(

if isinstance(annotation, Name):
name = annotation.id
lname = name.lower() # map `String` to string
if lname in ElementaryTypeName:
return ElementaryType(lname)

if name in contract.structures_as_dict:
return UserDefinedType(contract.structures_as_dict[name])

if name in contract.enums_as_dict:
return UserDefinedType(contract.enums_as_dict[name])

if name in contract.file_scope.contracts:
return UserDefinedType(contract.file_scope.contracts[name])

if name in contract.file_scope.structures:
return UserDefinedType(contract.file_scope.structures[name])
elif isinstance(annotation, Subscript):
assert isinstance(annotation.slice, Index)
# This is also a strange construct...
# This is also a strange construct... https://github.com/vyperlang/vyper/issues/3577
if isinstance(annotation.slice.value, Tuple):
assert isinstance(annotation.value, Name)
if annotation.value.id == "DynArray":
Expand All @@ -44,17 +60,17 @@ def parse_type(
type_ = parse_type(annotation.value, caller_context)

elif isinstance(annotation.value, Name):
# TODO it is weird that the ast_type is `Index` when it's a type annotation and not an expression, so we grab the value.
# Subscript(src='13:10:0', node_id=7, value=Name(src='13:6:0', node_id=8, id='String'), slice=Index(src='13:10:0', node_id=12, value=Int(src='20:2:0', node_id=10, value=64)))
# TODO it is weird that the ast_type is `Index` when it's a type annotation and not an expression, so we grab the value. https://github.com/vyperlang/vyper/issues/3577
type_ = parse_type(annotation.value, caller_context)
if annotation.value.id == "String":
# This is an elementary type
return type_

length = parse_expression(annotation.slice.value, caller_context)
return ArrayType(type_, length)

elif isinstance(annotation, Call):
# TODO event variable represented as Call
# TODO event variable represented as Call https://github.com/vyperlang/vyper/issues/3579
return parse_type(annotation.args[0], caller_context)

elif isinstance(annotation, Tuple):
Expand All @@ -80,22 +96,4 @@ def parse_type(

return UserDefinedType(st)

else:
assert False

lname = name.lower() # TODO map String to string
if lname in ElementaryTypeName:
return ElementaryType(lname)

if name in contract.structures_as_dict:
return UserDefinedType(contract.structures_as_dict[name])

if name in contract.enums_as_dict:
return UserDefinedType(contract.enums_as_dict[name])

if name in contract.file_scope.contracts:
return UserDefinedType(contract.file_scope.contracts[name])

if name in contract.file_scope.structures:
return UserDefinedType(contract.file_scope.structures[name])
assert False
raise ParsingError(f"Type name not found {name} context {caller_context}")
9 changes: 3 additions & 6 deletions slither/vyper_parsing/variables/local_variable.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@


class LocalVariableVyper:
def __init__(self, variable: LocalVariable, variable_data: Union[Arg, Name]) -> None:
def __init__(self, variable: LocalVariable, variable_data: Union[Arg, AnnAssign, Name]) -> None:
self._variable: LocalVariable = variable

if isinstance(variable_data, Arg):
Expand All @@ -15,12 +15,9 @@ def __init__(self, variable: LocalVariable, variable_data: Union[Arg, Name]) ->
elif isinstance(variable_data, AnnAssign):
self._variable.name = variable_data.target.id
self._elem_to_parse = variable_data.annotation
elif isinstance(variable_data, Name):
self._variable.name = variable_data.id
self._elem_to_parse = variable_data
else:
# param Subscript
self._variable.name = ""
assert isinstance(variable_data, Name)
self._variable.name = variable_data.id
self._elem_to_parse = variable_data

assert isinstance(self._elem_to_parse, (Name, Subscript, Call, Tuple))
Expand Down
5 changes: 5 additions & 0 deletions tests/unit/core/test_function_declaration.py
Original file line number Diff line number Diff line change
Expand Up @@ -305,6 +305,7 @@ def test_public_variable(solc_binary_path) -> None:
assert var.type == ElementaryType("bytes32")


# pylint: disable=too-many-statements
def test_vyper_functions(slither_from_vyper_source) -> None:
with slither_from_vyper_source(
"""
Expand Down Expand Up @@ -352,6 +353,7 @@ def __default__():
assert not f.view
assert not f.pure
assert not f.is_implemented
assert f.is_empty

f = functions["__default__()"]
assert f.function_type == FunctionType.FALLBACK
Expand All @@ -360,6 +362,7 @@ def __default__():
assert not f.view
assert not f.pure
assert not f.is_implemented
assert f.is_empty

f = functions["withdraw()"]
assert f.function_type == FunctionType.NORMAL
Expand All @@ -370,10 +373,12 @@ def __default__():
assert f.can_send_eth()
assert f.can_reenter()
assert f.is_implemented
assert not f.is_empty

f = functions["withdraw_locked()"]
assert not f.is_reentrant
assert f.is_implemented
assert not f.is_empty

var = contract.get_state_variable_from_name("balances")
assert var
Expand Down

0 comments on commit 404914c

Please sign in to comment.