diff --git a/mypy/messages.py b/mypy/messages.py index c5756a463894..96ff55fb4a1e 100644 --- a/mypy/messages.py +++ b/mypy/messages.py @@ -2295,6 +2295,20 @@ def report_protocol_problems( parent_error=parent_error, skip_self=class_obj or is_module, ) + # Clarify common confusion around Container[T] and __contains__. + if name == "__contains__" and supertype.type.fullname in { + "typing.Container", + "collections.abc.Container", + }: + self.note( + 'Note: "Container[T]" does not restrict the argument type of ' + '"__contains__" (it must accept "object"). The type parameter "T" ' + "is used for membership checks (e.g. flagging non-overlapping " + "checks under --strict).", + context, + offset=OFFSET, + parent_error=parent_error, + ) self.print_more(conflict_types, context, OFFSET, MAX_ITEMS, code=parent_error.code) # Report flag conflicts (i.e. settable vs read-only etc.) diff --git a/test-data/unit/check-protocols.test b/test-data/unit/check-protocols.test index c5136415edd3..442e2003598e 100644 --- a/test-data/unit/check-protocols.test +++ b/test-data/unit/check-protocols.test @@ -4693,3 +4693,26 @@ class Impl: pass x: Proto = Impl() + +[case testContainerContainsAcceptsStr] +from collections.abc import Container + +class StringContainer: + def __contains__(self, item: str) -> bool: + return True + +c: Container[str] = StringContainer() # E: Incompatible types in assignment (expression has type "StringContainer", variable has type "Container[str]") \ + # N: Following member(s) of "StringContainer" have conflicts: \ + # N: Expected: \ + # N: def __contains__(self, object, /) -> bool \ + # N: Got: \ + # N: def __contains__(self, str, /) -> bool \ + # N: Note: "Container[T]" does not restrict the argument type of "__contains__" (it must accept "object"). The type parameter "T" is used for membership checks (e.g. flagging non-overlapping checks under --strict). +[file collections/__init__.pyi] +[file collections/abc.pyi] +from typing import Protocol, TypeVar + +T_co = TypeVar("T_co", covariant=True) + +class Container(Protocol[T_co]): + def __contains__(self, x: object) -> bool: ...