From 380e2ba3b88b1fc949bfbc733b73eb1d46d5cbc2 Mon Sep 17 00:00:00 2001
From: STerliakov <terlya.stas@gmail.com>
Date: Sun, 4 May 2025 16:21:22 +0200
Subject: [PATCH 1/2] Bind selftypes in decorated classmethods

---
 mypy/checkmember.py                |  6 ++++-
 test-data/unit/check-selftype.test | 41 ++++++++++++++++++++++++++++++
 2 files changed, 46 insertions(+), 1 deletion(-)

diff --git a/mypy/checkmember.py b/mypy/checkmember.py
index 1a76372d4731..26cbed470659 100644
--- a/mypy/checkmember.py
+++ b/mypy/checkmember.py
@@ -1203,9 +1203,13 @@ def analyze_class_attribute_access(
         t = get_proper_type(t)
         if isinstance(t, FunctionLike) and is_classmethod:
             t = check_self_arg(t, mx.self_type, False, mx.context, name, mx.msg)
-        result = add_class_tvars(
+        t = add_class_tvars(
             t, isuper, is_classmethod, is_staticmethod, mx.self_type, original_vars=original_vars
         )
+        if is_decorated and not is_staticmethod:
+            t = expand_self_type_if_needed(t, mx, node.node.var, itype, is_class=is_classmethod)
+
+        result = t
         # __set__ is not called on class objects.
         if not mx.is_lvalue:
             result = analyze_descriptor_access(result, mx)
diff --git a/test-data/unit/check-selftype.test b/test-data/unit/check-selftype.test
index ffa1a369e883..36a0fde31f19 100644
--- a/test-data/unit/check-selftype.test
+++ b/test-data/unit/check-selftype.test
@@ -2221,3 +2221,44 @@ class B:
 class C(A, B):  # OK: both methods take Self
     pass
 [builtins fixtures/tuple.pyi]
+
+[case testSelfInFuncDecoratedClassmethod]
+from collections.abc import Callable
+from typing import Self, TypeVar
+
+T = TypeVar("T")
+
+def debug(make: Callable[[type[T]], T]) -> Callable[[type[T]], T]:
+    return make
+
+class Foo:
+    @classmethod
+    @debug
+    def make(cls) -> Self:
+        return cls()
+
+class Bar(Foo): ...
+
+reveal_type(Foo.make())  # N: Revealed type is "__main__.Foo"
+reveal_type(Foo().make())  # N: Revealed type is "__main__.Foo"
+reveal_type(Bar.make())  # N: Revealed type is "__main__.Bar"
+reveal_type(Bar().make())  # N: Revealed type is "__main__.Bar"
+[builtins fixtures/tuple.pyi]
+
+[case testSelfInClassDecoratedClassmethod]
+from typing import Callable, Generic, TypeVar, Self
+
+T = TypeVar("T")
+
+class W(Generic[T]):
+    def __init__(self, fn: Callable[..., T]) -> None: ...
+    def __call__(self) -> T: ...
+
+class Check:
+    @W
+    def foo(self) -> Self:
+        ...
+
+reveal_type(Check.foo())  # N: Revealed type is "def () -> __main__.Check"
+reveal_type(Check().foo())  # N: Revealed type is "__main__.Check"
+[builtins fixtures/tuple.pyi]

From 4442e4a52f3fbbc75f70f2788aaca622c518edb4 Mon Sep 17 00:00:00 2001
From: STerliakov <terlya.stas@gmail.com>
Date: Sun, 4 May 2025 16:26:12 +0200
Subject: [PATCH 2/2] Fix typing

---
 mypy/checkmember.py | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)

diff --git a/mypy/checkmember.py b/mypy/checkmember.py
index 26cbed470659..0210f8518434 100644
--- a/mypy/checkmember.py
+++ b/mypy/checkmember.py
@@ -1207,7 +1207,9 @@ def analyze_class_attribute_access(
             t, isuper, is_classmethod, is_staticmethod, mx.self_type, original_vars=original_vars
         )
         if is_decorated and not is_staticmethod:
-            t = expand_self_type_if_needed(t, mx, node.node.var, itype, is_class=is_classmethod)
+            t = expand_self_type_if_needed(
+                t, mx, cast(Decorator, node.node).var, itype, is_class=is_classmethod
+            )
 
         result = t
         # __set__ is not called on class objects.