Skip to content

Commit 8384628

Browse files
committed
Consistently defer methods in nested classes
1 parent aa9393e commit 8384628

File tree

2 files changed

+29
-6
lines changed

2 files changed

+29
-6
lines changed

mypy/checker.py

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -561,7 +561,7 @@ def handle_cannot_determine_type(self, name: str, context: Context) -> None:
561561
# lambdas because they are coupled to the surrounding function
562562
# through the binder and the inferred type of the lambda, so it
563563
# would get messy.
564-
enclosing_class = self.scope.enclosing_class()
564+
enclosing_class = self.scope.enclosing_class(node)
565565
self.defer_node(node, enclosing_class)
566566
# Set a marker so that we won't infer additional types in this
567567
# function. Any inferred types could be bogus, because there's at
@@ -2153,7 +2153,14 @@ def check_method_override_for_base_with_name(
21532153
if self.pass_num < self.last_pass:
21542154
# If there are passes left, defer this node until next pass,
21552155
# otherwise try reconstructing the method type from available information.
2156-
self.defer_node(defn, defn.info)
2156+
# For consistency, defer an enclosing top-level function (if any).
2157+
top_level = self.scope.top_level_function()
2158+
if isinstance(top_level, FuncDef):
2159+
self.defer_node(top_level, self.scope.enclosing_class(top_level))
2160+
else:
2161+
# Specify enclosing class explicitly, as we check type override before
2162+
# entering e.g. decorators or overloads.
2163+
self.defer_node(defn, defn.info)
21572164
return True
21582165
elif isinstance(original_node, (FuncDef, OverloadedFuncDef)):
21592166
original_type = self.function_type(original_node)
@@ -8558,6 +8565,7 @@ def current_function(self) -> FuncItem | None:
85588565
return None
85598566

85608567
def top_level_function(self) -> FuncItem | None:
8568+
"""Return top-level non-lambda function."""
85618569
for e in self.stack:
85628570
if isinstance(e, FuncItem) and not isinstance(e, LambdaExpr):
85638571
return e
@@ -8568,11 +8576,11 @@ def active_class(self) -> TypeInfo | None:
85688576
return self.stack[-1]
85698577
return None
85708578

8571-
def enclosing_class(self) -> TypeInfo | None:
8579+
def enclosing_class(self, func: FuncItem | None = None) -> TypeInfo | None:
85728580
"""Is there a class *directly* enclosing this function?"""
8573-
top = self.current_function()
8574-
assert top, "This method must be called from inside a function"
8575-
index = self.stack.index(top)
8581+
func = func or self.current_function()
8582+
assert func, "This method must be called from inside a function"
8583+
index = self.stack.index(func)
85768584
assert index, "CheckerScope stack must always start with a module"
85778585
enclosing = self.stack[index - 1]
85788586
if isinstance(enclosing, TypeInfo):

test-data/unit/check-inference.test

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3930,3 +3930,18 @@ def deco(fn: Callable[[], T]) -> Callable[[], T]: ...
39303930

39313931
@deco
39323932
def defer() -> int: ...
3933+
3934+
[case testDeferMethodOfNestedClass]
3935+
from typing import Optional, Callable, TypeVar
3936+
3937+
class Out:
3938+
def meth(self) -> None:
3939+
class In:
3940+
def meth(self) -> None:
3941+
reveal_type(defer()) # N: Revealed type is "builtins.int"
3942+
3943+
T = TypeVar("T")
3944+
def deco(fn: Callable[[], T]) -> Callable[[], T]: ...
3945+
3946+
@deco
3947+
def defer() -> int: ...

0 commit comments

Comments
 (0)