def attributes_assigned_in_init_can_be_used_in_methods_when_init_method_is_defined_after_other_method(): init_func = nodes.func( name="__init__", args=nodes.args([nodes.arg("self_init")]), body=[ nodes.assign( [nodes.attr(nodes.ref("self_init"), "message")], nodes.str_literal("Hello"), type=nodes.ref("str"), ) ], type=nodes.signature( args=[nodes.signature_arg(nodes.ref("Self"))], returns=nodes.ref("none") ), ) node = nodes.class_("User", [ nodes.func( name="g", args=nodes.args([nodes.arg("self_g")]), body=[nodes.ret(nodes.attr(nodes.ref("self_g"), "message"))], type=nodes.signature( args=[nodes.signature_arg(nodes.ref("Self"))], returns=nodes.ref("str") ), ), init_func, ]) _infer_class_type(node, ["__init__", "g"], [(init_func, ["self_init"])])
def names_in_class_are_not_declared_in_outer_scope(): node = nodes.class_("User", [ nodes.assign([nodes.ref("x")], nodes.none()) ]) declarations = find_declarations(node) assert not declarations.is_declared("x")
def class_definition_base_classes_are_resolved(): ref = nodes.ref("object") node = nodes.class_("User", [], base_classes=[ref]) declarations = _create_declarations(["User", "object"]) references = resolve(node, declarations) assert_is(declarations.declaration("object"), references.referenced_declaration(ref))
def attributes_assigned_in_init_can_be_used_outside_of_class(): init_func = nodes.func( name="__init__", args=nodes.args([nodes.arg("self_init")]), body=[ nodes.assign( [nodes.attr(nodes.ref("self_init"), "message")], nodes.str_literal("Hello"), type=nodes.ref("str"), ) ], type=nodes.signature( args=[nodes.signature_arg(nodes.ref("Self"))], returns=nodes.ref("none") ) ) class_node = nodes.class_("User", [init_func]) node = [ class_node, nodes.assign([nodes.ref("x")], nodes.attr(nodes.call(nodes.ref("User"), []), "message")), ] context = _update_context(node, declared_names_in_node=NodeDict([ (class_node, ["Self", "__init__"]), (init_func, ["self_init"]), ])) assert_equal(types.str_type, context.lookup_name("x"))
def method_can_call_method_on_same_instance_defined_later_in_body(): node = nodes.class_("User", [ nodes.func( name="f", args=nodes.args([nodes.arg("self_f")]), body=[ nodes.ret(nodes.call(nodes.attr(nodes.ref("self_f"), "g"), [])) ], type=nodes.signature( args=[nodes.signature_arg(nodes.ref("Self"))], returns=nodes.ref("none") ), ), nodes.func( name="g", args=nodes.args([nodes.arg("self_g")]), body=[ nodes.ret(nodes.call(nodes.attr(nodes.ref("self_g"), "f"), [])) ], type=nodes.signature( args=[nodes.signature_arg(nodes.ref("Self"))], returns=nodes.ref("none") ), ) ]) _infer_class_type(node, ["f", "g"])
def init_method_cannot_call_other_methods(): node = nodes.class_("User", [ nodes.func( name="__init__", args=nodes.args([nodes.arg("self_init")]), body=[ nodes.assign([nodes.ref("x")], nodes.call(nodes.attr(nodes.ref("self_init"), "g"), [])) ], type=nodes.signature( args=[nodes.signature_arg(nodes.ref("Self"))], returns=nodes.ref("none") ), ), nodes.func( name="g", args=nodes.args([nodes.arg("self_g")]), body=[], type=nodes.signature( args=[nodes.signature_arg(nodes.ref("Self"))], returns=nodes.ref("none") ), ), ]) try: _infer_class_type(node, ["__init__", "g"]) assert False, "Expected error" except errors.InitMethodCannotGetSelfAttributes as error: assert_equal("__init__ methods cannot get attributes of self", str(error))
def break_is_not_valid_in_class_in_while_loop_body(): break_node = nodes.break_() func_node = nodes.class_("User", [break_node]) node = nodes.while_(nodes.bool_literal(True), [func_node], []) error = assert_raises(errors.TypeCheckError, lambda: check_loop_control(node, False)) assert_equal(break_node, error.node) assert_equal("'break' outside loop", str(error))
def attributes_with_function_type_defined_in_class_definition_body_are_not_present_on_meta_type(): node = nodes.class_("User", [ nodes.assign([nodes.ref("is_person")], nodes.ref("true_func")), ]) meta_type = _infer_meta_type(node, ["is_person"], type_bindings={ "true_func": types.func([types.object_type], types.none_type) }) assert "is_person" not in meta_type.attrs
def class_definition_bodies_can_access_variables_from_outer_scope(): ref = nodes.ref("x") node = nodes.class_("User", [nodes.expression_statement(ref)]) declarations = _create_declarations(["x", "User"]) references = resolve(node, declarations) assert_is(declarations.declaration("x"), references.referenced_declaration(ref))
def declarations_in_class_include_declarations_in_body(): node = nodes.class_("User", [ nodes.assign([nodes.ref("x")], nodes.none()) ]) declarations = _declarations_in(node) assert isinstance(declarations.declaration("x"), name_declaration.VariableDeclarationNode) assert not declarations.is_declared("User")
def error_if_base_class_is_not_object(): type_bindings = {"Person": types.meta_type(types.class_type("Person"))} node = nodes.class_("User", [], base_classes=[nodes.ref("Person")]) try: _infer_class_type(node, [], type_bindings=type_bindings) assert False, "Expected error" except errors.UnsupportedError as error: assert_equal("base classes other than 'object' are not supported", str(error))
def test_function_definitions_in_body_are_stored_as_methods(self): _assert_transform( nodes.class_("Blah", [nodes.func("f", nodes.args([]), [], type=None)]), cc.class_( "Blah", methods=[cc.func("f", [], [cc.ret(cc.none)])], body=[cc.declare("f")], ), )
def _create_class_with_init(signature, args, body): return nodes.class_("User", [ nodes.func( name="__init__", args=args, body=body, type=signature ) ])
def class_definitions_assignments_shadow_variables_of_same_name_in_outer_scope(): ref = nodes.ref("x") body = [nodes.assign([ref], nodes.none())] node = nodes.class_("User", body) declarations = _create_declarations(["x", "User"]) references = resolve(node, declarations) assert_is_not(declarations.declaration("x"), references.referenced_declaration(ref))
def init_must_be_function_definition(): func_node = nodes.assign([nodes.ref("__init__")], nodes.ref("f")) node = nodes.class_("User", [func_node]) try: _infer_class_type(node, ["__init__"], type_bindings={ "f": types.func([types.object_type], types.str_type) }) assert False, "Expected error" except errors.InitAttributeMustBeFunctionDefinitionError as error: assert_equal(func_node, error.node)
def method_can_reference_later_function_if_class_is_not_used_in_the_interim(): g_ref = nodes.ref("g") f = nodes.func("f", type=None, args=nodes.Arguments([]), body=[ nodes.ret(nodes.call(g_ref, [])) ]) g = nodes.func("g", type=None, args=nodes.Arguments([]), body=[]) _updated_bindings(nodes.module([ nodes.class_("a", [f]), g, ]))
def method_signature_is_checked_when_defined_by_assignment(): func_node = nodes.assign([nodes.ref("is_person")], nodes.ref("f")) node = nodes.class_("User", [func_node]) try: _infer_class_type(node, ["is_person"], type_bindings={ "f": types.func([], types.bool_type) }) assert False, "Expected error" except errors.MethodHasNoArgumentsError as error: assert_equal(func_node, error.node) assert_equal("is_person", error.attr_name)
def class_definition_functions_ignore_class_scope_when_resolving_references(): ref = nodes.ref("x") node = nodes.class_("User", [ nodes.assign([nodes.ref("x")], nodes.none()), nodes.func("f", nodes.args([]), [nodes.ret(ref)], type=None), ]) declarations = _create_declarations(["x", "User"]) references = resolve(node, declarations) assert_is(declarations.declaration("x"), references.referenced_declaration(ref))
def test_assignments_in_body_are_transformed(self): _assert_transform( nodes.class_("Blah", [nodes.assign([nodes.ref("value")], nodes.none())]), cc.class_( "Blah", methods=[], body=[ cc.declare("value"), cc.assign(cc.ref("value"), cc.none), ], ), )
def attributes_with_function_type_defined_in_class_definition_body_are_bound_to_class_type(): node = nodes.class_("User", [ nodes.func( name="is_person", args=nodes.args([nodes.arg("self")]), body=[nodes.ret(nodes.bool_literal(True))], type=nodes.signature( args=[nodes.signature_arg(nodes.ref("Self"))], returns=nodes.ref("bool") ), ), ]) class_type = _infer_class_type(node, ["is_person"]) assert_equal(types.func([], types.bool_type), class_type.attrs.type_of("is_person"))
def first_argument_in_method_signature_can_be_strict_supertype_of_class(): node = nodes.class_("User", [ nodes.func( name="is_person", args=nodes.args([nodes.arg("self")]), body=[nodes.ret(nodes.bool_literal(True))], type=nodes.signature( args=[nodes.signature_arg(nodes.ref("object"))], returns=nodes.ref("bool") ), ) ]) class_type = _infer_class_type(node, ["is_person"]) assert_equal(types.func([], types.bool_type), class_type.attrs.type_of("is_person"))
def class_cannot_be_referenced_if_method_dependencies_are_not_bound(): g_ref = nodes.ref("g") f = nodes.func("f", type=None, args=nodes.Arguments([]), body=[ nodes.ret(nodes.call(g_ref, [])) ]) g = nodes.func("g", type=None, args=nodes.Arguments([]), body=[]) try: _updated_bindings(nodes.module([ nodes.class_("a", [f]), nodes.expression_statement(nodes.ref("a")), g, ])) assert False, "Expected error" except errors.UnboundLocalError as error: assert_equal(g_ref, error.node)
def methods_must_have_at_least_one_argument(): func_node = nodes.func( name="is_person", args=nodes.args([]), body=[nodes.ret(nodes.bool_literal(True))], type=nodes.signature( args=[], returns=nodes.ref("bool") ) ) node = nodes.class_("User", [func_node]) try: _infer_class_type(node, ["is_person"]) assert False, "Expected error" except errors.MethodHasNoArgumentsError as error: assert_equal(func_node, error.node) assert_equal("'is_person' method must have at least one argument", str(error))
def self_argument_in_method_signature_cannot_be_unrelated_type(): func_node = nodes.func( name="is_person", args=nodes.args([nodes.arg("self")]), body=[nodes.ret(nodes.bool_literal(True))], type=nodes.signature( args=[nodes.signature_arg(nodes.ref("bool"))], returns=nodes.ref("bool") ), ) node = nodes.class_("User", [func_node]) try: _infer_class_type(node, ["is_person"]) assert False, "Expected error" except errors.UnexpectedReceiverTypeError as error: assert_equal(func_node, error.node) assert_equal(types.bool_type, error.receiver_type) assert_equal("first argument of methods should have Self type but was 'bool'", str(error))
def can_assign_literal_to_attribute(): init_func = nodes.func( name="__init__", args=nodes.args([nodes.arg("self")]), body=[ nodes.assign( [nodes.attr(nodes.ref("self"), "value")], nodes.none(), ) ], type=_no_arg_init_signature, ) node = nodes.class_("Box", [ init_func ]) class_type = _infer_class_type( node, ["__init__"], [(init_func, ["self"])]) assert_equal(types.none_type, class_type.attrs.type_of("value"))
def error_is_raised_if_attribute_assignment_has_no_explicit_and_not_literal_nor_argument_value(): init_func = nodes.func( name="__init__", args=nodes.args([nodes.arg("self")]), body=[ nodes.assign( [nodes.attr(nodes.ref("self"), "message")], nodes.ref("message"), ) ], type=_no_arg_init_signature, ) node = nodes.class_("User", [ init_func ]) error = assert_raises(errors.ClassAttributeTypeError, lambda: _infer_class_type( node, ["__init__"], [(init_func, ["self"])], type_bindings={"message": types.str_type})) assert_equal("Could not infer type of attribute. Attribute assignments must be explicitly typed, or a literal, or an argument", str(error))
def can_assign_any_value_to_attribute_if_assignment_is_explicitly_typed(): init_func = nodes.func( name="__init__", args=nodes.args([nodes.arg("self")]), body=[ nodes.assign( [nodes.attr(nodes.ref("self"), "message")], nodes.ref("message"), type=nodes.ref("str"), ) ], type=_no_arg_init_signature, ) node = nodes.class_("User", [ init_func ]) class_type = _infer_class_type( node, ["__init__"], [(init_func, ["self"])], type_bindings={"message": types.str_type}) assert_equal(types.str_type, class_type.attrs.type_of("message"))
def class_definition_is_resolved_to_class_declaration(): node = nodes.class_("User", []) declarations = _create_declarations(["User"]) references = resolve(node, declarations) assert_is(declarations.declaration("User"), references.referenced_declaration(node))
def test_parse_class_with_generics(): expected_node = nodes.class_("Option", [], type_params=[nodes.formal_type_parameter("T")]) _assert_statement_parse(expected_node, "#:generic T\nclass Option:\n pass")
def test_parse_class_with_base_classes(): expected_node = nodes.class_("User", [], base_classes=[nodes.ref("Person")]) _assert_statement_parse(expected_node, "class User(Person):\n pass")