PY-64304 EA-247016 Don't report illegal "await" expressions used as assignment targets during parsing

Instead, parse them as usual and later report in the dedicated AssignTargetAnnotator
and TypeAnnotationTargetAnnotator. This way, a PsiError element appearing in the PSI
tree of a type declaration statement doesn't cause PyAstTypeDeclarationStatement.getTarget
nullability contract violation.

GitOrigin-RevId: a3e90088cfac7938c398d4d293a72dbd127a2cd0
This commit is contained in:
Mikhail Golubev
2024-04-23 12:26:23 +03:00
committed by intellij-monorepo-bot
parent 37e44bd3ec
commit b9d7fee816
11 changed files with 59 additions and 12 deletions

View File

@@ -64,7 +64,6 @@ tuple.expression.expected=Tuple expression expected
value.expression.expected=Value expression expected
unexpected.expression.part=Unexpected expression part
unexpected.f.string.token=Unexpected f-string token
can.t.assign.to.await.expression=Cannot assign to await expression
for.expected='for' expected
rarrow.expected='->' expected
unexpected.tokens=Unexpected tokens

View File

@@ -1189,12 +1189,7 @@ public class ExpressionParsing extends Parsing {
expr.done(PyElementTypes.PREFIX_EXPRESSION);
}
else {
if (isTargetExpression) {
expr.error(message("can.t.assign.to.await.expression"));
}
else {
expr.done(PyElementTypes.PREFIX_EXPRESSION);
}
expr.done(PyElementTypes.PREFIX_EXPRESSION);
}
return true;

View File

@@ -95,6 +95,7 @@ ANN.cant.aug.assign.starred.assignment.target.must.be.in.list.or.tuple=Starred a
ANN.cant.assign.to.literal=Cannot assign to literal
ANN.cant.delete.literal=Cannot delete literal
ANN.cant.assign.to.lambda=Cannot assign to lambda
ANN.cant.assign.to.await.expr=Cannot assign to await expression
ANN.break.outside.loop='break' outside loop
ANN.continue.outside.loop='continue' outside loop

View File

@@ -21,7 +21,7 @@ import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.psi.PsiElement;
import com.intellij.psi.util.PsiTreeUtil;
import com.jetbrains.python.PyNames;
import com.jetbrains.python.PyPsiBundle;
import com.jetbrains.python.PyTokenTypes;
import com.jetbrains.python.codeInsight.controlflow.ScopeOwner;
import com.jetbrains.python.codeInsight.dataflow.scope.ScopeUtil;
import com.jetbrains.python.psi.*;
@@ -118,7 +118,9 @@ public class AssignTargetAnnotator extends PyAnnotator {
node.getForComponents().forEach(
it -> {
checkNotAssignmentExpression(it.getIteratorVariable(), targetMessage);
PyExpression iteratorVariable = it.getIteratorVariable();
iteratorVariable.accept(new ExprVisitor(Operation.For));
checkNotAssignmentExpression(iteratorVariable, targetMessage);
checkNoAssignmentExpressionAsChild(it.getIteratedList(), iterableMessage);
}
);
@@ -294,5 +296,12 @@ public class AssignTargetAnnotator extends PyAnnotator {
public void visitPyBoolLiteralExpression(@NotNull PyBoolLiteralExpression node) {
getHolder().newAnnotation(HighlightSeverity.ERROR, message("ANN.assignment.to.keyword")).range(node).create();
}
@Override
public void visitPyPrefixExpression(@NotNull PyPrefixExpression node) {
if (node.getOperator() == PyTokenTypes.AWAIT_KEYWORD && node.getOperand() != null) {
getHolder().newAnnotation(HighlightSeverity.ERROR, message("ANN.cant.assign.to.await.expr")).range(node).create();
}
}
}
}

View File

@@ -12,3 +12,7 @@ a, *b, c = seq
() = []
[] = []
async def f():
<error descr="Cannot assign to await expression">await foo()</error> = 1
<error descr="An illegal target for a variable annotation"><error descr="Cannot assign to await expression">await foo()</error></error>: int = 1

View File

@@ -5,6 +5,8 @@ f(42).attr: dict
<error descr="An illegal target for a variable annotation">2 ** 8</error>: int
<error descr="An illegal target for a variable annotation">f()</error>: bool
async def f():
<error descr="An illegal target for a variable annotation">await foo()</error>: int
<error descr="A variable annotation cannot be combined with tuple unpacking">x, y, z</error>: Tuple[int, ...]
(<error descr="A variable annotation cannot be combined with tuple unpacking">x, y, z</error>): Tuple[int, int, int]
<error descr="A variable annotation cannot be combined with tuple unpacking">[x, y, z]</error>: Tuple[Any, Any, Any]

View File

@@ -35,7 +35,7 @@ PyFile:Await.py
PsiWhiteSpace('\n ')
PyStatementList
PyAssignmentStatement
PsiErrorElement:Cannot assign to await expression
PyPrefixExpression
PsiElement(Py:AWAIT_KEYWORD)('await')
PsiWhiteSpace(' ')
PyTargetExpression: x

View File

@@ -0,0 +1,2 @@
async def f():
await foo(42): int

View File

@@ -0,0 +1,30 @@
PyFile:VariableAnnotationRecoveryAwaitExpressionAsTarget.py
PyFunction('f')
PsiElement(Py:ASYNC_KEYWORD)('async')
PsiWhiteSpace(' ')
PsiElement(Py:DEF_KEYWORD)('def')
PsiWhiteSpace(' ')
PsiElement(Py:IDENTIFIER)('f')
PyParameterList
PsiElement(Py:LPAR)('(')
PsiElement(Py:RPAR)(')')
PsiElement(Py:COLON)(':')
PsiWhiteSpace('\n ')
PyStatementList
PyTypeDeclarationStatement
PyPrefixExpression
PsiElement(Py:AWAIT_KEYWORD)('await')
PsiWhiteSpace(' ')
PyCallExpression: foo
PyTargetExpression: foo
PsiElement(Py:IDENTIFIER)('foo')
PyArgumentList
PsiElement(Py:LPAR)('(')
PyNumericLiteralExpression
PsiElement(Py:INTEGER_LITERAL)('42')
PsiElement(Py:RPAR)(')')
PyAnnotation
PsiElement(Py:COLON)(':')
PsiWhiteSpace(' ')
PyReferenceExpression: int
PsiElement(Py:IDENTIFIER)('int')

View File

@@ -72,7 +72,7 @@ public class PythonHighlightingTest extends PyTestCase {
}
public void testAssignmentTargets3K() {
doTest(LanguageLevel.PYTHON34, true, false);
doTest(LanguageLevel.getLatest(), true, false);
}
public void testBreakOutsideOfLoop() {

View File

@@ -893,6 +893,11 @@ public class PythonParsingTest extends ParsingTestCase {
doTest(LanguageLevel.PYTHON36);
}
// PY-64304 EA-247016
public void testVariableAnnotationRecoveryAwaitExpressionAsTarget() {
doTest(LanguageLevel.PYTHON36);
}
// PY-20770
public void testAsyncComprehensions() {
doTest(LanguageLevel.PYTHON36);
@@ -1322,7 +1327,7 @@ public class PythonParsingTest extends ParsingTestCase {
public void testTypeKeywordAsIdentifier() {
doTest(LanguageLevel.PYTHON312);
}
public void doTest() {
doTest(LanguageLevel.PYTHON26);
}