Repository: python/mypy

Issue:
Mypy crash on master with property alias with setters
<!--
  Use this form only if mypy reports an "INTERNAL ERROR" and/or gives a traceback.
  Please include the traceback and all other messages below (use `mypy --show-traceback`).
-->

**Crash Report**

Running latest mypy master on typeshed's openpyxl stubs with property aliases can crash

**Traceback**

```python-traceback
stubs\openpyxl\openpyxl\worksheet\_write_only.pyi:13: error: INTERNAL ERROR -- Please try using mypy 
master on GitHub:
https://mypy.readthedocs.io/en/stable/common_issues.html#using-a-development-mypy-build
Please report a bug at https://github.com/python/mypy/issues
version: 1.18.0+dev.e8147f2bdcd8899109fe1a9cffc770c436de95dc
Traceback (most recent call last):
  File "<frozen runpy>", line 198, in _run_module_as_main
  File "<frozen runpy>", line 88, in _run_code
  File "E:\Users\Avasam\Documents\Git\typeshed\.venv\Scripts\mypy.exe\__main__.py", line 10, in <module>
    sys.exit(console_entry())
  File "E:\Users\Avasam\Documents\Git\typeshed\.venv\Lib\site-packages\mypy\__main__.py", line 15, in console_entry
    main()
  File "E:\Users\Avasam\Documents\Git\typeshed\.venv\Lib\site-packages\mypy\main.py", line 127, in main
    res, messages, blockers = run_build(sources, options, fscache, t0, stdout, stderr)
  File "E:\Users\Avasam\Documents\Git\typeshed\.venv\Lib\site-packages\mypy\main.py", line 211, in run_build
    res = build.build(sources, options, None, flush_errors, fscache, stdout, stderr)
  File "E:\Users\Avasam\Documents\Git\typeshed\.venv\Lib\site-packages\mypy\build.py", line 191, in build
    result = _build(
  File "E:\Users\Avasam\Documents\Git\typeshed\.venv\Lib\site-packages\mypy\build.py", line 267, in _build
    graph = dispatch(sources, manager, stdout)
  File "E:\Users\Avasam\Documents\Git\typeshed\.venv\Lib\site-packages\mypy\build.py", line 2939, in 
dispatch
    process_graph(graph, manager)
  File "E:\Users\Avasam\Documents\Git\typeshed\.venv\Lib\site-packages\mypy\build.py", line 3337, in 
process_graph
    process_stale_scc(graph, scc, manager)
  File "E:\Users\Avasam\Documents\Git\typeshed\.venv\Lib\site-packages\mypy\build.py", line 3438, in 
process_stale_scc
    graph[id].type_check_first_pass()
  File "E:\Users\Avasam\Documents\Git\typeshed\.venv\Lib\site-packages\mypy\build.py", line 2311, in 
type_check_first_pass
    self.type_checker().check_first_pass()
  File "E:\Users\Avasam\Documents\Git\typeshed\.venv\Lib\site-packages\mypy\checker.py", line 483, in check_first_pass
    self.accept(d)
  File "E:\Users\Avasam\Documents\Git\typeshed\.venv\Lib\site-packages\mypy\checker.py", line 594, in accept
    stmt.accept(self)
  File "E:\Users\Avasam\Documents\Git\typeshed\.venv\Lib\site-packages\mypy\nodes.py", line 1251, in 
accept
    return visitor.visit_class_def(self)
  File "E:\Users\Avasam\Documents\Git\typeshed\.venv\Lib\site-packages\mypy\checker.py", line 2598, in visit_class_def
    self.accept(defn.defs)
  File "E:\Users\Avasam\Documents\Git\typeshed\.venv\Lib\site-packages\mypy\checker.py", line 594, in accept
    stmt.accept(self)
  File "E:\Users\Avasam\Documents\Git\typeshed\.venv\Lib\site-packages\mypy\nodes.py", line 1332, in 
accept
    return visitor.visit_block(self)
  File "E:\Users\Avasam\Documents\Git\typeshed\.venv\Lib\site-packages\mypy\checker.py", line 3059, in visit_block
    self.accept(s)
  File "E:\Users\Avasam\Documents\Git\typeshed\.venv\Lib\site-packages\mypy\checker.py", line 594, in accept
    stmt.accept(self)
    ~~~~~~~~~~~^^^^^^
  File "E:\Users\Avasam\Documents\Git\typeshed\.venv\Lib\site-packages\mypy\nodes.py", line 1419, in 
accept
    return visitor.visit_assignment_stmt(self)
           ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^
  File "E:\Users\Avasam\Documents\Git\typeshed\.venv\Lib\site-packages\mypy\checker.py", line 3110, in visit_assignment_stmt
    self.check_assignment(s.lvalues[-1], s.rvalue, s.type is None, s.new_syntax)
    ~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "E:\Users\Avasam\Documents\Git\typeshed\.venv\Lib\site-packages\mypy\checker.py", line 3337, in check_assignment
    self.check_compatibility_all_supers(lvalue, rvalue)
    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^
  File "E:\Users\Avasam\Documents\Git\typeshed\.venv\Lib\site-packages\mypy\checker.py", line 3471, in check_compatibility_all_supers
    lvalue_type, _ = self.node_type_from_base(lvalue_node.name, lvalue_node.info, lvalue)
                     ~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "E:\Users\Avasam\Documents\Git\typeshed\.venv\Lib\site-packages\mypy\checker.py", line 3617, in node_type_from_base
    base_type = analyze_instance_member_access(name, instance, mx, base)
  File "E:\Users\Avasam\Documents\Git\typeshed\.venv\Lib\site-packages\mypy\checkmember.py", line 386, in analyze_instance_member_access
    return analyze_member_var_access(name, typ, info, mx)
  File "E:\Users\Avasam\Documents\Git\typeshed\.venv\Lib\site-packages\mypy\checkmember.py", line 568, in analyze_member_var_access
    return analyze_var(name, v, itype, mx, implicit=implicit, is_trivial_self=is_trivial_self)       
  File "E:\Users\Avasam\Documents\Git\typeshed\.venv\Lib\site-packages\mypy\checkmember.py", line 912, in analyze_var
    item = expand_and_bind_callable(p_ct, var, itype, name, mx, is_trivial_self)
  File "E:\Users\Avasam\Documents\Git\typeshed\.venv\Lib\site-packages\mypy\checkmember.py", line 968, in expand_and_bind_callable
    assert isinstance(expanded, CallableType)
           ~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^
AssertionError:
```

**To Reproduce**

1. Fork https://github.com/Avasam/typeshed/tree/stubtest-property-alias
    - This is the exact commit I was on: https://github.com/Avasam/typeshed/commit/58fd978ca5738d1f4afdaf146a39a45b68b1d830
2. Install mypy from master
    - `uv venv`
    - `uv pip install -r .\requirements-tests.txt`
    - `uv pip install git+https://github.com/python/mypy`
3. Run mypy: `uv run mypy ./stubs/openpyxl --show-traceback`

For reference, this is the failing stub. Note that the error is on the first property alias that has a setter.
```python
from openpyxl import _Decodable
from openpyxl.workbook.child import _WorkbookChild
from openpyxl.workbook.workbook import Workbook
from openpyxl.worksheet.worksheet import Worksheet

class WriteOnlyWorksheet(_WorkbookChild):
    mime_type = Worksheet.mime_type
    add_chart = Worksheet.add_chart
    add_image = Worksheet.add_image
    add_table = Worksheet.add_table
    tables = Worksheet.tables
    print_titles = Worksheet.print_titles
    print_title_cols = Worksheet.print_title_cols  # error: INTERNAL ERROR
    print_title_rows = Worksheet.print_title_rows
    freeze_panes = Worksheet.freeze_panes
    print_area = Worksheet.print_area
    sheet_view = Worksheet.sheet_view
    def __init__(self, parent: Workbook | None, title: str | _Decodable | None) -> None: ...
    @property
    def closed(self) -> bool: ...
    def close(self) -> None: ...
    def append(self, row) -> None: ...
```

**Your Environment**

<!-- Include as many relevant details about the environment you experienced the bug in -->

- Mypy version used: mypy 1.18.0+dev.e8147f2bdcd8899109fe1a9cffc770c436de95dc (compiled: no)
- Mypy command-line flags: None
- Mypy configuration options from `mypy.ini` (and other config files): None
- Python version used: Python 3.13.1
- Operating system and version: Windows 10 Version 10.0.19045 Build 19045


Discussion:
Just curious if it is related to a TODO I added just above that line recently? Could you please:
* Replace the line where crash occurs with `assert isinstance(expanded, CallableType), expanded` and tell me what it prints?
* Show the signature of `print_title_cols` in `_WorkbookChild` (or any other relevant base class)?
(Maybe generally `assert isinstance(t, SomeType)` should be `assert isinstance(t, SomeType), t` across the code base for easier debugging in case of a crash?)
1. `AssertionError: Overload(def () -> builtins.str | None)`
2. https://foss.heptapod.net/ is currently broken so I can't link directly to source (https://foss.heptapod.net/openpyxl/openpyxl). But `WriteOnlyWorksheet.print_title_cols` is:
```py
class WriteOnlyWorksheet(_WorkbookChild):
    print_title_cols = Worksheet.print_title_cols
```

`_WorkbookChild` has no `print_title_cols`. And `Worksheet.print_title_cols` is:

```py
class Worksheet(_WorkbookChild):
    @property
    def print_title_cols(self):  # -> str | None
        """Columns to be printed at the left side of every page (ex: 'A:C')"""
        if self._print_cols:
            return str(self._print_cols)

    @print_title_cols.setter
    def print_title_cols(self, cols):
        """
        Set cols to be printed on the left of every page
        format ``A:C`
        """
        if cols is not None:
            self._print_cols = ColRange(cols)
```

OK, I see, should be an easy fix. It is a bit late today, so probably tomorrow.
Thanks! No hurry here since my use-case is for typeshed, I couldn't really use it until the next mypy release (1.18? pretty sure that feature didn't make it to 1.17) anyway
I actually already have a fix locally, but got distracted (again) by some performance work. Btw after a fix I see an error in a test
```
[case testSettablePropertyAlias]
class A:
    @property
    def prop(self)-> str: ...
    @prop.setter
    def prop(self, vals: str): ...

class C:
    prop = A.prop

C().prop  # E: Invalid self argument "C" to attribute function "prop" with type "Callable[[A], str]"
[builtins fixtures/property.pyi]
```
But I think this is actually correct (we show same error for method aliases, and it is genuinely unsafe). I think your options are (assuming `A` can't be a base class of `C` for some reasons):
* Make `A` a protocol
* Annotate `self` in `prop` definition with a protocol that `C` satisfies
* Annotate `self` with `Any` (not very safe but very simple)
That error also makes sense to me. Given that `self` can't match.

Meaning I may not be able to use property aliases as much as I wanted to in openpyxl, but I'll look at my options.



Patch:
diff --git a/mypy/checker.py b/mypy/checker.py
index 68f9bd4c1383..35407a676ad8 100644
--- a/mypy/checker.py
+++ b/mypy/checker.py
@@ -130,6 +130,7 @@
     WhileStmt,
     WithStmt,
     YieldExpr,
+    get_func_def,
     is_final_node,
 )
 from mypy.operators import flip_ops, int_op_to_method, neg_ops
@@ -703,6 +704,12 @@ def _visit_overloaded_func_def(self, defn: OverloadedFuncDef) -> None:
                     # TODO: keep precise type for callables with tricky but valid signatures.
                     setter_type = fallback_setter_type
                 defn.items[0].var.setter_type = setter_type
+                if isinstance(defn.type, Overloaded):
+                    # Update legacy property type for decorated properties.
+                    getter_type = self.extract_callable_type(defn.items[0].var.type, defn)
+                    if getter_type is not None:
+                        getter_type.definition = defn.items[0]
+                        defn.type.items[0] = getter_type
         for i, fdef in enumerate(defn.items):
             assert isinstance(fdef, Decorator)
             if defn.is_property:
@@ -730,7 +737,7 @@ def _visit_overloaded_func_def(self, defn: OverloadedFuncDef) -> None:
                     assert isinstance(item, Decorator)
                     item_type = self.extract_callable_type(item.var.type, item)
                     if item_type is not None:
-                        item_type.definition = item.func
+                        item_type.definition = item
                         item_types.append(item_type)
                 if item_types:
                     defn.type = Overloaded(item_types)
@@ -2501,8 +2508,9 @@ def check_override(
 
                 override_ids = override.type_var_ids()
                 type_name = None
-                if isinstance(override.definition, FuncDef):
-                    type_name = override.definition.info.name
+                definition = get_func_def(override)
+                if isinstance(definition, FuncDef):
+                    type_name = definition.info.name
 
                 def erase_override(t: Type) -> Type:
                     return erase_typevars(t, ids_to_erase=override_ids)
@@ -3509,6 +3517,7 @@ def check_compatibility_all_supers(self, lvalue: RefExpr, rvalue: Expression) ->
                     continue
 
                 base_type, base_node = self.node_type_from_base(lvalue_node.name, base, lvalue)
+                # TODO: if the r.h.s. is a descriptor, we should check setter override as well.
                 custom_setter = is_custom_settable_property(base_node)
                 if isinstance(base_type, PartialType):
                     base_type = None
@@ -4494,6 +4503,8 @@ def set_inferred_type(self, var: Var, lvalue: Lvalue, type: Type) -> None:
                     if isinstance(p_type, Overloaded):
                         # TODO: in theory we can have a property with a deleter only.
                         var.is_settable_property = True
+                        assert isinstance(definition, Decorator), definition
+                        var.setter_type = definition.var.setter_type
 
     def set_inference_error_fallback_type(self, var: Var, lvalue: Lvalue, type: Type) -> None:
         """Store best known type for variable if type inference failed.
@@ -5356,6 +5367,8 @@ def visit_decorator_inner(
             self.check_untyped_after_decorator(sig, e.func)
         self.require_correct_self_argument(sig, e.func)
         sig = set_callable_name(sig, e.func)
+        if isinstance(sig, CallableType):
+            sig.definition = e
         e.var.type = sig
         e.var.is_ready = True
         if e.func.is_property:
@@ -8651,8 +8664,10 @@ def visit_type_alias_type(self, t: TypeAliasType) -> Type:
         return t.copy_modified(args=[a.accept(self) for a in t.args])
 
 
-def is_classmethod_node(node: Node | None) -> bool | None:
+def is_classmethod_node(node: SymbolNode | None) -> bool | None:
     """Find out if a node describes a classmethod."""
+    if isinstance(node, Decorator):
+        node = node.func
     if isinstance(node, FuncDef):
         return node.is_class
     if isinstance(node, Var):
@@ -8660,8 +8675,10 @@ def is_classmethod_node(node: Node | None) -> bool | None:
     return None
 
 
-def is_node_static(node: Node | None) -> bool | None:
+def is_node_static(node: SymbolNode | None) -> bool | None:
     """Find out if a node describes a static function method."""
+    if isinstance(node, Decorator):
+        node = node.func
     if isinstance(node, FuncDef):
         return node.is_static
     if isinstance(node, Var):
diff --git a/mypy/checkmember.py b/mypy/checkmember.py
index da67591a4553..2c41f2e273cc 100644
--- a/mypy/checkmember.py
+++ b/mypy/checkmember.py
@@ -976,8 +976,15 @@ def expand_and_bind_callable(
     freeze_all_type_vars(expanded)
     if not var.is_property:
         return expanded
-    # TODO: a decorated property can result in Overloaded here.
-    assert isinstance(expanded, CallableType)
+    if isinstance(expanded, Overloaded):
+        # Legacy way to store settable properties is with overloads. Also in case it is
+        # an actual overloaded property, selecting first item that passed check_self_arg()
+        # is a good approximation, long-term we should use check_call() inference below.
+        if not expanded.items:
+            # A broken overload, error should be already reported.
+            return AnyType(TypeOfAny.from_error)
+        expanded = expanded.items[0]
+    assert isinstance(expanded, CallableType), expanded
     if var.is_settable_property and mx.is_lvalue and var.setter_type is not None:
         if expanded.variables:
             type_ctx = mx.rvalue or TempNode(AnyType(TypeOfAny.special_form), context=mx.context)
diff --git a/mypy/fixup.py b/mypy/fixup.py
index 0007fe8faabf..18bdc1c6f497 100644
--- a/mypy/fixup.py
+++ b/mypy/fixup.py
@@ -181,8 +181,7 @@ def visit_overloaded_func_def(self, o: OverloadedFuncDef) -> None:
         if isinstance(o.type, Overloaded):
             # For error messages we link the original definition for each item.
             for typ, item in zip(o.type.items, o.items):
-                if isinstance(item, Decorator):
-                    typ.definition = item.func
+                typ.definition = item
 
     def visit_decorator(self, d: Decorator) -> None:
         if self.current_info is not None:
@@ -193,8 +192,9 @@ def visit_decorator(self, d: Decorator) -> None:
             d.var.accept(self)
         for node in d.decorators:
             node.accept(self)
-        if isinstance(d.var.type, ProperType) and isinstance(d.var.type, CallableType):
-            d.var.type.definition = d.func
+        typ = d.var.type
+        if isinstance(typ, ProperType) and isinstance(typ, CallableType):
+            typ.definition = d.func
 
     def visit_class_def(self, c: ClassDef) -> None:
         for v in c.type_vars:
diff --git a/mypy/messages.py b/mypy/messages.py
index 6b55da59d183..f626d4c71916 100644
--- a/mypy/messages.py
+++ b/mypy/messages.py
@@ -55,6 +55,7 @@
     SymbolTable,
     TypeInfo,
     Var,
+    get_func_def,
     reverse_builtin_aliases,
 )
 from mypy.operators import op_methods, op_methods_to_symbols
@@ -2938,10 +2939,11 @@ def format_single(arg: Type) -> str:
 
 def pretty_class_or_static_decorator(tp: CallableType) -> str | None:
     """Return @classmethod or @staticmethod, if any, for the given callable type."""
-    if tp.definition is not None and isinstance(tp.definition, SYMBOL_FUNCBASE_TYPES):
-        if tp.definition.is_class:
+    definition = get_func_def(tp)
+    if definition is not None and isinstance(definition, SYMBOL_FUNCBASE_TYPES):
+        if definition.is_class:
             return "@classmethod"
-        if tp.definition.is_static:
+        if definition.is_static:
             return "@staticmethod"
     return None
 
@@ -2991,12 +2993,13 @@ def [T <: int] f(self, x: int, y: T) -> None
             slash = True
 
     # If we got a "special arg" (i.e: self, cls, etc...), prepend it to the arg list
+    definition = get_func_def(tp)
     if (
-        isinstance(tp.definition, FuncDef)
-        and hasattr(tp.definition, "arguments")
+        isinstance(definition, FuncDef)
+        and hasattr(definition, "arguments")
         and not tp.from_concatenate
     ):
-        definition_arg_names = [arg.variable.name for arg in tp.definition.arguments]
+        definition_arg_names = [arg.variable.name for arg in definition.arguments]
         if (
             len(definition_arg_names) > len(tp.arg_names)
             and definition_arg_names[0]
@@ -3005,7 +3008,7 @@ def [T <: int] f(self, x: int, y: T) -> None
             if s:
                 s = ", " + s
             s = definition_arg_names[0] + s
-        s = f"{tp.definition.name}({s})"
+        s = f"{definition.name}({s})"
     elif tp.name:
         first_arg = get_first_arg(tp)
         if first_arg:
@@ -3051,9 +3054,10 @@ def [T <: int] f(self, x: int, y: T) -> None
 
 
 def get_first_arg(tp: CallableType) -> str | None:
-    if not isinstance(tp.definition, FuncDef) or not tp.definition.info or tp.definition.is_static:
+    definition = get_func_def(tp)
+    if not isinstance(definition, FuncDef) or not definition.info or definition.is_static:
         return None
-    return tp.definition.original_first_arg
+    return definition.original_first_arg
 
 
 def variance_string(variance: int) -> str:
diff --git a/mypy/nodes.py b/mypy/nodes.py
index 9d5867c5371d..99b9bf72c948 100644
--- a/mypy/nodes.py
+++ b/mypy/nodes.py
@@ -4380,6 +4380,13 @@ def is_final_node(node: SymbolNode | None) -> bool:
     return isinstance(node, (Var, FuncDef, OverloadedFuncDef, Decorator)) and node.is_final
 
 
+def get_func_def(typ: mypy.types.CallableType) -> SymbolNode | None:
+    definition = typ.definition
+    if isinstance(definition, Decorator):
+        definition = definition.func
+    return definition
+
+
 def local_definitions(
     names: SymbolTable, name_prefix: str, info: TypeInfo | None = None
 ) -> Iterator[Definition]:
diff --git a/mypy/semanal.py b/mypy/semanal.py
index 99d1eb36e788..fb66fb5158db 100644
--- a/mypy/semanal.py
+++ b/mypy/semanal.py
@@ -1243,6 +1243,7 @@ def analyze_overloaded_func_def(self, defn: OverloadedFuncDef) -> None:
             bare_setter_type = self.analyze_property_with_multi_part_definition(defn)
             typ = function_type(first_item.func, self.named_type("builtins.function"))
             assert isinstance(typ, CallableType)
+            typ.definition = first_item
             types = [typ]
         else:
             # This is a normal overload. Find the item signatures, the
@@ -1374,6 +1375,7 @@ def analyze_overload_sigs_and_impl(
             if isinstance(item, Decorator):
                 callable = function_type(item.func, self.named_type("builtins.function"))
                 assert isinstance(callable, CallableType)
+                callable.definition = item
                 if not any(refers_to_fullname(dec, OVERLOAD_NAMES) for dec in item.decorators):
                     if i == len(defn.items) - 1 and not self.is_stub_file:
                         # Last item outside a stub is impl
diff --git a/mypy/types.py b/mypy/types.py
index b4771b15f77a..a73ac3c3524a 100644
--- a/mypy/types.py
+++ b/mypy/types.py
@@ -1887,6 +1887,9 @@ def __init__(
         self.fallback = fallback
         assert not name or "<bound method" not in name
         self.name = name
+        # The rules for what exactly is considered a definition:
+        #   * If it is a non-decorated function, FuncDef is the definition
+        #   * If it is a decorated function, enclosing Decorator is the definition
         self.definition = definition
         self.variables = variables
         self.is_ellipsis_args = is_ellipsis_args


Test patch:
diff --git a/test-data/unit/check-classes.test b/test-data/unit/check-classes.test
index f713fe69bcd2..62f538260fff 100644
--- a/test-data/unit/check-classes.test
+++ b/test-data/unit/check-classes.test
@@ -9211,3 +9211,50 @@ class CGT(BG[T]): ...
 
 reveal_type(CGI)  # N: Revealed type is "def () -> __main__.CGI"
 reveal_type(CGT)  # N: Revealed type is "def [T] () -> __main__.CGT[T`1]"
+
+[case testSettablePropertyAlias]
+from typing import Any, TypeVar
+
+class A:
+    @property
+    def prop(self: Any) -> str: ...
+    @prop.setter
+    def prop(self, val: str) -> None: ...
+
+T = TypeVar("T")
+class AT:
+    @property
+    def prop(self: T) -> T: ...
+    @prop.setter
+    def prop(self: T, val: list[T]) -> None: ...
+
+class B:
+    prop: str
+    prop_t: str
+
+class C(B):
+    prop = A.prop
+    prop_t = AT.prop  # E: Incompatible types in assignment (expression has type "C", base class "B" defined the type as "str")
+
+reveal_type(C().prop)  # N: Revealed type is "builtins.str"
+C().prop = "no"  # E: Invalid self argument "C" to attribute function "prop" with type "Callable[[A, str], None]"
+reveal_type(C().prop_t)  # N: Revealed type is "__main__.C"
+C().prop_t = 1  # E: Incompatible types in assignment (expression has type "int", variable has type "list[C]")
+[builtins fixtures/property.pyi]
+
+[case testClassEqDecoratedAbstractNote]
+from abc import abstractmethod
+
+class C:
+    @abstractmethod
+    def __eq__(self, other: C) -> bool: ...
+[builtins fixtures/plugin_attrs.pyi]
+[out]
+main:5: error: Argument 1 of "__eq__" is incompatible with supertype "builtins.object"; supertype defines the argument type as "object"
+main:5: note: This violates the Liskov substitution principle
+main:5: note: See https://mypy.readthedocs.io/en/stable/common_issues.html#incompatible-overrides
+main:5: note: It is recommended for "__eq__" to work with arbitrary objects, for example:
+main:5: note:     def __eq__(self, other: object) -> bool:
+main:5: note:         if not isinstance(other, C):
+main:5: note:             return NotImplemented
+main:5: note:         return <logic to compare two C instances>
diff --git a/test-data/unit/check-dataclasses.test b/test-data/unit/check-dataclasses.test
index a6ac30e20c36..f43c49c200c8 100644
--- a/test-data/unit/check-dataclasses.test
+++ b/test-data/unit/check-dataclasses.test
@@ -2055,7 +2055,7 @@ from dataclasses import dataclass, replace, InitVar
 from typing import ClassVar
 
 @dataclass
-class A:
+class A:  # N: "replace" of "A" defined here
     x: int
     q: InitVar[int]
     q2: InitVar[int] = 0
diff --git a/test-data/unit/fine-grained.test b/test-data/unit/fine-grained.test
index c25ed79e7356..84734e2c27bf 100644
--- a/test-data/unit/fine-grained.test
+++ b/test-data/unit/fine-grained.test
@@ -7312,9 +7312,7 @@ class C:
 ==
 mod.py:9: error: Incompatible types in assignment (expression has type "int", variable has type "str")
 
-[case testOverloadedMethodSupertype-only_when_cache]
--- Different cache/no-cache tests because
--- CallableType.def_extras.first_arg differs ("self"/None)
+[case testOverloadedMethodSupertype]
 from typing import overload, Any
 import b
 class Child(b.Parent):
@@ -7355,49 +7353,6 @@ main:4: note:          def f(self, arg: int) -> int
 main:4: note:          @overload
 main:4: note:          def f(self, arg: str) -> str
 
-[case testOverloadedMethodSupertype2-only_when_nocache]
--- Different cache/no-cache tests because
--- CallableType.def_extras.first_arg differs ("self"/None)
-from typing import overload, Any
-import b
-class Child(b.Parent):
-    @overload                           # Fail
-    def f(self, arg: int) -> int: ...
-    @overload
-    def f(self, arg: str) -> str: ...
-    def f(self, arg: Any) -> Any: ...
-[file b.py]
-from typing import overload, Any
-class C: pass
-class Parent:
-    @overload
-    def f(self, arg: int) -> int: ...
-    @overload
-    def f(self, arg: str) -> str: ...
-    def f(self, arg: Any) -> Any: ...
-[file b.py.2]
-from typing import overload, Any
-class C: pass
-class Parent:
-    @overload
-    def f(self, arg: int) -> int: ...
-    @overload
-    def f(self, arg: str) -> C: ...
-    def f(self, arg: Any) -> Any: ...
-[out]
-==
-main:4: error: Signature of "f" incompatible with supertype "b.Parent"
-main:4: note:      Superclass:
-main:4: note:          @overload
-main:4: note:          def f(self, arg: int) -> int
-main:4: note:          @overload
-main:4: note:          def f(self, arg: str) -> C
-main:4: note:      Subclass:
-main:4: note:          @overload
-main:4: note:          def f(arg: int) -> int
-main:4: note:          @overload
-main:4: note:          def f(arg: str) -> str
-
 [case testOverloadedInitSupertype]
 import a
 [file a.py]
@@ -8486,9 +8441,7 @@ class D:
 ==
 a.py:3: error: Cannot override final attribute "meth" (previously declared in base class "C")
 
-[case testFinalBodyReprocessedAndStillFinalOverloaded-only_when_cache]
--- Different cache/no-cache tests because
--- CallableType.def_extras.first_arg differs ("self"/None)
+[case testFinalBodyReprocessedAndStillFinalOverloaded]
 import a
 [file a.py]
 from c import C
@@ -8533,53 +8486,6 @@ a.py:3: note:          def meth(self, x: str) -> str
 a.py:3: note:      Subclass:
 a.py:3: note:          def meth(self) -> None
 
-[case testFinalBodyReprocessedAndStillFinalOverloaded2-only_when_nocache]
--- Different cache/no-cache tests because
--- CallableType.def_extras.first_arg differs ("self"/None)
-import a
-[file a.py]
-from c import C
-class A:
-    def meth(self) -> None: ...
-
-[file a.py.3]
-from c import C
-class A(C):
-    def meth(self) -> None: ...
-
-[file c.py]
-from typing import final, overload, Union
-from d import D
-
-class C:
-    @overload
-    def meth(self, x: int) -> int: ...
-    @overload
-    def meth(self, x: str) -> str: ...
-    @final
-    def meth(self, x: Union[int, str]) -> Union[int, str]:
-        D(int())
-        return x
-[file d.py]
-class D:
-    def __init__(self, x: int) -> None: ...
-[file d.py.2]
-from typing import Optional
-class D:
-    def __init__(self, x: Optional[int]) -> None: ...
-[out]
-==
-==
-a.py:3: error: Cannot override final attribute "meth" (previously declared in base class "C")
-a.py:3: error: Signature of "meth" incompatible with supertype "c.C"
-a.py:3: note:      Superclass:
-a.py:3: note:          @overload
-a.py:3: note:          def meth(x: int) -> int
-a.py:3: note:          @overload
-a.py:3: note:          def meth(x: str) -> str
-a.py:3: note:      Subclass:
-a.py:3: note:          def meth(self) -> None
-
 [case testIfMypyUnreachableClass]
 from a import x
 
diff --git a/test-data/unit/pythoneval.test b/test-data/unit/pythoneval.test
index 4bd94dfce03e..9b5d8a1ac54c 100644
--- a/test-data/unit/pythoneval.test
+++ b/test-data/unit/pythoneval.test
@@ -1970,6 +1970,7 @@ a2 = replace()
 a2 = replace(a, x='spam')
 a2 = replace(a, x=42, q=42)
 [out]
+_testDataclassReplace.py:4: note: "replace" of "A" defined here
 _testDataclassReplace.py:9: note: Revealed type is "_testDataclassReplace.A"
 _testDataclassReplace.py:10: error: Too few arguments for "replace"
 _testDataclassReplace.py:11: error: Argument "x" to "replace" of "A" has incompatible type "str"; expected "int"