diff --git a/mypy/checker.py b/mypy/checker.py
index b90221a0a5a5..130e420eba88 100644
--- a/mypy/checker.py
+++ b/mypy/checker.py
@@ -42,7 +42,8 @@
 from mypy.sametypes import is_same_type
 from mypy.messages import (
     MessageBuilder, make_inferred_type_note, append_invariance_notes, pretty_seq,
-    format_type, format_type_bare, format_type_distinctly, SUGGESTED_TEST_FIXTURES
+    format_type, format_type_bare, format_type_distinctly, SUGGESTED_TEST_FIXTURES,
+    SubtypingContext, ClassOrStaticContext,
 )
 import mypy.checkexpr
 from mypy.checkmember import (
@@ -1528,13 +1529,13 @@ def check_method_override_for_base_with_name(
             # Construct the type of the overriding method.
             if isinstance(defn, (FuncDef, OverloadedFuncDef)):
                 typ: Type = self.function_type(defn)
-                override_class_or_static = defn.is_class or defn.is_static
+                override_static = defn.is_static
                 override_class = defn.is_class
             else:
                 assert defn.var.is_ready
                 assert defn.var.type is not None
                 typ = defn.var.type
-                override_class_or_static = defn.func.is_class or defn.func.is_static
+                override_static = defn.func.is_static
                 override_class = defn.func.is_class
             typ = get_proper_type(typ)
             if isinstance(typ, FunctionLike) and not is_static(context):
@@ -1567,27 +1568,35 @@ def check_method_override_for_base_with_name(
                 else:
                     assert False, str(base_attr.node)
             if isinstance(original_node, (FuncDef, OverloadedFuncDef)):
-                original_class_or_static = original_node.is_class or original_node.is_static
+                original_class = original_node.is_class
+                original_static = original_node.is_static
             elif isinstance(original_node, Decorator):
                 fdef = original_node.func
-                original_class_or_static = fdef.is_class or fdef.is_static
+                original_class = fdef.is_class
+                original_static = fdef.is_static
             else:
-                original_class_or_static = False  # a variable can't be class or static
+                original_class = original_static = False  # a variable can't be class or static
+
+            subtyping_context = SubtypingContext(
+                original=ClassOrStaticContext(original_class, original_static),
+                override=ClassOrStaticContext(override_class, override_static),
+            )
+
             if isinstance(original_type, AnyType) or isinstance(typ, AnyType):
                 pass
             elif isinstance(original_type, FunctionLike) and isinstance(typ, FunctionLike):
                 original = self.bind_and_map_method(base_attr, original_type,
                                                     defn.info, base)
                 # Check that the types are compatible.
-                # TODO overloaded signatures
-                self.check_override(typ,
-                                    original,
-                                    defn.name,
-                                    name,
-                                    base.name,
-                                    original_class_or_static,
-                                    override_class_or_static,
-                                    context)
+                self.check_override(
+                    typ,
+                    original,
+                    defn.name,
+                    name,
+                    base.name,
+                    context,
+                    subtyping_context=subtyping_context,
+                )
             elif is_equivalent(original_type, typ):
                 # Assume invariance for a non-callable attribute here. Note
                 # that this doesn't affect read-only properties which can have
@@ -1600,7 +1609,9 @@ def check_method_override_for_base_with_name(
                 pass
             else:
                 self.msg.signature_incompatible_with_supertype(
-                    defn.name, name, base.name, context)
+                    defn.name, name, base.name, context,
+                    subtyping_context=subtyping_context,
+                )
         return False
 
     def bind_and_map_method(self, sym: SymbolTableNode, typ: FunctionLike,
@@ -1640,9 +1651,9 @@ def get_op_other_domain(self, tp: FunctionLike) -> Optional[Type]:
 
     def check_override(self, override: FunctionLike, original: FunctionLike,
                        name: str, name_in_super: str, supertype: str,
-                       original_class_or_static: bool,
-                       override_class_or_static: bool,
-                       node: Context) -> None:
+                       node: Context,
+                       *,
+                       subtyping_context: SubtypingContext) -> None:
         """Check a method override with given signatures.
 
         Arguments:
@@ -1667,6 +1678,14 @@ def check_override(self, override: FunctionLike, original: FunctionLike,
                 fail = True
                 op_method_wider_note = True
         if isinstance(original, FunctionLike) and isinstance(override, FunctionLike):
+            original_class_or_static = (
+                subtyping_context.original.is_class
+                or subtyping_context.original.is_static
+            )
+            override_class_or_static = (
+                subtyping_context.override.is_class
+                or subtyping_context.override.is_static
+            )
             if original_class_or_static and not override_class_or_static:
                 fail = True
             elif isinstance(original, CallableType) and isinstance(override, CallableType):
@@ -1743,7 +1762,10 @@ def erase_override(t: Type) -> Type:
             if not emitted_msg:
                 # Fall back to generic incompatibility message.
                 self.msg.signature_incompatible_with_supertype(
-                    name, name_in_super, supertype, node, original=original, override=override)
+                    name, name_in_super, supertype, node,
+                    original=original, override=override,
+                    subtyping_context=subtyping_context,
+                )
             if op_method_wider_note:
                 self.note("Overloaded operator methods can't have wider argument types"
                           " in overrides", node, code=codes.OVERRIDE)
diff --git a/mypy/messages.py b/mypy/messages.py
index da284cc88ba4..85b8bc618056 100644
--- a/mypy/messages.py
+++ b/mypy/messages.py
@@ -15,7 +15,10 @@
 import difflib
 from textwrap import dedent
 
-from typing import cast, List, Dict, Any, Sequence, Iterable, Iterator, Tuple, Set, Optional, Union
+from typing import (
+    cast, List, Dict, Any, Sequence, Iterable, Iterator,
+    Tuple, Set, Optional, Union, NamedTuple,
+)
 from typing_extensions import Final
 
 from mypy.erasetype import erase_type
@@ -86,6 +89,24 @@
 }
 
 
+class ClassOrStaticContext(NamedTuple):
+    """We use this type for better error messages with `@classmethod` and `@staticmethod`.
+
+    The problem is: we cannot just rely on the regular metadata,
+    since it is not serialized / deserialized properly. See #11791
+    """
+
+    is_class: bool
+    is_static: bool
+
+
+class SubtypingContext(NamedTuple):
+    """Container we use when working with incorrect subtyping message."""
+
+    original: ClassOrStaticContext
+    override: ClassOrStaticContext
+
+
 class MessageBuilder:
     """Helper class for reporting type checker error messages with parameters.
 
@@ -817,6 +838,7 @@ def overload_signature_incompatible_with_supertype(
 
     def signature_incompatible_with_supertype(
             self, name: str, name_in_super: str, supertype: str, context: Context,
+            subtyping_context: SubtypingContext,
             original: Optional[FunctionLike] = None,
             override: Optional[FunctionLike] = None) -> None:
         code = codes.OVERRIDE
@@ -824,7 +846,6 @@ def signature_incompatible_with_supertype(
         self.fail('Signature of "{}" incompatible with {}'.format(
             name, target), context, code=code)
 
-        INCLUDE_DECORATOR = True  # Include @classmethod and @staticmethod decorators, if any
         ALLOW_DUPS = True  # Allow duplicate notes, needed when signatures are duplicates
         ALIGN_OFFSET = 1  # One space, to account for the difference between error and note
         OFFSET = 4  # Four spaces, so that notes will look like this:
@@ -833,36 +854,36 @@ def signature_incompatible_with_supertype(
         # note:          def f(self) -> str
         # note:      Subclass:
         # note:          def f(self, x: str) -> None
-        if original is not None and isinstance(original, (CallableType, Overloaded)) \
-                and override is not None and isinstance(override, (CallableType, Overloaded)):
+        if isinstance(original, FunctionLike) and isinstance(override, FunctionLike):
             self.note('Superclass:', context, offset=ALIGN_OFFSET + OFFSET, code=code)
-            self.pretty_callable_or_overload(original, context, offset=ALIGN_OFFSET + 2 * OFFSET,
-                                            add_class_or_static_decorator=INCLUDE_DECORATOR,
-                                            allow_dups=ALLOW_DUPS, code=code)
+            self.pretty_callable_or_overload(
+                original, context, offset=ALIGN_OFFSET + 2 * OFFSET,
+                class_or_static=subtyping_context.original,
+                allow_dups=ALLOW_DUPS, code=code)
 
             self.note('Subclass:', context, offset=ALIGN_OFFSET + OFFSET, code=code)
-            self.pretty_callable_or_overload(override, context, offset=ALIGN_OFFSET + 2 * OFFSET,
-                                            add_class_or_static_decorator=INCLUDE_DECORATOR,
-                                            allow_dups=ALLOW_DUPS, code=code)
+            self.pretty_callable_or_overload(
+                override, context, offset=ALIGN_OFFSET + 2 * OFFSET,
+                class_or_static=subtyping_context.override,
+                allow_dups=ALLOW_DUPS, code=code)
 
     def pretty_callable_or_overload(self,
-                                    tp: Union[CallableType, Overloaded],
+                                    tp: FunctionLike,
                                     context: Context,
                                     *,
+                                    class_or_static: ClassOrStaticContext,
                                     offset: int = 0,
-                                    add_class_or_static_decorator: bool = False,
                                     allow_dups: bool = False,
                                     code: Optional[ErrorCode] = None) -> None:
         if isinstance(tp, CallableType):
-            if add_class_or_static_decorator:
-                decorator = pretty_class_or_static_decorator(tp)
-                if decorator is not None:
-                    self.note(decorator, context, offset=offset, allow_dups=allow_dups, code=code)
+            decorator = pretty_class_or_static_decorator(tp, class_or_static)
+            if decorator is not None:
+                self.note(decorator, context, offset=offset, allow_dups=allow_dups, code=code)
             self.note(pretty_callable(tp), context,
                       offset=offset, allow_dups=allow_dups, code=code)
         elif isinstance(tp, Overloaded):
             self.pretty_overload(tp, context, offset,
-                                 add_class_or_static_decorator=add_class_or_static_decorator,
+                                 class_or_static=class_or_static,
                                  allow_dups=allow_dups, code=code)
 
     def argument_incompatible_with_supertype(
@@ -1530,14 +1551,14 @@ def pretty_overload(self,
                         context: Context,
                         offset: int,
                         *,
-                        add_class_or_static_decorator: bool = False,
+                        class_or_static: Optional[ClassOrStaticContext] = None,
                         allow_dups: bool = False,
                         code: Optional[ErrorCode] = None) -> None:
         for item in tp.items:
             self.note('@overload', context, offset=offset, allow_dups=allow_dups, code=code)
 
-            if add_class_or_static_decorator:
-                decorator = pretty_class_or_static_decorator(item)
+            if class_or_static is not None:
+                decorator = pretty_class_or_static_decorator(item, class_or_static)
                 if decorator is not None:
                     self.note(decorator, context, offset=offset, allow_dups=allow_dups, code=code)
 
@@ -1897,13 +1918,21 @@ def format_type_distinctly(*types: Type, bare: bool = False) -> Tuple[str, ...]:
         return tuple(quote_type_string(s) for s in strs)
 
 
-def pretty_class_or_static_decorator(tp: CallableType) -> Optional[str]:
+def pretty_class_or_static_decorator(
+    tp: CallableType, context: ClassOrStaticContext,
+) -> Optional[str]:
     """Return @classmethod or @staticmethod, if any, for the given callable type."""
+    is_static = context.is_static
+    is_class = context.is_class
     if tp.definition is not None and isinstance(tp.definition, SYMBOL_FUNCBASE_TYPES):
         if tp.definition.is_class:
-            return '@classmethod'
+            is_class = True
         if tp.definition.is_static:
-            return '@staticmethod'
+            is_static = True
+    if is_static:
+        return '@staticmethod'
+    if is_class:
+        return '@classmethod'
     return None
 
 
diff --git a/test-data/unit/check-incremental.test b/test-data/unit/check-incremental.test
index 1d2262ab303d..af176f3f3254 100644
--- a/test-data/unit/check-incremental.test
+++ b/test-data/unit/check-incremental.test
@@ -5610,3 +5610,67 @@ class C:
     pass
 [rechecked]
 [stale]
+
+[case testIncrementalClassMethodErrorMessage]
+from a import Foo  # https://github.com/python/mypy/issues/11791
+
+class Bar(Foo):
+    def foo(cls, arg: int) -> int:
+        pass
+[file a.py]
+class Foo:
+    @classmethod
+    def foo(cls, arg: int) -> int:
+        pass
+[file a.py.2]
+class Foo:
+    @classmethod
+    def foo(cls, arg: int) -> int:
+        pass
+[builtins fixtures/classmethod.pyi]
+[out]
+main:4: error: Signature of "foo" incompatible with supertype "Foo"
+main:4: note:      Superclass:
+main:4: note:          @classmethod
+main:4: note:          def foo(cls, arg: int) -> int
+main:4: note:      Subclass:
+main:4: note:          def foo(cls, arg: int) -> int
+[out2]
+main:4: error: Signature of "foo" incompatible with supertype "Foo"
+main:4: note:      Superclass:
+main:4: note:          @classmethod
+main:4: note:          def foo(cls, arg: int) -> int
+main:4: note:      Subclass:
+main:4: note:          def foo(cls, arg: int) -> int
+
+[case testIncrementalStaticMethodErrorMessage]
+from a import Foo
+
+class Bar(Foo):
+    def foo(self, arg: int) -> int:
+        pass
+[file a.py]
+class Foo:
+    @staticmethod
+    def foo(arg: int) -> int:
+        pass
+[file a.py.2]
+class Foo:
+    @staticmethod
+    def foo(arg: int) -> int:
+        pass
+[builtins fixtures/classmethod.pyi]
+[out]
+main:4: error: Signature of "foo" incompatible with supertype "Foo"
+main:4: note:      Superclass:
+main:4: note:          @staticmethod
+main:4: note:          def foo(arg: int) -> int
+main:4: note:      Subclass:
+main:4: note:          def foo(self, arg: int) -> int
+[out2]
+main:4: error: Signature of "foo" incompatible with supertype "Foo"
+main:4: note:      Superclass:
+main:4: note:          @staticmethod
+main:4: note:          def foo(arg: int) -> int
+main:4: note:      Subclass:
+main:4: note:          def foo(self, arg: int) -> int