From 2f533480a1c71fa8faa4e0e809164f8e90543ecf Mon Sep 17 00:00:00 2001 From: Petr Date: Tue, 20 May 2025 18:55:27 +0200 Subject: [PATCH] PY-78822 `Type argument for "Protocol" must be a type parameter` is not reported by PyCharm GitOrigin-RevId: 8e02ca094674c75bed86e1feffeac5909828f38c --- .../resources/messages/PyPsiBundle.properties | 2 +- .../python/inspections/PyTypeHintsInspection.kt | 13 +++++++++---- python/testData/typing/ignored.txt | 1 - .../inspections/PyTypeHintsInspectionTest.java | 5 ++++- 4 files changed, 14 insertions(+), 7 deletions(-) diff --git a/python/python-psi-impl/resources/messages/PyPsiBundle.properties b/python/python-psi-impl/resources/messages/PyPsiBundle.properties index 1af93b912fab..fba454fbb87d 100644 --- a/python/python-psi-impl/resources/messages/PyPsiBundle.properties +++ b/python/python-psi-impl/resources/messages/PyPsiBundle.properties @@ -1164,7 +1164,7 @@ INSP.type.hints.cannot.inherit.from.plain.generic=Cannot inherit from plain 'Gen INSP.type.hints.cannot.inherit.from.generic.multiple.times=Cannot inherit from 'Generic[...]' multiple times INSP.type.hints.generic.or.protocol.should.list.all.type.variables=''Generic[...]'' or ''Protocol[...]'' should list all type variables ({0}) INSP.type.hints.illegal.literal.parameter='Literal' may be parameterized with literal ints, byte and unicode strings, bools, Enum values, None, other literal types, or type aliases to other literal types -INSP.type.hints.parameters.to.generic.must.all.be.type.variables=Parameters to 'Generic[...]' must all be type variables +INSP.type.hints.parameters.to.generic.must.all.be.type.variables=Parameters to ''{0,choice,0#Generic|1#Protocol}[...]'' must all be type variables INSP.type.hints.parameters.to.generic.must.all.be.unique=Parameters to 'Generic[...]' must all be unique INSP.type.hints.non.default.type.vars.cannot.follow.defaults=Non-default TypeVars cannot follow ones with defaults INSP.type.hints.default.type.var.cannot.follow.type.var.tuple=TypeVar with a default value cannot follow TypeVarTuple diff --git a/python/python-psi-impl/src/com/jetbrains/python/inspections/PyTypeHintsInspection.kt b/python/python-psi-impl/src/com/jetbrains/python/inspections/PyTypeHintsInspection.kt index 413d92c39687..9ba2e3daa6be 100644 --- a/python/python-psi-impl/src/com/jetbrains/python/inspections/PyTypeHintsInspection.kt +++ b/python/python-psi-impl/src/com/jetbrains/python/inspections/PyTypeHintsInspection.kt @@ -935,7 +935,8 @@ class PyTypeHintsInspection : PyInspection() { qNames.forEach { when (it) { - genericQName -> checkTypingGenericParameters(node) + genericQName -> checkTypingGenericParameters(node, false) + protocolQName, protocolExtQName -> checkTypingGenericParameters(node, true) literalQName, literalExtQName -> checkLiteralParameter(index) annotatedQName, annotatedExtQName -> checkAnnotatedParameter(index) typeAliasQName, typeAliasExtQName -> reportParameterizedTypeAlias(index) @@ -1105,7 +1106,7 @@ class PyTypeHintsInspection : PyInspection() { return typeArgumentTypes } - private fun checkTypingGenericParameters(node: PySubscriptionExpression) { + private fun checkTypingGenericParameters(node: PySubscriptionExpression, isProtocol: Boolean) { val indexExpression = node.indexExpression ?: return val typeExpressions = (indexExpression as? PyTupleExpression)?.elements ?: arrayOf(indexExpression) val typeParams = mutableSetOf() @@ -1115,13 +1116,17 @@ class PyTypeHintsInspection : PyInspection() { for (typeExpr in typeExpressions) { if (typeExpr !is PyReferenceExpression && typeExpr !is PyStarExpression && typeExpr !is PySubscriptionExpression) { - registerProblem(typeExpr, PyPsiBundle.message("INSP.type.hints.parameters.to.generic.must.all.be.type.variables"), + registerProblem(typeExpr, + PyPsiBundle.message("INSP.type.hints.parameters.to.generic.must.all.be.type.variables", + if (isProtocol) 1 else 0), ProblemHighlightType.GENERIC_ERROR) continue } val typeParameterType = Ref.deref(PyTypingTypeProvider.getType(typeExpr, myTypeEvalContext)) if (typeParameterType !is PyTypeParameterType) { - registerProblem(typeExpr, PyPsiBundle.message("INSP.type.hints.parameters.to.generic.must.all.be.type.variables"), + registerProblem(typeExpr, + PyPsiBundle.message("INSP.type.hints.parameters.to.generic.must.all.be.type.variables", + if (isProtocol) 1 else 0), ProblemHighlightType.GENERIC_ERROR) continue } diff --git a/python/testData/typing/ignored.txt b/python/testData/typing/ignored.txt index 0b47cf40f7d5..57a87a293bd0 100644 --- a/python/testData/typing/ignored.txt +++ b/python/testData/typing/ignored.txt @@ -27,7 +27,6 @@ directives_deprecated.py directives_type_checking.py directives_version_platform.py generics_base_class.py -generics_basic.py generics_defaults.py generics_defaults_referential.py generics_paramspec_components.py diff --git a/python/testSrc/com/jetbrains/python/inspections/PyTypeHintsInspectionTest.java b/python/testSrc/com/jetbrains/python/inspections/PyTypeHintsInspectionTest.java index de0ee9643eb2..b8c7d5b585ac 100644 --- a/python/testSrc/com/jetbrains/python/inspections/PyTypeHintsInspectionTest.java +++ b/python/testSrc/com/jetbrains/python/inspections/PyTypeHintsInspectionTest.java @@ -115,13 +115,16 @@ public class PyTypeHintsInspectionTest extends PyInspectionTestCase { // PY-28227 public void testGenericParametersTypes() { doTestByText(""" - from typing import Generic, TypeVar + from typing import Generic, Protocol, TypeVar class A1(Generic[0]): pass class B1(Generic[int]): pass + + class B11(Protocol[int]): + pass class A2(Generic[0, 0]): pass