PY-76859 Report a warning if annotation for the self parameter of __init__ method contains class type variables

GitOrigin-RevId: 5ad374adfedaf426b32ddbd6facd077a506c9248
This commit is contained in:
Petr
2025-12-10 18:17:49 +00:00
committed by intellij-monorepo-bot
parent 765735660d
commit eb116ceb78
4 changed files with 31 additions and 3 deletions
@@ -1259,6 +1259,7 @@ INSP.type.hints.type.statement.incorrect.scope=A 'type' statement can be used on
INSP.type.hints.at.most.one.unpacked.tuple=Type argument list can have at most one unpacked TypeVarTuple or unbounded tuple
INSP.type.hints.cannot.use.covariant.in.function.param=Covariant type variable cannot be used in parameter type
INSP.type.hints.cannot.use.contravariant.in.return.type=Contravariant type variable cannot be used in function return type
INSP.type.hints.cannot.use.class.scope.type.variables.in.annotation.for.self.parameter.of__init__=Class-scoped type variables should not be used in the annotation for 'self' parameter of '__init__' method
QFIX.remove.function.annotations=Remove function annotations
QFIX.replace.with.target.name=Replace with the target name
QFIX.remove.generic.parameters=Remove generic parameters
@@ -400,10 +400,10 @@ class PyTypeHintsInspection : PyInspection() {
}
}
checkTypeCommentAndParameters(node)
checkTypeVarsInFunctionAnnotations(node)
reportTypeParametersUsedByOuterScope(node)
checkInitSelfParameterAnnotation(node)
}
override fun visitPyTargetExpression(node: PyTargetExpression) {
@@ -1435,7 +1435,7 @@ class PyTypeHintsInspection : PyInspection() {
}
else if (hasSelf && actualParametersSize == commentParametersSize) {
val actualSelfType =
(myTypeEvalContext.getType(cls!!) as? PyInstantiableType<*>)
(myTypeEvalContext.getType(cls) as? PyInstantiableType<*>)
?.let { if (modifier == PyAstFunction.Modifier.CLASSMETHOD) it.toClass() else it.toInstance() }
?: return
@@ -1478,6 +1478,27 @@ class PyTypeHintsInspection : PyInspection() {
}
}
private fun checkInitSelfParameterAnnotation(function: PyFunction) {
// Report the use of class-scoped type vars in a type hint for `self` parameter of the `__init__` method,
// which is not allowed
if (function.name != PyNames.INIT) return
val method = function.asMethod() ?: return
val containingClass = method.containingClass ?: return
val selfParameter = function.parameterList.parameters.firstOrNull()
if (selfParameter !is PyNamedParameter) return
val selfAnnotationValue = selfParameter.annotation?.value ?: return
val selfAnnotationType = PyTypingTypeProvider.getType(selfAnnotationValue, myTypeEvalContext) ?: return
val generics = PyTypeChecker.collectGenerics(selfAnnotationType.get(), myTypeEvalContext)
if (generics.typeVars.any { it.scopeOwner === containingClass }) {
registerProblem(selfAnnotationValue,
PyPsiBundle.message("INSP.type.hints.cannot.use.class.scope.type.variables.in.annotation.for.self.parameter.of__init__"))
}
}
private fun checkTypeArgumentsMatchTypeParameters(
node: PySubscriptionExpression,
typeParameters: List<PyType>,
-1
View File
@@ -8,7 +8,6 @@ callables_annotation.py
callables_kwargs.py
callables_protocol.py
callables_subtyping.py
constructors_call_init.py
constructors_call_new.py
constructors_call_type.py
constructors_callable.py
@@ -373,6 +373,13 @@ public class PyTypeHintsInspectionTest extends PyInspectionTestCase {
""");
}
public void testSelfAnnotationUsesClassScopedTypeParameters() {
doTestByText("""
class MyClass[T1, T2]:
def __init__(self: <warning descr="Class-scoped type variables should not be used in the annotation for 'self' parameter of '__init__' method">MyClass[T2, T1]</warning>) -> None: ...
""");
}
// PY-28249
public void testInstanceAndClassChecksOnAny() {
doTestByText("""