mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
Merge branch 'vlan/async-await'
This commit is contained in:
@@ -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:
|
||||
|
||||
@@ -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."""
|
||||
|
||||
|
||||
@@ -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."""
|
||||
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
@@ -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) {
|
||||
|
||||
+52
-5
@@ -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;
|
||||
|
||||
+39
-11
@@ -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
|
||||
@@ -0,0 +1 @@
|
||||
asy<caret> # Comment
|
||||
@@ -0,0 +1,2 @@
|
||||
async def foo():
|
||||
await # comment
|
||||
@@ -0,0 +1,2 @@
|
||||
async def foo():
|
||||
awa<caret> # comment
|
||||
@@ -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
|
||||
@@ -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')),
|
||||
|
||||
@@ -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
|
||||
@@ -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')
|
||||
@@ -0,0 +1,7 @@
|
||||
async def f():
|
||||
async for x in xs:
|
||||
pass
|
||||
|
||||
|
||||
async for y in ys:
|
||||
pass
|
||||
@@ -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')
|
||||
@@ -0,0 +1,6 @@
|
||||
async def foo():
|
||||
async with x:
|
||||
pass
|
||||
|
||||
async with y:
|
||||
pass
|
||||
@@ -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')
|
||||
@@ -0,0 +1,5 @@
|
||||
async def f(x):
|
||||
await x
|
||||
|
||||
|
||||
await = 1
|
||||
@@ -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')
|
||||
@@ -0,0 +1,9 @@
|
||||
@foo
|
||||
async def bar():
|
||||
await x
|
||||
return 0
|
||||
|
||||
|
||||
@baz(x, y)
|
||||
async def quux():
|
||||
return await x
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user