mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
PY-21655 Fixed: False inspection: @asyncio.coroutine decorated function treated as non-awaitable
Don't mark "await" on generator based coroutines as unresolved.
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2000-2016 JetBrains s.r.o.
|
||||
* Copyright 2000-2017 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.
|
||||
@@ -170,6 +170,7 @@ public class PyNames {
|
||||
public static final String ANEXT = "__anext__";
|
||||
public static final String AENTER = "__aenter__";
|
||||
public static final String AEXIT = "__aexit__";
|
||||
public static final String DUNDER_AWAIT = "__await__";
|
||||
public static final String SIZEOF = "__sizeof__";
|
||||
public static final String INIT_SUBCLASS = "__init_subclass__";
|
||||
public static final String FSPATH = "__fspath__";
|
||||
@@ -426,7 +427,7 @@ public class PyNames {
|
||||
.put("__imatmul__", _self_other_descr)
|
||||
.put("__matmul__", _self_other_descr)
|
||||
.put("__rmatmul__", _self_other_descr)
|
||||
.put("__await__", _only_self_descr)
|
||||
.put(DUNDER_AWAIT, _only_self_descr)
|
||||
.put(AENTER, _only_self_descr)
|
||||
.put(AEXIT, _exit_descr)
|
||||
.put(AITER, _only_self_descr)
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2000-2016 JetBrains s.r.o.
|
||||
* Copyright 2000-2017 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.
|
||||
@@ -89,7 +89,9 @@ public class PyDeprecationInspection extends PyInspection {
|
||||
if (LanguageLevel.forElement(node).isAtLeast(LanguageLevel.PYTHON33) && decoratorList != null) {
|
||||
Arrays
|
||||
.stream(decoratorList.getDecorators())
|
||||
.filter(decorator -> PyKnownDecoratorUtil.asKnownDecorator(decorator, myTypeEvalContext) == KnownDecorator.ABC_ABSTRACTPROPERTY)
|
||||
.filter(
|
||||
decorator -> PyKnownDecoratorUtil.asKnownDecorators(decorator, myTypeEvalContext).contains(KnownDecorator.ABC_ABSTRACTPROPERTY)
|
||||
)
|
||||
.forEach(
|
||||
decorator -> {
|
||||
final QualifiedName abcAbsPropertyQName = KnownDecorator.ABC_ABSTRACTPROPERTY.getQualifiedName();
|
||||
|
||||
+24
-1
@@ -47,6 +47,7 @@ import com.jetbrains.python.codeInsight.imports.AutoImportHintAction;
|
||||
import com.jetbrains.python.codeInsight.imports.AutoImportQuickFix;
|
||||
import com.jetbrains.python.codeInsight.imports.OptimizeImportsQuickFix;
|
||||
import com.jetbrains.python.codeInsight.imports.PythonImportUtils;
|
||||
import com.jetbrains.python.codeInsight.typing.PyTypingTypeProvider;
|
||||
import com.jetbrains.python.console.PydevConsoleRunner;
|
||||
import com.jetbrains.python.documentation.docstrings.DocStringParameterReference;
|
||||
import com.jetbrains.python.documentation.docstrings.DocStringTypeReference;
|
||||
@@ -68,6 +69,7 @@ import com.jetbrains.python.psi.resolve.QualifiedNameFinder;
|
||||
import com.jetbrains.python.psi.types.*;
|
||||
import com.jetbrains.python.sdk.PythonSdkType;
|
||||
import com.jetbrains.python.sdk.skeletons.PySkeletonRefresher;
|
||||
import one.util.streamex.StreamEx;
|
||||
import org.jetbrains.annotations.Nls;
|
||||
import org.jetbrains.annotations.NonNls;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
@@ -789,7 +791,7 @@ public class PyUnresolvedReferencesInspection extends PyInspection {
|
||||
}
|
||||
}
|
||||
}
|
||||
if (type instanceof PyClassTypeImpl) {
|
||||
if (type instanceof PyClassType) {
|
||||
PyClass cls = ((PyClassType)type).getPyClass();
|
||||
if (PyTypeChecker.overridesGetAttr(cls, myTypeEvalContext)) {
|
||||
return true;
|
||||
@@ -804,6 +806,8 @@ public class PyUnresolvedReferencesInspection extends PyInspection {
|
||||
return true;
|
||||
}
|
||||
if (hasUnresolvedDynamicMember((PyClassType)type, reference, name, myTypeEvalContext)) return true;
|
||||
|
||||
if (isAwaitOnGeneratorBasedCoroutine(name, reference, cls)) return true;
|
||||
}
|
||||
if (type instanceof PyFunctionTypeImpl) {
|
||||
final PyCallable callable = ((PyFunctionTypeImpl)type).getCallable();
|
||||
@@ -854,6 +858,25 @@ public class PyUnresolvedReferencesInspection extends PyInspection {
|
||||
return false;
|
||||
}
|
||||
|
||||
private boolean isAwaitOnGeneratorBasedCoroutine(@NotNull String name, @NotNull PsiReference reference, @NotNull PyClass cls) {
|
||||
if (PyNames.DUNDER_AWAIT.equals(name) &&
|
||||
reference instanceof PyOperatorReference &&
|
||||
PyTypingTypeProvider.GENERATOR.equals(cls.getQualifiedName())) {
|
||||
final PyExpression receiver = ((PyOperatorReference)reference).getReceiver();
|
||||
|
||||
if (receiver instanceof PyCallExpression) {
|
||||
final boolean resolvedToGeneratorBasedCoroutine = StreamEx
|
||||
.of(((PyCallExpression)receiver).multiResolveCalleeFunction(getResolveContext()))
|
||||
.select(PyFunction.class)
|
||||
.anyMatch(function -> PyKnownDecoratorUtil.hasGeneratorBasedCoroutineDecorator(function, myTypeEvalContext));
|
||||
|
||||
if (resolvedToGeneratorBasedCoroutine) return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private void addCreateMemberFromUsageFixes(PyType type, PsiReference reference, String refText, List<LocalQuickFix> actions) {
|
||||
PsiElement element = reference.getElement();
|
||||
if (type instanceof PyClassTypeImpl) {
|
||||
|
||||
@@ -15,11 +15,10 @@
|
||||
*/
|
||||
package com.jetbrains.python.psi;
|
||||
|
||||
import com.google.common.collect.Iterators;
|
||||
import com.intellij.psi.PsiElement;
|
||||
import com.intellij.psi.PsiReference;
|
||||
import com.intellij.psi.util.QualifiedName;
|
||||
import com.intellij.util.containers.Convertor;
|
||||
import com.intellij.util.containers.ContainerUtil;
|
||||
import com.jetbrains.python.PyNames;
|
||||
import com.jetbrains.python.psi.types.TypeEvalContext;
|
||||
import one.util.streamex.StreamEx;
|
||||
@@ -28,7 +27,6 @@ import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
import static com.intellij.util.containers.ContainerUtil.newMapFromValues;
|
||||
import static com.jetbrains.python.psi.PyKnownDecoratorUtil.KnownDecorator.*;
|
||||
import static com.jetbrains.python.psi.PyUtil.as;
|
||||
|
||||
@@ -66,7 +64,9 @@ public class PyKnownDecoratorUtil {
|
||||
//ATEXIT_REGISTER("atexit.register", true),
|
||||
//ATEXIT_UNREGISTER("atexit.unregister", false),
|
||||
|
||||
ASYNCIO_COROUTINE("asyncio.tasks.coroutine"),
|
||||
ASYNCIO_TASKS_COROUTINE("asyncio.tasks.coroutine"),
|
||||
ASYNCIO_COROUTINES_COROUTINE("asyncio.coroutines.coroutine"),
|
||||
TYPES_COROUTINE("types.coroutine"),
|
||||
|
||||
UNITTEST_SKIP("unittest.case.skip"),
|
||||
UNITTEST_SKIP_IF("unittest.case.skipIf"),
|
||||
@@ -112,13 +112,11 @@ public class PyKnownDecoratorUtil {
|
||||
DJANGO_UTILS_FUNCTIONAL_CACHED_PROPERTY,
|
||||
KOMBU_UTILS_CACHED_PROPERTY);
|
||||
|
||||
private static final Map<String, KnownDecorator> ourByShortName = newMapFromValues(Iterators.forArray(values()),
|
||||
new Convertor<KnownDecorator, String>() {
|
||||
@Override
|
||||
public String convert(KnownDecorator o) {
|
||||
return o.getShortName();
|
||||
}
|
||||
});
|
||||
private static final Set<KnownDecorator> GENERATOR_BASED_COROUTINE_DECORATORS = EnumSet.of(ASYNCIO_TASKS_COROUTINE,
|
||||
ASYNCIO_COROUTINES_COROUTINE,
|
||||
TYPES_COROUTINE);
|
||||
|
||||
private static final Map<String, List<KnownDecorator>> BY_SHORT_NAME = StreamEx.of(values()).groupingBy(KnownDecorator::getShortName);
|
||||
|
||||
/**
|
||||
* Map decorators of element to {@link PyKnownDecoratorUtil.KnownDecorator}.
|
||||
@@ -137,16 +135,16 @@ public class PyKnownDecoratorUtil {
|
||||
|
||||
return StreamEx
|
||||
.of(decoratorList.getDecorators())
|
||||
.map(decorator -> asKnownDecorator(decorator, context))
|
||||
.flatMap(decorator -> asKnownDecorators(decorator, context).stream())
|
||||
.nonNull()
|
||||
.toList();
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static KnownDecorator asKnownDecorator(@NotNull PyDecorator decorator, @NotNull TypeEvalContext context) {
|
||||
@NotNull
|
||||
public static List<KnownDecorator> asKnownDecorators(@NotNull PyDecorator decorator, @NotNull TypeEvalContext context) {
|
||||
final QualifiedName qualifiedName = decorator.getQualifiedName();
|
||||
if (qualifiedName == null) {
|
||||
return null;
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
if (context.maySwitchToAST(decorator)) {
|
||||
@@ -157,18 +155,16 @@ public class PyKnownDecoratorUtil {
|
||||
|
||||
if (resolved != null && resolved.getQualifiedName() != null) {
|
||||
final QualifiedName resolvedName = QualifiedName.fromDottedString(resolved.getQualifiedName());
|
||||
final KnownDecorator knownDecorator = ourByShortName.get(resolvedName.getLastComponent());
|
||||
final List<KnownDecorator> knownDecorators = BY_SHORT_NAME.getOrDefault(resolvedName.getLastComponent(), Collections.emptyList());
|
||||
|
||||
if (knownDecorator != null && resolvedName.equals(knownDecorator.getQualifiedName())) {
|
||||
return knownDecorator;
|
||||
}
|
||||
return ContainerUtil.filter(knownDecorators, knownDecorator -> resolvedName.equals(knownDecorator.getQualifiedName()));
|
||||
}
|
||||
}
|
||||
else {
|
||||
return ourByShortName.get(qualifiedName.getLastComponent());
|
||||
return BY_SHORT_NAME.getOrDefault(qualifiedName.getLastComponent(), Collections.emptyList());
|
||||
}
|
||||
|
||||
return null;
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@@ -231,7 +227,11 @@ public class PyKnownDecoratorUtil {
|
||||
}
|
||||
|
||||
public static boolean isPropertyDecorator(@NotNull PyDecorator decorator, @NotNull TypeEvalContext context) {
|
||||
return PROPERTY_DECORATORS.contains(asKnownDecorator(decorator, context));
|
||||
return ContainerUtil.exists(asKnownDecorators(decorator, context), PROPERTY_DECORATORS::contains);
|
||||
}
|
||||
|
||||
public static boolean hasGeneratorBasedCoroutineDecorator(@NotNull PyFunction function, @NotNull TypeEvalContext context) {
|
||||
return ContainerUtil.exists(getKnownDecorators(function, context), GENERATOR_BASED_COROUTINE_DECORATORS::contains);
|
||||
}
|
||||
|
||||
private static boolean allDecoratorsAreKnown(@NotNull PyDecoratable element, @NotNull List<KnownDecorator> decorators) {
|
||||
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
import asyncio
|
||||
|
||||
|
||||
@asyncio.coroutine
|
||||
def foo():
|
||||
yield from asyncio.sleep(1)
|
||||
return 3
|
||||
|
||||
async def bar():
|
||||
return await foo() * 2
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
from .coroutines import *
|
||||
|
||||
|
||||
def sleep(i):
|
||||
pass
|
||||
+2
@@ -0,0 +1,2 @@
|
||||
def coroutine(fn):
|
||||
pass
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
import types
|
||||
import asyncio
|
||||
|
||||
|
||||
@types.coroutine
|
||||
def foo():
|
||||
yield from asyncio.sleep(1)
|
||||
return 3
|
||||
|
||||
async def bar():
|
||||
return await foo() * 2
|
||||
+2
@@ -0,0 +1,2 @@
|
||||
def sleep(i):
|
||||
pass
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
def coroutine(fn):
|
||||
pass
|
||||
|
||||
|
||||
def sleep(i):
|
||||
pass
|
||||
@@ -0,0 +1,5 @@
|
||||
from .coroutines import *
|
||||
|
||||
|
||||
def sleep(i):
|
||||
pass
|
||||
+2
@@ -0,0 +1,2 @@
|
||||
def coroutine(fn):
|
||||
pass
|
||||
@@ -0,0 +1,2 @@
|
||||
def sleep(i):
|
||||
pass
|
||||
@@ -0,0 +1,6 @@
|
||||
def coroutine(fn):
|
||||
pass
|
||||
|
||||
|
||||
def sleep(i):
|
||||
pass
|
||||
@@ -519,6 +519,35 @@ public class Py3TypeTest extends PyTestCase {
|
||||
" print(expr)");
|
||||
}
|
||||
|
||||
// PY-21655
|
||||
public void testUsageOfFunctionDecoratedWithAsyncioCoroutine() {
|
||||
myFixture.copyDirectoryToProject(TEST_DIRECTORY + getTestName(false), "");
|
||||
runWithLanguageLevel(LanguageLevel.PYTHON35, () -> doTest("int",
|
||||
"import asyncio\n" +
|
||||
"@asyncio.coroutine\n" +
|
||||
"def foo():\n" +
|
||||
" yield from asyncio.sleep(1)\n" +
|
||||
" return 3\n" +
|
||||
"async def bar():\n" +
|
||||
" expr = await foo()\n" +
|
||||
" return expr"));
|
||||
}
|
||||
|
||||
// PY-21655
|
||||
public void testUsageOfFunctionDecoratedWithTypesCoroutine() {
|
||||
myFixture.copyDirectoryToProject(TEST_DIRECTORY + getTestName(false), "");
|
||||
runWithLanguageLevel(LanguageLevel.PYTHON35, () -> doTest("int",
|
||||
"import asyncio\n" +
|
||||
"import types\n" +
|
||||
"@types.coroutine\n" +
|
||||
"def foo():\n" +
|
||||
" yield from asyncio.sleep(1)\n" +
|
||||
" return 3\n" +
|
||||
"async def bar():\n" +
|
||||
" expr = await foo()\n" +
|
||||
" return expr"));
|
||||
}
|
||||
|
||||
// PY-22513
|
||||
public void testGenericKwargs() {
|
||||
doTest("Dict[str, Union[int, str]]",
|
||||
|
||||
+10
@@ -206,6 +206,16 @@ public class Py3UnresolvedReferencesInspectionTest extends PyTestCase {
|
||||
doTest();
|
||||
}
|
||||
|
||||
// PY-21655
|
||||
public void testUsageOfFunctionDecoratedWithAsyncioCoroutine() {
|
||||
doMultiFileTest("a.py");
|
||||
}
|
||||
|
||||
// PY-21655
|
||||
public void testUsageOfFunctionDecoratedWithTypesCoroutine() {
|
||||
doMultiFileTest("a.py");
|
||||
}
|
||||
|
||||
// PY-22899, PY-22937
|
||||
public void testCallTypeGetAttributeAndSetAttrInInheritor() {
|
||||
doTest();
|
||||
|
||||
Reference in New Issue
Block a user