[refurb] Make fix unsafe if it deletes comments (FURB157) (#22668)

<!--
Thank you for contributing to Ruff/ty! To help us out with reviewing,
please consider the following:

- Does this pull request include a summary of the change? (See below.)
- Does this pull request include a descriptive title? (Please prefix
with `[ty]` for ty pull
  requests.)
- Does this pull request include references to any relevant issues?
-->

## Summary

<!-- What's the purpose of the change? What does it do, and why? -->

## Test Plan

<!-- How was it tested? -->
This commit is contained in:
chiri
2026-01-20 19:42:53 +03:00
committed by GitHub
parent 991c31d48a
commit 1813a7032c
3 changed files with 63 additions and 11 deletions

View File

@@ -91,3 +91,11 @@ Decimal("000_000")
Decimal("_-1") # Should flag as verbose
Decimal("_+1") # Should flag as verbose
Decimal("_-1_000") # Should flag as verbose
Decimal(
float(
# text
"Infinity"
)
)

View File

@@ -1,15 +1,15 @@
use ruff_macros::{ViolationMetadata, derive_message_formats};
use std::borrow::Cow;
use itertools::Itertools;
use ruff_diagnostics::Applicability;
use ruff_macros::{ViolationMetadata, derive_message_formats};
use ruff_python_ast::{self as ast, Expr};
use ruff_python_trivia::PythonWhitespace;
use ruff_text_size::Ranged;
use std::borrow::Cow;
use crate::checkers::ast::Checker;
use crate::linter::float::as_non_finite_float_string_literal;
use crate::{Edit, Fix, FixAvailability, Violation};
/// ## What it does
/// Checks for unnecessary string literal or float casts in `Decimal`
/// constructors.
@@ -45,6 +45,9 @@ use crate::{Edit, Fix, FixAvailability, Violation};
/// Decimal("Infinity")
/// ```
///
/// ## Fix safety
/// This rule's fix is marked as safe, unless the expression contains comments.
///
/// ## References
/// - [Python documentation: `decimal`](https://docs.python.org/3/library/decimal.html)
#[derive(ViolationMetadata)]
@@ -153,10 +156,16 @@ pub(crate) fn verbose_decimal_constructor(checker: &Checker, call: &ast::ExprCal
value.range(),
);
diagnostic.set_fix(Fix::safe_edit(Edit::range_replacement(
replacement,
value.range(),
)));
let applicability = if checker.comment_ranges().intersects(value.range()) {
Applicability::Unsafe
} else {
Applicability::Safe
};
diagnostic.set_fix(Fix::applicable_edit(
Edit::range_replacement(replacement, value.range()),
applicability,
));
}
Expr::Call(ast::ExprCall {
func, arguments, ..
@@ -193,10 +202,16 @@ pub(crate) fn verbose_decimal_constructor(checker: &Checker, call: &ast::ExprCal
value.range(),
);
diagnostic.set_fix(Fix::safe_edit(Edit::range_replacement(
replacement,
value.range(),
)));
let applicability = if checker.comment_ranges().intersects(value.range()) {
Applicability::Unsafe
} else {
Applicability::Safe
};
diagnostic.set_fix(Fix::applicable_edit(
Edit::range_replacement(replacement, value.range()),
applicability,
));
}
_ => {}
}

View File

@@ -728,6 +728,7 @@ help: Replace with `-1`
91 + Decimal(-1) # Should flag as verbose
92 | Decimal("_+1") # Should flag as verbose
93 | Decimal("_-1_000") # Should flag as verbose
94 |
FURB157 [*] Verbose expression in `Decimal` constructor
--> FURB157.py:92:9
@@ -745,6 +746,8 @@ help: Replace with `+1`
- Decimal("_+1") # Should flag as verbose
92 + Decimal(+1) # Should flag as verbose
93 | Decimal("_-1_000") # Should flag as verbose
94 |
95 |
FURB157 [*] Verbose expression in `Decimal` constructor
--> FURB157.py:93:9
@@ -760,3 +763,29 @@ help: Replace with `-1_000`
92 | Decimal("_+1") # Should flag as verbose
- Decimal("_-1_000") # Should flag as verbose
93 + Decimal(-1_000) # Should flag as verbose
94 |
95 |
96 | Decimal(
FURB157 [*] Verbose expression in `Decimal` constructor
--> FURB157.py:97:5
|
96 | Decimal(
97 | / float(
98 | | # text
99 | | "Infinity"
100 | | )
| |_____^
101 | )
|
help: Replace with `"Infinity"`
94 |
95 |
96 | Decimal(
- float(
- # text
- "Infinity"
- )
97 + "Infinity"
98 | )
note: This is an unsafe fix and may change runtime behavior