fix name resolution for shadowed state variable

pull/2099/head
alpharush 1 year ago
parent 08af1ee9e5
commit f9633ca67a
  1. 1
      slither/core/expressions/__init__.py
  2. 6
      slither/core/expressions/self_identifier.py
  3. 17
      slither/vyper_parsing/expressions/expression_parsing.py
  4. 13
      slither/vyper_parsing/expressions/find_variable.py
  5. 2
      tests/e2e/vyper_parsing/snapshots/ast_parsing__vyper_cfgir_for_for_loop__0.txt
  6. 2
      tests/e2e/vyper_parsing/snapshots/ast_parsing__vyper_cfgir_precedence_foo__0.txt
  7. 2
      tests/e2e/vyper_parsing/snapshots/ast_parsing__vyper_cfgir_tuple_bar__0.txt
  8. 4
      tests/unit/core/test_data/src_mapping/SelfIdentifier.vy
  9. 13
      tests/unit/core/test_source_mapping.py

@ -12,6 +12,7 @@ from .new_contract import NewContract
from .new_elementary_type import NewElementaryType
from .super_call_expression import SuperCallExpression
from .super_identifier import SuperIdentifier
from .self_identifier import SelfIdentifier
from .tuple_expression import TupleExpression
from .type_conversion import TypeConversion
from .unary_operation import UnaryOperation, UnaryOperationType

@ -0,0 +1,6 @@
from slither.core.expressions.identifier import Identifier
class SelfIdentifier(Identifier):
def __str__(self):
return "self." + str(self._value)

@ -20,7 +20,7 @@ from slither.core.expressions import (
NewContract,
NewElementaryType,
SuperCallExpression,
SuperIdentifier,
SelfIdentifier,
TupleExpression,
TypeConversion,
UnaryOperation,
@ -42,7 +42,7 @@ from slither.core.solidity_types import (
from slither.core.declarations.contract import Contract
from slither.vyper_parsing.expressions.find_variable import find_variable
from slither.vyper_parsing.type_parsing import parse_type
from slither.all_exceptions import ParsingError
if TYPE_CHECKING:
from slither.core.expressions.expression import Expression
@ -237,14 +237,14 @@ def parse_expression(expression: Dict, caller_context) -> "Expression":
if isinstance(expression, Attribute):
member_name = expression.attr
if isinstance(expression.value, Name):
print(expression)
# TODO this is ambiguous because it could be a state variable or a call to balance
if expression.value.id == "self" and member_name != "balance":
var, was_created = find_variable(member_name, caller_context)
# TODO replace with self
var, was_created = find_variable(member_name, caller_context, is_self=True)
if was_created:
var.set_offset(expression.src, caller_context.compilation_unit)
parsed_expr = SuperIdentifier(var)
parsed_expr = SelfIdentifier(var)
parsed_expr.set_offset(expression.src, caller_context.compilation_unit)
var.references.append(parsed_expr.source_mapping)
return parsed_expr
expr = parse_expression(expression.value, caller_context)
@ -267,12 +267,11 @@ def parse_expression(expression: Dict, caller_context) -> "Expression":
# (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`
# TODO this is using the wrong caller_context and needs to be interface instead of self namespace
print(expr)
print(expr.__class__.__name__)
if isinstance(expr, TypeConversion) and isinstance(expr.type, UserDefinedType):
# try:
# If we access a member of an interface, needs to be interface instead of self namespace
var, was_created = find_variable(member_name, expr.type.type)
if isinstance(var, Function):
rets = var.returns
@ -288,8 +287,6 @@ def parse_expression(expression: Dict, caller_context) -> "Expression":
else f"tuple({','.join(map(get_type_str, rets))})"
)
member_name_ret_type = type_str
# except:
# pass
member_access = MemberAccess(member_name, member_name_ret_type, expr)

@ -28,7 +28,6 @@ if TYPE_CHECKING:
from slither.vyper_parsing.declarations.function import FunctionVyper
def _find_variable_in_function_parser(
var_name: str,
function_parser: Optional["FunctionVyper"],
@ -86,6 +85,7 @@ def _find_in_contract(
def find_variable(
var_name: str,
caller_context,
is_self: bool = False,
) -> Tuple[
Union[
Variable,
@ -96,8 +96,6 @@ def find_variable(
Event,
Enum,
Structure,
CustomError,
TypeAlias,
],
bool,
]:
@ -136,9 +134,12 @@ def find_variable(
caller_context if isinstance(caller_context, FunctionContract) else None
)
# print("function_parser", function_parser)
ret1 = _find_variable_in_function_parser(var_name, function_parser)
if ret1:
return ret1, False
# If a local shadows a state variable but the attribute is `self`, we want to
# return the state variable and not the local.
if not is_self:
ret1 = _find_variable_in_function_parser(var_name, function_parser)
if ret1:
return ret1, False
ret = _find_in_contract(var_name, next_context, caller_context)
if ret:

@ -16,7 +16,7 @@ counter_var(uint256) := 0(uint256)"];
3[label="Node Type: IF_LOOP 3
EXPRESSION:
counter_var <= len()(super.strategies)
counter_var <= len()(self.strategies)
IRs:
TMP_0(uint256) = SOLIDITY_CALL len()(strategies)

@ -5,7 +5,7 @@ digraph{
1[label="Node Type: RETURN 1
EXPRESSION:
x != super.fb() && x != super.fa()
x != self.fb() && x != self.fa()
IRs:
TMP_0(uint256) = INTERNAL_CALL, precedence.fb()()

@ -21,7 +21,7 @@ b(int128) := 0(uint256)"];
3[label="Node Type: EXPRESSION 3
EXPRESSION:
(a,b) = super.foo()
(a,b) = self.foo()
IRs:
TUPLE_0(int128,int128) = INTERNAL_CALL, tuple.foo()()

@ -0,0 +1,4 @@
name: public(String[64])
@external
def __init__(name: String[64]):
self.name = name

@ -113,3 +113,16 @@ def test_references_user_defined_types_when_casting(solc_binary_path):
assert len(a.references) == 2
lines = _sort_references_lines(a.references)
assert lines == [12, 18]
def test_references_self_identifier():
"""
Tests that shadowing state variables with local variables does not affect references.
"""
file = Path(SRC_MAPPING_TEST_ROOT, "SelfIdentifier.vy").as_posix()
slither = Slither(file)
contracts = slither.compilation_units[0].contracts
a = contracts[0].state_variables[0]
assert len(a.references) == 1
lines = _sort_references_lines(a.references)
assert lines == [4]
Loading…
Cancel
Save