diff --git a/python/psi-api/src/com/jetbrains/python/psi/PyNoneLiteralExpression.java b/python/psi-api/src/com/jetbrains/python/psi/PyNoneLiteralExpression.java index ca83704e149d..f5a6f2ad37d5 100644 --- a/python/psi-api/src/com/jetbrains/python/psi/PyNoneLiteralExpression.java +++ b/python/psi-api/src/com/jetbrains/python/psi/PyNoneLiteralExpression.java @@ -6,4 +6,5 @@ package com.jetbrains.python.psi; * @author yole */ public interface PyNoneLiteralExpression extends PyLiteralExpression { + boolean isEllipsis(); } diff --git a/python/src/com/jetbrains/python/parsing/ExpressionParsing.java b/python/src/com/jetbrains/python/parsing/ExpressionParsing.java index e597bf45096e..74ee45358b63 100644 --- a/python/src/com/jetbrains/python/parsing/ExpressionParsing.java +++ b/python/src/com/jetbrains/python/parsing/ExpressionParsing.java @@ -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")); } diff --git a/python/src/com/jetbrains/python/parsing/FunctionParsing.java b/python/src/com/jetbrains/python/parsing/FunctionParsing.java index 40ca2371334d..7bb12f969585 100644 --- a/python/src/com/jetbrains/python/parsing/FunctionParsing.java +++ b/python/src/com/jetbrains/python/parsing/FunctionParsing.java @@ -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); } diff --git a/python/src/com/jetbrains/python/parsing/StatementParsing.java b/python/src/com/jetbrains/python/parsing/StatementParsing.java index 37751366a56e..9cb92b119ddc 100644 --- a/python/src/com/jetbrains/python/parsing/StatementParsing.java +++ b/python/src/com/jetbrains/python/parsing/StatementParsing.java @@ -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)) { diff --git a/python/src/com/jetbrains/python/psi/impl/PyNoneLiteralExpressionImpl.java b/python/src/com/jetbrains/python/psi/impl/PyNoneLiteralExpressionImpl.java index 21b7fe494a5a..256a1c86881e 100644 --- a/python/src/com/jetbrains/python/psi/impl/PyNoneLiteralExpressionImpl.java +++ b/python/src/com/jetbrains/python/psi/impl/PyNoneLiteralExpressionImpl.java @@ -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; + } } diff --git a/python/src/com/jetbrains/python/validation/CompatibilityVisitor.java b/python/src/com/jetbrains/python/validation/CompatibilityVisitor.java index bd1471516bf4..81febf3554f2 100644 --- a/python/src/com/jetbrains/python/validation/CompatibilityVisitor.java +++ b/python/src/com/jetbrains/python/validation/CompatibilityVisitor.java @@ -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; diff --git a/python/testData/inspections/PyCompatibilityInspection/ellipsisAsStatementPy2.py b/python/testData/inspections/PyCompatibilityInspection/ellipsisAsStatementPy2.py new file mode 100644 index 000000000000..6e4f90cd7034 --- /dev/null +++ b/python/testData/inspections/PyCompatibilityInspection/ellipsisAsStatementPy2.py @@ -0,0 +1,2 @@ +def foo(): + ... diff --git a/python/testData/inspections/PyCompatibilityInspection/ellipsisInSubscriptionPy2.py b/python/testData/inspections/PyCompatibilityInspection/ellipsisInSubscriptionPy2.py new file mode 100644 index 000000000000..e0e70df1ee91 --- /dev/null +++ b/python/testData/inspections/PyCompatibilityInspection/ellipsisInSubscriptionPy2.py @@ -0,0 +1,5 @@ +import numpy + +x = numpy.zeros((3, 4, 5)) +y = x[..., 0] # pass +y = x[..., 0, :] # pass diff --git a/python/testData/psi/EllipsisAsStatement.py b/python/testData/psi/EllipsisAsStatement.py deleted file mode 100644 index e1efdfe13f1e..000000000000 --- a/python/testData/psi/EllipsisAsStatement.py +++ /dev/null @@ -1,5 +0,0 @@ -def a(): - ... # <- highlighted as "Statement seems to have no effect" - pass - -a() \ No newline at end of file diff --git a/python/testData/psi/EllipsisAsStatement.txt b/python/testData/psi/EllipsisAsStatement.txt deleted file mode 100644 index dbe4c271bd31..000000000000 --- a/python/testData/psi/EllipsisAsStatement.txt +++ /dev/null @@ -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 - - PsiElement(Py:DOT)('.') - PsiErrorElement:Statement expected, found Py:DOT - - PsiElement(Py:DOT)('.') - PsiErrorElement:Statement expected, found Py:DOT - - 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)(')') \ No newline at end of file diff --git a/python/testData/psi/EllipsisPython3.py b/python/testData/psi/EllipsisPython3.py new file mode 100644 index 000000000000..e4d7775aa912 --- /dev/null +++ b/python/testData/psi/EllipsisPython3.py @@ -0,0 +1,5 @@ +... +x = ... +[1, 2, ...] +def f(): + ... diff --git a/python/testData/psi/EllipsisPython3.txt b/python/testData/psi/EllipsisPython3.txt new file mode 100644 index 000000000000..af6f8ea82aa2 --- /dev/null +++ b/python/testData/psi/EllipsisPython3.txt @@ -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)('.') \ No newline at end of file diff --git a/python/testSrc/com/jetbrains/python/PythonParsingTest.java b/python/testSrc/com/jetbrains/python/PythonParsingTest.java index 4cdd61188e8f..f338c203a2c8 100644 --- a/python/testSrc/com/jetbrains/python/PythonParsingTest.java +++ b/python/testSrc/com/jetbrains/python/PythonParsingTest.java @@ -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; diff --git a/python/testSrc/com/jetbrains/python/inspections/PyCompatibilityInspectionTest.java b/python/testSrc/com/jetbrains/python/inspections/PyCompatibilityInspectionTest.java index 0bafbd187f90..e4f263bdb89b 100644 --- a/python/testSrc/com/jetbrains/python/inspections/PyCompatibilityInspectionTest.java +++ b/python/testSrc/com/jetbrains/python/inspections/PyCompatibilityInspectionTest.java @@ -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);