diff --git a/mypy/messages.py b/mypy/messages.py index 2e07d7f63498..8a07121bd47b 100644 --- a/mypy/messages.py +++ b/mypy/messages.py @@ -540,6 +540,13 @@ def has_no_attr( context, code=codes.UNION_ATTR, ) + + self.note( + "Use \"if is not None\" to ensure you have no 'None' values", + context, + code=codes.UNION_ATTR, + ) + return codes.UNION_ATTR elif isinstance(original_type, TypeVarType): bound = get_proper_type(original_type.upper_bound) diff --git a/test-data/unit/check-classes.test b/test-data/unit/check-classes.test index 38afc5cd4301..af2fe34ff8ce 100644 --- a/test-data/unit/check-classes.test +++ b/test-data/unit/check-classes.test @@ -3646,7 +3646,7 @@ def process(cls: Type[Union[BasicUser, ProUser]]): obj = cls() cls.bar(obj) cls.mro() # Defined in class type - cls.error # E: Item "type" of "Union[Type[BasicUser], Type[ProUser]]" has no attribute "error" + cls.error # E: Item "type" of "Union[Type[BasicUser], Type[ProUser]]" has no attribute "error", try using "if is not \'None' to avoid this error [builtins fixtures/classmethod.pyi] [out] diff --git a/test-data/unit/check-errorcodes.test b/test-data/unit/check-errorcodes.test index 21112b7d85a2..d8477e1b25bb 100644 --- a/test-data/unit/check-errorcodes.test +++ b/test-data/unit/check-errorcodes.test @@ -357,7 +357,7 @@ class A: class B: y: str a: Union[A, B] -a.x # E: Item "B" of "Union[A, B]" has no attribute "x" [union-attr] +a.x # E: Item "B" of "Union[A, B]" has no attribute "x", use "if not \'None' to avoid this issue [union-attr] [case testErrorCodeFunctionHasNoAnnotation] # flags: --disallow-untyped-defs diff --git a/test-data/unit/check-generics.test b/test-data/unit/check-generics.test index 767b55efcac2..757a409df77a 100644 --- a/test-data/unit/check-generics.test +++ b/test-data/unit/check-generics.test @@ -818,7 +818,7 @@ if not isinstance(s, str): z = None # type: TNode # Same as TNode[Any] z.x -z.foo() # E: Item "Node[int]" of "Union[Any, Node[int]]" has no attribute "foo" +z.foo() # E: Item "Node[int]" of "Union[Any, Node[int]]" has no attribute "foo," use "if not \'None' to prevent this issue [builtins fixtures/isinstance.pyi] diff --git a/test-data/unit/check-inference.test b/test-data/unit/check-inference.test index 42b5a05ab39a..bd980739b5ed 100644 --- a/test-data/unit/check-inference.test +++ b/test-data/unit/check-inference.test @@ -2864,7 +2864,7 @@ class C: a = None # E: Need type annotation for "a" (hint: "a: Optional[] = ...") def f(self, x) -> None: - C.a.y # E: Item "None" of "Optional[Any]" has no attribute "y" + C.a.y # E: Item "None" of "Optional[Any]" has no attribute "y," use if is not None to prevent this issue [case testLocalPartialTypesAccessPartialNoneAttribute2] # flags: --local-partial-types diff --git a/test-data/unit/check-isinstance.test b/test-data/unit/check-isinstance.test index 058db1ea8197..46a86da92df9 100644 --- a/test-data/unit/check-isinstance.test +++ b/test-data/unit/check-isinstance.test @@ -578,7 +578,7 @@ v = A() # type: Union[A, B, C] if isinstance(v, (B, C)): v.method2(123) - v.method3('xyz') # E: Item "B" of "Union[B, C]" has no attribute "method3" + v.method3('xyz') # E: Item "B" of "Union[B, C]" has no attribute "method3," use if is not None to prevent this issue [builtins fixtures/isinstance.pyi] [case testIsinstanceNeverWidens] @@ -1007,7 +1007,7 @@ def bar() -> None: if isinstance(x, int): x + 1 else: - x.a # E: Item "str" of "Union[str, A]" has no attribute "a" + x.a # E: Item "str" of "Union[str, A]" has no attribute "a," use if is not None to prevent this issue x = 'a' [builtins fixtures/isinstancelist.pyi] @@ -2138,7 +2138,7 @@ reveal_type(x) # N: Revealed type is "Any" from typing import Union from foo import A # type: ignore def f(x: Union[A, str]) -> None: - x.method_only_in_a() # E: Item "str" of "Union[Any, str]" has no attribute "method_only_in_a" + x.method_only_in_a() # E: Item "str" of "Union[Any, str]" has no attribute "method_only_in_a," use if is not None to prevent this issue if isinstance(x, A): x.method_only_in_a() [builtins fixtures/isinstance.pyi] diff --git a/test-data/unit/check-narrowing.test b/test-data/unit/check-narrowing.test index dc2cfd46d9ad..5899f7ef1631 100644 --- a/test-data/unit/check-narrowing.test +++ b/test-data/unit/check-narrowing.test @@ -417,7 +417,7 @@ else: reveal_type(ok_mixture) # N: Revealed type is "Tuple[Literal[__main__.Key.C], fallback=__main__.KeyedNamedTuple]" impossible_mixture: Union[KeyedObject, KeyedTypedDict] -if impossible_mixture.key is Key.A: # E: Item "KeyedTypedDict" of "Union[KeyedObject, KeyedTypedDict]" has no attribute "key" +if impossible_mixture.key is Key.A: # E: Item "KeyedTypedDict" of "Union[KeyedObject, KeyedTypedDict]" has no attribute "key," use if is not None to prevent this issue reveal_type(impossible_mixture) # N: Revealed type is "Union[__main__.KeyedObject, TypedDict('__main__.KeyedTypedDict', {'key': Literal[__main__.Key.B]})]" else: reveal_type(impossible_mixture) # N: Revealed type is "Union[__main__.KeyedObject, TypedDict('__main__.KeyedTypedDict', {'key': Literal[__main__.Key.B]})]" diff --git a/test-data/unit/check-optional.test b/test-data/unit/check-optional.test index 5ed4c15f470e..f00ee65df36e 100644 --- a/test-data/unit/check-optional.test +++ b/test-data/unit/check-optional.test @@ -567,7 +567,7 @@ if int(): if int(): x = f('x') # E: Argument 1 to "f" has incompatible type "str"; expected "int" -x.x = 1 # E: Item "None" of "Optional[Node[int]]" has no attribute "x" +x.x = 1 # E: Item "None" of "Optional[Node[int]]" has no attribute "x," use if is not None to prevent this issue if x is not None: x.x = 1 # OK here @@ -620,7 +620,7 @@ A = None # type: Any class C(A): pass x = None # type: Optional[C] -x.foo() # E: Item "None" of "Optional[C]" has no attribute "foo" +x.foo() # E: Item "None" of "Optional[C]" has no attribute "foo," use if is not None to prevent this issue [case testIsinstanceAndOptionalAndAnyBase] from typing import Any, Optional diff --git a/test-data/unit/check-overloading.test b/test-data/unit/check-overloading.test index 243568c54253..754ca166a578 100644 --- a/test-data/unit/check-overloading.test +++ b/test-data/unit/check-overloading.test @@ -5166,7 +5166,7 @@ def foo() -> None: ... def foo(iterable: Iterable[_T]) -> None: ... def foo(iterable = None) -> None: pass -foo(bar('lol').foo()) # E: Item "int" of "Union[A, int]" has no attribute "foo" \ +foo(bar('lol').foo()) # E: Item "int" of "Union[A, int]" has no attribute "foo," use if is not None to prevent this issue \ # E: Argument 1 to "bar" has incompatible type "str"; expected "int" diff --git a/test-data/unit/check-unions.test b/test-data/unit/check-unions.test index 8e92b6a91e8a..2b77fc8408d0 100644 --- a/test-data/unit/check-unions.test +++ b/test-data/unit/check-unions.test @@ -64,9 +64,9 @@ z: str if int(): y = w.y -v.y # E: Item "C" of "Union[C, D]" has no attribute "y" \ +v.y # E: Item "C" of "Union[C, D]" has no attribute "y," use if is not None to prevent this issue \ # E: Item "D" of "Union[C, D]" has no attribute "y" -u.y # E: Item "C" of "Union[A, C, D]" has no attribute "y" \ +u.y # E: Item "C" of "Union[A, C, D]" has no attribute "y," use if is not None to prevent this issue \ # E: Item "D" of "Union[A, C, D]" has no attribute "y" if int(): z = w.y # E: Incompatible types in assignment (expression has type "int", variable has type "str")