diff --git a/crates/ruff_linter/resources/test/fixtures/flake8_type_checking/quote.py b/crates/ruff_linter/resources/test/fixtures/flake8_type_checking/quote.py index 77075dae2f..d13e8cd970 100644 --- a/crates/ruff_linter/resources/test/fixtures/flake8_type_checking/quote.py +++ b/crates/ruff_linter/resources/test/fixtures/flake8_type_checking/quote.py @@ -90,3 +90,10 @@ def f(): def func() -> DataFrame[[DataFrame[_P, _R]], DataFrame[_P, _R]]: ... + + +def f(): + from pandas import DataFrame, Series + + def func(self) -> DataFrame | list[Series]: + pass diff --git a/crates/ruff_linter/src/rules/flake8_type_checking/helpers.rs b/crates/ruff_linter/src/rules/flake8_type_checking/helpers.rs index 56c475c915..2d24a47aa2 100644 --- a/crates/ruff_linter/src/rules/flake8_type_checking/helpers.rs +++ b/crates/ruff_linter/src/rules/flake8_type_checking/helpers.rs @@ -1,4 +1,5 @@ use anyhow::Result; +use std::cmp::Reverse; use ruff_diagnostics::Edit; use ruff_python_ast::helpers::{map_callable, map_subscript}; @@ -286,11 +287,17 @@ pub(crate) fn quote_annotation( /// Filter out any [`Edit`]s that are completely contained by any other [`Edit`]. pub(crate) fn filter_contained(edits: Vec) -> Vec { + let mut edits = edits; + + // Sort such that the largest edits are prioritized. + edits.sort_unstable_by_key(|edit| (edit.start(), Reverse(edit.end()))); + + // Remove any edits that are completely contained by another edit. let mut filtered: Vec = Vec::with_capacity(edits.len()); for edit in edits { - if filtered + if !filtered .iter() - .all(|filtered_edit| !filtered_edit.range().contains_range(edit.range())) + .any(|filtered_edit| filtered_edit.range().contains_range(edit.range())) { filtered.push(edit); } diff --git a/crates/ruff_linter/src/rules/flake8_type_checking/snapshots/ruff_linter__rules__flake8_type_checking__tests__quote_typing-only-third-party-import_quote.py.snap b/crates/ruff_linter/src/rules/flake8_type_checking/snapshots/ruff_linter__rules__flake8_type_checking__tests__quote_typing-only-third-party-import_quote.py.snap index 9a0e6d8adb..d9bb6a9824 100644 --- a/crates/ruff_linter/src/rules/flake8_type_checking/snapshots/ruff_linter__rules__flake8_type_checking__tests__quote_typing-only-third-party-import_quote.py.snap +++ b/crates/ruff_linter/src/rules/flake8_type_checking/snapshots/ruff_linter__rules__flake8_type_checking__tests__quote_typing-only-third-party-import_quote.py.snap @@ -296,6 +296,8 @@ quote.py:78:24: TCH002 [*] Move third-party import `pandas.DataFrame` into a typ 91 |- def func() -> DataFrame[[DataFrame[_P, _R]], DataFrame[_P, _R]]: 91 |+ def func() -> "DataFrame[[DataFrame[_P, _R]], DataFrame[_P, _R]]": 92 92 | ... +93 93 | +94 94 | quote.py:78:35: TCH002 [*] Move third-party import `pandas.Series` into a type-checking block | @@ -337,5 +339,61 @@ quote.py:78:35: TCH002 [*] Move third-party import `pandas.Series` into a type-c 91 |- def func() -> DataFrame[[DataFrame[_P, _R]], DataFrame[_P, _R]]: 91 |+ def func() -> "DataFrame[[DataFrame[_P, _R]], DataFrame[_P, _R]]": 92 92 | ... +93 93 | +94 94 | +quote.py:96:24: TCH002 [*] Move third-party import `pandas.DataFrame` into a type-checking block + | +95 | def f(): +96 | from pandas import DataFrame, Series + | ^^^^^^^^^ TCH002 +97 | +98 | def func(self) -> DataFrame | list[Series]: + | + = help: Move into type-checking block +ℹ Unsafe fix + 1 |+from typing import TYPE_CHECKING + 2 |+ + 3 |+if TYPE_CHECKING: + 4 |+ from pandas import DataFrame, Series +1 5 | def f(): +2 6 | from pandas import DataFrame +3 7 | +-------------------------------------------------------------------------------- +93 97 | +94 98 | +95 99 | def f(): +96 |- from pandas import DataFrame, Series +97 100 | +98 |- def func(self) -> DataFrame | list[Series]: + 101 |+ def func(self) -> "DataFrame | list[Series]": +99 102 | pass + +quote.py:96:35: TCH002 [*] Move third-party import `pandas.Series` into a type-checking block + | +95 | def f(): +96 | from pandas import DataFrame, Series + | ^^^^^^ TCH002 +97 | +98 | def func(self) -> DataFrame | list[Series]: + | + = help: Move into type-checking block + +ℹ Unsafe fix + 1 |+from typing import TYPE_CHECKING + 2 |+ + 3 |+if TYPE_CHECKING: + 4 |+ from pandas import DataFrame, Series +1 5 | def f(): +2 6 | from pandas import DataFrame +3 7 | +-------------------------------------------------------------------------------- +93 97 | +94 98 | +95 99 | def f(): +96 |- from pandas import DataFrame, Series +97 100 | +98 |- def func(self) -> DataFrame | list[Series]: + 101 |+ def func(self) -> "DataFrame | list[Series]": +99 102 | pass diff --git a/crates/ruff_linter/src/test.rs b/crates/ruff_linter/src/test.rs index 63a69f3857..b646d76bd1 100644 --- a/crates/ruff_linter/src/test.rs +++ b/crates/ruff_linter/src/test.rs @@ -91,7 +91,7 @@ pub fn test_snippet(contents: &str, settings: &LinterSettings) -> Vec { } thread_local! { - static MAX_ITERATIONS: std::cell::Cell = const { std::cell::Cell::new(8) }; + static MAX_ITERATIONS: std::cell::Cell = const { std::cell::Cell::new(10) }; } pub fn set_max_iterations(max: usize) {