From 59d5c27308dfb5fe5e6f71a9dc99cbd79a56ac8b Mon Sep 17 00:00:00 2001 From: Dmitry Jemerov Date: Thu, 13 Dec 2012 20:42:23 +0100 Subject: [PATCH] don't parse compound statements after semicolon at top level of file (PY-7660) --- .../python/parsing/ParsingScope.java | 9 ++++++ .../jetbrains/python/parsing/PyParser.java | 14 ++++++++-- .../python/parsing/StatementParsing.java | 2 ++ .../psi/CompoundStatementAfterSemicolon.py | 2 ++ .../psi/CompoundStatementAfterSemicolon.txt | 28 +++++++++++++++++++ .../jetbrains/python/PythonParsingTest.java | 3 ++ 6 files changed, 55 insertions(+), 3 deletions(-) create mode 100644 python/testData/psi/CompoundStatementAfterSemicolon.py create mode 100644 python/testData/psi/CompoundStatementAfterSemicolon.txt diff --git a/python/src/com/jetbrains/python/parsing/ParsingScope.java b/python/src/com/jetbrains/python/parsing/ParsingScope.java index 019a1d7d140b..61b76cb7d964 100644 --- a/python/src/com/jetbrains/python/parsing/ParsingScope.java +++ b/python/src/com/jetbrains/python/parsing/ParsingScope.java @@ -7,6 +7,7 @@ public class ParsingScope { private boolean myFunction = false; private boolean myClass = false; private boolean mySuite = false; + private boolean myAfterSemicolon = false; public ParsingScope withFunction(boolean flag) { final ParsingScope result = copy(); @@ -38,6 +39,14 @@ public class ParsingScope { return mySuite; } + public boolean isAfterSemicolon() { + return myAfterSemicolon; + } + + public void setAfterSemicolon(boolean value) { + myAfterSemicolon = value; + } + protected ParsingScope createInstance() { return new ParsingScope(); } diff --git a/python/src/com/jetbrains/python/parsing/PyParser.java b/python/src/com/jetbrains/python/parsing/PyParser.java index f71af6d6d689..7baf7fff9aa1 100644 --- a/python/src/com/jetbrains/python/parsing/PyParser.java +++ b/python/src/com/jetbrains/python/parsing/PyParser.java @@ -30,10 +30,18 @@ public class PyParser implements PsiParser { long start = System.currentTimeMillis(); final PsiBuilder.Marker rootMarker = builder.mark(); ParsingContext context = createParsingContext(builder, myLanguageLevel, myFutureFlag); - StatementParsing stmt_parser = context.getStatementParser(); - builder.setTokenTypeRemapper(stmt_parser); // must be done before touching the caching lexer with eof() call. + StatementParsing statementParser = context.getStatementParser(); + builder.setTokenTypeRemapper(statementParser); // must be done before touching the caching lexer with eof() call. + boolean lastAfterSemicolon = false; while (!builder.eof()) { - stmt_parser.parseStatement(context.emptyParsingScope()); + ParsingScope scope = context.emptyParsingScope(); + if (lastAfterSemicolon) { + statementParser.parseSimpleStatement(scope); + } + else { + statementParser.parseStatement(scope); + } + lastAfterSemicolon = scope.isAfterSemicolon(); } rootMarker.done(root); ASTNode ast = builder.getTreeBuilt(); diff --git a/python/src/com/jetbrains/python/parsing/StatementParsing.java b/python/src/com/jetbrains/python/parsing/StatementParsing.java index 96c7d4cb5e88..51e35da65b7b 100644 --- a/python/src/com/jetbrains/python/parsing/StatementParsing.java +++ b/python/src/com/jetbrains/python/parsing/StatementParsing.java @@ -254,8 +254,10 @@ public class StatementParsing extends Parsing implements ITokenTypeRemapper { else if (builder.getTokenType() == PyTokenTypes.SEMICOLON) { if (!scope.isSuite()) { builder.advanceLexer(); + scope.setAfterSemicolon(true); if (builder.getTokenType() == PyTokenTypes.STATEMENT_BREAK) { builder.advanceLexer(); + scope.setAfterSemicolon(false); } } } diff --git a/python/testData/psi/CompoundStatementAfterSemicolon.py b/python/testData/psi/CompoundStatementAfterSemicolon.py new file mode 100644 index 000000000000..59a6223c95f6 --- /dev/null +++ b/python/testData/psi/CompoundStatementAfterSemicolon.py @@ -0,0 +1,2 @@ +a = 0; if a: + pass diff --git a/python/testData/psi/CompoundStatementAfterSemicolon.txt b/python/testData/psi/CompoundStatementAfterSemicolon.txt new file mode 100644 index 000000000000..2f26f33efc2b --- /dev/null +++ b/python/testData/psi/CompoundStatementAfterSemicolon.txt @@ -0,0 +1,28 @@ +PyFile:CompoundStatementAfterSemicolon.py + PyAssignmentStatement + PyTargetExpression: a + PsiElement(Py:IDENTIFIER)('a') + PsiWhiteSpace(' ') + PsiElement(Py:EQ)('=') + PsiWhiteSpace(' ') + PyNumericLiteralExpression + PsiElement(Py:INTEGER_LITERAL)('0') + PsiElement(Py:SEMICOLON)(';') + PsiWhiteSpace(' ') + PsiElement(Py:IF_KEYWORD)('if') + PsiErrorElement:Statement expected, found Py:IF_KEYWORD + + PsiWhiteSpace(' ') + PyExpressionStatement + PyReferenceExpression: a + PsiElement(Py:IDENTIFIER)('a') + PsiErrorElement:End of statement expected + + PsiElement(Py:COLON)(':') + PsiErrorElement:Statement expected, found Py:COLON + + PsiWhiteSpace('\n ') + PsiErrorElement:Unexpected indent + + PyPassStatement + PsiElement(Py:PASS_KEYWORD)('pass') \ No newline at end of file diff --git a/python/testSrc/com/jetbrains/python/PythonParsingTest.java b/python/testSrc/com/jetbrains/python/PythonParsingTest.java index b4485a5e57c9..17513fd7c48f 100644 --- a/python/testSrc/com/jetbrains/python/PythonParsingTest.java +++ b/python/testSrc/com/jetbrains/python/PythonParsingTest.java @@ -352,6 +352,9 @@ public class PythonParsingTest extends ParsingTestCase { doTest(LanguageLevel.PYTHON25); } + public void testCompoundStatementAfterSemicolon() { // PY-7660 + doTest(); + } public void doTest(LanguageLevel languageLevel) { LanguageLevel prev = myLanguageLevel;