Skip to content

Commit

Permalink
[flake8-bugbear] Offer unsafe autofix for no-explicit-stacklevel
Browse files Browse the repository at this point in the history
…(`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
  • Loading branch information
dylwil3 authored Dec 7, 2024
1 parent 2c13e65 commit d340134
Show file tree
Hide file tree
Showing 3 changed files with 74 additions and 9 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,9 @@
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
)
Original file line number Diff line number Diff line change
@@ -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
Expand All @@ -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
Expand All @@ -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());

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::new(NoExplicitStacklevel, call.func.range()));
checker.diagnostics.push(diagnostic);
}
Original file line number Diff line number Diff line change
@@ -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 |
Expand All @@ -11,12 +10,54 @@ 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)
| ^^^^^^^^^^^^^ B028
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 | )

0 comments on commit d340134

Please sign in to comment.