From 5f9b330155c016afe97bb68229f5bd0dc73be1d8 Mon Sep 17 00:00:00 2001 From: hauntsaninja Date: Tue, 31 Dec 2024 15:48:15 -0800 Subject: [PATCH] Dedicated error code for explicit any --- docs/source/error_code_list.rst | 16 +++++ docs/source/error_code_list2.rst | 21 ++++--- mypy/errorcodes.py | 4 ++ mypy/messages.py | 2 +- test-data/unit/check-flags.test | 66 ++++++++++---------- test-data/unit/fixtures/typing-typeddict.pyi | 3 +- 6 files changed, 66 insertions(+), 46 deletions(-) diff --git a/docs/source/error_code_list.rst b/docs/source/error_code_list.rst index 73171131bc8d..49cb8a0c06c1 100644 --- a/docs/source/error_code_list.rst +++ b/docs/source/error_code_list.rst @@ -1241,6 +1241,22 @@ Consider this example: `PEP 705 `_ specifies how ``ReadOnly`` special form works for ``TypedDict`` objects. +.. _code-narrowed-type-not-subtype: + +Check that ``TypeIs`` narrows types [narrowed-type-not-subtype] +--------------------------------------------------------------- + +:pep:`742` requires that when ``TypeIs`` is used, the narrowed +type must be a subtype of the original type:: + + from typing_extensions import TypeIs + + def f(x: int) -> TypeIs[str]: # Error, str is not a subtype of int + ... + + def g(x: object) -> TypeIs[str]: # OK + ... + .. _code-misc: Miscellaneous checks [misc] diff --git a/docs/source/error_code_list2.rst b/docs/source/error_code_list2.rst index df8b696745fc..508574b36e09 100644 --- a/docs/source/error_code_list2.rst +++ b/docs/source/error_code_list2.rst @@ -594,18 +594,19 @@ Correct usage: When this code is enabled, using ``reveal_locals`` is always an error, because there's no way one can import it. -.. _code-narrowed-type-not-subtype: -Check that ``TypeIs`` narrows types [narrowed-type-not-subtype] ---------------------------------------------------------------- +.. _code-explicit-any: -:pep:`742` requires that when ``TypeIs`` is used, the narrowed -type must be a subtype of the original type:: +Check that explicit Any type annotations are not allowed [explicit-any] +----------------------------------------------------------------------- - from typing_extensions import TypeIs +If you use :option:`--disallow-any-explicit `, mypy generates an error +if you use an explicit ``Any`` type annotation. - def f(x: int) -> TypeIs[str]: # Error, str is not a subtype of int - ... +Example: - def g(x: object) -> TypeIs[str]: # OK - ... +.. code-block:: python + + # mypy: disallow-any-explicit + from typing import Any + x: Any = 1 # Error: Explicit "Any" type annotation [explicit-any] diff --git a/mypy/errorcodes.py b/mypy/errorcodes.py index 5736be5c143e..8f650aa30605 100644 --- a/mypy/errorcodes.py +++ b/mypy/errorcodes.py @@ -304,6 +304,10 @@ def __hash__(self) -> int: "General", ) +EXPLICIT_ANY: Final = ErrorCode( + "explicit-any", "Warn about explicit Any type annotations", "General" +) + DEPRECATED: Final = ErrorCode( "deprecated", "Warn when importing or using deprecated (overloaded) functions, methods or classes", diff --git a/mypy/messages.py b/mypy/messages.py index 5fa4dc0c05ad..cdd8f3187d63 100644 --- a/mypy/messages.py +++ b/mypy/messages.py @@ -1821,7 +1821,7 @@ def need_annotation_for_var( ) def explicit_any(self, ctx: Context) -> None: - self.fail('Explicit "Any" is not allowed', ctx) + self.fail('Explicit "Any" is not allowed', ctx, code=codes.EXPLICIT_ANY) def unsupported_target_for_star_typeddict(self, typ: Type, ctx: Context) -> None: self.fail( diff --git a/test-data/unit/check-flags.test b/test-data/unit/check-flags.test index 86a65d85a8b2..6dceb28b5cb6 100644 --- a/test-data/unit/check-flags.test +++ b/test-data/unit/check-flags.test @@ -1829,51 +1829,51 @@ x: A # E:4: Missing type parameters for generic type "A" [builtins fixtures/list.pyi] [case testDisallowAnyExplicitDefSignature] -# flags: --disallow-any-explicit +# flags: --disallow-any-explicit --show-error-codes from typing import Any, List -def f(x: Any) -> None: # E: Explicit "Any" is not allowed +def f(x: Any) -> None: # E: Explicit "Any" is not allowed [explicit-any] pass -def g() -> Any: # E: Explicit "Any" is not allowed +def g() -> Any: # E: Explicit "Any" is not allowed [explicit-any] pass -def h() -> List[Any]: # E: Explicit "Any" is not allowed +def h() -> List[Any]: # E: Explicit "Any" is not allowed [explicit-any] pass [builtins fixtures/list.pyi] [case testDisallowAnyExplicitVarDeclaration] -# flags: --disallow-any-explicit +# flags: --disallow-any-explicit --show-error-codes from typing import Any -v: Any = '' # E: Explicit "Any" is not allowed -w = '' # type: Any # E: Explicit "Any" is not allowed +v: Any = '' # E: Explicit "Any" is not allowed [explicit-any] +w = '' # type: Any # E: Explicit "Any" is not allowed [explicit-any] class X: - y = '' # type: Any # E: Explicit "Any" is not allowed + y = '' # type: Any # E: Explicit "Any" is not allowed [explicit-any] [case testDisallowAnyExplicitGenericVarDeclaration] -# flags: --disallow-any-explicit +# flags: --disallow-any-explicit --show-error-codes from typing import Any, List -v: List[Any] = [] # E: Explicit "Any" is not allowed +v: List[Any] = [] # E: Explicit "Any" is not allowed [explicit-any] [builtins fixtures/list.pyi] [case testDisallowAnyExplicitInheritance] -# flags: --disallow-any-explicit +# flags: --disallow-any-explicit --show-error-codes from typing import Any, List -class C(Any): # E: Explicit "Any" is not allowed +class C(Any): # E: Explicit "Any" is not allowed [explicit-any] pass -class D(List[Any]): # E: Explicit "Any" is not allowed +class D(List[Any]): # E: Explicit "Any" is not allowed [explicit-any] pass [builtins fixtures/list.pyi] [case testDisallowAnyExplicitAlias] -# flags: --disallow-any-explicit +# flags: --disallow-any-explicit --show-error-codes from typing import Any, List -X = Any # E: Explicit "Any" is not allowed -Y = List[Any] # E: Explicit "Any" is not allowed +X = Any # E: Explicit "Any" is not allowed [explicit-any] +Y = List[Any] # E: Explicit "Any" is not allowed [explicit-any] def foo(x: X) -> Y: # no error x.nonexistent() # no error @@ -1881,68 +1881,68 @@ def foo(x: X) -> Y: # no error [builtins fixtures/list.pyi] [case testDisallowAnyExplicitGenericAlias] -# flags: --disallow-any-explicit +# flags: --disallow-any-explicit --show-error-codes from typing import Any, TypeVar, Tuple T = TypeVar('T') -TupleAny = Tuple[Any, T] # E: Explicit "Any" is not allowed +TupleAny = Tuple[Any, T] # E: Explicit "Any" is not allowed [explicit-any] def foo(x: TupleAny[str]) -> None: # no error pass -def goo(x: TupleAny[Any]) -> None: # E: Explicit "Any" is not allowed +def goo(x: TupleAny[Any]) -> None: # E: Explicit "Any" is not allowed [explicit-any] pass [builtins fixtures/tuple.pyi] [case testDisallowAnyExplicitCast] -# flags: --disallow-any-explicit +# flags: --disallow-any-explicit --show-error-codes from typing import Any, List, cast x = 1 -y = cast(Any, x) # E: Explicit "Any" is not allowed -z = cast(List[Any], x) # E: Explicit "Any" is not allowed +y = cast(Any, x) # E: Explicit "Any" is not allowed [explicit-any] +z = cast(List[Any], x) # E: Explicit "Any" is not allowed [explicit-any] [builtins fixtures/list.pyi] [case testDisallowAnyExplicitNamedTuple] -# flags: --disallow-any-explicit +# flags: --disallow-any-explicit --show-error-codes from typing import Any, List, NamedTuple -Point = NamedTuple('Point', [('x', List[Any]), ('y', Any)]) # E: Explicit "Any" is not allowed +Point = NamedTuple('Point', [('x', List[Any]), ('y', Any)]) # E: Explicit "Any" is not allowed [explicit-any] [builtins fixtures/list.pyi] [case testDisallowAnyExplicitTypeVarConstraint] -# flags: --disallow-any-explicit +# flags: --disallow-any-explicit --show-error-codes from typing import Any, List, TypeVar -T = TypeVar('T', Any, List[Any]) # E: Explicit "Any" is not allowed +T = TypeVar('T', Any, List[Any]) # E: Explicit "Any" is not allowed [explicit-any] [builtins fixtures/list.pyi] [case testDisallowAnyExplicitNewType] -# flags: --disallow-any-explicit +# flags: --disallow-any-explicit --show-error-codes from typing import Any, List, NewType # this error does not come from `--disallow-any-explicit` flag -Baz = NewType('Baz', Any) # E: Argument 2 to NewType(...) must be subclassable (got "Any") -Bar = NewType('Bar', List[Any]) # E: Explicit "Any" is not allowed +Baz = NewType('Baz', Any) # E: Argument 2 to NewType(...) must be subclassable (got "Any") [valid-newtype] +Bar = NewType('Bar', List[Any]) # E: Explicit "Any" is not allowed [explicit-any] [builtins fixtures/list.pyi] [case testDisallowAnyExplicitTypedDictSimple] -# flags: --disallow-any-explicit +# flags: --disallow-any-explicit --show-error-codes from mypy_extensions import TypedDict from typing import Any -M = TypedDict('M', {'x': str, 'y': Any}) # E: Explicit "Any" is not allowed +M = TypedDict('M', {'x': str, 'y': Any}) # E: Explicit "Any" is not allowed [explicit-any] M(x='x', y=2) # no error def f(m: M) -> None: pass # no error [builtins fixtures/dict.pyi] [case testDisallowAnyExplicitTypedDictGeneric] -# flags: --disallow-any-explicit +# flags: --disallow-any-explicit --show-error-codes from mypy_extensions import TypedDict from typing import Any, List -M = TypedDict('M', {'x': str, 'y': List[Any]}) # E: Explicit "Any" is not allowed +M = TypedDict('M', {'x': str, 'y': List[Any]}) # E: Explicit "Any" is not allowed [explicit-any] N = TypedDict('N', {'x': str, 'y': List}) # no error [builtins fixtures/dict.pyi] diff --git a/test-data/unit/fixtures/typing-typeddict.pyi b/test-data/unit/fixtures/typing-typeddict.pyi index a54dc8bcfa94..df703b239743 100644 --- a/test-data/unit/fixtures/typing-typeddict.pyi +++ b/test-data/unit/fixtures/typing-typeddict.pyi @@ -47,8 +47,7 @@ class Iterator(Iterable[T_co], Protocol): def __next__(self) -> T_co: pass class Sequence(Iterable[T_co]): - # misc is for explicit Any. - def __getitem__(self, n: Any) -> T_co: pass # type: ignore[misc] + def __getitem__(self, n: Any) -> T_co: pass # type: ignore[explicit-any] class Mapping(Iterable[T], Generic[T, T_co], metaclass=ABCMeta): def keys(self) -> Iterable[T]: pass # Approximate return type