mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
Make parser stricter for for statements and expressions (PY-36478)
Now it more follows original python grammar. GitOrigin-RevId: 199f30b819c85a4eba5cab9b3f67fbf1bb7f18b6
This commit is contained in:
committed by
intellij-monorepo-bot
parent
9335c066f5
commit
48a952a327
@@ -217,6 +217,7 @@ ANN.cannot.assign.to.debug=Cannot assign to __debug__
|
||||
ANN.unparenthesized.assignment.expression.statement=Unparenthesized assignment expressions are prohibited at the top level of an expression statement
|
||||
ANN.unparenthesized.assignment.expression.value=Unparenthesized assignment expressions are prohibited at the top level of the right hand side of an assignment statement
|
||||
ANN.assignment.expressions.within.a.comprehension.cannot.be.used.in.a.class.body=Assignment expressions within a comprehension cannot be used in a class body
|
||||
ANN.assignment.expression.as.a.target=Assignment expression cannot be used as a target here
|
||||
ANN.ignore.errors.like.this=Ignore errors like this
|
||||
ANN.function.cannot.be.async=function \"{0}\" cannot be async
|
||||
ANN.python.does.not.support.yield.from.inside.async.functions=Python does not support 'yield from' inside async functions
|
||||
|
||||
@@ -264,7 +264,7 @@ public class ExpressionParsing extends Parsing {
|
||||
assertCurrentToken(PyTokenTypes.FOR_KEYWORD);
|
||||
while (true) {
|
||||
myBuilder.advanceLexer();
|
||||
parseExpression(true, true);
|
||||
parseStarTargets();
|
||||
parseComprehensionRange(exprType == PyElementTypes.GENERATOR_EXPRESSION);
|
||||
while (myBuilder.getTokenType() == PyTokenTypes.IF_KEYWORD) {
|
||||
myBuilder.advanceLexer();
|
||||
@@ -284,6 +284,32 @@ public class ExpressionParsing extends Parsing {
|
||||
expr.done(exprType);
|
||||
}
|
||||
|
||||
public boolean parseStarTargets() {
|
||||
SyntaxTreeBuilder.Marker expr = myBuilder.mark();
|
||||
if (!parseStarExpression(true)) {
|
||||
myBuilder.error(message("PARSE.expected.expression"));
|
||||
expr.drop();
|
||||
return false;
|
||||
}
|
||||
if (myBuilder.getTokenType() == PyTokenTypes.COMMA) {
|
||||
while (myBuilder.getTokenType() == PyTokenTypes.COMMA) {
|
||||
myBuilder.advanceLexer();
|
||||
SyntaxTreeBuilder.Marker expr2 = myBuilder.mark();
|
||||
if (!parseStarExpression(true)) {
|
||||
myBuilder.error(message("PARSE.expected.expression"));
|
||||
expr2.rollbackTo();
|
||||
break;
|
||||
}
|
||||
expr2.drop();
|
||||
}
|
||||
expr.done(PyElementTypes.TUPLE_EXPRESSION);
|
||||
}
|
||||
else {
|
||||
expr.drop();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
protected void parseComprehensionRange(boolean generatorExpression) {
|
||||
checkMatches(PyTokenTypes.IN_KEYWORD, message("PARSE.expected.in"));
|
||||
boolean result;
|
||||
|
||||
@@ -670,7 +670,7 @@ public class StatementParsing extends Parsing implements ITokenTypeRemapper {
|
||||
protected void parseForPart() {
|
||||
final SyntaxTreeBuilder.Marker forPart = myBuilder.mark();
|
||||
myBuilder.advanceLexer();
|
||||
getExpressionParser().parseExpression(true, true);
|
||||
getExpressionParser().parseStarTargets();
|
||||
checkMatches(PyTokenTypes.IN_KEYWORD, PyPsiBundle.message("PARSE.expected.in"));
|
||||
getExpressionParser().parseExpression();
|
||||
parseColonAndSuite();
|
||||
|
||||
@@ -24,9 +24,11 @@ import com.jetbrains.python.PyNames;
|
||||
import com.jetbrains.python.codeInsight.controlflow.ScopeOwner;
|
||||
import com.jetbrains.python.codeInsight.dataflow.scope.ScopeUtil;
|
||||
import com.jetbrains.python.psi.*;
|
||||
import com.jetbrains.python.psi.impl.PyPsiUtils;
|
||||
import com.jetbrains.python.sdk.PythonSdkUtil;
|
||||
import org.jetbrains.annotations.Nls;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import static com.jetbrains.python.PyPsiBundle.message;
|
||||
|
||||
@@ -79,6 +81,7 @@ public class AssignTargetAnnotator extends PyAnnotator {
|
||||
PyExpression target = node.getForPart().getTarget();
|
||||
if (target != null) {
|
||||
target.accept(new ExprVisitor(Operation.For));
|
||||
checkTargetIsNotAssignmentExpression(target);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -110,6 +113,20 @@ public class AssignTargetAnnotator extends PyAnnotator {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitPyComprehensionElement(@NotNull PyComprehensionElement node) {
|
||||
node.getForComponents().forEach(it -> checkTargetIsNotAssignmentExpression(it.getIteratorVariable()));
|
||||
}
|
||||
|
||||
private void checkTargetIsNotAssignmentExpression(@Nullable PyExpression expression) {
|
||||
if (PyPsiUtils.flattenParens(expression) instanceof PyAssignmentExpression) {
|
||||
getHolder()
|
||||
.newAnnotation(HighlightSeverity.ERROR, PyBundle.message("ANN.assignment.expression.as.a.target"))
|
||||
.range(expression)
|
||||
.create();
|
||||
}
|
||||
}
|
||||
|
||||
private class ExprVisitor extends PyElementVisitor {
|
||||
private final Operation myOp;
|
||||
private final @Nls String DELETING_NONE = message("ANN.deleting.none");
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
[x1 for <error descr="Assignment expression cannot be used as a target here">(x1 := 2)</error> in (1, 2, 3)]
|
||||
|
||||
for <error descr="Assignment expression cannot be used as a target here">(x1 := 2)</error> in (1, 2, 3):
|
||||
pass
|
||||
@@ -12,4 +12,7 @@
|
||||
(b := -)
|
||||
(x := )
|
||||
|
||||
x = (b[j] := z) = 'spam' # z is a reference
|
||||
x = (b[j] := z) = 'spam' # z is a reference
|
||||
|
||||
[x1 for x1 := 2 in (1, 2, 3)]
|
||||
[x1 for (x1 := 2) in (1, 2, 3)]
|
||||
@@ -142,4 +142,81 @@ PyFile:InvalidAssignmentExpressions.py
|
||||
PyStringLiteralExpression: spam
|
||||
PsiElement(Py:SINGLE_QUOTED_STRING)(''spam'')
|
||||
PsiWhiteSpace(' ')
|
||||
PsiComment(Py:END_OF_LINE_COMMENT)('# z is a reference')
|
||||
PsiComment(Py:END_OF_LINE_COMMENT)('# z is a reference')
|
||||
PsiWhiteSpace('\n\n')
|
||||
PyAssignmentExpression
|
||||
PyListCompExpression
|
||||
PsiElement(Py:LBRACKET)('[')
|
||||
PyReferenceExpression: x1
|
||||
PsiElement(Py:IDENTIFIER)('x1')
|
||||
PsiWhiteSpace(' ')
|
||||
PsiElement(Py:FOR_KEYWORD)('for')
|
||||
PsiWhiteSpace(' ')
|
||||
PyTargetExpression: x1
|
||||
PsiElement(Py:IDENTIFIER)('x1')
|
||||
PsiErrorElement:'in' expected
|
||||
<empty list>
|
||||
PsiWhiteSpace(' ')
|
||||
PsiElement(Py:COLONEQ)(':=')
|
||||
PsiWhiteSpace(' ')
|
||||
PyBinaryExpression
|
||||
PyNumericLiteralExpression
|
||||
PsiElement(Py:INTEGER_LITERAL)('2')
|
||||
PsiWhiteSpace(' ')
|
||||
PsiElement(Py:IN_KEYWORD)('in')
|
||||
PsiWhiteSpace(' ')
|
||||
PyParenthesizedExpression
|
||||
PsiElement(Py:LPAR)('(')
|
||||
PyTupleExpression
|
||||
PyNumericLiteralExpression
|
||||
PsiElement(Py:INTEGER_LITERAL)('1')
|
||||
PsiElement(Py:COMMA)(',')
|
||||
PsiWhiteSpace(' ')
|
||||
PyNumericLiteralExpression
|
||||
PsiElement(Py:INTEGER_LITERAL)('2')
|
||||
PsiElement(Py:COMMA)(',')
|
||||
PsiWhiteSpace(' ')
|
||||
PyNumericLiteralExpression
|
||||
PsiElement(Py:INTEGER_LITERAL)('3')
|
||||
PsiElement(Py:RPAR)(')')
|
||||
PsiElement(Py:RBRACKET)(']')
|
||||
PsiErrorElement:Statement expected, found Py:LBRACKET
|
||||
<empty list>
|
||||
PsiWhiteSpace('\n')
|
||||
PyExpressionStatement
|
||||
PyListCompExpression
|
||||
PsiElement(Py:LBRACKET)('[')
|
||||
PyReferenceExpression: x1
|
||||
PsiElement(Py:IDENTIFIER)('x1')
|
||||
PsiWhiteSpace(' ')
|
||||
PsiElement(Py:FOR_KEYWORD)('for')
|
||||
PsiWhiteSpace(' ')
|
||||
PyParenthesizedExpression
|
||||
PsiElement(Py:LPAR)('(')
|
||||
PyAssignmentExpression
|
||||
PyTargetExpression: x1
|
||||
PsiElement(Py:IDENTIFIER)('x1')
|
||||
PsiWhiteSpace(' ')
|
||||
PsiElement(Py:COLONEQ)(':=')
|
||||
PsiWhiteSpace(' ')
|
||||
PyNumericLiteralExpression
|
||||
PsiElement(Py:INTEGER_LITERAL)('2')
|
||||
PsiElement(Py:RPAR)(')')
|
||||
PsiWhiteSpace(' ')
|
||||
PsiElement(Py:IN_KEYWORD)('in')
|
||||
PsiWhiteSpace(' ')
|
||||
PyParenthesizedExpression
|
||||
PsiElement(Py:LPAR)('(')
|
||||
PyTupleExpression
|
||||
PyNumericLiteralExpression
|
||||
PsiElement(Py:INTEGER_LITERAL)('1')
|
||||
PsiElement(Py:COMMA)(',')
|
||||
PsiWhiteSpace(' ')
|
||||
PyNumericLiteralExpression
|
||||
PsiElement(Py:INTEGER_LITERAL)('2')
|
||||
PsiElement(Py:COMMA)(',')
|
||||
PsiWhiteSpace(' ')
|
||||
PyNumericLiteralExpression
|
||||
PsiElement(Py:INTEGER_LITERAL)('3')
|
||||
PsiElement(Py:RPAR)(')')
|
||||
PsiElement(Py:RBRACKET)(']')
|
||||
@@ -488,6 +488,11 @@ public class PythonHighlightingTest extends PyTestCase {
|
||||
doTest(LanguageLevel.PYTHON38, false, false);
|
||||
}
|
||||
|
||||
// PY-36478
|
||||
public void testAssignmentExpressionAsATarget() {
|
||||
doTest(LanguageLevel.getLatest(), false, false);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private static EditorColorsScheme createTemporaryColorScheme() {
|
||||
EditorColorsManager manager = EditorColorsManager.getInstance();
|
||||
|
||||
Reference in New Issue
Block a user