Repository: python/mypy

Issue:
Lambda inference
Sorry I didn't look through all issues, so this migh very well be a duplicate.

This should be rejected by MyPy (which is true for pytype btw):

```py
def foo(a : int) -> str:
    return (lambda x: x + 1)(a)
```

`x` in the lambda can only ever be of type `int` anyway, so the result of the whole expression can only be `int` as well, never `str`.


Discussion:
Yes, this seems like a mypy bug. The technical explanation is that mypy doesn't use information from the surrounding call expression as part of the type context for the lambda expression.

Resolving patch:
diff --git a/mypy/checkexpr.py b/mypy/checkexpr.py
--- a/mypy/checkexpr.py
+++ b/mypy/checkexpr.py
@@ -212,7 +212,20 @@ def visit_call_expr(self, e: CallExpr, allow_none_return: bool = False) -> Type:
                     elif typ.node.is_newtype:
                         self.msg.fail(messages.CANNOT_ISINSTANCE_NEWTYPE, e)
         self.try_infer_partial_type(e)
-        callee_type = self.accept(e.callee, always_allow_any=True)
+        if isinstance(e.callee, LambdaExpr):
+            formal_to_actual = map_actuals_to_formals(
+                e.arg_kinds, e.arg_names,
+                e.callee.arg_kinds, e.callee.arg_names,
+                lambda i: self.accept(e.args[i]))
+
+            arg_types = [join.join_type_list([self.accept(e.args[j]) for j in formal_to_actual[i]])
+                         for i in range(len(e.callee.arg_kinds))]
+            type_context = CallableType(arg_types, e.callee.arg_kinds, e.callee.arg_names,
+                                        ret_type=self.object_type(),
+                                        fallback=self.named_type('builtins.function'))
+        else:
+            type_context = None
+        callee_type = self.accept(e.callee, type_context, always_allow_any=True)
         if (self.chk.options.disallow_untyped_calls and
                 self.chk.in_checked_function() and
                 isinstance(callee_type, CallableType)


Test patch:
diff --git a/test-data/unit/check-inference-context.test b/test-data/unit/check-inference-context.test
--- a/test-data/unit/check-inference-context.test
+++ b/test-data/unit/check-inference-context.test
@@ -569,6 +569,22 @@ class A: pass
 class B: pass
 [builtins fixtures/list.pyi]
 
+[case testInferLambdaTypeUsingContext]
+x : str = (lambda x: x + 1)(1)  # E: Incompatible types in assignment (expression has type "int", variable has type "str")
+reveal_type((lambda x, y: x + y)(1, 2))  # E: Revealed type is 'builtins.int'
+(lambda x, y: x + y)(1, "")  # E: Unsupported operand types for + ("int" and "str")
+(lambda *, x, y: x + y)(x=1, y="")  # E: Unsupported operand types for + ("int" and "str")
+reveal_type((lambda s, i: s)(i=0, s='x')) # E: Revealed type is 'builtins.str'
+reveal_type((lambda s, i: i)(i=0, s='x')) # E: Revealed type is 'builtins.int'
+reveal_type((lambda x, s, i: x)(1.0, i=0, s='x')) # E: Revealed type is 'builtins.float'
+(lambda x, s, i: x)() # E: Too few arguments
+(lambda: 0)(1) # E: Too many arguments
+-- varargs are not handled, but it should not crash
+reveal_type((lambda *k, s, i: i)(type, i=0, s='x')) # E: Revealed type is 'Any'
+reveal_type((lambda s, *k, i: i)(i=0, s='x')) # E: Revealed type is 'Any'
+reveal_type((lambda s, i, **k: i)(i=0, s='x')) # E: Revealed type is 'Any'
+[builtins fixtures/dict.pyi]
+
 [case testInferLambdaAsGenericFunctionArgument]
 from typing import TypeVar, List, Any, Callable
 t = TypeVar('t')