mirror of https://github.com/astral-sh/ruff
[`flake8-bugbear`] Add `flake8-bugbear`'s B030 rule (#3400)
This commit is contained in:
parent
da1f83fe32
commit
3349ceb969
|
|
@ -0,0 +1,47 @@
|
||||||
|
"""
|
||||||
|
Should emit:
|
||||||
|
B030:
|
||||||
|
- line 12, column 8
|
||||||
|
- line 17, column 9
|
||||||
|
- line 22, column 21
|
||||||
|
- line 27, column 37
|
||||||
|
"""
|
||||||
|
|
||||||
|
try:
|
||||||
|
pass
|
||||||
|
except 1: # error
|
||||||
|
pass
|
||||||
|
|
||||||
|
try:
|
||||||
|
pass
|
||||||
|
except (1, ValueError): # error
|
||||||
|
pass
|
||||||
|
|
||||||
|
try:
|
||||||
|
pass
|
||||||
|
except (ValueError, (RuntimeError, (KeyError, TypeError))): # error
|
||||||
|
pass
|
||||||
|
|
||||||
|
try:
|
||||||
|
pass
|
||||||
|
except (ValueError, *(RuntimeError, (KeyError, TypeError))): # error
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
try:
|
||||||
|
pass
|
||||||
|
except (ValueError, *(RuntimeError, TypeError)): # ok
|
||||||
|
pass
|
||||||
|
|
||||||
|
try:
|
||||||
|
pass
|
||||||
|
except (ValueError, *[RuntimeError, *(TypeError,)]): # ok
|
||||||
|
pass
|
||||||
|
|
||||||
|
def what_to_catch():
|
||||||
|
return ...
|
||||||
|
|
||||||
|
try:
|
||||||
|
pass
|
||||||
|
except what_to_catch(): # ok
|
||||||
|
pass
|
||||||
|
|
@ -3766,6 +3766,13 @@ where
|
||||||
if self.settings.rules.enabled(&Rule::ExceptWithEmptyTuple) {
|
if self.settings.rules.enabled(&Rule::ExceptWithEmptyTuple) {
|
||||||
flake8_bugbear::rules::except_with_empty_tuple(self, excepthandler);
|
flake8_bugbear::rules::except_with_empty_tuple(self, excepthandler);
|
||||||
}
|
}
|
||||||
|
if self
|
||||||
|
.settings
|
||||||
|
.rules
|
||||||
|
.enabled(&Rule::ExceptWithNonExceptionClasses)
|
||||||
|
{
|
||||||
|
flake8_bugbear::rules::except_with_non_exception_classes(self, excepthandler);
|
||||||
|
}
|
||||||
if self.settings.rules.enabled(&Rule::ReraiseNoCause) {
|
if self.settings.rules.enabled(&Rule::ReraiseNoCause) {
|
||||||
tryceratops::rules::reraise_no_cause(self, body);
|
tryceratops::rules::reraise_no_cause(self, body);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -206,6 +206,7 @@ pub fn code_to_rule(linter: Linter, code: &str) -> Option<Rule> {
|
||||||
(Flake8Bugbear, "026") => Rule::StarArgUnpackingAfterKeywordArg,
|
(Flake8Bugbear, "026") => Rule::StarArgUnpackingAfterKeywordArg,
|
||||||
(Flake8Bugbear, "027") => Rule::EmptyMethodWithoutAbstractDecorator,
|
(Flake8Bugbear, "027") => Rule::EmptyMethodWithoutAbstractDecorator,
|
||||||
(Flake8Bugbear, "029") => Rule::ExceptWithEmptyTuple,
|
(Flake8Bugbear, "029") => Rule::ExceptWithEmptyTuple,
|
||||||
|
(Flake8Bugbear, "030") => Rule::ExceptWithNonExceptionClasses,
|
||||||
(Flake8Bugbear, "032") => Rule::UnintentionalTypeAnnotation,
|
(Flake8Bugbear, "032") => Rule::UnintentionalTypeAnnotation,
|
||||||
(Flake8Bugbear, "904") => Rule::RaiseWithoutFromInsideExcept,
|
(Flake8Bugbear, "904") => Rule::RaiseWithoutFromInsideExcept,
|
||||||
(Flake8Bugbear, "905") => Rule::ZipWithoutExplicitStrict,
|
(Flake8Bugbear, "905") => Rule::ZipWithoutExplicitStrict,
|
||||||
|
|
|
||||||
|
|
@ -210,6 +210,7 @@ ruff_macros::register_rules!(
|
||||||
rules::flake8_bugbear::rules::RaiseWithoutFromInsideExcept,
|
rules::flake8_bugbear::rules::RaiseWithoutFromInsideExcept,
|
||||||
rules::flake8_bugbear::rules::ZipWithoutExplicitStrict,
|
rules::flake8_bugbear::rules::ZipWithoutExplicitStrict,
|
||||||
rules::flake8_bugbear::rules::ExceptWithEmptyTuple,
|
rules::flake8_bugbear::rules::ExceptWithEmptyTuple,
|
||||||
|
rules::flake8_bugbear::rules::ExceptWithNonExceptionClasses,
|
||||||
rules::flake8_bugbear::rules::UnintentionalTypeAnnotation,
|
rules::flake8_bugbear::rules::UnintentionalTypeAnnotation,
|
||||||
// flake8-blind-except
|
// flake8-blind-except
|
||||||
rules::flake8_blind_except::rules::BlindExcept,
|
rules::flake8_blind_except::rules::BlindExcept,
|
||||||
|
|
|
||||||
|
|
@ -42,6 +42,7 @@ mod tests {
|
||||||
#[test_case(Rule::EmptyMethodWithoutAbstractDecorator, Path::new("B027.py"); "B027")]
|
#[test_case(Rule::EmptyMethodWithoutAbstractDecorator, Path::new("B027.py"); "B027")]
|
||||||
#[test_case(Rule::EmptyMethodWithoutAbstractDecorator, Path::new("B027.pyi"); "B027_pyi")]
|
#[test_case(Rule::EmptyMethodWithoutAbstractDecorator, Path::new("B027.pyi"); "B027_pyi")]
|
||||||
#[test_case(Rule::ExceptWithEmptyTuple, Path::new("B029.py"); "B029")]
|
#[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")]
|
#[test_case(Rule::UnintentionalTypeAnnotation, Path::new("B032.py"); "B032")]
|
||||||
#[test_case(Rule::RaiseWithoutFromInsideExcept, Path::new("B904.py"); "B904")]
|
#[test_case(Rule::RaiseWithoutFromInsideExcept, Path::new("B904.py"); "B904")]
|
||||||
#[test_case(Rule::ZipWithoutExplicitStrict, Path::new("B905.py"); "B905")]
|
#[test_case(Rule::ZipWithoutExplicitStrict, Path::new("B905.py"); "B905")]
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,60 @@
|
||||||
|
use std::collections::VecDeque;
|
||||||
|
|
||||||
|
use rustpython_parser::ast::{Excepthandler, ExcepthandlerKind, Expr, ExprKind};
|
||||||
|
|
||||||
|
use ruff_macros::{derive_message_formats, violation};
|
||||||
|
use ruff_python_ast::types::Range;
|
||||||
|
|
||||||
|
use crate::checkers::ast::Checker;
|
||||||
|
use crate::registry::Diagnostic;
|
||||||
|
use crate::violation::Violation;
|
||||||
|
|
||||||
|
#[violation]
|
||||||
|
pub struct ExceptWithNonExceptionClasses;
|
||||||
|
|
||||||
|
impl Violation for ExceptWithNonExceptionClasses {
|
||||||
|
#[derive_message_formats]
|
||||||
|
fn message(&self) -> String {
|
||||||
|
format!("`except` handlers should only be exception classes or tuples of exception classes")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Given an [`Expr`], flatten any [`ExprKind::Starred`] expressions.
|
||||||
|
/// This should leave any unstarred iterables alone (subsequently raising a
|
||||||
|
/// warning for B029).
|
||||||
|
fn flatten_starred_iterables(expr: &Expr) -> Vec<&Expr> {
|
||||||
|
let ExprKind::Tuple { elts, .. } = &expr.node else {
|
||||||
|
return vec![expr];
|
||||||
|
};
|
||||||
|
let mut flattened_exprs: Vec<&Expr> = Vec::with_capacity(elts.len());
|
||||||
|
let mut exprs_to_process: VecDeque<&Expr> = elts.iter().collect();
|
||||||
|
while let Some(expr) = exprs_to_process.pop_front() {
|
||||||
|
match &expr.node {
|
||||||
|
ExprKind::Starred { value, .. } => match &value.node {
|
||||||
|
ExprKind::Tuple { elts, .. } | ExprKind::List { elts, .. } => {
|
||||||
|
exprs_to_process.append(&mut elts.iter().collect());
|
||||||
|
}
|
||||||
|
_ => flattened_exprs.push(expr),
|
||||||
|
},
|
||||||
|
_ => flattened_exprs.push(expr),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
flattened_exprs
|
||||||
|
}
|
||||||
|
|
||||||
|
/// B030
|
||||||
|
pub fn except_with_non_exception_classes(checker: &mut Checker, excepthandler: &Excepthandler) {
|
||||||
|
let ExcepthandlerKind::ExceptHandler { type_, .. } = &excepthandler.node;
|
||||||
|
let Some(type_) = type_ else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
for expr in flatten_starred_iterables(type_) {
|
||||||
|
match expr.node {
|
||||||
|
ExprKind::Attribute { .. } | ExprKind::Name { .. } | ExprKind::Call { .. } => (),
|
||||||
|
_ => checker.diagnostics.push(Diagnostic::new(
|
||||||
|
ExceptWithNonExceptionClasses,
|
||||||
|
Range::from(expr),
|
||||||
|
)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -11,6 +11,9 @@ pub use duplicate_exceptions::{
|
||||||
duplicate_exceptions, DuplicateHandlerException, DuplicateTryBlockException,
|
duplicate_exceptions, DuplicateHandlerException, DuplicateTryBlockException,
|
||||||
};
|
};
|
||||||
pub use except_with_empty_tuple::{except_with_empty_tuple, ExceptWithEmptyTuple};
|
pub use except_with_empty_tuple::{except_with_empty_tuple, ExceptWithEmptyTuple};
|
||||||
|
pub use except_with_non_exception_classes::{
|
||||||
|
except_with_non_exception_classes, ExceptWithNonExceptionClasses,
|
||||||
|
};
|
||||||
pub use f_string_docstring::{f_string_docstring, FStringDocstring};
|
pub use f_string_docstring::{f_string_docstring, FStringDocstring};
|
||||||
pub use function_call_argument_default::{
|
pub use function_call_argument_default::{
|
||||||
function_call_argument_default, FunctionCallArgumentDefault,
|
function_call_argument_default, FunctionCallArgumentDefault,
|
||||||
|
|
@ -52,6 +55,7 @@ mod cached_instance_method;
|
||||||
mod cannot_raise_literal;
|
mod cannot_raise_literal;
|
||||||
mod duplicate_exceptions;
|
mod duplicate_exceptions;
|
||||||
mod except_with_empty_tuple;
|
mod except_with_empty_tuple;
|
||||||
|
mod except_with_non_exception_classes;
|
||||||
mod f_string_docstring;
|
mod f_string_docstring;
|
||||||
mod function_call_argument_default;
|
mod function_call_argument_default;
|
||||||
mod function_uses_loop_variable;
|
mod function_uses_loop_variable;
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,57 @@
|
||||||
|
---
|
||||||
|
source: crates/ruff/src/rules/flake8_bugbear/mod.rs
|
||||||
|
expression: diagnostics
|
||||||
|
---
|
||||||
|
- kind:
|
||||||
|
name: ExceptWithNonExceptionClasses
|
||||||
|
body: "`except` handlers should only be exception classes or tuples of exception classes"
|
||||||
|
suggestion: ~
|
||||||
|
fixable: false
|
||||||
|
location:
|
||||||
|
row: 12
|
||||||
|
column: 7
|
||||||
|
end_location:
|
||||||
|
row: 12
|
||||||
|
column: 8
|
||||||
|
fix: ~
|
||||||
|
parent: ~
|
||||||
|
- kind:
|
||||||
|
name: ExceptWithNonExceptionClasses
|
||||||
|
body: "`except` handlers should only be exception classes or tuples of exception classes"
|
||||||
|
suggestion: ~
|
||||||
|
fixable: false
|
||||||
|
location:
|
||||||
|
row: 17
|
||||||
|
column: 8
|
||||||
|
end_location:
|
||||||
|
row: 17
|
||||||
|
column: 9
|
||||||
|
fix: ~
|
||||||
|
parent: ~
|
||||||
|
- kind:
|
||||||
|
name: ExceptWithNonExceptionClasses
|
||||||
|
body: "`except` handlers should only be exception classes or tuples of exception classes"
|
||||||
|
suggestion: ~
|
||||||
|
fixable: false
|
||||||
|
location:
|
||||||
|
row: 22
|
||||||
|
column: 20
|
||||||
|
end_location:
|
||||||
|
row: 22
|
||||||
|
column: 57
|
||||||
|
fix: ~
|
||||||
|
parent: ~
|
||||||
|
- kind:
|
||||||
|
name: ExceptWithNonExceptionClasses
|
||||||
|
body: "`except` handlers should only be exception classes or tuples of exception classes"
|
||||||
|
suggestion: ~
|
||||||
|
fixable: false
|
||||||
|
location:
|
||||||
|
row: 27
|
||||||
|
column: 36
|
||||||
|
end_location:
|
||||||
|
row: 27
|
||||||
|
column: 57
|
||||||
|
fix: ~
|
||||||
|
parent: ~
|
||||||
|
|
||||||
|
|
@ -1471,6 +1471,7 @@
|
||||||
"B027",
|
"B027",
|
||||||
"B029",
|
"B029",
|
||||||
"B03",
|
"B03",
|
||||||
|
"B030",
|
||||||
"B032",
|
"B032",
|
||||||
"B9",
|
"B9",
|
||||||
"B90",
|
"B90",
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue