improve parser recovery if keyword is used as function or class name (PY-8319)

This commit is contained in:
Dmitry Jemerov
2013-11-05 19:35:38 +01:00
parent 5d6bac5b12
commit 664ee85163
8 changed files with 52 additions and 7 deletions

View File

@@ -44,12 +44,7 @@ public class FunctionParsing extends Parsing {
protected void parseFunctionInnards(PsiBuilder.Marker functionMarker) {
myBuilder.advanceLexer();
if (myBuilder.getTokenType() == PyTokenTypes.IDENTIFIER) {
myBuilder.advanceLexer();
}
else {
myBuilder.error(message("PARSE.expected.func.name"));
}
checkMatchesOrSkip(PyTokenTypes.IDENTIFIER, message("PARSE.expected.func.name"));
parseParameterList();
parseReturnTypeAnnotation();
checkMatches(PyTokenTypes.COLON, message("PARSE.expected.colon"));

View File

@@ -61,6 +61,17 @@ public class Parsing {
return false;
}
protected boolean checkMatchesOrSkip(final IElementType token, final String message) {
if (myBuilder.getTokenType() == token) {
myBuilder.advanceLexer();
return true;
}
PsiBuilder.Marker mark = myBuilder.mark();
myBuilder.advanceLexer();
mark.error(message);
return false;
}
protected void assertCurrentToken(final PyElementType tokenType) {
LOG.assertTrue(myBuilder.getTokenType() == tokenType);
}

View File

@@ -777,7 +777,7 @@ public class StatementParsing extends Parsing implements ITokenTypeRemapper {
public void parseClassDeclaration(PsiBuilder.Marker classMarker, ParsingScope scope) {
assertCurrentToken(PyTokenTypes.CLASS_KEYWORD);
myBuilder.advanceLexer();
checkMatches(PyTokenTypes.IDENTIFIER, IDENTIFIER_EXPECTED);
checkMatchesOrSkip(PyTokenTypes.IDENTIFIER, IDENTIFIER_EXPECTED);
if (myBuilder.getTokenType() == PyTokenTypes.LPAR) {
getExpressionParser().parseArgumentList();
}

View File

@@ -0,0 +1,2 @@
class from:
pass

View File

@@ -0,0 +1,13 @@
PyFile:KeywordAsClassName.py
PyClass: null
PsiElement(Py:CLASS_KEYWORD)('class')
PsiWhiteSpace(' ')
PsiErrorElement:Identifier expected
PsiElement(Py:FROM_KEYWORD)('from')
PyArgumentList
<empty list>
PsiElement(Py:COLON)(':')
PsiWhiteSpace('\n ')
PyStatementList
PyPassStatement
PsiElement(Py:PASS_KEYWORD)('pass')

View File

@@ -0,0 +1,2 @@
def from():
pass

View File

@@ -0,0 +1,14 @@
PyFile:KeywordAsFunctionName.py
PyFunction('null')
PsiElement(Py:DEF_KEYWORD)('def')
PsiWhiteSpace(' ')
PsiErrorElement:function name expected
PsiElement(Py:FROM_KEYWORD)('from')
PyParameterList
PsiElement(Py:LPAR)('(')
PsiElement(Py:RPAR)(')')
PsiElement(Py:COLON)(':')
PsiWhiteSpace('\n ')
PyStatementList
PyPassStatement
PsiElement(Py:PASS_KEYWORD)('pass')

View File

@@ -410,6 +410,14 @@ public class PythonParsingTest extends ParsingTestCase {
doTest();
}
public void testKeywordAsClassName() { // PY-8319
doTest();
}
public void testKeywordAsFunctionName() { // PY-8319
doTest();
}
public void doTest(LanguageLevel languageLevel) {
LanguageLevel prev = myLanguageLevel;
myLanguageLevel = languageLevel;