Support 'yield from' syntax (PY-6702)

This commit is contained in:
Andrey Vlasovskikh
2012-06-05 15:41:35 +04:00
parent 56a0c67e4a
commit b9939f1601
9 changed files with 67 additions and 3 deletions
@@ -531,6 +531,9 @@ public class ExpressionParsing extends Parsing {
if (myBuilder.getTokenType() == PyTokenTypes.YIELD_KEYWORD) {
PsiBuilder.Marker yieldExpr = myBuilder.mark();
myBuilder.advanceLexer();
if (myBuilder.getTokenType() == PyTokenTypes.FROM_KEYWORD) {
myBuilder.advanceLexer();
}
parseTupleExpression(false, isTargetExpression, false);
yieldExpr.done(PyElementTypes.YIELD_EXPRESSION);
return true;
@@ -8,4 +8,6 @@ import org.jetbrains.annotations.Nullable;
public interface PyYieldExpression extends PyExpression {
@Nullable
PyExpression getExpression();
boolean isDelegating();
}
@@ -1,8 +1,8 @@
package com.jetbrains.python.psi.impl;
import com.intellij.lang.ASTNode;
import com.jetbrains.python.PyElementTypes;
import com.jetbrains.python.PythonDialectsTokenSetProvider;
import com.intellij.psi.util.PsiTreeUtil;
import com.jetbrains.python.PyTokenTypes;
import com.jetbrains.python.psi.PyElementVisitor;
import com.jetbrains.python.psi.PyExpression;
import com.jetbrains.python.psi.PyYieldExpression;
@@ -24,7 +24,13 @@ public class PyYieldExpressionImpl extends PyElementImpl implements PyYieldExpre
@Override
public PyExpression getExpression() {
return childToPsi(PythonDialectsTokenSetProvider.INSTANCE.getExpressionTokens(), 0);
final PyExpression[] expressions = PsiTreeUtil.getChildrenOfType(this, PyExpression.class);
return (expressions != null && expressions.length > 0) ? expressions[0] : null;
}
@Override
public boolean isDelegating() {
return getNode().findChildByType(PyTokenTypes.FROM_KEYWORD) != null;
}
@Override
@@ -460,6 +460,20 @@ public abstract class CompatibilityVisitor extends PyAnnotator {
len, node, null);
}
@Override
public void visitPyYieldExpression(PyYieldExpression node) {
super.visitPyYieldExpression(node);
if (!node.isDelegating()) {
return;
}
for (LanguageLevel level : myVersionsToProcess) {
if (level.isOlderThan(LanguageLevel.PYTHON33)) {
registerProblem(node, "Python versions < 3.3 do not support this syntax. Delegating to a subgenerator is available since " +
"Python 3.3; use explicit iteration over subgenerator instead.");
}
}
}
private boolean shouldBeCompatibleWithPy3() {
if (myVersionsToProcess.contains(LanguageLevel.PYTHON30) || myVersionsToProcess.contains(LanguageLevel.PYTHON31)
|| myVersionsToProcess.contains(LanguageLevel.PYTHON32))
@@ -0,0 +1,2 @@
def f(g):
<error descr="Python versions < 3.3 do not support this syntax. Delegating to a subgenerator is available since Python 3.3; use explicit iteration over subgenerator instead.">yield from g()</error>
+2
View File
@@ -0,0 +1,2 @@
def f(g):
yield from g()
+25
View File
@@ -0,0 +1,25 @@
PyFile:YieldFrom.py
PyFunction('f')
PsiElement(Py:DEF_KEYWORD)('def')
PsiWhiteSpace(' ')
PsiElement(Py:IDENTIFIER)('f')
PyParameterList
PsiElement(Py:LPAR)('(')
PyNamedParameter('g')
PsiElement(Py:IDENTIFIER)('g')
PsiElement(Py:RPAR)(')')
PsiElement(Py:COLON)(':')
PsiWhiteSpace('\n ')
PyStatementList
PyExpressionStatement
PyYieldExpression
PsiElement(Py:YIELD_KEYWORD)('yield')
PsiWhiteSpace(' ')
PsiElement(Py:FROM_KEYWORD)('from')
PsiWhiteSpace(' ')
PyCallExpression: g
PyReferenceExpression: g
PsiElement(Py:IDENTIFIER)('g')
PyArgumentList
PsiElement(Py:LPAR)('(')
PsiElement(Py:RPAR)(')')
@@ -152,6 +152,11 @@ public class PythonHighlightingTest extends PyTestCase {
doTest(LanguageLevel.PYTHON33, true, false);
}
// PY-6702
public void testYieldFromBefore33() {
doTest(LanguageLevel.PYTHON32, true, false);
}
public void testYieldInNestedFunction() {
// highlight func declaration first, lest we get an "Extra fragment highlighted" error.
EditorColorsManager manager = EditorColorsManager.getInstance();
@@ -318,6 +318,11 @@ public class PythonParsingTest extends ParsingTestCase {
doTest();
}
// PY-6702
public void testYieldFrom() {
doTest(LanguageLevel.PYTHON33);
}
public void doTest() {
doTest(LanguageLevel.PYTHON25);
}