Skip to content

Commit c2d02a3

Browse files
authored
Detailed 'signature incompatible with supertype' for non-callables (#15263)
Previously the "signature incompatible with supertype" error only included detailed comparison when both types are callables; now it will compare in all cases.
1 parent 0334ebc commit c2d02a3

7 files changed

+156
-34
lines changed

mypy/checker.py

+3-1
Original file line numberDiff line numberDiff line change
@@ -1988,7 +1988,9 @@ def check_method_override_for_base_with_name(
19881988
# If the attribute is read-only, allow covariance
19891989
pass
19901990
else:
1991-
self.msg.signature_incompatible_with_supertype(defn.name, name, base.name, context)
1991+
self.msg.signature_incompatible_with_supertype(
1992+
defn.name, name, base.name, context, original=original_type, override=typ
1993+
)
19921994
return False
19931995

19941996
def bind_and_map_method(

mypy/messages.py

+31-10
Original file line numberDiff line numberDiff line change
@@ -1136,13 +1136,18 @@ def signature_incompatible_with_supertype(
11361136
name_in_super: str,
11371137
supertype: str,
11381138
context: Context,
1139-
original: FunctionLike | None = None,
1140-
override: FunctionLike | None = None,
1139+
*,
1140+
original: ProperType,
1141+
override: ProperType,
11411142
) -> None:
11421143
code = codes.OVERRIDE
11431144
target = self.override_target(name, name_in_super, supertype)
11441145
self.fail(f'Signature of "{name}" incompatible with {target}', context, code=code)
11451146

1147+
original_str, override_str = format_type_distinctly(
1148+
original, override, options=self.options, bare=True
1149+
)
1150+
11461151
INCLUDE_DECORATOR = True # Include @classmethod and @staticmethod decorators, if any
11471152
ALLOW_DUPS = True # Allow duplicate notes, needed when signatures are duplicates
11481153
ALIGN_OFFSET = 1 # One space, to account for the difference between error and note
@@ -1152,13 +1157,10 @@ def signature_incompatible_with_supertype(
11521157
# note: def f(self) -> str
11531158
# note: Subclass:
11541159
# note: def f(self, x: str) -> None
1155-
if (
1156-
original is not None
1157-
and isinstance(original, (CallableType, Overloaded))
1158-
and override is not None
1159-
and isinstance(override, (CallableType, Overloaded))
1160-
):
1161-
self.note("Superclass:", context, offset=ALIGN_OFFSET + OFFSET, code=code)
1160+
self.note(
1161+
"Superclass:", context, offset=ALIGN_OFFSET + OFFSET, allow_dups=ALLOW_DUPS, code=code
1162+
)
1163+
if isinstance(original, (CallableType, Overloaded)):
11621164
self.pretty_callable_or_overload(
11631165
original,
11641166
context,
@@ -1167,8 +1169,19 @@ def signature_incompatible_with_supertype(
11671169
allow_dups=ALLOW_DUPS,
11681170
code=code,
11691171
)
1172+
else:
1173+
self.note(
1174+
original_str,
1175+
context,
1176+
offset=ALIGN_OFFSET + 2 * OFFSET,
1177+
allow_dups=ALLOW_DUPS,
1178+
code=code,
1179+
)
11701180

1171-
self.note("Subclass:", context, offset=ALIGN_OFFSET + OFFSET, code=code)
1181+
self.note(
1182+
"Subclass:", context, offset=ALIGN_OFFSET + OFFSET, allow_dups=ALLOW_DUPS, code=code
1183+
)
1184+
if isinstance(override, (CallableType, Overloaded)):
11721185
self.pretty_callable_or_overload(
11731186
override,
11741187
context,
@@ -1177,6 +1190,14 @@ def signature_incompatible_with_supertype(
11771190
allow_dups=ALLOW_DUPS,
11781191
code=code,
11791192
)
1193+
else:
1194+
self.note(
1195+
override_str,
1196+
context,
1197+
offset=ALIGN_OFFSET + 2 * OFFSET,
1198+
allow_dups=ALLOW_DUPS,
1199+
code=code,
1200+
)
11801201

11811202
def pretty_callable_or_overload(
11821203
self,

test-data/unit/check-abstract.test

+5-1
Original file line numberDiff line numberDiff line change
@@ -790,7 +790,11 @@ class A(metaclass=ABCMeta):
790790
def x(self) -> int: pass
791791
class B(A):
792792
@property
793-
def x(self) -> str: return "no" # E: Signature of "x" incompatible with supertype "A"
793+
def x(self) -> str: return "no" # E: Signature of "x" incompatible with supertype "A" \
794+
# N: Superclass: \
795+
# N: int \
796+
# N: Subclass: \
797+
# N: str
794798
b = B()
795799
b.x() # E: "str" not callable
796800
[builtins fixtures/property.pyi]

test-data/unit/check-classes.test

+97-18
Original file line numberDiff line numberDiff line change
@@ -115,7 +115,11 @@ class Base:
115115
__hash__ = None
116116

117117
class Derived(Base):
118-
def __hash__(self) -> int: # E: Signature of "__hash__" incompatible with supertype "Base"
118+
def __hash__(self) -> int: # E: Signature of "__hash__" incompatible with supertype "Base" \
119+
# N: Superclass: \
120+
# N: None \
121+
# N: Subclass: \
122+
# N: def __hash__(self) -> int
119123
pass
120124

121125
# Correct:
@@ -157,7 +161,11 @@ class Base:
157161

158162

159163
class Derived(Base):
160-
def partial_type(self) -> int: # E: Signature of "partial_type" incompatible with supertype "Base"
164+
def partial_type(self) -> int: # E: Signature of "partial_type" incompatible with supertype "Base" \
165+
# N: Superclass: \
166+
# N: List[Any] \
167+
# N: Subclass: \
168+
# N: def partial_type(self) -> int
161169
...
162170

163171

@@ -567,11 +575,45 @@ class A:
567575

568576
class B(A):
569577
@dec
570-
def f(self) -> int: pass # E: Signature of "f" incompatible with supertype "A"
571-
def g(self) -> int: pass # E: Signature of "g" incompatible with supertype "A"
578+
def f(self) -> int: pass # E: Signature of "f" incompatible with supertype "A" \
579+
# N: Superclass: \
580+
# N: def f(self) -> str \
581+
# N: Subclass: \
582+
# N: str
583+
def g(self) -> int: pass # E: Signature of "g" incompatible with supertype "A" \
584+
# N: Superclass: \
585+
# N: str \
586+
# N: Subclass: \
587+
# N: def g(self) -> int
572588
@dec
573589
def h(self) -> str: pass
574590

591+
[case testOverrideIncompatibleWithMultipleSupertypes]
592+
class A:
593+
def f(self, *, a: int) -> None:
594+
return
595+
596+
class B(A):
597+
def f(self, *, b: int) -> None: # E: Signature of "f" incompatible with supertype "A" \
598+
# N: Superclass: \
599+
# N: def f(self, *, a: int) -> None \
600+
# N: Subclass: \
601+
# N: def f(self, *, b: int) -> None
602+
return
603+
604+
class C(B):
605+
def f(self, *, c: int) -> None: # E: Signature of "f" incompatible with supertype "B" \
606+
# N: Superclass: \
607+
# N: def f(self, *, b: int) -> None \
608+
# N: Subclass: \
609+
# N: def f(self, *, c: int) -> None \
610+
# E: Signature of "f" incompatible with supertype "A" \
611+
# N: Superclass: \
612+
# N: def f(self, *, a: int) -> None \
613+
# N: Subclass: \
614+
# N: def f(self, *, c: int) -> None
615+
return
616+
575617
[case testOverrideStaticMethodWithStaticMethod]
576618
class A:
577619
@staticmethod
@@ -4223,11 +4265,12 @@ class A:
42234265
def a(self) -> None: pass
42244266
b = 1
42254267
class B(A):
4226-
a = 1
4227-
def b(self) -> None: pass
4228-
[out]
4229-
main:5: error: Incompatible types in assignment (expression has type "int", base class "A" defined the type as "Callable[[A], None]")
4230-
main:6: error: Signature of "b" incompatible with supertype "A"
4268+
a = 1 # E: Incompatible types in assignment (expression has type "int", base class "A" defined the type as "Callable[[A], None]")
4269+
def b(self) -> None: pass # E: Signature of "b" incompatible with supertype "A" \
4270+
# N: Superclass: \
4271+
# N: int \
4272+
# N: Subclass: \
4273+
# N: def b(self) -> None
42314274

42324275
[case testVariableProperty]
42334276
class A:
@@ -6166,7 +6209,11 @@ import a
61666209
[file b.py]
61676210
import a
61686211
class Sub(a.Base):
6169-
def x(self) -> int: pass # E: Signature of "x" incompatible with supertype "Base"
6212+
def x(self) -> int: pass # E: Signature of "x" incompatible with supertype "Base" \
6213+
# N: Superclass: \
6214+
# N: int \
6215+
# N: Subclass: \
6216+
# N: def x(self) -> int
61706217

61716218
[file a.py]
61726219
import b
@@ -6182,7 +6229,11 @@ import a
61826229
import c
61836230
class Sub(a.Base):
61846231
@c.deco
6185-
def x(self) -> int: pass # E: Signature of "x" incompatible with supertype "Base"
6232+
def x(self) -> int: pass # E: Signature of "x" incompatible with supertype "Base" \
6233+
# N: Superclass: \
6234+
# N: int \
6235+
# N: Subclass: \
6236+
# N: def x(*Any, **Any) -> Tuple[int, int]
61866237

61876238
[file a.py]
61886239
import b
@@ -6204,7 +6255,11 @@ import a
62046255
import c
62056256
class Sub(a.Base):
62066257
@c.deco
6207-
def x(self) -> int: pass # E: Signature of "x" incompatible with supertype "Base"
6258+
def x(self) -> int: pass # E: Signature of "x" incompatible with supertype "Base" \
6259+
# N: Superclass: \
6260+
# N: int \
6261+
# N: Subclass: \
6262+
# N: def x(*Any, **Any) -> Tuple[int, int]
62086263

62096264
[file a.py]
62106265
import b
@@ -7687,13 +7742,29 @@ class Parent:
76877742
foobar = TypeVar("foobar")
76887743

76897744
class Child(Parent):
7690-
def foo(self, val: int) -> int: # E: Signature of "foo" incompatible with supertype "Parent"
7745+
def foo(self, val: int) -> int: # E: Signature of "foo" incompatible with supertype "Parent" \
7746+
# N: Superclass: \
7747+
# N: None \
7748+
# N: Subclass: \
7749+
# N: def foo(self, val: int) -> int
76917750
return val
7692-
def bar(self, val: str) -> str: # E: Signature of "bar" incompatible with supertype "Parent"
7751+
def bar(self, val: str) -> str: # E: Signature of "bar" incompatible with supertype "Parent" \
7752+
# N: Superclass: \
7753+
# N: None \
7754+
# N: Subclass: \
7755+
# N: def bar(self, val: str) -> str
76937756
return val
7694-
def baz(self, val: float) -> float: # E: Signature of "baz" incompatible with supertype "Parent"
7757+
def baz(self, val: float) -> float: # E: Signature of "baz" incompatible with supertype "Parent" \
7758+
# N: Superclass: \
7759+
# N: None \
7760+
# N: Subclass: \
7761+
# N: def baz(self, val: float) -> float
76957762
return val
7696-
def foobar(self) -> bool: # E: Signature of "foobar" incompatible with supertype "Parent"
7763+
def foobar(self) -> bool: # E: Signature of "foobar" incompatible with supertype "Parent" \
7764+
# N: Superclass: \
7765+
# N: None \
7766+
# N: Subclass: \
7767+
# N: def foobar(self) -> bool
76977768
return False
76987769

76997770
x: Parent.foo = lambda: 5
@@ -7761,7 +7832,11 @@ class B:
77617832
...
77627833
class C(B):
77637834
@property
7764-
def foo(self) -> int: # E: Signature of "foo" incompatible with supertype "B"
7835+
def foo(self) -> int: # E: Signature of "foo" incompatible with supertype "B" \
7836+
# N: Superclass: \
7837+
# N: def foo(self) -> int \
7838+
# N: Subclass: \
7839+
# N: int
77657840
...
77667841
[builtins fixtures/property.pyi]
77677842

@@ -7771,7 +7846,11 @@ class B:
77717846
def foo(self) -> int:
77727847
...
77737848
class C(B):
7774-
def foo(self) -> int: # E: Signature of "foo" incompatible with supertype "B"
7849+
def foo(self) -> int: # E: Signature of "foo" incompatible with supertype "B" \
7850+
# N: Superclass: \
7851+
# N: int \
7852+
# N: Subclass: \
7853+
# N: def foo(self) -> int
77757854
...
77767855
[builtins fixtures/property.pyi]
77777856

test-data/unit/check-dataclasses.test

+5-1
Original file line numberDiff line numberDiff line change
@@ -198,7 +198,11 @@ class Base:
198198
@dataclass
199199
class BadDerived1(Base):
200200
def foo(self) -> int: # E: Dataclass attribute may only be overridden by another attribute \
201-
# E: Signature of "foo" incompatible with supertype "Base"
201+
# E: Signature of "foo" incompatible with supertype "Base" \
202+
# N: Superclass: \
203+
# N: int \
204+
# N: Subclass: \
205+
# N: def foo(self) -> int
202206
return 1
203207

204208
@dataclass

test-data/unit/check-functions.test

+10-2
Original file line numberDiff line numberDiff line change
@@ -2848,7 +2848,11 @@ class C(A): # inverted order of decorators
28482848
class D(A):
28492849
@property
28502850
@override
2851-
def f(self) -> int: pass # E: Signature of "f" incompatible with supertype "A"
2851+
def f(self) -> int: pass # E: Signature of "f" incompatible with supertype "A" \
2852+
# N: Superclass: \
2853+
# N: str \
2854+
# N: Subclass: \
2855+
# N: int
28522856
[builtins fixtures/property.pyi]
28532857
[typing fixtures/typing-full.pyi]
28542858

@@ -2877,7 +2881,11 @@ class C(A):
28772881
def f(self, value: str) -> None: pass
28782882

28792883
class D(A):
2880-
@override # E: Signature of "f" incompatible with supertype "A"
2884+
@override # E: Signature of "f" incompatible with supertype "A" \
2885+
# N: Superclass: \
2886+
# N: str \
2887+
# N: Subclass: \
2888+
# N: int
28812889
@property
28822890
def f(self) -> int: pass
28832891

test-data/unit/check-plugin-attrs.test

+5-1
Original file line numberDiff line numberDiff line change
@@ -1935,7 +1935,11 @@ class Sub(Base):
19351935
last_name: str
19361936

19371937
@property
1938-
def name(self) -> int: ... # E: Signature of "name" incompatible with supertype "Base"
1938+
def name(self) -> int: ... # E: Signature of "name" incompatible with supertype "Base" \
1939+
# N: Superclass: \
1940+
# N: str \
1941+
# N: Subclass: \
1942+
# N: int
19391943

19401944
# This matches runtime semantics
19411945
reveal_type(Sub) # N: Revealed type is "def (*, name: builtins.str, first_name: builtins.str, last_name: builtins.str) -> __main__.Sub"

0 commit comments

Comments
 (0)