diff --git a/mypy/errors.py b/mypy/errors.py index 4e62a48aeb27..427659253540 100644 --- a/mypy/errors.py +++ b/mypy/errors.py @@ -503,6 +503,9 @@ def add_error_info(self, info: ErrorInfo) -> None: self.used_ignored_lines[file][scope_line].append( (info.code or codes.MISC).code ) + if file not in self.ignored_files: + info.hidden = True + self._add_error_info(file, info) return if file in self.ignored_files: return @@ -724,6 +727,8 @@ def generate_unused_ignore_errors(self, file: str) -> None: blocker=False, only_once=False, allow_dups=False, + origin=(self.file, [line]), + target=self.target_module, ) self._add_error_info(file, info) @@ -776,6 +781,8 @@ def generate_ignore_without_code_errors( blocker=False, only_once=False, allow_dups=False, + origin=(self.file, [line]), + target=self.target_module, ) self._add_error_info(file, info) @@ -800,8 +807,9 @@ def blocker_module(self) -> str | None: return None def is_errors_for_file(self, file: str) -> bool: - """Are there any errors for the given file?""" - return file in self.error_info_map + """Are there any visible errors for the given file?""" + errors = self.error_info_map.get(file, ()) + return any(error.hidden is False for error in errors) def prefer_simple_messages(self) -> bool: """Should we generate simple/fast error messages? diff --git a/mypy/server/update.py b/mypy/server/update.py index 0cc7a2229514..0371d5d40266 100644 --- a/mypy/server/update.py +++ b/mypy/server/update.py @@ -667,6 +667,8 @@ def restore(ids: list[str]) -> None: state.type_check_first_pass() state.type_check_second_pass() state.detect_possibly_undefined_vars() + state.generate_unused_ignore_notes() + state.generate_ignore_without_code_notes() t2 = time.time() state.finish_passes() t3 = time.time() @@ -1028,6 +1030,10 @@ def key(node: FineGrainedDeferredNode) -> int: if graph[module_id].type_checker().check_second_pass(): more = True + graph[module_id].detect_possibly_undefined_vars() + graph[module_id].generate_unused_ignore_notes() + graph[module_id].generate_ignore_without_code_notes() + if manager.options.export_types: manager.all_types.update(graph[module_id].type_map()) diff --git a/test-data/unit/daemon.test b/test-data/unit/daemon.test index 18a03a92207d..d772635ac027 100644 --- a/test-data/unit/daemon.test +++ b/test-data/unit/daemon.test @@ -571,3 +571,113 @@ class A: x: int class B: x: int + +[case testUnusedTypeIgnorePreservedOnRerun] +-- Regression test for https://github.com/python/mypy/issues/9655 +$ dmypy start -- --warn-unused-ignores --no-error-summary --hide-error-codes +Daemon started +$ dmypy check -- bar.py +bar.py:2: error: Unused "type: ignore" comment +== Return code: 1 +$ dmypy check -- bar.py +bar.py:2: error: Unused "type: ignore" comment +== Return code: 1 + +[file foo/__init__.py] +[file foo/empty.py] +[file bar.py] +from foo.empty import * +a = 1 # type: ignore + +[case testTypeIgnoreWithoutCodePreservedOnRerun] +-- Regression test for https://github.com/python/mypy/issues/9655 +$ dmypy start -- --enable-error-code ignore-without-code --no-error-summary +Daemon started +$ dmypy check -- bar.py +bar.py:2: error: "type: ignore" comment without error code [ignore-without-code] +== Return code: 1 +$ dmypy check -- bar.py +bar.py:2: error: "type: ignore" comment without error code [ignore-without-code] +== Return code: 1 + +[file foo/__init__.py] +[file foo/empty.py] +[file bar.py] +from foo.empty import * +a = 1 # type: ignore + +[case testUnusedTypeIgnorePreservedAfterChange] +-- Regression test for https://github.com/python/mypy/issues/9655 +$ dmypy start -- --warn-unused-ignores --no-error-summary --hide-error-codes +Daemon started +$ dmypy check -- bar.py +bar.py:2: error: Unused "type: ignore" comment +== Return code: 1 +$ echo '' >> bar.py +$ dmypy check -- bar.py +bar.py:2: error: Unused "type: ignore" comment +== Return code: 1 + +[file foo/__init__.py] +[file foo/empty.py] +[file bar.py] +from foo.empty import * +a = 1 # type: ignore + +[case testTypeIgnoreWithoutCodePreservedAfterChange] +-- Regression test for https://github.com/python/mypy/issues/9655 +$ dmypy start -- --enable-error-code ignore-without-code --no-error-summary +Daemon started +$ dmypy check -- bar.py +bar.py:2: error: "type: ignore" comment without error code [ignore-without-code] +== Return code: 1 +$ echo '' >> bar.py +$ dmypy check -- bar.py +bar.py:2: error: "type: ignore" comment without error code [ignore-without-code] +== Return code: 1 + +[file foo/__init__.py] +[file foo/empty.py] +[file bar.py] +from foo.empty import * +a = 1 # type: ignore + +[case testPossiblyUndefinedVarsPreservedAfterUpdate] +-- Regression test for https://github.com/python/mypy/issues/9655 +$ dmypy start -- --enable-error-code possibly-undefined --no-error-summary +Daemon started +$ dmypy check -- bar.py +bar.py:4: error: Name "a" may be undefined [possibly-undefined] +== Return code: 1 +$ dmypy check -- bar.py +bar.py:4: error: Name "a" may be undefined [possibly-undefined] +== Return code: 1 + +[file foo/__init__.py] +[file foo/empty.py] +[file bar.py] +from foo.empty import * +if False: + a = 1 +a + +[case testReturnTypeIgnoreAfterUnknownImport] +-- Return type ignores after unknown imports and unused modules are respected on the second pass. +$ dmypy start -- --warn-unused-ignores --no-error-summary +Daemon started +$ dmypy check -- foo.py +foo.py:2: error: Cannot find implementation or library stub for module named "a_module_which_does_not_exist" [import-not-found] +foo.py:2: note: See https://mypy.readthedocs.io/en/stable/running_mypy.html#missing-imports +== Return code: 1 +$ dmypy check -- foo.py +foo.py:2: error: Cannot find implementation or library stub for module named "a_module_which_does_not_exist" [import-not-found] +foo.py:2: note: See https://mypy.readthedocs.io/en/stable/running_mypy.html#missing-imports +== Return code: 1 + +[file unused/__init__.py] +[file unused/empty.py] +[file foo.py] +from unused.empty import * +import a_module_which_does_not_exist +def is_foo() -> str: + return True # type: ignore diff --git a/test-data/unit/fine-grained.test b/test-data/unit/fine-grained.test index 68f72a2aa992..6cae8a81bf51 100644 --- a/test-data/unit/fine-grained.test +++ b/test-data/unit/fine-grained.test @@ -5390,6 +5390,7 @@ from enum import Enum class C(Enum): X = 0 +[builtins fixtures/tuple.pyi] [typing fixtures/typing-medium.pyi] [out] == @@ -5486,6 +5487,7 @@ C = Enum('C', 'X Y') from enum import Enum C = Enum('C', 'X') +[builtins fixtures/tuple.pyi] [typing fixtures/typing-medium.pyi] [out] == diff --git a/test-data/unit/fixtures/async_await.pyi b/test-data/unit/fixtures/async_await.pyi index 96ade881111b..28daa0321947 100644 --- a/test-data/unit/fixtures/async_await.pyi +++ b/test-data/unit/fixtures/async_await.pyi @@ -1,9 +1,12 @@ import typing +from typing_extensions import override T = typing.TypeVar('T') U = typing.TypeVar('U') class list(typing.Sequence[T]): + @override def __iter__(self) -> typing.Iterator[T]: ... + @override def __getitem__(self, i: int) -> T: ... def __contains__(self, item: object) -> bool: ... diff --git a/test-data/unit/fixtures/dataclasses.pyi b/test-data/unit/fixtures/dataclasses.pyi index 059c853a621f..29f87ae97e62 100644 --- a/test-data/unit/fixtures/dataclasses.pyi +++ b/test-data/unit/fixtures/dataclasses.pyi @@ -3,6 +3,7 @@ from typing import ( Generic, Iterator, Iterable, Mapping, Optional, Sequence, Tuple, TypeVar, Union, overload, ) +from typing_extensions import override _T = TypeVar('_T') _U = TypeVar('_U') @@ -29,8 +30,10 @@ class dict(Mapping[KT, VT]): def __init__(self, **kwargs: VT) -> None: pass @overload def __init__(self, arg: Iterable[Tuple[KT, VT]], **kwargs: VT) -> None: pass + @override def __getitem__(self, key: KT) -> VT: pass def __setitem__(self, k: KT, v: VT) -> None: pass + @override def __iter__(self) -> Iterator[KT]: pass def __contains__(self, item: object) -> int: pass def update(self, a: Mapping[KT, VT]) -> None: pass @@ -42,7 +45,9 @@ class dict(Mapping[KT, VT]): class list(Generic[_T], Sequence[_T]): def __contains__(self, item: object) -> int: pass + @override def __getitem__(self, key: int) -> _T: pass + @override def __iter__(self) -> Iterator[_T]: pass class function: pass diff --git a/test-data/unit/fixtures/dict.pyi b/test-data/unit/fixtures/dict.pyi index 19d175ff79ab..dfb7fab13422 100644 --- a/test-data/unit/fixtures/dict.pyi +++ b/test-data/unit/fixtures/dict.pyi @@ -5,6 +5,7 @@ import _typeshed from typing import ( TypeVar, Generic, Iterable, Iterator, Mapping, Tuple, overload, Optional, Union, Sequence ) +from typing_extensions import override T = TypeVar('T') KT = TypeVar('KT') @@ -23,8 +24,10 @@ class dict(Mapping[KT, VT]): def __init__(self, **kwargs: VT) -> None: pass @overload def __init__(self, arg: Iterable[Tuple[KT, VT]], **kwargs: VT) -> None: pass + @override def __getitem__(self, key: KT) -> VT: pass def __setitem__(self, k: KT, v: VT) -> None: pass + @override def __iter__(self) -> Iterator[KT]: pass def __contains__(self, item: object) -> int: pass def update(self, a: SupportsKeysAndGetItem[KT, VT]) -> None: pass @@ -46,7 +49,9 @@ class str: pass # for keyword argument key type class bytes: pass class list(Sequence[T]): # needed by some test cases + @override def __getitem__(self, x: int) -> T: pass + @override def __iter__(self) -> Iterator[T]: pass def __mul__(self, x: int) -> list[T]: pass def __contains__(self, item: object) -> bool: pass diff --git a/test-data/unit/fixtures/list.pyi b/test-data/unit/fixtures/list.pyi index 90fbabe8bc92..ac5b7e49b147 100644 --- a/test-data/unit/fixtures/list.pyi +++ b/test-data/unit/fixtures/list.pyi @@ -1,6 +1,7 @@ # Builtins stub used in list-related test cases. from typing import TypeVar, Generic, Iterable, Iterator, Sequence, overload +from typing_extensions import override T = TypeVar('T') @@ -15,11 +16,13 @@ class list(Sequence[T]): def __init__(self) -> None: pass @overload def __init__(self, x: Iterable[T]) -> None: pass + @override def __iter__(self) -> Iterator[T]: pass def __len__(self) -> int: pass def __contains__(self, item: object) -> bool: pass def __add__(self, x: list[T]) -> list[T]: pass def __mul__(self, x: int) -> list[T]: pass + @override def __getitem__(self, x: int) -> T: pass def __setitem__(self, x: int, v: T) -> None: pass def append(self, x: T) -> None: pass diff --git a/test-data/unit/fixtures/plugin_attrs.pyi b/test-data/unit/fixtures/plugin_attrs.pyi index 57e5ecd1b2bc..ffaa82798ae7 100644 --- a/test-data/unit/fixtures/plugin_attrs.pyi +++ b/test-data/unit/fixtures/plugin_attrs.pyi @@ -1,5 +1,6 @@ # Builtins stub used to support attrs plugin tests. from typing import Union, overload, Generic, Sequence, TypeVar, Type, Iterable, Iterator +from typing_extensions import override class object: def __init__(self) -> None: pass @@ -31,6 +32,8 @@ T = TypeVar("T") Tco = TypeVar('Tco', covariant=True) class tuple(Sequence[Tco], Generic[Tco]): def __new__(cls: Type[T], iterable: Iterable[Tco] = ...) -> T: ... + @override def __iter__(self) -> Iterator[Tco]: pass def __contains__(self, item: object) -> bool: pass + @override def __getitem__(self, x: int) -> Tco: pass diff --git a/test-data/unit/fixtures/primitives.pyi b/test-data/unit/fixtures/primitives.pyi index 63128a8ae03d..bc75aa70b6ed 100644 --- a/test-data/unit/fixtures/primitives.pyi +++ b/test-data/unit/fixtures/primitives.pyi @@ -1,6 +1,7 @@ # builtins stub with non-generic primitive types import _typeshed from typing import Generic, TypeVar, Sequence, Iterator, Mapping, Iterable, Tuple, Union +from typing_extensions import override T = TypeVar('T') V = TypeVar('V') @@ -27,35 +28,48 @@ class complex: class bool(int): pass class str(Sequence[str]): def __add__(self, s: str) -> str: pass + @override def __iter__(self) -> Iterator[str]: pass def __contains__(self, other: object) -> bool: pass + @override def __getitem__(self, item: int) -> str: pass def format(self, *args: object, **kwargs: object) -> str: pass class bytes(Sequence[int]): + @override def __iter__(self) -> Iterator[int]: pass def __contains__(self, other: object) -> bool: pass + @override def __getitem__(self, item: int) -> int: pass class bytearray(Sequence[int]): def __init__(self, x: bytes) -> None: pass + @override def __iter__(self) -> Iterator[int]: pass def __contains__(self, other: object) -> bool: pass + @override def __getitem__(self, item: int) -> int: pass class memoryview(Sequence[int]): def __init__(self, x: bytes) -> None: pass + @override def __iter__(self) -> Iterator[int]: pass def __contains__(self, other: object) -> bool: pass + @override def __getitem__(self, item: int) -> int: pass class tuple(Generic[T]): def __contains__(self, other: object) -> bool: pass class list(Sequence[T]): + @override def __iter__(self) -> Iterator[T]: pass def __contains__(self, other: object) -> bool: pass + @override def __getitem__(self, item: int) -> T: pass class dict(Mapping[T, V]): + @override def __iter__(self) -> Iterator[T]: pass class set(Iterable[T]): + @override def __iter__(self) -> Iterator[T]: pass class frozenset(Iterable[T]): + @override def __iter__(self) -> Iterator[T]: pass class function: pass class ellipsis: pass @@ -64,7 +78,9 @@ class range(Sequence[int]): def __init__(self, __x: int, __y: int = ..., __z: int = ...) -> None: pass def count(self, value: int) -> int: pass def index(self, value: int) -> int: pass + @override def __getitem__(self, i: int) -> int: pass + @override def __iter__(self) -> Iterator[int]: pass def __contains__(self, other: object) -> bool: pass diff --git a/test-data/unit/fixtures/set.pyi b/test-data/unit/fixtures/set.pyi index 71d3bd2eee18..cbf801f0551a 100644 --- a/test-data/unit/fixtures/set.pyi +++ b/test-data/unit/fixtures/set.pyi @@ -1,6 +1,7 @@ # Builtins stub used in set-related test cases. from typing import TypeVar, Generic, Iterator, Iterable, Set +from typing_extensions import override T = TypeVar('T') @@ -19,6 +20,7 @@ class ellipsis: pass class set(Iterable[T], Generic[T]): def __init__(self, iterable: Iterable[T] = ...) -> None: ... + @override def __iter__(self) -> Iterator[T]: pass def __contains__(self, item: object) -> bool: pass def __ior__(self, x: Set[T]) -> None: pass diff --git a/test-data/unit/fixtures/tuple.pyi b/test-data/unit/fixtures/tuple.pyi index e270f3d79d3e..3202d46c841b 100644 --- a/test-data/unit/fixtures/tuple.pyi +++ b/test-data/unit/fixtures/tuple.pyi @@ -2,7 +2,7 @@ import _typeshed from typing import Iterable, Iterator, TypeVar, Generic, Sequence, Optional, overload, Tuple, Type - +from typing_extensions import override T = TypeVar("T") Tco = TypeVar('Tco', covariant=True) @@ -14,11 +14,14 @@ class type: def __call__(self, *a: object) -> object: pass class tuple(Sequence[Tco], Generic[Tco]): def __new__(cls: Type[T], iterable: Iterable[Tco] = ...) -> T: ... + @override def __iter__(self) -> Iterator[Tco]: pass def __contains__(self, item: object) -> bool: pass @overload + @override def __getitem__(self, x: int) -> Tco: pass @overload + @override def __getitem__(self, x: slice) -> Tuple[Tco, ...]: ... def __mul__(self, n: int) -> Tuple[Tco, ...]: pass def __rmul__(self, n: int) -> Tuple[Tco, ...]: pass @@ -41,10 +44,13 @@ class bytearray: pass class list(Sequence[T], Generic[T]): @overload + @override def __getitem__(self, i: int) -> T: ... @overload + @override def __getitem__(self, s: slice) -> list[T]: ... def __contains__(self, item: object) -> bool: ... + @override def __iter__(self) -> Iterator[T]: ... def isinstance(x: object, t: type) -> bool: pass diff --git a/test-data/unit/fixtures/typing-medium.pyi b/test-data/unit/fixtures/typing-medium.pyi index 03be1d0a664d..cbb210283a50 100644 --- a/test-data/unit/fixtures/typing-medium.pyi +++ b/test-data/unit/fixtures/typing-medium.pyi @@ -6,6 +6,8 @@ # Many of the definitions have special handling in the type checker, so they # can just be initialized to anything. +from typing_extensions import override + cast = 0 overload = 0 Any = 0 @@ -49,6 +51,7 @@ class Iterator(Iterable[T_co], Protocol): def __next__(self) -> T_co: pass class Generator(Iterator[T], Generic[T, U, V]): + @override def __iter__(self) -> 'Generator[T, U, V]': pass class Sequence(Iterable[T_co]): diff --git a/test-data/unit/fixtures/typing-typeddict.pyi b/test-data/unit/fixtures/typing-typeddict.pyi index 24a2f1328981..79afebb8e3d1 100644 --- a/test-data/unit/fixtures/typing-typeddict.pyi +++ b/test-data/unit/fixtures/typing-typeddict.pyi @@ -7,6 +7,7 @@ # can just be initialized to anything. from abc import ABCMeta +from typing_extensions import override cast = 0 assert_type = 0 @@ -63,6 +64,7 @@ class Mapping(Iterable[T], Generic[T, T_co], metaclass=ABCMeta): class _TypedDict(Mapping[str, object]): # Needed to make this class non-abstract. It is explicitly declared abstract in # typeshed, but we don't want to import abc here, as it would slow down the tests. + @override def __iter__(self) -> Iterator[str]: ... def copy(self: T) -> T: ... # Using NoReturn so that only calls using the plugin hook can go through.