// Copyright 2000-2017 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
package com.jetbrains.python;

import com.intellij.idea.TestFor;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.RecursionManager;
import com.intellij.psi.PsiFile;
import com.jetbrains.python.fixtures.PyTestCase;
import com.jetbrains.python.inspections.PyTypeCheckerInspectionTest;
import com.jetbrains.python.psi.LanguageLevel;
import com.jetbrains.python.psi.PyExpression;
import com.jetbrains.python.psi.types.*;
import com.jetbrains.python.psi.types.PyTypeChecker.GenericSubstitutions;
import org.jetbrains.annotations.NotNull;

import java.util.List;
import java.util.Map;

public class Py3TypeTest extends PyTestCase {
  public static final String TEST_DIRECTORY = "/types/";

  // See PyReferenceExpressionImpl.getQualifiedReferenceType for explanations.
  public void testQualifiedNameResolution() {
    doTest("str", """
      class C:
          def m(self):
              self.t = 5
      
      def f(self: C, x: float):
          self.t = "foo"
          expr = self.t
      """);

    doTest("int", """
      class C:
          def m(self):
              self.t: int = 5
      
      def f(self: C, x: float):
          self.t = "foo"
          expr = self.t
      """);

    doTest("int", """
      class C:
          def __init__(self):
              self.t: int = 5
      
      def f(self: C, x: float):
          self.t = "foo"
          expr = self.t
      """);
  }

  // PY-83047
  public void testQualifiedReferenceTypeNarrowing() {
    doTest("int | None", """
      class C:
          def __init__(self):
              self.t: int | None = 5
      
          def f(self, x: float):
              if x < 0:
                  self.t = None
      
              expr = self.t
      """);

    doTest("int", """
      class C:
          def __init__(self):
              self.t: int | None = 5
      
          def f(self, x: float):
              if self.t is not None:
                  expr = self.t
      """);

    doTest("None", """
      class C:
          def __init__(self):
              self.t: int | None = 5
      
          def f(self, x: float):
              if self.t is None:
                  expr = self.t
      """);

    // Same, but as a separate function

    doTest("int | None", """
      class C:
          def __init__(self):
              self.t: int | None = 5
      
      def f(self: C, x: float):
          if x < 0:
              self.t = None
      
          expr = self.t
      """);

    doTest("int", """
      class C:
          def __init__(self):
              self.t: int | None = 5
      
      def f(self: C, x: float):
          if self.t is not None:
              expr = self.t
      """);

    doTest("None", """
      class C:
          def __init__(self):
              self.t: int | None = 5
      
      def f(self: C, x: float):
          if self.t is None:
              expr = self.t
      """);
  }

  /**
   * Overload signatures for dict.get and dict.pop in builtins.pyi differ slightly,
   * dict.get has default value for "default" parameter. This affect the logic of overload resolution.
   * Therefore it makes sense to test both.
   * <p>
   * <pre>{@code
   * @overload
   * def get(self, key: _KT, default: None = None, /) -> _VT | None: ...
   * # mode overloads...
   * }</pre>
   * <p>
   * <pre>{@code
   * @overload
   * def pop(self, key: _KT, /) -> _VT: ...
   * # mode overloads...
   * }</pre>
   */
  // PY-82818
  public void testGetFromDictWithDefaultNoneValue() {
    doTest("Any | None", """
      d = {}
      expr = d.get("abc", None)""");
  }

  // PY-82818
  public void testPopFromDictWithDefaultNoneValue() {
    doTest("Any", """
      d = {}
      expr = d.pop("abc", None)""");
  }

  // PY-83351
  public void testWhileStatementNarrowing() {
    doTest("int",
           """
             def foo(x: int | None):
                 while x:
                     expr = x
                     x = None
             """);
    doTest("int",
           """
             def foo(x: int | None):
                 while not (not (((not (not x))))):
                     expr = x
                     x = None
             """);
  }

  // PY-83597
  public void testAndExpressionNarrowing() {
    doTest("int", """
      def foo(x: int | None):
          x and (expr := x)
      """);
  }

  // PY-83348
  public void testOrExpressionType() {
    doTest("int | str", """
      def foo(x: int | None):
          expr = x or "foo"
      """);
    doTest("str", """
      def foo(x: None):
          expr = x or "foo"
      """);
  }

  public void testYieldInsideLambda() {
    // Checks that foo is not a generator
    doTest("int", """
      def foo():
          y = lambda x: (yield x)
          return 42
      expr = foo()
      """);
  }

  // PY-21069
  public void testDunderGetattr() {
    doTest("MyClass", """
      class MyClass:
          def __getattr__(self, item) -> 'MyClass':
              pass
      
      expr = MyClass().attr
      """);
  }

  // PY-78964
  public void testFunctionReturnTypeTryFinally() {
    doTest("str",
           """
             def test():
                 try:
                     return 42
                 finally:
                     return "str"
             
                 return True
             
             expr = test()
             """);
  }

  // PY-20710
  public void testLambdaGenerator() {
    doTest("Generator[int, Any, Any]", """
       expr = (lambda: (yield 1))()
      """);
  }

  // PY-20710
  public void testGeneratorDelegatingToLambdaGenerator() {
    doTest("Generator[int, Any, str]", """
       def g():
           yield from (lambda: (yield 1))()
           return "foo"
       expr = g()
      """);
  }

  // PY-20710
  public void testYieldExpressionTypeFromGeneratorSendTypeHint() {
    doTest("int", """
      from typing import Generator
      
      def g() -> Generator[str, int, None]:
          expr = yield "foo"
      """);
  }

  // PY-20710
  public void testYieldFromExpressionTypeFromGeneratorReturnTypeHint() {
    doTest("int", """
      from typing import Generator, Any
      
      def delegate() -> Generator[None, Any, int]:
          yield
          return 42
      
      def g():
          expr = yield from delegate()
      """);
  }

  // PY-20710
  public void testYieldFromLambda() {
    doTest("Generator[int | str, str | Any, bool]",
           """
             from typing import Generator
             
             def gen1() -> Generator[int, str, bool]:
                 yield 42
                 return True
             
             def gen2():
                 yield "str"
                 return True
             
             l = lambda: (yield from gen1()) or (yield from gen2())
             expr = l()
             """);
  }

  //// PY-76659
  //public void testRecursiveResolve() {
  //  doTest("int",
  //         """
  //           x = 42
  //           while x:
  //               x = x + 1
  //           expr = x""");
  //}
  //
  //// PY-76659
  //public void testRecursiveResolve2() {
  //  doTest("int",
  //         """
  //           x = 42
  //           b: bool = ...
  //           while x:
  //               if b:
  //                   x = x + 1
  //                   expr = x
  //               else:
  //                   x = x - 1
  //           """);
  //}
  //
  //// PY-76659
  //public void testDeclareAfterUse() {
  //  doTest("int | Any",
  //         """
  //           from typing import Any, TypeGuard
  //
  //           def is_positive_integer(value: Any) -> TypeGuard[int]:
  //               return isinstance(value, int) and value > 0
  //
  //           def bar() -> object:
  //               return 321
  //
  //           def foo():
  //               for i in range(1, 100):
  //                   if i > 1:
  //                       expr = x
  //                   x = bar()
  //                   if not is_positive_integer(x):
  //                       break
  //           """);
  //}
  //

  /// / PY-76659
  //public void testClassChain() {
  //  doTest("B | C | D | A",
  //         """
  //           class A:
  //               def bar() -> "B":
  //                   return B()
  //           class B:
  //               def bar() -> "C":
  //                   return C()
  //           class C:
  //               def bar() -> "D":
  //                   return D()
  //           class D:
  //               def bar() -> A:
  //                   return A()
  //
  //           def foo(b):
  //               x = A()
  //               while b:
  //                   x = x.bar()
  //
  //               expr = x""");
  //}

  // PY-6702
  public void testYieldFromType() {
    doTest("str | int | float",
           """
             def subgen():
                 for i in [1, 2, 3]:
                     yield i
             
             def gen():
                 yield 'foo'
                 yield from subgen()
                 yield 3.14
             
             for expr in gen():
                 pass
             """);
  }

  // PY-12944
  public void testYieldFromReturnType() {
    doTest("None",
           """
             def a():
                 yield 1
                 return 'a'
             
             y = [1, 2, 3]
             
             def b():
                 expr = yield from y
                 return expr
             """);
    doTest("str",
           """
             def a():
                 yield 1
                 return 'a'
             
             def b():
                 expr = yield from a()
                 return expr
             """);
    doTest("int",
           """
             def g():
                 yield 1
                 return 'abc'
             
             def f()
                 x = yield from g()
             
             for expr in f():
                 pass""");
  }

  public void testYieldFromHomogeneousTuple() {
    doTest("str",
           """
             import typing
             def get_tuple() -> typing.Tuple[str, ...]:
                 pass
             def gen()
                 yield from get_tuple()
             for expr in gen():    pass""");
  }

  public void testYieldFromHeterogeneousTuple() {
    doTest("int | str",
           """
             import typing
             def get_tuple() -> typing.Tuple[int, int, str]:
                 pass
             def gen()
                 yield from get_tuple()
             for expr in gen():    pass""");
  }

  public void testYieldFromUnknownTuple() {
    doTest("Any",
           """
             def get_tuple() -> tuple:
                 pass
             def gen()
                 yield from get_tuple()
             for expr in gen():    pass""");
  }

  public void testYieldFromUnknownList() {
    doTest("Any",
           """
             def get_list() -> list:
                 pass
             def gen()
                 yield from get_list()
             for expr in gen():    pass""");
  }

  public void testYieldFromUnknownDict() {
    doTest("Any",
           """
             def get_dict() -> dict:
                 pass
             def gen()
                 yield from get_dict()
             for expr in gen():    pass""");
  }

  public void testYieldFromUnknownSet() {
    doTest("Any",
           """
             def get_set() -> set:
                 pass
             def gen()
                 yield from get_set()
             for expr in gen():    pass""");
  }

  public void testAwaitAwaitable() {
    doTest("int",
           """
             class C:
                 def __await__(self):
                     yield 'foo'
                     return 0
             
             async def foo():
                 c = C()
                 expr = await c
             """);
  }

  public void testAsyncDefReturnType() {
    doTest("Coroutine[Any, Any, int]",
           """
             async def foo(x):
                 await x
                 return 0
             
             def bar(y):
                 expr = foo(y)
             """);
  }

  public void testAwaitCoroutine() {
    doTest("int",
           """
             async def foo(x):
                 await x
                 return 0
             
             async def bar(y):
                 expr = await foo(y)
             """);
  }

  // Not in PEP 484 as for now, see https://github.com/ambv/typehinting/issues/119
  public void testCoroutineReturnTypeAnnotation() {
    doTest("int",
           """
             async def foo() -> int: ...
             
             async def bar():
                 expr = await foo()
             """);
  }

  // PY-16987
  public void testNoTypeInGoogleDocstringParamAnnotation() {
    doTest("int", """
      def f(x: int):
          ""\"
          Args:
              x: foo
          ""\"   \s
          expr = x""");
  }

  // PY-16987
  public void testUnfilledTypeInGoogleDocstringParamAnnotation() {
    doTest("int", """
      def f(x: int):
          ""\"
          Args:
              x (): foo
          ""\"   \s
          expr = x""");
  }

  // PY-16987
  public void testNoTypeInNumpyDocstringParamAnnotation() {
    doTest("int", """
      def f(x: int):
          ""\"
          Parameters
          ----------
          x
              foo
          ""\"
          expr = x""");
  }

  // PY-17010
  public void testAnnotatedReturnTypePrecedesDocstring() {
    doTest("int", """
      def func() -> int:
          ""\"
          Returns:
              str
          ""\"
      expr = func()""");
  }

  // PY-17010
  public void testAnnotatedParamTypePrecedesDocstring() {
    doTest("int", """
      def func(x: int):
          ""\"
          Args:
              x (str):
          ""\"
          expr = x""");
  }

  public void testOpenDefault() {
    doTest("TextIOWrapper[_WrappedBuffer]",
           "expr = open('foo')\n");
  }

  public void testOpenText() {
    doTest("TextIOWrapper[_WrappedBuffer]",
           "expr = open('foo', 'r')\n");
  }

  public void testOpenBinary() {
    doTest("BufferedReader[_BufferedReaderStream]",
           "expr = open('foo', 'rb')\n");
  }

  public void testIoOpenDefault() {
    doTest("TextIOWrapper[_WrappedBuffer]",
           """
             import io
             expr = io.open('foo')
             """);
  }

  public void testIoOpenText() {
    doTest("TextIOWrapper[_WrappedBuffer]",
           """
             import io
             expr = io.open('foo', 'r')
             """);
  }

  public void testIoOpenBinary() {
    doTest("BufferedReader[_BufferedReaderStream]",
           """
             import io
             expr = io.open('foo', 'rb')
             """);
  }

  // PY-1427
  public void testBytesLiteral() {
    doTest("bytes", "expr = b'foo'");
  }

  // PY-20770
  public void testAsyncGenerator() {
    doTest("AsyncGenerator[int, Any]",
           """
             async def asyncgen():
                 yield 42
             expr = asyncgen()""");
  }

  // PY-20770
  public void testAsyncGeneratorDunderAiter() {
    doTest("AsyncIterator[int]",
           """
             async def asyncgen():
                 yield 42
             expr = asyncgen().__aiter__()""");
  }

  // PY-20770
  public void testAsyncGeneratorDunderAnext() {
    doTest("Coroutine[Any, Any, int]",
           """
             async def asyncgen():
                 yield 42
             expr = asyncgen().__anext__()""");
  }

  // PY-20770
  public void testAsyncGeneratorAwaitOnDunderAnext() {
    doTest("int",
           """
             async def asyncgen():
                 yield 42
             async def asyncusage()
                 expr = await asyncgen().__anext__()""");
  }

  // PY-20770
  public void testAsyncGeneratorAsend() {
    doTest("Coroutine[Any, Any, int]",
           """
             async def asyncgen():
                 yield 42
             expr = asyncgen().asend("hello")""");
  }

  // PY-20770
  public void testAsyncGeneratorAwaitOnAsend() {
    doTest("int",
           """
             async def asyncgen():
                 yield 42
             async def asyncusage():
                 expr = await asyncgen().asend("hello")""");
  }

  // PY-20770
  public void testIteratedAsyncGeneratorElement() {
    doTest("int",
           """
             async def asyncgen():
                 yield 10
             async def run():
                 async for i in asyncgen():
                     expr = i""");
  }

  // PY-20770
  public void testElementInAsyncComprehensions() {
    doTest("int",
           """
             async def asyncgen():
                 yield 10
             async def run():
                 {expr async for expr in asyncgen()}
             """);

    doTest("int",
           """
             async def asyncgen():
                 yield 10
             async def run():
                 [expr async for expr in asyncgen()]
             """);

    doTest("int",
           """
             async def asyncgen():
                 yield 10
             async def run():
                 {expr: expr ** 2 async for expr in asyncgen()}
             """);

    doTest("int",
           """
             async def asyncgen():
                 yield 10
             async def run():
                 (expr async for expr in asyncgen())
             """);

    doTest("int",
           """
             async def asyncgen():
                 yield 10
             async def run():
                 list(expr async for expr in asyncgen())
             """);

    doTest("int",
           """
             async def asyncgen():
                 yield 10
             async def run():
                 dataset = {data async for expr in asyncgen()
                                 async for data in asyncgen()
                                 if check(data)}
             """);

    doTest("int",
           """
             async def asyncgen():
                 yield 10
             async def run():
                 dataset = {expr async for line in asyncgen()
                                 async for expr in asyncgen()
                                 if check(expr)}
             """);
  }

  // PY-20770
  public void testAwaitInComprehensions() {
    doTest("list[int]",
           """
             async def asyncgen():
                 yield 10
             async def run():
                 expr = [await z for z in [asyncgen().__anext__()]]
             """);
  }

  // PY-20770
  public void testAwaitInAsyncComprehensions() {
    doTest("list[int]",
           """
             async def asyncgen():
                 yield 10
             async def asyncgen2():
                 yield asyncgen().__anext__()
             async def run():
                 expr = [await z async for z in asyncgen2()]
             """);
  }

  public void testIsNotNone() {
    doTest("int",
           """
             def test_1(self, c):
                 x = 1 if c else None
                 if x is not None:
                     expr = x
             """);

    doTest("int",
           """
             def test_1(self, c):
                 x = 1 if c else None
                 if None is not x:
                     expr = x
             """);

    doTest("int",
           """
             def test_1(self, c):
                 x = 1 if c else None
                 if not x is None:
                     expr = x
             """);

    doTest("int",
           """
             def test_1(self, c):
                 x = 1 if c else None
                 if not None is x:
                     expr = x
             """);
  }

  public void testIsNone() {
    doTest("None",
           """
             def test_1(self, c):
                 x = 1 if c else None
                 if x is None:
                     expr = x
             """);

    doTest("None",
           """
             def test_1(self, c):
                 x = 1 if c else None
                 if None is x:
                     expr = x
             """);
  }

  public void testIsEnumMember() {
    doTest("Literal[Answer.Yes, Answer.No]",
           """
             from enum import Enum
             
             class Answer(Enum):
                 Yes = 1
                 No = 2
             
             def foo(v: object):
                 if v is Answer.Yes or v is Answer.No:
                     expr = v
             """);
    doTest("Literal[Answer.Yes, Answer.No]",
           """
             from enum import Enum
             
             class Answer(Enum):
                 Yes = 1
                 No = 2
             
             def foo(v: object):
                 if v is not Answer.Yes and v is not Answer.No:
                     raise ValueError("Invalid value")
                 expr = v
             """);
    doTest("Literal[Answer.Yes, Answer.No]",
           """
             from enum import Enum
             
             class Answer(Enum):
                 Yes = 1
                 No = 2
             
             def foo(v: object):
                 assert v is Answer.Yes or v is Answer.No
                 expr = v
             """);
  }

  public void testLiteralTypeNarrowingEquals() {
    doTest("Literal[\"abba\"]",
           """
             from typing import Literal
             def foo(v: str):
                 if (v == "abba"):
                     expr = v
             """);
    doTest("Literal[\"ab\"]",
           """
             from typing import Literal
             def foo(v: Literal["abba", "ab"]):
                 if (v != "abba"):
                     expr = v
             """);
    doTest("Literal[\"abc\"]",
           """
             from typing import Literal
             abc: Literal["abc"] = "abc"
             def foo(v: str):
                 if (v == abc):
                     expr = v
             """);
    doTest("Literal[\"abba\"]",
           """
             from typing import Literal
             if ((v := input()) == "abba"):
                 expr = v
             """);
  }

  public void testLiteralTypeNarrowingIn() {
    doTest("Literal[1, 2]",
           """
             def f(a: int):
                 if a in (1, 2, ""):
                     expr = a
             """);
    doTest("Literal[1, 2]",
           """
             def f(a: int):
                 if a in {1, 2, ""}:
                     expr = a
             """);
    doTest("Literal[1, 2]",
           """
             def f(a: int):
                 if a in [1, 2, ""]:
                     expr = a
             """);
    doTest("Literal[-10, \"a\"]",
           """
             from enum import Enum
             class E(Enum):
                 A = 1
             def f(a: int | str):
                 if a in (-10, E.A, "a"):
                     expr = a
             """);
    doTest("Literal[\"abb\"]",
           """
             from typing import Literal
             def f(a: Literal[3, "abb", "ab", False]):
                 if a in ("abb", True):
                     expr = a
             """);
    doTest("Literal[3, \"ab\"]",
           """
             from typing import Literal
             def f(a: Literal[3, "abb", "ab", False]):
                 if a not in ("abb", False):
                     expr = a
             """);
    doTest("Literal[\"abb\", False]",
           """
             from typing import Literal
             def f(a: Literal[10, "abb", "ab", False]):
                 if a not in ("abb", False):
                     pass
                 else:
                     expr = a
             """);
    doTest("Literal[-1] | None",
           """
             def f(v: object):
                 if v in (-1, None):
                     expr = v
             """);
    doTest("Literal[\"abba\"]",
           """
             from typing import Literal
             if (a := input()) in ("abba", False):
                 expr = a
             """);
  }

  // PY-21083
  public void testFloatFromhex() {
    doTest("float",
           "expr = float.fromhex(\"0.5\")");
  }

  // PY-20073
  public void testMapReturnType() {
    doTest("int",
           "for x in map(lambda x: 42, 'foo'):\n" +
           "    expr = x");
  }

  // PY-20757
  public void testMinElseNone() {
    doTest("Any | None",
           """
             def get_value(v):
                 if v:
                     return min(v)
                 else:
                     return None
             expr = get_value([])""");
  }

  // PY-21350
  public void testBuiltinInput() {
    doTest("str",
           "expr = input()");
  }

  public void testMinResult() {
    doTest("int",
           "expr = min(1, 2, 3)");
  }

  public void testMaxResult() {
    doTest("int",
           "expr = max(1, 2, 3)");
  }

  // PY-21692
  public void testSumResult() {
    doTest("int",
           "expr = sum([1, 2, 3])");
  }

  public void testNumpyResolveRaterDoesNotIncreaseRateForNotNdarrayRightOperatorFoundInStub() {
    myFixture.copyDirectoryToProject(TEST_DIRECTORY + getTestName(false), "");
    doTest("D1 | D2",
           """
             class D1(object):
                 pass
             class D2(object):
                 pass
             expr = D1() / D2()""");
  }

  // PY-22181
  public void testIterationOverIterableWithSeparateIterator() {
    doTest("int",
           """
             class AIter(object):
                 def __next__(self):
                     return 5
             class A(object):
                 def __iter__(self):
                     return AIter()
             a = A()
             for expr in a:
                 print(expr)""");
  }

  // PY-22181
  public void testAsyncIterationOverIterableWithSeparateIterator() {
    doTest("int",
           """
             class AIter(object):
                 async def __anext__(self):
                     return 5
             class A(object):
                 def __aiter__(self):
                     return AIter()
             a = A()
             async for expr in a:
                 print(expr)""");
  }

  // PY-60714
  public void testAsyncIteratorUnwrapsCoroutineFromAnext() {
    doTest("bytes", """
      class AIterator:
          def __aiter__(self):
              return self
      
          async def __anext__(self) -> bytes:
              return b"a"
      
      async for expr in AIterator():
          print(expt)
      """);
  }

  // PY-41061
  public void testForIterationOverObjectWithIterAndAiter() {
    doTest("int",
           """
             from collections.abc import Iterator, AsyncIterator
             
             class MyIterable:
                 def __iter__(self) -> Iterator[int]: ...
             
                 def __aiter__(self) -> AsyncIterator[str]: ...
             
             for expr in MyIterable(): ...""");

    doTest("int",
           """
             from collections.abc import Iterator, AsyncIterator
             
             class MyIterable:
                 def __iter__(self) -> Iterator[int]: ...
             
                 def __aiter__(self) -> AsyncIterator[str]: ...
             
             _ = [expr for expr in MyIterable()]""");
  }

  // PY-41061
  public void testAsyncForIterationOverObjectWithIterAndAiter() {
    doTest("str",
           """
             from collections.abc import Iterator, AsyncIterator
             
             class MyIterable:
                 def __iter__(self) -> Iterator[int]: ...
             
                 def __aiter__(self) -> AsyncIterator[str]: ...
             
             async def foo():
                 async for expr in MyIterable(): ...""");

    doTest("str",
           """
             from collections.abc import Iterator, AsyncIterator
             
             class MyIterable:
                 def __iter__(self) -> Iterator[int]: ...
             
                 def __aiter__(self) -> AsyncIterator[str]: ...
             
             async def foo():
                 _ = [expr async for expr in MyIterable()]""");
  }

  // PY-21655
  public void testUsageOfFunctionDecoratedWithAsyncioCoroutine() {
    myFixture.copyDirectoryToProject(TEST_DIRECTORY + getTestName(false), "");
    doTest("int",
           """
             import asyncio
             @asyncio.coroutine
             def foo():
                 yield from asyncio.sleep(1)
                 return 3
             async def bar():
                 expr = await foo()
                 return expr""");
  }

  // PY-21655
  public void testUsageOfFunctionDecoratedWithTypesCoroutine() {
    myFixture.copyDirectoryToProject(TEST_DIRECTORY + getTestName(false), "");
    doTest("int",
           """
             import asyncio
             import types
             @types.coroutine
             def foo():
                 yield from asyncio.sleep(1)
                 return 3
             async def bar():
                 expr = await foo()
                 return expr""");
  }

  // PY-22513
  public void testGenericKwargs() {
    doTest("dict[str, int | str]",
           """
             from typing import Any, Dict, TypeVar
             
             T = TypeVar('T')
             
             def generic_kwargs(**kwargs: T) -> Dict[str, T]:
                 pass
             
             expr = generic_kwargs(a=1, b='foo')
             """);
  }

  // PY-19323
  public void testReturnedTypingCallable() {
    doTest("(...) -> Any",
           """
             from typing import Callable
             def f() -> Callable:
                 pass
             expr = f()""");
  }

  public void testReturnedTypingCallableWithUnknownParameters() {
    doTest("(...) -> int",
           """
             from typing import Callable
             def f() -> Callable[..., int]:
                 pass
             expr = f()""");
  }

  public void testReturnedTypingCallableWithKnownParameters() {
    doTest("(int, str) -> int",
           """
             from typing import Callable
             def f() -> Callable[[int, str], int]:
                 pass
             expr = f()""");
  }

  // PY-81606
  public void testCallable() {
    doTest("(x: int, /, s: str, *, k: bytes) -> None",
           """
             def func(x: int, /, s: str, *, k: bytes) -> None:
                 pass
             expr = func""");
  }

  // PY-24445
  public void testIsSubclassInsideListComprehension() {
    doTest("list[type[A]]",
           "class A: pass\n" +
           "expr = [e for e in [] if issubclass(e, A)]");
  }

  public void testIsInstanceInsideListComprehension() {
    doTest("list[A]",
           "class A: pass\n" +
           "expr = [e for e in [] if isinstance(e, A)]");
  }

  // PY-24405
  public void testAsyncWithType() {
    doTest("str",
           """
             class AContext:
                 async def __aenter__(self) -> str:
                     pass
             async def foo():
                 async with AContext() as c:
                     expr = c""");
  }

  // PY-24067
  public void testAsyncFunctionReturnTypeInDocstring() {
    doTest("Coroutine[Any, Any, int]",
           """
             async def f():
                 ""\"
                 :rtype: int
                 ""\"
                 pass
             expr = f()""");
  }

  // PY-27518
  public void testAsyncFunctionReturnTypeInNumpyDocstring() {
    doTest("Coroutine[Any, Any, int]",
           """
             async def f():
                 ""\"
                 An integer.
             
                 Returns
                 -------
                 int
                     A number
                 ""\"
                 pass
             expr = f()""");
  }

  // PY-26847
  public void testAwaitOnImportedCoroutine() {
    doMultiFileTest("Any",
                    """
                      from mycoroutines import mycoroutine
                      
                      async def main():
                          expr = await mycoroutine()""");
  }

  // PY-26643
  public void testReplaceSelfInCoroutine() {
    doTest("Coroutine[Any, Any, B]",
           """
             class A:
                 async def foo(self):
                     return self
             class B(A):
                 pass
             expr = B().foo()""");
  }

  // PY-4813
  public void testParameterTypeInferenceInSubclassFromDocstring() {
    doTest("int",
           """
             class Base:
                 def test(self, param):
                     ""\"
                     :param param:
                     :type param: int
                     ""\"
                     pass
             
             class Subclass(Base):
                 def test(self, param):
