diff --git a/python/src/com/jetbrains/python/validation/CompatibilityVisitor.java b/python/src/com/jetbrains/python/validation/CompatibilityVisitor.java
index 88df9ba33371..540425adc484 100644
--- a/python/src/com/jetbrains/python/validation/CompatibilityVisitor.java
+++ b/python/src/com/jetbrains/python/validation/CompatibilityVisitor.java
@@ -31,6 +31,7 @@ import com.intellij.util.ArrayUtil;
import com.intellij.util.containers.ContainerUtil;
import com.jetbrains.python.PyNames;
import com.jetbrains.python.PyTokenTypes;
+import com.jetbrains.python.codeInsight.dataflow.scope.ScopeUtil;
import com.jetbrains.python.inspections.quickfix.*;
import com.jetbrains.python.psi.*;
import com.jetbrains.python.psi.impl.PyStringLiteralExpressionImpl;
@@ -449,6 +450,18 @@ public abstract class CompatibilityVisitor extends PyAnnotator {
public void visitPyYieldExpression(PyYieldExpression node) {
super.visitPyYieldExpression(node);
+ Optional
+ .ofNullable(ScopeUtil.getScopeOwner(node))
+ .map(owner -> PyUtil.as(owner, PyFunction.class))
+ .filter(function -> function.isAsync() && function.isAsyncAllowed())
+ .ifPresent(
+ function -> {
+ if (!node.isDelegating() && myVersionsToProcess.contains(LanguageLevel.PYTHON35)) {
+ registerProblem(node, "Python version 3.5 does not support 'yield' inside async functions");
+ }
+ }
+ );
+
if (!node.isDelegating()) {
return;
}
diff --git a/python/src/com/jetbrains/python/validation/ReturnAnnotator.java b/python/src/com/jetbrains/python/validation/ReturnAnnotator.java
index f665bcf8893a..65856a6cbd07 100644
--- a/python/src/com/jetbrains/python/validation/ReturnAnnotator.java
+++ b/python/src/com/jetbrains/python/validation/ReturnAnnotator.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2000-2014 JetBrains s.r.o.
+ * Copyright 2000-2016 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -21,7 +21,7 @@ import com.jetbrains.python.codeInsight.dataflow.scope.ScopeUtil;
import com.jetbrains.python.psi.*;
/**
- * Highlights incorrect return statements: 'return' and 'yield' outside functions, 'yield' inside async functions.
+ * Highlights incorrect return statements: 'return' and 'yield' outside functions
*/
public class ReturnAnnotator extends PyAnnotator {
public void visitPyReturnStatement(final PyReturnStatement node) {
@@ -36,8 +36,13 @@ public class ReturnAnnotator extends PyAnnotator {
if (!(owner instanceof PyFunction || owner instanceof PyLambdaExpression)) {
getHolder().createErrorAnnotation(node, "'yield' outside of function");
}
- if (owner instanceof PyFunction && ((PyFunction)owner).isAsync()) {
- getHolder().createErrorAnnotation(node, "'yield' inside async function");
+
+ if (node.isDelegating() && owner instanceof PyFunction) {
+ final PyFunction function = (PyFunction)owner;
+
+ if (function.isAsync() && function.isAsyncAllowed()) {
+ getHolder().createErrorAnnotation(node, "Python does not support 'yield from' inside async functions");
+ }
}
}
}
diff --git a/python/testData/highlighting/yieldInsideAsyncDef.py b/python/testData/highlighting/yieldInsideAsyncDef.py
deleted file mode 100644
index 0b4b8f47aac4..000000000000
--- a/python/testData/highlighting/yieldInsideAsyncDef.py
+++ /dev/null
@@ -1,5 +0,0 @@
-async def foo(x):
- await x
- yield x
- yield from x
- return x
diff --git a/python/testData/highlighting/yieldInsideAsyncDefPy35.py b/python/testData/highlighting/yieldInsideAsyncDefPy35.py
new file mode 100644
index 000000000000..ca6a4659a9bd
--- /dev/null
+++ b/python/testData/highlighting/yieldInsideAsyncDefPy35.py
@@ -0,0 +1,5 @@
+async def foo(x):
+ await x
+ yield x
+ yield from x
+ return x
diff --git a/python/testData/highlighting/yieldInsideAsyncDefPy36.py b/python/testData/highlighting/yieldInsideAsyncDefPy36.py
new file mode 100644
index 000000000000..abee1b6a2859
--- /dev/null
+++ b/python/testData/highlighting/yieldInsideAsyncDefPy36.py
@@ -0,0 +1,5 @@
+async def foo(x):
+ await x
+ yield x
+ yield from x
+ return x
diff --git a/python/testData/inspections/PyCompatibilityInspection/yieldInsideAsyncDef.py b/python/testData/inspections/PyCompatibilityInspection/yieldInsideAsyncDef.py
new file mode 100644
index 000000000000..8c3c97a2ef8f
--- /dev/null
+++ b/python/testData/inspections/PyCompatibilityInspection/yieldInsideAsyncDef.py
@@ -0,0 +1,5 @@
+async def foo(x):
+ await x
+ yield x
+ yield from x
+ return x
diff --git a/python/testSrc/com/jetbrains/python/PythonHighlightingTest.java b/python/testSrc/com/jetbrains/python/PythonHighlightingTest.java
index 1c5f58aa50a5..c4225963dd3a 100644
--- a/python/testSrc/com/jetbrains/python/PythonHighlightingTest.java
+++ b/python/testSrc/com/jetbrains/python/PythonHighlightingTest.java
@@ -258,10 +258,15 @@ public class PythonHighlightingTest extends PyTestCase {
doTest(LanguageLevel.PYTHON35, true, false);
}
- public void testYieldInsideAsyncDef() {
+ public void testYieldInsideAsyncDefPy35() {
doTest(LanguageLevel.PYTHON35, false, false);
}
+ // PY-20770
+ public void testYieldInsideAsyncDefPy36() {
+ doTest(LanguageLevel.PYTHON36, true, false);
+ }
+
public void testUnpackingStar() {
doTest(LanguageLevel.PYTHON35, false, false);
}
diff --git a/python/testSrc/com/jetbrains/python/inspections/PyCompatibilityInspectionTest.java b/python/testSrc/com/jetbrains/python/inspections/PyCompatibilityInspectionTest.java
index fdd5d0ecc4dd..b485d2ba6e25 100644
--- a/python/testSrc/com/jetbrains/python/inspections/PyCompatibilityInspectionTest.java
+++ b/python/testSrc/com/jetbrains/python/inspections/PyCompatibilityInspectionTest.java
@@ -194,6 +194,11 @@ public class PyCompatibilityInspectionTest extends PyTestCase {
doTest(LanguageLevel.PYTHON36);
}
+ // PY-20770
+ public void testYieldInsideAsyncDef() {
+ doTest(LanguageLevel.PYTHON36);
+ }
+
private void doTest(@NotNull LanguageLevel level) {
runWithLanguageLevel(level, this::doTest);
}