Skip to content

Detailed 'signature incompatible with supertype' for non-callables #15263

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion mypy/checker.py
Original file line number Diff line number Diff line change
Expand Up @@ -1988,7 +1988,9 @@ def check_method_override_for_base_with_name(
# If the attribute is read-only, allow covariance
pass
else:
self.msg.signature_incompatible_with_supertype(defn.name, name, base.name, context)
self.msg.signature_incompatible_with_supertype(
defn.name, name, base.name, context, original=original_type, override=typ
)
return False

def bind_and_map_method(
Expand Down
41 changes: 31 additions & 10 deletions mypy/messages.py
Original file line number Diff line number Diff line change
Expand Up @@ -1136,13 +1136,18 @@ def signature_incompatible_with_supertype(
name_in_super: str,
supertype: str,
context: Context,
original: FunctionLike | None = None,
override: FunctionLike | None = None,
*,
original: ProperType,
override: ProperType,
) -> None:
code = codes.OVERRIDE
target = self.override_target(name, name_in_super, supertype)
self.fail(f'Signature of "{name}" incompatible with {target}', context, code=code)

original_str, override_str = format_type_distinctly(
original, override, options=self.options, bare=True
)

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
Expand All @@ -1152,13 +1157,10 @@ 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))
):
self.note("Superclass:", context, offset=ALIGN_OFFSET + OFFSET, code=code)
self.note(
"Superclass:", context, offset=ALIGN_OFFSET + OFFSET, allow_dups=ALLOW_DUPS, code=code
)
if isinstance(original, (CallableType, Overloaded)):
self.pretty_callable_or_overload(
original,
context,
Expand All @@ -1167,8 +1169,19 @@ def signature_incompatible_with_supertype(
allow_dups=ALLOW_DUPS,
code=code,
)
else:
self.note(
original_str,
context,
offset=ALIGN_OFFSET + 2 * OFFSET,
allow_dups=ALLOW_DUPS,
code=code,
)

self.note("Subclass:", context, offset=ALIGN_OFFSET + OFFSET, code=code)
self.note(
"Subclass:", context, offset=ALIGN_OFFSET + OFFSET, allow_dups=ALLOW_DUPS, code=code
)
if isinstance(override, (CallableType, Overloaded)):
self.pretty_callable_or_overload(
override,
context,
Expand All @@ -1177,6 +1190,14 @@ def signature_incompatible_with_supertype(
allow_dups=ALLOW_DUPS,
code=code,
)
else:
self.note(
override_str,
context,
offset=ALIGN_OFFSET + 2 * OFFSET,
allow_dups=ALLOW_DUPS,
code=code,
)

def pretty_callable_or_overload(
self,
Expand Down
6 changes: 5 additions & 1 deletion test-data/unit/check-abstract.test
Original file line number Diff line number Diff line change
Expand Up @@ -790,7 +790,11 @@ class A(metaclass=ABCMeta):
def x(self) -> int: pass
class B(A):
@property
def x(self) -> str: return "no" # E: Signature of "x" incompatible with supertype "A"
def x(self) -> str: return "no" # E: Signature of "x" incompatible with supertype "A" \
# N: Superclass: \
# N: int \
# N: Subclass: \
# N: str
b = B()
b.x() # E: "str" not callable
[builtins fixtures/property.pyi]
Expand Down
115 changes: 97 additions & 18 deletions test-data/unit/check-classes.test
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,11 @@ class Base:
__hash__ = None

class Derived(Base):
def __hash__(self) -> int: # E: Signature of "__hash__" incompatible with supertype "Base"
def __hash__(self) -> int: # E: Signature of "__hash__" incompatible with supertype "Base" \
# N: Superclass: \
# N: None \
# N: Subclass: \
# N: def __hash__(self) -> int
pass

# Correct:
Expand Down Expand Up @@ -157,7 +161,11 @@ class Base:


class Derived(Base):
def partial_type(self) -> int: # E: Signature of "partial_type" incompatible with supertype "Base"
def partial_type(self) -> int: # E: Signature of "partial_type" incompatible with supertype "Base" \
# N: Superclass: \
# N: List[Any] \
# N: Subclass: \
# N: def partial_type(self) -> int
...


Expand Down Expand Up @@ -567,11 +575,45 @@ class A:

class B(A):
@dec
def f(self) -> int: pass # E: Signature of "f" incompatible with supertype "A"
def g(self) -> int: pass # E: Signature of "g" incompatible with supertype "A"
def f(self) -> int: pass # E: Signature of "f" incompatible with supertype "A" \
# N: Superclass: \
# N: def f(self) -> str \
# N: Subclass: \
# N: str
def g(self) -> int: pass # E: Signature of "g" incompatible with supertype "A" \
# N: Superclass: \
# N: str \
# N: Subclass: \
# N: def g(self) -> int
@dec
def h(self) -> str: pass

[case testOverrideIncompatibleWithMultipleSupertypes]
class A:
def f(self, *, a: int) -> None:
return

class B(A):
def f(self, *, b: int) -> None: # E: Signature of "f" incompatible with supertype "A" \
# N: Superclass: \
# N: def f(self, *, a: int) -> None \
# N: Subclass: \
# N: def f(self, *, b: int) -> None
return

class C(B):
def f(self, *, c: int) -> None: # E: Signature of "f" incompatible with supertype "B" \
# N: Superclass: \
# N: def f(self, *, b: int) -> None \
# N: Subclass: \
# N: def f(self, *, c: int) -> None \
# E: Signature of "f" incompatible with supertype "A" \
# N: Superclass: \
# N: def f(self, *, a: int) -> None \
# N: Subclass: \
# N: def f(self, *, c: int) -> None
return

[case testOverrideStaticMethodWithStaticMethod]
class A:
@staticmethod
Expand Down Expand Up @@ -4223,11 +4265,12 @@ class A:
def a(self) -> None: pass
b = 1
class B(A):
a = 1
def b(self) -> None: pass
[out]
main:5: error: Incompatible types in assignment (expression has type "int", base class "A" defined the type as "Callable[[A], None]")
main:6: error: Signature of "b" incompatible with supertype "A"
a = 1 # E: Incompatible types in assignment (expression has type "int", base class "A" defined the type as "Callable[[A], None]")
def b(self) -> None: pass # E: Signature of "b" incompatible with supertype "A" \
# N: Superclass: \
# N: int \
# N: Subclass: \
# N: def b(self) -> None

[case testVariableProperty]
class A:
Expand Down Expand Up @@ -6166,7 +6209,11 @@ import a
[file b.py]
import a
class Sub(a.Base):
def x(self) -> int: pass # E: Signature of "x" incompatible with supertype "Base"
def x(self) -> int: pass # E: Signature of "x" incompatible with supertype "Base" \
# N: Superclass: \
# N: int \
# N: Subclass: \
# N: def x(self) -> int

[file a.py]
import b
Expand All @@ -6182,7 +6229,11 @@ import a
import c
class Sub(a.Base):
@c.deco
def x(self) -> int: pass # E: Signature of "x" incompatible with supertype "Base"
def x(self) -> int: pass # E: Signature of "x" incompatible with supertype "Base" \
# N: Superclass: \
# N: int \
# N: Subclass: \
# N: def x(*Any, **Any) -> Tuple[int, int]

[file a.py]
import b
Expand All @@ -6204,7 +6255,11 @@ import a
import c
class Sub(a.Base):
@c.deco
def x(self) -> int: pass # E: Signature of "x" incompatible with supertype "Base"
def x(self) -> int: pass # E: Signature of "x" incompatible with supertype "Base" \
# N: Superclass: \
# N: int \
# N: Subclass: \
# N: def x(*Any, **Any) -> Tuple[int, int]

[file a.py]
import b
Expand Down Expand Up @@ -7687,13 +7742,29 @@ class Parent:
foobar = TypeVar("foobar")

class Child(Parent):
def foo(self, val: int) -> int: # E: Signature of "foo" incompatible with supertype "Parent"
def foo(self, val: int) -> int: # E: Signature of "foo" incompatible with supertype "Parent" \
# N: Superclass: \
# N: None \
# N: Subclass: \
# N: def foo(self, val: int) -> int
return val
def bar(self, val: str) -> str: # E: Signature of "bar" incompatible with supertype "Parent"
def bar(self, val: str) -> str: # E: Signature of "bar" incompatible with supertype "Parent" \
# N: Superclass: \
# N: None \
# N: Subclass: \
# N: def bar(self, val: str) -> str
return val
def baz(self, val: float) -> float: # E: Signature of "baz" incompatible with supertype "Parent"
def baz(self, val: float) -> float: # E: Signature of "baz" incompatible with supertype "Parent" \
# N: Superclass: \
# N: None \
# N: Subclass: \
# N: def baz(self, val: float) -> float
return val
def foobar(self) -> bool: # E: Signature of "foobar" incompatible with supertype "Parent"
def foobar(self) -> bool: # E: Signature of "foobar" incompatible with supertype "Parent" \
# N: Superclass: \
# N: None \
# N: Subclass: \
# N: def foobar(self) -> bool
return False

x: Parent.foo = lambda: 5
Expand Down Expand Up @@ -7761,7 +7832,11 @@ class B:
...
class C(B):
@property
def foo(self) -> int: # E: Signature of "foo" incompatible with supertype "B"
def foo(self) -> int: # E: Signature of "foo" incompatible with supertype "B" \
# N: Superclass: \
# N: def foo(self) -> int \
# N: Subclass: \
# N: int
...
[builtins fixtures/property.pyi]

Expand All @@ -7771,7 +7846,11 @@ class B:
def foo(self) -> int:
...
class C(B):
def foo(self) -> int: # E: Signature of "foo" incompatible with supertype "B"
def foo(self) -> int: # E: Signature of "foo" incompatible with supertype "B" \
# N: Superclass: \
# N: int \
# N: Subclass: \
# N: def foo(self) -> int
...
[builtins fixtures/property.pyi]

Expand Down
6 changes: 5 additions & 1 deletion test-data/unit/check-dataclasses.test
Original file line number Diff line number Diff line change
Expand Up @@ -198,7 +198,11 @@ class Base:
@dataclass
class BadDerived1(Base):
def foo(self) -> int: # E: Dataclass attribute may only be overridden by another attribute \
# E: Signature of "foo" incompatible with supertype "Base"
# E: Signature of "foo" incompatible with supertype "Base" \
# N: Superclass: \
# N: int \
# N: Subclass: \
# N: def foo(self) -> int
return 1

@dataclass
Expand Down
12 changes: 10 additions & 2 deletions test-data/unit/check-functions.test
Original file line number Diff line number Diff line change
Expand Up @@ -2848,7 +2848,11 @@ class C(A): # inverted order of decorators
class D(A):
@property
@override
def f(self) -> int: pass # E: Signature of "f" incompatible with supertype "A"
def f(self) -> int: pass # E: Signature of "f" incompatible with supertype "A" \
# N: Superclass: \
# N: str \
# N: Subclass: \
# N: int
[builtins fixtures/property.pyi]
[typing fixtures/typing-full.pyi]

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

class D(A):
@override # E: Signature of "f" incompatible with supertype "A"
@override # E: Signature of "f" incompatible with supertype "A" \
# N: Superclass: \
# N: str \
# N: Subclass: \
# N: int
@property
def f(self) -> int: pass

Expand Down
6 changes: 5 additions & 1 deletion test-data/unit/check-plugin-attrs.test
Original file line number Diff line number Diff line change
Expand Up @@ -1877,7 +1877,11 @@ class Sub(Base):
last_name: str

@property
def name(self) -> int: ... # E: Signature of "name" incompatible with supertype "Base"
def name(self) -> int: ... # E: Signature of "name" incompatible with supertype "Base" \
# N: Superclass: \
# N: str \
# N: Subclass: \
# N: int

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