mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-01-06 03:21:12 +07:00
PY-48940: Allow unparenthesized assignment expressions within sequence indexes, but not in slices.
(cherry picked from commit c8bfbc4149b840033f9c24a9190860abdd0ff6a8) IJ-CR-11891 GitOrigin-RevId: 3ce6f98d5b031c705c461505a5c307c0241ce97e
This commit is contained in:
committed by
intellij-monorepo-bot
parent
a86c492a92
commit
333e20478b
@@ -525,41 +525,7 @@ public class ExpressionParsing extends Parsing {
|
||||
}
|
||||
else if (tokenType == PyTokenTypes.LBRACKET) {
|
||||
myBuilder.advanceLexer();
|
||||
SyntaxTreeBuilder.Marker sliceOrTupleStart = myBuilder.mark();
|
||||
SyntaxTreeBuilder.Marker sliceItemStart = myBuilder.mark();
|
||||
if (atToken(PyTokenTypes.COLON)) {
|
||||
sliceOrTupleStart.drop();
|
||||
SyntaxTreeBuilder.Marker sliceMarker = myBuilder.mark();
|
||||
sliceMarker.done(PyElementTypes.EMPTY_EXPRESSION);
|
||||
parseSliceEnd(expr, sliceItemStart);
|
||||
}
|
||||
else {
|
||||
boolean hadExpression = parseSingleExpression(false);
|
||||
if (atToken(PyTokenTypes.COLON)) {
|
||||
sliceOrTupleStart.drop();
|
||||
parseSliceEnd(expr, sliceItemStart);
|
||||
}
|
||||
else if (atToken(PyTokenTypes.COMMA)) {
|
||||
sliceItemStart.done(PyElementTypes.SLICE_ITEM);
|
||||
if (!parseSliceListTail(expr, sliceOrTupleStart)) {
|
||||
sliceOrTupleStart.rollbackTo();
|
||||
if (!parseTupleExpression(false, false, false)) {
|
||||
myBuilder.error(message("tuple.expression.expected"));
|
||||
}
|
||||
checkMatches(PyTokenTypes.RBRACKET, message("PARSE.expected.rbracket"));
|
||||
expr.done(PyElementTypes.SUBSCRIPTION_EXPRESSION);
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (!hadExpression) {
|
||||
myBuilder.error(message("PARSE.expected.expression"));
|
||||
}
|
||||
sliceOrTupleStart.drop();
|
||||
sliceItemStart.drop();
|
||||
checkMatches(PyTokenTypes.RBRACKET, message("PARSE.expected.rbracket"));
|
||||
expr.done(PyElementTypes.SUBSCRIPTION_EXPRESSION);
|
||||
}
|
||||
}
|
||||
parseSliceOrSubscriptionExpression(expr, false);
|
||||
if (isTargetExpression && !recastQualifier) {
|
||||
recastFirstIdentifier = true; // subscription is always a reference
|
||||
recastQualifier = true; // recast non-first qualifiers too
|
||||
@@ -581,6 +547,49 @@ public class ExpressionParsing extends Parsing {
|
||||
return true;
|
||||
}
|
||||
|
||||
private void parseSliceOrSubscriptionExpression(@NotNull SyntaxTreeBuilder.Marker expr, boolean isSlice) {
|
||||
SyntaxTreeBuilder.Marker sliceOrTupleStart = myBuilder.mark();
|
||||
SyntaxTreeBuilder.Marker sliceItemStart = myBuilder.mark();
|
||||
if (atToken(PyTokenTypes.COLON)) {
|
||||
sliceOrTupleStart.drop();
|
||||
SyntaxTreeBuilder.Marker sliceMarker = myBuilder.mark();
|
||||
sliceMarker.done(PyElementTypes.EMPTY_EXPRESSION);
|
||||
parseSliceEnd(expr, sliceItemStart);
|
||||
}
|
||||
else {
|
||||
var hadExpression = isSlice ? parseSingleExpression(false) : parseNamedTestExpression(false, false);
|
||||
if (atToken(PyTokenTypes.COLON)) {
|
||||
if (!isSlice) {
|
||||
sliceOrTupleStart.rollbackTo();
|
||||
parseSliceOrSubscriptionExpression(expr, true);
|
||||
return;
|
||||
}
|
||||
sliceOrTupleStart.drop();
|
||||
parseSliceEnd(expr, sliceItemStart);
|
||||
}
|
||||
else if (atToken(PyTokenTypes.COMMA)) {
|
||||
sliceItemStart.done(PyElementTypes.SLICE_ITEM);
|
||||
if (!parseSliceListTail(expr, sliceOrTupleStart)) {
|
||||
sliceOrTupleStart.rollbackTo();
|
||||
if (!parseTupleExpression(false, false, false)) {
|
||||
myBuilder.error(message("tuple.expression.expected"));
|
||||
}
|
||||
checkMatches(PyTokenTypes.RBRACKET, message("PARSE.expected.rbracket"));
|
||||
expr.done(PyElementTypes.SUBSCRIPTION_EXPRESSION);
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (!hadExpression) {
|
||||
myBuilder.error(message("PARSE.expected.expression"));
|
||||
}
|
||||
sliceOrTupleStart.drop();
|
||||
sliceItemStart.drop();
|
||||
checkMatches(PyTokenTypes.RBRACKET, message("PARSE.expected.rbracket"));
|
||||
expr.done(PyElementTypes.SUBSCRIPTION_EXPRESSION);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private boolean parseEllipsis() {
|
||||
if (atToken(PyTokenTypes.DOT)) {
|
||||
final SyntaxTreeBuilder.Marker maybeEllipsis = myBuilder.mark();
|
||||
|
||||
8
python/testData/psi/AssignmentExpressionsInIndexes.py
Normal file
8
python/testData/psi/AssignmentExpressionsInIndexes.py
Normal file
@@ -0,0 +1,8 @@
|
||||
s = [1, 2]
|
||||
|
||||
s[(c := 0)] # valid
|
||||
s[d := 0] # valid
|
||||
|
||||
s[(d := 0): (e := 1)] # valid
|
||||
s[d := 0: (e := 1)] # invalid
|
||||
s[d := 0: e := 1] # invalid
|
||||
160
python/testData/psi/AssignmentExpressionsInIndexes.txt
Normal file
160
python/testData/psi/AssignmentExpressionsInIndexes.txt
Normal file
@@ -0,0 +1,160 @@
|
||||
PyFile:AssignmentExpressionsInIndexes.py
|
||||
PyAssignmentStatement
|
||||
PyTargetExpression: s
|
||||
PsiElement(Py:IDENTIFIER)('s')
|
||||
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\n')
|
||||
PyExpressionStatement
|
||||
PySubscriptionExpression
|
||||
PyReferenceExpression: s
|
||||
PsiElement(Py:IDENTIFIER)('s')
|
||||
PsiElement(Py:LBRACKET)('[')
|
||||
PyParenthesizedExpression
|
||||
PsiElement(Py:LPAR)('(')
|
||||
PyAssignmentExpression
|
||||
PyTargetExpression: c
|
||||
PsiElement(Py:IDENTIFIER)('c')
|
||||
PsiWhiteSpace(' ')
|
||||
PsiElement(Py:COLONEQ)(':=')
|
||||
PsiWhiteSpace(' ')
|
||||
PyNumericLiteralExpression
|
||||
PsiElement(Py:INTEGER_LITERAL)('0')
|
||||
PsiElement(Py:RPAR)(')')
|
||||
PsiElement(Py:RBRACKET)(']')
|
||||
PsiWhiteSpace(' ')
|
||||
PsiComment(Py:END_OF_LINE_COMMENT)('# valid')
|
||||
PsiWhiteSpace('\n')
|
||||
PyExpressionStatement
|
||||
PySubscriptionExpression
|
||||
PyReferenceExpression: s
|
||||
PsiElement(Py:IDENTIFIER)('s')
|
||||
PsiElement(Py:LBRACKET)('[')
|
||||
PyAssignmentExpression
|
||||
PyTargetExpression: d
|
||||
PsiElement(Py:IDENTIFIER)('d')
|
||||
PsiWhiteSpace(' ')
|
||||
PsiElement(Py:COLONEQ)(':=')
|
||||
PsiWhiteSpace(' ')
|
||||
PyNumericLiteralExpression
|
||||
PsiElement(Py:INTEGER_LITERAL)('0')
|
||||
PsiElement(Py:RBRACKET)(']')
|
||||
PsiWhiteSpace(' ')
|
||||
PsiComment(Py:END_OF_LINE_COMMENT)('# valid')
|
||||
PsiWhiteSpace('\n\n')
|
||||
PyExpressionStatement
|
||||
PySliceExpression
|
||||
PyReferenceExpression: s
|
||||
PsiElement(Py:IDENTIFIER)('s')
|
||||
PsiElement(Py:LBRACKET)('[')
|
||||
PySliceItem
|
||||
PyParenthesizedExpression
|
||||
PsiElement(Py:LPAR)('(')
|
||||
PyAssignmentExpression
|
||||
PyTargetExpression: d
|
||||
PsiElement(Py:IDENTIFIER)('d')
|
||||
PsiWhiteSpace(' ')
|
||||
PsiElement(Py:COLONEQ)(':=')
|
||||
PsiWhiteSpace(' ')
|
||||
PyNumericLiteralExpression
|
||||
PsiElement(Py:INTEGER_LITERAL)('0')
|
||||
PsiElement(Py:RPAR)(')')
|
||||
PsiElement(Py:COLON)(':')
|
||||
PsiWhiteSpace(' ')
|
||||
PyParenthesizedExpression
|
||||
PsiElement(Py:LPAR)('(')
|
||||
PyAssignmentExpression
|
||||
PyTargetExpression: e
|
||||
PsiElement(Py:IDENTIFIER)('e')
|
||||
PsiWhiteSpace(' ')
|
||||
PsiElement(Py:COLONEQ)(':=')
|
||||
PsiWhiteSpace(' ')
|
||||
PyNumericLiteralExpression
|
||||
PsiElement(Py:INTEGER_LITERAL)('1')
|
||||
PsiElement(Py:RPAR)(')')
|
||||
PsiElement(Py:RBRACKET)(']')
|
||||
PsiWhiteSpace(' ')
|
||||
PsiComment(Py:END_OF_LINE_COMMENT)('# valid')
|
||||
PsiWhiteSpace('\n')
|
||||
PyAssignmentExpression
|
||||
PySubscriptionExpression
|
||||
PyReferenceExpression: s
|
||||
PsiElement(Py:IDENTIFIER)('s')
|
||||
PsiElement(Py:LBRACKET)('[')
|
||||
PyReferenceExpression: d
|
||||
PsiElement(Py:IDENTIFIER)('d')
|
||||
PsiErrorElement:']' expected
|
||||
<empty list>
|
||||
PsiWhiteSpace(' ')
|
||||
PsiElement(Py:COLONEQ)(':=')
|
||||
PsiWhiteSpace(' ')
|
||||
PyNumericLiteralExpression
|
||||
PsiElement(Py:INTEGER_LITERAL)('0')
|
||||
PsiElement(Py:COLON)(':')
|
||||
PsiErrorElement:Statement expected, found Py:IDENTIFIER
|
||||
<empty list>
|
||||
PsiWhiteSpace(' ')
|
||||
PyExpressionStatement
|
||||
PyParenthesizedExpression
|
||||
PsiElement(Py:LPAR)('(')
|
||||
PyAssignmentExpression
|
||||
PyTargetExpression: e
|
||||
PsiElement(Py:IDENTIFIER)('e')
|
||||
PsiWhiteSpace(' ')
|
||||
PsiElement(Py:COLONEQ)(':=')
|
||||
PsiWhiteSpace(' ')
|
||||
PyNumericLiteralExpression
|
||||
PsiElement(Py:INTEGER_LITERAL)('1')
|
||||
PsiElement(Py:RPAR)(')')
|
||||
PsiErrorElement:End of statement expected
|
||||
<empty list>
|
||||
PsiElement(Py:RBRACKET)(']')
|
||||
PsiErrorElement:Statement expected, found Py:RBRACKET
|
||||
<empty list>
|
||||
PsiWhiteSpace(' ')
|
||||
PsiComment(Py:END_OF_LINE_COMMENT)('# invalid')
|
||||
PsiWhiteSpace('\n')
|
||||
PyAssignmentExpression
|
||||
PySubscriptionExpression
|
||||
PyReferenceExpression: s
|
||||
PsiElement(Py:IDENTIFIER)('s')
|
||||
PsiElement(Py:LBRACKET)('[')
|
||||
PyReferenceExpression: d
|
||||
PsiElement(Py:IDENTIFIER)('d')
|
||||
PsiErrorElement:']' expected
|
||||
<empty list>
|
||||
PsiWhiteSpace(' ')
|
||||
PsiElement(Py:COLONEQ)(':=')
|
||||
PsiWhiteSpace(' ')
|
||||
PyNumericLiteralExpression
|
||||
PsiElement(Py:INTEGER_LITERAL)('0')
|
||||
PsiElement(Py:COLON)(':')
|
||||
PsiErrorElement:Statement expected, found Py:IDENTIFIER
|
||||
<empty list>
|
||||
PsiWhiteSpace(' ')
|
||||
PyExpressionStatement
|
||||
PyAssignmentExpression
|
||||
PyTargetExpression: e
|
||||
PsiElement(Py:IDENTIFIER)('e')
|
||||
PsiWhiteSpace(' ')
|
||||
PsiElement(Py:COLONEQ)(':=')
|
||||
PsiWhiteSpace(' ')
|
||||
PyNumericLiteralExpression
|
||||
PsiElement(Py:INTEGER_LITERAL)('1')
|
||||
PsiErrorElement:End of statement expected
|
||||
<empty list>
|
||||
PsiElement(Py:RBRACKET)(']')
|
||||
PsiErrorElement:Statement expected, found Py:RBRACKET
|
||||
<empty list>
|
||||
PsiWhiteSpace(' ')
|
||||
PsiComment(Py:END_OF_LINE_COMMENT)('# invalid')
|
||||
@@ -21,10 +21,6 @@ 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
|
||||
|
||||
|
||||
@@ -415,57 +415,6 @@ PyFile:InvalidNonParenthesizedAssignmentExpressions.py
|
||||
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')
|
||||
PyExpressionStatement
|
||||
PySubscriptionExpression
|
||||
PyReferenceExpression: l
|
||||
PsiElement(Py:IDENTIFIER)('l')
|
||||
PsiElement(Py:LBRACKET)('[')
|
||||
PyAssignmentExpression
|
||||
PyTargetExpression: a
|
||||
PsiElement(Py:IDENTIFIER)('a')
|
||||
PsiWhiteSpace(' ')
|
||||
PsiElement(Py:COLONEQ)(':=')
|
||||
PsiWhiteSpace(' ')
|
||||
PyNumericLiteralExpression
|
||||
PsiElement(Py:INTEGER_LITERAL)('0')
|
||||
PsiElement(Py:RBRACKET)(']')
|
||||
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(' ')
|
||||
|
||||
@@ -1191,6 +1191,11 @@ public class PythonParsingTest extends ParsingTestCase {
|
||||
doTest(LanguageLevel.getLatest());
|
||||
}
|
||||
|
||||
// PY-48940
|
||||
public void testAssignmentExpressionsInIndexes() {
|
||||
doTest(LanguageLevel.getLatest());
|
||||
}
|
||||
|
||||
public void doTest() {
|
||||
doTest(LanguageLevel.PYTHON26);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user