From c5902a3cdbf105ca49ef17974241ed5b67d1a6c8 Mon Sep 17 00:00:00 2001 From: chiri Date: Mon, 19 Jan 2026 18:33:18 +0300 Subject: [PATCH] [`refurb`] Make fix unsafe if it deletes comments (`FURB140`) (#22679) ## Summary ## Test Plan --- .../resources/test/fixtures/refurb/FURB140.py | 8 ++++++ .../refurb/rules/reimplemented_starmap.rs | 17 +++++++++++- ...es__refurb__tests__FURB140_FURB140.py.snap | 27 ++++++++++++++++++- 3 files changed, 50 insertions(+), 2 deletions(-) diff --git a/crates/ruff_linter/resources/test/fixtures/refurb/FURB140.py b/crates/ruff_linter/resources/test/fixtures/refurb/FURB140.py index 2df7314c2a..ca042ec3dc 100644 --- a/crates/ruff_linter/resources/test/fixtures/refurb/FURB140.py +++ b/crates/ruff_linter/resources/test/fixtures/refurb/FURB140.py @@ -57,3 +57,11 @@ from itertools import starmap as sm [" ".join(x)(x, y) for x, y in zipped()] [" ".join(x)(*x) for x in zipped()] + +all( + predicate(a, b) + # text + for a, b + # text + in some_iterable +) diff --git a/crates/ruff_linter/src/rules/refurb/rules/reimplemented_starmap.rs b/crates/ruff_linter/src/rules/refurb/rules/reimplemented_starmap.rs index d9501a645b..b769d197b4 100644 --- a/crates/ruff_linter/src/rules/refurb/rules/reimplemented_starmap.rs +++ b/crates/ruff_linter/src/rules/refurb/rules/reimplemented_starmap.rs @@ -1,4 +1,5 @@ use anyhow::{Result, bail}; +use ruff_diagnostics::Applicability; use ruff_macros::{ViolationMetadata, derive_message_formats}; use ruff_python_ast::comparable::ComparableExpr; use ruff_python_ast::helpers::any_over_expr; @@ -40,6 +41,9 @@ use crate::{Edit, Fix, FixAvailability, Violation}; /// all(starmap(predicate, some_iterable)) /// ``` /// +/// ## Fix safety +/// This rule's fix is marked as safe, unless the expression contains comments. +/// /// ## References /// - [Python documentation: `itertools.starmap`](https://docs.python.org/3/library/itertools.html#itertools.starmap) /// @@ -155,7 +159,18 @@ pub(crate) fn reimplemented_starmap(checker: &Checker, target: &StarmapCandidate )?, target.range(), ); - Ok(Fix::safe_edits(import_edit, [main_edit])) + + let applicability = if checker.comment_ranges().intersects(target.range()) { + Applicability::Unsafe + } else { + Applicability::Safe + }; + + Ok(Fix::applicable_edits( + import_edit, + [main_edit], + applicability, + )) }); } diff --git a/crates/ruff_linter/src/rules/refurb/snapshots/ruff_linter__rules__refurb__tests__FURB140_FURB140.py.snap b/crates/ruff_linter/src/rules/refurb/snapshots/ruff_linter__rules__refurb__tests__FURB140_FURB140.py.snap index 515d7764bb..dbf8864f3a 100644 --- a/crates/ruff_linter/src/rules/refurb/snapshots/ruff_linter__rules__refurb__tests__FURB140_FURB140.py.snap +++ b/crates/ruff_linter/src/rules/refurb/snapshots/ruff_linter__rules__refurb__tests__FURB140_FURB140.py.snap @@ -184,4 +184,29 @@ help: Replace with `itertools.starmap` 31 + set(sm(foo, [(85, 60), (100, 80)])) 32 | 33 | # Non-errors. -34 | +34 | + +FURB140 [*] Use `itertools.starmap` instead of the generator + --> FURB140.py:62:5 + | +61 | all( +62 | / predicate(a, b) +63 | | # text +64 | | for a, b +65 | | # text +66 | | in some_iterable + | |____________________^ +67 | ) + | +help: Replace with `itertools.starmap` +59 | [" ".join(x)(*x) for x in zipped()] +60 | +61 | all( + - predicate(a, b) + - # text + - for a, b + - # text + - in some_iterable +62 + sm(predicate, some_iterable) +63 | ) +note: This is an unsafe fix and may change runtime behavior