PY-15653 Do not consume open parenthesis after missing identifier in class and function headers

This commit is contained in:
Mikhail Golubev
2015-04-21 15:14:25 +03:00
parent c1ed100ca8
commit 6794f3299c
10 changed files with 67 additions and 5 deletions
@@ -45,7 +45,7 @@ public class FunctionParsing extends Parsing {
protected void parseFunctionInnards(PsiBuilder.Marker functionMarker) {
myBuilder.advanceLexer();
parseIdentifierOrSkip();
parseIdentifierOrSkip(PyTokenTypes.LPAR);
parseParameterList();
parseReturnTypeAnnotation();
checkMatches(PyTokenTypes.COLON, message("PARSE.expected.colon"));
@@ -213,7 +213,7 @@ public class FunctionParsing extends Parsing {
return false;
}
PsiBuilder.Marker invalidElements = myBuilder.mark();
while (!atToken(endToken) && !atToken(PyTokenTypes.LINE_BREAK) && !atToken(PyTokenTypes.COMMA) && !atToken(null)) {
while (!atToken(endToken) && !atAnyOfTokens(PyTokenTypes.LINE_BREAK, PyTokenTypes.COMMA, null)) {
nextToken();
}
invalidElements.error(message("PARSE.expected.formal.param.name"));
@@ -63,14 +63,14 @@ public class Parsing {
return false;
}
protected boolean parseIdentifierOrSkip() {
protected boolean parseIdentifierOrSkip(@NotNull IElementType... validSuccessiveTokens) {
if (myBuilder.getTokenType() == PyTokenTypes.IDENTIFIER) {
myBuilder.advanceLexer();
return true;
}
else {
final PsiBuilder.Marker nameExpected = myBuilder.mark();
if (myBuilder.getTokenType() != PyTokenTypes.STATEMENT_BREAK) {
if (myBuilder.getTokenType() != PyTokenTypes.STATEMENT_BREAK && !atAnyOfTokens(validSuccessiveTokens)) {
myBuilder.advanceLexer();
}
nameExpected.error(PyBundle.message("PARSE.expected.identifier"));
@@ -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();
parseIdentifierOrSkip();
parseIdentifierOrSkip(PyTokenTypes.LPAR, PyTokenTypes.COLON);
if (myBuilder.getTokenType() == PyTokenTypes.LPAR) {
getExpressionParser().parseArgumentList();
}
@@ -0,0 +1,2 @@
class :
pass
@@ -0,0 +1,13 @@
PyFile:MissingClassNameAndThenColon.py
PyClass: null
PsiElement(Py:CLASS_KEYWORD)('class')
PsiErrorElement:Identifier expected
<empty list>
PsiWhiteSpace(' ')
PyArgumentList
<empty list>
PsiElement(Py:COLON)(':')
PsiWhiteSpace('\n ')
PyStatementList
PyPassStatement
PsiElement(Py:PASS_KEYWORD)('pass')
@@ -0,0 +1,2 @@
class ():
pass
@@ -0,0 +1,14 @@
PyFile:MissingClassNameAndThenListOfBaseClasses.py
PyClass: null
PsiElement(Py:CLASS_KEYWORD)('class')
PsiErrorElement:Identifier expected
<empty list>
PsiWhiteSpace(' ')
PyArgumentList
PsiElement(Py:LPAR)('(')
PsiElement(Py:RPAR)(')')
PsiElement(Py:COLON)(':')
PsiWhiteSpace('\n ')
PyStatementList
PyPassStatement
PsiElement(Py:PASS_KEYWORD)('pass')
@@ -0,0 +1,2 @@
def ():
pass
@@ -0,0 +1,14 @@
PyFile:MissingFunctionNameAndThenParametersList.py
PyFunction('null')
PsiElement(Py:DEF_KEYWORD)('def')
PsiErrorElement:Identifier expected
<empty list>
PsiWhiteSpace(' ')
PyParameterList
PsiElement(Py:LPAR)('(')
PsiElement(Py:RPAR)(')')
PsiElement(Py:COLON)(':')
PsiWhiteSpace('\n ')
PyStatementList
PyPassStatement
PsiElement(Py:PASS_KEYWORD)('pass')
@@ -473,6 +473,21 @@ public class PythonParsingTest extends ParsingTestCase {
doTest(LanguageLevel.PYTHON35);
}
// PY-15653
public void testMissingFunctionNameAndThenParametersList() {
doTest();
}
// PY-15653
public void testMissingClassNameAndThenListOfBaseClasses() {
doTest();
}
// PY-15653
public void testMissingClassNameAndThenColon() {
doTest();
}
public void doTest(LanguageLevel languageLevel) {
LanguageLevel prev = myLanguageLevel;
myLanguageLevel = languageLevel;