mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
tweak parsing of incomplete statements (PY-3792)
This commit is contained in:
@@ -551,7 +551,7 @@ public class StatementParsing extends Parsing implements ITokenTypeRemapper {
|
||||
final PsiBuilder.Marker ifPart = myBuilder.mark();
|
||||
myBuilder.advanceLexer();
|
||||
getExpressionParser().parseExpression();
|
||||
if (checkMatches(PyTokenTypes.COLON, "colon expected")) {
|
||||
if (expectColon()) {
|
||||
parseSuite(scope);
|
||||
}
|
||||
ifPart.done(PyElementTypes.IF_PART_IF);
|
||||
@@ -559,8 +559,9 @@ public class StatementParsing extends Parsing implements ITokenTypeRemapper {
|
||||
while (myBuilder.getTokenType() == elifKeyword) {
|
||||
myBuilder.advanceLexer();
|
||||
getExpressionParser().parseExpression();
|
||||
checkMatches(PyTokenTypes.COLON, "colon expected");
|
||||
parseSuite(scope);
|
||||
if (expectColon()) {
|
||||
parseSuite(scope);
|
||||
}
|
||||
elifPart.done(PyElementTypes.IF_PART_ELIF);
|
||||
elifPart = myBuilder.mark();
|
||||
}
|
||||
@@ -568,14 +569,28 @@ public class StatementParsing extends Parsing implements ITokenTypeRemapper {
|
||||
final PsiBuilder.Marker elsePart = myBuilder.mark();
|
||||
if (myBuilder.getTokenType() == elseKeyword) {
|
||||
myBuilder.advanceLexer();
|
||||
checkMatches(PyTokenTypes.COLON, "colon expected");
|
||||
parseSuite(scope);
|
||||
if (expectColon()) {
|
||||
parseSuite(scope);
|
||||
}
|
||||
elsePart.done(PyElementTypes.ELSE_PART);
|
||||
}
|
||||
else elsePart.drop();
|
||||
ifStatement.done(elementType);
|
||||
}
|
||||
|
||||
private boolean expectColon() {
|
||||
if (myBuilder.getTokenType() == PyTokenTypes.COLON) {
|
||||
myBuilder.advanceLexer();
|
||||
return true;
|
||||
}
|
||||
final PsiBuilder.Marker marker = myBuilder.mark();
|
||||
if (myBuilder.getTokenType() == PyTokenTypes.STATEMENT_BREAK) {
|
||||
myBuilder.advanceLexer();
|
||||
}
|
||||
marker.error("colon expected");
|
||||
return false;
|
||||
}
|
||||
|
||||
private void parseForStatement(ParsingScope scope) {
|
||||
assertCurrentToken(PyTokenTypes.FOR_KEYWORD);
|
||||
final PsiBuilder.Marker statement = myBuilder.mark();
|
||||
@@ -583,8 +598,9 @@ public class StatementParsing extends Parsing implements ITokenTypeRemapper {
|
||||
final PsiBuilder.Marker elsePart = myBuilder.mark();
|
||||
if (myBuilder.getTokenType() == PyTokenTypes.ELSE_KEYWORD) {
|
||||
myBuilder.advanceLexer();
|
||||
checkMatches(PyTokenTypes.COLON, "colon expected");
|
||||
parseSuite(scope);
|
||||
if (expectColon()) {
|
||||
parseSuite(scope);
|
||||
}
|
||||
elsePart.done(PyElementTypes.ELSE_PART);
|
||||
}
|
||||
else elsePart.drop();
|
||||
@@ -597,8 +613,9 @@ public class StatementParsing extends Parsing implements ITokenTypeRemapper {
|
||||
getExpressionParser().parseExpression(true, true);
|
||||
checkMatches(PyTokenTypes.IN_KEYWORD, "'in' expected");
|
||||
getExpressionParser().parseExpression();
|
||||
checkMatches(PyTokenTypes.COLON, "colon expected");
|
||||
parseSuite(scope);
|
||||
if (expectColon()) {
|
||||
parseSuite(scope);
|
||||
}
|
||||
forPart.done(PyElementTypes.FOR_PART);
|
||||
}
|
||||
|
||||
@@ -610,14 +627,16 @@ public class StatementParsing extends Parsing implements ITokenTypeRemapper {
|
||||
if (!getExpressionParser().parseSingleExpression(false)) {
|
||||
myBuilder.error("expression expected");
|
||||
}
|
||||
checkMatches(PyTokenTypes.COLON, "colon expected");
|
||||
parseSuite(scope);
|
||||
if (expectColon()) {
|
||||
parseSuite(scope);
|
||||
}
|
||||
whilePart.done(PyElementTypes.WHILE_PART);
|
||||
final PsiBuilder.Marker elsePart = myBuilder.mark();
|
||||
if (myBuilder.getTokenType() == PyTokenTypes.ELSE_KEYWORD) {
|
||||
myBuilder.advanceLexer();
|
||||
checkMatches(PyTokenTypes.COLON, "colon expected");
|
||||
parseSuite(scope);
|
||||
if (expectColon()) {
|
||||
parseSuite(scope);
|
||||
}
|
||||
elsePart.done(PyElementTypes.ELSE_PART);
|
||||
}
|
||||
else elsePart.drop();
|
||||
@@ -629,8 +648,9 @@ public class StatementParsing extends Parsing implements ITokenTypeRemapper {
|
||||
final PsiBuilder.Marker statement = myBuilder.mark();
|
||||
final PsiBuilder.Marker tryPart = myBuilder.mark();
|
||||
myBuilder.advanceLexer();
|
||||
checkMatches(PyTokenTypes.COLON, "colon expected");
|
||||
parseSuite(scope);
|
||||
if (expectColon()) {
|
||||
parseSuite(scope);
|
||||
}
|
||||
tryPart.done(PyElementTypes.TRY_PART);
|
||||
boolean haveExceptClause = false;
|
||||
if (myBuilder.getTokenType() == PyTokenTypes.EXCEPT_KEYWORD) {
|
||||
@@ -650,15 +670,17 @@ public class StatementParsing extends Parsing implements ITokenTypeRemapper {
|
||||
}
|
||||
}
|
||||
}
|
||||
checkMatches(PyTokenTypes.COLON, "colon expected");
|
||||
parseSuite(scope);
|
||||
if (expectColon()) {
|
||||
parseSuite(scope);
|
||||
}
|
||||
exceptBlock.done(PyElementTypes.EXCEPT_PART);
|
||||
}
|
||||
final PsiBuilder.Marker elsePart = myBuilder.mark();
|
||||
if (myBuilder.getTokenType() == PyTokenTypes.ELSE_KEYWORD) {
|
||||
myBuilder.advanceLexer();
|
||||
checkMatches(PyTokenTypes.COLON, "colon expected");
|
||||
parseSuite(scope);
|
||||
if (expectColon()) {
|
||||
parseSuite(scope);
|
||||
}
|
||||
elsePart.done(PyElementTypes.ELSE_PART);
|
||||
}
|
||||
else elsePart.drop();
|
||||
@@ -666,8 +688,9 @@ public class StatementParsing extends Parsing implements ITokenTypeRemapper {
|
||||
final PsiBuilder.Marker finallyPart = myBuilder.mark();
|
||||
if (myBuilder.getTokenType() == PyTokenTypes.FINALLY_KEYWORD) {
|
||||
myBuilder.advanceLexer();
|
||||
checkMatches(PyTokenTypes.COLON, "colon expected");
|
||||
parseSuite(scope);
|
||||
if (expectColon()) {
|
||||
parseSuite(scope);
|
||||
}
|
||||
finallyPart.done(PyElementTypes.FINALLY_PART);
|
||||
}
|
||||
else {
|
||||
@@ -698,8 +721,9 @@ public class StatementParsing extends Parsing implements ITokenTypeRemapper {
|
||||
break;
|
||||
}
|
||||
}
|
||||
checkMatches(PyTokenTypes.COLON, "colon expected");
|
||||
parseSuite(scope);
|
||||
if (expectColon()) {
|
||||
parseSuite(scope);
|
||||
}
|
||||
statement.done(PyElementTypes.WITH_STATEMENT);
|
||||
}
|
||||
|
||||
@@ -719,8 +743,13 @@ public class StatementParsing extends Parsing implements ITokenTypeRemapper {
|
||||
final PsiBuilder.Marker inheritMarker = myBuilder.mark();
|
||||
inheritMarker.done(PyElementTypes.ARGUMENT_LIST);
|
||||
}
|
||||
checkMatches(PyTokenTypes.COLON, "colon expected");
|
||||
parseSuite(scope.withClass(true));
|
||||
if (expectColon()) {
|
||||
parseSuite(scope.withClass(true));
|
||||
}
|
||||
else {
|
||||
final PsiBuilder.Marker mark = myBuilder.mark();
|
||||
mark.done(PyElementTypes.STATEMENT_LIST);
|
||||
}
|
||||
classMarker.done(PyElementTypes.CLASS_DECLARATION);
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
def foo():
|
||||
if xyzzy()
|
||||
|
||||
def xyzzy():
|
||||
pass
|
||||
@@ -0,0 +1,5 @@
|
||||
def foo():
|
||||
if xy<caret>
|
||||
|
||||
def xyzzy():
|
||||
pass
|
||||
@@ -0,0 +1,5 @@
|
||||
def foo():
|
||||
for item in ba
|
||||
|
||||
def bar():
|
||||
pass
|
||||
@@ -0,0 +1,37 @@
|
||||
PyFile:IncompleteFor.py
|
||||
PyFunction('foo')
|
||||
PsiElement(Py:DEF_KEYWORD)('def')
|
||||
PsiWhiteSpace(' ')
|
||||
PsiElement(Py:IDENTIFIER)('foo')
|
||||
PyParameterList
|
||||
PsiElement(Py:LPAR)('(')
|
||||
PsiElement(Py:RPAR)(')')
|
||||
PsiElement(Py:COLON)(':')
|
||||
PsiWhiteSpace('\n ')
|
||||
PyStatementList
|
||||
PyForStatement
|
||||
PyForPart
|
||||
PsiElement(Py:FOR_KEYWORD)('for')
|
||||
PsiWhiteSpace(' ')
|
||||
PyTargetExpression: item
|
||||
PsiElement(Py:IDENTIFIER)('item')
|
||||
PsiWhiteSpace(' ')
|
||||
PsiElement(Py:IN_KEYWORD)('in')
|
||||
PsiWhiteSpace(' ')
|
||||
PyReferenceExpression: ba
|
||||
PsiElement(Py:IDENTIFIER)('ba')
|
||||
PsiErrorElement:colon expected
|
||||
<empty list>
|
||||
PsiWhiteSpace('\n\n')
|
||||
PyFunction('bar')
|
||||
PsiElement(Py:DEF_KEYWORD)('def')
|
||||
PsiWhiteSpace(' ')
|
||||
PsiElement(Py:IDENTIFIER)('bar')
|
||||
PyParameterList
|
||||
PsiElement(Py:LPAR)('(')
|
||||
PsiElement(Py:RPAR)(')')
|
||||
PsiElement(Py:COLON)(':')
|
||||
PsiWhiteSpace('\n ')
|
||||
PyStatementList
|
||||
PyPassStatement
|
||||
PsiElement(Py:PASS_KEYWORD)('pass')
|
||||
@@ -0,0 +1,5 @@
|
||||
def foo():
|
||||
if ba
|
||||
|
||||
def bar():
|
||||
pass
|
||||
@@ -0,0 +1,32 @@
|
||||
PyFile:IncompleteStatementList.py
|
||||
PyFunction('foo')
|
||||
PsiElement(Py:DEF_KEYWORD)('def')
|
||||
PsiWhiteSpace(' ')
|
||||
PsiElement(Py:IDENTIFIER)('foo')
|
||||
PyParameterList
|
||||
PsiElement(Py:LPAR)('(')
|
||||
PsiElement(Py:RPAR)(')')
|
||||
PsiElement(Py:COLON)(':')
|
||||
PsiWhiteSpace('\n ')
|
||||
PyStatementList
|
||||
PyIfStatement
|
||||
PyIfPartIf
|
||||
PsiElement(Py:IF_KEYWORD)('if')
|
||||
PsiWhiteSpace(' ')
|
||||
PyReferenceExpression: ba
|
||||
PsiElement(Py:IDENTIFIER)('ba')
|
||||
PsiErrorElement:colon expected
|
||||
<empty list>
|
||||
PsiWhiteSpace('\n\n')
|
||||
PyFunction('bar')
|
||||
PsiElement(Py:DEF_KEYWORD)('def')
|
||||
PsiWhiteSpace(' ')
|
||||
PsiElement(Py:IDENTIFIER)('bar')
|
||||
PyParameterList
|
||||
PsiElement(Py:LPAR)('(')
|
||||
PsiElement(Py:RPAR)(')')
|
||||
PsiElement(Py:COLON)(':')
|
||||
PsiWhiteSpace('\n ')
|
||||
PyStatementList
|
||||
PyPassStatement
|
||||
PsiElement(Py:PASS_KEYWORD)('pass')
|
||||
@@ -26,6 +26,8 @@ PyFile:MissingParenInCall.py
|
||||
PsiElement(Py:COMMA)(',')
|
||||
PsiErrorElement:expression expected
|
||||
<empty list>
|
||||
PsiErrorElement:colon expected
|
||||
<empty list>
|
||||
PsiWhiteSpace('\n\n')
|
||||
PyFunction('clean')
|
||||
PsiElement(Py:DEF_KEYWORD)('def')
|
||||
|
||||
@@ -72,10 +72,11 @@ PyFile:WithStatement2.py
|
||||
PyWithItem
|
||||
PsiErrorElement:expression expected
|
||||
<empty list>
|
||||
PyStatementList
|
||||
PsiElement(Py:EQ)('=')
|
||||
PsiErrorElement:statement expected, found Py:EQ
|
||||
<empty list>
|
||||
PsiErrorElement:colon expected
|
||||
<empty list>
|
||||
PsiElement(Py:EQ)('=')
|
||||
PsiErrorElement:statement expected, found Py:EQ
|
||||
<empty list>
|
||||
PsiWhiteSpace(' ')
|
||||
PyExpressionStatement
|
||||
PyNumericLiteralExpression
|
||||
|
||||
@@ -465,4 +465,8 @@ public class PythonCompletionTest extends PyTestCase {
|
||||
public void testPrivateMemberType() { // PY-4589
|
||||
doTest();
|
||||
}
|
||||
|
||||
public void testCompleteBeforeSyntaxError() { // PY-3792
|
||||
doTest();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -298,6 +298,14 @@ public class PythonParsingTest extends ParsingTestCase {
|
||||
doTest();
|
||||
}
|
||||
|
||||
public void testIncompleteStatementList() { // PY-3792
|
||||
doTest();
|
||||
}
|
||||
|
||||
public void testIncompleteFor() { // PY-3792
|
||||
doTest();
|
||||
}
|
||||
|
||||
public void doTest() {
|
||||
doTest(LanguageLevel.PYTHON25);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user