Skip to content

Commit

Permalink
Fixes #5705 (#8866)
Browse files Browse the repository at this point in the history
Added notes as suggested in the issue description after error from
incompatible arguments in subtype is raised.
  • Loading branch information
ChetanKhanna committed May 22, 2020
1 parent 41927cf commit 05571b1
Show file tree
Hide file tree
Showing 9 changed files with 80 additions and 19 deletions.
8 changes: 8 additions & 0 deletions mypy/messages.py
Expand Up @@ -772,6 +772,14 @@ def argument_incompatible_with_supertype(
.format(arg_num, name, target, arg_type_in_supertype_f),
context,
code=codes.OVERRIDE)
self.note(
'This violates the Liskov substitution principle',
context,
code=codes.OVERRIDE)
self.note(
'See https://mypy.readthedocs.io/en/stable/common_issues.html#incompatible-overrides',
context,
code=codes.OVERRIDE)

if name == "__eq__" and type_name:
multiline_msg = self.comparison_method_example_msg(class_name=type_name)
Expand Down
18 changes: 14 additions & 4 deletions test-data/unit/check-abstract.test
Expand Up @@ -311,7 +311,9 @@ class A(metaclass=ABCMeta):
def g(self, x: int) -> int: pass
class B(A):
def f(self, x: str) -> int: \
# E: Argument 1 of "f" is incompatible with supertype "A"; supertype defines the argument type as "int"
# E: Argument 1 of "f" is incompatible with supertype "A"; supertype defines the argument type as "int" \
# N: This violates the Liskov substitution principle \
# N: See https://mypy.readthedocs.io/en/stable/common_issues.html#incompatible-overrides
pass
def g(self, x: int) -> int: pass
[out]
Expand All @@ -327,7 +329,9 @@ class J(metaclass=ABCMeta):
def g(self, x: str) -> str: pass
class A(I, J):
def f(self, x: str) -> int: pass \
# E: Argument 1 of "f" is incompatible with supertype "I"; supertype defines the argument type as "int"
# E: Argument 1 of "f" is incompatible with supertype "I"; supertype defines the argument type as "int" \
# N: This violates the Liskov substitution principle \
# N: See https://mypy.readthedocs.io/en/stable/common_issues.html#incompatible-overrides
def g(self, x: str) -> int: pass \
# E: Return type "int" of "g" incompatible with return type "str" in supertype "J"
def h(self) -> int: pass # Not related to any base class
Expand All @@ -342,7 +346,9 @@ class J(metaclass=ABCMeta):
class I(J): pass
class A(I):
def f(self, x: str) -> int: pass \
# E: Argument 1 of "f" is incompatible with supertype "J"; supertype defines the argument type as "int"
# E: Argument 1 of "f" is incompatible with supertype "J"; supertype defines the argument type as "int" \
# N: This violates the Liskov substitution principle \
# N: See https://mypy.readthedocs.io/en/stable/common_issues.html#incompatible-overrides
[out]

[case testInvalidOverridingAbstractMethod]
Expand All @@ -353,7 +359,9 @@ class J(metaclass=ABCMeta):
def f(self, x: 'J') -> None: pass
class I(J):
@abstractmethod
def f(self, x: 'I') -> None: pass # E: Argument 1 of "f" is incompatible with supertype "J"; supertype defines the argument type as "J"
def f(self, x: 'I') -> None: pass # E: Argument 1 of "f" is incompatible with supertype "J"; supertype defines the argument type as "J" \
# N: This violates the Liskov substitution principle \
# N: See https://mypy.readthedocs.io/en/stable/common_issues.html#incompatible-overrides
[out]

[case testAbstractClassCoAndContraVariance]
Expand All @@ -375,6 +383,8 @@ class A(I):
pass
[out]
main:11: error: Argument 1 of "h" is incompatible with supertype "I"; supertype defines the argument type as "I"
main:11: note: This violates the Liskov substitution principle
main:11: note: See https://mypy.readthedocs.io/en/stable/common_issues.html#incompatible-overrides
main:11: error: Return type "I" of "h" incompatible with return type "A" in supertype "I"


Expand Down
24 changes: 20 additions & 4 deletions test-data/unit/check-classes.test
Expand Up @@ -282,6 +282,8 @@ class B(A):
def h(self, x: A, y: 'B') -> object: pass # Fail
[out]
main:7: error: Argument 1 of "f" is incompatible with supertype "A"; supertype defines the argument type as "A"
main:7: note: This violates the Liskov substitution principle
main:7: note: See https://mypy.readthedocs.io/en/stable/common_issues.html#incompatible-overrides
main:9: error: Return type "object" of "h" incompatible with return type "A" in supertype "A"

[case testEqMethodsOverridingWithNonObjects]
Expand All @@ -290,6 +292,8 @@ class A:
[builtins fixtures/attr.pyi]
[out]
main:2: error: Argument 1 of "__eq__" is incompatible with supertype "object"; supertype defines the argument type as "object"
main:2: note: This violates the Liskov substitution principle
main:2: note: See https://mypy.readthedocs.io/en/stable/common_issues.html#incompatible-overrides
main:2: note: It is recommended for "__eq__" to work with arbitrary objects, for example:
main:2: note: def __eq__(self, other: object) -> bool:
main:2: note: if not isinstance(other, A):
Expand Down Expand Up @@ -318,6 +322,8 @@ class C(B): # with gap in implementations
pass
[out]
main:6: error: Argument 1 of "f" is incompatible with supertype "A"; supertype defines the argument type as "B"
main:6: note: This violates the Liskov substitution principle
main:6: note: See https://mypy.readthedocs.io/en/stable/common_issues.html#incompatible-overrides

[case testMethodOverridingAcrossDeepInheritanceHierarchy2]
import typing
Expand Down Expand Up @@ -459,7 +465,9 @@ class B(A):
@staticmethod
def f(x: int, y: str) -> None: pass
@staticmethod
def g(x: str, y: str) -> None: pass # E: Argument 1 of "g" is incompatible with supertype "A"; supertype defines the argument type as "int"
def g(x: str, y: str) -> None: pass # E: Argument 1 of "g" is incompatible with supertype "A"; supertype defines the argument type as "int" \
# N: This violates the Liskov substitution principle \
# N: See https://mypy.readthedocs.io/en/stable/common_issues.html#incompatible-overrides
[builtins fixtures/classmethod.pyi]

[case testOverrideClassMethodWithClassMethod]
Expand All @@ -473,7 +481,9 @@ class B(A):
@classmethod
def f(cls, x: int, y: str) -> None: pass
@classmethod
def g(cls, x: str, y: str) -> None: pass # E: Argument 1 of "g" is incompatible with supertype "A"; supertype defines the argument type as "int"
def g(cls, x: str, y: str) -> None: pass # E: Argument 1 of "g" is incompatible with supertype "A"; supertype defines the argument type as "int" \
# N: This violates the Liskov substitution principle \
# N: See https://mypy.readthedocs.io/en/stable/common_issues.html#incompatible-overrides
[builtins fixtures/classmethod.pyi]

[case testOverrideClassMethodWithStaticMethod]
Expand All @@ -489,7 +499,9 @@ class B(A):
@staticmethod
def f(x: int) -> None: pass
@staticmethod
def g(x: str) -> int: pass # E: Argument 1 of "g" is incompatible with supertype "A"; supertype defines the argument type as "int"
def g(x: str) -> int: pass # E: Argument 1 of "g" is incompatible with supertype "A"; supertype defines the argument type as "int" \
# N: This violates the Liskov substitution principle \
# N: See https://mypy.readthedocs.io/en/stable/common_issues.html#incompatible-overrides
@staticmethod
def h() -> int: pass
[builtins fixtures/classmethod.pyi]
Expand All @@ -507,7 +519,9 @@ class B(A):
@classmethod
def f(cls, x: int) -> None: pass
@classmethod
def g(cls, x: int) -> int: pass # E: Argument 1 of "g" is incompatible with supertype "A"; supertype defines the argument type as "str"
def g(cls, x: int) -> int: pass # E: Argument 1 of "g" is incompatible with supertype "A"; supertype defines the argument type as "str" \
# N: This violates the Liskov substitution principle \
# N: See https://mypy.readthedocs.io/en/stable/common_issues.html#incompatible-overrides
@classmethod
def h(cls) -> int: pass
[builtins fixtures/classmethod.pyi]
Expand Down Expand Up @@ -2478,6 +2492,8 @@ class D(A):
[out]
main:6: error: Return type "A" of "__iadd__" incompatible with return type "B" in "__add__" of supertype "A"
main:8: error: Argument 1 of "__iadd__" is incompatible with "__add__" of supertype "A"; supertype defines the argument type as "A"
main:8: note: This violates the Liskov substitution principle
main:8: note: See https://mypy.readthedocs.io/en/stable/common_issues.html#incompatible-overrides
main:8: error: Signatures of "__iadd__" and "__add__" are incompatible

[case testGetattribute]
Expand Down
4 changes: 3 additions & 1 deletion test-data/unit/check-columns.test
Expand Up @@ -262,7 +262,9 @@ if int():
class A:
def f(self, x: int) -> None: pass
class B(A):
def f(self, x: str) -> None: pass # E:5: Argument 1 of "f" is incompatible with supertype "A"; supertype defines the argument type as "int"
def f(self, x: str) -> None: pass # E:5: Argument 1 of "f" is incompatible with supertype "A"; supertype defines the argument type as "int" \
# N:5: This violates the Liskov substitution principle \
# N:5: See https://mypy.readthedocs.io/en/stable/common_issues.html#incompatible-overrides
class C(A):
def f(self, x: int) -> int: pass # E:5: Return type "int" of "f" incompatible with return type "None" in supertype "A"
class D(A):
Expand Down
4 changes: 3 additions & 1 deletion test-data/unit/check-dynamic-typing.test
Expand Up @@ -704,7 +704,9 @@ class C:
class B(C):
def f(self, a): pass
class A(B):
def f(self, a: 'D') -> None: # E: Argument 1 of "f" is incompatible with supertype "C"; supertype defines the argument type as "A"
def f(self, a: 'D') -> None: # E: Argument 1 of "f" is incompatible with supertype "C"; supertype defines the argument type as "A" \
# N: This violates the Liskov substitution principle \
# N: See https://mypy.readthedocs.io/en/stable/common_issues.html#incompatible-overrides
pass
class D: pass
[out]
Expand Down
4 changes: 3 additions & 1 deletion test-data/unit/check-errorcodes.test
Expand Up @@ -279,7 +279,9 @@ class D:
def f(self, x: int) -> int:
return 0
class E(D):
def f(self, x: str) -> int: # E: Argument 1 of "f" is incompatible with supertype "D"; supertype defines the argument type as "int" [override]
def f(self, x: str) -> int: # E: Argument 1 of "f" is incompatible with supertype "D"; supertype defines the argument type as "int" [override] \
# N: This violates the Liskov substitution principle \
# N: See https://mypy.readthedocs.io/en/stable/common_issues.html#incompatible-overrides
return 0

class O:
Expand Down
5 changes: 4 additions & 1 deletion test-data/unit/check-functions.test
Expand Up @@ -46,7 +46,10 @@ class A(object):
def f(self, a: int, b: str) -> None: pass

class B(A):
def f(self, b: str, a: int) -> None: pass # E: Argument 1 of "f" is incompatible with supertype "A"; supertype defines the argument type as "int" # E: Argument 2 of "f" is incompatible with supertype "A"; supertype defines the argument type as "str"
def f(self, b: str, a: int) -> None: pass # E: Argument 1 of "f" is incompatible with supertype "A"; supertype defines the argument type as "int" \
# N: This violates the Liskov substitution principle \
# N: See https://mypy.readthedocs.io/en/stable/common_issues.html#incompatible-overrides \
# E: Argument 2 of "f" is incompatible with supertype "A"; supertype defines the argument type as "str"

class C(A):
def f(self, foo: int, bar: str) -> None: pass
Expand Down
30 changes: 23 additions & 7 deletions test-data/unit/check-generic-subtyping.test
Expand Up @@ -189,7 +189,9 @@ class C: pass
class D: pass
class A(B[C]):
def f(self, a: D) -> None: pass \
# E: Argument 1 of "f" is incompatible with supertype "B"; supertype defines the argument type as "C"
# E: Argument 1 of "f" is incompatible with supertype "B"; supertype defines the argument type as "C" \
# N: This violates the Liskov substitution principle \
# N: See https://mypy.readthedocs.io/en/stable/common_issues.html#incompatible-overrides
def g(self, a: C) -> None: pass
[out]

Expand All @@ -202,7 +204,9 @@ class B:
def g(self, a: C) -> None: pass
class A(B, Generic[T]):
def f(self, a: T) -> None: pass \
# E: Argument 1 of "f" is incompatible with supertype "B"; supertype defines the argument type as "C"
# E: Argument 1 of "f" is incompatible with supertype "B"; supertype defines the argument type as "C" \
# N: This violates the Liskov substitution principle \
# N: See https://mypy.readthedocs.io/en/stable/common_issues.html#incompatible-overrides
def g(self, a: 'C') -> None: pass
[out]

Expand All @@ -215,7 +219,9 @@ class B(Generic[T]):
def g(self, a: T) -> None: pass
class A(B[S], Generic[T, S]):
def f(self, a: T) -> None: pass \
# E: Argument 1 of "f" is incompatible with supertype "B"; supertype defines the argument type as "S"
# E: Argument 1 of "f" is incompatible with supertype "B"; supertype defines the argument type as "S" \
# N: This violates the Liskov substitution principle \
# N: See https://mypy.readthedocs.io/en/stable/common_issues.html#incompatible-overrides
def g(self, a: S) -> None: pass
[out]

Expand All @@ -233,7 +239,9 @@ class C(Generic[T, U, V]):
class B(C[D, D, T], Generic[T]): pass
class A(B[S], Generic[T, S]):
def f(self, a: T) -> None: pass \
# E: Argument 1 of "f" is incompatible with supertype "C"; supertype defines the argument type as "S"
# E: Argument 1 of "f" is incompatible with supertype "C"; supertype defines the argument type as "S" \
# N: This violates the Liskov substitution principle \
# N: See https://mypy.readthedocs.io/en/stable/common_issues.html#incompatible-overrides
def g(self, a: S) -> None: pass
[out]

Expand Down Expand Up @@ -283,6 +291,8 @@ class D(A):
def f(self, x: T1, y: S) -> None: pass # TODO: This error could be more specific.
[out]
main:12: error: Argument 2 of "f" is incompatible with supertype "A"; supertype defines the argument type as "S"
main:12: note: This violates the Liskov substitution principle
main:12: note: See https://mypy.readthedocs.io/en/stable/common_issues.html#incompatible-overrides
main:14: error: Signature of "f" incompatible with supertype "A"


Expand Down Expand Up @@ -538,7 +548,9 @@ class I(Generic[T]):
def g(self, a: T) -> None: pass
class A(I[C]):
def f(self, a: 'D') -> None: pass \
# E: Argument 1 of "f" is incompatible with supertype "I"; supertype defines the argument type as "C"
# E: Argument 1 of "f" is incompatible with supertype "I"; supertype defines the argument type as "C" \
# N: This violates the Liskov substitution principle \
# N: See https://mypy.readthedocs.io/en/stable/common_issues.html#incompatible-overrides
def g(self, a: 'C') -> None: pass
class C: pass
class D: pass
Expand Down Expand Up @@ -570,7 +582,9 @@ class B(I[C]):
def g(self, a: 'C', b: Any) -> None: pass
class A(B):
def g(self, a: 'C', b: 'C') -> None: pass \
# E: Argument 2 of "g" is incompatible with supertype "I"; supertype defines the argument type as "D"
# E: Argument 2 of "g" is incompatible with supertype "I"; supertype defines the argument type as "D" \
# N: This violates the Liskov substitution principle \
# N: See https://mypy.readthedocs.io/en/stable/common_issues.html#incompatible-overrides
def f(self, a: 'C', b: 'C') -> None: pass
class C: pass
class D: pass
Expand All @@ -587,7 +601,9 @@ class B(I[C]):
def f(self, a: 'C', b: Any) -> None: pass
class A(B):
def f(self, a: 'C', b: 'D') -> None: pass \
# E: Argument 2 of "f" is incompatible with supertype "I"; supertype defines the argument type as "C"
# E: Argument 2 of "f" is incompatible with supertype "I"; supertype defines the argument type as "C" \
# N: This violates the Liskov substitution principle \
# N: See https://mypy.readthedocs.io/en/stable/common_issues.html#incompatible-overrides
class C: pass
class D: pass
[out]
Expand Down
2 changes: 2 additions & 0 deletions test-data/unit/check-multiple-inheritance.test
Expand Up @@ -539,6 +539,8 @@ class IntFlag(int, Flag):
def __or__(self: _T, other: str) -> _T: ...
[out]
main:8: error: Argument 1 of "__or__" is incompatible with supertype "Flag"; supertype defines the argument type as "IntFlag"
main:8: note: This violates the Liskov substitution principle
main:8: note: See https://mypy.readthedocs.io/en/stable/common_issues.html#incompatible-overrides

[case testMultipleInheritance_MethodDefinitionsCompatibleNoOverride]
from typing import TypeVar, Union
Expand Down

0 comments on commit 05571b1

Please sign in to comment.