diff --git a/python/resources/inspectionDescriptions/PyAsyncCallInspection.html b/python/resources/inspectionDescriptions/PyAsyncCallInspection.html
new file mode 100644
index 000000000000..9bc2d4281a40
--- /dev/null
+++ b/python/resources/inspectionDescriptions/PyAsyncCallInspection.html
@@ -0,0 +1,5 @@
+
+
+This inspection highlights coroutines which were called without await
+
+
\ No newline at end of file
diff --git a/python/src/META-INF/python-core-common.xml b/python/src/META-INF/python-core-common.xml
index 6694ab60ad24..9b3ad71c3301 100644
--- a/python/src/META-INF/python-core-common.xml
+++ b/python/src/META-INF/python-core-common.xml
@@ -430,6 +430,7 @@
+
diff --git a/python/src/com/jetbrains/python/PyBundle.properties b/python/src/com/jetbrains/python/PyBundle.properties
index 192a7e3b1c1d..7fbf6c32f5fb 100644
--- a/python/src/com/jetbrains/python/PyBundle.properties
+++ b/python/src/com/jetbrains/python/PyBundle.properties
@@ -603,6 +603,8 @@ INSP.NAME.global.$0.undefined=Global variable ''{0}'' is undefined at the module
INSP.NAME.assignment.to.loop.or.with.parameter.display.name=Assignment to 'for' loop or 'with' statement parameter
INSP.NAME.assignment.to.loop.or.with.parameter.display.message=Variable ''{0}'' already declared in ''for'' loop or ''with'' statement above
+#PyAsyncCallInspection
+INSP.NAME.coroutine.is.not.awaited=Coroutine ''{0}'' is not awaited
# PyTestParametrizedInspection
INSP.NAME.pytest-parametrized=Checks that functions decorated by pytest parametrize have correct arguments
diff --git a/python/src/com/jetbrains/python/inspections/PyAsyncCallInspection.kt b/python/src/com/jetbrains/python/inspections/PyAsyncCallInspection.kt
new file mode 100644
index 000000000000..2e5e6a15b466
--- /dev/null
+++ b/python/src/com/jetbrains/python/inspections/PyAsyncCallInspection.kt
@@ -0,0 +1,104 @@
+// Copyright 2000-2018 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.inspections
+
+import com.intellij.codeInspection.LocalInspectionToolSession
+import com.intellij.codeInspection.LocalQuickFix
+import com.intellij.codeInspection.ProblemDescriptor
+import com.intellij.codeInspection.ProblemsHolder
+import com.intellij.openapi.project.Project
+import com.intellij.psi.PsiElementVisitor
+import com.jetbrains.python.PyBundle
+import com.jetbrains.python.PyNames
+import com.jetbrains.python.codeInsight.dataflow.scope.ScopeUtil
+import com.jetbrains.python.codeInsight.typing.PyTypingTypeProvider
+import com.jetbrains.python.psi.*
+import com.jetbrains.python.psi.resolve.PyResolveContext
+import com.jetbrains.python.psi.types.PyABCUtil
+import com.jetbrains.python.psi.types.PyClassType
+import com.jetbrains.python.psi.types.TypeEvalContext
+import com.jetbrains.python.refactoring.PyReplaceExpressionUtil
+
+
+class PyAsyncCallInspection : PyInspection() {
+
+ override fun buildVisitor(holder: ProblemsHolder, isOnTheFly: Boolean, session: LocalInspectionToolSession): PsiElementVisitor {
+ return Visitor(holder, session)
+ }
+
+ private class Visitor(holder: ProblemsHolder, session: LocalInspectionToolSession) : PyInspectionVisitor(holder, session) {
+
+ override fun visitPyExpressionStatement(node: PyExpressionStatement) {
+ val expr = node.expression
+ if (expr is PyCallExpression && isAwaitableCall(expr)) {
+ val awaitableType = when {
+ isOuterFunctionAsync(expr) -> AwaitableType.AWAITABLE
+ isOuterFunctionCoroutine(expr, myTypeEvalContext) -> AwaitableType.COROUTINE
+ else -> return
+ }
+ // no need to check whether await or yield from is missed, because it's PyCallExpression already, not PyPrefixExpression
+ val functionName = getCalledCoroutineName(expr, resolveContext) ?: return
+ registerProblem(node, PyBundle.message("INSP.NAME.coroutine.is.not.awaited", functionName),
+ PyAddAwaitCallForCoroutineFix(awaitableType))
+ }
+ }
+
+ private fun isAwaitableCall(callExpr: PyCallExpression): Boolean {
+ val type = myTypeEvalContext.getType(callExpr) ?: return false
+ return PyABCUtil.isSubtype(type, PyNames.AWAITABLE, myTypeEvalContext) ||
+ type is PyClassType &&
+ PyTypingTypeProvider.GENERATOR == type.classQName &&
+ PyKnownDecoratorUtil.isResolvedToGeneratorBasedCoroutine(callExpr, resolveContext, myTypeEvalContext)
+ }
+ }
+
+ companion object {
+ enum class AwaitableType {
+ AWAITABLE, COROUTINE
+ }
+
+ const val coroutineIsNotAwaited = "Coroutine is not awaited"
+
+ private fun getCalledCoroutineName(callExpression: PyCallExpression, resolveContext: PyResolveContext): String? {
+ val callee = callExpression.callee as? PyReferenceExpression ?: return null
+ val function = callExpression.multiResolveCalleeFunction(resolveContext).firstOrNull() as? PyFunction ?: return null
+ return if (function.name == PyNames.INIT) callee.name else function.name
+ }
+
+ fun isOuterFunctionAsync(node: PyExpression): Boolean {
+ return (ScopeUtil.getScopeOwner(node) as? PyFunction)?.isAsync ?: false
+ }
+
+ private fun isOuterFunctionCoroutine(node: PyExpression, typeEvalContext: TypeEvalContext): Boolean {
+ val pyFunction = (ScopeUtil.getScopeOwner(node) as? PyFunction) ?: return false
+ return PyKnownDecoratorUtil.hasGeneratorBasedCoroutineDecorator(pyFunction, typeEvalContext)
+ }
+ }
+
+ private class PyAddAwaitCallForCoroutineFix(val type: AwaitableType) : LocalQuickFix {
+ override fun getFamilyName() = coroutineIsNotAwaited
+
+ override fun applyFix(project: Project, descriptor: ProblemDescriptor) {
+ val psiElement = descriptor.psiElement
+ if (psiElement is PyExpressionStatement) {
+ val sb = StringBuilder()
+ when (type) {
+ AwaitableType.AWAITABLE -> {
+ sb.append("""async def foo():
+ | """.trimMargin()).append(PyNames.AWAIT).append(" ").append(psiElement.text)
+ }
+ AwaitableType.COROUTINE -> {
+ sb.append("""def foo():
+ | """.trimMargin()).append(PyNames.YIELD).append(" ").append(PyNames.FROM).append(" ")
+ .append(psiElement.text)
+ }
+ }
+ val generator = PyElementGenerator.getInstance(project)
+ val function = generator.createFromText(LanguageLevel.forElement(psiElement), PyFunction::class.java, sb.toString())
+ val awaitedStatement = function.statementList.statements.firstOrNull()
+ if (awaitedStatement != null) {
+ PyReplaceExpressionUtil.replaceExpression(psiElement, awaitedStatement)
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/python/src/com/jetbrains/python/inspections/unresolvedReference/PyUnresolvedReferencesInspection.java b/python/src/com/jetbrains/python/inspections/unresolvedReference/PyUnresolvedReferencesInspection.java
index ac071e57b6e3..b5295251ca91 100644
--- a/python/src/com/jetbrains/python/inspections/unresolvedReference/PyUnresolvedReferencesInspection.java
+++ b/python/src/com/jetbrains/python/inspections/unresolvedReference/PyUnresolvedReferencesInspection.java
@@ -877,12 +877,7 @@ public class PyUnresolvedReferencesInspection extends PyInspection {
final PyExpression receiver = ((PyOperatorReference)reference).getReceiver();
if (receiver instanceof PyCallExpression) {
- final boolean resolvedToGeneratorBasedCoroutine = StreamEx
- .of(((PyCallExpression)receiver).multiResolveCalleeFunction(getResolveContext()))
- .select(PyFunction.class)
- .anyMatch(function -> PyKnownDecoratorUtil.hasGeneratorBasedCoroutineDecorator(function, myTypeEvalContext));
-
- if (resolvedToGeneratorBasedCoroutine) return true;
+ return PyKnownDecoratorUtil.isResolvedToGeneratorBasedCoroutine((PyCallExpression)receiver, getResolveContext(), myTypeEvalContext);
}
}
diff --git a/python/src/com/jetbrains/python/psi/PyKnownDecoratorUtil.java b/python/src/com/jetbrains/python/psi/PyKnownDecoratorUtil.java
index 18e3b81618c3..d152af33e4c4 100644
--- a/python/src/com/jetbrains/python/psi/PyKnownDecoratorUtil.java
+++ b/python/src/com/jetbrains/python/psi/PyKnownDecoratorUtil.java
@@ -6,6 +6,7 @@ import com.intellij.psi.PsiReference;
import com.intellij.psi.util.QualifiedName;
import com.intellij.util.containers.ContainerUtil;
import com.jetbrains.python.PyNames;
+import com.jetbrains.python.psi.resolve.PyResolveContext;
import com.jetbrains.python.psi.types.TypeEvalContext;
import one.util.streamex.StreamEx;
import org.jetbrains.annotations.NotNull;
@@ -210,6 +211,15 @@ public class PyKnownDecoratorUtil {
return ContainerUtil.exists(getKnownDecorators(function, context), GENERATOR_BASED_COROUTINE_DECORATORS::contains);
}
+ public static boolean isResolvedToGeneratorBasedCoroutine(@NotNull PyCallExpression receiver,
+ @NotNull PyResolveContext resolveContext,
+ @NotNull TypeEvalContext typeEvalContext) {
+ return StreamEx
+ .of((receiver).multiResolveCalleeFunction(resolveContext))
+ .select(PyFunction.class)
+ .anyMatch(function -> hasGeneratorBasedCoroutineDecorator(function, typeEvalContext));
+ }
+
public static boolean hasRedeclarationDecorator(@NotNull PyFunction function, @NotNull TypeEvalContext context) {
return getKnownDecorators(function, context).contains(TYPING_OVERLOAD);
}
diff --git a/python/src/com/jetbrains/python/psi/types/PyABCUtil.java b/python/src/com/jetbrains/python/psi/types/PyABCUtil.java
index 483e30b6a4c5..10041326e459 100644
--- a/python/src/com/jetbrains/python/psi/types/PyABCUtil.java
+++ b/python/src/com/jetbrains/python/psi/types/PyABCUtil.java
@@ -97,6 +97,9 @@ public class PyABCUtil {
if (PyNames.ASYNC_ITERABLE.equals(superClassName)) {
return hasMethod(subClass, PyNames.AITER, inherited, context);
}
+ if (PyNames.AWAITABLE.equals(superClassName)) {
+ return hasMethod(subClass, PyNames.DUNDER_AWAIT, inherited, context);
+ }
if (PyNames.PATH_LIKE.equals(superClassName)) {
return hasMethod(subClass, PyNames.FSPATH, inherited, context);
}
diff --git a/python/testData/inspections/PyAsyncCallInspection/AsyncioCorFromAsyncioCorCall/a.py b/python/testData/inspections/PyAsyncCallInspection/AsyncioCorFromAsyncioCorCall/a.py
new file mode 100644
index 000000000000..e2be236d4780
--- /dev/null
+++ b/python/testData/inspections/PyAsyncCallInspection/AsyncioCorFromAsyncioCorCall/a.py
@@ -0,0 +1,15 @@
+import asyncio
+
+
+@asyncio.coroutine
+def bar():
+ yield from asyncio.sleep(1)
+
+
+@asyncio.coroutine
+def check():
+ bar()
+
+
+async def check():
+ bar()
\ No newline at end of file
diff --git a/python/testData/inspections/PyAsyncCallInspection/AsyncioCorFromAsyncioCorCall/asyncio/__init__.py b/python/testData/inspections/PyAsyncCallInspection/AsyncioCorFromAsyncioCorCall/asyncio/__init__.py
new file mode 100644
index 000000000000..8e5f5dbc26be
--- /dev/null
+++ b/python/testData/inspections/PyAsyncCallInspection/AsyncioCorFromAsyncioCorCall/asyncio/__init__.py
@@ -0,0 +1,5 @@
+from .coroutines import *
+
+
+def sleep(s):
+ pass
diff --git a/python/testData/inspections/PyAsyncCallInspection/AsyncioCorFromAsyncioCorCall/asyncio/coroutines.py b/python/testData/inspections/PyAsyncCallInspection/AsyncioCorFromAsyncioCorCall/asyncio/coroutines.py
new file mode 100644
index 000000000000..8e5abf01b538
--- /dev/null
+++ b/python/testData/inspections/PyAsyncCallInspection/AsyncioCorFromAsyncioCorCall/asyncio/coroutines.py
@@ -0,0 +1,2 @@
+def coroutine(fn):
+ pass
diff --git a/python/testData/inspections/PyAsyncCallInspection/CorrectAsyncioCorCall/a.py b/python/testData/inspections/PyAsyncCallInspection/CorrectAsyncioCorCall/a.py
new file mode 100644
index 000000000000..38d8023d0759
--- /dev/null
+++ b/python/testData/inspections/PyAsyncCallInspection/CorrectAsyncioCorCall/a.py
@@ -0,0 +1,22 @@
+import asyncio
+
+
+@asyncio.coroutine
+def foo():
+ return 24
+
+
+@asyncio.coroutine
+def baz():
+ yield from foo()
+
+
+@asyncio.coroutine
+def gen():
+ return foo()
+
+
+@asyncio.coroutine
+def wrap(co):
+ res = yield from co
+ return res
diff --git a/python/testData/inspections/PyAsyncCallInspection/CorrectAsyncioCorCall/asyncio/__init__.py b/python/testData/inspections/PyAsyncCallInspection/CorrectAsyncioCorCall/asyncio/__init__.py
new file mode 100644
index 000000000000..8e5f5dbc26be
--- /dev/null
+++ b/python/testData/inspections/PyAsyncCallInspection/CorrectAsyncioCorCall/asyncio/__init__.py
@@ -0,0 +1,5 @@
+from .coroutines import *
+
+
+def sleep(s):
+ pass
diff --git a/python/testData/inspections/PyAsyncCallInspection/CorrectAsyncioCorCall/asyncio/coroutines.py b/python/testData/inspections/PyAsyncCallInspection/CorrectAsyncioCorCall/asyncio/coroutines.py
new file mode 100644
index 000000000000..8e5abf01b538
--- /dev/null
+++ b/python/testData/inspections/PyAsyncCallInspection/CorrectAsyncioCorCall/asyncio/coroutines.py
@@ -0,0 +1,2 @@
+def coroutine(fn):
+ pass
diff --git a/python/testData/inspections/PyAsyncCallInspection/TypesCorFromAsyncCall/a.py b/python/testData/inspections/PyAsyncCallInspection/TypesCorFromAsyncCall/a.py
new file mode 100644
index 000000000000..7fd44b685af6
--- /dev/null
+++ b/python/testData/inspections/PyAsyncCallInspection/TypesCorFromAsyncCall/a.py
@@ -0,0 +1,14 @@
+import types
+
+
+def foo():
+ return 23
+
+
+@types.coroutine
+def bar():
+ yield from foo()
+
+
+async def check():
+ bar()
\ No newline at end of file
diff --git a/python/testData/inspections/PyAsyncCallInspection/TypesCorFromAsyncCall/types.py b/python/testData/inspections/PyAsyncCallInspection/TypesCorFromAsyncCall/types.py
new file mode 100644
index 000000000000..68f0e4fbb99a
--- /dev/null
+++ b/python/testData/inspections/PyAsyncCallInspection/TypesCorFromAsyncCall/types.py
@@ -0,0 +1,2 @@
+def coroutine(fn):
+ pass
\ No newline at end of file
diff --git a/python/testData/inspections/PyAsyncCallInspection/asyncFromAsyncCall.py b/python/testData/inspections/PyAsyncCallInspection/asyncFromAsyncCall.py
new file mode 100644
index 000000000000..cf54c40f8776
--- /dev/null
+++ b/python/testData/inspections/PyAsyncCallInspection/asyncFromAsyncCall.py
@@ -0,0 +1,9 @@
+
+
+async def bar():
+ return "hey"
+
+
+async def foo():
+ bar()
+ return True
diff --git a/python/testData/inspections/PyAsyncCallInspection/correctCalls.py b/python/testData/inspections/PyAsyncCallInspection/correctCalls.py
new file mode 100644
index 000000000000..74f4b8e4d80f
--- /dev/null
+++ b/python/testData/inspections/PyAsyncCallInspection/correctCalls.py
@@ -0,0 +1,38 @@
+import asyncio
+
+
+async def foo():
+ return 24
+
+
+async def with_await():
+ await foo()
+
+
+async def gen():
+ return foo()
+
+
+async def wrap(co):
+ await_co = await co
+ return await_co
+
+
+async def baz():
+ cor = foo()
+ return await wrap(cor)
+
+
+async def wrap_twice(co):
+ await_co = await co
+ return await await_co
+
+
+async def bar():
+ return await wrap_twice(gen())
+
+
+loop = asyncio.get_event_loop()
+loop.run_until_complete(bar())
+loop.run_until_complete(baz())
+loop.close()
diff --git a/python/testData/inspections/PyAsyncCallInspection/futureLikeFromAsyncCall.py b/python/testData/inspections/PyAsyncCallInspection/futureLikeFromAsyncCall.py
new file mode 100644
index 000000000000..5c329bfdf8a0
--- /dev/null
+++ b/python/testData/inspections/PyAsyncCallInspection/futureLikeFromAsyncCall.py
@@ -0,0 +1,10 @@
+import asyncio
+
+
+class FutureLike:
+ def __await__(self):
+ yield from asyncio.sleep(2)
+
+
+async def foo():
+ FutureLike()
\ No newline at end of file
diff --git a/python/testData/quickFixes/PyAsyncCallQuickFixTest/addAwaitBeforeCall.py b/python/testData/quickFixes/PyAsyncCallQuickFixTest/addAwaitBeforeCall.py
new file mode 100644
index 000000000000..c29f3118dd79
--- /dev/null
+++ b/python/testData/quickFixes/PyAsyncCallQuickFixTest/addAwaitBeforeCall.py
@@ -0,0 +1,9 @@
+
+
+async def bar():
+ return "hey"
+
+
+async def foo():
+ bar()
+ return True
diff --git a/python/testData/quickFixes/PyAsyncCallQuickFixTest/addAwaitBeforeCall_after.py b/python/testData/quickFixes/PyAsyncCallQuickFixTest/addAwaitBeforeCall_after.py
new file mode 100644
index 000000000000..addd5e524d72
--- /dev/null
+++ b/python/testData/quickFixes/PyAsyncCallQuickFixTest/addAwaitBeforeCall_after.py
@@ -0,0 +1,9 @@
+
+
+async def bar():
+ return "hey"
+
+
+async def foo():
+ await bar()
+ return True
diff --git a/python/testData/quickFixes/PyAsyncCallQuickFixTest/addYieldFromBeforeCall.py b/python/testData/quickFixes/PyAsyncCallQuickFixTest/addYieldFromBeforeCall.py
new file mode 100644
index 000000000000..7190bd54f4a7
--- /dev/null
+++ b/python/testData/quickFixes/PyAsyncCallQuickFixTest/addYieldFromBeforeCall.py
@@ -0,0 +1,13 @@
+import asyncio
+
+
+@asyncio.coroutine
+def bar():
+ yield from asyncio.sleep(2)
+ return "hey"
+
+
+@asyncio.coroutine
+def foo():
+ bar()
+ return True
diff --git a/python/testData/quickFixes/PyAsyncCallQuickFixTest/addYieldFromBeforeCall_after.py b/python/testData/quickFixes/PyAsyncCallQuickFixTest/addYieldFromBeforeCall_after.py
new file mode 100644
index 000000000000..289959537a5c
--- /dev/null
+++ b/python/testData/quickFixes/PyAsyncCallQuickFixTest/addYieldFromBeforeCall_after.py
@@ -0,0 +1,13 @@
+import asyncio
+
+
+@asyncio.coroutine
+def bar():
+ yield from asyncio.sleep(2)
+ return "hey"
+
+
+@asyncio.coroutine
+def foo():
+ yield from bar()
+ return True
diff --git a/python/testData/quickFixes/PyAsyncCallQuickFixTest/asyncio/__init__.py b/python/testData/quickFixes/PyAsyncCallQuickFixTest/asyncio/__init__.py
new file mode 100644
index 000000000000..8e5f5dbc26be
--- /dev/null
+++ b/python/testData/quickFixes/PyAsyncCallQuickFixTest/asyncio/__init__.py
@@ -0,0 +1,5 @@
+from .coroutines import *
+
+
+def sleep(s):
+ pass
diff --git a/python/testData/quickFixes/PyAsyncCallQuickFixTest/asyncio/coroutines.py b/python/testData/quickFixes/PyAsyncCallQuickFixTest/asyncio/coroutines.py
new file mode 100644
index 000000000000..8e5abf01b538
--- /dev/null
+++ b/python/testData/quickFixes/PyAsyncCallQuickFixTest/asyncio/coroutines.py
@@ -0,0 +1,2 @@
+def coroutine(fn):
+ pass
diff --git a/python/testData/quickFixes/__init__.py b/python/testData/quickFixes/__init__.py
new file mode 100644
index 000000000000..e69de29bb2d1
diff --git a/python/testSrc/com/jetbrains/python/inspections/PyAsyncCallInspectionTest.java b/python/testSrc/com/jetbrains/python/inspections/PyAsyncCallInspectionTest.java
new file mode 100644
index 000000000000..51555eb9de91
--- /dev/null
+++ b/python/testSrc/com/jetbrains/python/inspections/PyAsyncCallInspectionTest.java
@@ -0,0 +1,65 @@
+// Copyright 2000-2018 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.inspections;
+
+import com.intellij.testFramework.LightProjectDescriptor;
+import com.jetbrains.python.fixtures.PyInspectionTestCase;
+import com.jetbrains.python.psi.LanguageLevel;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+
+public class PyAsyncCallInspectionTest extends PyInspectionTestCase {
+
+ // PY-17292
+ public void testAsyncFromAsyncCall() {
+ doTest();
+ }
+
+ // PY-17292
+ public void testCorrectAsyncioCorCall() {
+ doMultiFileTest("a.py");
+ }
+
+ // PY-17292
+ public void testTypesCorFromAsyncCall() {
+ doMultiFileTest("a.py");
+ }
+
+ // PY-17292
+ public void testAsyncioCorFromAsyncioCorCall() {
+ doMultiFileTest("a.py");
+ }
+
+ // PY-17292
+ public void testFutureLikeFromAsyncCall() {
+ doTest();
+ }
+
+ // PY-17292
+ public void testCorrectCalls() {
+ doTest();
+ }
+
+
+ @Override
+ protected void doMultiFileTest(@NotNull String filename) {
+ runWithLanguageLevel(LanguageLevel.PYTHON35, () -> super.doMultiFileTest(filename));
+ }
+
+ @Override
+ protected void doTest() {
+ runWithLanguageLevel(LanguageLevel.PYTHON35, () -> super.doTest());
+ }
+
+ @NotNull
+ @Override
+ protected Class extends PyInspection> getInspectionClass() {
+ return PyAsyncCallInspection.class;
+ }
+
+ @Nullable
+ @Override
+ protected LightProjectDescriptor getProjectDescriptor() {
+ return ourPy3Descriptor;
+ }
+}
diff --git a/python/testSrc/com/jetbrains/python/quickFixes/PyAsyncCallQuickFixTest.java b/python/testSrc/com/jetbrains/python/quickFixes/PyAsyncCallQuickFixTest.java
new file mode 100644
index 000000000000..7068760130c4
--- /dev/null
+++ b/python/testSrc/com/jetbrains/python/quickFixes/PyAsyncCallQuickFixTest.java
@@ -0,0 +1,29 @@
+// Copyright 2000-2018 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.quickFixes;
+
+import com.intellij.testFramework.LightProjectDescriptor;
+import com.jetbrains.python.PyQuickFixTestCase;
+import com.jetbrains.python.inspections.PyAsyncCallInspection;
+import com.jetbrains.python.psi.LanguageLevel;
+import org.jetbrains.annotations.Nullable;
+
+public class PyAsyncCallQuickFixTest extends PyQuickFixTestCase {
+
+ // PY-17292
+ public void testAddAwaitBeforeCall() {
+ doQuickFixTest(PyAsyncCallInspection.class, PyAsyncCallInspection.coroutineIsNotAwaited, LanguageLevel.PYTHON35);
+ }
+
+ // PY-17292
+ public void testAddYieldFromBeforeCall() {
+ runWithLanguageLevel(LanguageLevel.PYTHON35,
+ () -> doMultifilesTest(PyAsyncCallInspection.class, PyAsyncCallInspection.coroutineIsNotAwaited,
+ new String[]{"addYieldFromBeforeCall.py", "asyncio/__init__.py", "asyncio/coroutines.py"}));
+ }
+
+ @Nullable
+ @Override
+ protected LightProjectDescriptor getProjectDescriptor() {
+ return ourPy3Descriptor;
+ }
+}