mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
Highlight plain Protocol in instance and class checks (PY-28249)
Improve processing `runtime` and `Protocol` from typing-extensions. Improve processing decorators when one of them could be resolved to some of known decorators.
This commit is contained in:
@@ -164,7 +164,7 @@ public class PyTypingTypeProvider extends PyTypeProviderBase {
|
||||
}
|
||||
// Check for the exact name in advance for performance reasons
|
||||
if ("Protocol".equals(referenceExpression.getName())) {
|
||||
if (resolveToQualifiedNames(referenceExpression, context).contains(PROTOCOL)) {
|
||||
if (ContainerUtil.exists(resolveToQualifiedNames(referenceExpression, context), n -> PROTOCOL.equals(n) || PROTOCOL_EXT.equals(n))) {
|
||||
return createTypingProtocolType(referenceExpression);
|
||||
}
|
||||
}
|
||||
@@ -465,16 +465,18 @@ public class PyTypingTypeProvider extends PyTypeProviderBase {
|
||||
public Ref<PyType> getReferenceType(@NotNull PsiElement referenceTarget, @NotNull TypeEvalContext context, @Nullable PsiElement anchor) {
|
||||
if (referenceTarget instanceof PyTargetExpression) {
|
||||
final PyTargetExpression target = (PyTargetExpression)referenceTarget;
|
||||
final String targetQName = target.getQualifiedName();
|
||||
|
||||
// Depends on typing.Generic defined as a target expression
|
||||
if (GENERIC.equals(target.getQualifiedName())) {
|
||||
if (GENERIC.equals(targetQName)) {
|
||||
return Ref.create(createTypingGenericType(target));
|
||||
}
|
||||
// Depends on typing.Protocol defined as a target expression
|
||||
if (PROTOCOL.equals(target.getQualifiedName())) {
|
||||
if (PROTOCOL.equals(targetQName) || PROTOCOL_EXT.equals(targetQName)) {
|
||||
return Ref.create(createTypingProtocolType(target));
|
||||
}
|
||||
// Depends on typing.Callable defined as a target expression
|
||||
if (CALLABLE.equals(target.getQualifiedName())) {
|
||||
if (CALLABLE.equals(targetQName)) {
|
||||
return Ref.create(createTypingCallableType(referenceTarget));
|
||||
}
|
||||
|
||||
|
||||
@@ -11,12 +11,11 @@ import com.jetbrains.python.PyNames
|
||||
import com.jetbrains.python.codeInsight.typing.PyTypingTypeProvider
|
||||
import com.jetbrains.python.codeInsight.typing.inspectProtocolSubclass
|
||||
import com.jetbrains.python.codeInsight.typing.isProtocol
|
||||
import com.jetbrains.python.psi.PyCallExpression
|
||||
import com.jetbrains.python.psi.PyClass
|
||||
import com.jetbrains.python.psi.PyKnownDecoratorUtil
|
||||
import com.jetbrains.python.psi.*
|
||||
import com.jetbrains.python.psi.PyKnownDecoratorUtil.KnownDecorator.TYPING_RUNTIME
|
||||
import com.jetbrains.python.psi.PyTypedElement
|
||||
import com.jetbrains.python.psi.PyKnownDecoratorUtil.KnownDecorator.TYPING_RUNTIME_EXT
|
||||
import com.jetbrains.python.psi.resolve.PyResolveContext
|
||||
import com.jetbrains.python.psi.resolve.PyResolveUtil
|
||||
import com.jetbrains.python.psi.resolve.RatedResolveResult
|
||||
import com.jetbrains.python.psi.types.PyClassLikeType
|
||||
import com.jetbrains.python.psi.types.PyClassType
|
||||
@@ -85,15 +84,22 @@ class PyProtocolInspection : PyInspection() {
|
||||
|
||||
private fun checkRuntimeProtocolInIsInstance(node: PyCallExpression) {
|
||||
if (node.isCalleeText(PyNames.ISINSTANCE, PyNames.ISSUBCLASS)) {
|
||||
val base = node.arguments.getOrNull(1)
|
||||
if (base != null) {
|
||||
val type = myTypeEvalContext.getType(base)
|
||||
if (type is PyClassType &&
|
||||
isProtocol(type, myTypeEvalContext) &&
|
||||
!PyKnownDecoratorUtil.getKnownDecorators(type.pyClass, myTypeEvalContext).contains(TYPING_RUNTIME)) {
|
||||
val base = node.arguments.getOrNull(1) ?: return
|
||||
|
||||
if (base is PyReferenceExpression) {
|
||||
val qNames = PyResolveUtil.resolveImportedElementQNameLocally(base).asSequence().map { it.toString() }
|
||||
if (qNames.any { it == PyTypingTypeProvider.PROTOCOL || it == PyTypingTypeProvider.PROTOCOL_EXT }) {
|
||||
registerProblem(base, "Only @runtime protocols can be used with instance and class checks", GENERIC_ERROR)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
val type = myTypeEvalContext.getType(base)
|
||||
if (type is PyClassType &&
|
||||
isProtocol(type, myTypeEvalContext) &&
|
||||
!PyKnownDecoratorUtil.getKnownDecorators(type.pyClass, myTypeEvalContext).any { it == TYPING_RUNTIME || it == TYPING_RUNTIME_EXT}) {
|
||||
registerProblem(base, "Only @runtime protocols can be used with instance and class checks", GENERIC_ERROR)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -62,6 +62,7 @@ public class PyKnownDecoratorUtil {
|
||||
|
||||
TYPING_OVERLOAD("typing." + PyNames.OVERLOAD),
|
||||
TYPING_RUNTIME("typing.runtime"),
|
||||
TYPING_RUNTIME_EXT("typing_extensions.runtime"),
|
||||
|
||||
REPRLIB_RECURSIVE_REPR("reprlib.recursive_repr"),
|
||||
|
||||
@@ -241,6 +242,8 @@ public class PyKnownDecoratorUtil {
|
||||
|
||||
private static boolean allDecoratorsAreKnown(@NotNull PyDecoratable element, @NotNull List<KnownDecorator> decorators) {
|
||||
final PyDecoratorList decoratorList = element.getDecoratorList();
|
||||
return decoratorList == null ? decorators.isEmpty() : decoratorList.getDecorators().length == decorators.size();
|
||||
return decoratorList == null
|
||||
? decorators.isEmpty()
|
||||
: decoratorList.getDecorators().length == StreamEx.of(decorators).groupingBy(KnownDecorator::getShortName).size();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,3 +22,7 @@ assert issubclass(ClosableImpl, Closable1)
|
||||
|
||||
assert isinstance(ClosableImpl(), <error descr="Only @runtime protocols can be used with instance and class checks">Closable2</error>)
|
||||
assert issubclass(ClosableImpl, <error descr="Only @runtime protocols can be used with instance and class checks">Closable2</error>)
|
||||
|
||||
assert isinstance(ClosableImpl(), <error descr="Only @runtime protocols can be used with instance and class checks">Protocol</error>)
|
||||
B = Protocol
|
||||
assert issubclass(ClosableImpl, <error descr="Only @runtime protocols can be used with instance and class checks">B</error>)
|
||||
|
||||
Reference in New Issue
Block a user