diff --git a/mypy/checkmember.py b/mypy/checkmember.py index 3efc39753627..41ed646d4239 100644 --- a/mypy/checkmember.py +++ b/mypy/checkmember.py @@ -291,6 +291,9 @@ def analyze_type_type_member_access(name: str, # Access member on metaclass object via Type[Type[C]] if isinstance(typ.item.item, Instance): item = typ.item.item.type.metaclass_type + elif isinstance(typ.item, NoneType) and name == '__bool__': + # Special case, `type(None).__bool__` is defined on python3. + return analyze_none_member_access(name, typ.item, mx, from_none_type=True) if item and not mx.is_operator: # See comment above for why operators are skipped result = analyze_class_attribute_access(item, name, mx, override_info) @@ -316,17 +319,30 @@ def analyze_union_member_access(name: str, typ: UnionType, mx: MemberContext) -> return make_simplified_union(results) -def analyze_none_member_access(name: str, typ: NoneType, mx: MemberContext) -> Type: +def analyze_none_member_access( + name: str, typ: NoneType, mx: MemberContext, + *, + from_none_type: bool = False, +) -> Type: is_python_3 = mx.chk.options.python_version[0] >= 3 # In Python 2 "None" has exactly the same attributes as "object". Python 3 adds a single # extra attribute, "__bool__". if is_python_3 and name == '__bool__': literal_false = LiteralType(False, fallback=mx.named_type('builtins.bool')) - return CallableType(arg_types=[], - arg_kinds=[], - arg_names=[], - ret_type=literal_false, - fallback=mx.named_type('builtins.function')) + arg_types = [] + arg_kinds = [] + arg_names = [] + if from_none_type: # This means that `type(None).__bool__` is accessed. + arg_types.append(typ) + arg_kinds.append(ARG_POS) + arg_names.append('self') + return CallableType( + arg_types=arg_types, + arg_kinds=arg_kinds, + arg_names=arg_names, + ret_type=literal_false, + fallback=mx.named_type('builtins.function'), + ) elif mx.chk.should_suppress_optional_error([typ]): return AnyType(TypeOfAny.from_error) else: diff --git a/test-data/unit/check-basic.test b/test-data/unit/check-basic.test index 03dee485c848..d0ca7cf93727 100644 --- a/test-data/unit/check-basic.test +++ b/test-data/unit/check-basic.test @@ -404,6 +404,13 @@ b = none.__bool__() reveal_type(b) # N: Revealed type is "Literal[False]" [builtins fixtures/bool.pyi] +[case testNoneHasBoolPython27] +# flags: --python-version 2.7 +none = None +b = none.__bool__() # E: "None" has no attribute "__bool__" +reveal_type(b) # N: Revealed type is "Any" +[builtins fixtures/bool.pyi] + [case testNoneHasBoolShowNoneErrorsFalse] none = None b = none.__bool__() @@ -413,6 +420,28 @@ reveal_type(b) # N: Revealed type is "Literal[False]" \[mypy] show_none_errors = False +[case testTypeNoneHasBool] +from typing import Type +none: Type[None] +reveal_type(none.__bool__) # N: Revealed type is "def (self: None) -> Literal[False]" +[builtins fixtures/bool.pyi] + +[case testTypeNoneHasBoolPython27] +# flags: --python-version 2.7 +from typing import Type +none = type(None) # type: Type[None] +none.__bool__ # E: "Type[None]" has no attribute "__bool__" +[builtins fixtures/bool.pyi] + +[case testTypeNoneHasBoolShowNoneErrorsFalse] +from typing import Type +none: Type[None] +reveal_type(none.__bool__) # N: Revealed type is "def (self: None) -> Literal[False]" +[builtins fixtures/bool.pyi] +[file mypy.ini] +\[mypy] +show_none_errors = False + [case testAssignmentInvariantNoteForList] from typing import List x: List[int]