Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[flake8-bugbear] Allow tuples of exceptions (B030) #10437

Merged
merged 11 commits into from Mar 18, 2024
49 changes: 49 additions & 0 deletions crates/ruff_linter/resources/test/fixtures/flake8_bugbear/B030.py
Expand Up @@ -33,6 +33,13 @@
except (*a, *(RuntimeError, (KeyError, TypeError))): # error
pass


try:
pass
except* a + (RuntimeError, (KeyError, TypeError)): # error
pass


try:
pass
except (ValueError, *(RuntimeError, TypeError)): # ok
Expand Down Expand Up @@ -76,3 +83,45 @@ def what_to_catch():
pass
except what_to_catch(): # ok
pass


try:
pass
except (a, b) + (c, d): # ok
pass


try:
pass
except* (a, b) + (c, d): # ok
pass


try:
pass
except* (a, (b) + (c)): # ok
pass


try:
pass
except (a, b) + (c, d) + (e, f): # ok
pass


try:
pass
except a + (b, c): # ok
pass


try:
pass
except (ValueError, *(RuntimeError, TypeError), *((ArithmeticError,) + (EOFError,))):
pass
Comment on lines +118 to +121
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@autinerd added this test case



try:
pass
except ((a, b) + (c, d)) + ((e, f) + (g)): # ok
pass
Expand Up @@ -44,24 +44,48 @@ impl Violation for ExceptWithNonExceptionClasses {
}
}

/// Given an [`Expr`], flatten any [`Expr::Starred`] expressions.
/// Given an [`Expr`], flatten any [`Expr::Starred`] expressions and any
/// [`Expr::BinOp`] expressions into a flat list of expressions.
/// This should leave any unstarred iterables alone (subsequently raising a
/// warning for B029).
fn flatten_starred_iterables(expr: &Expr) -> Vec<&Expr> {
let Expr::Tuple(ast::ExprTuple { elts, .. }) = expr else {
return vec![expr];
fn flatten_starred_iterables_and_binops(expr: &Expr) -> Vec<&Expr> {
// Unpack the top-level Tuple into queue, otherwise add as-is.
let mut exprs_to_process: VecDeque<&Expr> = match expr {
Expr::Tuple(ast::ExprTuple { elts, .. }) => elts.iter().collect(),
_ => vec![expr].into(),
};
let mut flattened_exprs: Vec<&Expr> = Vec::with_capacity(elts.len());
let mut exprs_to_process: VecDeque<&Expr> = elts.iter().collect();

let mut flattened_exprs: Vec<&Expr> = Vec::new();
while let Some(expr) = exprs_to_process.pop_front() {
match expr {
Expr::Starred(ast::ExprStarred { value, .. }) => match value.as_ref() {
Expr::Tuple(ast::ExprTuple { elts, .. })
| Expr::List(ast::ExprList { elts, .. }) => {
exprs_to_process.append(&mut elts.iter().collect());
}
Expr::BinOp(ast::ExprBinOp { .. }) => {
exprs_to_process.push_back(value);
}
_ => flattened_exprs.push(value),
},
Expr::BinOp(ast::ExprBinOp { left, right, .. }) => {
for expr in [left, right] {
match expr.as_ref() {
// If left or right are tuples or starred we should flatten them.
Expr::Tuple(ast::ExprTuple { elts, .. }) => {
exprs_to_process.append(&mut elts.iter().collect());
}
Expr::Starred(ast::ExprStarred { value, .. }) => {
exprs_to_process.push_back(value);
}
// If they are binops we should flatten them.
Expr::BinOp(ast::ExprBinOp { .. }) => {
exprs_to_process.push_back(expr);
}
_ => flattened_exprs.push(expr),
}
}
}
_ => flattened_exprs.push(expr),
}
}
Expand All @@ -78,7 +102,7 @@ pub(crate) fn except_with_non_exception_classes(
let Some(type_) = type_ else {
return;
};
for expr in flatten_starred_iterables(type_) {
for expr in flatten_starred_iterables_and_binops(type_) {
if !matches!(
expr,
Expr::Subscript(_) | Expr::Attribute(_) | Expr::Name(_) | Expr::Call(_),
Expand Down
Expand Up @@ -46,4 +46,11 @@ B030.py:33:29: B030 `except` handlers should only be exception classes or tuples
34 | pass
|


B030.py:39:28: B030 `except` handlers should only be exception classes or tuples of exception classes
|
37 | try:
38 | pass
39 | except* a + (RuntimeError, (KeyError, TypeError)): # error
| ^^^^^^^^^^^^^^^^^^^^^ B030
40 | pass
|