[flake8-bugbear] Add no-explicit-stacklevel (B028) (#3550)
This commit is contained in:
11
crates/ruff/resources/test/fixtures/flake8_bugbear/B028.py
vendored
Normal file
11
crates/ruff/resources/test/fixtures/flake8_bugbear/B028.py
vendored
Normal file
@@ -0,0 +1,11 @@
|
||||
import warnings
|
||||
|
||||
"""
|
||||
Should emit:
|
||||
B028 - on lines 8 and 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)
|
||||
@@ -2456,6 +2456,9 @@ where
|
||||
{
|
||||
flake8_bugbear::rules::zip_without_explicit_strict(self, expr, func, keywords);
|
||||
}
|
||||
if self.settings.rules.enabled(Rule::NoExplicitStacklevel) {
|
||||
flake8_bugbear::rules::no_explicit_stacklevel(self, func, args, keywords);
|
||||
}
|
||||
|
||||
// flake8-pie
|
||||
if self.settings.rules.enabled(Rule::UnnecessaryDictKwargs) {
|
||||
|
||||
@@ -227,6 +227,7 @@ pub fn code_to_rule(linter: Linter, code: &str) -> Option<Rule> {
|
||||
(Flake8Bugbear, "025") => Rule::DuplicateTryBlockException,
|
||||
(Flake8Bugbear, "026") => Rule::StarArgUnpackingAfterKeywordArg,
|
||||
(Flake8Bugbear, "027") => Rule::EmptyMethodWithoutAbstractDecorator,
|
||||
(Flake8Bugbear, "028") => Rule::NoExplicitStacklevel,
|
||||
(Flake8Bugbear, "029") => Rule::ExceptWithEmptyTuple,
|
||||
(Flake8Bugbear, "030") => Rule::ExceptWithNonExceptionClasses,
|
||||
(Flake8Bugbear, "032") => Rule::UnintentionalTypeAnnotation,
|
||||
|
||||
@@ -186,6 +186,7 @@ ruff_macros::register_rules!(
|
||||
rules::flake8_bugbear::rules::UnreliableCallableCheck,
|
||||
rules::flake8_bugbear::rules::StripWithMultiCharacters,
|
||||
rules::flake8_bugbear::rules::MutableArgumentDefault,
|
||||
rules::flake8_bugbear::rules::NoExplicitStacklevel,
|
||||
rules::flake8_bugbear::rules::UnusedLoopControlVariable,
|
||||
rules::flake8_bugbear::rules::FunctionCallArgumentDefault,
|
||||
rules::flake8_bugbear::rules::GetAttrWithConstant,
|
||||
|
||||
@@ -41,6 +41,7 @@ mod tests {
|
||||
#[test_case(Rule::StarArgUnpackingAfterKeywordArg, Path::new("B026.py"); "B026")]
|
||||
#[test_case(Rule::EmptyMethodWithoutAbstractDecorator, Path::new("B027.py"); "B027")]
|
||||
#[test_case(Rule::EmptyMethodWithoutAbstractDecorator, Path::new("B027.pyi"); "B027_pyi")]
|
||||
#[test_case(Rule::NoExplicitStacklevel, Path::new("B028.py"); "B028")]
|
||||
#[test_case(Rule::ExceptWithEmptyTuple, Path::new("B029.py"); "B029")]
|
||||
#[test_case(Rule::ExceptWithNonExceptionClasses, Path::new("B030.py"); "B030")]
|
||||
#[test_case(Rule::UnintentionalTypeAnnotation, Path::new("B032.py"); "B032")]
|
||||
|
||||
@@ -25,6 +25,7 @@ pub use loop_variable_overrides_iterator::{
|
||||
loop_variable_overrides_iterator, LoopVariableOverridesIterator,
|
||||
};
|
||||
pub use mutable_argument_default::{mutable_argument_default, MutableArgumentDefault};
|
||||
pub use no_explicit_stacklevel::{no_explicit_stacklevel, NoExplicitStacklevel};
|
||||
pub use raise_without_from_inside_except::{
|
||||
raise_without_from_inside_except, RaiseWithoutFromInsideExcept,
|
||||
};
|
||||
@@ -63,6 +64,7 @@ mod getattr_with_constant;
|
||||
mod jump_statement_in_finally;
|
||||
mod loop_variable_overrides_iterator;
|
||||
mod mutable_argument_default;
|
||||
mod no_explicit_stacklevel;
|
||||
mod raise_without_from_inside_except;
|
||||
mod redundant_tuple_in_exception_handler;
|
||||
mod setattr_with_constant;
|
||||
|
||||
@@ -0,0 +1,68 @@
|
||||
use rustpython_parser::ast::{Expr, Keyword};
|
||||
|
||||
use ruff_diagnostics::{Diagnostic, Violation};
|
||||
use ruff_macros::{derive_message_formats, violation};
|
||||
use ruff_python_ast::helpers::SimpleCallArgs;
|
||||
use ruff_python_ast::types::Range;
|
||||
|
||||
use crate::checkers::ast::Checker;
|
||||
|
||||
/// ## What it does
|
||||
/// Checks for `warnings.warn` calls without an explicit `stacklevel` keyword
|
||||
/// argument.
|
||||
///
|
||||
/// ## Why is this bad?
|
||||
/// The `warnings.warn` method uses a `stacklevel` of 1 by default, which
|
||||
/// limits the rendered stack trace to that of the line on which the
|
||||
/// `warn` method is called.
|
||||
///
|
||||
/// It's recommended to use a `stacklevel` of 2 or higher, give the caller
|
||||
/// more context about the warning.
|
||||
///
|
||||
/// ## Example
|
||||
/// ```python
|
||||
/// warnings.warn("This is a warning")
|
||||
/// ```
|
||||
///
|
||||
/// Use instead:
|
||||
/// ```python
|
||||
/// warnings.warn("This is a warning", stacklevel=2)
|
||||
/// ```
|
||||
#[violation]
|
||||
pub struct NoExplicitStacklevel;
|
||||
|
||||
impl Violation for NoExplicitStacklevel {
|
||||
#[derive_message_formats]
|
||||
fn message(&self) -> String {
|
||||
format!("No explicit `stacklevel` keyword argument found")
|
||||
}
|
||||
}
|
||||
|
||||
/// B028
|
||||
pub fn no_explicit_stacklevel(
|
||||
checker: &mut Checker,
|
||||
func: &Expr,
|
||||
args: &[Expr],
|
||||
keywords: &[Keyword],
|
||||
) {
|
||||
if !checker
|
||||
.ctx
|
||||
.resolve_call_path(func)
|
||||
.map_or(false, |call_path| {
|
||||
call_path.as_slice() == ["warnings", "warn"]
|
||||
})
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if SimpleCallArgs::new(args, keywords)
|
||||
.keyword_argument("stacklevel")
|
||||
.is_some()
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
checker
|
||||
.diagnostics
|
||||
.push(Diagnostic::new(NoExplicitStacklevel, Range::from(func)));
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
---
|
||||
source: crates/ruff/src/rules/flake8_bugbear/mod.rs
|
||||
expression: diagnostics
|
||||
---
|
||||
- kind:
|
||||
name: NoExplicitStacklevel
|
||||
body: "No explicit `stacklevel` keyword argument found"
|
||||
suggestion: ~
|
||||
fixable: false
|
||||
location:
|
||||
row: 8
|
||||
column: 0
|
||||
end_location:
|
||||
row: 8
|
||||
column: 13
|
||||
fix: ~
|
||||
parent: ~
|
||||
- kind:
|
||||
name: NoExplicitStacklevel
|
||||
body: "No explicit `stacklevel` keyword argument found"
|
||||
suggestion: ~
|
||||
fixable: false
|
||||
location:
|
||||
row: 9
|
||||
column: 0
|
||||
end_location:
|
||||
row: 9
|
||||
column: 13
|
||||
fix: ~
|
||||
parent: ~
|
||||
|
||||
1
ruff.schema.json
generated
1
ruff.schema.json
generated
@@ -1469,6 +1469,7 @@
|
||||
"B025",
|
||||
"B026",
|
||||
"B027",
|
||||
"B028",
|
||||
"B029",
|
||||
"B03",
|
||||
"B030",
|
||||
|
||||
Reference in New Issue
Block a user