From ff4e909b45128f91b674fb70ee92e9b7c7b70bc4 Mon Sep 17 00:00:00 2001 From: STerliakov Date: Fri, 27 Jun 2025 00:59:24 +0200 Subject: [PATCH 1/5] Do not display import-not-found after module-level always false assert --- mypy/errors.py | 2 ++ test-data/unit/check-unreachable-code.test | 7 +++++++ 2 files changed, 9 insertions(+) diff --git a/mypy/errors.py b/mypy/errors.py index 5dd411c39e95..7335c63db76a 100644 --- a/mypy/errors.py +++ b/mypy/errors.py @@ -633,6 +633,8 @@ def add_error_info(self, info: ErrorInfo) -> None: return if file in self.ignored_files: return + if file in self.skipped_lines and set(lines) <= self.skipped_lines[file]: + return if info.only_once: if info.message in self.only_once_messages: return diff --git a/test-data/unit/check-unreachable-code.test b/test-data/unit/check-unreachable-code.test index 368431127b76..b0982177062c 100644 --- a/test-data/unit/check-unreachable-code.test +++ b/test-data/unit/check-unreachable-code.test @@ -778,6 +778,13 @@ assert sys.platform == 'lol' def bar() -> None: pass [builtins fixtures/ops.pyi] +[case testUnreachableAfterToplevelAssertImportThirdParty] +# flags: --platform unknown +import sys +assert sys.platform == 'linux' +import does_not_exist +[builtins fixtures/ops.pyi] + [case testUnreachableAfterToplevelAssertNotInsideIf] import sys if sys.version_info[0] >= 2: From 767a886d165ce23a2419afc69944a7c0059b1c5f Mon Sep 17 00:00:00 2001 From: STerliakov Date: Fri, 27 Jun 2025 01:03:19 +0200 Subject: [PATCH 2/5] Add incremental test --- test-data/unit/check-incremental.test | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/test-data/unit/check-incremental.test b/test-data/unit/check-incremental.test index 4c170ec4753f..ef9656e32b4d 100644 --- a/test-data/unit/check-incremental.test +++ b/test-data/unit/check-incremental.test @@ -6886,3 +6886,12 @@ class A: [out] [out2] main:3: error: Too few arguments + +[case testUnreachableAfterToplevelAssertImportThirdParty] +# flags: --platform unknown +import sys +assert sys.platform == 'linux' +import does_not_exist +[builtins fixtures/ops.pyi] +[out] +[out2] From 8141d73cd8107357d56c00e175cc4262a88d4e2a Mon Sep 17 00:00:00 2001 From: STerliakov Date: Fri, 27 Jun 2025 01:55:48 +0200 Subject: [PATCH 3/5] Fix iterator exhaustion --- mypy/errors.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mypy/errors.py b/mypy/errors.py index 7335c63db76a..7e329708529b 100644 --- a/mypy/errors.py +++ b/mypy/errors.py @@ -631,10 +631,10 @@ def add_error_info(self, info: ErrorInfo) -> None: # Annotation requests us to ignore all errors on this line. self.used_ignored_lines[file][scope_line].append(err_code.code) return + if file in self.skipped_lines and scope_line in self.skipped_lines[file]: + return if file in self.ignored_files: return - if file in self.skipped_lines and set(lines) <= self.skipped_lines[file]: - return if info.only_once: if info.message in self.only_once_messages: return From 01969b580011e7b0ef40b023fc05b22e250b6ecd Mon Sep 17 00:00:00 2001 From: STerliakov Date: Sun, 29 Jun 2025 17:00:32 +0200 Subject: [PATCH 4/5] Remove imports instead of patching errors --- mypy/errors.py | 2 -- mypy/semanal_pass1.py | 4 ++++ 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/mypy/errors.py b/mypy/errors.py index 7e329708529b..5dd411c39e95 100644 --- a/mypy/errors.py +++ b/mypy/errors.py @@ -631,8 +631,6 @@ def add_error_info(self, info: ErrorInfo) -> None: # Annotation requests us to ignore all errors on this line. self.used_ignored_lines[file][scope_line].append(err_code.code) return - if file in self.skipped_lines and scope_line in self.skipped_lines[file]: - return if file in self.ignored_files: return if info.only_once: diff --git a/mypy/semanal_pass1.py b/mypy/semanal_pass1.py index aaa01969217a..ce6c86e260ab 100644 --- a/mypy/semanal_pass1.py +++ b/mypy/semanal_pass1.py @@ -74,6 +74,10 @@ def visit_file(self, file: MypyFile, fnam: str, mod_id: str, options: Options) - if last.end_line is not None: # We are on a Python version recent enough to support end lines. self.skipped_lines |= set(range(next_def.line, last.end_line + 1)) + # To avoid running a new visitor here, let's assume nobody does + # `assert sys.platform == 'foo'; import unknown` on one line + # at top level. + file.imports = [i for i in file.imports if i.line < next_def.line] del file.defs[i + 1 :] break file.skipped_lines = self.skipped_lines From 4db99a54f11647d6ecd484e2c7760df8df9d6904 Mon Sep 17 00:00:00 2001 From: STerliakov Date: Sun, 29 Jun 2025 21:39:34 +0200 Subject: [PATCH 5/5] Use `(line, column)` pair as A5rocks suggested --- mypy/semanal_pass1.py | 7 +++---- test-data/unit/check-unreachable-code.test | 9 +++++++++ 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/mypy/semanal_pass1.py b/mypy/semanal_pass1.py index ce6c86e260ab..266fd236a01f 100644 --- a/mypy/semanal_pass1.py +++ b/mypy/semanal_pass1.py @@ -74,10 +74,9 @@ def visit_file(self, file: MypyFile, fnam: str, mod_id: str, options: Options) - if last.end_line is not None: # We are on a Python version recent enough to support end lines. self.skipped_lines |= set(range(next_def.line, last.end_line + 1)) - # To avoid running a new visitor here, let's assume nobody does - # `assert sys.platform == 'foo'; import unknown` on one line - # at top level. - file.imports = [i for i in file.imports if i.line < next_def.line] + file.imports = [ + i for i in file.imports if (i.line, i.column) <= (defn.line, defn.column) + ] del file.defs[i + 1 :] break file.skipped_lines = self.skipped_lines diff --git a/test-data/unit/check-unreachable-code.test b/test-data/unit/check-unreachable-code.test index b0982177062c..7ac7e35566d4 100644 --- a/test-data/unit/check-unreachable-code.test +++ b/test-data/unit/check-unreachable-code.test @@ -785,6 +785,15 @@ assert sys.platform == 'linux' import does_not_exist [builtins fixtures/ops.pyi] +[case testUnreachableAfterToplevelAssertImportThirdParty2] +# flags: --platform unknown +import sys +import bad; assert sys.platform == 'linux'; import does_not_exist +[builtins fixtures/ops.pyi] +[out] +main:3: error: Cannot find implementation or library stub for module named "bad" +main:3: note: See https://mypy.readthedocs.io/en/stable/running_mypy.html#missing-imports + [case testUnreachableAfterToplevelAssertNotInsideIf] import sys if sys.version_info[0] >= 2: