From 92f9cf0a8ea4c5a4f59c3d0074d0996c98228c00 Mon Sep 17 00:00:00 2001 From: daleselaji-dev <265319989+daleselaji-dev@users.noreply.github.com> Date: Thu, 27 Aug 2026 11:15:37 +0800 Subject: [PATCH] fix(errors): avoid duplicate diagnostics for duplicate paths --- mypy/errors.py | 18 +++++++++++++++--- test-data/unit/cmdline.test | 20 ++++++++++++++++++++ 2 files changed, 35 insertions(+), 3 deletions(-) diff --git a/mypy/errors.py b/mypy/errors.py index 19f01205d5565..6447d0c9ba5dd 100644 --- a/mypy/errors.py +++ b/mypy/errors.py @@ -681,7 +681,16 @@ def report( return info def _add_error_info(self, file: str, info: ErrorInfo) -> None: - assert file not in self.flushed_files + # A file can be revisited under a second module name after an earlier + # pass has flushed its diagnostics. Do not duplicate an error that has + # already been emitted for the same physical file, even if the two + # module states use different path spellings. + normalized_file = os.path.normcase(os.path.abspath(file)) + if any( + os.path.normcase(os.path.abspath(flushed_file)) == normalized_file + for flushed_file in self.flushed_files + ): + return # process the stack of ErrorWatchers before modifying any internal state # in case we need to filter out the error entirely if self._filter_error(file, info): @@ -1094,7 +1103,10 @@ def format_messages( Use a form suitable for displaying to the user. """ - self.flushed_files.add(path) + normalized_path = os.path.normcase(os.path.abspath(path)) + if normalized_path in self.flushed_files: + return [] + self.flushed_files.add(normalized_path) if formatter is not None: errors = create_errors(error_tuples) return [formatter.report_error(err) for err in errors] @@ -1129,7 +1141,7 @@ def new_messages(self) -> list[str]: """ msgs = [] for path in self.error_info_map.keys(): - if path not in self.flushed_files: + if os.path.normcase(os.path.abspath(path)) not in self.flushed_files: error_tuples = self.file_messages(path) msgs.extend( self.format_messages(path, error_tuples, formatter=self.error_formatter) diff --git a/test-data/unit/cmdline.test b/test-data/unit/cmdline.test index 7066034b3e39c..67d7fe3efde1d 100644 --- a/test-data/unit/cmdline.test +++ b/test-data/unit/cmdline.test @@ -822,6 +822,26 @@ src/foo/bar.py: note: a) adding `__init__.py` somewhere, src/foo/bar.py: note: b) using `--explicit-package-bases` or adjusting `MYPYPATH` == Return code: 2 +[case testDuplicatePackageRootsReportsTypeError] +# cmd: mypy --config-file pyproject.toml +[file pyproject.toml] +\[tool.mypy] +python_version = "3.10" +strict = true +mypy_path = ["src", "src/outer/inner"] +packages = ["outer", "example"] +explicit_package_bases = true +[file src/outer/__init__.py] +[file src/outer/inner/example/__init__.py] +[file src/outer/inner/example/module.py] +def takes_int(value: int) -> None: + pass + +def broken() -> None: + takes_int("not an int") +[out] +src/outer/inner/example/module.py:5: error: Argument 1 to "takes_int" has incompatible type "str"; expected "int" + [case testEnableInvalidErrorCode] # cmd: mypy --enable-error-code YOLO test.py [file test.py]