mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
PY-84538 Report protocol instantiation as an error
(cherry picked from commit ad941a5d9a5fb852b0c28cbf259fdcc5ce3f3efb) IJ-MR-173617 GitOrigin-RevId: 97c2b967b5a2c42d0f1c6c5f09a65beb64bd634c
This commit is contained in:
committed by
intellij-monorepo-bot
parent
db45df3fd6
commit
96a0d7da31
@@ -1100,6 +1100,7 @@ INSP.protocol.all.bases.protocol.must.be.protocols=All bases of a protocol must
|
||||
INSP.protocol.only.runtime.checkable.protocols.can.be.used.with.instance.class.checks=Only @runtime_checkable protocols can be used with instance and class checks
|
||||
INSP.protocol.newtype.cannot.be.used.with.protocol.classes=NewType cannot be used with protocol classes
|
||||
INSP.protocol.element.type.incompatible.with.protocol=Type of ''{0}'' is incompatible with ''{1}''
|
||||
INSP.protocol.cannot.instantiate.protocol.class=Cannot instantiate protocol class ''{0}''
|
||||
|
||||
# PyShadowingBuiltinsInspection
|
||||
INSP.NAME.shadowing.builtins=Shadowing built-in names
|
||||
|
||||
+27
-11
@@ -18,10 +18,12 @@ import com.jetbrains.python.psi.types.*
|
||||
|
||||
class PyProtocolInspection : PyInspection() {
|
||||
|
||||
override fun buildVisitor(holder: ProblemsHolder,
|
||||
isOnTheFly: Boolean,
|
||||
session: LocalInspectionToolSession): PsiElementVisitor = Visitor(
|
||||
holder,PyInspectionVisitor.getContext(session))
|
||||
override fun buildVisitor(
|
||||
holder: ProblemsHolder,
|
||||
isOnTheFly: Boolean,
|
||||
session: LocalInspectionToolSession,
|
||||
): PsiElementVisitor = Visitor(
|
||||
holder, PyInspectionVisitor.getContext(session))
|
||||
|
||||
private class Visitor(holder: ProblemsHolder, context: TypeEvalContext) : PyInspectionVisitor(holder, context) {
|
||||
|
||||
@@ -40,6 +42,7 @@ class PyProtocolInspection : PyInspection() {
|
||||
|
||||
checkRuntimeProtocolInIsInstance(node)
|
||||
checkNewTypeWithProtocols(node)
|
||||
checkProtocolInstantiation(node)
|
||||
}
|
||||
|
||||
private fun checkCompatibility(type: PyClassType, superClassTypes: List<PyClassLikeType?>) {
|
||||
@@ -110,21 +113,21 @@ class PyProtocolInspection : PyInspection() {
|
||||
val resolved = callee.followAssignmentsChain(resolveContext).element ?: return
|
||||
val isNewTypeCall = resolved is PyQualifiedNameOwner && resolved.qualifiedName == PyTypingTypeProvider.NEW_TYPE
|
||||
if (isNewTypeCall) {
|
||||
val base = node.arguments.getOrNull(1)
|
||||
if (base != null) {
|
||||
val type = myTypeEvalContext.getType(base)
|
||||
if (type is PyClassLikeType && isProtocol(type, myTypeEvalContext)) {
|
||||
registerProblem(base, PyPsiBundle.message("INSP.protocol.newtype.cannot.be.used.with.protocol.classes"))
|
||||
}
|
||||
val base = node.arguments.getOrNull(1)
|
||||
if (base != null) {
|
||||
val type = myTypeEvalContext.getType(base)
|
||||
if (type is PyClassLikeType && isProtocol(type, myTypeEvalContext)) {
|
||||
registerProblem(base, PyPsiBundle.message("INSP.protocol.newtype.cannot.be.used.with.protocol.classes"))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun checkMemberCompatibility(
|
||||
protocolElement: PyTypedElement,
|
||||
subclassElements: List<PyTypedResolveResult>,
|
||||
type: PyClassType,
|
||||
protocol: PyClassType
|
||||
protocol: PyClassType,
|
||||
) {
|
||||
val expectedMemberType = myTypeEvalContext.getType(protocolElement)
|
||||
|
||||
@@ -139,5 +142,18 @@ class PyProtocolInspection : PyInspection() {
|
||||
registerProblem(place, PyPsiBundle.message("INSP.protocol.element.type.incompatible.with.protocol", elementName, protocol.name))
|
||||
}
|
||||
}
|
||||
|
||||
private fun checkProtocolInstantiation(node: PyCallExpression) {
|
||||
val calleeReferenceExpression = node.callee
|
||||
if (calleeReferenceExpression is PyReferenceExpression) {
|
||||
val resolveResult = calleeReferenceExpression.followAssignmentsChain(resolveContext)
|
||||
val cls = resolveResult.getElement()
|
||||
if (cls is PyClass) {
|
||||
if (isProtocol(cls, myTypeEvalContext)) {
|
||||
registerProblem(node, PyPsiBundle.message("INSP.protocol.cannot.instantiate.protocol.class", cls.name))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -44,6 +44,69 @@ public class PyProtocolInspectionTest extends PyInspectionTestCase {
|
||||
});
|
||||
}
|
||||
|
||||
// PY-76903
|
||||
public void testProtocolCannotBeInstantiated() {
|
||||
doTestByText("""
|
||||
from typing import Protocol
|
||||
|
||||
class P(Protocol):
|
||||
...
|
||||
|
||||
p = <warning descr="Cannot instantiate protocol class 'P'">P()</warning>
|
||||
""");
|
||||
}
|
||||
|
||||
// PY-76903
|
||||
public void testProtocolSubclassCanBeInstantiated() {
|
||||
doTestByText("""
|
||||
from typing import Protocol
|
||||
|
||||
class P(Protocol):
|
||||
...
|
||||
|
||||
|
||||
class C(P):
|
||||
...
|
||||
|
||||
c = C()
|
||||
""");
|
||||
}
|
||||
|
||||
// PY-76903
|
||||
public void testProtocolFunctionCall() {
|
||||
doTestByText("""
|
||||
from typing import Protocol
|
||||
|
||||
class P(Protocol):
|
||||
def foo(self) -> None: ...
|
||||
|
||||
|
||||
def f(p: P):
|
||||
p.foo()
|
||||
""");
|
||||
}
|
||||
|
||||
// PY-76903
|
||||
public void testProtocolTypeButConcreteValue() {
|
||||
doTestByText("""
|
||||
from typing import Protocol, reveal_type
|
||||
|
||||
class P(Protocol):
|
||||
def foo(self) -> None: ...
|
||||
|
||||
|
||||
class I(P):
|
||||
def foo(self) -> None: ...
|
||||
|
||||
|
||||
def f() -> type[P]:
|
||||
return I
|
||||
|
||||
t = f()
|
||||
t()
|
||||
""");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doTest() {
|
||||
runWithLanguageLevel(LanguageLevel.PYTHON37, () -> super.doTest());
|
||||
|
||||
Reference in New Issue
Block a user