Merge branch 'vlan/async-await'

This commit is contained in:
Andrey Vlasovskikh
2015-09-14 11:58:35 +03:00
90 changed files with 2018 additions and 985 deletions
@@ -6,7 +6,7 @@ import string
import time
# !!! Don't forget to update VERSION and required_gen_version if necessary !!!
VERSION = "1.136"
VERSION = "1.137"
OUT_ENCODING = 'utf-8'
@@ -1014,6 +1014,8 @@ class ModuleRedeclarator(object):
self.classes_buf.out(0, txt)
txt = create_method()
self.classes_buf.out(0, txt)
txt = create_coroutine()
self.classes_buf.out(0, txt)
# Fake <type 'namedtuple'>
if version[0] >= 3 or (version[0] == 2 and version[1] >= 6):
@@ -135,6 +135,39 @@ class __method(object):
"""
return txt
def create_coroutine():
if version[0] == 3 and version[1] >= 5:
return """
class __coroutine(object):
'''A mock class representing coroutine type.'''
def __init__(self):
self.__name__ = ''
self.__qualname__ = ''
self.cr_await = None
self.cr_frame = None
self.cr_running = False
self.cr_code = None
def __await__(self):
return []
def __iter__(self):
return []
def close(self):
pass
def send(self, value):
pass
def throw(self, type, value=None, traceback=None):
pass
"""
return ""
def _searchbases(cls, accum):
# logic copied from inspect.py
if cls not in accum:
+17 -7
View File
@@ -382,7 +382,7 @@ class enumerate(object):
:type iterable: collections.Iterable[T]
:type start: numbers.Integral
:rtype: enumerate[int, T]
:rtype: enumerate[T]
"""
pass
@@ -396,7 +396,7 @@ class enumerate(object):
def __iter__(self):
"""x.__iter__() <==> iter(x).
:rtype: enumerate[int, T]
:rtype: collections.Iterator[(int, T)]
"""
return self
@@ -2026,6 +2026,12 @@ class list(object):
"""
return []
def __iter__(self):
"""
:rtype: collections.Iterator[T]
"""
return []
def __getitem__(self, y):
"""y-th item of x, origin 0.
@@ -2450,18 +2456,20 @@ class file(object):
class __generator(object):
"""A mock class representing the generator function type."""
def __init__(self, value):
def __init__(self):
"""Create a generator object.
:type value: T
:rtype: __generator[T]
:rtype: __generator[T, U, V]
"""
self.gi_code = None
self.gi_frame = None
self.gi_running = 0
def __iter__(self):
"""Defined to support iteration over container."""
"""Defined to support iteration over container.
:rtype: collections.Iterator[T]
"""
pass
def next(self):
@@ -2483,7 +2491,8 @@ class __generator(object):
"""Resumes the generator and "sends" a value that becomes the
result of the current yield-expression.
:rtype: T
:type value: U
:rtype: None
"""
pass
@@ -2494,6 +2503,7 @@ class __generator(object):
"""
pass
class __function(object):
"""A mock class representing function type."""
+17 -7
View File
@@ -370,7 +370,7 @@ class enumerate(object):
:type iterable: collections.Iterable[T]
:type start: int | long
:rtype: enumerate[int, T]
:rtype: enumerate[T]
"""
pass
@@ -384,7 +384,7 @@ class enumerate(object):
def __iter__(self):
"""x.__iter__() <==> iter(x).
:rtype: enumerate[int, T]
:rtype: collections.Iterator[(int, T)]
"""
return self
@@ -1716,6 +1716,12 @@ class list(object):
"""
return []
def __iter__(self):
"""
:rtype: collections.Iterator[T]
"""
return []
def __getitem__(self, y):
"""y-th item of x, origin 0.
@@ -1999,18 +2005,20 @@ class dict(object):
class __generator(object):
"""A mock class representing the generator function type."""
def __init__(self, value):
def __init__(self):
"""Create a generator object.
:type value: T
:rtype: __generator[T]
:rtype: __generator[T, U, V]
"""
self.gi_code = None
self.gi_frame = None
self.gi_running = 0
def __iter__(self):
"""Defined to support iteration over container."""
"""Defined to support iteration over container.
:rtype: collections.Iterator[T]
"""
pass
def __next__(self):
@@ -2032,7 +2040,8 @@ class __generator(object):
"""Resumes the generator and "sends" a value that becomes the
result of the current yield-expression.
:rtype: T
:type value: U
:rtype: None
"""
pass
@@ -2043,6 +2052,7 @@ class __generator(object):
"""
pass
class __function(object):
"""A mock class representing function type."""
+1 -1
View File
@@ -6,7 +6,7 @@
(default) 1.127 # anything not explicitly marked
(built-in) 1.130 # skeletons of all built-in modules are built together
(built-in) 1.137 # skeletons of all built-in modules are built together
# Note: modules like itertools, etc are "(built-in)" and are ignored if given separately
_fileio 1.127
@@ -32,7 +32,7 @@ public class IpnbPyParser extends PyParser {
builder.setTokenTypeRemapper(statementParser);
while (!builder.eof()) {
statementParser.parseStatement(context.emptyParsingScope());
statementParser.parseStatement();
}
rootMarker.done(root);
return builder.getTreeBuilt();
@@ -93,6 +93,7 @@ public class PyNames {
public static final String FAKE_FUNCTION = "__function";
public static final String FAKE_METHOD = "__method";
public static final String FAKE_NAMEDTUPLE = "__namedtuple";
public static final String FAKE_COROUTINE = "__coroutine";
public static final String FUTURE_MODULE = "__future__";
public static final String UNICODE_LITERALS = "unicode_literals";
@@ -265,6 +266,7 @@ public class PyNames {
private static final BuiltinDescription _self_other_descr = new BuiltinDescription("(self, other)");
private static final BuiltinDescription _self_item_descr = new BuiltinDescription("(self, item)");
private static final BuiltinDescription _self_key_descr = new BuiltinDescription("(self, key)");
private static final BuiltinDescription _exit_descr = new BuiltinDescription("(self, exc_type, exc_val, exc_tb)");
private static final ImmutableMap<String, BuiltinDescription> BuiltinMethods = ImmutableMap.<String, BuiltinDescription>builder()
.put("__abs__", _only_self_descr)
@@ -290,7 +292,7 @@ public class PyNames {
//_BuiltinMethods.put("__doc__", _only_self_descr);
//_BuiltinMethods.put("__docformat__", _only_self_descr);
.put("__enter__", _only_self_descr)
.put("__exit__", new BuiltinDescription("(self, exc_type, exc_val, exc_tb)"))
.put("__exit__", _exit_descr)
.put("__eq__", _self_other_descr)
//_BuiltinMethods.put("__file__", _only_self_descr);
.put("__float__", _only_self_descr)
@@ -396,6 +398,11 @@ public class PyNames {
.put("__imatmul__", _self_other_descr)
.put("__matmul__", _self_other_descr)
.put("__rmatmul__", _self_other_descr)
.put("__await__", _only_self_descr)
.put("__aenter__", _only_self_descr)
.put("__aexit__", _exit_descr)
.put("__aiter__", _only_self_descr)
.put("__anext__", _only_self_descr)
.build();
public static ImmutableMap<String, BuiltinDescription> getBuiltinMethods(LanguageLevel level) {
@@ -450,6 +457,8 @@ public class PyNames {
public static final String IN = "in";
public static final String NOT = "not";
public static final String LAMBDA = "lambda";
public static final String ASYNC = "async";
public static final String AWAIT = "await";
/**
* Contains keywords as of CPython 2.5.
@@ -73,6 +73,8 @@ public class PyTokenTypes {
public static final PyElementType FALSE_KEYWORD = new PyElementType("FALSE_KEYWORD");
public static final PyElementType NONLOCAL_KEYWORD = new PyElementType("NONLOCAL_KEYWORD");
public static final PyElementType DEBUG_KEYWORD = new PyElementType("DEBUG_KEYWORD");
public static final PyElementType ASYNC_KEYWORD = new PyElementType("ASYNC_KEYWORD");
public static final PyElementType AWAIT_KEYWORD = new PyElementType("AWAIT_KEYWORD", "__await__");
public static final PyElementType INTEGER_LITERAL = new PyElementType("INTEGER_LITERAL");
public static final PyElementType FLOAT_LITERAL = new PyElementType("FLOAT_LITERAL");
@@ -79,6 +79,8 @@ extends
@Nullable
Modifier getModifier();
boolean isAsync();
/**
* Flags that mark common alterations of a function: decoration by and wrapping in classmethod() and staticmethod().
*/
@@ -21,4 +21,5 @@ import com.jetbrains.python.psi.PyFunction;
public interface PyFunctionStub extends NamedStub<PyFunction> {
String getDocString();
String getDeprecationMessage();
boolean isAsync();
}
+2
View File
@@ -41,6 +41,7 @@
<lang.documentationProvider language="Python" implementationClass="com.jetbrains.python.documentation.PythonDocumentationProvider"/>
<lang.documentationProvider language="Python" implementationClass="com.jetbrains.python.console.PydevDocumentationProvider"/>
<lang.emacs language="Python" implementationClass="com.jetbrains.python.editor.PyEmacsHandler"/>
<highlightRangeExtension implementation="com.jetbrains.python.validation.DumbAwareHighlightingAnnotator"/>
<annotator language="Python" implementationClass="com.jetbrains.python.validation.PyAnnotatingVisitor"/>
<annotator language="Python" implementationClass="com.jetbrains.python.validation.PyDumbAwareAnnotator"/>
<quoteHandler fileType="Python" className="com.jetbrains.python.editor.PythonQuoteHandler"/>
@@ -617,6 +618,7 @@
<dumbAnnotator implementation="com.jetbrains.python.validation.GeneratorInArgumentListAnnotator"/>
<dumbAnnotator implementation="com.jetbrains.python.validation.StarAnnotator"/>
<dumbAnnotator implementation="com.jetbrains.python.validation.StringLiteralQuotesAnnotator"/>
<dumbAnnotator implementation="com.jetbrains.python.validation.DumbAwareHighlightingAnnotator"/>
<customTargetExpressionStubType implementation="com.jetbrains.python.psi.impl.stubs.PropertyStubType"/>
<dialectsTokenSetContributor implementation="com.jetbrains.python.PythonTokenSetContributor"/>
@@ -130,7 +130,8 @@ public interface PyElementTypes {
PyTokenTypes.MINUS, PyTokenTypes.MULT, PyTokenTypes.AT, PyTokenTypes.FLOORDIV, PyTokenTypes.DIV,
PyTokenTypes.PERC, PyTokenTypes.EXP);
TokenSet UNARY_OPS = TokenSet.create(PyTokenTypes.NOT_KEYWORD, PyTokenTypes.PLUS, PyTokenTypes.MINUS, PyTokenTypes.TILDE);
TokenSet UNARY_OPS = TokenSet.create(PyTokenTypes.NOT_KEYWORD, PyTokenTypes.PLUS, PyTokenTypes.MINUS, PyTokenTypes.TILDE,
PyTokenTypes.AWAIT_KEYWORD);
// Parts
PyElementType IF_PART_IF = new PyElementType("IF_IF", PyIfPartIfImpl.class);
@@ -73,7 +73,7 @@ public class PythonTokenSetContributor extends PythonDialectsTokenSetContributor
LAMBDA_KEYWORD, NOT_KEYWORD, OR_KEYWORD, PASS_KEYWORD, PRINT_KEYWORD,
RAISE_KEYWORD, RETURN_KEYWORD, TRY_KEYWORD, WITH_KEYWORD, WHILE_KEYWORD,
YIELD_KEYWORD,
NONE_KEYWORD, TRUE_KEYWORD, FALSE_KEYWORD, NONLOCAL_KEYWORD, DEBUG_KEYWORD);
NONE_KEYWORD, TRUE_KEYWORD, FALSE_KEYWORD, NONLOCAL_KEYWORD, DEBUG_KEYWORD, ASYNC_KEYWORD, AWAIT_KEYWORD);
}
@NotNull
@@ -187,20 +187,9 @@ public class PyTypingTypeProvider extends PyTypeProviderBase {
final PyClass cls = function.getContainingClass();
if (cls != null) {
final List<PyGenericType> genericTypes = collectGenericTypes(cls, context);
final PyType elementType;
if (genericTypes.size() == 1) {
elementType = genericTypes.get(0);
}
else if (genericTypes.size() > 1) {
elementType = PyTupleType.create(cls, genericTypes.toArray(new PyType[genericTypes.size()]));
}
else {
elementType = null;
}
if (elementType != null) {
return new PyCollectionTypeImpl(cls, false, elementType);
final List<PyType> elementTypes = new ArrayList<PyType>(genericTypes);
if (!elementTypes.isEmpty()) {
return new PyCollectionTypeImpl(cls, false, elementTypes);
}
}
}
@@ -438,6 +427,9 @@ public class PyTypingTypeProvider extends PyTypeProviderBase {
types.add(getType(expr, context));
}
}
else if (indexExpr != null) {
types.add(getType(indexExpr, context));
}
return types;
}
@@ -450,13 +442,12 @@ public class PyTypingTypeProvider extends PyTypeProviderBase {
final PyType operandType = getType(operand, context);
if (operandType instanceof PyClassType) {
final PyClass cls = ((PyClassType)operandType).getPyClass();
final List<PyType> indexTypes = getIndexTypes(subscriptionExpr, context);
if (PyNames.TUPLE.equals(cls.getQualifiedName())) {
final List<PyType> indexTypes = getIndexTypes(subscriptionExpr, context);
return PyTupleType.create(expression, indexTypes.toArray(new PyType[indexTypes.size()]));
}
else if (indexExpr != null) {
final PyType indexType = context.getType(indexExpr);
return new PyCollectionTypeImpl(cls, false, indexType);
return new PyCollectionTypeImpl(cls, false, indexTypes);
}
}
}
@@ -26,17 +26,20 @@ public class PyCodeFragment extends CodeFragment {
private final Set<String> myGlobalWrites;
private final Set<String> myNonlocalWrites;
private final boolean myYieldInside;
private final boolean myAsync;
public PyCodeFragment(final Set<String> input,
final Set<String> output,
final Set<String> globalWrites,
final Set<String> nonlocalWrites,
final boolean returnInside,
final boolean yieldInside) {
final boolean yieldInside,
final boolean isAsync) {
super(input, output, returnInside);
myGlobalWrites = globalWrites;
myNonlocalWrites = nonlocalWrites;
myYieldInside = yieldInside;
myAsync = isAsync;
}
public Set<String> getGlobalWrites() {
@@ -50,4 +53,8 @@ public class PyCodeFragment extends CodeFragment {
public boolean isYieldInside() {
return myYieldInside;
}
public boolean isAsync() {
return myAsync;
}
}
@@ -99,13 +99,13 @@ public class PyCodeFragmentUtil {
}
}
final boolean yieldsFound = subGraphAnalysis.yieldExpressions > 0;
if (yieldsFound && LanguageLevel.forElement(owner).isOlderThan(LanguageLevel.PYTHON33)) {
throw new CannotCreateCodeFragmentException(PyBundle.message("refactoring.extract.method.error.yield"));
}
final boolean isAsync = owner instanceof PyFunction && ((PyFunction)owner).isAsync();
return new PyCodeFragment(inputNames, outputNames, globalWrites, nonlocalWrites, subGraphAnalysis.returns > 0, yieldsFound);
return new PyCodeFragment(inputNames, outputNames, globalWrites, nonlocalWrites, subGraphAnalysis.returns > 0, yieldsFound, isAsync);
}
private static boolean resolvesToBoundMethodParameter(@NotNull PsiElement element) {
@@ -34,6 +34,8 @@ import com.jetbrains.python.PyNames;
import com.jetbrains.python.PyTokenTypes;
import com.jetbrains.python.PythonLanguage;
import com.jetbrains.python.codeInsight.PyUnindentingInsertHandler;
import com.jetbrains.python.codeInsight.controlflow.ScopeOwner;
import com.jetbrains.python.codeInsight.dataflow.scope.ScopeUtil;
import com.jetbrains.python.documentation.doctest.PyDocstringFile;
import com.jetbrains.python.psi.*;
import org.jetbrains.annotations.NonNls;
@@ -182,13 +184,19 @@ public class PyKeywordCompletionContributor extends CompletionContributor {
}
}
private static class Py3kFilter implements ElementFilter {
private static class LanguageLevelAtLeastFilter implements ElementFilter {
@NotNull private final LanguageLevel myLevel;
public LanguageLevelAtLeastFilter(@NotNull LanguageLevel level) {
myLevel = level;
}
public boolean isAcceptable(Object element, PsiElement context) {
if (!(element instanceof PsiElement)) {
return false;
}
final PsiFile containingFile = ((PsiElement)element).getContainingFile();
return containingFile instanceof PyFile && ((PyFile)containingFile).getLanguageLevel().isPy3K();
return containingFile instanceof PyFile && ((PyFile)containingFile).getLanguageLevel().isAtLeast(myLevel);
}
public boolean isClassAcceptable(Class hintClass) {
@@ -196,6 +204,7 @@ public class PyKeywordCompletionContributor extends CompletionContributor {
}
}
private static class NotParameterOrDefaultValue implements ElementFilter {
@Override
@@ -359,7 +368,8 @@ public class PyKeywordCompletionContributor extends CompletionContributor {
));
*/
private static final FilterPattern PY3K = new FilterPattern(new Py3kFilter());
private static final FilterPattern PY3K = new FilterPattern(new PyKeywordCompletionContributor.LanguageLevelAtLeastFilter(LanguageLevel.PYTHON30));
private static final FilterPattern PY35 = new FilterPattern(new LanguageLevelAtLeastFilter(LanguageLevel.PYTHON35));
// ======
@@ -477,6 +487,28 @@ public class PyKeywordCompletionContributor extends CompletionContributor {
,
new PyKeywordCompletionProvider(PyNames.NONLOCAL)
);
extend(CompletionType.BASIC,
psiElement()
.withLanguage(PythonLanguage.getInstance())
.and(PY35)
.andNot(AFTER_QUALIFIER)
.with(new PatternCondition<PsiElement>("insideAsyncDef") {
@Override
public boolean accepts(@NotNull PsiElement element, ProcessingContext context) {
final ScopeOwner owner = ScopeUtil.getScopeOwner(element);
return owner instanceof PyFunction && ((PyFunction)owner).isAsync();
}
})
.andOr(IN_BEGIN_STMT,
psiElement()
.inside(false, psiElement(PyAssignmentStatement.class), psiElement(PyTargetExpression.class))
.afterLeaf(psiElement().withElementType(PyTokenTypes.EQ)),
psiElement()
.inside(false, psiElement(PyAugAssignmentStatement.class), psiElement(PyTargetExpression.class))
.afterLeaf(psiElement().withElementType(PyTokenTypes.AUG_ASSIGN_OPERATIONS)),
psiElement().inside(true, psiElement(PyParenthesizedExpression.class))),
new PyKeywordCompletionProvider(PyNames.AWAIT));
}
private void addWithinIf() {
@@ -578,8 +610,23 @@ public class PyKeywordCompletionContributor extends CompletionContributor {
.andNot(AFTER_QUALIFIER)
.andNot(IN_FUNCTION_HEADER)
,
new PyKeywordCompletionProvider(TailType.NONE, PyNames.TRUE, PyNames.FALSE, PyNames.NONE)
);
new PyKeywordCompletionProvider(TailType.NONE, PyNames.TRUE, PyNames.FALSE, PyNames.NONE));
extend(CompletionType.BASIC,
psiElement()
.withLanguage(PythonLanguage.getInstance())
.and(PY35)
.andNot(IN_COMMENT)
.andNot(IN_IMPORT_STMT)
.andNot(IN_PARAM_LIST)
.andNot(AFTER_QUALIFIER)
.andNot(IN_STRING_LITERAL),
new PyKeywordCompletionProvider(PyNames.ASYNC));
extend(CompletionType.BASIC,
psiElement()
.withLanguage(PythonLanguage.getInstance())
.and(PY35)
.afterLeaf(psiElement().withElementType(PyTokenTypes.IDENTIFIER).withText(PyNames.ASYNC)),
new PyKeywordCompletionProvider(PyNames.DEF, PyNames.WITH, PyNames.FOR));
}
private void addAs() {
@@ -56,7 +56,7 @@ public class PyConsoleParser extends PyParser{
builder.setTokenTypeRemapper(stmt_parser); // must be done before touching the caching lexer with eof() call.
while (!builder.eof()) {
stmt_parser.parseStatement(context.emptyParsingScope());
stmt_parser.parseStatement();
}
rootMarker.done(root);
return builder.getTreeBuilt();
@@ -72,7 +72,7 @@ public class PyConsoleParsingContext extends ParsingContext {
@Override
public void parseStatement(ParsingScope scope) {
public void parseStatement() {
if (myStartsWithIPythonSymbol) {
parseIPythonCommand();
}
@@ -89,7 +89,7 @@ public class PyConsoleParsingContext extends ParsingContext {
myBuilder.advanceLexer();
}
}
super.parseStatement(scope);
super.parseStatement();
}
}
@@ -101,13 +101,14 @@ public class PyConsoleParsingContext extends ParsingContext {
ipythonCommand.done(PyElementTypes.EMPTY_EXPRESSION);
}
protected void checkEndOfStatement(ParsingScope scope) {
protected void checkEndOfStatement() {
if (myPythonConsoleData.isIPythonEnabled()) {
PsiBuilder builder = myContext.getBuilder();
if (builder.getTokenType() == PyTokenTypes.STATEMENT_BREAK) {
builder.advanceLexer();
}
else if (builder.getTokenType() == PyTokenTypes.SEMICOLON) {
final ParsingScope scope = getParsingContext().getScope();
if (!scope.isSuite()) {
builder.advanceLexer();
if (builder.getTokenType() == PyTokenTypes.STATEMENT_BREAK) {
@@ -131,7 +132,7 @@ public class PyConsoleParsingContext extends ParsingContext {
}
}
else {
super.checkEndOfStatement(scope);
super.checkEndOfStatement();
}
}
}
@@ -200,23 +200,22 @@ public class PyTypeModelBuilder {
TypeModel result = null;
if (type instanceof PyCollectionType) {
final String name = type.getName();
final PyType elementType = ((PyCollectionType)type).getElementType(myContext);
final List<TypeModel> elementTypes = new ArrayList<TypeModel>();
if (elementType instanceof PyTupleType) {
final PyTupleType tupleType = (PyTupleType)elementType;
final int n = tupleType.getElementCount();
for (int i = 0; i < n; i++) {
final PyType t = tupleType.getElementType(i);
if (t != null) {
elementTypes.add(build(t, true));
}
final List<PyType> elementTypes = ((PyCollectionType)type).getElementTypes(myContext);
boolean nullOnlyTypes = true;
for (PyType elementType : elementTypes) {
if (elementType != null) {
nullOnlyTypes = false;
break;
}
}
else if (elementType != null) {
elementTypes.add(build(elementType, true));
}
if (!elementTypes.isEmpty()) {
result = new CollectionOf(name, elementTypes);
final List<TypeModel> elementModels = new ArrayList<TypeModel>();
if (!nullOnlyTypes) {
for (PyType elementType : elementTypes) {
elementModels.add(build(elementType, true));
}
if (!elementModels.isEmpty()) {
result = new CollectionOf(name, elementModels);
}
}
}
else if (type instanceof PyUnionType && allowUnions) {
@@ -161,7 +161,7 @@ public class PyCompatibilityInspection extends PyInspection {
if (element.getTextLength() == 0) {
return;
}
range = TextRange.create(range.getStartOffset() - element.getTextOffset(), range.getEndOffset() - element.getTextOffset());
range = range.shiftRight(-element.getTextRange().getStartOffset());
if (quickFix != null)
myHolder.registerProblem(element, range, message, quickFix);
else
@@ -21,6 +21,7 @@ import com.intellij.psi.PsiElementVisitor;
import com.intellij.psi.ResolveResult;
import com.intellij.psi.util.PsiTreeUtil;
import com.jetbrains.python.PyBundle;
import com.jetbrains.python.PyTokenTypes;
import com.jetbrains.python.inspections.quickfix.StatementEffectFunctionCallQuickFix;
import com.jetbrains.python.inspections.quickfix.StatementEffectIntroduceVariableQuickFix;
import com.jetbrains.python.psi.*;
@@ -149,6 +150,10 @@ public class PyStatementEffectInspection extends PyInspection {
}
}
}
else if (expression instanceof PyPrefixExpression) {
final PyPrefixExpression prefixExpr = (PyPrefixExpression)expression;
return prefixExpr.getOperator() == PyTokenTypes.AWAIT_KEYWORD;
}
return false;
}
}
@@ -915,7 +915,7 @@ public class ExpressionParsing extends Parsing {
private boolean parsePowerExpression(boolean isTargetExpression) {
PsiBuilder.Marker expr = myBuilder.mark();
if (!parseMemberExpression(isTargetExpression)) {
if (!parseAwaitExpression(isTargetExpression)) {
expr.drop();
return false;
}
@@ -933,4 +933,19 @@ public class ExpressionParsing extends Parsing {
return true;
}
private boolean parseAwaitExpression(boolean isTargetExpression) {
if (atToken(PyTokenTypes.AWAIT_KEYWORD)) {
PsiBuilder.Marker expr = myBuilder.mark();
myBuilder.advanceLexer();
if (!parseMemberExpression(isTargetExpression)) {
myBuilder.error(message("PARSE.expected.expression"));
}
expr.done(PyElementTypes.PREFIX_EXPRESSION);
return true;
}
else {
return parseMemberExpression(isTargetExpression);
}
}
}
@@ -20,6 +20,7 @@ import com.intellij.lang.WhitespacesBinders;
import com.intellij.psi.tree.IElementType;
import com.jetbrains.python.PyElementTypes;
import com.jetbrains.python.PyTokenTypes;
import org.jetbrains.annotations.NotNull;
import static com.jetbrains.python.PyBundle.message;
@@ -33,23 +34,25 @@ public class FunctionParsing extends Parsing {
super(context);
}
public void parseFunctionDeclaration() {
public void parseFunctionDeclaration(@NotNull PsiBuilder.Marker endMarker) {
assertCurrentToken(PyTokenTypes.DEF_KEYWORD);
final PsiBuilder.Marker functionMarker = myBuilder.mark();
parseFunctionInnards(functionMarker);
parseFunctionInnards(endMarker);
}
protected IElementType getFunctionType() {
return FUNCTION_TYPE;
}
protected void parseFunctionInnards(PsiBuilder.Marker functionMarker) {
protected void parseFunctionInnards(@NotNull PsiBuilder.Marker functionMarker) {
myBuilder.advanceLexer();
parseIdentifierOrSkip(PyTokenTypes.LPAR);
parseParameterList();
parseReturnTypeAnnotation();
checkMatches(PyTokenTypes.COLON, message("PARSE.expected.colon"));
getStatementParser().parseSuite(functionMarker, getFunctionType(), myContext.emptyParsingScope().withFunction(true));
final ParsingContext context = getParsingContext();
context.pushScope(context.getScope().withFunction(true));
getStatementParser().parseSuite(functionMarker, getFunctionType());
context.popScope();
}
public void parseReturnTypeAnnotation() {
@@ -63,7 +66,7 @@ public class FunctionParsing extends Parsing {
}
}
public void parseDecoratedDeclaration(ParsingScope scope) {
public void parseDecoratedDeclaration() {
assertCurrentToken(PyTokenTypes.AT); // ??? need this?
final PsiBuilder.Marker decoratorStartMarker = myBuilder.mark();
final PsiBuilder.Marker decoListMarker = myBuilder.mark();
@@ -92,15 +95,27 @@ public class FunctionParsing extends Parsing {
}
if (decorated) decoListMarker.done(PyElementTypes.DECORATOR_LIST);
//else decoListMarker.rollbackTo();
parseDeclarationAfterDecorator(decoratorStartMarker, scope);
parseDeclarationAfterDecorator(decoratorStartMarker);
}
protected void parseDeclarationAfterDecorator(PsiBuilder.Marker endMarker, ParsingScope scope) {
protected void parseDeclarationAfterDecorator(PsiBuilder.Marker endMarker) {
if (myBuilder.getTokenType() == PyTokenTypes.ASYNC_KEYWORD) {
myBuilder.advanceLexer();
myContext.pushScope(myContext.getScope().withAsync());
parseSyncDeclarationAfterDecorator(endMarker);
myContext.popScope();
}
else {
parseSyncDeclarationAfterDecorator(endMarker);
}
}
private void parseSyncDeclarationAfterDecorator(PsiBuilder.Marker endMarker) {
if (myBuilder.getTokenType() == PyTokenTypes.DEF_KEYWORD) {
parseFunctionInnards(endMarker); // it calls endMarker.done()
parseFunctionInnards(endMarker);
}
else if (myBuilder.getTokenType() == PyTokenTypes.CLASS_KEYWORD) {
getStatementParser().parseClassDeclaration(endMarker, scope);
getStatementParser().parseClassDeclaration(endMarker);
}
else {
myBuilder.error(message("PARSE.expected.@.or.def"));
@@ -17,6 +17,10 @@ package com.jetbrains.python.parsing;
import com.intellij.lang.PsiBuilder;
import com.jetbrains.python.psi.LanguageLevel;
import org.jetbrains.annotations.NotNull;
import java.util.ArrayDeque;
import java.util.Deque;
public class ParsingContext {
private final StatementParsing stmtParser;
@@ -24,6 +28,7 @@ public class ParsingContext {
private final FunctionParsing functionParser;
private final PsiBuilder myBuilder;
private final LanguageLevel myLanguageLevel;
private final Deque<ParsingScope> myScopes;
public ParsingContext(final PsiBuilder builder, LanguageLevel languageLevel, StatementParsing.FUTURE futureFlag) {
myBuilder = builder;
@@ -31,6 +36,22 @@ public class ParsingContext {
stmtParser = new StatementParsing(this, futureFlag);
expressionParser = new ExpressionParsing(this);
functionParser = new FunctionParsing(this);
myScopes = new ArrayDeque<ParsingScope>();
myScopes.push(emptyParsingScope());
}
@NotNull
public ParsingScope popScope() {
return myScopes.pop();
}
public void pushScope(@NotNull ParsingScope scope) {
myScopes.push(scope);
}
@NotNull
public ParsingScope getScope() {
return myScopes.peek();
}
public StatementParsing getStatementParser() {
@@ -23,6 +23,9 @@ public class ParsingScope {
private boolean myClass = false;
private boolean mySuite = false;
private boolean myAfterSemicolon = false;
private boolean myAsync = false;
protected ParsingScope() {}
public ParsingScope withFunction(boolean flag) {
final ParsingScope result = copy();
@@ -42,6 +45,12 @@ public class ParsingScope {
return result;
}
public ParsingScope withAsync() {
final ParsingScope result = copy();
result.myAsync = true;
return result;
}
public boolean isFunction() {
return myFunction;
}
@@ -54,6 +63,10 @@ public class ParsingScope {
return mySuite;
}
public boolean isAsync() {
return myAsync;
}
public boolean isAfterSemicolon() {
return myAfterSemicolon;
}
@@ -71,6 +84,7 @@ public class ParsingScope {
result.myFunction = myFunction;
result.myClass = myClass;
result.mySuite = mySuite;
result.myAsync = myAsync;
return result;
}
}
@@ -49,14 +49,15 @@ public class PyParser implements PsiParser {
builder.setTokenTypeRemapper(statementParser); // must be done before touching the caching lexer with eof() call.
boolean lastAfterSemicolon = false;
while (!builder.eof()) {
ParsingScope scope = context.emptyParsingScope();
context.pushScope(context.emptyParsingScope());
if (lastAfterSemicolon) {
statementParser.parseSimpleStatement(scope);
statementParser.parseSimpleStatement();
}
else {
statementParser.parseStatement(scope);
statementParser.parseStatement();
}
lastAfterSemicolon = scope.isAfterSemicolon();
lastAfterSemicolon = context.getScope().isAfterSemicolon();
context.popScope();
}
rootMarker.done(root);
ASTNode ast = builder.getTreeBuilt();
@@ -23,6 +23,7 @@ import com.intellij.psi.tree.TokenSet;
import com.intellij.util.text.CharArrayUtil;
import com.jetbrains.python.PyElementTypes;
import com.jetbrains.python.PyTokenTypes;
import com.jetbrains.python.psi.LanguageLevel;
import com.jetbrains.python.psi.PyElementType;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.Nullable;
@@ -49,6 +50,8 @@ public class StatementParsing extends Parsing implements ITokenTypeRemapper {
@NonNls protected static final String TOK_FALSE = "False";
@NonNls protected static final String TOK_NONLOCAL = "nonlocal";
@NonNls protected static final String TOK_EXEC = "exec";
@NonNls protected static final String TOK_ASYNC = "async";
@NonNls protected static final String TOK_AWAIT = "await";
private static final String EXPRESSION_EXPECTED = "Expression expected";
public static final String IDENTIFIER_EXPECTED = "Identifier expected";
@@ -86,7 +89,7 @@ public class StatementParsing extends Parsing implements ITokenTypeRemapper {
myBuilder.setTokenTypeRemapper(this); // clear cached token type
}
public void parseStatement(ParsingScope scope) {
public void parseStatement() {
while (myBuilder.getTokenType() == PyTokenTypes.STATEMENT_BREAK) {
myBuilder.advanceLexer();
@@ -98,103 +101,107 @@ public class StatementParsing extends Parsing implements ITokenTypeRemapper {
if (firstToken == null) return;
if (firstToken == PyTokenTypes.WHILE_KEYWORD) {
parseWhileStatement(scope);
parseWhileStatement();
return;
}
if (firstToken == PyTokenTypes.IF_KEYWORD) {
parseIfStatement(PyTokenTypes.IF_KEYWORD, PyTokenTypes.ELIF_KEYWORD, PyTokenTypes.ELSE_KEYWORD, PyElementTypes.IF_STATEMENT, scope);
parseIfStatement(PyTokenTypes.IF_KEYWORD, PyTokenTypes.ELIF_KEYWORD, PyTokenTypes.ELSE_KEYWORD, PyElementTypes.IF_STATEMENT);
return;
}
if (firstToken == PyTokenTypes.FOR_KEYWORD) {
parseForStatement(scope);
parseForStatement(myBuilder.mark());
return;
}
if (firstToken == PyTokenTypes.TRY_KEYWORD) {
parseTryStatement(scope);
parseTryStatement();
return;
}
if (firstToken == PyTokenTypes.DEF_KEYWORD) {
getFunctionParser().parseFunctionDeclaration();
getFunctionParser().parseFunctionDeclaration(myBuilder.mark());
return;
}
if (firstToken == PyTokenTypes.AT) {
getFunctionParser().parseDecoratedDeclaration(scope);
getFunctionParser().parseDecoratedDeclaration();
return;
}
if (firstToken == PyTokenTypes.CLASS_KEYWORD) {
parseClassDeclaration(scope);
parseClassDeclaration();
return;
}
if (firstToken == PyTokenTypes.WITH_KEYWORD) {
parseWithStatement(scope);
parseWithStatement(myBuilder.mark());
return;
}
if (firstToken == PyTokenTypes.ASYNC_KEYWORD) {
parseAsyncStatement();
return;
}
parseSimpleStatement(scope);
parseSimpleStatement();
}
protected void parseSimpleStatement(ParsingScope scope) {
protected void parseSimpleStatement() {
PsiBuilder builder = myContext.getBuilder();
final IElementType firstToken = builder.getTokenType();
if (firstToken == null) {
return;
}
if (firstToken == PyTokenTypes.PRINT_KEYWORD && hasPrintStatement()) {
parsePrintStatement(builder, scope);
parsePrintStatement(builder);
return;
}
if (firstToken == PyTokenTypes.ASSERT_KEYWORD) {
parseAssertStatement(scope);
parseAssertStatement();
return;
}
if (firstToken == PyTokenTypes.BREAK_KEYWORD) {
parseKeywordStatement(builder, PyElementTypes.BREAK_STATEMENT, scope);
parseKeywordStatement(builder, PyElementTypes.BREAK_STATEMENT);
return;
}
if (firstToken == PyTokenTypes.CONTINUE_KEYWORD) {
parseKeywordStatement(builder, PyElementTypes.CONTINUE_STATEMENT, scope);
parseKeywordStatement(builder, PyElementTypes.CONTINUE_STATEMENT);
return;
}
if (firstToken == PyTokenTypes.DEL_KEYWORD) {
parseDelStatement(scope);
parseDelStatement();
return;
}
if (firstToken == PyTokenTypes.EXEC_KEYWORD) {
parseExecStatement(scope);
parseExecStatement();
return;
}
if (firstToken == PyTokenTypes.GLOBAL_KEYWORD) {
parseNameDefiningStatement(scope, PyElementTypes.GLOBAL_STATEMENT);
parseNameDefiningStatement(PyElementTypes.GLOBAL_STATEMENT);
return;
}
if (firstToken == PyTokenTypes.NONLOCAL_KEYWORD) {
parseNameDefiningStatement(scope, PyElementTypes.NONLOCAL_STATEMENT);
parseNameDefiningStatement(PyElementTypes.NONLOCAL_STATEMENT);
return;
}
if (firstToken == PyTokenTypes.IMPORT_KEYWORD) {
parseImportStatement(scope, PyElementTypes.IMPORT_STATEMENT, PyElementTypes.IMPORT_ELEMENT);
parseImportStatement(PyElementTypes.IMPORT_STATEMENT, PyElementTypes.IMPORT_ELEMENT);
return;
}
if (firstToken == PyTokenTypes.FROM_KEYWORD) {
parseFromImportStatement(scope);
parseFromImportStatement();
return;
}
if (firstToken == PyTokenTypes.PASS_KEYWORD) {
parseKeywordStatement(builder, PyElementTypes.PASS_STATEMENT, scope);
parseKeywordStatement(builder, PyElementTypes.PASS_STATEMENT);
return;
}
if (firstToken == PyTokenTypes.RETURN_KEYWORD) {
parseReturnStatement(builder, scope);
parseReturnStatement(builder);
return;
}
if (firstToken == PyTokenTypes.RAISE_KEYWORD) {
parseRaiseStatement(scope);
parseRaiseStatement();
return;
}
PsiBuilder.Marker exprStatement = builder.mark();
if (builder.getTokenType() == PyTokenTypes.YIELD_KEYWORD) {
getExpressionParser().parseYieldOrTupleExpression(false);
checkEndOfStatement(scope);
checkEndOfStatement();
exprStatement.done(PyElementTypes.EXPRESSION_STATEMENT);
return;
}
@@ -242,7 +249,7 @@ public class StatementParsing extends Parsing implements ITokenTypeRemapper {
}
}
checkEndOfStatement(scope);
checkEndOfStatement();
exprStatement.done(statementType);
return;
}
@@ -270,8 +277,9 @@ public class StatementParsing extends Parsing implements ITokenTypeRemapper {
return myContext.getLanguageLevel().hasPrintStatement() && !myFutureFlags.contains(FUTURE.PRINT_FUNCTION);
}
protected void checkEndOfStatement(ParsingScope scope) {
protected void checkEndOfStatement() {
PsiBuilder builder = myContext.getBuilder();
final ParsingScope scope = getParsingContext().getScope();
if (builder.getTokenType() == PyTokenTypes.STATEMENT_BREAK) {
builder.advanceLexer();
scope.setAfterSemicolon(false);
@@ -291,7 +299,7 @@ public class StatementParsing extends Parsing implements ITokenTypeRemapper {
}
}
private void parsePrintStatement(final PsiBuilder builder, ParsingScope scope) {
private void parsePrintStatement(final PsiBuilder builder) {
LOG.assertTrue(builder.getTokenType() == PyTokenTypes.PRINT_KEYWORD);
final PsiBuilder.Marker statement = builder.mark();
builder.advanceLexer();
@@ -311,29 +319,29 @@ public class StatementParsing extends Parsing implements ITokenTypeRemapper {
}
getExpressionParser().parseSingleExpression(false);
}
checkEndOfStatement(scope);
checkEndOfStatement();
statement.done(PyElementTypes.PRINT_STATEMENT);
}
protected void parseKeywordStatement(PsiBuilder builder, IElementType statementType, ParsingScope scope) {
protected void parseKeywordStatement(PsiBuilder builder, IElementType statementType) {
final PsiBuilder.Marker statement = builder.mark();
builder.advanceLexer();
checkEndOfStatement(scope);
checkEndOfStatement();
statement.done(statementType);
}
private void parseReturnStatement(PsiBuilder builder, ParsingScope inSuite) {
private void parseReturnStatement(PsiBuilder builder) {
LOG.assertTrue(builder.getTokenType() == PyTokenTypes.RETURN_KEYWORD);
final PsiBuilder.Marker returnStatement = builder.mark();
builder.advanceLexer();
if (builder.getTokenType() != null && !getEndOfStatementsTokens().contains(builder.getTokenType())) {
getExpressionParser().parseExpression();
}
checkEndOfStatement(inSuite);
checkEndOfStatement();
returnStatement.done(PyElementTypes.RETURN_STATEMENT);
}
private void parseDelStatement(ParsingScope inSuite) {
private void parseDelStatement() {
assertCurrentToken(PyTokenTypes.DEL_KEYWORD);
final PsiBuilder.Marker delStatement = myBuilder.mark();
myBuilder.advanceLexer();
@@ -349,11 +357,11 @@ public class StatementParsing extends Parsing implements ITokenTypeRemapper {
}
}
checkEndOfStatement(inSuite);
checkEndOfStatement();
delStatement.done(PyElementTypes.DEL_STATEMENT);
}
private void parseRaiseStatement(ParsingScope inSuite) {
private void parseRaiseStatement() {
assertCurrentToken(PyTokenTypes.RAISE_KEYWORD);
final PsiBuilder.Marker raiseStatement = myBuilder.mark();
myBuilder.advanceLexer();
@@ -374,11 +382,11 @@ public class StatementParsing extends Parsing implements ITokenTypeRemapper {
}
}
}
checkEndOfStatement(inSuite);
checkEndOfStatement();
raiseStatement.done(PyElementTypes.RAISE_STATEMENT);
}
private void parseAssertStatement(ParsingScope scope) {
private void parseAssertStatement() {
assertCurrentToken(PyTokenTypes.ASSERT_KEYWORD);
final PsiBuilder.Marker assertStatement = myBuilder.mark();
myBuilder.advanceLexer();
@@ -389,7 +397,7 @@ public class StatementParsing extends Parsing implements ITokenTypeRemapper {
myContext.getBuilder().error(EXPRESSION_EXPECTED);
}
}
checkEndOfStatement(scope);
checkEndOfStatement();
}
else {
myContext.getBuilder().error(EXPRESSION_EXPECTED);
@@ -397,12 +405,12 @@ public class StatementParsing extends Parsing implements ITokenTypeRemapper {
assertStatement.done(PyElementTypes.ASSERT_STATEMENT);
}
protected void parseImportStatement(ParsingScope scope, IElementType statementType, IElementType elementType) {
protected void parseImportStatement(IElementType statementType, IElementType elementType) {
final PsiBuilder builder = myContext.getBuilder();
final PsiBuilder.Marker importStatement = builder.mark();
builder.advanceLexer();
parseImportElements(elementType, true, false, false);
checkEndOfStatement(scope);
checkEndOfStatement();
importStatement.done(statementType);
}
@@ -411,7 +419,7 @@ public class StatementParsing extends Parsing implements ITokenTypeRemapper {
from identifier import id, id... -- may be either relative or absolute
from . import identifier -- only relative
*/
private void parseFromImportStatement(ParsingScope inSuite) {
private void parseFromImportStatement() {
PsiBuilder builder = myContext.getBuilder();
assertCurrentToken(PyTokenTypes.FROM_KEYWORD);
myFutureImportPhase = Phase.FROM;
@@ -447,7 +455,7 @@ public class StatementParsing extends Parsing implements ITokenTypeRemapper {
statementType = types.statement;
parseImportElements(types.element, false, false, from_future);
}
checkEndOfStatement(inSuite);
checkEndOfStatement();
fromImportStatement.done(statementType);
myFutureImportPhase = Phase.NONE;
}
@@ -563,7 +571,7 @@ public class StatementParsing extends Parsing implements ITokenTypeRemapper {
return true;
}
private void parseNameDefiningStatement(ParsingScope scope, final PyElementType elementType) {
private void parseNameDefiningStatement(final PyElementType elementType) {
final PsiBuilder.Marker globalStatement = myBuilder.mark();
myBuilder.advanceLexer();
parseIdentifier(PyElementTypes.TARGET_EXPRESSION);
@@ -571,11 +579,11 @@ public class StatementParsing extends Parsing implements ITokenTypeRemapper {
myBuilder.advanceLexer();
parseIdentifier(PyElementTypes.TARGET_EXPRESSION);
}
checkEndOfStatement(scope);
checkEndOfStatement();
globalStatement.done(elementType);
}
private void parseExecStatement(ParsingScope inSuite) {
private void parseExecStatement() {
assertCurrentToken(PyTokenTypes.EXEC_KEYWORD);
final PsiBuilder.Marker execStatement = myBuilder.mark();
myBuilder.advanceLexer();
@@ -588,12 +596,12 @@ public class StatementParsing extends Parsing implements ITokenTypeRemapper {
getExpressionParser().parseSingleExpression(false);
}
}
checkEndOfStatement(inSuite);
checkEndOfStatement();
execStatement.done(PyElementTypes.EXEC_STATEMENT);
}
protected void parseIfStatement(PyElementType ifKeyword, PyElementType elifKeyword, PyElementType elseKeyword, PyElementType elementType,
ParsingScope scope) {
protected void parseIfStatement(PyElementType ifKeyword, PyElementType elifKeyword, PyElementType elseKeyword,
PyElementType elementType) {
assertCurrentToken(ifKeyword);
final PsiBuilder.Marker ifStatement = myBuilder.mark();
final PsiBuilder.Marker ifPart = myBuilder.mark();
@@ -601,7 +609,7 @@ public class StatementParsing extends Parsing implements ITokenTypeRemapper {
if (!getExpressionParser().parseSingleExpression(false)) {
myBuilder.error("expression expected");
}
parseColonAndSuite(scope);
parseColonAndSuite();
ifPart.done(PyElementTypes.IF_PART_IF);
PsiBuilder.Marker elifPart = myBuilder.mark();
while (myBuilder.getTokenType() == elifKeyword) {
@@ -609,7 +617,7 @@ public class StatementParsing extends Parsing implements ITokenTypeRemapper {
if (!getExpressionParser().parseSingleExpression(false)) {
myBuilder.error("expression expected");
}
parseColonAndSuite(scope);
parseColonAndSuite();
elifPart.done(PyElementTypes.IF_PART_ELIF);
elifPart = myBuilder.mark();
}
@@ -617,7 +625,7 @@ public class StatementParsing extends Parsing implements ITokenTypeRemapper {
final PsiBuilder.Marker elsePart = myBuilder.mark();
if (myBuilder.getTokenType() == elseKeyword) {
myBuilder.advanceLexer();
parseColonAndSuite(scope);
parseColonAndSuite();
elsePart.done(PyElementTypes.ELSE_PART);
}
else {
@@ -647,33 +655,32 @@ public class StatementParsing extends Parsing implements ITokenTypeRemapper {
return result;
}
private void parseForStatement(ParsingScope scope) {
private void parseForStatement(PsiBuilder.Marker endMarker) {
assertCurrentToken(PyTokenTypes.FOR_KEYWORD);
final PsiBuilder.Marker statement = myBuilder.mark();
parseForPart(scope);
parseForPart();
final PsiBuilder.Marker elsePart = myBuilder.mark();
if (myBuilder.getTokenType() == PyTokenTypes.ELSE_KEYWORD) {
myBuilder.advanceLexer();
parseColonAndSuite(scope);
parseColonAndSuite();
elsePart.done(PyElementTypes.ELSE_PART);
}
else {
elsePart.drop();
}
statement.done(PyElementTypes.FOR_STATEMENT);
endMarker.done(PyElementTypes.FOR_STATEMENT);
}
protected void parseForPart(ParsingScope scope) {
protected void parseForPart() {
final PsiBuilder.Marker forPart = myBuilder.mark();
myBuilder.advanceLexer();
getExpressionParser().parseExpression(true, true);
checkMatches(PyTokenTypes.IN_KEYWORD, "'in' expected");
getExpressionParser().parseExpression();
parseColonAndSuite(scope);
parseColonAndSuite();
forPart.done(PyElementTypes.FOR_PART);
}
private void parseWhileStatement(ParsingScope scope) {
private void parseWhileStatement() {
assertCurrentToken(PyTokenTypes.WHILE_KEYWORD);
final PsiBuilder.Marker statement = myBuilder.mark();
final PsiBuilder.Marker whilePart = myBuilder.mark();
@@ -681,12 +688,12 @@ public class StatementParsing extends Parsing implements ITokenTypeRemapper {
if (!getExpressionParser().parseSingleExpression(false)) {
myBuilder.error(EXPRESSION_EXPECTED);
}
parseColonAndSuite(scope);
parseColonAndSuite();
whilePart.done(PyElementTypes.WHILE_PART);
final PsiBuilder.Marker elsePart = myBuilder.mark();
if (myBuilder.getTokenType() == PyTokenTypes.ELSE_KEYWORD) {
myBuilder.advanceLexer();
parseColonAndSuite(scope);
parseColonAndSuite();
elsePart.done(PyElementTypes.ELSE_PART);
}
else {
@@ -695,12 +702,12 @@ public class StatementParsing extends Parsing implements ITokenTypeRemapper {
statement.done(PyElementTypes.WHILE_STATEMENT);
}
private void parseTryStatement(ParsingScope scope) {
private void parseTryStatement() {
assertCurrentToken(PyTokenTypes.TRY_KEYWORD);
final PsiBuilder.Marker statement = myBuilder.mark();
final PsiBuilder.Marker tryPart = myBuilder.mark();
myBuilder.advanceLexer();
parseColonAndSuite(scope);
parseColonAndSuite();
tryPart.done(PyElementTypes.TRY_PART);
boolean haveExceptClause = false;
if (myBuilder.getTokenType() == PyTokenTypes.EXCEPT_KEYWORD) {
@@ -720,13 +727,13 @@ public class StatementParsing extends Parsing implements ITokenTypeRemapper {
}
}
}
parseColonAndSuite(scope);
parseColonAndSuite();
exceptBlock.done(PyElementTypes.EXCEPT_PART);
}
final PsiBuilder.Marker elsePart = myBuilder.mark();
if (myBuilder.getTokenType() == PyTokenTypes.ELSE_KEYWORD) {
myBuilder.advanceLexer();
parseColonAndSuite(scope);
parseColonAndSuite();
elsePart.done(PyElementTypes.ELSE_PART);
}
else {
@@ -736,7 +743,7 @@ public class StatementParsing extends Parsing implements ITokenTypeRemapper {
final PsiBuilder.Marker finallyPart = myBuilder.mark();
if (myBuilder.getTokenType() == PyTokenTypes.FINALLY_KEYWORD) {
myBuilder.advanceLexer();
parseColonAndSuite(scope);
parseColonAndSuite();
finallyPart.done(PyElementTypes.FINALLY_PART);
}
else {
@@ -750,9 +757,9 @@ public class StatementParsing extends Parsing implements ITokenTypeRemapper {
statement.done(PyElementTypes.TRY_EXCEPT_STATEMENT);
}
private void parseColonAndSuite(ParsingScope scope) {
private void parseColonAndSuite() {
if (expectColon()) {
parseSuite(scope);
parseSuite();
}
else {
final PsiBuilder.Marker mark = myBuilder.mark();
@@ -760,9 +767,8 @@ public class StatementParsing extends Parsing implements ITokenTypeRemapper {
}
}
private void parseWithStatement(ParsingScope scope) {
private void parseWithStatement(PsiBuilder.Marker endMarker) {
assertCurrentToken(PyTokenTypes.WITH_KEYWORD);
final PsiBuilder.Marker statement = myBuilder.mark();
myBuilder.advanceLexer();
while (true) {
PsiBuilder.Marker withItem = myBuilder.mark();
@@ -780,16 +786,16 @@ public class StatementParsing extends Parsing implements ITokenTypeRemapper {
break;
}
}
parseColonAndSuite(scope);
statement.done(PyElementTypes.WITH_STATEMENT);
parseColonAndSuite();
endMarker.done(PyElementTypes.WITH_STATEMENT);
}
private void parseClassDeclaration(ParsingScope scope) {
private void parseClassDeclaration() {
final PsiBuilder.Marker classMarker = myBuilder.mark();
parseClassDeclaration(classMarker, scope);
parseClassDeclaration(classMarker);
}
public void parseClassDeclaration(PsiBuilder.Marker classMarker, ParsingScope scope) {
public void parseClassDeclaration(PsiBuilder.Marker classMarker) {
assertCurrentToken(PyTokenTypes.CLASS_KEYWORD);
myBuilder.advanceLexer();
parseIdentifierOrSkip(PyTokenTypes.LPAR, PyTokenTypes.COLON);
@@ -800,15 +806,41 @@ public class StatementParsing extends Parsing implements ITokenTypeRemapper {
final PsiBuilder.Marker inheritMarker = myBuilder.mark();
inheritMarker.done(PyElementTypes.ARGUMENT_LIST);
}
parseColonAndSuite(scope.withClass(true));
final ParsingContext context = getParsingContext();
context.pushScope(context.getScope().withClass(true));
parseColonAndSuite();
context.popScope();
classMarker.done(PyElementTypes.CLASS_DECLARATION);
}
public void parseSuite(ParsingScope scope) {
parseSuite(null, null, scope);
private void parseAsyncStatement() {
assertCurrentToken(PyTokenTypes.ASYNC_KEYWORD);
final PsiBuilder.Marker marker = myBuilder.mark();
myBuilder.advanceLexer();
final IElementType token = myBuilder.getTokenType();
if (token == PyTokenTypes.DEF_KEYWORD) {
final ParsingContext context = getParsingContext();
context.pushScope(context.getScope().withAsync());
getFunctionParser().parseFunctionDeclaration(marker);
context.popScope();
}
else if (token == PyTokenTypes.WITH_KEYWORD) {
parseWithStatement(marker);
}
else if (token == PyTokenTypes.FOR_KEYWORD) {
parseForStatement(marker);
}
else {
marker.drop();
myBuilder.error("'def' or 'with' or 'for' expected");
}
}
public void parseSuite(@Nullable PsiBuilder.Marker endMarker, @Nullable IElementType elType, ParsingScope scope) {
public void parseSuite() {
parseSuite(null, null);
}
public void parseSuite(@Nullable PsiBuilder.Marker endMarker, @Nullable IElementType elType) {
if (myBuilder.getTokenType() == PyTokenTypes.STATEMENT_BREAK) {
myBuilder.advanceLexer();
@@ -821,7 +853,7 @@ public class StatementParsing extends Parsing implements ITokenTypeRemapper {
}
else {
while (!myBuilder.eof() && myBuilder.getTokenType() != PyTokenTypes.DEDENT) {
parseStatement(scope);
parseStatement();
}
}
}
@@ -837,10 +869,6 @@ public class StatementParsing extends Parsing implements ITokenTypeRemapper {
if (indentFound && !myBuilder.eof()) {
checkMatches(PyTokenTypes.DEDENT, "Dedent expected");
}
// NOTE: the following line advances the PsiBuilder lexer and thus
// ensures that the whitespace following the statement list is included
// in the block containing the statement list
myBuilder.getTokenType();
}
else {
final PsiBuilder.Marker marker = myBuilder.mark();
@@ -848,12 +876,17 @@ public class StatementParsing extends Parsing implements ITokenTypeRemapper {
myBuilder.error("Statement expected");
}
else {
parseSimpleStatement(scope.withSuite(true));
final ParsingContext context = getParsingContext();
context.pushScope(context.getScope().withSuite(true));
parseSimpleStatement();
context.popScope();
while (matchToken(PyTokenTypes.SEMICOLON)) {
if (matchToken(PyTokenTypes.STATEMENT_BREAK)) {
break;
}
parseSimpleStatement(scope.withSuite(true));
context.pushScope(context.getScope().withSuite(true));
parseSimpleStatement();
context.popScope();
}
}
marker.done(PyElementTypes.STATEMENT_LIST);
@@ -905,6 +938,18 @@ public class StatementParsing extends Parsing implements ITokenTypeRemapper {
if (isWordAtPosition(text, start, end, TOK_NONLOCAL)) {
return PyTokenTypes.NONLOCAL_KEYWORD;
}
if (myContext.getLanguageLevel().isAtLeast(LanguageLevel.PYTHON35)) {
if (isWordAtPosition(text, start, end, TOK_ASYNC)) {
if (myContext.getScope().isAsync() || myBuilder.lookAhead(1) == PyTokenTypes.DEF_KEYWORD) {
return PyTokenTypes.ASYNC_KEYWORD;
}
}
if (isWordAtPosition(text, start, end, TOK_AWAIT)) {
if (myContext.getScope().isAsync()) {
return PyTokenTypes.AWAIT_KEYWORD;
}
}
}
}
else if (!myContext.getLanguageLevel().isPy3K() && source == PyTokenTypes.IDENTIFIER) {
if (isWordAtPosition(text, start, end, TOK_EXEC)) {
@@ -63,7 +63,7 @@ public class PyFileElementType extends IStubFileElementType<PyFileStub> {
@Override
public int getStubVersion() {
// Don't forget to update versions of indexes that use the updated stub-based elements
return 50;
return 51;
}
@Nullable
@@ -40,9 +40,7 @@ import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.io.File;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.*;
/**
* Provides access to Python builtins via skeletons.
@@ -164,28 +162,39 @@ public class PyBuiltinCache {
public PyType createLiteralCollectionType(final PySequenceExpression sequence, final String name, @NotNull TypeEvalContext context) {
final PyClass cls = getClass(name);
if (cls != null) {
return new PyCollectionTypeImpl(cls, false, getSequenceElementType(sequence, context));
return new PyCollectionTypeImpl(cls, false, getSequenceElementTypes(sequence, context));
}
return null;
}
@Nullable
private static PyType getSequenceElementType(@NotNull PySequenceExpression sequence, @NotNull TypeEvalContext context) {
@NotNull
private static List<PyType> getSequenceElementTypes(@NotNull PySequenceExpression sequence, @NotNull TypeEvalContext context) {
final PyExpression[] elements = sequence.getElements();
if (elements.length == 0 || elements.length > 10 /* performance */) {
return null;
return Collections.singletonList(null);
}
final PyType result = context.getType(elements[0]);
if (result == null) {
return null;
final PyType firstElementType = context.getType(elements[0]);
if (firstElementType == null) {
return Collections.singletonList(null);
}
for (int i = 1; i < elements.length; i++) {
final PyType elementType = context.getType(elements[i]);
if (elementType == null || !elementType.equals(result)) {
return null;
if (elementType == null || !elementType.equals(firstElementType)) {
return Collections.singletonList(null);
}
}
return result;
if (sequence instanceof PyDictLiteralExpression) {
if (firstElementType instanceof PyTupleType) {
final PyTupleType tupleType = (PyTupleType)firstElementType;
if (tupleType.getElementCount() == 2) {
return Arrays.asList(tupleType.getElementType(0), tupleType.getElementType(1));
}
}
return Arrays.asList(null, null);
}
else {
return Collections.singletonList(firstElementType);
}
}
@Nullable
@@ -510,8 +510,8 @@ public class PyCallExpressionHelper {
if (cls != null) {
if (init.getContainingClass() != cls) {
if (t instanceof PyCollectionType) {
final PyType elementType = ((PyCollectionType)t).getElementType(context);
return Ref.create(new PyCollectionTypeImpl(cls, false, elementType));
final List<PyType> elementTypes = ((PyCollectionType)t).getElementTypes(context);
return Ref.create(new PyCollectionTypeImpl(cls, false, elementTypes));
}
return Ref.create(new PyClassTypeImpl(cls, false));
}
@@ -44,6 +44,7 @@ public class PyFunctionBuilder {
private String[] myDocStringLines = null;
@NotNull
private final Map<String, String> myDecoratorValues = new HashMap<String, String>();
private boolean myAsync = false;
/**
* Creates builder copying signature and doc from another one.
@@ -136,6 +137,11 @@ public class PyFunctionBuilder {
return this;
}
public PyFunctionBuilder makeAsync() {
myAsync = true;
return this;
}
public PyFunctionBuilder statement(String text) {
myStatements.add(text);
return this;
@@ -166,6 +172,9 @@ public class PyFunctionBuilder {
}
decoratorAppender.append("\n");
}
if (myAsync) {
builder.append("async ");
}
builder.append("def ");
builder.append(myName).append("(");
builder.append(StringUtil.join(myParameters, ", "));
@@ -183,6 +183,12 @@ public class PyFunctionImpl extends PyBaseElementImpl<PyFunctionStub> implements
@Nullable
@Override
public PyType getReturnType(@NotNull TypeEvalContext context, @NotNull TypeEvalContext.Key key) {
final PyType type = getReturnType(context);
return isAsync() ? createCoroutineType(type) : type;
}
@Nullable
private PyType getReturnType(@NotNull TypeEvalContext context) {
for (PyTypeProvider typeProvider : Extensions.getExtensions(PyTypeProvider.EP_NAME)) {
final Ref<PyType> returnTypeRef = typeProvider.getReturnType(this, context);
if (returnTypeRef != null) {
@@ -318,7 +324,9 @@ public class PyFunctionImpl extends PyBaseElementImpl<PyFunctionStub> implements
final PyType type = context.getType(node);
if (node.isDelegating() && type instanceof PyCollectionType) {
final PyCollectionType collectionType = (PyCollectionType)type;
types.add(collectionType.getElementType(context));
// TODO: Select the parameter types that matches T in Iterable[T]
final List<PyType> elementTypes = collectionType.getElementTypes(context);
types.add(elementTypes.isEmpty() ? null : elementTypes.get(0));
}
else {
types.add(type);
@@ -340,7 +348,8 @@ public class PyFunctionImpl extends PyBaseElementImpl<PyFunctionStub> implements
if (elementType != null) {
final PyClass generator = cache.getClass(PyNames.FAKE_GENERATOR);
if (generator != null) {
return Ref.create(new PyCollectionTypeImpl(generator, false, elementType.get()));
final List<PyType> parameters = Arrays.asList(elementType.get(), null, getReturnStatementType(context));
return Ref.create(new PyCollectionTypeImpl(generator, false, parameters));
}
}
if (!types.isEmpty()) {
@@ -351,7 +360,7 @@ public class PyFunctionImpl extends PyBaseElementImpl<PyFunctionStub> implements
@Nullable
public PyType getReturnStatementType(TypeEvalContext typeEvalContext) {
ReturnVisitor visitor = new ReturnVisitor(this, typeEvalContext);
final ReturnVisitor visitor = new ReturnVisitor(this, typeEvalContext);
final PyStatementList statements = getStatementList();
statements.accept(visitor);
if (isGeneratedStub() && !visitor.myHasReturns) {
@@ -363,6 +372,16 @@ public class PyFunctionImpl extends PyBaseElementImpl<PyFunctionStub> implements
return visitor.result();
}
@Nullable
private PyType createCoroutineType(@Nullable PyType returnType) {
final PyBuiltinCache cache = PyBuiltinCache.getInstance(this);
if (returnType instanceof PyClassLikeType && PyNames.FAKE_COROUTINE.equals(((PyClassLikeType)returnType).getClassQName())) {
return returnType;
}
final PyClass generator = cache.getClass(PyNames.FAKE_COROUTINE);
return generator != null ? new PyCollectionTypeImpl(generator, false, Collections.singletonList(returnType)) : null;
}
public PyFunction asMethod() {
if (getContainingClass() != null) {
return this;
@@ -636,6 +655,15 @@ public class PyFunctionImpl extends PyBaseElementImpl<PyFunctionStub> implements
return null;
}
@Override
public boolean isAsync() {
final PyFunctionStub stub = getStub();
if (stub != null) {
return stub.isAsync();
}
return getNode().findChildByType(PyTokenTypes.ASYNC_KEYWORD) != null;
}
@Nullable
private Modifier getWrappersFromStub() {
final StubElement parentStub = getStub().getParentStub();
@@ -19,13 +19,12 @@ import com.intellij.lang.ASTNode;
import com.intellij.psi.PsiElement;
import com.jetbrains.python.PyNames;
import com.jetbrains.python.psi.*;
import com.jetbrains.python.psi.types.PyCollectionTypeImpl;
import com.jetbrains.python.psi.types.PyType;
import com.jetbrains.python.psi.types.TypeEvalContext;
import com.jetbrains.python.psi.types.*;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
/**
@@ -48,8 +47,8 @@ public class PyGeneratorExpressionImpl extends PyComprehensionElementImpl implem
final PyBuiltinCache cache = PyBuiltinCache.getInstance(this);
final PyClass generator = cache.getClass(PyNames.FAKE_GENERATOR);
if (resultExpr != null && generator != null) {
final PyType elementType = context.getType(resultExpr);
return new PyCollectionTypeImpl(generator, false, elementType);
final List<PyType> parameters = Arrays.asList(context.getType(resultExpr), null, PyNoneType.INSTANCE);
return new PyCollectionTypeImpl(generator, false, parameters);
}
return null;
}
@@ -26,6 +26,8 @@ import com.jetbrains.python.psi.types.TypeEvalContext;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.Collections;
/**
* @author yole
*/
@@ -47,7 +49,7 @@ public class PyListCompExpressionImpl extends PyComprehensionElementImpl impleme
final PyClass list = cache.getClass("list");
if (resultExpr != null && list != null) {
final PyType elementType = context.getType(resultExpr);
return new PyCollectionTypeImpl(list, false, elementType);
return new PyCollectionTypeImpl(list, false, Collections.singletonList(elementType));
}
return cache.getListType();
}
@@ -224,8 +224,8 @@ public class PyNamedParameterImpl extends PyBaseElementImpl<PyNamedParameterStub
initType = context.getReturnType(init);
if (init.getContainingClass() != containingClass) {
if (initType instanceof PyCollectionType) {
final PyType elementType = ((PyCollectionType)initType).getElementType(context);
return new PyCollectionTypeImpl(containingClass, false, elementType);
final List<PyType> elementTypes = ((PyCollectionType)initType).getElementTypes(context);
return new PyCollectionTypeImpl(containingClass, false, elementTypes);
}
}
}
@@ -27,11 +27,13 @@ import com.jetbrains.python.PythonDialectsTokenSetProvider;
import com.jetbrains.python.psi.*;
import com.jetbrains.python.psi.impl.references.PyOperatorReference;
import com.jetbrains.python.psi.resolve.PyResolveContext;
import com.jetbrains.python.psi.types.PyType;
import com.jetbrains.python.psi.types.TypeEvalContext;
import com.jetbrains.python.psi.types.*;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.ArrayList;
import java.util.List;
/**
* @author yole
*/
@@ -81,11 +83,23 @@ public class PyPrefixExpressionImpl extends PyElementImpl implements PyPrefixExp
if (getOperator() == PyTokenTypes.NOT_KEYWORD) {
return PyBuiltinCache.getInstance(this).getBoolType();
}
final boolean isAwait = getOperator() == PyTokenTypes.AWAIT_KEYWORD;
if (isAwait) {
final PyExpression operand = getOperand();
if (operand != null) {
final PyType operandType = context.getType(operand);
final PyType type = getGeneratorReturnType(operandType, context);
if (type != null) {
return type;
}
}
}
final PsiReference ref = getReference(PyResolveContext.noImplicits().withTypeEvalContext(context));
final PsiElement resolved = ref.resolve();
if (resolved instanceof PyCallable) {
// TODO: Make PyPrefixExpression a PyCallSiteExpression, use getCallType() here and analyze it in PyTypeChecker.analyzeCallSite()
return ((PyCallable)resolved).getReturnType(context, key);
final PyType returnType = ((PyCallable)resolved).getReturnType(context, key);
return isAwait ? getGeneratorReturnType(returnType, context) : returnType;
}
return null;
}
@@ -123,4 +137,40 @@ public class PyPrefixExpressionImpl extends PyElementImpl implements PyPrefixExp
final PsiElement op = getPsiOperator();
return op != null ? op.getNode() : null;
}
@Nullable
private static PyType getGeneratorReturnType(@Nullable PyType type, @NotNull TypeEvalContext context) {
if (type instanceof PyClassLikeType) {
final PyClassLikeType classLikeType = (PyClassLikeType)type;
// TODO: Understand typing.Generator as well
final String classQName = classLikeType.getClassQName();
if (PyNames.FAKE_GENERATOR.equals(classQName)) {
if (type instanceof PyCollectionType) {
final PyCollectionType collectionType = (PyCollectionType)type;
final List<PyType> elementTypes = collectionType.getElementTypes(context);
if (elementTypes.size() == 3) {
return elementTypes.get(2);
}
}
}
else if (PyNames.FAKE_COROUTINE.equals(classQName)) {
if (type instanceof PyCollectionType) {
final PyCollectionType collectionType = (PyCollectionType)type;
final List<PyType> elementTypes = collectionType.getElementTypes(context);
if (elementTypes.size() == 1) {
return elementTypes.get(0);
}
}
}
}
else if (type instanceof PyUnionType) {
final List<PyType> memberReturnTypes = new ArrayList<PyType>();
final PyUnionType unionType = (PyUnionType)type;
for (PyType member : unionType.getMembers()) {
memberReturnTypes.add(getGeneratorReturnType(member, context));
}
return PyUnionType.union(memberReturnTypes);
}
return null;
}
}
@@ -88,7 +88,9 @@ public class PySubscriptionExpressionImpl extends PyElementImpl implements PySub
res = ((PySubscriptableType)type).getElementType(indexExpression, context);
}
else if (type instanceof PyCollectionType) {
res = ((PyCollectionType)type).getElementType(context);
// TODO: Select the parameter type that matches T in Iterable[T]
final List<PyType> elementTypes = ((PyCollectionType)type).getElementTypes(context);
res = elementTypes.isEmpty() ? null : elementTypes.get(0);
}
}
}
@@ -346,20 +346,7 @@ public class PyTargetExpressionImpl extends PyBaseElementImpl<PyTargetExpression
@Nullable
private static PyType getIterationType(@Nullable PyType iterableType, @Nullable PyExpression source, @NotNull PsiElement anchor,
@NotNull TypeEvalContext context) {
PyType result = null;
if (iterableType instanceof PyCollectionType) {
result = ((PyCollectionType)iterableType).getElementType(context);
if (iterableType instanceof PyClassType) {
final PyClass cls = ((PyClassType)iterableType).getPyClass();
if (result instanceof PyTupleType && PyABCUtil.isSubclass(cls, PyNames.MAPPING)) {
final PyTupleType mappingType = (PyTupleType)result;
if (mappingType.getElementCount() == 2) {
result = mappingType.getElementType(0);
}
}
}
}
else if (iterableType instanceof PyTupleType) {
if (iterableType instanceof PyTupleType) {
final PyTupleType tupleType = (PyTupleType)iterableType;
final List<PyType> memberTypes = new ArrayList<PyType>();
for (int i = 0; i < tupleType.getElementCount(); i++) {
@@ -377,30 +364,45 @@ public class PyTargetExpressionImpl extends PyBaseElementImpl<PyTargetExpression
}
else if (iterableType != null && PyABCUtil.isSubtype(iterableType, PyNames.ITERATOR, context)) {
final PyFunction iterateMethod = findMethodByName(iterableType, PyNames.ITER, context);
PyType iterateMethodType = null;
if (iterateMethod != null) {
iterateMethodType = getContextSensitiveType(iterateMethod, context, source);
}
if (iterateMethodType instanceof PyCollectionType) {
final PyCollectionType collectionType = (PyCollectionType)iterateMethodType;
result = collectionType.getElementType(context);
}
if (result == null) {
final String nextMethodName = LanguageLevel.forElement(anchor).isAtLeast(LanguageLevel.PYTHON30) ?
PyNames.DUNDER_NEXT : PyNames.NEXT;
final PyFunction next = findMethodByName(iterableType, nextMethodName, context);
if (next != null) {
result = getContextSensitiveType(next, context, source);
final PyType iterateReturnType = getContextSensitiveType(iterateMethod, context, source);
final PyType type = getCollectionElementType(iterateReturnType, context);
if (!isTrivialType(type)) {
return type;
}
}
if (result == null) {
final PyFunction getItem = findMethodByName(iterableType, PyNames.GETITEM, context);
if (getItem != null) {
result = getContextSensitiveType(getItem, context, source);
final String nextMethodName = LanguageLevel.forElement(anchor).isAtLeast(LanguageLevel.PYTHON30) ?
PyNames.DUNDER_NEXT : PyNames.NEXT;
final PyFunction next = findMethodByName(iterableType, nextMethodName, context);
if (next != null) {
final PyType type = getContextSensitiveType(next, context, source);
if (!isTrivialType(type)) {
return type;
}
}
final PyFunction getItem = findMethodByName(iterableType, PyNames.GETITEM, context);
if (getItem != null) {
final PyType type = getContextSensitiveType(getItem, context, source);
if (!isTrivialType(type)) {
return type;
}
}
}
return result;
return null;
}
private static boolean isTrivialType(@Nullable PyType type) {
return type == null || type instanceof PyNoneType;
}
@Nullable
private static PyType getCollectionElementType(@Nullable PyType type, @NotNull TypeEvalContext context) {
if (type instanceof PyCollectionType) {
final List<PyType> elementTypes = ((PyCollectionType)type).getElementTypes(context);
// TODO: Select the parameter type that matches T in Iterable[T]
return elementTypes.isEmpty() ? null : elementTypes.get(0);
}
return null;
}
@Nullable
@@ -56,7 +56,8 @@ public class PyFunctionElementType extends PyStubElementType<PyFunctionStub, PyF
String message = function.extractDeprecationMessage();
final PyStringLiteralExpression docStringExpression = function.getDocStringExpression();
return new PyFunctionStubImpl(psi.getName(), PyPsiUtils.strValue(docStringExpression),
message == null ? null : StringRef.fromString(message), parentStub, getStubElementType());
message == null ? null : StringRef.fromString(message), function.isAsync(), parentStub,
getStubElementType());
}
public void serialize(@NotNull final PyFunctionStub stub, @NotNull final StubOutputStream dataStream)
@@ -64,6 +65,7 @@ public class PyFunctionElementType extends PyStubElementType<PyFunctionStub, PyF
dataStream.writeName(stub.getName());
dataStream.writeUTFFast(stub.getDocString() != null ? stub.getDocString() : "");
dataStream.writeName(stub.getDeprecationMessage());
dataStream.writeBoolean(stub.isAsync());
}
@NotNull
@@ -71,7 +73,9 @@ public class PyFunctionElementType extends PyStubElementType<PyFunctionStub, PyF
String name = StringRef.toString(dataStream.readName());
String docString = dataStream.readUTFFast();
StringRef deprecationMessage = dataStream.readName();
return new PyFunctionStubImpl(name, docString.length() > 0 ? docString : null, deprecationMessage, parentStub, getStubElementType());
final boolean isAsync = dataStream.readBoolean();
return new PyFunctionStubImpl(name, docString.length() > 0 ? docString : null, deprecationMessage, isAsync, parentStub,
getStubElementType());
}
public void indexStub(@NotNull final PyFunctionStub stub, @NotNull final IndexSink sink) {
@@ -27,13 +27,15 @@ public class PyFunctionStubImpl extends StubBase<PyFunction> implements PyFuncti
private final String myName;
private final String myDocString;
private final StringRef myDeprecationMessage;
private final boolean myAsync;
public PyFunctionStubImpl(final String name, final String docString, @Nullable final StringRef deprecationMessage,
public PyFunctionStubImpl(final String name, final String docString, @Nullable final StringRef deprecationMessage, boolean isAsync,
final StubElement parent, IStubElementType stubElementType) {
super(parent, stubElementType);
myName = name;
myDocString = docString;
myDeprecationMessage = deprecationMessage;
myAsync = isAsync;
}
public String getName() {
@@ -49,6 +51,11 @@ public class PyFunctionStubImpl extends StubBase<PyFunction> implements PyFuncti
return myDeprecationMessage == null ? null : myDeprecationMessage.getString();
}
@Override
public boolean isAsync() {
return myAsync;
}
@Override
public String toString() {
return "PyFunctionStub(" + myName + ")";
@@ -15,12 +15,14 @@
*/
package com.jetbrains.python.psi.types;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.annotations.NotNull;
import java.util.List;
/**
* @author yole
*/
public interface PyCollectionType extends PyType {
@Nullable
PyType getElementType(TypeEvalContext context);
@NotNull
List<PyType> getElementTypes(@NotNull TypeEvalContext context);
}
@@ -21,30 +21,33 @@ import com.jetbrains.python.psi.stubs.PyClassNameIndex;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.List;
/**
* @author yole
*/
public class PyCollectionTypeImpl extends PyClassTypeImpl implements PyCollectionType {
private final PyType myElementType;
@NotNull private final List<PyType> myElementTypes;
public PyCollectionTypeImpl(@NotNull PyClass source, boolean isDefinition, PyType elementType) {
public PyCollectionTypeImpl(@NotNull PyClass source, boolean isDefinition, @NotNull List<PyType> elementTypes) {
super(source, isDefinition);
myElementType = elementType;
myElementTypes = elementTypes;
}
@NotNull
@Override
public PyType getElementType(TypeEvalContext context) {
return myElementType;
public List<PyType> getElementTypes(@NotNull TypeEvalContext context) {
return myElementTypes;
}
@Nullable
public static PyCollectionTypeImpl createTypeByQName(@NotNull Project project, String classQualifiedName, boolean isDefinition,
PyType elementType) {
PyClass pyClass = PyClassNameIndex.findClass(classQualifiedName, project);
@NotNull List<PyType> elementTypes) {
final PyClass pyClass = PyClassNameIndex.findClass(classQualifiedName, project);
if (pyClass == null) {
return null;
}
return new PyCollectionTypeImpl(pyClass, isDefinition, elementType);
return new PyCollectionTypeImpl(pyClass, isDefinition, elementTypes);
}
@Override
@@ -56,15 +59,24 @@ public class PyCollectionTypeImpl extends PyClassTypeImpl implements PyCollectio
PyCollectionType type = (PyCollectionType)o;
final TypeEvalContext context = TypeEvalContext.codeInsightFallback(myClass.getProject());
if (myElementType != null ? !myElementType.equals(type.getElementType(context)) : type.getElementType(context) != null) return false;
final List<PyType> otherElementTypes = type.getElementTypes(context);
if (myElementTypes.size() != otherElementTypes.size()) return false;
for (int i = 0; i < myElementTypes.size(); i++) {
final PyType elementType = myElementTypes.get(i);
final PyType otherElementType = otherElementTypes.get(i);
if (elementType == null && otherElementType != null) return false;
if (elementType != null && !elementType.equals(otherElementType)) return false;
}
return true;
}
@Override
public int hashCode() {
int result = super.hashCode();
result = 31 * result + (myElementType != null ? myElementType.hashCode() : 0);
result = 31 * result;
for (PyType type : myElementTypes) {
result += type != null ? type.hashCode() : 0;
}
return result;
}
}
@@ -121,9 +121,16 @@ public class PyTypeChecker {
if (!matchClasses(superClass, subClass, context)) {
return false;
}
final PyType superElementType = ((PyCollectionType)expected).getElementType(context);
final PyType subElementType = ((PyCollectionType)actual).getElementType(context);
return match(superElementType, subElementType, context, substitutions, recursive);
// TODO: Match generic parameters based on the correspondence between the generic parameters of subClass and its base classes
final List<PyType> superElementTypes = ((PyCollectionType)expected).getElementTypes(context);
final List<PyType> subElementTypes = ((PyCollectionType)actual).getElementTypes(context);
for (int i = 0; i < subElementTypes.size(); i++) {
final PyType superElementType = i < superElementTypes.size() ? superElementTypes.get(i) : null;
if (!match(superElementType, subElementTypes.get(i), context, substitutions, recursive)) {
return false;
}
}
return true;
}
else if (expected instanceof PyTupleType && actual instanceof PyTupleType) {
final PyTupleType superTupleType = (PyTupleType)expected;
@@ -309,7 +316,9 @@ public class PyTypeChecker {
}
else if (type instanceof PyCollectionType) {
final PyCollectionType collection = (PyCollectionType)type;
collectGenerics(collection.getElementType(context), context, collected, visited);
for (PyType elementType : collection.getElementTypes(context)) {
collectGenerics(elementType, context, collected, visited);
}
}
else if (type instanceof PyTupleType) {
final PyTupleType tuple = (PyTupleType)type;
@@ -350,9 +359,12 @@ public class PyTypeChecker {
}
else if (type instanceof PyCollectionTypeImpl) {
final PyCollectionTypeImpl collection = (PyCollectionTypeImpl)type;
final PyType elem = collection.getElementType(context);
final PyType subst = substitute(elem, substitutions, context);
return new PyCollectionTypeImpl(collection.getPyClass(), collection.isDefinition(), subst);
final List<PyType> elementTypes = collection.getElementTypes(context);
final List<PyType> substitutes = new ArrayList<PyType>();
for (PyType elementType : elementTypes) {
substitutes.add(substitute(elementType, substitutions, context));
}
return new PyCollectionTypeImpl(collection.getPyClass(), collection.isDefinition(), substitutes);
}
else if (type instanceof PyTupleType) {
final PyTupleType tuple = (PyTupleType)type;
@@ -39,10 +39,7 @@ import org.jetbrains.annotations.Nullable;
import java.io.IOException;
import java.io.StringReader;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.*;
import static com.jetbrains.python.psi.types.PyTypeTokenTypes.IDENTIFIER;
import static com.jetbrains.python.psi.types.PyTypeTokenTypes.PARAMETER;
@@ -188,17 +185,16 @@ public class PyTypeParser {
final List<ParseResult> third = value.getSecond();
final PyType firstType = first.getType();
if (firstType instanceof PyClassType) {
final List<PyType> tupleTypes = new ArrayList<PyType>();
tupleTypes.add(second.getType());
final List<PyType> typesInBrackets = new ArrayList<PyType>();
typesInBrackets.add(second.getType());
ParseResult result = first;
result = result.merge(second);
for (ParseResult r : third) {
tupleTypes.add(r.getType());
typesInBrackets.add(r.getType());
result = result.merge(r);
}
final PyType elementType = third.isEmpty() ? second.getType() :
PyTupleType.create(anchor, tupleTypes.toArray(new PyType[tupleTypes.size()]));
final PyCollectionTypeImpl type = new PyCollectionTypeImpl(((PyClassType)firstType).getPyClass(), false, elementType);
final List<PyType> elementTypes = third.isEmpty() ? Collections.singletonList(second.getType()) : typesInBrackets;
final PyCollectionTypeImpl type = new PyCollectionTypeImpl(((PyClassType)firstType).getPyClass(), false, elementTypes);
return result.withType(type);
}
return EMPTY_RESULT;
@@ -215,7 +211,8 @@ public class PyTypeParser {
final PyType secondType = secondResult.getType();
if (firstType != null) {
if (firstType instanceof PyClassType && secondType != null) {
return result.withType(new PyCollectionTypeImpl(((PyClassType)firstType).getPyClass(), false, secondType));
return result.withType(new PyCollectionTypeImpl(((PyClassType)firstType).getPyClass(), false,
Collections.singletonList(secondType)));
}
return result.withType(firstType);
}
@@ -232,8 +229,9 @@ public class PyTypeParser {
final ParseResult third = value.getSecond();
final PyType firstType = first.getType();
if (firstType instanceof PyClassType) {
final PyTupleType tupleType = PyTupleType.create(anchor, new PyType[]{second.getType(), third.getType()});
final PyCollectionTypeImpl type = new PyCollectionTypeImpl(((PyClassType)firstType).getPyClass(), false, tupleType);
final List<PyType> elementTypes = Arrays.asList(second.getType(), third.getType());
final PyCollectionTypeImpl type = new PyCollectionTypeImpl(((PyClassType)firstType).getPyClass(), false,
elementTypes);
return first.merge(second).merge(third).withType(type);
}
return EMPTY_RESULT;
@@ -117,6 +117,11 @@ public class PyExtractMethodUtil {
.refactoringStarted(getRefactoringId(), beforeData);
final StringBuilder builder = new StringBuilder();
final boolean isAsync = fragment.isAsync();
if (isAsync) {
builder.append("async ");
}
builder.append("def f():\n ");
final List<PsiElement> newMethodElements = new ArrayList<PsiElement>(elementsRange);
final boolean hasOutputVariables = !fragment.getOutputVariables().isEmpty();
@@ -124,14 +129,17 @@ public class PyExtractMethodUtil {
final LanguageLevel languageLevel = LanguageLevel.forElement(statement1);
if (hasOutputVariables) {
// Generate return modified variables statements
StringUtil.join(fragment.getOutputVariables(), ", ", builder);
final String outputVariables = StringUtil.join(fragment.getOutputVariables(), ", ");
String newMethodText = builder + "return " + outputVariables;
builder.append(outputVariables);
final PsiElement returnStatement = generator.createFromText(languageLevel, PyElement.class, "return " + builder.toString());
final PyFunction function = generator.createFromText(languageLevel, PyFunction.class, newMethodText);
final PsiElement returnStatement = function.getStatementList().getStatements()[0];
newMethodElements.add(returnStatement);
}
// Generate method
PyFunction generatedMethod = generateMethodFromElements(project, methodName, variableData, newMethodElements, flags);
PyFunction generatedMethod = generateMethodFromElements(project, methodName, variableData, newMethodElements, flags, isAsync);
generatedMethod = insertGeneratedMethod(statement1, generatedMethod);
// Process parameters
@@ -148,7 +156,10 @@ public class PyExtractMethodUtil {
else if (fragment.isReturnInstructionInside()) {
builder.append("return ");
}
if (fragment.isYieldInside()) {
if (isAsync) {
builder.append("await ");
}
else if (fragment.isYieldInside()) {
builder.append("yield from ");
}
if (isMethod) {
@@ -156,7 +167,8 @@ public class PyExtractMethodUtil {
}
builder.append(methodName).append("(");
builder.append(createCallArgsString(variableData)).append(")");
PsiElement callElement = generator.createFromText(languageLevel, PyElement.class, builder.toString());
final PyFunction function = generator.createFromText(languageLevel, PyFunction.class, builder.toString());
PsiElement callElement = function.getStatementList().getStatements()[0];
// replace statements with call
callElement = replaceElements(elementsRange, callElement);
@@ -297,7 +309,8 @@ public class PyExtractMethodUtil {
@Override
public void run() {
// Generate method
PyFunction generatedMethod = generateMethodFromExpression(project, methodName, variableData, expression, flags);
final boolean isAsync = fragment.isAsync();
PyFunction generatedMethod = generateMethodFromExpression(project, methodName, variableData, expression, flags, isAsync);
generatedMethod = insertGeneratedMethod(expression, generatedMethod);
// Process parameters
@@ -306,7 +319,14 @@ public class PyExtractMethodUtil {
// Generating call element
final StringBuilder builder = new StringBuilder();
if (fragment.isYieldInside()) {
if (isAsync) {
builder.append("async ");
}
builder.append("def f():\n ");
if (isAsync) {
builder.append("await ");
}
else if (fragment.isYieldInside()) {
builder.append("yield from ");
}
else {
@@ -318,8 +338,9 @@ public class PyExtractMethodUtil {
builder.append(methodName);
builder.append("(").append(createCallArgsString(variableData)).append(")");
final PyElementGenerator generator = PyElementGenerator.getInstance(project);
final PyElement generated =
generator.createFromText(LanguageLevel.forElement(expression), PyElement.class, builder.toString());
final PyFunction function = generator.createFromText(LanguageLevel.forElement(expression), PyFunction.class,
builder.toString());
final PyElement generated = function.getStatementList().getStatements()[0];
PsiElement callElement = null;
if (generated instanceof PyReturnStatement) {
callElement = ((PyReturnStatement)generated).getExpression();
@@ -495,10 +516,13 @@ public class PyExtractMethodUtil {
@NotNull final String methodName,
@NotNull final AbstractVariableData[] variableData,
@NotNull final PsiElement expression,
@Nullable final PyUtil.MethodFlags flags) {
@Nullable final PyUtil.MethodFlags flags, boolean isAsync) {
final PyFunctionBuilder builder = new PyFunctionBuilder(methodName);
addDecorators(builder, flags);
addFakeParameters(builder, variableData);
if (isAsync) {
builder.makeAsync();
}
final String text;
if (expression instanceof PyYieldExpression) {
text = String.format("(%s)", expression.getText());
@@ -515,10 +539,14 @@ public class PyExtractMethodUtil {
@NotNull final String methodName,
@NotNull final AbstractVariableData[] variableData,
@NotNull final List<PsiElement> elementsRange,
@Nullable PyUtil.MethodFlags flags) {
@Nullable PyUtil.MethodFlags flags,
boolean isAsync) {
assert !elementsRange.isEmpty() : "Empty statements list was selected!";
final PyFunctionBuilder builder = new PyFunctionBuilder(methodName);
if (isAsync) {
builder.makeAsync();
}
addDecorators(builder, flags);
addFakeParameters(builder, variableData);
final PyFunction method = builder.buildFunction(project, LanguageLevel.forElement(elementsRange.get(0)));
@@ -194,7 +194,8 @@ public class PythonSdkUpdater implements StartupActivity {
addNewSysPathEntries(sdkUpdater, sysPath);
removeSourceRoots(sdkUpdater);
removeDuplicateClassRoots(sdkUpdater);
updateSkeletonsPath(sdkUpdater);
updateBinarySkeletonsPath(sdkUpdater);
updateUserSkeletonsPath(sdkUpdater);
}
/**
@@ -251,29 +252,42 @@ public class PythonSdkUpdater implements StartupActivity {
return false;
}
/**
* Updates user skeletons path in the Python SDK table.
*/
private static void updateUserSkeletonsPath(@NotNull PySdkUpdater sdkUpdater) {
updateSkeletonsPath(sdkUpdater, PyUserSkeletonsUtil.getUserSkeletonsDirectory(), PyUserSkeletonsUtil.USER_SKELETONS_DIR,
"User skeletons");
}
/**
* Updates binary skeletons path in the Python SDK table.
*/
private static void updateSkeletonsPath(@NotNull PySdkUpdater sdkUpdater) {
private static void updateBinarySkeletonsPath(@NotNull PySdkUpdater sdkUpdater) {
final String skeletonsPath = PythonSdkType.getSkeletonsPath(PathManager.getSystemPath(), sdkUpdater.getHomePath());
if (skeletonsPath != null) {
final VirtualFile skeletonsDir = StandardFileSystems.local().refreshAndFindFileByPath(skeletonsPath);
if (skeletonsDir != null) {
LOG.info("Binary skeletons directory for SDK \"" + sdkUpdater.getSdk().getName() + "\" (" + sdkUpdater.getHomePath() + "): " + skeletonsDir.getPath());
final List<VirtualFile> sourceRoots = Arrays.asList(sdkUpdater.getSdk().getRootProvider().getFiles(OrderRootType.CLASSES));
boolean skeletonsDirFound = false;
for (final VirtualFile root : sourceRoots) {
if (root.equals(skeletonsDir)) {
skeletonsDirFound = true;
}
if (PythonSdkType.isSkeletonsPath(root.getPath()) && !skeletonsDirFound) {
sdkUpdater.addRoot(root, OrderRootType.CLASSES);
}
}
if (!skeletonsDirFound) {
sdkUpdater.addRoot(skeletonsDir, OrderRootType.CLASSES);
updateSkeletonsPath(sdkUpdater, skeletonsDir, PythonSdkType.SKELETON_DIR_NAME, "Binary skeletons");
}
}
}
private static void updateSkeletonsPath(@NotNull PySdkUpdater sdkUpdater,
@Nullable VirtualFile skeletonsDir,
@NotNull String skeletonsDirPattern,
@NotNull String skeletonsTitle) {
if (skeletonsDir != null) {
LOG.info(skeletonsTitle + " directory for SDK \"" + sdkUpdater.getSdk().getName() + "\" (" + sdkUpdater.getHomePath() + "): " +
skeletonsDir.getPath());
final List<VirtualFile> sourceRoots = Arrays.asList(sdkUpdater.getSdk().getRootProvider().getFiles(OrderRootType.CLASSES));
sdkUpdater.removeRoots(OrderRootType.CLASSES);
for (final VirtualFile root : sourceRoots) {
if (!root.getPath().contains(skeletonsDirPattern)) {
sdkUpdater.addRoot(root, OrderRootType.CLASSES);
}
}
sdkUpdater.addRoot(skeletonsDir, OrderRootType.CLASSES);
}
}
@@ -32,10 +32,13 @@ addtoken
addusersitepackages
addval
adpcm
aenter
aexit
afterfork
aggr
aifc
aiff
aiter
alaw
algor
aliasmbcs
@@ -46,6 +49,7 @@ altsep
amost
amper
amperequal
anext
anyobject
apilevel
appbundle
@@ -383,6 +383,13 @@ public abstract class CompatibilityVisitor extends PyAnnotator {
for (PyWithItem item : problemItems) {
registerProblem(item, message.toString());
}
checkAsyncKeyword(node);
}
@Override
public void visitPyForStatement(PyForStatement node) {
super.visitPyForStatement(node);
checkAsyncKeyword(node);
}
@Override
@@ -507,6 +514,25 @@ public abstract class CompatibilityVisitor extends PyAnnotator {
len, node, null);
}
@Override
public void visitPyFunction(PyFunction node) {
super.visitPyFunction(node);
checkAsyncKeyword(node);
}
@Override
public void visitPyPrefixExpression(PyPrefixExpression node) {
super.visitPyPrefixExpression(node);
if (node.getOperator() == PyTokenTypes.AWAIT_KEYWORD) {
for (LanguageLevel level : myVersionsToProcess) {
if (level.isOlderThan(LanguageLevel.PYTHON35)) {
registerProblem(node, "Python versions < 3.5 do not support this syntax");
break;
}
}
}
}
@Override
public void visitPyYieldExpression(PyYieldExpression node) {
super.visitPyYieldExpression(node);
@@ -571,6 +597,18 @@ public abstract class CompatibilityVisitor extends PyAnnotator {
}
}
private void checkAsyncKeyword(PsiElement node) {
final ASTNode asyncNode = node.getNode().findChildByType(PyTokenTypes.ASYNC_KEYWORD);
if (asyncNode != null) {
for (LanguageLevel level : myVersionsToProcess) {
if (level.isOlderThan(LanguageLevel.PYTHON35)) {
registerProblem(node, asyncNode.getTextRange(), "Python versions < 3.5 do not support this syntax", null, true);
break;
}
}
}
}
private static class YieldVisitor extends PyElementVisitor {
private boolean _haveYield = false;
@@ -0,0 +1,64 @@
/*
* Copyright 2000-2015 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.jetbrains.python.validation;
import com.intellij.codeInsight.daemon.impl.HighlightRangeExtension;
import com.intellij.lang.ASTNode;
import com.intellij.lang.annotation.Annotation;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiFile;
import com.jetbrains.python.PyTokenTypes;
import com.jetbrains.python.highlighting.PyHighlighter;
import com.jetbrains.python.psi.*;
import org.jetbrains.annotations.NotNull;
/**
* @author vlan
*/
public class DumbAwareHighlightingAnnotator extends PyAnnotator implements HighlightRangeExtension {
@Override
public void visitPyFunction(PyFunction node) {
highlightKeyword(node, PyTokenTypes.ASYNC_KEYWORD);
}
@Override
public void visitPyForStatement(PyForStatement node) {
highlightKeyword(node, PyTokenTypes.ASYNC_KEYWORD);
}
@Override
public void visitPyWithStatement(PyWithStatement node) {
highlightKeyword(node, PyTokenTypes.ASYNC_KEYWORD);
}
@Override
public void visitPyPrefixExpression(PyPrefixExpression node) {
highlightKeyword(node, PyTokenTypes.AWAIT_KEYWORD);
}
@Override
public boolean isForceHighlightParents(@NotNull PsiFile file) {
return file instanceof PyFile;
}
private void highlightKeyword(@NotNull PsiElement node, @NotNull PyElementType elementType) {
final ASTNode astNode = node.getNode().findChildByType(elementType);
if (astNode != null) {
final Annotation annotation = getHolder().createInfoAnnotation(astNode, null);
annotation.setTextAttributes(PyHighlighter.PY_KEYWORD);
}
}
}
@@ -21,7 +21,7 @@ import com.jetbrains.python.codeInsight.dataflow.scope.ScopeUtil;
import com.jetbrains.python.psi.*;
/**
* Highlights incorrect return statements: 'return' and 'yield' outside functions, returning values from generators.
* Highlights incorrect return statements: 'return' and 'yield' outside functions, 'yield' inside async functions.
*/
public class ReturnAnnotator extends PyAnnotator {
public void visitPyReturnStatement(final PyReturnStatement node) {
@@ -36,5 +36,8 @@ public class ReturnAnnotator extends PyAnnotator {
if (!(owner instanceof PyFunction || owner instanceof PyLambdaExpression)) {
getHolder().createErrorAnnotation(node, "'yield' outside of function");
}
if (owner instanceof PyFunction && ((PyFunction)owner).isAsync()) {
getHolder().createErrorAnnotation(node, "'yield' inside async function");
}
}
}
File diff suppressed because it is too large Load Diff
@@ -0,0 +1 @@
async # Comment
+1
View File
@@ -0,0 +1 @@
asy<caret> # Comment
@@ -0,0 +1,2 @@
async def foo():
await # comment
+2
View File
@@ -0,0 +1,2 @@
async def foo():
awa<caret> # comment
+28
View File
@@ -0,0 +1,28 @@
<info descr="null">async</info> def <info descr="null">foo</info>():
pass
async = 1
<info descr="null">async</info> def <info descr="null">bar</info>():
pass
<info descr="null">async</info> def<error descr="'(' expected"><error descr="Identifier expected"> </error></error> # Incomplete<EOLError descr="':' expected"></EOLError>
<error descr="Indent expected">d</error>ef <info descr="null">regular</info>(<info descr="null">xs</info>):
<info descr="null">async</info> def <info descr="null">quux</info>():
<info descr="null">async</info> for x in xs:
pass
<info descr="null">async</info> with xs:
pass
<info descr="null">async</info> for x in xs:
pass
async<error descr="End of statement expected"> </error>with <info descr="null">xs</info>:
pass
return async
+5
View File
@@ -0,0 +1,5 @@
<info descr="null">async</info> def <info descr="null">foo</info>():
<info descr="null">await</info> x
await = 0
@@ -1,3 +1,3 @@
def foo():
class C:
def <info descr="null">foo</info>():
class <info descr="null">C</info>:
<error descr="'return' outside of function">return 1</error>
@@ -1,3 +1,3 @@
def f():
def <info descr="null">f</info>():
yield 42
<error descr="Python versions < 3.3 do not allow 'return' with argument inside generator.">return 28</error>
@@ -0,0 +1,5 @@
async def foo(x):
await x
<error descr="'yield' inside async function">yield x</error>
<error descr="'yield' inside async function">yield from x</error>
return x
@@ -0,0 +1,7 @@
<warning descr="Python versions < 3.5 do not support this syntax">async</warning> def foo(x):
<warning descr="Python version 2.4 doesn't support this syntax."><warning descr="Python versions < 3.5 do not support this syntax">async</warning> with x:
y = <warning descr="Python versions < 3.5 do not support this syntax">await x</warning>
if <warning descr="Python versions < 3.5 do not support this syntax">await y</warning>:
return <warning descr="Python versions < 3.5 do not support this syntax">await z</warning></warning>
<warning descr="Python versions < 3.5 do not support this syntax">async</warning> for y in x:
pass
@@ -0,0 +1,8 @@
async def f(x):
y = await x
await x
if await x:
pass
f(await x)
<warning descr="Statement seems to have no effect">x</warning>
return await x
@@ -79,19 +79,19 @@ def test():
return xs
return [
''.join(gen(10)),
f_1(<warning descr="Expected type 'list[int]', got '__generator[str]' instead">gen(11)</warning>),
f_2(<warning descr="Expected type 'Sequence[int]', got '__generator[str]' instead">gen(11)</warning>),
f_3(<warning descr="Expected type 'Container[int]', got '__generator[str]' instead">gen(11)</warning>),
f_4(<warning descr="Expected type 'Iterator[int]', got '__generator[str]' instead">gen(11)</warning>),
f_5(<warning descr="Expected type 'Iterable[int]', got '__generator[str]' instead">gen(11)</warning>),
f_6(<warning descr="Expected type 'list', got '__generator[str]' instead">gen(11)</warning>),
f_7(<warning descr="Expected type 'Sequence', got '__generator[str]' instead">gen(11)</warning>),
f_8(<warning descr="Expected type 'Container', got '__generator[str]' instead">gen(11)</warning>),
f_1(<warning descr="Expected type 'list[int]', got '__generator[str, Any, None]' instead">gen(11)</warning>),
f_2(<warning descr="Expected type 'Sequence[int]', got '__generator[str, Any, None]' instead">gen(11)</warning>),
f_3(<warning descr="Expected type 'Container[int]', got '__generator[str, Any, None]' instead">gen(11)</warning>),
f_4(<warning descr="Expected type 'Iterator[int]', got '__generator[str, Any, None]' instead">gen(11)</warning>),
f_5(<warning descr="Expected type 'Iterable[int]', got '__generator[str, Any, None]' instead">gen(11)</warning>),
f_6(<warning descr="Expected type 'list', got '__generator[str, Any, None]' instead">gen(11)</warning>),
f_7(<warning descr="Expected type 'Sequence', got '__generator[str, Any, None]' instead">gen(11)</warning>),
f_8(<warning descr="Expected type 'Container', got '__generator[str, Any, None]' instead">gen(11)</warning>),
f_9(gen(11)),
f_10(gen(11)),
f_11(<warning descr="Expected type 'list[Union[str, unicode]]', got '__generator[str]' instead">gen(11)</warning>),
f_12(<warning descr="Expected type 'Sequence[Union[str, unicode]]', got '__generator[str]' instead">gen(11)</warning>),
f_13(<warning descr="Expected type 'Container[Union[str, unicode]]', got '__generator[str]' instead">gen(11)</warning>),
f_11(<warning descr="Expected type 'list[Union[str, unicode]]', got '__generator[str, Any, None]' instead">gen(11)</warning>),
f_12(<warning descr="Expected type 'Sequence[Union[str, unicode]]', got '__generator[str, Any, None]' instead">gen(11)</warning>),
f_13(<warning descr="Expected type 'Container[Union[str, unicode]]', got '__generator[str, Any, None]' instead">gen(11)</warning>),
f_14(gen(11)),
f_15(gen(11)),
f_15('foo'.split('o')),
+15
View File
@@ -0,0 +1,15 @@
async def foo(x, y):
pass
async def foo_nested():
pass
async = 10
def bar():
print(async)
async def bar_nested():
pass
+80
View File
@@ -0,0 +1,80 @@
PyFile:AsyncDef.py
PyFunction('foo')
PsiElement(Py:ASYNC_KEYWORD)('async')
PsiWhiteSpace(' ')
PsiElement(Py:DEF_KEYWORD)('def')
PsiWhiteSpace(' ')
PsiElement(Py:IDENTIFIER)('foo')
PyParameterList
PsiElement(Py:LPAR)('(')
PyNamedParameter('x')
PsiElement(Py:IDENTIFIER)('x')
PsiElement(Py:COMMA)(',')
PsiWhiteSpace(' ')
PyNamedParameter('y')
PsiElement(Py:IDENTIFIER)('y')
PsiElement(Py:RPAR)(')')
PsiElement(Py:COLON)(':')
PsiWhiteSpace('\n ')
PyStatementList
PyPassStatement
PsiElement(Py:PASS_KEYWORD)('pass')
PsiWhiteSpace('\n\n ')
PyFunction('foo_nested')
PsiElement(Py:ASYNC_KEYWORD)('async')
PsiWhiteSpace(' ')
PsiElement(Py:DEF_KEYWORD)('def')
PsiWhiteSpace(' ')
PsiElement(Py:IDENTIFIER)('foo_nested')
PyParameterList
PsiElement(Py:LPAR)('(')
PsiElement(Py:RPAR)(')')
PsiElement(Py:COLON)(':')
PsiWhiteSpace('\n ')
PyStatementList
PyPassStatement
PsiElement(Py:PASS_KEYWORD)('pass')
PsiWhiteSpace('\n\n\n')
PyAssignmentStatement
PyTargetExpression: async
PsiElement(Py:IDENTIFIER)('async')
PsiWhiteSpace(' ')
PsiElement(Py:EQ)('=')
PsiWhiteSpace(' ')
PyNumericLiteralExpression
PsiElement(Py:INTEGER_LITERAL)('10')
PsiWhiteSpace('\n\n\n')
PyFunction('bar')
PsiElement(Py:DEF_KEYWORD)('def')
PsiWhiteSpace(' ')
PsiElement(Py:IDENTIFIER)('bar')
PyParameterList
PsiElement(Py:LPAR)('(')
PsiElement(Py:RPAR)(')')
PsiElement(Py:COLON)(':')
PsiWhiteSpace('\n ')
PyStatementList
PyExpressionStatement
PyCallExpression: print
PyReferenceExpression: print
PsiElement(Py:IDENTIFIER)('print')
PyArgumentList
PsiElement(Py:LPAR)('(')
PyReferenceExpression: async
PsiElement(Py:IDENTIFIER)('async')
PsiElement(Py:RPAR)(')')
PsiWhiteSpace('\n\n ')
PyFunction('bar_nested')
PsiElement(Py:ASYNC_KEYWORD)('async')
PsiWhiteSpace(' ')
PsiElement(Py:DEF_KEYWORD)('def')
PsiWhiteSpace(' ')
PsiElement(Py:IDENTIFIER)('bar_nested')
PyParameterList
PsiElement(Py:LPAR)('(')
PsiElement(Py:RPAR)(')')
PsiElement(Py:COLON)(':')
PsiWhiteSpace('\n ')
PyStatementList
PyPassStatement
PsiElement(Py:PASS_KEYWORD)('pass')
+7
View File
@@ -0,0 +1,7 @@
async def f():
async for x in xs:
pass
async for y in ys:
pass
+54
View File
@@ -0,0 +1,54 @@
PyFile:AsyncFor.py
PyFunction('f')
PsiElement(Py:ASYNC_KEYWORD)('async')
PsiWhiteSpace(' ')
PsiElement(Py:DEF_KEYWORD)('def')
PsiWhiteSpace(' ')
PsiElement(Py:IDENTIFIER)('f')
PyParameterList
PsiElement(Py:LPAR)('(')
PsiElement(Py:RPAR)(')')
PsiElement(Py:COLON)(':')
PsiWhiteSpace('\n ')
PyStatementList
PyForStatement
PsiElement(Py:ASYNC_KEYWORD)('async')
PsiWhiteSpace(' ')
PyForPart
PsiElement(Py:FOR_KEYWORD)('for')
PsiWhiteSpace(' ')
PyTargetExpression: x
PsiElement(Py:IDENTIFIER)('x')
PsiWhiteSpace(' ')
PsiElement(Py:IN_KEYWORD)('in')
PsiWhiteSpace(' ')
PyReferenceExpression: xs
PsiElement(Py:IDENTIFIER)('xs')
PsiElement(Py:COLON)(':')
PsiWhiteSpace('\n ')
PyStatementList
PyPassStatement
PsiElement(Py:PASS_KEYWORD)('pass')
PsiWhiteSpace('\n\n\n')
PyExpressionStatement
PyReferenceExpression: async
PsiElement(Py:IDENTIFIER)('async')
PsiErrorElement:End of statement expected
<empty list>
PsiWhiteSpace(' ')
PyForStatement
PyForPart
PsiElement(Py:FOR_KEYWORD)('for')
PsiWhiteSpace(' ')
PyTargetExpression: y
PsiElement(Py:IDENTIFIER)('y')
PsiWhiteSpace(' ')
PsiElement(Py:IN_KEYWORD)('in')
PsiWhiteSpace(' ')
PyReferenceExpression: ys
PsiElement(Py:IDENTIFIER)('ys')
PsiElement(Py:COLON)(':')
PsiWhiteSpace('\n ')
PyStatementList
PyPassStatement
PsiElement(Py:PASS_KEYWORD)('pass')
+6
View File
@@ -0,0 +1,6 @@
async def foo():
async with x:
pass
async with y:
pass
+44
View File
@@ -0,0 +1,44 @@
PyFile:AsyncWith.py
PyFunction('foo')
PsiElement(Py:ASYNC_KEYWORD)('async')
PsiWhiteSpace(' ')
PsiElement(Py:DEF_KEYWORD)('def')
PsiWhiteSpace(' ')
PsiElement(Py:IDENTIFIER)('foo')
PyParameterList
PsiElement(Py:LPAR)('(')
PsiElement(Py:RPAR)(')')
PsiElement(Py:COLON)(':')
PsiWhiteSpace('\n ')
PyStatementList
PyWithStatement
PsiElement(Py:ASYNC_KEYWORD)('async')
PsiWhiteSpace(' ')
PsiElement(Py:WITH_KEYWORD)('with')
PsiWhiteSpace(' ')
PyWithItem
PyReferenceExpression: x
PsiElement(Py:IDENTIFIER)('x')
PsiElement(Py:COLON)(':')
PsiWhiteSpace('\n ')
PyStatementList
PyPassStatement
PsiElement(Py:PASS_KEYWORD)('pass')
PsiWhiteSpace('\n\n')
PyExpressionStatement
PyReferenceExpression: async
PsiElement(Py:IDENTIFIER)('async')
PsiErrorElement:End of statement expected
<empty list>
PsiWhiteSpace(' ')
PyWithStatement
PsiElement(Py:WITH_KEYWORD)('with')
PsiWhiteSpace(' ')
PyWithItem
PyReferenceExpression: y
PsiElement(Py:IDENTIFIER)('y')
PsiElement(Py:COLON)(':')
PsiWhiteSpace('\n ')
PyStatementList
PyPassStatement
PsiElement(Py:PASS_KEYWORD)('pass')
+5
View File
@@ -0,0 +1,5 @@
async def f(x):
await x
await = 1
+30
View File
@@ -0,0 +1,30 @@
PyFile:Await.py
PyFunction('f')
PsiElement(Py:ASYNC_KEYWORD)('async')
PsiWhiteSpace(' ')
PsiElement(Py:DEF_KEYWORD)('def')
PsiWhiteSpace(' ')
PsiElement(Py:IDENTIFIER)('f')
PyParameterList
PsiElement(Py:LPAR)('(')
PyNamedParameter('x')
PsiElement(Py:IDENTIFIER)('x')
PsiElement(Py:RPAR)(')')
PsiElement(Py:COLON)(':')
PsiWhiteSpace('\n ')
PyStatementList
PyExpressionStatement
PyPrefixExpression
PsiElement(Py:AWAIT_KEYWORD)('await')
PsiWhiteSpace(' ')
PyReferenceExpression: x
PsiElement(Py:IDENTIFIER)('x')
PsiWhiteSpace('\n\n\n')
PyAssignmentStatement
PyTargetExpression: await
PsiElement(Py:IDENTIFIER)('await')
PsiWhiteSpace(' ')
PsiElement(Py:EQ)('=')
PsiWhiteSpace(' ')
PyNumericLiteralExpression
PsiElement(Py:INTEGER_LITERAL)('1')
+9
View File
@@ -0,0 +1,9 @@
@foo
async def bar():
await x
return 0
@baz(x, y)
async def quux():
return await x
+69
View File
@@ -0,0 +1,69 @@
PyFile:DecoratedAsyncDef.py
PyFunction('bar')
PyDecoratorList
PyDecorator: @foo
PsiElement(Py:AT)('@')
PyReferenceExpression: foo
PsiElement(Py:IDENTIFIER)('foo')
PyArgumentList
<empty list>
PsiWhiteSpace('\n')
PsiElement(Py:ASYNC_KEYWORD)('async')
PsiWhiteSpace(' ')
PsiElement(Py:DEF_KEYWORD)('def')
PsiWhiteSpace(' ')
PsiElement(Py:IDENTIFIER)('bar')
PyParameterList
PsiElement(Py:LPAR)('(')
PsiElement(Py:RPAR)(')')
PsiElement(Py:COLON)(':')
PsiWhiteSpace('\n ')
PyStatementList
PyExpressionStatement
PyPrefixExpression
PsiElement(Py:AWAIT_KEYWORD)('await')
PsiWhiteSpace(' ')
PyReferenceExpression: x
PsiElement(Py:IDENTIFIER)('x')
PsiWhiteSpace('\n ')
PyReturnStatement
PsiElement(Py:RETURN_KEYWORD)('return')
PsiWhiteSpace(' ')
PyNumericLiteralExpression
PsiElement(Py:INTEGER_LITERAL)('0')
PsiWhiteSpace('\n\n\n')
PyFunction('quux')
PyDecoratorList
PyDecorator: @baz
PsiElement(Py:AT)('@')
PyReferenceExpression: baz
PsiElement(Py:IDENTIFIER)('baz')
PyArgumentList
PsiElement(Py:LPAR)('(')
PyReferenceExpression: x
PsiElement(Py:IDENTIFIER)('x')
PsiElement(Py:COMMA)(',')
PsiWhiteSpace(' ')
PyReferenceExpression: y
PsiElement(Py:IDENTIFIER)('y')
PsiElement(Py:RPAR)(')')
PsiWhiteSpace('\n')
PsiElement(Py:ASYNC_KEYWORD)('async')
PsiWhiteSpace(' ')
PsiElement(Py:DEF_KEYWORD)('def')
PsiWhiteSpace(' ')
PsiElement(Py:IDENTIFIER)('quux')
PyParameterList
PsiElement(Py:LPAR)('(')
PsiElement(Py:RPAR)(')')
PsiElement(Py:COLON)(':')
PsiWhiteSpace('\n ')
PyStatementList
PyReturnStatement
PsiElement(Py:RETURN_KEYWORD)('return')
PsiWhiteSpace(' ')
PyPrefixExpression
PsiElement(Py:AWAIT_KEYWORD)('await')
PsiWhiteSpace(' ')
PyReferenceExpression: x
PsiElement(Py:IDENTIFIER)('x')
@@ -0,0 +1,8 @@
async def foo(x):
y = await bar(x)
return await y
async def bar(x_new):
y = await x_new
return y
@@ -0,0 +1,3 @@
async def foo(x):
<selection>y = await x</selection>
return await y
@@ -0,0 +1,7 @@
async def foo(x):
y = await bar(x)
return y
async def bar(x_new):
return await x_new + 1
@@ -0,0 +1,3 @@
async def foo(x):
y = <selection>await x + 1</selection>
return y
@@ -157,6 +157,24 @@ public class Py3CompletionTest extends PyTestCase {
doTest();
}
public void testAsync() {
runWithLanguageLevel(LanguageLevel.PYTHON35, new Runnable() {
@Override
public void run() {
doTest();
}
});
}
public void testAwait() {
runWithLanguageLevel(LanguageLevel.PYTHON35, new Runnable() {
@Override
public void run() {
doTest();
}
});
}
@Override
protected String getTestDataPath() {
return super.getTestDataPath() + "/completion";
@@ -0,0 +1,128 @@
/*
* Copyright 2000-2015 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.jetbrains.python;
import com.intellij.testFramework.LightProjectDescriptor;
import com.jetbrains.python.documentation.PythonDocumentationProvider;
import com.jetbrains.python.fixtures.PyTestCase;
import com.jetbrains.python.psi.LanguageLevel;
import com.jetbrains.python.psi.PyExpression;
import com.jetbrains.python.psi.types.PyType;
import com.jetbrains.python.psi.types.TypeEvalContext;
/**
* @author vlan
*/
public class Py3TypeTest extends PyTestCase {
public static final String TEST_DIRECTORY = "/types/";
@Override
protected LightProjectDescriptor getProjectDescriptor() {
return ourPy3Descriptor;
}
// PY-6702
public void testYieldFromType() {
runWithLanguageLevel(LanguageLevel.PYTHON33, new Runnable() {
@Override
public void run() {
doTest("Union[str, int, float]",
"def subgen():\n" +
" for i in [1, 2, 3]:\n" +
" yield i\n" +
"\n" +
"def gen():\n" +
" yield 'foo'\n" +
" yield from subgen()\n" +
" yield 3.14\n" +
"\n" +
"for expr in gen():\n" +
" pass\n");
}
});
}
public void testAwaitAwaitable() {
runWithLanguageLevel(LanguageLevel.PYTHON35, new Runnable() {
@Override
public void run() {
doTest("int",
"class C:\n" +
" def __await__(self):\n" +
" yield 'foo'\n" +
" return 0\n" +
"\n" +
"async def foo():\n" +
" c = C()\n" +
" expr = await c\n");
}
});
}
public void testAsyncDefReturnType() {
runWithLanguageLevel(LanguageLevel.PYTHON35, new Runnable() {
@Override
public void run() {
doTest("__coroutine[int]",
"async def foo(x):\n" +
" await x\n" +
" return 0\n" +
"\n" +
"def bar(y):\n" +
" expr = foo(y)\n");
}
});
}
public void testAwaitCoroutine() {
runWithLanguageLevel(LanguageLevel.PYTHON35, new Runnable() {
@Override
public void run() {
doTest("int",
"async def foo(x):\n" +
" await x\n" +
" return 0\n" +
"\n" +
"async def bar(y):\n" +
" expr = await foo(y)\n");
}
});
}
// Not in PEP 484 as for now, see https://github.com/ambv/typehinting/issues/119
public void testCoroutineReturnTypeAnnotation() {
runWithLanguageLevel(LanguageLevel.PYTHON35, new Runnable() {
@Override
public void run() {
doTest("int",
"async def foo() -> int: ...\n" +
"\n" +
"async def bar():\n" +
" expr = await foo()\n");
}
});
}
private void doTest(final String expectedType, final String text) {
myFixture.configureByText(PythonFileType.INSTANCE, text);
final PyExpression expr = myFixture.findElementByText("expr", PyExpression.class);
final TypeEvalContext context = TypeEvalContext.userInitiated(expr.getProject(), expr.getContainingFile()).withTracing();
final PyType actual = context.getType(expr);
final String actualType = PythonDocumentationProvider.getTypeName(actual, context);
assertEquals(expectedType, actualType);
}
}
@@ -50,8 +50,9 @@ public class PyTypeParserTest extends PyTestCase {
public void testListType() {
myFixture.configureByFile("typeParser/typeParser.py");
final PyCollectionType type = (PyCollectionType) PyTypeParser.getTypeByName(myFixture.getFile(), "list of MyObject");
assertNotNull(type);
assertClassType(type, "list");
assertClassType(type.getElementType(getTypeEvalContext()), "MyObject");
assertClassType(type.getElementTypes(getTypeEvalContext()).get(0), "MyObject");
}
public void testDictType() {
@@ -59,12 +60,9 @@ public class PyTypeParserTest extends PyTestCase {
final PyCollectionType type = (PyCollectionType) PyTypeParser.getTypeByName(myFixture.getFile(), "dict from str to MyObject");
assertNotNull(type);
assertClassType(type, "dict");
final PyType elementType = type.getElementType(getTypeEvalContext());
assertInstanceOf(elementType, PyTupleType.class);
final PyTupleType tupleType = (PyTupleType)elementType;
assertEquals(2, tupleType.getElementCount());
assertClassType(tupleType.getElementType(0), "str");
assertClassType(tupleType.getElementType(1), "MyObject");
final List<PyType> elementTypes = type.getElementTypes(getTypeEvalContext());
assertClassType(elementTypes.get(0), "str");
assertClassType(elementTypes.get(1), "MyObject");
}
private TypeEvalContext getTypeEvalContext() {
@@ -182,8 +180,8 @@ public class PyTypeParserTest extends PyTestCase {
final PyCollectionType collectionType = (PyCollectionType)type;
assertNotNull(collectionType);
assertEquals("list", collectionType.getName());
final PyType elementType = collectionType.getElementType(TypeEvalContext.codeInsightFallback(null));
assertInstanceOf(elementType, PyUnionType.class);
final List<PyType> elementTypes = collectionType.getElementTypes(TypeEvalContext.codeInsightFallback(null));
assertInstanceOf(elementTypes.get(0), PyUnionType.class);
}
public void testBoundedGeneric() {
@@ -203,9 +201,8 @@ public class PyTypeParserTest extends PyTestCase {
final PyCollectionType collectionType = (PyCollectionType)type;
assertNotNull(collectionType);
assertEquals("list", collectionType.getName());
final PyType elementType = collectionType.getElementType(TypeEvalContext.codeInsightFallback(null));
assertNotNull(elementType);
assertEquals("int", elementType.getName());
final List<PyType> elementTypes = collectionType.getElementTypes(TypeEvalContext.codeInsightFallback(null));
assertEquals("int", elementTypes.get(0).getName());
}
public void testBracketMultipleParams() {
@@ -215,14 +212,12 @@ public class PyTypeParserTest extends PyTestCase {
final PyCollectionType collectionType = (PyCollectionType)type;
assertNotNull(collectionType);
assertEquals("dict", collectionType.getName());
final PyType elementType = collectionType.getElementType(TypeEvalContext.codeInsightFallback(null));
assertNotNull(elementType);
assertInstanceOf(elementType, PyTupleType.class);
final PyTupleType tupleType = (PyTupleType)elementType;
final PyType first = tupleType.getElementType(0);
final List<PyType> elementTypes = collectionType.getElementTypes(TypeEvalContext.codeInsightFallback(null));
assertEquals(2, elementTypes.size());
final PyType first = elementTypes.get(0);
assertNotNull(first);
assertEquals("str", first.getName());
final PyType second = tupleType.getElementType(1);
final PyType second = elementTypes.get(1);
assertNotNull(second);
assertEquals("int", second.getName());
}
@@ -23,6 +23,8 @@ import com.jetbrains.python.psi.impl.PythonLanguageLevelPusher;
import com.jetbrains.python.psi.types.*;
import org.jetbrains.annotations.NotNull;
import java.util.List;
/**
* @author yole
*/
@@ -357,28 +359,6 @@ public class PyTypeTest extends PyTestCase {
assertNull(actual);
}
// PY-6702
public void testYieldFromType() {
PythonLanguageLevelPusher.setForcedLanguageLevel(myFixture.getProject(), LanguageLevel.PYTHON33);
try {
doTest("Union[str, int, float]",
"def subgen():\n" +
" for i in [1, 2, 3]:\n" +
" yield i\n" +
"\n" +
"def gen():\n" +
" yield 'foo'\n" +
" yield from subgen()\n" +
" yield 3.14\n" +
"\n" +
"for expr in gen():\n" +
" pass\n");
}
finally {
PythonLanguageLevelPusher.setForcedLanguageLevel(myFixture.getProject(), null);
}
}
public void testFunctionAssignment() {
doTest("int",
"def f():\n" +
@@ -475,6 +455,15 @@ public class PyTypeTest extends PyTestCase {
"expr = f().next()\n");
}
public void testGeneratorFunctionType() {
doTest("__generator[str, Any, int]",
"def f():\n" +
" yield 'foo'\n" +
" return 0\n" +
"\n" +
"expr = f()\n");
}
// PY-7020
public void testListComprehensionType() {
final PyExpression expr = parseExpr("expr = [str(x) for x in range(10)]\n");
@@ -482,11 +471,10 @@ public class PyTypeTest extends PyTestCase {
final PyType type = context.getType(expr);
assertNotNull(type);
assertInstanceOf(type, PyCollectionType.class);
assertEquals(type.getName(), "list");
assertEquals("list", type.getName());
final PyCollectionType collectionType = (PyCollectionType)type;
final PyType elementType = collectionType.getElementType(context);
assertNotNull(elementType);
assertEquals(elementType.getName(), "str");
final List<PyType> elementTypes = collectionType.getElementTypes(context);
assertEquals("str", elementTypes.get(0).getName());
}
// PY-7021
@@ -496,11 +484,20 @@ public class PyTypeTest extends PyTestCase {
final PyType type = context.getType(expr);
assertNotNull(type);
assertInstanceOf(type, PyCollectionType.class);
assertEquals(type.getName(), "__generator");
assertEquals("__generator", type.getName());
final PyCollectionType collectionType = (PyCollectionType)type;
final PyType elementType = collectionType.getElementType(context);
assertNotNull(elementType);
assertEquals(elementType.getName(), "str");
final List<PyType> elementTypes = collectionType.getElementTypes(context);
assertEquals("str", elementTypes.get(0).getName());
assertTrue(PyTypeChecker.isUnknown(elementTypes.get(1)));
assertEquals("None", elementTypes.get(2).getName());
}
// PY-7021
public void testIterOverGeneratorComprehension() {
doTest("str",
"xs = (str(x) for x in range(10))\n" +
"for expr in xs:\n" +
" pass\n");
}
// EA-40207
@@ -229,6 +229,18 @@ public class PythonHighlightingTest extends PyTestCase {
doTest();
}
public void testAsync() {
doTest(LanguageLevel.PYTHON35, true, true);
}
public void testAwait() {
doTest(LanguageLevel.PYTHON35, true, true);
}
public void testYieldInsideAsyncDef() {
doTest(LanguageLevel.PYTHON35, false, false);
}
// ---
private void doTest(final LanguageLevel languageLevel, final boolean checkWarnings, final boolean checkInfos) {
PythonLanguageLevelPusher.setForcedLanguageLevel(myFixture.getProject(), languageLevel);
@@ -159,10 +159,6 @@ public class PythonInspectionsTest extends PyTestCase {
}
}
public void testPyStatementEffectInspection() {
doHighlightingTest(PyStatementEffectInspection.class, LanguageLevel.PYTHON26);
}
public void testPySimplifyBooleanCheckInspection() {
doHighlightingTest(PySimplifyBooleanCheckInspection.class, LanguageLevel.PYTHON26);
}
@@ -486,6 +486,26 @@ public class PythonParsingTest extends ParsingTestCase {
doTest();
}
public void testAsyncDef() {
doTest(LanguageLevel.PYTHON35);
}
public void testAsyncWith() {
doTest(LanguageLevel.PYTHON35);
}
public void testAsyncFor() {
doTest(LanguageLevel.PYTHON35);
}
public void testAwait() {
doTest(LanguageLevel.PYTHON35);
}
public void testDecoratedAsyncDef() {
doTest(LanguageLevel.PYTHON35);
}
public void doTest(LanguageLevel languageLevel) {
LanguageLevel prev = myLanguageLevel;
myLanguageLevel = languageLevel;
@@ -155,6 +155,10 @@ public class PyCompatibilityInspectionTest extends PyTestCase {
doTest(LanguageLevel.PYTHON35);
}
public void testAsyncAwait() {
doTest(LanguageLevel.PYTHON35);
}
private void doTest(@NotNull LanguageLevel level) {
runWithLanguageLevel(level, new Runnable() {
@Override
@@ -0,0 +1,48 @@
/*
* Copyright 2000-2015 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.jetbrains.python.inspections;
import com.jetbrains.python.fixtures.PyInspectionTestCase;
import com.jetbrains.python.psi.LanguageLevel;
import org.jetbrains.annotations.NotNull;
/**
* @author vlan
*/
public class PyStatementEffectInspectionTest extends PyInspectionTestCase {
public void testBasic() {
doTest();
}
public void testAwait() {
doTest(LanguageLevel.PYTHON35);
}
private void doTest(@NotNull LanguageLevel level) {
runWithLanguageLevel(level, new Runnable() {
@Override
public void run() {
doTest();
}
});
}
@NotNull
@Override
protected Class<? extends PyInspection> getInspectionClass() {
return PyStatementEffectInspection.class;
}
}
@@ -278,4 +278,12 @@ public class PyExtractMethodTest extends LightMarkedTestCase {
public void testProhibitedAtClassLevel() {
doFail("foo", "Cannot perform refactoring at class level");
}
public void testAsyncDef() {
doTest("bar", LanguageLevel.PYTHON35);
}
public void testAwaitExpression() {
doTest("bar", LanguageLevel.PYTHON35);
}
}