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