Skip to content

Commit d37c2be

Browse files
authored
Now lambda is counted as a valid context in handle_cannot_determine_type (#11215)
Closes #11212 Previously `mypy` was ignoring `lambda` scope in `handle_cannot_determine_type`. I guess it was done to fix some `lambda` related crashes. I've also noticed one crash with this new solution in existing test. I happened because when right `or` part is unreachable, there's not `binder.frame` for it. So, I've made a temporary scope of it.
1 parent e0b4b05 commit d37c2be

File tree

4 files changed

+35
-4
lines changed

4 files changed

+35
-4
lines changed

mypy/checker.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -389,8 +389,8 @@ def defer_node(self, node: DeferredNodeType, enclosing_class: Optional[TypeInfo]
389389
self.deferred_nodes.append(DeferredNode(node, enclosing_class))
390390

391391
def handle_cannot_determine_type(self, name: str, context: Context) -> None:
392-
node = self.scope.top_non_lambda_function()
393-
if self.pass_num < self.last_pass and isinstance(node, FuncDef):
392+
node = self.scope.top_function()
393+
if self.pass_num < self.last_pass and isinstance(node, (FuncDef, LambdaExpr)):
394394
# Don't report an error yet. Just defer. Note that we don't defer
395395
# lambdas because they are coupled to the surrounding function
396396
# through the binder and the inferred type of the lambda, so it

mypy/checkexpr.py

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3876,7 +3876,13 @@ def visit_conditional_expr(self, e: ConditionalExpr, allow_none_return: bool = F
38763876
def analyze_cond_branch(self, map: Optional[Dict[Expression, Type]],
38773877
node: Expression, context: Optional[Type],
38783878
allow_none_return: bool = False) -> Type:
3879-
with self.chk.binder.frame_context(can_skip=True, fall_through=0):
3879+
# We need to be have the correct amount of binder frames.
3880+
# Sometimes it can be missing for unreachable parts.
3881+
with (
3882+
self.chk.binder.frame_context(can_skip=True, fall_through=0)
3883+
if len(self.chk.binder.frames) > 1
3884+
else self.chk.binder.top_frame_context()
3885+
):
38803886
if map is None:
38813887
# We still need to type check node, in case we want to
38823888
# process it for isinstance checks later

test-data/unit/check-callable.test

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -301,6 +301,28 @@ def f(t: T) -> A:
301301

302302
[builtins fixtures/callable.pyi]
303303

304+
[case testCallableTypeVarBoundAndLambdaDefer]
305+
# See https://github.com/python/mypy/issues/11212
306+
from typing import Callable, TypeVar
307+
308+
C = TypeVar('C', bound=Callable)
309+
310+
def dec(val: None) -> Callable[[C], C]:
311+
def wrapper(f):
312+
return f
313+
return wrapper
314+
315+
lambda: foo() + 2 # error was here
316+
317+
@dec(None)
318+
def foo() -> int:
319+
return 2
320+
321+
lambda: foo() + 2 # double check
322+
323+
reveal_type(foo() + 2) # N: Revealed type is "builtins.int"
324+
[builtins fixtures/callable.pyi]
325+
304326
[case testCallableTypeUnion]
305327
from abc import ABCMeta, abstractmethod
306328
from typing import Type, Union

test-data/unit/check-optional.test

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -738,8 +738,11 @@ class A:
738738

739739
def f(self, x: Optional['A']) -> None:
740740
assert x
741-
lambda: (self.y, x.a) # E: Cannot determine type of "y"
741+
lambda: (self.y, x.a)
742742
self.y = int()
743+
[out]
744+
main:8: error: Cannot determine type of "y"
745+
main:8: error: Item "None" of "Optional[A]" has no attribute "a"
743746
[builtins fixtures/isinstancelist.pyi]
744747

745748
[case testDeferredAndOptionalInferenceSpecialCase]

0 commit comments

Comments
 (0)