mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
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:
@@ -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)(')')
|
||||
@@ -0,0 +1,5 @@
|
||||
...
|
||||
x = ...
|
||||
[1, 2, ...]
|
||||
def f():
|
||||
...
|
||||
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user