PY-32067: Highlight 'await' outside async functions as errors

GitOrigin-RevId: 9862d18848b3a826ba11e9c0e15e56a2c94bcc9e
This commit is contained in:
Irina Fediaeva
2023-03-21 20:57:34 +02:00
committed by intellij-monorepo-bot
parent 324032d5f3
commit 3afa041f1b
15 changed files with 169 additions and 1 deletions

View File

@@ -163,6 +163,8 @@ ANN.try.except.can.not.have.except.and.star.except=Try statement cannot contain
ANN.exception.group.in.star.except=ExceptionGroup cannot be used in except*
ANN.continue.break.or.return.in.star.except='break', 'continue' and 'return' cannot appear in an except* block
ANN.await.outside.async.function='await' outside async function
### parsing
PARSE.expected.expression=Expression expected
PARSE.expected.rbracket=']' expected

View File

@@ -0,0 +1,21 @@
package com.jetbrains.python.validation;
import com.jetbrains.python.PyPsiBundle;
import com.jetbrains.python.PyTokenTypes;
import com.jetbrains.python.codeInsight.dataflow.scope.ScopeUtil;
import com.jetbrains.python.psi.PyFunction;
import com.jetbrains.python.psi.PyPrefixExpression;
import org.jetbrains.annotations.NotNull;
public final class PyAsyncAwaitAnnotator extends PyAnnotator {
@Override
public void visitPyPrefixExpression(@NotNull PyPrefixExpression node) {
super.visitPyPrefixExpression(node);
if (node.getOperator() == PyTokenTypes.AWAIT_KEYWORD) {
var scopeOwner = ScopeUtil.getScopeOwner(node);
if (!(scopeOwner instanceof PyFunction pyFunction && pyFunction.isAsync())) {
markError(node.getFirstChild(), PyPsiBundle.message("ANN.await.outside.async.function"));
}
}
}
}

View File

@@ -26,7 +26,8 @@ public class PyAnnotatingVisitor implements Annotator {
GlobalAnnotator.class,
ImportAnnotator.class,
PyBuiltinAnnotator.class,
UnsupportedFeatures.class
UnsupportedFeatures.class,
PyAsyncAwaitAnnotator.class
};
private final PyAnnotator[] myAnnotators;

View File

@@ -0,0 +1,12 @@
import asyncio
class AsyncIterator:
def __init__(self, data):
pass
def __aiter__(self):
<error descr="'await' outside async function">await</error> asyncio.sleep(1)
def __anext__(self):
<error descr="'await' outside async function">await</error> asyncio.sleep(1)

View File

@@ -0,0 +1,5 @@
def example(y):
return [x async for x in <error descr="'await' outside async function">await</error> y]
async def example_correct(y):
return [x async for x in await y]

View File

@@ -0,0 +1,14 @@
import asyncio
def useless_decorator(param):
def decorator(func):
pass
return decorator
@useless_decorator(param=(<error descr="'await' outside async function">await</error> asyncio.sleep(1)))
def fun():
pass
@useless_decorator(param=(<error descr="'await' outside async function">await</error> asyncio.sleep(1)))
async def fun2():
pass

View File

@@ -0,0 +1,5 @@
import asyncio
async def async_func():
def non_async_local(x=(await asyncio.sleep(10))):
pass

View File

@@ -0,0 +1,7 @@
import asyncio
def f(a=<error descr="'await' outside async function">await</error> asyncio.sleep(3) if True else 0):
return a
async def f2(a=<error descr="'await' outside async function">await</error> asyncio.sleep(3) if True else 0):
return a

View File

@@ -0,0 +1,7 @@
def example(x):
async for i in <error descr="'await' outside async function">await</error> x:
yield i
async def example_correct(x):
async for i in await x:
yield i

View File

@@ -0,0 +1,7 @@
import asyncio
def example():
<error descr="'await' outside async function">await</error> asyncio.sleep(1)
async def example_correct():
await asyncio.sleep(1)

View File

@@ -0,0 +1,4 @@
import asyncio
def example():
await<error descr="End of statement expected"> </error>asyncio.sleep(1)

View File

@@ -0,0 +1,11 @@
async def my_function():
def subfunction(x):
<error descr="'await' outside async function">await</error> x
async def my_function_correct():
async def subfunction(x):
await x
def my_non_async_function_correct():
async def subfunction(x):
await x

View File

@@ -0,0 +1,9 @@
import asyncio
async def f():
await (await asyncio.sleep(1))
def f2():
<error descr="'await' outside async function">await</error> (<error descr="'await' outside async function">await</error> asyncio.sleep(1))
<error descr="'await' outside async function">await</error> (<error descr="'await' outside async function">await</error> asyncio.sleep(1))

View File

@@ -0,0 +1,7 @@
import asyncio
a = <error descr="'await' outside async function">await</error> asyncio.sleep(1)
class C:
b = <error descr="'await' outside async function">await</error> asyncio.sleep(1)

View File

@@ -119,6 +119,62 @@ public class Py3HighlightingTest extends PyTestCase {
doTestWithLanguageLevel(LanguageLevel.PYTHON39, false, false);
}
// PY-32067
public void testAwaitInNonAsyncFunction() {
doTest(false, false);
}
// PY-32067
public void testAwaitInLoopInNonAsyncFunction() {
doTest(false, false);
}
// PY-32067
public void testAwaitInComprehensionInNonAsyncFunction() {
doTest(false, false);
}
// PY-32067
public void testAwaitInNonAsyncInnerFunctionOfAsyncFunction() {
doTest(false, false);
}
// PY-32067
public void testAwaitInAsyncIterator() {
doTest(false, false);
}
// PY-32067
public void testAwaitInFunctionDefaultArg() {
doTest(false, false);
}
// PY-32067
public void testAwaitOutsideFunction() {
doTest(false, false);
}
// PY-32067
public void testAwaitInNonAsyncFunctionPy27() {
doTestWithLanguageLevel(LanguageLevel.PYTHON27, false, false);
}
// PY-32067
public void testAwaitInDefaultArgOfInnerNonAsyncFunction() {
doTest(false, false);
}
// PY-32067
public void testAwaitInDefaultArgOfFunctionDecorator() {
doTest(false, false);
}
// PY-32067
public void testAwaitInsideAwaitExpression() {
doTest(false, false);
}
private void doTestWithLanguageLevel(LanguageLevel languageLevel, boolean checkWarnings, boolean checkInfos) {
runWithLanguageLevel(languageLevel, () -> doTest(checkWarnings, checkInfos));
}