@@ -423,6 +423,7 @@ def analyze_type_callable_member_access(name: str, typ: FunctionLike, mx: Member
423423 # the corresponding method in the current instance to avoid this edge case.
424424 # See https://github.com/python/mypy/pull/1787 for more info.
425425 # TODO: do not rely on same type variables being present in all constructor overloads.
426+ check_abstract_class_attribute_access (name , instance_type , mx )
426427 result = analyze_class_attribute_access (
427428 instance_type ,
428429 name ,
@@ -438,6 +439,36 @@ def analyze_type_callable_member_access(name: str, typ: FunctionLike, mx: Member
438439 assert False , f"Unexpected type { instance_type !r} "
439440
440441
442+ def check_abstract_class_attribute_access (
443+ name : str , instance_type : Instance , mx : MemberContext
444+ ) -> None :
445+ """Report accessing an abstract static or class method through its own class.
446+
447+ Only reached for a direct reference to the class. Through `type[T]`, or on an
448+ instance, the runtime object may be a subclass which implements the method,
449+ which is also why instance methods are excluded here.
450+ """
451+ if mx .suppress_errors or not instance_type .type .is_abstract :
452+ return
453+ if all (attr != name for attr , _ in instance_type .type .abstract_attributes ):
454+ return
455+
456+ node = instance_type .type .get (name )
457+ if node is None :
458+ return
459+ func = node .node .func if isinstance (node .node , Decorator ) else node .node
460+ if not isinstance (func , SYMBOL_FUNCBASE_TYPES ):
461+ return
462+
463+ if func .is_static :
464+ kind = "static method"
465+ elif func .is_class :
466+ kind = "class method"
467+ else :
468+ return
469+ mx .msg .cannot_access_abstract_class_attribute (instance_type .type .name , name , kind , mx .context )
470+
471+
441472def analyze_type_type_member_access (
442473 name : str , typ : TypeType , mx : MemberContext , override_info : TypeInfo | None
443474) -> Type :
0 commit comments