mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
support keyword arguments in superclass list (http://www.python.org/dev/peps/pep-3115/)
This commit is contained in:
@@ -228,7 +228,7 @@ public class ExpressionParsing extends Parsing {
|
||||
expr = expr.precede();
|
||||
}
|
||||
else if (tokenType == PyTokenTypes.LPAR) {
|
||||
parseArgumentList(builder);
|
||||
parseArgumentList();
|
||||
expr.done(PyElementTypes.CALL_EXPRESSION);
|
||||
expr = expr.precede();
|
||||
}
|
||||
@@ -297,48 +297,48 @@ public class ExpressionParsing extends Parsing {
|
||||
expr.done(PyElementTypes.SLICE_EXPRESSION);
|
||||
}
|
||||
|
||||
public void parseArgumentList(final PsiBuilder builder) {
|
||||
LOG.assertTrue(builder.getTokenType() == PyTokenTypes.LPAR);
|
||||
final PsiBuilder.Marker arglist = builder.mark();
|
||||
final PsiBuilder.Marker genexpr = builder.mark();
|
||||
builder.advanceLexer();
|
||||
public void parseArgumentList() {
|
||||
LOG.assertTrue(myBuilder.getTokenType() == PyTokenTypes.LPAR);
|
||||
final PsiBuilder.Marker arglist = myBuilder.mark();
|
||||
final PsiBuilder.Marker genexpr = myBuilder.mark();
|
||||
myBuilder.advanceLexer();
|
||||
int argNumber = 0;
|
||||
boolean needBracket = true;
|
||||
while (builder.getTokenType() != PyTokenTypes.RPAR) {
|
||||
while (myBuilder.getTokenType() != PyTokenTypes.RPAR) {
|
||||
argNumber++;
|
||||
if (argNumber > 1) {
|
||||
if (argNumber == 2 && builder.getTokenType() == PyTokenTypes.FOR_KEYWORD) {
|
||||
if (argNumber == 2 && myBuilder.getTokenType() == PyTokenTypes.FOR_KEYWORD && genexpr != null) {
|
||||
parseListCompExpression(genexpr, PyTokenTypes.RPAR, PyElementTypes.GENERATOR_EXPRESSION);
|
||||
needBracket = false;
|
||||
break;
|
||||
}
|
||||
else if (builder.getTokenType() == PyTokenTypes.COMMA) {
|
||||
builder.advanceLexer();
|
||||
if (builder.getTokenType() == PyTokenTypes.RPAR) {
|
||||
else if (myBuilder.getTokenType() == PyTokenTypes.COMMA) {
|
||||
myBuilder.advanceLexer();
|
||||
if (myBuilder.getTokenType() == PyTokenTypes.RPAR) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
else {
|
||||
builder.error(message("PARSE.expected.comma.or.rpar"));
|
||||
myBuilder.error(message("PARSE.expected.comma.or.rpar"));
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (builder.getTokenType() == PyTokenTypes.MULT || builder.getTokenType() == PyTokenTypes.EXP) {
|
||||
final PsiBuilder.Marker starArgMarker = builder.mark();
|
||||
builder.advanceLexer();
|
||||
if (myBuilder.getTokenType() == PyTokenTypes.MULT || myBuilder.getTokenType() == PyTokenTypes.EXP) {
|
||||
final PsiBuilder.Marker starArgMarker = myBuilder.mark();
|
||||
myBuilder.advanceLexer();
|
||||
if (!parseSingleExpression(false)) {
|
||||
builder.error(message("PARSE.expected.expression"));
|
||||
myBuilder.error(message("PARSE.expected.expression"));
|
||||
}
|
||||
starArgMarker.done(PyElementTypes.STAR_ARGUMENT_EXPRESSION);
|
||||
}
|
||||
else {
|
||||
if (builder.getTokenType() == PyTokenTypes.IDENTIFIER) {
|
||||
final PsiBuilder.Marker keywordArgMarker = builder.mark();
|
||||
builder.advanceLexer();
|
||||
if (builder.getTokenType() == PyTokenTypes.EQ) {
|
||||
builder.advanceLexer();
|
||||
if (myBuilder.getTokenType() == PyTokenTypes.IDENTIFIER) {
|
||||
final PsiBuilder.Marker keywordArgMarker = myBuilder.mark();
|
||||
myBuilder.advanceLexer();
|
||||
if (myBuilder.getTokenType() == PyTokenTypes.EQ) {
|
||||
myBuilder.advanceLexer();
|
||||
if (!parseSingleExpression(false)) {
|
||||
builder.error(message("PARSE.expected.expression"));
|
||||
myBuilder.error(message("PARSE.expected.expression"));
|
||||
}
|
||||
keywordArgMarker.done(PyElementTypes.KEYWORD_ARGUMENT_EXPRESSION);
|
||||
continue;
|
||||
@@ -346,13 +346,15 @@ public class ExpressionParsing extends Parsing {
|
||||
keywordArgMarker.rollbackTo();
|
||||
}
|
||||
if (!parseSingleExpression(false)) {
|
||||
builder.error(message("PARSE.expected.expression"));
|
||||
myBuilder.error(message("PARSE.expected.expression"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (needBracket) {
|
||||
genexpr.drop();
|
||||
if (genexpr != null) {
|
||||
genexpr.drop();
|
||||
}
|
||||
checkMatches(PyTokenTypes.RPAR, message("PARSE.expected.rpar"));
|
||||
}
|
||||
arglist.done(PyElementTypes.ARGUMENT_LIST);
|
||||
|
||||
@@ -48,7 +48,7 @@ public class FunctionParsing extends Parsing {
|
||||
myBuilder.advanceLexer();
|
||||
getStatementParser().parseDottedName();
|
||||
if (myBuilder.getTokenType() == PyTokenTypes.LPAR) {
|
||||
getExpressionParser().parseArgumentList(myBuilder);
|
||||
getExpressionParser().parseArgumentList();
|
||||
}
|
||||
else { // empty arglist node, so we always have it
|
||||
myBuilder.mark().done(PyElementTypes.ARGUMENT_LIST);
|
||||
|
||||
@@ -665,15 +665,13 @@ public class StatementParsing extends Parsing implements ITokenTypeRemapper {
|
||||
assertCurrentToken(PyTokenTypes.CLASS_KEYWORD);
|
||||
myBuilder.advanceLexer();
|
||||
checkMatches(PyTokenTypes.IDENTIFIER, "identifier expected");
|
||||
final PsiBuilder.Marker inheritMarker = myBuilder.mark();
|
||||
if (myBuilder.getTokenType() == PyTokenTypes.LPAR) {
|
||||
myBuilder.advanceLexer();
|
||||
if (myBuilder.getTokenType() != PyTokenTypes.RPAR) {
|
||||
getExpressionParser().parseExpression();
|
||||
}
|
||||
checkMatches(PyTokenTypes.RPAR, ") expected");
|
||||
getExpressionParser().parseArgumentList();
|
||||
}
|
||||
else {
|
||||
final PsiBuilder.Marker inheritMarker = myBuilder.mark();
|
||||
inheritMarker.done(PyElementTypes.ARGUMENT_LIST);
|
||||
}
|
||||
inheritMarker.done(PyElementTypes.PARENTHESIZED_EXPRESSION);
|
||||
checkMatches(PyTokenTypes.COLON, "colon expected");
|
||||
parseSuite();
|
||||
classMarker.done(PyElementTypes.CLASS_DECLARATION);
|
||||
|
||||
@@ -81,15 +81,9 @@ public class PyClassImpl extends PyPresentableElementImpl<PyClassStub> implement
|
||||
|
||||
@NotNull
|
||||
public PyExpression[] getSuperClassExpressions() {
|
||||
final PyParenthesizedExpression superExpression = PsiTreeUtil.getChildOfType(this, PyParenthesizedExpression.class);
|
||||
if (superExpression != null) {
|
||||
PyExpression expr = superExpression.getContainedExpression();
|
||||
if (expr instanceof PyTupleExpression) {
|
||||
return ((PyTupleExpression) expr).getElements();
|
||||
}
|
||||
if (expr != null) {
|
||||
return new PyExpression[] { expr };
|
||||
}
|
||||
final PyArgumentList argList = PsiTreeUtil.getChildOfType(this, PyArgumentList.class);
|
||||
if (argList != null) {
|
||||
return argList.getArguments();
|
||||
}
|
||||
return PyExpression.EMPTY_ARRAY;
|
||||
}
|
||||
@@ -181,7 +175,7 @@ public class PyClassImpl extends PyPresentableElementImpl<PyClassStub> implement
|
||||
List<PyClass> result = new ArrayList<PyClass>();
|
||||
// maybe a bare old-style class?
|
||||
// TODO: depend on language version: py3k does not do old style classes
|
||||
PsiElement paren = PsiTreeUtil.getChildOfType(this, PyParenthesizedExpression.class).getFirstChild(); // no NPE, we always have the par expr
|
||||
PsiElement paren = PsiTreeUtil.getChildOfType(this, PyArgumentList.class).getFirstChild(); // no NPE, we always have the par expr
|
||||
if (paren != null && "(".equals(paren.getText())) { // "()" after class name, it's new style
|
||||
for(PsiElement element: superClassElements) {
|
||||
if (element instanceof PyClass) {
|
||||
|
||||
@@ -44,7 +44,7 @@ public class PythonLanguageLevelPusher implements FilePropertyPusher<LanguageLev
|
||||
for (Module module : modules) {
|
||||
final Sdk sdk = PythonSdkType.findPythonSdk(module);
|
||||
myModuleSdks.put(module, sdk);
|
||||
if (!usedSdks.contains(sdk)) {
|
||||
if (sdk != null && !usedSdks.contains(sdk)) {
|
||||
usedSdks.add(sdk);
|
||||
updateSdkLanguageLevel(project, sdk);
|
||||
}
|
||||
|
||||
@@ -18,7 +18,7 @@ PyFile:ClassDecorators.py
|
||||
PsiElement(Py:CLASS_KEYWORD)('class')
|
||||
PsiWhiteSpace(' ')
|
||||
PsiElement(Py:IDENTIFIER)('A')
|
||||
PyParenthesizedExpression
|
||||
PyArgumentList
|
||||
<empty list>
|
||||
PsiElement(Py:COLON)(':')
|
||||
PsiWhiteSpace('\n ')
|
||||
|
||||
@@ -3,7 +3,7 @@ PyFile:EmptySuperclassList.py
|
||||
PsiElement(Py:CLASS_KEYWORD)('class')
|
||||
PsiWhiteSpace(' ')
|
||||
PsiElement(Py:IDENTIFIER)('A')
|
||||
PyParenthesizedExpression
|
||||
PyArgumentList
|
||||
PsiElement(Py:LPAR)('(')
|
||||
PsiElement(Py:RPAR)(')')
|
||||
PsiElement(Py:COLON)(':')
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
class Foo(base1, base2, metaclass=mymeta): pass
|
||||
class Bar(*bases, **kwds): pass
|
||||
|
||||
@@ -0,0 +1,49 @@
|
||||
PyFile:SuperclassKeywordArguments.py
|
||||
PyClass: Foo
|
||||
PsiElement(Py:CLASS_KEYWORD)('class')
|
||||
PsiWhiteSpace(' ')
|
||||
PsiElement(Py:IDENTIFIER)('Foo')
|
||||
PyArgumentList
|
||||
PsiElement(Py:LPAR)('(')
|
||||
PyReferenceExpression: base1
|
||||
PsiElement(Py:IDENTIFIER)('base1')
|
||||
PsiElement(Py:COMMA)(',')
|
||||
PsiWhiteSpace(' ')
|
||||
PyReferenceExpression: base2
|
||||
PsiElement(Py:IDENTIFIER)('base2')
|
||||
PsiElement(Py:COMMA)(',')
|
||||
PsiWhiteSpace(' ')
|
||||
PyKeywordArgumentImpl: metaclass
|
||||
PsiElement(Py:IDENTIFIER)('metaclass')
|
||||
PsiElement(Py:EQ)('=')
|
||||
PyReferenceExpression: mymeta
|
||||
PsiElement(Py:IDENTIFIER)('mymeta')
|
||||
PsiElement(Py:RPAR)(')')
|
||||
PsiElement(Py:COLON)(':')
|
||||
PsiWhiteSpace(' ')
|
||||
PyStatementList
|
||||
PyPassStatement
|
||||
PsiElement(Py:PASS_KEYWORD)('pass')
|
||||
PsiWhiteSpace('\n')
|
||||
PyClass: Bar
|
||||
PsiElement(Py:CLASS_KEYWORD)('class')
|
||||
PsiWhiteSpace(' ')
|
||||
PsiElement(Py:IDENTIFIER)('Bar')
|
||||
PyArgumentList
|
||||
PsiElement(Py:LPAR)('(')
|
||||
PyStarArgument
|
||||
PsiElement(Py:MULT)('*')
|
||||
PyReferenceExpression: bases
|
||||
PsiElement(Py:IDENTIFIER)('bases')
|
||||
PsiElement(Py:COMMA)(',')
|
||||
PsiWhiteSpace(' ')
|
||||
PyStarArgument
|
||||
PsiElement(Py:EXP)('**')
|
||||
PyReferenceExpression: kwds
|
||||
PsiElement(Py:IDENTIFIER)('kwds')
|
||||
PsiElement(Py:RPAR)(')')
|
||||
PsiElement(Py:COLON)(':')
|
||||
PsiWhiteSpace(' ')
|
||||
PyStatementList
|
||||
PyPassStatement
|
||||
PsiElement(Py:PASS_KEYWORD)('pass')
|
||||
@@ -1,5 +1,6 @@
|
||||
package com.jetbrains.python;
|
||||
|
||||
import com.jetbrains.python.refactoring.classes.PyPullUpTest;
|
||||
import junit.framework.TestSuite;
|
||||
|
||||
/**
|
||||
@@ -12,6 +13,7 @@ public class PythonAllTestsSuite {
|
||||
public static final Class[] tests = {
|
||||
PythonLexerTest.class,
|
||||
PythonParsingTest.class,
|
||||
PyStringLiteralTest.class,
|
||||
PyIndentTest.class,
|
||||
PyStatementPartsTest.class,
|
||||
PythonHighlightingTest.class,
|
||||
@@ -29,7 +31,8 @@ public class PythonAllTestsSuite {
|
||||
PythonDemorganLawIntentionTest.class,
|
||||
PyQuickFixTest.class,
|
||||
PySelectWordTest.class,
|
||||
PySurroundWithTest.class
|
||||
PySurroundWithTest.class,
|
||||
PyPullUpTest.class
|
||||
};
|
||||
|
||||
public static TestSuite suite() {
|
||||
|
||||
@@ -134,6 +134,10 @@ public class PythonParsingTest extends ParsingTestCase {
|
||||
doTest(LanguageLevel.PYTHON30);
|
||||
}
|
||||
|
||||
public void testSuperclassKeywordArguments() throws Exception {
|
||||
doTest(LanguageLevel.PYTHON30);
|
||||
}
|
||||
|
||||
public void doTest() throws Exception {
|
||||
doTest(LanguageLevel.PYTHON25);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user