From 35a2dcfea4e34dcfd9dd014911000e53b8d8bd3e Mon Sep 17 00:00:00 2001 From: Balaji Shetty Pachai <32358081+balajipachai@users.noreply.github.com> Date: Tue, 14 Jul 2020 00:28:46 +0530 Subject: [PATCH 1/5] Update testing-contracts-brownie.rst After following the doc and executing brownie test, I faced an error: E brownie.exceptions.EventLookupError: Unknown key '_value' - the 'DataChange' event includes these keys: setter, value The error is quite explanatory, the event DataChange has been defined with keys `value` and `setter` and were being accessed in the test cases by `_value` and `_setter`. Thus the proposed change --- docs/testing-contracts-brownie.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/testing-contracts-brownie.rst b/docs/testing-contracts-brownie.rst index 62e3a885b7..0bc83d0b7d 100644 --- a/docs/testing-contracts-brownie.rst +++ b/docs/testing-contracts-brownie.rst @@ -97,10 +97,10 @@ To test events, we examine the :py:class:`TransactionReceipt Date: Wed, 15 Jul 2020 19:04:41 +0300 Subject: [PATCH 2/5] fix: validate index value on references to arrays --- vyper/context/types/indexable/sequence.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/vyper/context/types/indexable/sequence.py b/vyper/context/types/indexable/sequence.py index a8ff614469..87bcc06e22 100644 --- a/vyper/context/types/indexable/sequence.py +++ b/vyper/context/types/indexable/sequence.py @@ -1,6 +1,7 @@ from typing import Tuple from vyper import ast as vy_ast +from vyper.context import validation from vyper.context.types.abstract import IntegerAbstractType from vyper.context.types.bases import ( BaseTypeDefinition, @@ -69,8 +70,10 @@ def get_index_type(self, node): if isinstance(node, vy_ast.Int): if node.value < 0: raise ArrayIndexException("Vyper does not support negative indexing", node) - if node.value < 0 or node.value >= self.length: + if node.value >= self.length: raise ArrayIndexException("Index out of range", node) + else: + validation.utils.validate_expected_type(node, IntegerAbstractType()) return self.value_type def compare_type(self, other): @@ -109,7 +112,7 @@ def get_index_type(self, node): raise InvalidType("Tuple indexes must be literals", node) if node.value < 0: raise ArrayIndexException("Vyper does not support negative indexing", node) - if node.value < 0 or node.value >= self.length: + if node.value >= self.length: raise ArrayIndexException("Index out of range", node) return self.value_type[node.value] From c6c9ddad7dd12427c296823c6c1bc572ccbdae33 Mon Sep 17 00:00:00 2001 From: Ben Hauser Date: Wed, 15 Jul 2020 19:07:27 +0300 Subject: [PATCH 3/5] feat: add descriptive repr for abstract types --- vyper/context/types/abstract.py | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/vyper/context/types/abstract.py b/vyper/context/types/abstract.py index ab1c42ace8..1e3ef9fe1c 100644 --- a/vyper/context/types/abstract.py +++ b/vyper/context/types/abstract.py @@ -16,16 +16,26 @@ def compare_type(self, other): pass return isinstance(other, type(self)) + def __repr__(self): + value = super().__repr__() + if value == object.__repr__(self): + # use `_description` when no parent class overrides the default python repr + return self._description + return value + class ArrayValueAbstractType(AbstractDataType): """ Abstract data class for single-value types occupying multiple memory slots. """ + _description = "fixed size bytes array or string" + class BytesAbstractType(AbstractDataType): """Abstract data class for bytes types (bytes32, bytes[]).""" + _description = "bytes" _id = "bytes" @@ -34,10 +44,14 @@ class NumericAbstractType(AbstractDataType): Abstract data class for numeric types (capable of arithmetic). """ + _description = "numeric value" + class IntegerAbstractType(NumericAbstractType): """Abstract data class for integer numeric types (int128, uint256).""" + _description = "integer" + class FixedAbstractType(NumericAbstractType): """ @@ -47,3 +61,5 @@ class FixedAbstractType(NumericAbstractType): still be used to expect decimal values in anticipation of multiple decimal types in a future release. """ + + _description = "decimal" From 47e6923aa8073f522a75894c09f30000f70341a8 Mon Sep 17 00:00:00 2001 From: Ben Hauser Date: Thu, 16 Jul 2020 19:14:55 +0300 Subject: [PATCH 4/5] feat: improve error message on invalid use of type object --- vyper/context/validation/utils.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/vyper/context/validation/utils.py b/vyper/context/validation/utils.py index 2c03b076fc..c62970cc37 100644 --- a/vyper/context/validation/utils.py +++ b/vyper/context/validation/utils.py @@ -350,9 +350,12 @@ def validate_expected_type(node, expected_type): if not isinstance(node, (vy_ast.List, vy_ast.Tuple)) and node.get_descendants( vy_ast.Name, include_self=True ): - raise TypeMismatch( - f"Given reference has type {given_types[0]}, expected {expected_str}", node - ) + given = given_types[0] + if isinstance(given, type) and types.BasePrimitive in given.mro(): + raise InvalidReference( + f"'{given._id}' is a type - expected a literal or variable", node + ) + raise TypeMismatch(f"Given reference has type {given}, expected {expected_str}", node) else: if len(given_types) == 1: given_str = str(given_types[0]) From 24b7a3d6a0cc013cb88b5a067a30166ef50437a2 Mon Sep 17 00:00:00 2001 From: Ben Hauser Date: Thu, 16 Jul 2020 19:01:32 +0300 Subject: [PATCH 5/5] test: array index types --- .../context/validation/test_array_index.py | 86 +++++++++++++++++++ 1 file changed, 86 insertions(+) create mode 100644 tests/functional/context/validation/test_array_index.py diff --git a/tests/functional/context/validation/test_array_index.py b/tests/functional/context/validation/test_array_index.py new file mode 100644 index 0000000000..7d601015a4 --- /dev/null +++ b/tests/functional/context/validation/test_array_index.py @@ -0,0 +1,86 @@ +import pytest + +from vyper.ast import parse_to_ast +from vyper.context.validation import validate_semantics +from vyper.exceptions import ( + ArrayIndexException, + InvalidReference, + InvalidType, + TypeMismatch, + UndeclaredDefinition, +) + + +@pytest.mark.parametrize("value", ["address", "Bytes[10]", "decimal", "bool"]) +def test_type_mismatch(namespace, value): + code = f""" + +a: uint256[3] + +@internal +def foo(b: {value}): + self.a[b] = 12 + """ + vyper_module = parse_to_ast(code) + with pytest.raises(TypeMismatch): + validate_semantics(vyper_module, {}) + + +@pytest.mark.parametrize("value", ["1.0", "0.0", "'foo'", "0x00", "b'\x01'", "False"]) +def test_invalid_literal(namespace, value): + code = f""" + +a: uint256[3] + +@internal +def foo(): + self.a[{value}] = 12 + """ + vyper_module = parse_to_ast(code) + with pytest.raises(InvalidType): + validate_semantics(vyper_module, {}) + + +@pytest.mark.parametrize("value", [-1, 3, -(2 ** 127), 2 ** 127 - 1, 2 ** 256 - 1]) +def test_out_of_bounds(namespace, value): + code = f""" + +a: uint256[3] + +@internal +def foo(): + self.a[{value}] = 12 + """ + vyper_module = parse_to_ast(code) + with pytest.raises(ArrayIndexException): + validate_semantics(vyper_module, {}) + + +@pytest.mark.parametrize("value", ["b", "self.b"]) +def test_undeclared_definition(namespace, value): + code = f""" + +a: uint256[3] + +@internal +def foo(): + self.a[{value}] = 12 + """ + vyper_module = parse_to_ast(code) + with pytest.raises(UndeclaredDefinition): + validate_semantics(vyper_module, {}) + + +@pytest.mark.parametrize("value", ["a", "foo", "int128"]) +def test_invalid_reference(namespace, value): + code = f""" + +a: uint256[3] + +@internal +def foo(): + self.a[{value}] = 12 + """ + vyper_module = parse_to_ast(code) + with pytest.raises(InvalidReference): + validate_semantics(vyper_module, {})