PY-90348 Don't type a parameter as Concatenate/ParamSpec from a decorator

(cherry picked from commit 11725b2cbd96eb0d8074f85f4b4cc700a552f6bf)

GitOrigin-RevId: cb38b7a3b094498d94b044f9369889dbb87b9066
This commit is contained in:
Daniil Kalinin
2026-07-21 14:09:16 +00:00
committed by intellij-monorepo-bot
parent e5b8e52e8d
commit 114c423c7f
2 changed files with 24 additions and 0 deletions
@@ -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;
@@ -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