Better error recovery for lines with single 'class' or 'def' keywords and normal functions defined below

This commit is contained in:
Andrey Vlasovskikh
2014-02-26 19:29:15 +04:00
parent a39c3b7998
commit ae1e6ca3e7
10 changed files with 91 additions and 11 deletions
@@ -654,7 +654,7 @@ PARSE.expected.colon.or.rbracket=':' or ']' expected
PARSE.expected.comma.or.rpar=',' or ')' expected
PARSE.expected.else='else' expected
PARSE.expected.func.name=function name expected
PARSE.expected.identifier=Identifier expected
PARSE.expected.comma.lpar.rpar=',' or '(' or ')' expected
PARSE.expected.statement.break=Statement break expected
PARSE.expected.@.or.def='@' or 'def' expected
@@ -45,7 +45,7 @@ public class FunctionParsing extends Parsing {
protected void parseFunctionInnards(PsiBuilder.Marker functionMarker) {
myBuilder.advanceLexer();
checkMatchesOrSkip(PyTokenTypes.IDENTIFIER, message("PARSE.expected.func.name"));
parseIdentifierOrSkip();
parseParameterList();
parseReturnTypeAnnotation();
checkMatches(PyTokenTypes.COLON, message("PARSE.expected.colon"));
@@ -18,7 +18,9 @@ package com.jetbrains.python.parsing;
import com.intellij.lang.PsiBuilder;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.psi.tree.IElementType;
import com.jetbrains.python.PyBundle;
import com.jetbrains.python.PyElementTypes;
import com.jetbrains.python.PyTokenTypes;
import com.jetbrains.python.psi.PyElementType;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -61,15 +63,19 @@ public class Parsing {
return false;
}
protected boolean checkMatchesOrSkip(final IElementType token, final String message) {
if (myBuilder.getTokenType() == token) {
protected boolean parseIdentifierOrSkip() {
if (myBuilder.getTokenType() == PyTokenTypes.IDENTIFIER) {
myBuilder.advanceLexer();
return true;
}
PsiBuilder.Marker mark = myBuilder.mark();
myBuilder.advanceLexer();
mark.error(message);
return false;
else {
final PsiBuilder.Marker nameExpected = myBuilder.mark();
if (myBuilder.getTokenType() != PyTokenTypes.STATEMENT_BREAK) {
myBuilder.advanceLexer();
}
nameExpected.error(PyBundle.message("PARSE.expected.identifier"));
return false;
}
}
protected void assertCurrentToken(final PyElementType tokenType) {
@@ -95,7 +101,7 @@ public class Parsing {
protected boolean matchToken(final IElementType tokenType) {
if (myBuilder.getTokenType() == tokenType) {
myBuilder.advanceLexer();
return true;
return true;
}
return false;
}
@@ -792,7 +792,7 @@ public class StatementParsing extends Parsing implements ITokenTypeRemapper {
public void parseClassDeclaration(PsiBuilder.Marker classMarker, ParsingScope scope) {
assertCurrentToken(PyTokenTypes.CLASS_KEYWORD);
myBuilder.advanceLexer();
checkMatchesOrSkip(PyTokenTypes.IDENTIFIER, IDENTIFIER_EXPECTED);
parseIdentifierOrSkip();
if (myBuilder.getTokenType() == PyTokenTypes.LPAR) {
getExpressionParser().parseArgumentList();
}
@@ -2,7 +2,7 @@ PyFile:KeywordAsFunctionName.py
PyFunction('null')
PsiElement(Py:DEF_KEYWORD)('def')
PsiWhiteSpace(' ')
PsiErrorElement:function name expected
PsiErrorElement:Identifier expected
PsiElement(Py:FROM_KEYWORD)('from')
PyParameterList
PsiElement(Py:LPAR)('(')
@@ -0,0 +1,5 @@
class
def foo():
pass
@@ -0,0 +1,25 @@
PyFile:SingleClassBeforeFunction.py
PyClass: null
PsiElement(Py:CLASS_KEYWORD)('class')
PsiErrorElement:Identifier expected
<empty list>
PyArgumentList
<empty list>
PsiErrorElement:Colon expected
<empty list>
PsiWhiteSpace('\n\n\n')
PyStatementList
PsiErrorElement:Indent expected
<empty list>
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
PyPassStatement
PsiElement(Py:PASS_KEYWORD)('pass')
@@ -0,0 +1,5 @@
def
def bar():
x = 1
@@ -0,0 +1,31 @@
PyFile:SingleDefBeforeFunction.py
PyFunction('null')
PsiElement(Py:DEF_KEYWORD)('def')
PsiErrorElement:Identifier expected
<empty list>
PsiErrorElement:'(' expected
<empty list>
PyParameterList
<empty list>
PsiWhiteSpace('\n\n\n')
PyStatementList
PsiErrorElement:Indent expected
<empty list>
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
PyAssignmentStatement
PyTargetExpression: x
PsiElement(Py:IDENTIFIER)('x')
PsiWhiteSpace(' ')
PsiElement(Py:EQ)('=')
PsiWhiteSpace(' ')
PyNumericLiteralExpression
PsiElement(Py:INTEGER_LITERAL)('1')
@@ -455,6 +455,14 @@ public class PythonParsingTest extends ParsingTestCase {
doTest();
}
public void testSingleDefBeforeFunction() {
doTest();
}
public void testSingleClassBeforeFunction() {
doTest();
}
public void doTest(LanguageLevel languageLevel) {
LanguageLevel prev = myLanguageLevel;
myLanguageLevel = languageLevel;