Make parser stricter for assignment expressions (PY-33886, PY-36478)

Now it more follows original python grammar.

GitOrigin-RevId: f2b45554d16ff87d946950591f0e92fedcfdfaf0
This commit is contained in:
Semyon Proshev
2020-07-23 02:10:30 +03:00
committed by intellij-monorepo-bot
parent 38e8df3b12
commit 9335c066f5
11 changed files with 637 additions and 67 deletions

View File

@@ -214,7 +214,8 @@ ANN.name.used.both.as.global.and.param=Name ''{0}'' used both as a parameter and
ANN.assignment.to.keyword=Assignment to keyword ANN.assignment.to.keyword=Assignment to keyword
ANN.cannot.assign.to.debug=Cannot assign to __debug__ ANN.cannot.assign.to.debug=Cannot assign to __debug__
ANN.unparenthesized.assignment.expressions.are.prohibited.0=Unparenthesized assignment expressions are prohibited {0} 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.expressions.within.a.comprehension.cannot.be.used.in.a.class.body=Assignment expressions within a comprehension cannot be used in a class body
ANN.ignore.errors.like.this=Ignore errors like this ANN.ignore.errors.like.this=Ignore errors like this
ANN.function.cannot.be.async=function \"{0}\" cannot be async ANN.function.cannot.be.async=function \"{0}\" cannot be async

View File

@@ -31,7 +31,7 @@ import static com.jetbrains.python.PyPsiBundle.message;
* @author yole * @author yole
*/ */
public class ExpressionParsing extends Parsing { public class ExpressionParsing extends Parsing {
private static final Logger LOG = Logger.getInstance("#ru.yole.pythonlanguage.parsing.ExpressionParsing"); private static final Logger LOG = Logger.getInstance(ExpressionParsing.class);
public static final WhitespacesAndCommentsBinder CONSUME_COMMENTS_AND_SPACES_TO_LEFT = (tokens, atStreamEdge, getter) -> tokens.size(); public static final WhitespacesAndCommentsBinder CONSUME_COMMENTS_AND_SPACES_TO_LEFT = (tokens, atStreamEdge, getter) -> tokens.size();
public ExpressionParsing(ParsingContext context) { public ExpressionParsing(ParsingContext context) {
@@ -234,7 +234,7 @@ public class ExpressionParsing extends Parsing {
expr.done(PyElementTypes.LIST_LITERAL_EXPRESSION); expr.done(PyElementTypes.LIST_LITERAL_EXPRESSION);
return; return;
} }
if (!parseSingleExpression(isTargetExpression)) { if (!parseNamedTestExpression(false, isTargetExpression)) {
builder.error(message("PARSE.expected.expression")); builder.error(message("PARSE.expected.expression"));
} }
if (atForOrAsyncFor()) { if (atForOrAsyncFor()) {
@@ -248,7 +248,7 @@ public class ExpressionParsing extends Parsing {
if (atToken(PyTokenTypes.RBRACKET)) { if (atToken(PyTokenTypes.RBRACKET)) {
break; break;
} }
if (!parseSingleExpression(isTargetExpression)) { if (!parseNamedTestExpression(false, isTargetExpression)) {
builder.error(message("PARSE.expected.expr.or.comma.or.bracket")); builder.error(message("PARSE.expected.expr.or.comma.or.bracket"));
break; break;
} }
@@ -603,12 +603,12 @@ public class ExpressionParsing extends Parsing {
while (atToken(PyTokenTypes.COMMA)) { while (atToken(PyTokenTypes.COMMA)) {
nextToken(); nextToken();
SyntaxTreeBuilder.Marker sliceItemStart = myBuilder.mark(); SyntaxTreeBuilder.Marker sliceItemStart = myBuilder.mark();
parseNamedTestExpression(false, false); parseTestExpression(false, false);
if (matchToken(PyTokenTypes.COLON)) { if (matchToken(PyTokenTypes.COLON)) {
inSlice = true; inSlice = true;
parseNamedTestExpression(false, false); parseTestExpression(false, false);
if (matchToken(PyTokenTypes.COLON)) { if (matchToken(PyTokenTypes.COLON)) {
parseNamedTestExpression(false, false); parseTestExpression(false, false);
} }
} }
sliceItemStart.done(PyElementTypes.SLICE_ITEM); sliceItemStart.done(PyElementTypes.SLICE_ITEM);
@@ -674,7 +674,7 @@ public class ExpressionParsing extends Parsing {
} }
keywordArgMarker.rollbackTo(); keywordArgMarker.rollbackTo();
} }
if (!parseSingleExpression(false)) { if (!parseNamedTestExpression(false, false)) {
myBuilder.error(message("PARSE.expected.expression")); myBuilder.error(message("PARSE.expected.expression"));
break; break;
} }
@@ -759,7 +759,7 @@ public class ExpressionParsing extends Parsing {
} }
public boolean parseSingleExpression(boolean isTargetExpression) { public boolean parseSingleExpression(boolean isTargetExpression) {
return parseNamedTestExpression(false, isTargetExpression); return parseTestExpression(false, isTargetExpression);
} }
public boolean parseOldExpression() { public boolean parseOldExpression() {
@@ -769,7 +769,7 @@ public class ExpressionParsing extends Parsing {
return parseORTestExpression(false, false); return parseORTestExpression(false, false);
} }
private boolean parseNamedTestExpression(boolean stopOnIn, boolean isTargetExpression) { public boolean parseNamedTestExpression(boolean stopOnIn, boolean isTargetExpression) {
final SyntaxTreeBuilder.Marker expr = myBuilder.mark(); final SyntaxTreeBuilder.Marker expr = myBuilder.mark();
if (isIdentifier(myBuilder) && myBuilder.lookAhead(1) == PyTokenTypes.COLONEQ) { if (isIdentifier(myBuilder) && myBuilder.lookAhead(1) == PyTokenTypes.COLONEQ) {
@@ -839,7 +839,7 @@ public class ExpressionParsing extends Parsing {
} }
else { else {
myBuilder.advanceLexer(); myBuilder.advanceLexer();
if (!parseNamedTestExpression(stopOnIn, isTargetExpression)) { if (!parseTestExpression(stopOnIn, isTargetExpression)) {
myBuilder.error(message("PARSE.expected.expression")); myBuilder.error(message("PARSE.expected.expression"));
} }
} }

View File

@@ -103,7 +103,7 @@ public class FunctionParsing extends Parsing {
* Parses decorator expression after {@code @} according to PEP-614 * Parses decorator expression after {@code @} according to PEP-614
*/ */
public void parseDecoratorExpression() { public void parseDecoratorExpression() {
if (!getExpressionParser().parseSingleExpression(false)) { if (!getExpressionParser().parseNamedTestExpression(false, false)) {
myBuilder.error(message("PARSE.expected.expression")); myBuilder.error(message("PARSE.expected.expression"));
} }
} }

View File

@@ -603,7 +603,7 @@ public class StatementParsing extends Parsing implements ITokenTypeRemapper {
final SyntaxTreeBuilder.Marker ifStatement = myBuilder.mark(); final SyntaxTreeBuilder.Marker ifStatement = myBuilder.mark();
final SyntaxTreeBuilder.Marker ifPart = myBuilder.mark(); final SyntaxTreeBuilder.Marker ifPart = myBuilder.mark();
myBuilder.advanceLexer(); myBuilder.advanceLexer();
if (!getExpressionParser().parseSingleExpression(false)) { if (!getExpressionParser().parseNamedTestExpression(false, false)) {
myBuilder.error(PyPsiBundle.message("PARSE.expected.expression")); myBuilder.error(PyPsiBundle.message("PARSE.expected.expression"));
} }
parseColonAndSuite(); parseColonAndSuite();
@@ -611,7 +611,7 @@ public class StatementParsing extends Parsing implements ITokenTypeRemapper {
SyntaxTreeBuilder.Marker elifPart = myBuilder.mark(); SyntaxTreeBuilder.Marker elifPart = myBuilder.mark();
while (myBuilder.getTokenType() == elifKeyword) { while (myBuilder.getTokenType() == elifKeyword) {
myBuilder.advanceLexer(); myBuilder.advanceLexer();
if (!getExpressionParser().parseSingleExpression(false)) { if (!getExpressionParser().parseNamedTestExpression(false, false)) {
myBuilder.error(PyPsiBundle.message("PARSE.expected.expression")); myBuilder.error(PyPsiBundle.message("PARSE.expected.expression"));
} }
parseColonAndSuite(); parseColonAndSuite();
@@ -682,7 +682,7 @@ public class StatementParsing extends Parsing implements ITokenTypeRemapper {
final SyntaxTreeBuilder.Marker statement = myBuilder.mark(); final SyntaxTreeBuilder.Marker statement = myBuilder.mark();
final SyntaxTreeBuilder.Marker whilePart = myBuilder.mark(); final SyntaxTreeBuilder.Marker whilePart = myBuilder.mark();
myBuilder.advanceLexer(); myBuilder.advanceLexer();
if (!getExpressionParser().parseSingleExpression(false)) { if (!getExpressionParser().parseNamedTestExpression(false, false)) {
myBuilder.error(PyPsiBundle.message("PARSE.expected.expression")); myBuilder.error(PyPsiBundle.message("PARSE.expected.expression"));
} }
parseColonAndSuite(); parseColonAndSuite();
@@ -768,7 +768,9 @@ public class StatementParsing extends Parsing implements ITokenTypeRemapper {
myBuilder.advanceLexer(); myBuilder.advanceLexer();
while (true) { while (true) {
SyntaxTreeBuilder.Marker withItem = myBuilder.mark(); SyntaxTreeBuilder.Marker withItem = myBuilder.mark();
getExpressionParser().parseExpression(); if (!getExpressionParser().parseSingleExpression(false)) {
myBuilder.error(PyPsiBundle.message("PARSE.expected.expression"));
}
if (myBuilder.getTokenType() == PyTokenTypes.AS_KEYWORD) { if (myBuilder.getTokenType() == PyTokenTypes.AS_KEYWORD) {
myBuilder.advanceLexer(); myBuilder.advanceLexer();
if (!getExpressionParser().parseSingleExpression(true)) { if (!getExpressionParser().parseSingleExpression(true)) {

View File

@@ -27,7 +27,6 @@ import com.jetbrains.python.psi.*;
import com.jetbrains.python.sdk.PythonSdkUtil; import com.jetbrains.python.sdk.PythonSdkUtil;
import org.jetbrains.annotations.Nls; import org.jetbrains.annotations.Nls;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import static com.jetbrains.python.PyPsiBundle.message; import static com.jetbrains.python.PyPsiBundle.message;
@@ -45,8 +44,13 @@ public class AssignTargetAnnotator extends PyAnnotator {
expression.accept(new ExprVisitor(Operation.Assign)); expression.accept(new ExprVisitor(Operation.Assign));
} }
errorOnUnparenthesizedAssignmentExpression(node.getAssignedValue(), PyExpression expression = node.getAssignedValue();
"at the top level of the right hand side of an assignment statement"); if (expression instanceof PyAssignmentExpression) {
getHolder()
.newAnnotation(HighlightSeverity.ERROR, PyBundle.message("ANN.unparenthesized.assignment.expression.value"))
.range(expression)
.create();
}
} }
@Override @Override
@@ -88,27 +92,13 @@ public class AssignTargetAnnotator extends PyAnnotator {
@Override @Override
public void visitPyExpressionStatement(@NotNull PyExpressionStatement node) { public void visitPyExpressionStatement(@NotNull PyExpressionStatement node) {
errorOnUnparenthesizedAssignmentExpression(node.getExpression(), "at the top level of an expression statement"); PyExpression expression = node.getExpression();
} if (expression instanceof PyAssignmentExpression) {
getHolder()
@Override .newAnnotation(HighlightSeverity.ERROR, PyBundle.message("ANN.unparenthesized.assignment.expression.statement"))
public void visitPyNamedParameter(@NotNull PyNamedParameter node) { .range(expression)
errorOnUnparenthesizedAssignmentExpression(node.getDefaultValue(), "at the top level of a function default value"); .create();
} }
@Override
public void visitPyKeywordArgument(@NotNull PyKeywordArgument node) {
errorOnUnparenthesizedAssignmentExpression(node.getValueExpression(), "for the value of a keyword argument in a call");
}
@Override
public void visitPyLambdaExpression(@NotNull PyLambdaExpression node) {
errorOnUnparenthesizedAssignmentExpression(node.getBody(), "at the top level of a lambda function");
}
@Override
public void visitPyAnnotation(@NotNull PyAnnotation node) {
errorOnUnparenthesizedAssignmentExpression(node.getValue(), "as annotations for arguments, return values and assignments");
} }
@Override @Override
@@ -120,13 +110,6 @@ public class AssignTargetAnnotator extends PyAnnotator {
} }
} }
private void errorOnUnparenthesizedAssignmentExpression(@Nullable PyExpression expression, @NotNull String suffix) {
if (expression instanceof PyAssignmentExpression) {
getHolder().newAnnotation(HighlightSeverity.ERROR,
PyBundle.message("ANN.unparenthesized.assignment.expressions.are.prohibited.0", suffix)).range(expression).create();
}
}
private class ExprVisitor extends PyElementVisitor { private class ExprVisitor extends PyElementVisitor {
private final Operation myOp; private final Operation myOp;
private final @Nls String DELETING_NONE = message("ANN.deleting.none"); private final @Nls String DELETING_NONE = message("ANN.deleting.none");

View File

@@ -4,23 +4,5 @@
y0 = <error descr="Unparenthesized assignment expressions are prohibited at the top level of the right hand side of an assignment statement">y1 := f(x)</error> # INVALID y0 = <error descr="Unparenthesized assignment expressions are prohibited at the top level of the right hand side of an assignment statement">y1 := f(x)</error> # INVALID
y0 = (y1 := f(x)) # Valid, though discouraged y0 = (y1 := f(x)) # Valid, though discouraged
foo(x = <error descr="Unparenthesized assignment expressions are prohibited for the value of a keyword argument in a call">y := f(x)</error>) # INVALID
foo(x=(y := f(x))) # Valid, though probably confusing
def foo(answer = <error descr="Unparenthesized assignment expressions are prohibited at the top level of a function default value">p := 42</error>): # INVALID
pass
def foo(answer=(p := 42)): # Valid, though not great style
pass
def foo(answer: <error descr="Unparenthesized assignment expressions are prohibited as annotations for arguments, return values and assignments">p := 42</error> = 5): # INVALID
pass
def foo(answer: (p := 42) = 5): # Valid, but probably never useful
pass
(lambda: <error descr="Unparenthesized assignment expressions are prohibited at the top level of a lambda function">x := 1</error>) # INVALID
lambda: (x := 1) # Valid, but unlikely to be useful
(x := lambda: 1) # Valid
lambda line: (m := re.match(pattern, line)) and m.group(1) # Valid
class A: class A:
[<error descr="Assignment expressions within a comprehension cannot be used in a class body">y := i</error> for i in range(2)] [<error descr="Assignment expressions within a comprehension cannot be used in a class body">y := i</error> for i in range(2)]

View File

@@ -16,4 +16,7 @@ foo(x := 1, cat='2')
(px, py, pz := position) # pz will be position, px and py are references (px, py, pz := position) # pz will be position, px and py are references
x = (y := z) = 'spam' # z is a reference x = (y := z) = 'spam' # z is a reference
result_list = [a := 1]
result_list = [(a := 1)]

View File

@@ -208,4 +208,43 @@ PyFile:AssignmentExpressions.py
PyStringLiteralExpression: spam PyStringLiteralExpression: spam
PsiElement(Py:SINGLE_QUOTED_STRING)(''spam'') PsiElement(Py:SINGLE_QUOTED_STRING)(''spam'')
PsiWhiteSpace(' ') PsiWhiteSpace(' ')
PsiComment(Py:END_OF_LINE_COMMENT)('# z is a reference') PsiComment(Py:END_OF_LINE_COMMENT)('# z is a reference')
PsiWhiteSpace('\n\n')
PyAssignmentStatement
PyTargetExpression: result_list
PsiElement(Py:IDENTIFIER)('result_list')
PsiWhiteSpace(' ')
PsiElement(Py:EQ)('=')
PsiWhiteSpace(' ')
PyListLiteralExpression
PsiElement(Py:LBRACKET)('[')
PyAssignmentExpression
PyTargetExpression: a
PsiElement(Py:IDENTIFIER)('a')
PsiWhiteSpace(' ')
PsiElement(Py:COLONEQ)(':=')
PsiWhiteSpace(' ')
PyNumericLiteralExpression
PsiElement(Py:INTEGER_LITERAL)('1')
PsiElement(Py:RBRACKET)(']')
PsiWhiteSpace('\n')
PyAssignmentStatement
PyTargetExpression: result_list
PsiElement(Py:IDENTIFIER)('result_list')
PsiWhiteSpace(' ')
PsiElement(Py:EQ)('=')
PsiWhiteSpace(' ')
PyListLiteralExpression
PsiElement(Py:LBRACKET)('[')
PyParenthesizedExpression
PsiElement(Py:LPAR)('(')
PyAssignmentExpression
PyTargetExpression: a
PsiElement(Py:IDENTIFIER)('a')
PsiWhiteSpace(' ')
PsiElement(Py:COLONEQ)(':=')
PsiWhiteSpace(' ')
PyNumericLiteralExpression
PsiElement(Py:INTEGER_LITERAL)('1')
PsiElement(Py:RPAR)(')')
PsiElement(Py:RBRACKET)(']')

View File

@@ -0,0 +1,34 @@
foo(x = y := f(x)) # INVALID
foo(x=(y := f(x))) # Valid, though probably confusing
def foo(answer = p := 42): # INVALID
pass
def foo(answer=(p := 42)): # Valid, though not great style
pass
def foo(answer: p := 42 = 5): # INVALID
pass
def foo(answer: (p := 42) = 5): # Valid, but probably never useful
pass
(lambda: x := 1) # INVALID
lambda: (x := 1) # Valid, but unlikely to be useful
result_set = {a := 1} # INVALID
result_set = {(a := 1)}
result_dict = {a := 1 : b := 2} # INVALID
result_dict = {(a := 1) : (b := 2)}
assert a := 1 # INVALID
assert (a := 1)
l = [1, 2]
l[a := 0] # INVALID
l[(a := 0)]
with f := open('file.txt'): # INVALID
pass
with (f := open('file.txt')):
pass

View File

@@ -0,0 +1,521 @@
PyFile:InvalidNonParenthesizedAssignmentExpressions.py
PyAssignmentExpression
PyCallExpression: foo
PyReferenceExpression: foo
PsiElement(Py:IDENTIFIER)('foo')
PyArgumentList
PsiElement(Py:LPAR)('(')
PyKeywordArgumentImpl: x
PsiElement(Py:IDENTIFIER)('x')
PsiWhiteSpace(' ')
PsiElement(Py:EQ)('=')
PsiWhiteSpace(' ')
PyReferenceExpression: y
PsiElement(Py:IDENTIFIER)('y')
PsiErrorElement:',' or ')' expected
<empty list>
PsiWhiteSpace(' ')
PsiElement(Py:COLONEQ)(':=')
PsiWhiteSpace(' ')
PyCallExpression: f
PyReferenceExpression: f
PsiElement(Py:IDENTIFIER)('f')
PyArgumentList
PsiElement(Py:LPAR)('(')
PyReferenceExpression: x
PsiElement(Py:IDENTIFIER)('x')
PsiElement(Py:RPAR)(')')
PsiElement(Py:RPAR)(')')
PsiErrorElement:Statement expected, found Py:IDENTIFIER
<empty list>
PsiWhiteSpace(' ')
PsiComment(Py:END_OF_LINE_COMMENT)('# INVALID')
PsiWhiteSpace('\n')
PyExpressionStatement
PyCallExpression: foo
PyReferenceExpression: foo
PsiElement(Py:IDENTIFIER)('foo')
PyArgumentList
PsiElement(Py:LPAR)('(')
PyKeywordArgumentImpl: x
PsiElement(Py:IDENTIFIER)('x')
PsiElement(Py:EQ)('=')
PyParenthesizedExpression
PsiElement(Py:LPAR)('(')
PyAssignmentExpression
PyTargetExpression: y
PsiElement(Py:IDENTIFIER)('y')
PsiWhiteSpace(' ')
PsiElement(Py:COLONEQ)(':=')
PsiWhiteSpace(' ')
PyCallExpression: f
PyReferenceExpression: f
PsiElement(Py:IDENTIFIER)('f')
PyArgumentList
PsiElement(Py:LPAR)('(')
PyReferenceExpression: x
PsiElement(Py:IDENTIFIER)('x')
PsiElement(Py:RPAR)(')')
PsiElement(Py:RPAR)(')')
PsiElement(Py:RPAR)(')')
PsiWhiteSpace(' ')
PsiComment(Py:END_OF_LINE_COMMENT)('# Valid, though probably confusing')
PsiWhiteSpace('\n\n')
PyFunction('foo')
PsiElement(Py:DEF_KEYWORD)('def')
PsiWhiteSpace(' ')
PsiElement(Py:IDENTIFIER)('foo')
PyParameterList
PsiElement(Py:LPAR)('(')
PyNamedParameter('answer')
PsiElement(Py:IDENTIFIER)('answer')
PsiWhiteSpace(' ')
PsiElement(Py:EQ)('=')
PsiWhiteSpace(' ')
PyReferenceExpression: p
PsiElement(Py:IDENTIFIER)('p')
PsiErrorElement:',' or '(' or ')' expected
<empty list>
PsiWhiteSpace(' ')
PyStatementList
PsiElement(Py:COLONEQ)(':=')
PsiErrorElement:Statement expected, found Py:COLONEQ
<empty list>
PsiWhiteSpace(' ')
PyExpressionStatement
PyNumericLiteralExpression
PsiElement(Py:INTEGER_LITERAL)('42')
PsiErrorElement:End of statement expected
<empty list>
PsiElement(Py:RPAR)(')')
PsiErrorElement:Statement expected, found Py:RPAR
<empty list>
PsiElement(Py:COLON)(':')
PsiErrorElement:Statement expected, found Py:COLON
<empty list>
PsiWhiteSpace(' ')
PsiComment(Py:END_OF_LINE_COMMENT)('# INVALID')
PsiWhiteSpace('\n ')
PsiErrorElement:Unexpected indent
<empty list>
PyPassStatement
PsiElement(Py:PASS_KEYWORD)('pass')
PsiErrorElement:Statement expected, found Py:DEDENT
<empty list>
PsiWhiteSpace('\n')
PyFunction('foo')
PsiElement(Py:DEF_KEYWORD)('def')
PsiWhiteSpace(' ')
PsiElement(Py:IDENTIFIER)('foo')
PyParameterList
PsiElement(Py:LPAR)('(')
PyNamedParameter('answer')
PsiElement(Py:IDENTIFIER)('answer')
PsiElement(Py:EQ)('=')
PyParenthesizedExpression
PsiElement(Py:LPAR)('(')
PyAssignmentExpression
PyTargetExpression: p
PsiElement(Py:IDENTIFIER)('p')
PsiWhiteSpace(' ')
PsiElement(Py:COLONEQ)(':=')
PsiWhiteSpace(' ')
PyNumericLiteralExpression
PsiElement(Py:INTEGER_LITERAL)('42')
PsiElement(Py:RPAR)(')')
PsiElement(Py:RPAR)(')')
PsiElement(Py:COLON)(':')
PsiWhiteSpace(' ')
PsiComment(Py:END_OF_LINE_COMMENT)('# Valid, though not great style')
PsiWhiteSpace('\n ')
PyStatementList
PyPassStatement
PsiElement(Py:PASS_KEYWORD)('pass')
PsiWhiteSpace('\n\n')
PyFunction('foo')
PsiElement(Py:DEF_KEYWORD)('def')
PsiWhiteSpace(' ')
PsiElement(Py:IDENTIFIER)('foo')
PyParameterList
PsiElement(Py:LPAR)('(')
PyNamedParameter('answer')
PsiElement(Py:IDENTIFIER)('answer')
PyAnnotation
PsiElement(Py:COLON)(':')
PsiWhiteSpace(' ')
PyReferenceExpression: p
PsiElement(Py:IDENTIFIER)('p')
PsiErrorElement:',' or '(' or ')' expected
<empty list>
PsiWhiteSpace(' ')
PyStatementList
PsiElement(Py:COLONEQ)(':=')
PsiErrorElement:Statement expected, found Py:COLONEQ
<empty list>
PsiWhiteSpace(' ')
PyAssignmentStatement
PyNumericLiteralExpression
PsiElement(Py:INTEGER_LITERAL)('42')
PsiWhiteSpace(' ')
PsiElement(Py:EQ)('=')
PsiWhiteSpace(' ')
PyNumericLiteralExpression
PsiElement(Py:INTEGER_LITERAL)('5')
PsiErrorElement:End of statement expected
<empty list>
PsiElement(Py:RPAR)(')')
PsiErrorElement:Statement expected, found Py:RPAR
<empty list>
PsiElement(Py:COLON)(':')
PsiErrorElement:Statement expected, found Py:COLON
<empty list>
PsiWhiteSpace(' ')
PsiComment(Py:END_OF_LINE_COMMENT)('# INVALID')
PsiWhiteSpace('\n ')
PsiErrorElement:Unexpected indent
<empty list>
PyPassStatement
PsiElement(Py:PASS_KEYWORD)('pass')
PsiErrorElement:Statement expected, found Py:DEDENT
<empty list>
PsiWhiteSpace('\n')
PyFunction('foo')
PsiElement(Py:DEF_KEYWORD)('def')
PsiWhiteSpace(' ')
PsiElement(Py:IDENTIFIER)('foo')
PyParameterList
PsiElement(Py:LPAR)('(')
PyNamedParameter('answer')
PsiElement(Py:IDENTIFIER)('answer')
PyAnnotation
PsiElement(Py:COLON)(':')
PsiWhiteSpace(' ')
PyParenthesizedExpression
PsiElement(Py:LPAR)('(')
PyAssignmentExpression
PyTargetExpression: p
PsiElement(Py:IDENTIFIER)('p')
PsiWhiteSpace(' ')
PsiElement(Py:COLONEQ)(':=')
PsiWhiteSpace(' ')
PyNumericLiteralExpression
PsiElement(Py:INTEGER_LITERAL)('42')
PsiElement(Py:RPAR)(')')
PsiWhiteSpace(' ')
PsiElement(Py:EQ)('=')
PsiWhiteSpace(' ')
PyNumericLiteralExpression
PsiElement(Py:INTEGER_LITERAL)('5')
PsiElement(Py:RPAR)(')')
PsiElement(Py:COLON)(':')
PsiWhiteSpace(' ')
PsiComment(Py:END_OF_LINE_COMMENT)('# Valid, but probably never useful')
PsiWhiteSpace('\n ')
PyStatementList
PyPassStatement
PsiElement(Py:PASS_KEYWORD)('pass')
PsiWhiteSpace('\n\n')
PyExpressionStatement
PyParenthesizedExpression
PsiElement(Py:LPAR)('(')
PyAssignmentExpression
PyLambdaExpression
PsiElement(Py:LAMBDA_KEYWORD)('lambda')
PyParameterList
<empty list>
PsiElement(Py:COLON)(':')
PsiWhiteSpace(' ')
PyReferenceExpression: x
PsiElement(Py:IDENTIFIER)('x')
PsiErrorElement:Identifier expected
<empty list>
PsiWhiteSpace(' ')
PsiElement(Py:COLONEQ)(':=')
PsiWhiteSpace(' ')
PyNumericLiteralExpression
PsiElement(Py:INTEGER_LITERAL)('1')
PsiElement(Py:RPAR)(')')
PsiWhiteSpace(' ')
PsiComment(Py:END_OF_LINE_COMMENT)('# INVALID')
PsiWhiteSpace('\n')
PyExpressionStatement
PyLambdaExpression
PsiElement(Py:LAMBDA_KEYWORD)('lambda')
PyParameterList
<empty list>
PsiElement(Py:COLON)(':')
PsiWhiteSpace(' ')
PyParenthesizedExpression
PsiElement(Py:LPAR)('(')
PyAssignmentExpression
PyTargetExpression: x
PsiElement(Py:IDENTIFIER)('x')
PsiWhiteSpace(' ')
PsiElement(Py:COLONEQ)(':=')
PsiWhiteSpace(' ')
PyNumericLiteralExpression
PsiElement(Py:INTEGER_LITERAL)('1')
PsiElement(Py:RPAR)(')')
PsiWhiteSpace(' ')
PsiComment(Py:END_OF_LINE_COMMENT)('# Valid, but unlikely to be useful')
PsiWhiteSpace('\n\n')
PyAssignmentStatement
PyTargetExpression: result_set
PsiElement(Py:IDENTIFIER)('result_set')
PsiWhiteSpace(' ')
PsiElement(Py:EQ)('=')
PsiWhiteSpace(' ')
PyAssignmentExpression
PyDictLiteralExpression
PsiElement(Py:LBRACE)('{')
PyReferenceExpression: a
PsiElement(Py:IDENTIFIER)('a')
PsiErrorElement:Expression expected
<empty list>
PsiWhiteSpace(' ')
PsiElement(Py:COLONEQ)(':=')
PsiWhiteSpace(' ')
PyNumericLiteralExpression
PsiElement(Py:INTEGER_LITERAL)('1')
PsiErrorElement:Expression expected
<empty list>
PsiElement(Py:RBRACE)('}')
PsiErrorElement:Statement expected, found Py:RBRACE
<empty list>
PsiWhiteSpace(' ')
PsiComment(Py:END_OF_LINE_COMMENT)('# INVALID')
PsiWhiteSpace('\n')
PyAssignmentStatement
PyTargetExpression: result_set
PsiElement(Py:IDENTIFIER)('result_set')
PsiWhiteSpace(' ')
PsiElement(Py:EQ)('=')
PsiWhiteSpace(' ')
PySetLiteralExpression
PsiElement(Py:LBRACE)('{')
PyParenthesizedExpression
PsiElement(Py:LPAR)('(')
PyAssignmentExpression
PyTargetExpression: a
PsiElement(Py:IDENTIFIER)('a')
PsiWhiteSpace(' ')
PsiElement(Py:COLONEQ)(':=')
PsiWhiteSpace(' ')
PyNumericLiteralExpression
PsiElement(Py:INTEGER_LITERAL)('1')
PsiElement(Py:RPAR)(')')
PsiElement(Py:RBRACE)('}')
PsiWhiteSpace('\n\n')
PyAssignmentStatement
PyTargetExpression: result_dict
PsiElement(Py:IDENTIFIER)('result_dict')
PsiWhiteSpace(' ')
PsiElement(Py:EQ)('=')
PsiWhiteSpace(' ')
PyAssignmentExpression
PyDictLiteralExpression
PsiElement(Py:LBRACE)('{')
PyReferenceExpression: a
PsiElement(Py:IDENTIFIER)('a')
PsiErrorElement:Expression expected
<empty list>
PsiWhiteSpace(' ')
PsiElement(Py:COLONEQ)(':=')
PsiWhiteSpace(' ')
PyNumericLiteralExpression
PsiElement(Py:INTEGER_LITERAL)('1')
PsiErrorElement:Expression expected
<empty list>
PsiWhiteSpace(' ')
PsiElement(Py:COLON)(':')
PsiErrorElement:Statement expected, found Py:COLON
<empty list>
PsiWhiteSpace(' ')
PyExpressionStatement
PyAssignmentExpression
PyTargetExpression: b
PsiElement(Py:IDENTIFIER)('b')
PsiWhiteSpace(' ')
PsiElement(Py:COLONEQ)(':=')
PsiWhiteSpace(' ')
PyNumericLiteralExpression
PsiElement(Py:INTEGER_LITERAL)('2')
PsiErrorElement:End of statement expected
<empty list>
PsiElement(Py:RBRACE)('}')
PsiErrorElement:Statement expected, found Py:RBRACE
<empty list>
PsiWhiteSpace(' ')
PsiComment(Py:END_OF_LINE_COMMENT)('# INVALID')
PsiWhiteSpace('\n')
PyAssignmentStatement
PyTargetExpression: result_dict
PsiElement(Py:IDENTIFIER)('result_dict')
PsiWhiteSpace(' ')
PsiElement(Py:EQ)('=')
PsiWhiteSpace(' ')
PyDictLiteralExpression
PsiElement(Py:LBRACE)('{')
PyKeyValueExpression
PyParenthesizedExpression
PsiElement(Py:LPAR)('(')
PyAssignmentExpression
PyTargetExpression: a
PsiElement(Py:IDENTIFIER)('a')
PsiWhiteSpace(' ')
PsiElement(Py:COLONEQ)(':=')
PsiWhiteSpace(' ')
PyNumericLiteralExpression
PsiElement(Py:INTEGER_LITERAL)('1')
PsiElement(Py:RPAR)(')')
PsiWhiteSpace(' ')
PsiElement(Py:COLON)(':')
PsiWhiteSpace(' ')
PyParenthesizedExpression
PsiElement(Py:LPAR)('(')
PyAssignmentExpression
PyTargetExpression: b
PsiElement(Py:IDENTIFIER)('b')
PsiWhiteSpace(' ')
PsiElement(Py:COLONEQ)(':=')
PsiWhiteSpace(' ')
PyNumericLiteralExpression
PsiElement(Py:INTEGER_LITERAL)('2')
PsiElement(Py:RPAR)(')')
PsiElement(Py:RBRACE)('}')
PsiWhiteSpace('\n\n')
PyAssertStatement
PsiElement(Py:ASSERT_KEYWORD)('assert')
PsiWhiteSpace(' ')
PyReferenceExpression: a
PsiElement(Py:IDENTIFIER)('a')
PsiErrorElement:End of statement expected
<empty list>
PsiWhiteSpace(' ')
PsiElement(Py:COLONEQ)(':=')
PsiErrorElement:Statement expected, found Py:COLONEQ
<empty list>
PsiWhiteSpace(' ')
PyExpressionStatement
PyNumericLiteralExpression
PsiElement(Py:INTEGER_LITERAL)('1')
PsiWhiteSpace(' ')
PsiComment(Py:END_OF_LINE_COMMENT)('# INVALID')
PsiWhiteSpace('\n')
PyAssertStatement
PsiElement(Py:ASSERT_KEYWORD)('assert')
PsiWhiteSpace(' ')
PyParenthesizedExpression
PsiElement(Py:LPAR)('(')
PyAssignmentExpression
PyTargetExpression: a
PsiElement(Py:IDENTIFIER)('a')
PsiWhiteSpace(' ')
PsiElement(Py:COLONEQ)(':=')
PsiWhiteSpace(' ')
PyNumericLiteralExpression
PsiElement(Py:INTEGER_LITERAL)('1')
PsiElement(Py:RPAR)(')')
PsiWhiteSpace('\n\n')
PyAssignmentStatement
PyTargetExpression: l
PsiElement(Py:IDENTIFIER)('l')
PsiWhiteSpace(' ')
PsiElement(Py:EQ)('=')
PsiWhiteSpace(' ')
PyListLiteralExpression
PsiElement(Py:LBRACKET)('[')
PyNumericLiteralExpression
PsiElement(Py:INTEGER_LITERAL)('1')
PsiElement(Py:COMMA)(',')
PsiWhiteSpace(' ')
PyNumericLiteralExpression
PsiElement(Py:INTEGER_LITERAL)('2')
PsiElement(Py:RBRACKET)(']')
PsiWhiteSpace('\n')
PyAssignmentExpression
PySubscriptionExpression
PyReferenceExpression: l
PsiElement(Py:IDENTIFIER)('l')
PsiElement(Py:LBRACKET)('[')
PyReferenceExpression: a
PsiElement(Py:IDENTIFIER)('a')
PsiErrorElement:']' expected
<empty list>
PsiWhiteSpace(' ')
PsiElement(Py:COLONEQ)(':=')
PsiWhiteSpace(' ')
PyNumericLiteralExpression
PsiElement(Py:INTEGER_LITERAL)('0')
PsiElement(Py:RBRACKET)(']')
PsiErrorElement:Statement expected, found Py:IDENTIFIER
<empty list>
PsiWhiteSpace(' ')
PsiComment(Py:END_OF_LINE_COMMENT)('# INVALID')
PsiWhiteSpace('\n')
PyExpressionStatement
PySubscriptionExpression
PyReferenceExpression: l
PsiElement(Py:IDENTIFIER)('l')
PsiElement(Py:LBRACKET)('[')
PyParenthesizedExpression
PsiElement(Py:LPAR)('(')
PyAssignmentExpression
PyTargetExpression: a
PsiElement(Py:IDENTIFIER)('a')
PsiWhiteSpace(' ')
PsiElement(Py:COLONEQ)(':=')
PsiWhiteSpace(' ')
PyNumericLiteralExpression
PsiElement(Py:INTEGER_LITERAL)('0')
PsiElement(Py:RPAR)(')')
PsiElement(Py:RBRACKET)(']')
PsiWhiteSpace('\n\n')
PyWithStatement
PsiElement(Py:WITH_KEYWORD)('with')
PsiWhiteSpace(' ')
PyWithItem
PyReferenceExpression: f
PsiElement(Py:IDENTIFIER)('f')
PsiWhiteSpace(' ')
PsiErrorElement:':' expected
PsiElement(Py:COLONEQ)(':=')
PsiWhiteSpace(' ')
PsiElement(Py:IDENTIFIER)('open')
PsiElement(Py:LPAR)('(')
PsiElement(Py:SINGLE_QUOTED_STRING)(''file.txt'')
PsiElement(Py:RPAR)(')')
PsiElement(Py:COLON)(':')
PsiWhiteSpace(' ')
PsiComment(Py:END_OF_LINE_COMMENT)('# INVALID')
PsiWhiteSpace('\n ')
PyStatementList
PyPassStatement
PsiElement(Py:PASS_KEYWORD)('pass')
PsiWhiteSpace('\n\n')
PyWithStatement
PsiElement(Py:WITH_KEYWORD)('with')
PsiWhiteSpace(' ')
PyWithItem
PyParenthesizedExpression
PsiElement(Py:LPAR)('(')
PyAssignmentExpression
PyTargetExpression: f
PsiElement(Py:IDENTIFIER)('f')
PsiWhiteSpace(' ')
PsiElement(Py:COLONEQ)(':=')
PsiWhiteSpace(' ')
PyCallExpression: open
PyReferenceExpression: open
PsiElement(Py:IDENTIFIER)('open')
PyArgumentList
PsiElement(Py:LPAR)('(')
PyStringLiteralExpression: file.txt
PsiElement(Py:SINGLE_QUOTED_STRING)(''file.txt'')
PsiElement(Py:RPAR)(')')
PsiElement(Py:RPAR)(')')
PsiElement(Py:COLON)(':')
PsiWhiteSpace('\n ')
PyStatementList
PyPassStatement
PsiElement(Py:PASS_KEYWORD)('pass')

View File

@@ -930,6 +930,11 @@ public class PythonParsingTest extends ParsingTestCase {
doTest(LanguageLevel.PYTHON38); doTest(LanguageLevel.PYTHON38);
} }
// PY-33886, PY-36478
public void testInvalidNonParenthesizedAssignmentExpressions() {
doTest(LanguageLevel.getLatest());
}
// PY-33886 // PY-33886
public void testAssignmentExpressionsInFString() { public void testAssignmentExpressionsInFString() {
doTest(LanguageLevel.PYTHON38); doTest(LanguageLevel.PYTHON38);