tweak parsing of incomplete statements (PY-3792)

This commit is contained in:
Dmitry Jemerov
2011-11-17 10:08:35 +01:00
parent 40f66a7eb7
commit 87fb922bf1
11 changed files with 162 additions and 29 deletions
@@ -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
+5
View File
@@ -0,0 +1,5 @@
def foo():
for item in ba
def bar():
pass
+37
View File
@@ -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')
+5 -4
View File
@@ -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);
}