From 0b69b719b1babbdd6914a2faea324e1234bf1ec9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hakan=20=C3=87elik?= Date: Wed, 18 Feb 2026 12:57:27 +0300 Subject: [PATCH] Fix false positive when import and usage are on the same line (#292) Change strict less-than to less-than-or-equal in Name.match_2 lineno comparison so that semicolon-separated import+usage on the same line is correctly recognized as used. Co-Authored-By: Claude Opus 4.6 --- src/unimport/statement.py | 4 ++-- .../analyzer/statement/semicolon_same_line.py | 21 +++++++++++++++++++ .../refactor/statement/semicolon_same_line.py | 2 ++ .../source/statement/semicolon_same_line.py | 2 ++ 4 files changed, 27 insertions(+), 2 deletions(-) create mode 100644 tests/cases/analyzer/statement/semicolon_same_line.py create mode 100644 tests/cases/refactor/statement/semicolon_same_line.py create mode 100644 tests/cases/source/statement/semicolon_same_line.py diff --git a/src/unimport/statement.py b/src/unimport/statement.py index cdcdf67f..b6f1d990 100644 --- a/src/unimport/statement.py +++ b/src/unimport/statement.py @@ -167,9 +167,9 @@ def match_2(self, imp: Import | ImportFrom) -> bool: sub_match = ( not primary_match and imp.is_match_sub_packages(self.name) and not self._has_more_specific_import(imp) ) - is_match = (imp.lineno < self.lineno or self._is_deferred_usage(imp)) and (primary_match or sub_match) + is_match = (imp.lineno <= self.lineno or self._is_deferred_usage(imp)) and (primary_match or sub_match) else: - is_match = (imp.lineno < self.lineno or self._is_deferred_usage(imp)) and ( + is_match = (imp.lineno <= self.lineno or self._is_deferred_usage(imp)) and ( self.name == imp.name or imp.is_match_sub_packages(self.name) ) diff --git a/tests/cases/analyzer/statement/semicolon_same_line.py b/tests/cases/analyzer/statement/semicolon_same_line.py new file mode 100644 index 00000000..0641dc6d --- /dev/null +++ b/tests/cases/analyzer/statement/semicolon_same_line.py @@ -0,0 +1,21 @@ +from typing import Union + +from unimport.statement import Import, ImportFrom, Name + +__all__ = ["NAMES", "IMPORTS", "UNUSED_IMPORTS"] + + +NAMES: list[Name] = [ + Name(lineno=1, name="print", is_all=False), + Name(lineno=1, name="pathlib.Path", is_all=False), + Name(lineno=1, name="__file__", is_all=False), + Name(lineno=2, name="sys.exit", is_all=False), + Name(lineno=2, name="doctest.testmod", is_all=False), + Name(lineno=2, name="doctest.ELLIPSIS", is_all=False), +] +IMPORTS: list[Union[Import, ImportFrom]] = [ + Import(lineno=1, column=1, name="pathlib", package="pathlib"), + Import(lineno=2, column=1, name="doctest", package="doctest"), + Import(lineno=2, column=2, name="sys", package="sys"), +] +UNUSED_IMPORTS: list[Union[Import, ImportFrom]] = [] diff --git a/tests/cases/refactor/statement/semicolon_same_line.py b/tests/cases/refactor/statement/semicolon_same_line.py new file mode 100644 index 00000000..498c92dc --- /dev/null +++ b/tests/cases/refactor/statement/semicolon_same_line.py @@ -0,0 +1,2 @@ +if True: import pathlib; print(pathlib.Path(__file__)) +if True: import doctest, sys; sys.exit(doctest.testmod(optionflags=doctest.ELLIPSIS)[0]) diff --git a/tests/cases/source/statement/semicolon_same_line.py b/tests/cases/source/statement/semicolon_same_line.py new file mode 100644 index 00000000..498c92dc --- /dev/null +++ b/tests/cases/source/statement/semicolon_same_line.py @@ -0,0 +1,2 @@ +if True: import pathlib; print(pathlib.Path(__file__)) +if True: import doctest, sys; sys.exit(doctest.testmod(optionflags=doctest.ELLIPSIS)[0])