Repository: python/mypy

Issue:
"Cannot determine type of ..." when using decorators
**Bug Report**

**To Reproduce**

Content of `tmp.py`

```python
from typing import Callable, TypeVar

C = TypeVar('C', bound=Callable)

def dec(val: None) -> Callable[[C], C]:
    def wrapper(f):
        return f
    return wrapper

# E: Cannot determine type of "foo"  [has-type]
lambda: foo() == 2

@dec(None)
def foo() -> int:
    return 2

foo() == 2  # this one is fine
```

Running mypy:

```
$ mypy --config-file='' tmp.py 
tmp.py:13: error: Cannot determine type of "foo"
```

**Your Environment**

- Mypy version used: 0.910
- Mypy command-line flags: --
- Mypy configuration options from `mypy.ini` (and other config files): --
- Python version used: Python 3.9.0
- Operating system and version: `5.4.0-81-generic #91-Ubuntu SMP Thu Jul 15 19:09:17 UTC 2021 x86_64 x86_64 x86_64 GNU/Linux`



Discussion:
Surprisingly, with a regular identity as a decorator it works fine:

```python
from typing import Callable, TypeVar

C = TypeVar('C', bound=Callable)

def dec(f: C) -> C:
    return f

lambda: foo() == 2

@dec
def foo() -> int:
    return 2
```

Another surprising discovery: it also works fine when both the function and the lambda are enclosed:

```python
from typing import Callable, TypeVar

C = TypeVar('C', bound=Callable)

def dec(val: None) -> Callable[[C], C]:
    def wrapper(f):
        return f
    return wrapper

def f() -> None:
    lambda: foo() == 2

    @dec(None)
    def foo() -> int:
        return 2
```

If you're wondering what can be a use case, I'm making a decorator for providing function usage examples for [deal](https://github.com/life4/deal):

```python
@deal.example(lambda: double1(2) == 4)
def double1(x: int) -> int:
    return x * 2
```

Here, lambda is passed as an argument into the decorator and it produces the same error as in the issue description.
@orsinium Hi 👋 😊 Glad to see you here (but, I am sorry that something does not work for you!)

I will try to solve this, I have some ideas about the possible cause, but no promises here: inference is always quite hard and any changes there can have massive influence on other features.
I'm glad to see you there too :) When you mentioned inference, I made another surprising discovery: it also works well when I use a function instead of a lambda:

```python
from typing import Callable, TypeVar

C = TypeVar('C', bound=Callable)

def dec(val: None) -> Callable[[C], C]:
    def wrapper(f):
        return f
    return wrapper

def f() -> None:
    foo() == 2

@dec(None)
def foo() -> int:
    return 2
```

What an interesting case! Something very specific to lambdas in a global namespace, hmm.

Resolving patch:
diff --git a/mypy/checker.py b/mypy/checker.py
--- a/mypy/checker.py
+++ b/mypy/checker.py
@@ -389,8 +389,8 @@ def defer_node(self, node: DeferredNodeType, enclosing_class: Optional[TypeInfo]
         self.deferred_nodes.append(DeferredNode(node, enclosing_class))
 
     def handle_cannot_determine_type(self, name: str, context: Context) -> None:
-        node = self.scope.top_non_lambda_function()
-        if self.pass_num < self.last_pass and isinstance(node, FuncDef):
+        node = self.scope.top_function()
+        if self.pass_num < self.last_pass and isinstance(node, (FuncDef, LambdaExpr)):
             # Don't report an error yet. Just defer. Note that we don't defer
             # lambdas because they are coupled to the surrounding function
             # through the binder and the inferred type of the lambda, so it
diff --git a/mypy/checkexpr.py b/mypy/checkexpr.py
--- a/mypy/checkexpr.py
+++ b/mypy/checkexpr.py
@@ -3876,7 +3876,13 @@ def visit_conditional_expr(self, e: ConditionalExpr, allow_none_return: bool = F
     def analyze_cond_branch(self, map: Optional[Dict[Expression, Type]],
                             node: Expression, context: Optional[Type],
                             allow_none_return: bool = False) -> Type:
-        with self.chk.binder.frame_context(can_skip=True, fall_through=0):
+        # We need to be have the correct amount of binder frames.
+        # Sometimes it can be missing for unreachable parts.
+        with (
+            self.chk.binder.frame_context(can_skip=True, fall_through=0)
+            if len(self.chk.binder.frames) > 1
+            else self.chk.binder.top_frame_context()
+        ):
             if map is None:
                 # We still need to type check node, in case we want to
                 # process it for isinstance checks later


Test patch:
diff --git a/test-data/unit/check-callable.test b/test-data/unit/check-callable.test
--- a/test-data/unit/check-callable.test
+++ b/test-data/unit/check-callable.test
@@ -301,6 +301,28 @@ def f(t: T) -> A:
 
 [builtins fixtures/callable.pyi]
 
+[case testCallableTypeVarBoundAndLambdaDefer]
+# See https://github.com/python/mypy/issues/11212
+from typing import Callable, TypeVar
+
+C = TypeVar('C', bound=Callable)
+
+def dec(val: None) -> Callable[[C], C]:
+    def wrapper(f):
+        return f
+    return wrapper
+
+lambda: foo() + 2  # error was here
+
+@dec(None)
+def foo() -> int:
+    return 2
+
+lambda: foo() + 2  # double check
+
+reveal_type(foo() + 2)  # N: Revealed type is "builtins.int"
+[builtins fixtures/callable.pyi]
+
 [case testCallableTypeUnion]
 from abc import ABCMeta, abstractmethod
 from typing import Type, Union
diff --git a/test-data/unit/check-optional.test b/test-data/unit/check-optional.test
--- a/test-data/unit/check-optional.test
+++ b/test-data/unit/check-optional.test
@@ -738,8 +738,11 @@ class A:
 
     def f(self, x: Optional['A']) -> None:
         assert x
-        lambda: (self.y, x.a) # E: Cannot determine type of "y"
+        lambda: (self.y, x.a)
         self.y = int()
+[out]
+main:8: error: Cannot determine type of "y"
+main:8: error: Item "None" of "Optional[A]" has no attribute "a"
 [builtins fixtures/isinstancelist.pyi]
 
 [case testDeferredAndOptionalInferenceSpecialCase]