Skip to content

Commit 1dd8e7f

Browse files
authored
Substitute type variables in return type of static methods (#16670)
`add_class_tvars` correctly instantiates type variables in the return type for class methods but not for static methods. Check if the analyzed member is a static method in `analyze_class_attribute_access` and substitute the type variable in the return type in `add_class_tvars` accordingly. Fixes #16668.
1 parent 43ffb49 commit 1dd8e7f

File tree

2 files changed

+26
-2
lines changed

2 files changed

+26
-2
lines changed

mypy/checkmember.py

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1063,11 +1063,14 @@ def analyze_class_attribute_access(
10631063
is_classmethod = (is_decorated and cast(Decorator, node.node).func.is_class) or (
10641064
isinstance(node.node, FuncBase) and node.node.is_class
10651065
)
1066+
is_staticmethod = (is_decorated and cast(Decorator, node.node).func.is_static) or (
1067+
isinstance(node.node, FuncBase) and node.node.is_static
1068+
)
10661069
t = get_proper_type(t)
10671070
if isinstance(t, FunctionLike) and is_classmethod:
10681071
t = check_self_arg(t, mx.self_type, False, mx.context, name, mx.msg)
10691072
result = add_class_tvars(
1070-
t, isuper, is_classmethod, mx.self_type, original_vars=original_vars
1073+
t, isuper, is_classmethod, is_staticmethod, mx.self_type, original_vars=original_vars
10711074
)
10721075
if not mx.is_lvalue:
10731076
result = analyze_descriptor_access(result, mx)
@@ -1177,6 +1180,7 @@ def add_class_tvars(
11771180
t: ProperType,
11781181
isuper: Instance | None,
11791182
is_classmethod: bool,
1183+
is_staticmethod: bool,
11801184
original_type: Type,
11811185
original_vars: Sequence[TypeVarLikeType] | None = None,
11821186
) -> Type:
@@ -1195,6 +1199,7 @@ class B(A[str]): pass
11951199
isuper: Current instance mapped to the superclass where method was defined, this
11961200
is usually done by map_instance_to_supertype()
11971201
is_classmethod: True if this method is decorated with @classmethod
1202+
is_staticmethod: True if this method is decorated with @staticmethod
11981203
original_type: The value of the type B in the expression B.foo() or the corresponding
11991204
component in case of a union (this is used to bind the self-types)
12001205
original_vars: Type variables of the class callable on which the method was accessed
@@ -1220,6 +1225,7 @@ class B(A[str]): pass
12201225
t = freshen_all_functions_type_vars(t)
12211226
if is_classmethod:
12221227
t = bind_self(t, original_type, is_classmethod=True)
1228+
if is_classmethod or is_staticmethod:
12231229
assert isuper is not None
12241230
t = expand_type_by_instance(t, isuper)
12251231
freeze_all_type_vars(t)
@@ -1230,7 +1236,12 @@ class B(A[str]): pass
12301236
cast(
12311237
CallableType,
12321238
add_class_tvars(
1233-
item, isuper, is_classmethod, original_type, original_vars=original_vars
1239+
item,
1240+
isuper,
1241+
is_classmethod,
1242+
is_staticmethod,
1243+
original_type,
1244+
original_vars=original_vars,
12341245
),
12351246
)
12361247
for item in t.items

test-data/unit/check-generics.test

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2297,6 +2297,19 @@ def func(x: S) -> S:
22972297
return C[S].get()
22982298
[builtins fixtures/classmethod.pyi]
22992299

2300+
[case testGenericStaticMethodInGenericFunction]
2301+
from typing import Generic, TypeVar
2302+
T = TypeVar('T')
2303+
S = TypeVar('S')
2304+
2305+
class C(Generic[T]):
2306+
@staticmethod
2307+
def get() -> T: ...
2308+
2309+
def func(x: S) -> S:
2310+
return C[S].get()
2311+
[builtins fixtures/staticmethod.pyi]
2312+
23002313
[case testMultipleAssignmentFromAnyIterable]
23012314
from typing import Any
23022315
class A:

0 commit comments

Comments
 (0)