def check_method_or_accessor_override_for_base(self, defn, base): """Check that function definition is compatible with any overridden definition in the specified supertype. """ if base: if defn.name() != '__init__': # Check method override (create is special). base_method = base.get_method(defn.name()) if base_method and base_method.info == base: # There is an overridden method in the supertype. # Construct the type of the overriding method. typ = method_type(defn) # Map the overridden method type to subtype context so that # it can be checked for compatibility. Note that multiple # types from multiple implemented interface instances may # be present. original_type = map_type_from_supertype( method_type(base_method), defn.info, base) # Check that the types are compatible. # TODO overloaded signatures self.check_override(typ, original_type, defn.name(), base_method.info.name(), defn) # Also check interface implementations. for iface in base.interfaces: self.check_method_or_accessor_override_for_base(defn, iface) # We have to check that the member is compatible with all # supertypes due to the dynamic type. Otherwise we could first # override with dynamic and then with an arbitary type. self.check_method_or_accessor_override_for_base(defn, base.base)
def type_object_type(info: TypeInfo, builtin_type: Function[[str], Instance]) -> Type: """Return the type of a type object. For a generic type G with type variables T and S the type is of form def [T, S](...) -> G[T, S], where ... are argument types for the __init__ method (without the self argument). """ init_method = info.get_method('__init__') if not init_method: # Must be an invalid class definition. return AnyType() else: # Construct callable type based on signature of __init__. Adjust # return type and insert type arguments. init_type = method_type(init_method, builtin_type('builtins.function')) if isinstance(init_type, Callable): return class_callable(init_type, info, builtin_type('builtins.type')) else: # Overloaded __init__. items = [] # type: List[Callable] for it in cast(Overloaded, init_type).items(): items.append(class_callable(it, info, builtin_type('builtins.type'))) return Overloaded(items)
def type_object_type(info: TypeInfo, type_type: Function[[], Type]) -> Type: """Return the type of a type object. For a generic type G with type variables T and S the type is of form def [T, S](...) -> G[T, S], where ... are argument types for the __init__ method. """ init_method = info.get_method('__init__') if not init_method: # Must be an invalid class definition. return AnyType() else: # Construct callable type based on signature of __init__. Adjust # return type and insert type arguments. init_type = method_type(init_method) if isinstance(init_type, Callable): return class_callable(init_type, info) else: # Overloaded __init__. items = [] # type: List[Callable] for it in cast(Overloaded, init_type).items(): items.append(class_callable(it, info)) return Overloaded(items)
def analyse_member_var_access(name: str, itype: Instance, info: TypeInfo, node: Context, is_lvalue: bool, is_super: bool, msg: MessageBuilder, report_type: Type = None) -> Type: """Analyse attribute access that does not target a method. This is logically part of analyse_member_access and the arguments are similar. """ # It was not a method. Try looking up a variable. v = lookup_member_var_or_accessor(info, name, is_lvalue) vv = v if isinstance(vv, Decorator): # The associated Var node of a decorator contains the type. v = vv.var if isinstance(v, Var): # Found a member variable. var = v itype = map_instance_to_supertype(itype, var.info) if var.type: t = expand_type_by_instance(var.type, itype) if var.is_initialized_in_class and isinstance(t, FunctionLike): if is_lvalue: if var.is_property: msg.read_only_property(name, info, node) else: msg.cant_assign_to_method(node) if not var.is_staticmethod: # Class-level function objects and classmethods become bound # methods: the former to the instance, the latter to the # class. functype = cast(FunctionLike, t) check_method_type(functype, itype, node, msg) signature = method_type(functype) if var.is_property: # A property cannot have an overloaded type => the cast # is fine. return cast(Callable, signature).ret_type else: return signature return t else: if not var.is_ready: msg.cannot_determine_type(var.name(), node) # Implicit 'Any' type. return AnyType() elif isinstance(v, FuncDef): assert False, "Did not expect a function" # Could not find the member. if is_super: msg.undefined_in_superclass(name, node) return AnyType() else: return msg.has_no_attr(report_type or itype, name, node)
def analyse_member_var_access(name: str, itype: Instance, info: TypeInfo, node: Context, is_lvalue: bool, is_super: bool, msg: MessageBuilder, report_type: Type = None) -> Type: """Analyse attribute access that does not target a method. This is logically part of analyse_member_access and the arguments are similar. """ # It was not a method. Try looking up a variable. v = lookup_member_var_or_accessor(info, name, is_lvalue) vv = v if isinstance(vv, Decorator): # The associated Var node of a decorator contains the type. v = vv.var if isinstance(v, Var): # Found a member variable. var = v itype = map_instance_to_supertype(itype, var.info) if var.type: t = expand_type_by_instance(var.type, itype) if (var.is_initialized_in_class and isinstance(t, FunctionLike) and not var.is_staticmethod): # Class-level function object becomes a bound method. functype = cast(FunctionLike, t) check_method_type(functype, itype, node, msg) signature = method_type(functype) if var.is_property: if is_lvalue: msg.read_only_property(name, info, node) # A property cannot have an overloaded type => the cast # is fine. return cast(Callable, signature).ret_type else: return signature return t else: if not var.is_ready: msg.cannot_determine_type(var.name(), node) # Implicit 'Any' type. return AnyType() elif isinstance(v, FuncDef): assert False, "Did not expect a function" # Could not find the member. if is_super: msg.undefined_in_superclass(name, node) return AnyType() else: return msg.has_no_attr(report_type or itype, name, node)
def analyze_var( name: str, var: Var, itype: Instance, info: TypeInfo, node: Context, is_lvalue: bool, msg: MessageBuilder, not_ready_callback: Callable[[str, Context], None], ) -> Type: """Analyze access to an attribute via a Var node. This is conceptually part of analyze_member_access and the arguments are similar. """ # Found a member variable. itype = map_instance_to_supertype(itype, var.info) typ = var.type if typ: if isinstance(typ, PartialType): return handle_partial_attribute_type(typ, is_lvalue, msg, var) t = expand_type_by_instance(typ, itype) if is_lvalue and var.is_property and not var.is_settable_property: # TODO allow setting attributes in subclass (although it is probably an error) msg.read_only_property(name, info, node) if var.is_initialized_in_class and isinstance(t, FunctionLike): if is_lvalue: if var.is_property: if not var.is_settable_property: msg.read_only_property(name, info, node) else: msg.cant_assign_to_method(node) if not var.is_staticmethod: # Class-level function objects and classmethods become bound # methods: the former to the instance, the latter to the # class. functype = t check_method_type(functype, itype, var.is_classmethod, node, msg) signature = method_type(functype) if var.is_property: # A property cannot have an overloaded type => the cast # is fine. return cast(CallableType, signature).ret_type else: return signature return t else: if not var.is_ready: not_ready_callback(var.name(), node) # Implicit 'Any' type. return AnyType()
def analyse_member_access(name, typ, node, is_lvalue, is_super, tuple_type, msg, override_info=None): """Analyse member access. This is a general operation that supports various different variations: 1. lvalue or non-lvalue access (i.e. setter or getter access) 2. supertype access (when using super(); is_super == True and override_info should refer to the supertype) """ if isinstance(typ, Instance): if name == "__init__" and not is_super: # Accessing __init__ in statically typed code would compromise # type safety unless used via super(). msg.fail(messages.CANNOT_ACCESS_INIT, node) return Any() # The base object has an instance type. itype = typ info = itype.type if override_info: info = override_info # Look up the member. First look up the method dictionary. method = None if not is_lvalue: method = info.get_method(name) if method: # Found a method. The call below has a unique result for all valid # programs. itype = map_instance_to_supertype(itype, method.info) return expand_type_by_instance(method_type(method), itype) else: # Not a method. return analyse_member_var_access(name, itype, info, node, is_lvalue, is_super, msg) elif isinstance(typ, Any): # The base object has dynamic type. return Any() elif isinstance(typ, TupleType): # Actually look up from the tuple type. return analyse_member_access(name, tuple_type, node, is_lvalue, is_super, tuple_type, msg) elif isinstance(typ, Callable) and (typ).is_type_obj(): # Class attribute access. return msg.not_implemented("class attributes", node) else: # The base object has an unsupported type. return msg.has_no_member(typ, name, node)
def analyze_var(name: str, var: Var, itype: Instance, info: TypeInfo, node: Context, is_lvalue: bool, msg: MessageBuilder, not_ready_callback: Callable[[str, Context], None]) -> Type: """Analyze access to an attribute via a Var node. This is conceptually part of analyze_member_access and the arguments are similar. """ # Found a member variable. itype = map_instance_to_supertype(itype, var.info) typ = var.type if typ: if isinstance(typ, PartialType): return handle_partial_attribute_type(typ, is_lvalue, msg, var) t = expand_type_by_instance(typ, itype) if is_lvalue and var.is_property and not var.is_settable_property: # TODO allow setting attributes in subclass (although it is probably an error) msg.read_only_property(name, info, node) if var.is_initialized_in_class and isinstance(t, FunctionLike): if is_lvalue: if var.is_property: if not var.is_settable_property: msg.read_only_property(name, info, node) else: msg.cant_assign_to_method(node) if not var.is_staticmethod: # Class-level function objects and classmethods become bound # methods: the former to the instance, the latter to the # class. functype = t check_method_type(functype, itype, var.is_classmethod, node, msg) signature = method_type(functype) if var.is_property: # A property cannot have an overloaded type => the cast # is fine. return cast(CallableType, signature).ret_type else: return signature return t else: if not var.is_ready: not_ready_callback(var.name(), node) # Implicit 'Any' type. return AnyType()
def analyze_var( name: str, var: Var, itype: Instance, info: TypeInfo, node: Context, is_lvalue: bool, msg: MessageBuilder ) -> Type: """Analyze access to an attribute via a Var node. This is conceptually part of analyze_member_access and the arguments are similar. """ # Found a member variable. itype = map_instance_to_supertype(itype, var.info) if var.type: t = expand_type_by_instance(var.type, itype) if var.is_initialized_in_class and isinstance(t, FunctionLike): if is_lvalue: if var.is_property: if not var.is_settable_property: msg.read_only_property(name, info, node) else: msg.cant_assign_to_method(node) if not var.is_staticmethod: # Class-level function objects and classmethods become bound # methods: the former to the instance, the latter to the # class. functype = cast(FunctionLike, t) check_method_type(functype, itype, node, msg) signature = method_type(functype) if var.is_property: # A property cannot have an overloaded type => the cast # is fine. return cast(CallableType, signature).ret_type else: return signature return t else: if not var.is_ready: msg.cannot_determine_type(var.name(), node) # Implicit 'Any' type. return AnyType()
def analyze_var(name: str, var: Var, itype: Instance, info: TypeInfo, node: Context, is_lvalue: bool, msg: MessageBuilder) -> Type: """Analyze access to an attribute via a Var node. This is conceptually part of analyze_member_access and the arguments are similar. """ # Found a member variable. itype = map_instance_to_supertype(itype, var.info) if var.type: t = expand_type_by_instance(var.type, itype) if var.is_initialized_in_class and isinstance(t, FunctionLike): if is_lvalue: if var.is_property: if not var.is_settable_property: msg.read_only_property(name, info, node) else: msg.cant_assign_to_method(node) if not var.is_staticmethod: # Class-level function objects and classmethods become bound # methods: the former to the instance, the latter to the # class. functype = cast(FunctionLike, t) check_method_type(functype, itype, node, msg) signature = method_type(functype) if var.is_property: # A property cannot have an overloaded type => the cast # is fine. return cast(CallableType, signature).ret_type else: return signature return t else: if not var.is_ready: msg.cannot_determine_type(var.name(), node) # Implicit 'Any' type. return AnyType()
def analyse_member_var_access(name, itype, info, node, is_lvalue, is_super, msg): """Analyse member access that does not target a method. This is logically part of analyse_member_access and the arguments are similar. """ # It was not a method. Try looking up a variable. v = lookup_member_var_or_accessor(info, name, is_lvalue) if isinstance(v, Var): # Found a member variable. var = v itype = map_instance_to_supertype(itype, var.info) if var.type: t = expand_type_by_instance(var.type, itype) if var.is_initialized_in_class and isinstance(t, FunctionLike): # Class-level function object becomes a bound method. functype = t check_method_type(functype, itype, node, msg) return method_type(functype) return t else: if not var.is_ready: msg.cannot_determine_type(var.name(), node) # Implicit 'any' type. return Any() elif isinstance(v, FuncDef): # Found a getter or a setter. raise NotImplementedError() # Could not find the member. if is_super: msg.undefined_in_superclass(name, node) return Any() else: return msg.has_no_member(itype, name, node)
def analyse_member_access(name: str, typ: Type, node: Context, is_lvalue: bool, is_super: bool, builtin_type: Function[[str], Instance], msg: MessageBuilder, override_info: TypeInfo = None, report_type: Type = None) -> Type: """Analyse attribute access. This is a general operation that supports various different variations: 1. lvalue or non-lvalue access (i.e. setter or getter access) 2. supertype access (when using super(); is_super == True and override_info should refer to the supertype) """ report_type = report_type or typ if isinstance(typ, Instance): if name == '__init__' and not is_super: # Accessing __init__ in statically typed code would compromise # type safety unless used via super(). msg.fail(messages.CANNOT_ACCESS_INIT, node) return AnyType() # The base object has an instance type. info = typ.type if override_info: info = override_info # Look up the member. First look up the method dictionary. method = info.get_method(name) if method: if is_lvalue: msg.cant_assign_to_method(node) typ = map_instance_to_supertype(typ, method.info) return expand_type_by_instance( method_type(method, builtin_type('builtins.function')), typ) else: # Not a method. return analyse_member_var_access(name, typ, info, node, is_lvalue, is_super, msg, report_type=report_type) elif isinstance(typ, AnyType): # The base object has dynamic type. return AnyType() elif isinstance(typ, UnionType): # The base object has dynamic type. msg.disable_type_names += 1 results = [analyse_member_access(name, subtype, node, is_lvalue, is_super, builtin_type, msg) for subtype in typ.items] msg.disable_type_names -= 1 return UnionType.make_simplified_union(results) elif isinstance(typ, TupleType): # Actually look up from the fallback instance type. return analyse_member_access(name, typ.fallback, node, is_lvalue, is_super, builtin_type, msg) elif (isinstance(typ, FunctionLike) and cast(FunctionLike, typ).is_type_obj()): # Class attribute. # TODO super? sig = cast(FunctionLike, typ) itype = cast(Instance, sig.items()[0].ret_type) result = analyse_class_attribute_access(itype, name, node, is_lvalue, builtin_type, msg) if result: return result # Look up from the 'type' type. return analyse_member_access(name, sig.fallback, node, is_lvalue, is_super, builtin_type, msg, report_type=report_type) elif isinstance(typ, FunctionLike): # Look up from the 'function' type. return analyse_member_access(name, typ.fallback, node, is_lvalue, is_super, builtin_type, msg, report_type=report_type) return msg.has_no_attr(report_type, name, node)
def analyse_member_access(name: str, typ: Type, node: Context, is_lvalue: bool, is_super: bool, basic_types: BasicTypes, msg: MessageBuilder, override_info: TypeInfo = None, report_type: Type = None) -> Type: """Analyse attribute access. This is a general operation that supports various different variations: 1. lvalue or non-lvalue access (i.e. setter or getter access) 2. supertype access (when using super(); is_super == True and override_info should refer to the supertype) """ report_type = report_type or typ if isinstance(typ, Instance): if name == '__init__' and not is_super: # Accessing __init__ in statically typed code would compromise # type safety unless used via super(). msg.fail(messages.CANNOT_ACCESS_INIT, node) return AnyType() # The base object has an instance type. info = typ.type if override_info: info = override_info # Look up the member. First look up the method dictionary. method = info.get_method(name) if method: if is_lvalue: msg.cant_assign_to_method(node) typ = map_instance_to_supertype(typ, method.info) return expand_type_by_instance(method_type(method), typ) else: # Not a method. return analyse_member_var_access(name, typ, info, node, is_lvalue, is_super, msg, report_type=report_type) elif isinstance(typ, AnyType): # The base object has dynamic type. return AnyType() elif isinstance(typ, UnionType): # The base object has dynamic type. msg.disable_type_names += 1 results = [ analyse_member_access(name, subtype, node, is_lvalue, is_super, basic_types, msg) for subtype in typ.items ] msg.disable_type_names -= 1 return UnionType.make_simplified_union(results) elif isinstance(typ, TupleType): # Actually look up from the 'tuple' type. return analyse_member_access(name, basic_types.tuple, node, is_lvalue, is_super, basic_types, msg) elif (isinstance(typ, FunctionLike) and cast(FunctionLike, typ).is_type_obj()): # Class attribute. # TODO super? sig = cast(FunctionLike, typ) itype = cast(Instance, sig.items()[0].ret_type) result = analyse_class_attribute_access(itype, name, node, is_lvalue, msg) if result: return result # Look up from the 'type' type. return analyse_member_access(name, basic_types.type_type, node, is_lvalue, is_super, basic_types, msg, report_type=report_type) elif isinstance(typ, FunctionLike): # Look up from the 'function' type. return analyse_member_access(name, basic_types.function, node, is_lvalue, is_super, basic_types, msg, report_type=report_type) return msg.has_no_attr(report_type, name, node)
itype = (Instance)typ info = itype.type if override_info: info = override_info # Look up the member. First look up the method dictionary. FuncBase method = None if not is_lvalue: method = info.get_method(name) if method: # Found a method. The call below has a unique result for all valid # programs. itype = map_instance_to_supertype(itype, method.info) return expand_type_by_instance(method_type(method), itype) else: # Not a method. return analyse_member_var_access(name, itype, info, node, is_lvalue, is_super, msg) elif isinstance(typ, Any): # The base object has dynamic type. return Any() elif isinstance(typ, TupleType): # Actually look up from the tuple type. return analyse_member_access(name, tuple_type, node, is_lvalue, is_super, tuple_type, msg) elif isinstance(typ, Callable) and ((Callable)typ).is_type_obj(): # Class attribute access. return msg.not_implemented('class attributes', node) else: