def test_subscript_negative(build_node, namespace):
    node = build_node("foo[-1]")
    type_def = Int128Definition()

    namespace["foo"] = ArrayDefinition(type_def, 3)
    with pytest.raises(ArrayIndexException):
        get_possible_types_from_node(node)
def test_boolop(build_node, namespace, op, left, right):
    node = build_node(f"{left} {op} {right}")
    with namespace.enter_scope():
        types_list = get_possible_types_from_node(node)

    assert len(types_list) == 1
    assert isinstance(types_list[0], BoolDefinition)
Example #3
0
 def visit_UnaryOp(self, node, type_):
     if type_ is None:
         type_ = get_possible_types_from_node(node.operand)
         if len(type_) == 1:
             type_ = type_.pop()
     node._metadata["type"] = type_
     self.visit(node.operand, type_)
def test_tuple_subscript(build_node, namespace):
    node = build_node("(foo, bar)[1]")

    namespace["foo"] = Int128Definition()
    namespace["bar"] = AddressDefinition()
    types_list = get_possible_types_from_node(node)

    assert types_list == [namespace["bar"]]
def test_tuple(build_node, namespace):
    node = build_node("(foo, bar)")

    namespace["foo"] = Int128Definition()
    namespace["bar"] = AddressDefinition()
    types_list = get_possible_types_from_node(node)

    assert types_list[0].value_type == [namespace["foo"], namespace["bar"]]
Example #6
0
 def visit_List(self, node, type_):
     if type_ is None:
         type_ = get_possible_types_from_node(node)
         if len(type_) >= 1:
             type_ = type_.pop()
     node._metadata["type"] = type_
     for element in node.elements:
         self.visit(element, type_.value_type)
def test_list(build_node, namespace, left, right):
    node = build_node(f"[{left}, {right}]")

    with namespace.enter_scope():
        types_list = get_possible_types_from_node(node)

    assert types_list
    for item in types_list:
        assert isinstance(item, (DynamicArrayDefinition, ArrayDefinition))
Example #8
0
 def visit_List(self, node, type_):
     if type_ is None:
         type_ = get_possible_types_from_node(node)
         # CMC 2022-04-14 this seems sus. try to only annotate
         # if get_possible_types only returns 1 type
         if len(type_) >= 1:
             type_ = type_.pop()
     node._metadata["type"] = type_
     for element in node.elements:
         self.visit(element, type_.value_type)
Example #9
0
    def visit_Subscript(self, node, type_):
        if isinstance(node.value, vy_ast.List):
            possible_base_types = get_possible_types_from_node(node.value)

            if len(possible_base_types) == 1:
                base_type = possible_base_types.pop()

            elif type_ and len(possible_base_types) > 1:
                for possible_type in possible_base_types:
                    if isinstance(possible_type.value_type, type(type_)):
                        base_type = possible_type
                        break

        else:
            base_type = get_exact_type_from_node(node.value)
        if isinstance(base_type, BaseTypeDefinition):
            # in the vast majority of cases `base_type` is a type definition,
            # however there are some edge cases with args to builtin functions
            self.visit(node.slice, base_type.get_index_type(node.slice.value))
        self.visit(node.value, base_type)
Example #10
0
    def visit_Subscript(self, node, type_):
        node._metadata["type"] = type_

        if isinstance(type_, TypeTypeDefinition):
            # don't recurse; can't annotate AST children of type definition
            return

        if isinstance(node.value, vy_ast.List):
            possible_base_types = get_possible_types_from_node(node.value)

            if len(possible_base_types) == 1:
                base_type = possible_base_types.pop()

            elif type_ is not None and len(possible_base_types) > 1:
                for possible_type in possible_base_types:
                    if isinstance(possible_type.value_type, type(type_)):
                        base_type = possible_type
                        break

        else:
            base_type = get_exact_type_from_node(node.value)

        self.visit(node.slice, base_type.get_index_type())
        self.visit(node.value, base_type)
def test_attribute_not_member_type(build_node, namespace):
    node = build_node("foo.bar")
    with namespace.enter_scope():
        namespace["foo"] = Int128Definition()
        with pytest.raises(StructureException):
            get_possible_types_from_node(node)
Example #12
0
 def visit_Constant(self, node, type_):
     if type_ is None:
         possible_types = get_possible_types_from_node(node)
         if len(possible_types) == 1:
             type_ = possible_types.pop()
     node._metadata["type"] = type_
def test_subscript(build_node, namespace):
    node = build_node("foo[1]")
    type_def = Int128Definition()

    namespace["foo"] = ArrayDefinition(type_def, 3)
    assert get_possible_types_from_node(node) == [type_def]
def test_attribute(build_node, namespace):
    node = build_node("self.foo")
    type_def = Int128Definition()
    with namespace.enter_scope():
        namespace["self"].add_member("foo", type_def)
        assert get_possible_types_from_node(node) == [type_def]
Example #15
0
    def visit_For(self, node):
        if isinstance(node.iter, vy_ast.Subscript):
            raise StructureException("Cannot iterate over a nested list",
                                     node.iter)

        if isinstance(node.iter, vy_ast.Call):
            # iteration via range()
            if node.iter.get("func.id") != "range":
                raise IteratorException(
                    "Cannot iterate over the result of a function call",
                    node.iter)
            validate_call_args(node.iter, (1, 2))

            args = node.iter.args
            if len(args) == 1:
                # range(CONSTANT)
                if not isinstance(args[0], vy_ast.Num):
                    raise StateAccessViolation("Value must be a literal", node)
                if args[0].value <= 0:
                    raise StructureException(
                        "For loop must have at least 1 iteration", args[0])
                validate_expected_type(args[0], IntegerAbstractType())
                type_list = get_possible_types_from_node(args[0])
            else:
                validate_expected_type(args[0], IntegerAbstractType())
                type_list = get_common_types(*args)
                if not isinstance(args[0], vy_ast.Constant):
                    # range(x, x + CONSTANT)
                    if not isinstance(args[1], vy_ast.BinOp) or not isinstance(
                            args[1].op, vy_ast.Add):
                        raise StructureException(
                            "Second element must be the first element plus a literal value",
                            args[0])
                    if not vy_ast.compare_nodes(args[0], args[1].left):
                        raise StructureException(
                            "First and second variable must be the same",
                            args[1].left)
                    if not isinstance(args[1].right, vy_ast.Int):
                        raise InvalidLiteral("Literal must be an integer",
                                             args[1].right)
                    if args[1].right.value < 1:
                        raise StructureException(
                            f"For loop has invalid number of iterations ({args[1].right.value}),"
                            " the value must be greater than zero",
                            args[1].right,
                        )
                else:
                    # range(CONSTANT, CONSTANT)
                    if not isinstance(args[1], vy_ast.Int):
                        raise InvalidType("Value must be a literal integer",
                                          args[1])
                    validate_expected_type(args[1], IntegerAbstractType())
                    if args[0].value >= args[1].value:
                        raise StructureException(
                            "Second value must be > first value", args[1])

        else:
            # iteration over a variable or literal list
            type_list = [
                i.value_type for i in get_possible_types_from_node(node.iter)
                if isinstance(i, (DynamicArrayDefinition, ArrayDefinition))
            ]

        if not type_list:
            raise InvalidType("Not an iterable type", node.iter)

        if isinstance(node.iter, (vy_ast.Name, vy_ast.Attribute)):
            # check for references to the iterated value within the body of the loop
            assign = _check_iterator_modification(node.iter, node)
            if assign:
                raise ImmutableViolation(
                    "Cannot modify array during iteration", assign)

        # Check if `iter` is a storage variable. get_descendants` is used to check for
        # nested `self` (e.g. structs)
        iter_is_storage_var = (isinstance(node.iter, vy_ast.Attribute) and len(
            node.iter.get_descendants(vy_ast.Name, {"id": "self"})) > 0)

        if iter_is_storage_var:
            # check if iterated value may be modified by function calls inside the loop
            iter_name = node.iter.attr
            for call_node in node.get_descendants(vy_ast.Call,
                                                  {"func.value.id": "self"}):
                fn_name = call_node.func.attr

                fn_node = self.vyper_module.get_children(
                    vy_ast.FunctionDef, {"name": fn_name})[0]
                if _check_iterator_modification(node.iter, fn_node):
                    # check for direct modification
                    raise ImmutableViolation(
                        f"Cannot call '{fn_name}' inside for loop, it potentially "
                        f"modifies iterated storage variable '{iter_name}'",
                        call_node,
                    )

                for name in self.namespace["self"].members[
                        fn_name].recursive_calls:
                    # check for indirect modification
                    fn_node = self.vyper_module.get_children(
                        vy_ast.FunctionDef, {"name": name})[0]
                    if _check_iterator_modification(node.iter, fn_node):
                        raise ImmutableViolation(
                            f"Cannot call '{fn_name}' inside for loop, it may call to '{name}' "
                            f"which potentially modifies iterated storage variable '{iter_name}'",
                            call_node,
                        )
        self.expr_visitor.visit(node.iter)

        for_loop_exceptions = []
        iter_name = node.target.id
        for type_ in type_list:
            # type check the for loop body using each possible type for iterator value
            type_ = copy.deepcopy(type_)
            type_.is_constant = True

            with self.namespace.enter_scope():
                try:
                    self.namespace[iter_name] = type_
                except VyperException as exc:
                    raise exc.with_annotation(node) from None

                try:
                    for n in node.body:
                        self.visit(n)
                    # type information is applied directly because the scope is
                    # closed prior to the call to `StatementAnnotationVisitor`
                    node.target._metadata["type"] = type_
                    return
                except (TypeMismatch, InvalidOperation) as exc:
                    for_loop_exceptions.append(exc)

        if len(set(str(i) for i in for_loop_exceptions)) == 1:
            # if every attempt at type checking raised the same exception
            raise for_loop_exceptions[0]

        # return an aggregate TypeMismatch that shows all possible exceptions
        # depending on which type is used
        types_str = [str(i) for i in type_list]
        given_str = f"{', '.join(types_str[:1])} or {types_str[-1]}"
        raise TypeMismatch(
            f"Iterator value '{iter_name}' may be cast as {given_str}, "
            "but type checking fails with all possible types:",
            node,
            *((f"Casting '{iter_name}' as {type_}: {exc.message}",
               exc.annotations[0])
              for type_, exc in zip(type_list, for_loop_exceptions)),
        )
def test_binop_invalid_decimal_pow(build_node, namespace):
    node = build_node("2.1 ** 2.1")
    with namespace.enter_scope():
        with pytest.raises(InvalidOperation):
            get_possible_types_from_node(node)
def test_binop_invalid_op(build_node, namespace, op, left, right):
    node = build_node(f"{left} {op} {right}")
    with namespace.enter_scope():
        with pytest.raises(InvalidOperation):
            get_possible_types_from_node(node)
def test_binop_type_mismatch(build_node, namespace, op, left, right):
    node = build_node(f"{left}{op}{right}")
    with namespace.enter_scope():
        with pytest.raises(TypeMismatch):
            get_possible_types_from_node(node)
def test_name_unknown(build_node, namespace):
    node = build_node("foo")
    with pytest.raises(UndeclaredDefinition):
        get_possible_types_from_node(node)
def test_attribute_missing_self(build_node, namespace):
    node = build_node("foo")
    with namespace.enter_scope():
        namespace["self"].add_member("foo", Int128Definition())
        with pytest.raises(InvalidReference):
            get_possible_types_from_node(node)
def test_attribute_unknown(build_node, namespace):
    node = build_node("foo.bar")
    with namespace.enter_scope():
        namespace["foo"] = AddressDefinition()
        with pytest.raises(UnknownAttribute):
            get_possible_types_from_node(node)
def test_attribute_not_in_self(build_node, namespace):
    node = build_node("self.foo")
    with namespace.enter_scope():
        namespace["foo"] = Int128Definition()
        with pytest.raises(InvalidReference):
            get_possible_types_from_node(node)
def test_binop(build_node, namespace, op, left, right):
    node = build_node(f"{left}{op}{right}")
    with namespace.enter_scope():
        get_possible_types_from_node(node)
def test_name(build_node, namespace):
    node = build_node("foo")
    type_def = Int128Definition()
    namespace["foo"] = type_def

    assert get_possible_types_from_node(node) == [type_def]