[flake8-bugbear] Make fix unsafe if it deletes comments (B013) (#22658)

<!--
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-19 21:08:04 +03:00
committed by GitHub
parent 21dd6029f5
commit 230e455e93
3 changed files with 54 additions and 7 deletions

View File

@@ -18,4 +18,12 @@ list_exceptions = [FileExistsError, FileNotFoundError]
try:
pass
except (*list_exceptions,):
pass
pass
try:
...
except (
ValueError,
# text
):
...

View File

@@ -1,3 +1,4 @@
use ruff_diagnostics::Applicability;
use ruff_macros::{ViolationMetadata, derive_message_formats};
use ruff_python_ast::{self as ast, ExceptHandler, Expr};
use ruff_text_size::Ranged;
@@ -33,6 +34,9 @@ use crate::{AlwaysFixableViolation, Edit, Fix};
/// ...
/// ```
///
/// ## Fix safety
/// This rule's fix is marked as safe, unless the exception handler contains comments.
///
/// ## References
/// - [Python documentation: `except` clause](https://docs.python.org/3/reference/compound_stmts.html#except-clause)
#[derive(ViolationMetadata)]
@@ -86,6 +90,13 @@ pub(crate) fn redundant_tuple_in_exception_handler(checker: &Checker, handlers:
},
type_.range(),
);
let applicability = if checker.comment_ranges().intersects(type_.range()) {
Applicability::Unsafe
} else {
Applicability::Safe
};
// If there's no space between the `except` and the tuple, we need to insert a space,
// as in:
// ```python
@@ -93,13 +104,16 @@ pub(crate) fn redundant_tuple_in_exception_handler(checker: &Checker, handlers:
// ```
// Otherwise, the output will be invalid syntax, since we're removing a set of
// parentheses.
diagnostic.set_fix(Fix::safe_edit(Edit::range_replacement(
pad(
checker.generator().expr(elt),
diagnostic.set_fix(Fix::applicable_edit(
Edit::range_replacement(
pad(
checker.generator().expr(elt),
type_.range(),
checker.locator(),
),
type_.range(),
checker.locator(),
),
type_.range(),
)));
applicability,
));
}
}

View File

@@ -39,3 +39,28 @@ help: Replace with `except ValueError`
14 | pass
15 |
16 | list_exceptions = [FileExistsError, FileNotFoundError]
B013 [*] A length-one tuple literal is redundant in exception handlers
--> B013.py:25:8
|
23 | try:
24 | ...
25 | except (
| ________^
26 | | ValueError,
27 | | # text
28 | | ):
| |_________^
29 | ...
|
help: Replace with `except ValueError`
22 |
23 | try:
24 | ...
- except (
- ValueError,
- # text
- ):
25 + except ValueError:
26 | ...
note: This is an unsafe fix and may change runtime behavior