Skip to content
Merged
2 changes: 1 addition & 1 deletion mypy/checker.py
Original file line number Diff line number Diff line change
Expand Up @@ -7746,7 +7746,7 @@ def warn_deprecated_overload_item(
if isinstance(item, Decorator) and isinstance(
candidate := item.func.type, CallableType
):
if selftype is not None:
if (selftype is not None) and (not node.is_static):
candidate = bind_self(candidate, selftype)
if candidate == target:
self.warn_deprecated(item.func, context)
Expand Down
12 changes: 5 additions & 7 deletions mypy/checkexpr.py
Original file line number Diff line number Diff line change
Expand Up @@ -1486,13 +1486,11 @@ def check_call_expr_with_callee_type(
proper_callee = get_proper_type(callee_type)
if isinstance(e.callee, (NameExpr, MemberExpr)):
node = e.callee.node
if (
(node is None)
and (member is not None)
and isinstance(object_type, Instance)
and ((symbol := object_type.type.names.get(member)) is not None)
):
node = symbol.node
if (node is None) and (member is not None) and isinstance(object_type, Instance):
for base in object_type.type.mro:
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: I think we can use object_type.type.get(member)

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You're right, I adjusted it.

if (symbol := base.names.get(member)) is not None:
node = symbol.node
break
self.chk.warn_deprecated_overload_item(
node, e, target=callee_type, selftype=object_type
)
Expand Down
128 changes: 127 additions & 1 deletion test-data/unit/check-deprecated.test
Original file line number Diff line number Diff line change
Expand Up @@ -377,7 +377,7 @@ for i in a: # E: function __main__.A.__iter__ is deprecated: no iteration
[builtins fixtures/tuple.pyi]


[case testDeprecatedOverloadedMethods]
[case testDeprecatedOverloadedInstanceMethods]
# flags: --enable-error-code=deprecated

from typing import Iterator, Union
Expand Down Expand Up @@ -405,6 +405,8 @@ class A:
@deprecated("use `h2` instead")
def h(self, v: Union[int, str]) -> A: ...

class B(A): ...

a = A()
a.f(1) # E: overload def (self: __main__.A, v: builtins.int) of function __main__.A.f is deprecated: pass `str` instead
a.f("x")
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would also test this case:

Suggested change
a.f("x")
a.f("x")
int_or_str: Union[int, str]
a.f(int_or_str)

It should not raise if all is good.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It does not raise a warning, but why do you think it should not?

(There is not even a warning for a.h, where the implementation is marked as deprecated, which is inconsistent with how functions are handled.)

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(There is not even a warning for a.h, where the implementation is marked as deprecated, which is inconsistent with how functions are handled.)

I made it consistent in c95f936. However, I added the in my opinion missing warnings to the test case, to prevent us from merging this too early by accident. I am curious to hear why you think the current behaviour is correct. (regarding int_or_str).

Expand All @@ -413,9 +415,133 @@ a.g("x") # E: overload def (self: __main__.A, v: builtins.str) of function __ma
a.h(1) # E: function __main__.A.h is deprecated: use `h2` instead
a.h("x") # E: function __main__.A.h is deprecated: use `h2` instead

b = B()
b.f(1) # E: overload def (self: __main__.A, v: builtins.int) of function __main__.A.f is deprecated: pass `str` instead
b.f("x")
b.g(1)
b.g("x") # E: overload def (self: __main__.A, v: builtins.str) of function __main__.A.g is deprecated: pass `int` instead
b.h(1) # E: function __main__.A.h is deprecated: use `h2` instead
b.h("x") # E: function __main__.A.h is deprecated: use `h2` instead

[builtins fixtures/tuple.pyi]


[case testDeprecatedOverloadedClassMethods]
# flags: --enable-error-code=deprecated

from typing import Iterator, Union
from typing_extensions import deprecated, overload

class A:
@overload
@classmethod
@deprecated("pass `str` instead")
def f(self, v: int) -> None: ...
@overload
@classmethod
def f(self, v: str) -> None: ...
@classmethod
def f(self, v: Union[int, str]) -> None: ...

@overload
@classmethod
def g(self, v: int) -> None: ...
@overload
@classmethod
@deprecated("pass `int` instead")
def g(self, v: str) -> None: ...
@classmethod
def g(self, v: Union[int, str]) -> None: ...

@overload
@classmethod
def h(self, v: int) -> A: ...
@overload
@classmethod
def h(self, v: str) -> A: ...
@deprecated("use `h2` instead")
@classmethod
def h(self, v: Union[int, str]) -> A: ...

class B(A): ...

a = A()
a.f(1) # E: overload def (self: type[__main__.A], v: builtins.int) of function __main__.A.f is deprecated: pass `str` instead
a.f("x")
a.g(1)
a.g("x") # E: overload def (self: type[__main__.A], v: builtins.str) of function __main__.A.g is deprecated: pass `int` instead
a.h(1) # E: function __main__.A.h is deprecated: use `h2` instead
a.h("x") # E: function __main__.A.h is deprecated: use `h2` instead

b = B()
b.f(1) # E: overload def (self: type[__main__.A], v: builtins.int) of function __main__.A.f is deprecated: pass `str` instead
b.f("x")
b.g(1)
b.g("x") # E: overload def (self: type[__main__.A], v: builtins.str) of function __main__.A.g is deprecated: pass `int` instead
b.h(1) # E: function __main__.A.h is deprecated: use `h2` instead
b.h("x") # E: function __main__.A.h is deprecated: use `h2` instead

[builtins fixtures/tuple.pyi]


[case testDeprecatedOverloadedStaticMethods]
# flags: --enable-error-code=deprecated

from typing import Iterator, Union
from typing_extensions import deprecated, overload

class A:
@overload
@staticmethod
@deprecated("pass `str` instead")
def f(v: int) -> None: ...
@overload
@staticmethod
def f(v: str) -> None: ...
@staticmethod
def f(v: Union[int, str]) -> None: ...

@overload
@staticmethod
def g(v: int) -> None: ...
@overload
@staticmethod
@deprecated("pass `int` instead")
def g(v: str) -> None: ...
@staticmethod
def g(v: Union[int, str]) -> None: ...

@overload
@staticmethod
def h(v: int) -> A: ...
@overload
@staticmethod
def h(v: str) -> A: ...
@deprecated("use `h2` instead")
@staticmethod
def h(v: Union[int, str]) -> A: ...

class B(A): ...

a = A()
a.f(1) # E: overload def (v: builtins.int) of function __main__.A.f is deprecated: pass `str` instead
a.f("x")
a.g(1)
a.g("x") # E: overload def (v: builtins.str) of function __main__.A.g is deprecated: pass `int` instead
a.h(1) # E: function __main__.A.h is deprecated: use `h2` instead
a.h("x") # E: function __main__.A.h is deprecated: use `h2` instead

b = B()
b.f(1) # E: overload def (v: builtins.int) of function __main__.A.f is deprecated: pass `str` instead
b.f("x")
b.g(1)
b.g("x") # E: overload def (v: builtins.str) of function __main__.A.g is deprecated: pass `int` instead
b.h(1) # E: function __main__.A.h is deprecated: use `h2` instead
b.h("x") # E: function __main__.A.h is deprecated: use `h2` instead

[builtins fixtures/classmethod.pyi]


[case testDeprecatedOverloadedSpecialMethods]
# flags: --enable-error-code=deprecated

Expand Down
Loading