From 114c423c7f4b6f7df3601ef9d579ecbbf5f38b4a Mon Sep 17 00:00:00 2001 From: Daniil Kalinin Date: Wed, 17 Jun 2026 15:50:19 +0200 Subject: [PATCH] PY-90348 Don't type a parameter as Concatenate/ParamSpec from a decorator (cherry picked from commit 11725b2cbd96eb0d8074f85f4b4cc700a552f6bf) GitOrigin-RevId: cb38b7a3b094498d94b044f9369889dbb87b9066 --- .../PyDecoratedFunctionTypeProvider.java | 6 ++++++ .../python/types/PyCallableTypeTest.kt | 18 ++++++++++++++++++ 2 files changed, 24 insertions(+) diff --git a/python/python-psi-impl/src/com/jetbrains/python/codeInsight/decorator/PyDecoratedFunctionTypeProvider.java b/python/python-psi-impl/src/com/jetbrains/python/codeInsight/decorator/PyDecoratedFunctionTypeProvider.java index afde21dd1406..7dc87678f477 100644 --- a/python/python-psi-impl/src/com/jetbrains/python/codeInsight/decorator/PyDecoratedFunctionTypeProvider.java +++ b/python/python-psi-impl/src/com/jetbrains/python/codeInsight/decorator/PyDecoratedFunctionTypeProvider.java @@ -20,6 +20,8 @@ import com.jetbrains.python.psi.types.PyCallableParameter; import com.jetbrains.python.psi.types.PyCallableType; import com.jetbrains.python.psi.types.PyCallableTypeImpl; import com.jetbrains.python.psi.types.PyClassType; +import com.jetbrains.python.psi.types.PyConcatenateType; +import com.jetbrains.python.psi.types.PyParamSpecType; import com.jetbrains.python.psi.types.PySyntheticCallHelper; import com.jetbrains.python.psi.types.PyType; import com.jetbrains.python.psi.types.PyTypeChecker; @@ -73,6 +75,10 @@ public final class PyDecoratedFunctionTypeProvider extends PyTypeProviderBase { if (paramPos < 0) return null; var type = ParamHelper.getExpectedTypeForPositionalParam(paramPos, expectedParams, context); + // A Concatenate/ParamSpec is a whole parameter list, not a single parameter's type; don't assign it to + // one parameter (e.g. `self` under @hybrid_method). Defer to regular inference (PY-90348). + if (type instanceof PyConcatenateType || type instanceof PyParamSpecType) return null; + return type != null ? Ref.create(type) : null; } return null; diff --git a/python/testSrc/com/jetbrains/python/types/PyCallableTypeTest.kt b/python/testSrc/com/jetbrains/python/types/PyCallableTypeTest.kt index d7f18526cba3..115188d5ccbd 100644 --- a/python/testSrc/com/jetbrains/python/types/PyCallableTypeTest.kt +++ b/python/testSrc/com/jetbrains/python/types/PyCallableTypeTest.kt @@ -736,6 +736,24 @@ class PyCallableTypeTest : PyCodeInsightTestCase() { return x + y """, ) + + @Test + @TestFor(issues = ["PY-90348"]) + fun `Concatenate-typed decorator keeps the receiver type`() = test(""" + from typing import Any, Callable, Concatenate, ParamSpec, TypeVar + + P = ParamSpec("P") + R = TypeVar("R") + + def deco(fn: Callable[Concatenate[Any, P], R]) -> Callable[P, R]: ... + + class C: + @deco + def m(self, a: int) -> int: + expr = self + # └ TYPE Self@C + return a + """) } @Nested