Fixed parsing ellipsis as an expression in Python 3 (PY-8257)

In Python 3 ellipsis can appear anywhere, not only in sequence slicings. The parser
now always passes ellipsis, it's incorrect use is detected at the unsupported features
annotator or compatibility inspection levels.
This commit is contained in:
Andrey Vlasovskikh
2013-02-18 16:08:33 +04:00
parent 8b5b707e9c
commit 377662303c
14 changed files with 164 additions and 98 deletions
@@ -6,4 +6,5 @@ package com.jetbrains.python.psi;
* @author yole
*/
public interface PyNoneLiteralExpression extends PyLiteralExpression {
boolean isEllipsis();
}
@@ -72,6 +72,9 @@ public class ExpressionParsing extends Parsing {
parseReprExpression(myBuilder);
return true;
}
else if (parseEllipsis()) {
return true;
}
return false;
}
@@ -97,7 +100,7 @@ public class ExpressionParsing extends Parsing {
expr.done(PyElementTypes.LIST_LITERAL_EXPRESSION);
return;
}
if (!parseSingleExpression(isTargetExpression, false)) {
if (!parseSingleExpression(isTargetExpression)) {
builder.error(message("PARSE.expected.expression"));
}
if (builder.getTokenType() == PyTokenTypes.FOR_KEYWORD) {
@@ -111,7 +114,7 @@ public class ExpressionParsing extends Parsing {
if (atToken(PyTokenTypes.RBRACKET)) {
break;
}
if (!parseSingleExpression(isTargetExpression, false)) {
if (!parseSingleExpression(isTargetExpression)) {
builder.error(message("PARSE.expected.expr.or.comma.or.bracket"));
break;
}
@@ -154,7 +157,7 @@ public class ExpressionParsing extends Parsing {
result = parseORTestExpression(false, false);
}
else {
result = parseTupleExpression(false, false, true, false);
result = parseTupleExpression(false, false, true);
}
if (!result) {
myBuilder.error("expression expected");
@@ -172,7 +175,7 @@ public class ExpressionParsing extends Parsing {
}
final PsiBuilder.Marker firstExprMarker = myBuilder.mark();
if (!parseSingleExpression(false, false)) {
if (!parseSingleExpression(false)) {
myBuilder.error("expression expected");
firstExprMarker.drop();
expr.done(PyElementTypes.DICT_LITERAL_EXPRESSION);
@@ -198,7 +201,7 @@ public class ExpressionParsing extends Parsing {
}
private void parseDictLiteralTail(PsiBuilder.Marker startMarker, PsiBuilder.Marker firstKeyValueMarker) {
if (!parseSingleExpression(false, false)) {
if (!parseSingleExpression(false)) {
myBuilder.error("expression expected");
firstKeyValueMarker.done(PyElementTypes.KEY_VALUE_EXPRESSION);
if (atToken(PyTokenTypes.RBRACE)) {
@@ -225,12 +228,12 @@ public class ExpressionParsing extends Parsing {
private boolean parseKeyValueExpression() {
final PsiBuilder.Marker marker = myBuilder.mark();
if (!parseSingleExpression(false, false)) {
if (!parseSingleExpression(false)) {
marker.drop();
return false;
}
checkMatches(PyTokenTypes.COLON, message("PARSE.expected.colon"));
if (!parseSingleExpression(false, false)) {
if (!parseSingleExpression(false)) {
myBuilder.error("value expression expected");
marker.drop();
return false;
@@ -242,7 +245,7 @@ public class ExpressionParsing extends Parsing {
private void parseSetLiteralTail(PsiBuilder.Marker startMarker) {
while (myBuilder.getTokenType() != PyTokenTypes.RBRACE) {
checkMatches(PyTokenTypes.COMMA, message("PARSE.expected.comma"));
if (!parseSingleExpression(false, false)) {
if (!parseSingleExpression(false)) {
break;
}
}
@@ -325,7 +328,7 @@ public class ExpressionParsing extends Parsing {
parseSliceEnd(expr, sliceItemStart);
}
else {
boolean hadExpression = parseSingleExpression(false, true);
boolean hadExpression = parseSingleExpression(false);
if (atToken(PyTokenTypes.COLON)) {
sliceOrTupleStart.drop();
parseSliceEnd(expr, sliceItemStart);
@@ -334,7 +337,7 @@ public class ExpressionParsing extends Parsing {
sliceItemStart.done(PyElementTypes.SLICE_ITEM);
if (!parseSliceListTail(expr, sliceOrTupleStart)) {
sliceOrTupleStart.rollbackTo();
if (!parseTupleExpression(false, false, false, true)) {
if (!parseTupleExpression(false, false, false)) {
myBuilder.error("tuple expression expected");
}
checkMatches(PyTokenTypes.RBRACKET, message("PARSE.expected.rbracket"));
@@ -404,13 +407,13 @@ public class ExpressionParsing extends Parsing {
sliceMarker.done(PyElementTypes.EMPTY_EXPRESSION);
}
else {
parseSingleExpression(false, true);
parseSingleExpression(false);
}
if (!BRACKET_COLON_COMMA.contains(myBuilder.getTokenType())) {
myBuilder.error(message("PARSE.expected.colon.or.rbracket"));
}
if (matchToken(PyTokenTypes.COLON)) {
parseSingleExpression(false, true);
parseSingleExpression(false);
}
sliceItemStart.done(PyElementTypes.SLICE_ITEM);
@@ -427,12 +430,12 @@ public class ExpressionParsing extends Parsing {
while (atToken(PyTokenTypes.COMMA)) {
nextToken();
PsiBuilder.Marker sliceItemStart = myBuilder.mark();
parseTestExpression(false, false, true);
parseTestExpression(false, false);
if (matchToken(PyTokenTypes.COLON)) {
inSlice = true;
parseTestExpression(false, false, true);
parseTestExpression(false, false);
if (matchToken(PyTokenTypes.COLON)) {
parseTestExpression(false, false, true);
parseTestExpression(false, false);
}
}
sliceItemStart.done(PyElementTypes.SLICE_ITEM);
@@ -479,7 +482,7 @@ public class ExpressionParsing extends Parsing {
if (myBuilder.getTokenType() == PyTokenTypes.MULT || myBuilder.getTokenType() == PyTokenTypes.EXP) {
final PsiBuilder.Marker starArgMarker = myBuilder.mark();
myBuilder.advanceLexer();
if (!parseSingleExpression(false, false)) {
if (!parseSingleExpression(false)) {
myBuilder.error(message("PARSE.expected.expression"));
}
starArgMarker.done(PyElementTypes.STAR_ARGUMENT_EXPRESSION);
@@ -490,7 +493,7 @@ public class ExpressionParsing extends Parsing {
myBuilder.advanceLexer();
if (myBuilder.getTokenType() == PyTokenTypes.EQ) {
myBuilder.advanceLexer();
if (!parseSingleExpression(false, false)) {
if (!parseSingleExpression(false)) {
myBuilder.error(message("PARSE.expected.expression"));
}
keywordArgMarker.done(PyElementTypes.KEYWORD_ARGUMENT_EXPRESSION);
@@ -498,7 +501,7 @@ public class ExpressionParsing extends Parsing {
}
keywordArgMarker.rollbackTo();
}
if (!parseSingleExpression(false, false)) {
if (!parseSingleExpression(false)) {
myBuilder.error(message("PARSE.expected.expression"));
break;
}
@@ -513,11 +516,11 @@ public class ExpressionParsing extends Parsing {
}
public boolean parseExpressionOptional() {
return parseTupleExpression(false, false, false, false);
return parseTupleExpression(false, false, false);
}
public boolean parseExpressionOptional(boolean isTargetExpression) {
return parseTupleExpression(false, isTargetExpression, false, false);
return parseTupleExpression(false, isTargetExpression, false);
}
public void parseExpression() {
@@ -527,7 +530,7 @@ public class ExpressionParsing extends Parsing {
}
public void parseExpression(boolean stopOnIn, boolean isTargetExpression) {
if (!parseTupleExpression(stopOnIn, isTargetExpression, false, false)) {
if (!parseTupleExpression(stopOnIn, isTargetExpression, false)) {
myBuilder.error(message("PARSE.expected.expression"));
}
}
@@ -538,7 +541,7 @@ public class ExpressionParsing extends Parsing {
myBuilder.advanceLexer();
if (myBuilder.getTokenType() == PyTokenTypes.FROM_KEYWORD) {
myBuilder.advanceLexer();
final boolean parsed = parseTupleExpression(false, isTargetExpression, false, false);
final boolean parsed = parseTupleExpression(false, isTargetExpression, false);
if (!parsed) {
myBuilder.error(message("PARSE.expected.expression"));
}
@@ -546,19 +549,19 @@ public class ExpressionParsing extends Parsing {
return parsed;
}
else {
parseTupleExpression(false, isTargetExpression, false, false);
parseTupleExpression(false, isTargetExpression, false);
yieldExpr.done(PyElementTypes.YIELD_EXPRESSION);
return true;
}
}
else {
return parseTupleExpression(false, isTargetExpression, false, false);
return parseTupleExpression(false, isTargetExpression, false);
}
}
protected boolean parseTupleExpression(boolean stopOnIn, boolean isTargetExpression, final boolean oldTest, boolean allowEllipsis) {
protected boolean parseTupleExpression(boolean stopOnIn, boolean isTargetExpression, final boolean oldTest) {
PsiBuilder.Marker expr = myBuilder.mark();
boolean exprParseResult = oldTest ? parseOldTestExpression() : parseTestExpression(stopOnIn, isTargetExpression, allowEllipsis);
boolean exprParseResult = oldTest ? parseOldTestExpression() : parseTestExpression(stopOnIn, isTargetExpression);
if (!exprParseResult) {
expr.drop();
return false;
@@ -567,7 +570,7 @@ public class ExpressionParsing extends Parsing {
while (myBuilder.getTokenType() == PyTokenTypes.COMMA) {
myBuilder.advanceLexer();
PsiBuilder.Marker expr2 = myBuilder.mark();
exprParseResult = oldTest ? parseOldTestExpression() : parseTestExpression(stopOnIn, isTargetExpression, allowEllipsis);
exprParseResult = oldTest ? parseOldTestExpression() : parseTestExpression(stopOnIn, isTargetExpression);
if (!exprParseResult) {
expr2.rollbackTo();
break;
@@ -582,8 +585,8 @@ public class ExpressionParsing extends Parsing {
return true;
}
public boolean parseSingleExpression(boolean isTargetExpression, boolean allowEllipsis) {
return parseTestExpression(false, isTargetExpression, allowEllipsis);
public boolean parseSingleExpression(boolean isTargetExpression) {
return parseTestExpression(false, isTargetExpression);
}
public boolean parseOldExpression() {
@@ -593,13 +596,10 @@ public class ExpressionParsing extends Parsing {
return parseORTestExpression(false, false);
}
private boolean parseTestExpression(boolean stopOnIn, boolean isTargetExpression, boolean allowEllipsis) {
private boolean parseTestExpression(boolean stopOnIn, boolean isTargetExpression) {
if (myBuilder.getTokenType() == PyTokenTypes.LAMBDA_KEYWORD) {
return parseLambdaExpression(false);
}
if (allowEllipsis && parseEllipsis()) {
return true;
}
PsiBuilder.Marker condExpr = myBuilder.mark();
if (!parseORTestExpression( stopOnIn, isTargetExpression)) {
condExpr.drop();
@@ -616,7 +616,7 @@ public class ExpressionParsing extends Parsing {
}
else {
myBuilder.advanceLexer();
if (!parseTestExpression(stopOnIn, isTargetExpression, allowEllipsis)) {
if (!parseTestExpression(stopOnIn, isTargetExpression)) {
myBuilder.error(message("PARSE.expected.expression"));
}
}
@@ -640,7 +640,7 @@ public class ExpressionParsing extends Parsing {
PsiBuilder.Marker expr = myBuilder.mark();
myBuilder.advanceLexer();
getFunctionParser().parseParameterListContents(PyTokenTypes.COLON, false, true);
boolean parseExpressionResult = oldTest ? parseOldTestExpression() : parseSingleExpression(false, false);
boolean parseExpressionResult = oldTest ? parseOldTestExpression() : parseSingleExpression(false);
if (!parseExpressionResult) {
myBuilder.error(message("PARSE.expected.expression"));
}
@@ -65,7 +65,7 @@ public class FunctionParsing extends Parsing {
PsiBuilder.Marker maybeReturnAnnotation = myBuilder.mark();
nextToken();
if (matchToken(PyTokenTypes.GT)) {
if (!myContext.getExpressionParser().parseSingleExpression(false, false)) {
if (!myContext.getExpressionParser().parseSingleExpression(false)) {
myBuilder.error(message("PARSE.expected.expression"));
}
maybeReturnAnnotation.done(PyElementTypes.ANNOTATION);
@@ -195,13 +195,13 @@ public class FunctionParsing extends Parsing {
if (!isLambda && myContext.getLanguageLevel().isPy3K() && atToken(PyTokenTypes.COLON)) {
PsiBuilder.Marker annotationMarker = myBuilder.mark();
nextToken();
if (!getExpressionParser().parseSingleExpression(false, false)) {
if (!getExpressionParser().parseSingleExpression(false)) {
myBuilder.error(message("PARSE.expected.expression"));
}
annotationMarker.done(PyElementTypes.ANNOTATION);
}
if (!isStarParameter && matchToken(PyTokenTypes.EQ)) {
if (!getExpressionParser().parseSingleExpression(false, false)) {
if (!getExpressionParser().parseSingleExpression(false)) {
PsiBuilder.Marker invalidElements = myBuilder.mark();
while(!atAnyOfTokens(endToken, PyTokenTypes.LINE_BREAK, PyTokenTypes.COMMA, null)) {
nextToken();
@@ -251,7 +251,7 @@ public class FunctionParsing extends Parsing {
}
if (myBuilder.getTokenType() == PyTokenTypes.EQ) {
myBuilder.advanceLexer();
getExpressionParser().parseSingleExpression(false, false);
getExpressionParser().parseSingleExpression(false);
}
tuple.done(PyElementTypes.TUPLE_PARAMETER);
}
@@ -278,18 +278,18 @@ public class StatementParsing extends Parsing implements ITokenTypeRemapper {
if (builder.getTokenType() == PyTokenTypes.GTGT) {
final PsiBuilder.Marker target = builder.mark();
builder.advanceLexer();
getExpressionParser().parseSingleExpression(false, false);
getExpressionParser().parseSingleExpression(false);
target.done(PyElementTypes.PRINT_TARGET);
}
else {
getExpressionParser().parseSingleExpression(false, false);
getExpressionParser().parseSingleExpression(false);
}
while (builder.getTokenType() == PyTokenTypes.COMMA) {
builder.advanceLexer();
if (getEndOfStatementsTokens().contains(builder.getTokenType())) {
break;
}
getExpressionParser().parseSingleExpression(false, false);
getExpressionParser().parseSingleExpression(false);
}
checkEndOfStatement(scope);
statement.done(PyElementTypes.PRINT_STATEMENT);
@@ -317,13 +317,13 @@ public class StatementParsing extends Parsing implements ITokenTypeRemapper {
assertCurrentToken(PyTokenTypes.DEL_KEYWORD);
final PsiBuilder.Marker delStatement = myBuilder.mark();
myBuilder.advanceLexer();
if (!getExpressionParser().parseSingleExpression(false, false)) {
if (!getExpressionParser().parseSingleExpression(false)) {
myBuilder.error("Expression expected");
}
while (myBuilder.getTokenType() == PyTokenTypes.COMMA) {
myBuilder.advanceLexer();
if (!getEndOfStatementsTokens().contains(myBuilder.getTokenType())) {
if (!getExpressionParser().parseSingleExpression(false, false)) {
if (!getExpressionParser().parseSingleExpression(false)) {
myBuilder.error("Expression expected");
}
}
@@ -338,18 +338,18 @@ public class StatementParsing extends Parsing implements ITokenTypeRemapper {
final PsiBuilder.Marker raiseStatement = myBuilder.mark();
myBuilder.advanceLexer();
if (!getEndOfStatementsTokens().contains(myBuilder.getTokenType())) {
getExpressionParser().parseSingleExpression(false, false);
getExpressionParser().parseSingleExpression(false);
if (myBuilder.getTokenType() == PyTokenTypes.COMMA) {
myBuilder.advanceLexer();
getExpressionParser().parseSingleExpression(false, false);
getExpressionParser().parseSingleExpression(false);
if (myBuilder.getTokenType() == PyTokenTypes.COMMA) {
myBuilder.advanceLexer();
getExpressionParser().parseSingleExpression(false, false);
getExpressionParser().parseSingleExpression(false);
}
}
else if (myBuilder.getTokenType() == PyTokenTypes.FROM_KEYWORD) {
myBuilder.advanceLexer();
if (!getExpressionParser().parseSingleExpression(false, false)) {
if (!getExpressionParser().parseSingleExpression(false)) {
myBuilder.error("Expression expected");
}
}
@@ -362,10 +362,10 @@ public class StatementParsing extends Parsing implements ITokenTypeRemapper {
assertCurrentToken(PyTokenTypes.ASSERT_KEYWORD);
final PsiBuilder.Marker assertStatement = myBuilder.mark();
myBuilder.advanceLexer();
if (getExpressionParser().parseSingleExpression(false, false)) {
if (getExpressionParser().parseSingleExpression(false)) {
if (myBuilder.getTokenType() == PyTokenTypes.COMMA) {
myBuilder.advanceLexer();
if (!getExpressionParser().parseSingleExpression(false, false)) {
if (!getExpressionParser().parseSingleExpression(false)) {
myContext.getBuilder().error(EXPRESSION_EXPECTED);
}
}
@@ -562,10 +562,10 @@ public class StatementParsing extends Parsing implements ITokenTypeRemapper {
getExpressionParser().parseExpression(true, false);
if (myBuilder.getTokenType() == PyTokenTypes.IN_KEYWORD) {
myBuilder.advanceLexer();
getExpressionParser().parseSingleExpression(false, false);
getExpressionParser().parseSingleExpression(false);
if (myBuilder.getTokenType() == PyTokenTypes.COMMA) {
myBuilder.advanceLexer();
getExpressionParser().parseSingleExpression(false, false);
getExpressionParser().parseSingleExpression(false);
}
}
checkEndOfStatement(inSuite);
@@ -646,7 +646,7 @@ public class StatementParsing extends Parsing implements ITokenTypeRemapper {
final PsiBuilder.Marker statement = myBuilder.mark();
final PsiBuilder.Marker whilePart = myBuilder.mark();
myBuilder.advanceLexer();
if (!getExpressionParser().parseSingleExpression(false, false)) {
if (!getExpressionParser().parseSingleExpression(false)) {
myBuilder.error(EXPRESSION_EXPECTED);
}
parseColonAndSuite(scope);
@@ -677,13 +677,13 @@ public class StatementParsing extends Parsing implements ITokenTypeRemapper {
final PsiBuilder.Marker exceptBlock = myBuilder.mark();
myBuilder.advanceLexer();
if (myBuilder.getTokenType() != PyTokenTypes.COLON) {
if (!getExpressionParser().parseSingleExpression(false, false)) {
if (!getExpressionParser().parseSingleExpression(false)) {
myBuilder.error(EXPRESSION_EXPECTED);
}
setExpectAsKeyword(true);
if (myBuilder.getTokenType() == PyTokenTypes.COMMA || myBuilder.getTokenType() == PyTokenTypes.AS_KEYWORD) {
myBuilder.advanceLexer();
if (!getExpressionParser().parseSingleExpression(true, false)) {
if (!getExpressionParser().parseSingleExpression(true)) {
myBuilder.error(EXPRESSION_EXPECTED);
}
}
@@ -738,7 +738,7 @@ public class StatementParsing extends Parsing implements ITokenTypeRemapper {
setExpectAsKeyword(true);
if (myBuilder.getTokenType() == PyTokenTypes.AS_KEYWORD) {
myBuilder.advanceLexer();
getExpressionParser().parseSingleExpression(true, false); // 'as' is followed by a target
getExpressionParser().parseSingleExpression(true); // 'as' is followed by a target
}
withItem.done(PyElementTypes.WITH_ITEM);
if (!matchToken(PyTokenTypes.COMMA)) {
@@ -1,6 +1,7 @@
package com.jetbrains.python.psi.impl;
import com.intellij.lang.ASTNode;
import com.jetbrains.python.PyTokenTypes;
import com.jetbrains.python.psi.PyElementVisitor;
import com.jetbrains.python.psi.PyNoneLiteralExpression;
import com.jetbrains.python.psi.types.PyNoneType;
@@ -24,4 +25,9 @@ public class PyNoneLiteralExpressionImpl extends PyElementImpl implements PyNone
protected void acceptPyVisitor(PyElementVisitor pyVisitor) {
pyVisitor.visitPyNoneLiteralExpression(this);
}
@Override
public boolean isEllipsis() {
return getNode().findChildByType(PyTokenTypes.DOT) != null;
}
}
@@ -511,6 +511,20 @@ public abstract class CompatibilityVisitor extends PyAnnotator {
}
}
@Override
public void visitPyNoneLiteralExpression(PyNoneLiteralExpression node) {
if (shouldBeCompatibleWithPy2() && node.isEllipsis()) {
final PySubscriptionExpression subscription = PsiTreeUtil.getParentOfType(node, PySubscriptionExpression.class);
if (subscription != null && PsiTreeUtil.isAncestor(subscription.getIndexExpression(), node, false)) {
return;
}
final PySliceItem sliceItem = PsiTreeUtil.getParentOfType(node, PySliceItem.class);
if (sliceItem != null) {
return;
}
registerProblem(node, "Python versions < 3.0 do not support '...' outside of sequence slicings.");
}
}
private static class YieldVisitor extends PyElementVisitor {
private boolean _haveYield = false;
@@ -0,0 +1,2 @@
def foo():
<warning descr="Python versions < 3.0 do not support '...' outside of sequence slicings.">...</warning>
@@ -0,0 +1,5 @@
import numpy
x = numpy.zeros((3, 4, 5))
y = x[..., 0] # pass
y = x[..., 0, :] # pass
@@ -1,5 +0,0 @@
def a():
... # <- highlighted as "Statement seems to have no effect"
pass
a()
@@ -1,33 +0,0 @@
PyFile:EllipsisAsStatement.py
PyFunction('a')
PsiElement(Py:DEF_KEYWORD)('def')
PsiWhiteSpace(' ')
PsiElement(Py:IDENTIFIER)('a')
PyParameterList
PsiElement(Py:LPAR)('(')
PsiElement(Py:RPAR)(')')
PsiElement(Py:COLON)(':')
PsiWhiteSpace('\n ')
PyStatementList
PsiElement(Py:DOT)('.')
PsiErrorElement:Statement expected, found Py:DOT
<empty list>
PsiElement(Py:DOT)('.')
PsiErrorElement:Statement expected, found Py:DOT
<empty list>
PsiElement(Py:DOT)('.')
PsiErrorElement:Statement expected, found Py:DOT
<empty list>
PsiWhiteSpace(' ')
PsiComment(Py:END_OF_LINE_COMMENT)('# <- highlighted as "Statement seems to have no effect"')
PsiWhiteSpace('\n ')
PyPassStatement
PsiElement(Py:PASS_KEYWORD)('pass')
PsiWhiteSpace('\n\n')
PyExpressionStatement
PyCallExpression: a
PyReferenceExpression: a
PsiElement(Py:IDENTIFIER)('a')
PyArgumentList
PsiElement(Py:LPAR)('(')
PsiElement(Py:RPAR)(')')
+5
View File
@@ -0,0 +1,5 @@
...
x = ...
[1, 2, ...]
def f():
...
+50
View File
@@ -0,0 +1,50 @@
PyFile:EllipsisPython3.py
PyExpressionStatement
PyNoneLiteralExpression
PsiElement(Py:DOT)('.')
PsiElement(Py:DOT)('.')
PsiElement(Py:DOT)('.')
PsiWhiteSpace('\n')
PyAssignmentStatement
PyTargetExpression: x
PsiElement(Py:IDENTIFIER)('x')
PsiWhiteSpace(' ')
PsiElement(Py:EQ)('=')
PsiWhiteSpace(' ')
PyNoneLiteralExpression
PsiElement(Py:DOT)('.')
PsiElement(Py:DOT)('.')
PsiElement(Py:DOT)('.')
PsiWhiteSpace('\n')
PyExpressionStatement
PyListLiteralExpression
PsiElement(Py:LBRACKET)('[')
PyNumericLiteralExpression
PsiElement(Py:INTEGER_LITERAL)('1')
PsiElement(Py:COMMA)(',')
PsiWhiteSpace(' ')
PyNumericLiteralExpression
PsiElement(Py:INTEGER_LITERAL)('2')
PsiElement(Py:COMMA)(',')
PsiWhiteSpace(' ')
PyNoneLiteralExpression
PsiElement(Py:DOT)('.')
PsiElement(Py:DOT)('.')
PsiElement(Py:DOT)('.')
PsiElement(Py:RBRACKET)(']')
PsiWhiteSpace('\n')
PyFunction('f')
PsiElement(Py:DEF_KEYWORD)('def')
PsiWhiteSpace(' ')
PsiElement(Py:IDENTIFIER)('f')
PyParameterList
PsiElement(Py:LPAR)('(')
PsiElement(Py:RPAR)(')')
PsiElement(Py:COLON)(':')
PsiWhiteSpace('\n ')
PyStatementList
PyExpressionStatement
PyNoneLiteralExpression
PsiElement(Py:DOT)('.')
PsiElement(Py:DOT)('.')
PsiElement(Py:DOT)('.')
@@ -356,10 +356,6 @@ public class PythonParsingTest extends ParsingTestCase {
doTest();
}
public void testEllipsisAsStatement() { // PY-7763
doTest();
}
// PY-8606
public void testEllipsisInSliceList() {
doTest();
@@ -374,6 +370,11 @@ public class PythonParsingTest extends ParsingTestCase {
doTest();
}
// PY-8752
public void testEllipsisPython3() {
doTest(LanguageLevel.PYTHON33);
}
public void doTest(LanguageLevel languageLevel) {
LanguageLevel prev = myLanguageLevel;
myLanguageLevel = languageLevel;
@@ -2,6 +2,7 @@ package com.jetbrains.python.inspections;
import com.jetbrains.python.fixtures.PyTestCase;
import com.jetbrains.python.psi.LanguageLevel;
import org.jetbrains.annotations.NotNull;
/**
* User : catherine
@@ -119,6 +120,25 @@ public class PyCompatibilityInspectionTest extends PyTestCase {
doTest();
}
// PY-7763
public void testEllipsisAsStatementPy2() {
doTest(LanguageLevel.PYTHON33);
}
// PY-8606
public void testEllipsisInSubscriptionPy2() {
doTest(LanguageLevel.PYTHON33);
}
private void doTest(@NotNull LanguageLevel level) {
runWithLanguageLevel(level, new Runnable() {
@Override
public void run() {
doTest();
}
});
}
private void doTest() {
myFixture.configureByFile("inspections/PyCompatibilityInspection/" + getTestName(true) + ".py");
myFixture.enableInspections(PyCompatibilityInspection.class);