[flake8-bugbear] Offer unsafe autofix for no-explicit-stacklevel (B028) (#14829)

This PR introduces an unsafe autofix for [no-explicit-stacklevel
(B028)](https://docs.astral.sh/ruff/rules/no-explicit-stacklevel/#no-explicit-stacklevel-b028):
we add the `stacklevel` argument, set to `2`.

Closes #14805
This commit is contained in:
Dylan 2024-12-07 07:24:37 -06:00 committed by GitHub
parent 2c13e6513d
commit d34013425f
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 74 additions and 9 deletions

View file

@ -9,3 +9,9 @@ warnings.warn(DeprecationWarning("test"))
warnings.warn(DeprecationWarning("test"), source=None)
warnings.warn(DeprecationWarning("test"), source=None, stacklevel=2)
warnings.warn(DeprecationWarning("test"), stacklevel=1)
warnings.warn(
DeprecationWarning("test"),
# some comments here
source = None # no trailing comma
)

View file

@ -1,9 +1,9 @@
use ruff_diagnostics::{Diagnostic, Violation};
use ruff_diagnostics::{AlwaysFixableViolation, Diagnostic, Fix};
use ruff_macros::{derive_message_formats, ViolationMetadata};
use ruff_python_ast::{self as ast};
use ruff_text_size::Ranged;
use crate::checkers::ast::Checker;
use crate::{checkers::ast::Checker, fix::edits::add_argument};
/// ## What it does
/// Checks for `warnings.warn` calls without an explicit `stacklevel` keyword
@ -28,16 +28,26 @@ use crate::checkers::ast::Checker;
/// warnings.warn("This is a warning", stacklevel=2)
/// ```
///
/// ## Fix safety
/// This rule's fix is marked as unsafe because it changes
/// the behavior of the code. Moreover, the fix will assign
/// a stacklevel of 2, while the user may wish to assign a
/// higher stacklevel to address the diagnostic.
///
/// ## References
/// - [Python documentation: `warnings.warn`](https://docs.python.org/3/library/warnings.html#warnings.warn)
#[derive(ViolationMetadata)]
pub(crate) struct NoExplicitStacklevel;
impl Violation for NoExplicitStacklevel {
impl AlwaysFixableViolation for NoExplicitStacklevel {
#[derive_message_formats]
fn message(&self) -> String {
"No explicit `stacklevel` keyword argument found".to_string()
}
fn fix_title(&self) -> String {
"Set `stacklevel=2`".to_string()
}
}
/// B028
@ -53,8 +63,16 @@ pub(crate) fn no_explicit_stacklevel(checker: &mut Checker, call: &ast::ExprCall
if call.arguments.find_keyword("stacklevel").is_some() {
return;
}
let mut diagnostic = Diagnostic::new(NoExplicitStacklevel, call.func.range());
checker
.diagnostics
.push(Diagnostic::new(NoExplicitStacklevel, call.func.range()));
let edit = add_argument(
"stacklevel=2",
&call.arguments,
checker.comment_ranges(),
checker.locator().contents(),
);
diagnostic.set_fix(Fix::unsafe_edit(edit));
checker.diagnostics.push(diagnostic);
}

View file

@ -1,8 +1,7 @@
---
source: crates/ruff_linter/src/rules/flake8_bugbear/mod.rs
snapshot_kind: text
---
B028.py:8:1: B028 No explicit `stacklevel` keyword argument found
B028.py:8:1: B028 [*] No explicit `stacklevel` keyword argument found
|
6 | """
7 |
@ -11,8 +10,19 @@ B028.py:8:1: B028 No explicit `stacklevel` keyword argument found
9 | warnings.warn(DeprecationWarning("test"), source=None)
10 | warnings.warn(DeprecationWarning("test"), source=None, stacklevel=2)
|
= help: Set `stacklevel=2`
B028.py:9:1: B028 No explicit `stacklevel` keyword argument found
Unsafe fix
5 5 | B028 - on lines 8 and 9
6 6 | """
7 7 |
8 |-warnings.warn(DeprecationWarning("test"))
8 |+warnings.warn(DeprecationWarning("test"), stacklevel=2)
9 9 | warnings.warn(DeprecationWarning("test"), source=None)
10 10 | warnings.warn(DeprecationWarning("test"), source=None, stacklevel=2)
11 11 | warnings.warn(DeprecationWarning("test"), stacklevel=1)
B028.py:9:1: B028 [*] No explicit `stacklevel` keyword argument found
|
8 | warnings.warn(DeprecationWarning("test"))
9 | warnings.warn(DeprecationWarning("test"), source=None)
@ -20,3 +30,34 @@ B028.py:9:1: B028 No explicit `stacklevel` keyword argument found
10 | warnings.warn(DeprecationWarning("test"), source=None, stacklevel=2)
11 | warnings.warn(DeprecationWarning("test"), stacklevel=1)
|
= help: Set `stacklevel=2`
Unsafe fix
6 6 | """
7 7 |
8 8 | warnings.warn(DeprecationWarning("test"))
9 |-warnings.warn(DeprecationWarning("test"), source=None)
10 9 | warnings.warn(DeprecationWarning("test"), source=None, stacklevel=2)
10 |+warnings.warn(DeprecationWarning("test"), source=None, stacklevel=2)
11 11 | warnings.warn(DeprecationWarning("test"), stacklevel=1)
12 12 |
13 13 | warnings.warn(
B028.py:13:1: B028 [*] No explicit `stacklevel` keyword argument found
|
11 | warnings.warn(DeprecationWarning("test"), stacklevel=1)
12 |
13 | warnings.warn(
| ^^^^^^^^^^^^^ B028
14 | DeprecationWarning("test"),
15 | # some comments here
|
= help: Set `stacklevel=2`
Unsafe fix
13 13 | warnings.warn(
14 14 | DeprecationWarning("test"),
15 15 | # some comments here
16 |- source = None # no trailing comma
16 |+ source = None, stacklevel=2 # no trailing comma
17 17 | )