From 0e61667352035dda591bb5bbba3d3f067c1f7d7a Mon Sep 17 00:00:00 2001 From: Semyon Proshev Date: Tue, 27 Sep 2016 15:26:58 +0300 Subject: [PATCH] PY-20770 Fixed: Support Python 3.6 asynchronous generators and comprehensions Update highlighting and compatibility visitor to correctly process async generators described in PEP 525 --- .../python/validation/CompatibilityVisitor.java | 13 +++++++++++++ .../python/validation/ReturnAnnotator.java | 13 +++++++++---- python/testData/highlighting/yieldInsideAsyncDef.py | 5 ----- .../highlighting/yieldInsideAsyncDefPy35.py | 5 +++++ .../highlighting/yieldInsideAsyncDefPy36.py | 5 +++++ .../yieldInsideAsyncDef.py | 5 +++++ .../jetbrains/python/PythonHighlightingTest.java | 7 ++++++- .../inspections/PyCompatibilityInspectionTest.java | 5 +++++ 8 files changed, 48 insertions(+), 10 deletions(-) delete mode 100644 python/testData/highlighting/yieldInsideAsyncDef.py create mode 100644 python/testData/highlighting/yieldInsideAsyncDefPy35.py create mode 100644 python/testData/highlighting/yieldInsideAsyncDefPy36.py create mode 100644 python/testData/inspections/PyCompatibilityInspection/yieldInsideAsyncDef.py 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); }