From 6e726b62902c5b327f5e4d1ca23c8c09cb961c6e Mon Sep 17 00:00:00 2001 From: Abdulrahman Alrahma Date: Fri, 6 Dec 2024 12:37:52 +0000 Subject: [PATCH 1/8] handle B025 --- .../test/fixtures/flake8_bugbear/B025.py | 27 ++++++++++++++++++- .../src/checkers/ast/analyze/statement.rs | 3 ++- .../rules/duplicate_exceptions.rs | 17 +++++++++--- 3 files changed, 42 insertions(+), 5 deletions(-) diff --git a/crates/ruff_linter/resources/test/fixtures/flake8_bugbear/B025.py b/crates/ruff_linter/resources/test/fixtures/flake8_bugbear/B025.py index 085f82f343c91..c2b2a1fefac87 100644 --- a/crates/ruff_linter/resources/test/fixtures/flake8_bugbear/B025.py +++ b/crates/ruff_linter/resources/test/fixtures/flake8_bugbear/B025.py @@ -1,6 +1,6 @@ """ Should emit: -B025 - on lines 15, 22, 31 +B025 - on lines 15, 22, 31, 40, 47, 56 """ import pickle @@ -36,3 +36,28 @@ a = 2 except (OSError, TypeError): a = 2 + +try: + a = 1 +except* ValueError: + a = 2 +except* ValueError: + a = 2 + +try: + a = 1 +except* pickle.PickleError: + a = 2 +except* ValueError: + a = 2 +except* pickle.PickleError: + a = 2 + +try: + a = 1 +except* (ValueError, TypeError): + a = 2 +except* ValueError: + a = 2 +except* (OSError, TypeError): + a = 2 diff --git a/crates/ruff_linter/src/checkers/ast/analyze/statement.rs b/crates/ruff_linter/src/checkers/ast/analyze/statement.rs index 73c86c2b33bee..e20951c8724ac 100644 --- a/crates/ruff_linter/src/checkers/ast/analyze/statement.rs +++ b/crates/ruff_linter/src/checkers/ast/analyze/statement.rs @@ -1435,6 +1435,7 @@ pub(crate) fn statement(stmt: &Stmt, checker: &mut Checker) { handlers, orelse, finalbody, + is_star, .. }) => { if checker.enabled(Rule::TooManyNestedBlocks) { @@ -1459,7 +1460,7 @@ pub(crate) fn statement(stmt: &Stmt, checker: &mut Checker) { Rule::DuplicateHandlerException, Rule::DuplicateTryBlockException, ]) { - flake8_bugbear::rules::duplicate_exceptions(checker, handlers); + flake8_bugbear::rules::duplicate_exceptions(checker, handlers, *is_star); } if checker.enabled(Rule::RedundantTupleInExceptionHandler) { flake8_bugbear::rules::redundant_tuple_in_exception_handler(checker, handlers); diff --git a/crates/ruff_linter/src/rules/flake8_bugbear/rules/duplicate_exceptions.rs b/crates/ruff_linter/src/rules/flake8_bugbear/rules/duplicate_exceptions.rs index bff4c8ea88832..836a1e3114611 100644 --- a/crates/ruff_linter/src/rules/flake8_bugbear/rules/duplicate_exceptions.rs +++ b/crates/ruff_linter/src/rules/flake8_bugbear/rules/duplicate_exceptions.rs @@ -42,13 +42,19 @@ use crate::registry::Rule; #[derive(ViolationMetadata)] pub(crate) struct DuplicateTryBlockException { name: String, + is_star: bool, } impl Violation for DuplicateTryBlockException { #[derive_message_formats] fn message(&self) -> String { - let DuplicateTryBlockException { name } = self; - format!("try-except block with duplicate exception `{name}`") + let DuplicateTryBlockException { name, is_star } = self; + let star = if *is_star { + "*".to_string() + } else { + String::new() + }; + format!("try-except{star} block with duplicate exception `{name}`") } } @@ -170,7 +176,11 @@ fn duplicate_handler_exceptions<'a>( } /// B025 -pub(crate) fn duplicate_exceptions(checker: &mut Checker, handlers: &[ExceptHandler]) { +pub(crate) fn duplicate_exceptions( + checker: &mut Checker, + handlers: &[ExceptHandler], + is_star: bool, +) { let mut seen: FxHashSet = FxHashSet::default(); let mut duplicates: FxHashMap> = FxHashMap::default(); for handler in handlers { @@ -210,6 +220,7 @@ pub(crate) fn duplicate_exceptions(checker: &mut Checker, handlers: &[ExceptHand checker.diagnostics.push(Diagnostic::new( DuplicateTryBlockException { name: name.segments().join("."), + is_star, }, expr.range(), )); From caa4c528b001733da3ae5630bb5acca929ac247e Mon Sep 17 00:00:00 2001 From: Abdulrahman Alrahma Date: Fri, 6 Dec 2024 18:34:58 +0000 Subject: [PATCH 2/8] remove obscure star string interpolation and accept test changes --- .../rules/duplicate_exceptions.rs | 9 ++--- ...__flake8_bugbear__tests__B025_B025.py.snap | 37 +++++++++++++++++++ 2 files changed, 41 insertions(+), 5 deletions(-) diff --git a/crates/ruff_linter/src/rules/flake8_bugbear/rules/duplicate_exceptions.rs b/crates/ruff_linter/src/rules/flake8_bugbear/rules/duplicate_exceptions.rs index 836a1e3114611..16392eb22a8c5 100644 --- a/crates/ruff_linter/src/rules/flake8_bugbear/rules/duplicate_exceptions.rs +++ b/crates/ruff_linter/src/rules/flake8_bugbear/rules/duplicate_exceptions.rs @@ -49,12 +49,11 @@ impl Violation for DuplicateTryBlockException { #[derive_message_formats] fn message(&self) -> String { let DuplicateTryBlockException { name, is_star } = self; - let star = if *is_star { - "*".to_string() + if *is_star { + format!("try-except* block with duplicate exception `{name}`") } else { - String::new() - }; - format!("try-except{star} block with duplicate exception `{name}`") + format!("try-except block with duplicate exception `{name}`") + } } } diff --git a/crates/ruff_linter/src/rules/flake8_bugbear/snapshots/ruff_linter__rules__flake8_bugbear__tests__B025_B025.py.snap b/crates/ruff_linter/src/rules/flake8_bugbear/snapshots/ruff_linter__rules__flake8_bugbear__tests__B025_B025.py.snap index 802a09d01dea0..117f480fc15da 100644 --- a/crates/ruff_linter/src/rules/flake8_bugbear/snapshots/ruff_linter__rules__flake8_bugbear__tests__B025_B025.py.snap +++ b/crates/ruff_linter/src/rules/flake8_bugbear/snapshots/ruff_linter__rules__flake8_bugbear__tests__B025_B025.py.snap @@ -38,3 +38,40 @@ B025.py:37:18: B025 try-except block with duplicate exception `TypeError` | ^^^^^^^^^ B025 38 | a = 2 | + +B025.py:44:9: B025 try-except* block with duplicate exception `ValueError` + | +42 | except* ValueError: +43 | a = 2 +44 | except* ValueError: + | ^^^^^^^^^^ B025 +45 | a = 2 + | + +B025.py:53:9: B025 try-except* block with duplicate exception `pickle.PickleError` + | +51 | except* ValueError: +52 | a = 2 +53 | except* pickle.PickleError: + | ^^^^^^^^^^^^^^^^^^ B025 +54 | a = 2 + | + +B025.py:60:9: B025 try-except* block with duplicate exception `ValueError` + | +58 | except* (ValueError, TypeError): +59 | a = 2 +60 | except* ValueError: + | ^^^^^^^^^^ B025 +61 | a = 2 +62 | except* (OSError, TypeError): + | + +B025.py:62:19: B025 try-except* block with duplicate exception `TypeError` + | +60 | except* ValueError: +61 | a = 2 +62 | except* (OSError, TypeError): + | ^^^^^^^^^ B025 +63 | a = 2 + | From e57470abe2ce2db1ef8ab6c63a6ccdda8460ec56 Mon Sep 17 00:00:00 2001 From: Abdulrahman Alrahma Date: Fri, 6 Dec 2024 19:58:15 +0000 Subject: [PATCH 3/8] handle B029 --- .../test/fixtures/flake8_bugbear/B029.py | 14 ++++++++++-- .../checkers/ast/analyze/except_handler.rs | 4 ++-- crates/ruff_linter/src/checkers/ast/mod.rs | 7 +++--- .../rules/except_with_empty_tuple.rs | 19 +++++++++++----- ...__flake8_bugbear__tests__B029_B029.py.snap | 22 +++++++++++++++++++ .../rules/pydoclint/rules/check_docstring.rs | 2 +- .../src/rules/tryceratops/helpers.rs | 2 +- crates/ruff_python_ast/src/visitor.rs | 6 ++--- .../tests/visitor.rs | 2 +- 9 files changed, 60 insertions(+), 18 deletions(-) diff --git a/crates/ruff_linter/resources/test/fixtures/flake8_bugbear/B029.py b/crates/ruff_linter/resources/test/fixtures/flake8_bugbear/B029.py index beb04a4059392..f30d0b387b886 100644 --- a/crates/ruff_linter/resources/test/fixtures/flake8_bugbear/B029.py +++ b/crates/ruff_linter/resources/test/fixtures/flake8_bugbear/B029.py @@ -1,6 +1,6 @@ """ Should emit: -B029 - on lines 8 and 13 +B029 - on lines 8, 13, 18 and 23 """ try: @@ -11,4 +11,14 @@ try: pass except () as e: - pass \ No newline at end of file + pass + +try: + pass +except* (): + pass + +try: + pass +except* () as e: + pass diff --git a/crates/ruff_linter/src/checkers/ast/analyze/except_handler.rs b/crates/ruff_linter/src/checkers/ast/analyze/except_handler.rs index 0c3d6372ab225..3539bd862b4d3 100644 --- a/crates/ruff_linter/src/checkers/ast/analyze/except_handler.rs +++ b/crates/ruff_linter/src/checkers/ast/analyze/except_handler.rs @@ -8,7 +8,7 @@ use crate::rules::{ }; /// Run lint rules over an [`ExceptHandler`] syntax node. -pub(crate) fn except_handler(except_handler: &ExceptHandler, checker: &mut Checker) { +pub(crate) fn except_handler(except_handler: &ExceptHandler, checker: &mut Checker, is_star: bool) { match except_handler { ExceptHandler::ExceptHandler(ast::ExceptHandlerExceptHandler { type_, @@ -60,7 +60,7 @@ pub(crate) fn except_handler(except_handler: &ExceptHandler, checker: &mut Check ); } if checker.enabled(Rule::ExceptWithEmptyTuple) { - flake8_bugbear::rules::except_with_empty_tuple(checker, except_handler); + flake8_bugbear::rules::except_with_empty_tuple(checker, except_handler, is_star); } if checker.enabled(Rule::ExceptWithNonExceptionClasses) { flake8_bugbear::rules::except_with_non_exception_classes(checker, except_handler); diff --git a/crates/ruff_linter/src/checkers/ast/mod.rs b/crates/ruff_linter/src/checkers/ast/mod.rs index 5fcf91b8e5bb0..122976d49048e 100644 --- a/crates/ruff_linter/src/checkers/ast/mod.rs +++ b/crates/ruff_linter/src/checkers/ast/mod.rs @@ -892,6 +892,7 @@ impl<'a> Visitor<'a> for Checker<'a> { handlers, orelse, finalbody, + is_star, .. }, ) => { @@ -908,7 +909,7 @@ impl<'a> Visitor<'a> for Checker<'a> { for except_handler in handlers { self.semantic.push_branch(); - self.visit_except_handler(except_handler); + self.visit_except_handler(except_handler, *is_star); self.semantic.pop_branch(); } @@ -1564,7 +1565,7 @@ impl<'a> Visitor<'a> for Checker<'a> { self.semantic.pop_node(); } - fn visit_except_handler(&mut self, except_handler: &'a ExceptHandler) { + fn visit_except_handler(&mut self, except_handler: &'a ExceptHandler, is_star: bool) { let flags_snapshot = self.semantic.flags; self.semantic.flags |= SemanticModelFlags::EXCEPTION_HANDLER; @@ -1609,7 +1610,7 @@ impl<'a> Visitor<'a> for Checker<'a> { } // Step 4: Analysis - analyze::except_handler(except_handler, self); + analyze::except_handler(except_handler, self, is_star); self.semantic.flags = flags_snapshot; } diff --git a/crates/ruff_linter/src/rules/flake8_bugbear/rules/except_with_empty_tuple.rs b/crates/ruff_linter/src/rules/flake8_bugbear/rules/except_with_empty_tuple.rs index 1e55a6abde3b8..519ba1ac01de1 100644 --- a/crates/ruff_linter/src/rules/flake8_bugbear/rules/except_with_empty_tuple.rs +++ b/crates/ruff_linter/src/rules/flake8_bugbear/rules/except_with_empty_tuple.rs @@ -34,18 +34,27 @@ use crate::checkers::ast::Checker; /// ## References /// - [Python documentation: `except` clause](https://docs.python.org/3/reference/compound_stmts.html#except-clause) #[derive(ViolationMetadata)] -pub(crate) struct ExceptWithEmptyTuple; +pub(crate) struct ExceptWithEmptyTuple { + is_star: bool, +} impl Violation for ExceptWithEmptyTuple { #[derive_message_formats] fn message(&self) -> String { - "Using `except ():` with an empty tuple does not catch anything; add exceptions to handle" - .to_string() + if self.is_star { + "Using `except* ():` with an empty tuple does not catch anything; add exceptions to handle".to_string() + } else { + "Using `except ():` with an empty tuple does not catch anything; add exceptions to handle".to_string() + } } } /// B029 -pub(crate) fn except_with_empty_tuple(checker: &mut Checker, except_handler: &ExceptHandler) { +pub(crate) fn except_with_empty_tuple( + checker: &mut Checker, + except_handler: &ExceptHandler, + is_star: bool, +) { let ExceptHandler::ExceptHandler(ast::ExceptHandlerExceptHandler { type_, .. }) = except_handler; let Some(type_) = type_ else { @@ -56,7 +65,7 @@ pub(crate) fn except_with_empty_tuple(checker: &mut Checker, except_handler: &Ex }; if elts.is_empty() { checker.diagnostics.push(Diagnostic::new( - ExceptWithEmptyTuple, + ExceptWithEmptyTuple { is_star }, except_handler.range(), )); } diff --git a/crates/ruff_linter/src/rules/flake8_bugbear/snapshots/ruff_linter__rules__flake8_bugbear__tests__B029_B029.py.snap b/crates/ruff_linter/src/rules/flake8_bugbear/snapshots/ruff_linter__rules__flake8_bugbear__tests__B029_B029.py.snap index 0417f6e65b9b7..57ecefcc16353 100644 --- a/crates/ruff_linter/src/rules/flake8_bugbear/snapshots/ruff_linter__rules__flake8_bugbear__tests__B029_B029.py.snap +++ b/crates/ruff_linter/src/rules/flake8_bugbear/snapshots/ruff_linter__rules__flake8_bugbear__tests__B029_B029.py.snap @@ -20,4 +20,26 @@ B029.py:13:1: B029 Using `except ():` with an empty tuple does not catch anythin 13 | / except () as e: 14 | | pass | |________^ B029 +15 | +16 | try: + | + +B029.py:18:1: B029 Using `except* ():` with an empty tuple does not catch anything; add exceptions to handle + | +16 | try: +17 | pass +18 | / except* (): +19 | | pass + | |________^ B029 +20 | +21 | try: + | + +B029.py:23:1: B029 Using `except* ():` with an empty tuple does not catch anything; add exceptions to handle + | +21 | try: +22 | pass +23 | / except* () as e: +24 | | pass + | |________^ B029 | diff --git a/crates/ruff_linter/src/rules/pydoclint/rules/check_docstring.rs b/crates/ruff_linter/src/rules/pydoclint/rules/check_docstring.rs index b315d4cbc4183..35f5dab59e3da 100644 --- a/crates/ruff_linter/src/rules/pydoclint/rules/check_docstring.rs +++ b/crates/ruff_linter/src/rules/pydoclint/rules/check_docstring.rs @@ -617,7 +617,7 @@ impl<'a> BodyVisitor<'a> { } impl<'a> Visitor<'a> for BodyVisitor<'a> { - fn visit_except_handler(&mut self, handler: &'a ast::ExceptHandler) { + fn visit_except_handler(&mut self, handler: &'a ast::ExceptHandler, _is_star: bool) { let ast::ExceptHandler::ExceptHandler(handler_inner) = handler; self.currently_suspended_exceptions = handler_inner.type_.as_deref(); visitor::walk_except_handler(self, handler); diff --git a/crates/ruff_linter/src/rules/tryceratops/helpers.rs b/crates/ruff_linter/src/rules/tryceratops/helpers.rs index f38ca0b16c50e..6e8ff9b7b9b4c 100644 --- a/crates/ruff_linter/src/rules/tryceratops/helpers.rs +++ b/crates/ruff_linter/src/rules/tryceratops/helpers.rs @@ -53,7 +53,7 @@ impl<'b> Visitor<'b> for LoggerCandidateVisitor<'_, 'b> { visitor::walk_expr(self, expr); } - fn visit_except_handler(&mut self, _except_handler: &'b ExceptHandler) { + fn visit_except_handler(&mut self, _except_handler: &'b ExceptHandler, _is_star: bool) { // Don't recurse into exception handlers, since we'll re-run the visitor on any such // handlers. } diff --git a/crates/ruff_python_ast/src/visitor.rs b/crates/ruff_python_ast/src/visitor.rs index 0f16af535461c..4f37ee3a03e1c 100644 --- a/crates/ruff_python_ast/src/visitor.rs +++ b/crates/ruff_python_ast/src/visitor.rs @@ -51,7 +51,7 @@ pub trait Visitor<'a> { fn visit_comprehension(&mut self, comprehension: &'a Comprehension) { walk_comprehension(self, comprehension); } - fn visit_except_handler(&mut self, except_handler: &'a ExceptHandler) { + fn visit_except_handler(&mut self, except_handler: &'a ExceptHandler, _is_star: bool) { walk_except_handler(self, except_handler); } fn visit_arguments(&mut self, arguments: &'a Arguments) { @@ -286,12 +286,12 @@ pub fn walk_stmt<'a, V: Visitor<'a> + ?Sized>(visitor: &mut V, stmt: &'a Stmt) { handlers, orelse, finalbody, - is_star: _, + is_star, range: _, }) => { visitor.visit_body(body); for except_handler in handlers { - visitor.visit_except_handler(except_handler); + visitor.visit_except_handler(except_handler, *is_star); } visitor.visit_body(orelse); visitor.visit_body(finalbody); diff --git a/crates/ruff_python_ast_integration_tests/tests/visitor.rs b/crates/ruff_python_ast_integration_tests/tests/visitor.rs index 128d0c3f12184..c1a11c98f7bd0 100644 --- a/crates/ruff_python_ast_integration_tests/tests/visitor.rs +++ b/crates/ruff_python_ast_integration_tests/tests/visitor.rs @@ -247,7 +247,7 @@ impl Visitor<'_> for RecordVisitor { self.exit_node(); } - fn visit_except_handler(&mut self, except_handler: &ExceptHandler) { + fn visit_except_handler(&mut self, except_handler: &ExceptHandler, _is_star: bool) { self.enter_node(except_handler); walk_except_handler(self, except_handler); self.exit_node(); From 1e5d4839039387ac537ac9d5b038e31bc4e9d0f4 Mon Sep 17 00:00:00 2001 From: Abdulrahman Alrahma Date: Fri, 6 Dec 2024 20:11:49 +0000 Subject: [PATCH 4/8] handle B030 --- .../test/fixtures/flake8_bugbear/B030.py | 20 ++++++++++ .../checkers/ast/analyze/except_handler.rs | 6 ++- .../except_with_non_exception_classes.rs | 21 +++++++--- ...__flake8_bugbear__tests__B030_B030.py.snap | 38 ++++++++++++++++++- 4 files changed, 77 insertions(+), 8 deletions(-) diff --git a/crates/ruff_linter/resources/test/fixtures/flake8_bugbear/B030.py b/crates/ruff_linter/resources/test/fixtures/flake8_bugbear/B030.py index 098f724249ac2..d9e2f94d23f8a 100644 --- a/crates/ruff_linter/resources/test/fixtures/flake8_bugbear/B030.py +++ b/crates/ruff_linter/resources/test/fixtures/flake8_bugbear/B030.py @@ -130,3 +130,23 @@ def what_to_catch(): pass except (a, b) * (c, d): # B030 pass + +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* (a, b) * (c, d): # B030 + pass diff --git a/crates/ruff_linter/src/checkers/ast/analyze/except_handler.rs b/crates/ruff_linter/src/checkers/ast/analyze/except_handler.rs index 3539bd862b4d3..4bb79b106a69c 100644 --- a/crates/ruff_linter/src/checkers/ast/analyze/except_handler.rs +++ b/crates/ruff_linter/src/checkers/ast/analyze/except_handler.rs @@ -63,7 +63,11 @@ pub(crate) fn except_handler(except_handler: &ExceptHandler, checker: &mut Check flake8_bugbear::rules::except_with_empty_tuple(checker, except_handler, is_star); } if checker.enabled(Rule::ExceptWithNonExceptionClasses) { - flake8_bugbear::rules::except_with_non_exception_classes(checker, except_handler); + flake8_bugbear::rules::except_with_non_exception_classes( + checker, + except_handler, + is_star, + ); } if checker.enabled(Rule::BinaryOpException) { pylint::rules::binary_op_exception(checker, except_handler); diff --git a/crates/ruff_linter/src/rules/flake8_bugbear/rules/except_with_non_exception_classes.rs b/crates/ruff_linter/src/rules/flake8_bugbear/rules/except_with_non_exception_classes.rs index 180596395846c..8b32d80b14750 100644 --- a/crates/ruff_linter/src/rules/flake8_bugbear/rules/except_with_non_exception_classes.rs +++ b/crates/ruff_linter/src/rules/flake8_bugbear/rules/except_with_non_exception_classes.rs @@ -35,13 +35,20 @@ use crate::checkers::ast::Checker; /// - [Python documentation: `except` clause](https://docs.python.org/3/reference/compound_stmts.html#except-clause) /// - [Python documentation: Built-in Exceptions](https://docs.python.org/3/library/exceptions.html#built-in-exceptions) #[derive(ViolationMetadata)] -pub(crate) struct ExceptWithNonExceptionClasses; +pub(crate) struct ExceptWithNonExceptionClasses { + is_star: bool, +} impl Violation for ExceptWithNonExceptionClasses { #[derive_message_formats] fn message(&self) -> String { - "`except` handlers should only be exception classes or tuples of exception classes" - .to_string() + if self.is_star { + "`except*` handlers should only be exception classes or tuples of exception classes" + .to_string() + } else { + "`except` handlers should only be exception classes or tuples of exception classes" + .to_string() + } } } @@ -49,6 +56,7 @@ impl Violation for ExceptWithNonExceptionClasses { pub(crate) fn except_with_non_exception_classes( checker: &mut Checker, except_handler: &ExceptHandler, + is_star: bool, ) { let ExceptHandler::ExceptHandler(ast::ExceptHandlerExceptHandler { type_, .. }) = except_handler; @@ -60,9 +68,10 @@ pub(crate) fn except_with_non_exception_classes( expr, Expr::Subscript(_) | Expr::Attribute(_) | Expr::Name(_) | Expr::Call(_), ) { - checker - .diagnostics - .push(Diagnostic::new(ExceptWithNonExceptionClasses, expr.range())); + checker.diagnostics.push(Diagnostic::new( + ExceptWithNonExceptionClasses { is_star }, + expr.range(), + )); } } } diff --git a/crates/ruff_linter/src/rules/flake8_bugbear/snapshots/ruff_linter__rules__flake8_bugbear__tests__B030_B030.py.snap b/crates/ruff_linter/src/rules/flake8_bugbear/snapshots/ruff_linter__rules__flake8_bugbear__tests__B030_B030.py.snap index a182032f77343..c105eb23a028e 100644 --- a/crates/ruff_linter/src/rules/flake8_bugbear/snapshots/ruff_linter__rules__flake8_bugbear__tests__B030_B030.py.snap +++ b/crates/ruff_linter/src/rules/flake8_bugbear/snapshots/ruff_linter__rules__flake8_bugbear__tests__B030_B030.py.snap @@ -47,7 +47,7 @@ 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 +B030.py:39:28: B030 `except*` handlers should only be exception classes or tuples of exception classes | 37 | try: 38 | pass @@ -64,3 +64,39 @@ B030.py:131:8: B030 `except` handlers should only be exception classes or tuples | ^^^^^^^^^^^^^^^ B030 132 | pass | + +B030.py:136:9: B030 `except*` handlers should only be exception classes or tuples of exception classes + | +134 | try: +135 | pass +136 | except* 1: # Error + | ^ B030 +137 | pass + | + +B030.py:141:10: B030 `except*` handlers should only be exception classes or tuples of exception classes + | +139 | try: +140 | pass +141 | except* (1, ValueError): # Error + | ^ B030 +142 | pass + | + +B030.py:146:22: B030 `except*` handlers should only be exception classes or tuples of exception classes + | +144 | try: +145 | pass +146 | except* (ValueError, (RuntimeError, (KeyError, TypeError))): # Error + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ B030 +147 | pass + | + +B030.py:151:9: B030 `except*` handlers should only be exception classes or tuples of exception classes + | +149 | try: +150 | pass +151 | except* (a, b) * (c, d): # B030 + | ^^^^^^^^^^^^^^^ B030 +152 | pass + | From 2a0bb8a5b8246c0d99e21e0ee070f2efc6626deb Mon Sep 17 00:00:00 2001 From: Abdulrahman Alrahma Date: Fri, 6 Dec 2024 20:29:14 +0000 Subject: [PATCH 5/8] handle B904 --- .../test/fixtures/flake8_bugbear/B904.py | 28 ++++++++- .../checkers/ast/analyze/except_handler.rs | 1 + .../rules/raise_without_from_inside_except.rs | 24 ++++--- ...__flake8_bugbear__tests__B904_B904.py.snap | 62 +++++++++++++++++++ 4 files changed, 107 insertions(+), 8 deletions(-) diff --git a/crates/ruff_linter/resources/test/fixtures/flake8_bugbear/B904.py b/crates/ruff_linter/resources/test/fixtures/flake8_bugbear/B904.py index ec4bc9e6f3a12..d752aeca6e9d6 100644 --- a/crates/ruff_linter/resources/test/fixtures/flake8_bugbear/B904.py +++ b/crates/ruff_linter/resources/test/fixtures/flake8_bugbear/B904.py @@ -1,6 +1,6 @@ """ Should emit: -B904 - on lines 10, 11, 16, 62, and 64 +B904 - on lines 10, 11, 16, 62, 64, 79, 81, 87, 88, 93 and 97 """ try: @@ -71,3 +71,29 @@ def context_switch(): match 0: case 0: raise RuntimeError("boom!") + +try: + ... +except* Exception as e: + if ...: + raise RuntimeError("boom!") + else: + raise RuntimeError("bang!") + +try: + raise ValueError +except* ValueError: + if "abc": + raise TypeError + raise UserWarning +except* AssertionError: + raise # Bare `raise` should not be an error +except* Exception as err: + assert err + raise Exception("No cause here...") +except* BaseException as err: + raise err +except* BaseException as err: + raise some_other_err +finally: + raise Exception("Nothing to chain from, so no warning here") diff --git a/crates/ruff_linter/src/checkers/ast/analyze/except_handler.rs b/crates/ruff_linter/src/checkers/ast/analyze/except_handler.rs index 4bb79b106a69c..46bc07e2fbb22 100644 --- a/crates/ruff_linter/src/checkers/ast/analyze/except_handler.rs +++ b/crates/ruff_linter/src/checkers/ast/analyze/except_handler.rs @@ -31,6 +31,7 @@ pub(crate) fn except_handler(except_handler: &ExceptHandler, checker: &mut Check checker, name.as_deref(), body, + is_star, ); } if checker.enabled(Rule::BlindExcept) { diff --git a/crates/ruff_linter/src/rules/flake8_bugbear/rules/raise_without_from_inside_except.rs b/crates/ruff_linter/src/rules/flake8_bugbear/rules/raise_without_from_inside_except.rs index 493cd06f72c3e..c2794292bb52d 100644 --- a/crates/ruff_linter/src/rules/flake8_bugbear/rules/raise_without_from_inside_except.rs +++ b/crates/ruff_linter/src/rules/flake8_bugbear/rules/raise_without_from_inside_except.rs @@ -47,14 +47,22 @@ use crate::checkers::ast::Checker; /// ## References /// - [Python documentation: `raise` statement](https://docs.python.org/3/reference/simple_stmts.html#the-raise-statement) #[derive(ViolationMetadata)] -pub(crate) struct RaiseWithoutFromInsideExcept; +pub(crate) struct RaiseWithoutFromInsideExcept { + is_star: bool, +} impl Violation for RaiseWithoutFromInsideExcept { #[derive_message_formats] fn message(&self) -> String { - "Within an `except` clause, raise exceptions with `raise ... from err` or `raise ... \ - from None` to distinguish them from errors in exception handling" - .to_string() + if self.is_star { + "Within an `except*` clause, raise exceptions with `raise ... from err` or `raise ... \ + from None` to distinguish them from errors in exception handling" + .to_string() + } else { + "Within an `except` clause, raise exceptions with `raise ... from err` or `raise ... \ + from None` to distinguish them from errors in exception handling" + .to_string() + } } } @@ -63,6 +71,7 @@ pub(crate) fn raise_without_from_inside_except( checker: &mut Checker, name: Option<&str>, body: &[Stmt], + is_star: bool, ) { let raises = { let mut visitor = RaiseStatementVisitor::default(); @@ -92,9 +101,10 @@ pub(crate) fn raise_without_from_inside_except( } } - checker - .diagnostics - .push(Diagnostic::new(RaiseWithoutFromInsideExcept, range)); + checker.diagnostics.push(Diagnostic::new( + RaiseWithoutFromInsideExcept { is_star }, + range, + )); } } } diff --git a/crates/ruff_linter/src/rules/flake8_bugbear/snapshots/ruff_linter__rules__flake8_bugbear__tests__B904_B904.py.snap b/crates/ruff_linter/src/rules/flake8_bugbear/snapshots/ruff_linter__rules__flake8_bugbear__tests__B904_B904.py.snap index 4b46459c289f7..dc4604c1bf312 100644 --- a/crates/ruff_linter/src/rules/flake8_bugbear/snapshots/ruff_linter__rules__flake8_bugbear__tests__B904_B904.py.snap +++ b/crates/ruff_linter/src/rules/flake8_bugbear/snapshots/ruff_linter__rules__flake8_bugbear__tests__B904_B904.py.snap @@ -66,4 +66,66 @@ B904.py:73:13: B904 Within an `except` clause, raise exceptions with `raise ... 72 | case 0: 73 | raise RuntimeError("boom!") | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ B904 +74 | +75 | try: + | + +B904.py:79:9: B904 Within an `except*` clause, raise exceptions with `raise ... from err` or `raise ... from None` to distinguish them from errors in exception handling + | +77 | except* Exception as e: +78 | if ...: +79 | raise RuntimeError("boom!") + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ B904 +80 | else: +81 | raise RuntimeError("bang!") + | + +B904.py:81:9: B904 Within an `except*` clause, raise exceptions with `raise ... from err` or `raise ... from None` to distinguish them from errors in exception handling + | +79 | raise RuntimeError("boom!") +80 | else: +81 | raise RuntimeError("bang!") + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ B904 +82 | +83 | try: + | + +B904.py:87:9: B904 Within an `except*` clause, raise exceptions with `raise ... from err` or `raise ... from None` to distinguish them from errors in exception handling + | +85 | except* ValueError: +86 | if "abc": +87 | raise TypeError + | ^^^^^^^^^^^^^^^ B904 +88 | raise UserWarning +89 | except* AssertionError: + | + +B904.py:88:5: B904 Within an `except*` clause, raise exceptions with `raise ... from err` or `raise ... from None` to distinguish them from errors in exception handling + | +86 | if "abc": +87 | raise TypeError +88 | raise UserWarning + | ^^^^^^^^^^^^^^^^^ B904 +89 | except* AssertionError: +90 | raise # Bare `raise` should not be an error + | + +B904.py:93:5: B904 Within an `except*` clause, raise exceptions with `raise ... from err` or `raise ... from None` to distinguish them from errors in exception handling + | +91 | except* Exception as err: +92 | assert err +93 | raise Exception("No cause here...") + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ B904 +94 | except* BaseException as err: +95 | raise err + | + +B904.py:97:5: B904 Within an `except*` clause, raise exceptions with `raise ... from err` or `raise ... from None` to distinguish them from errors in exception handling + | +95 | raise err +96 | except* BaseException as err: +97 | raise some_other_err + | ^^^^^^^^^^^^^^^^^^^^ B904 +98 | finally: +99 | raise Exception("Nothing to chain from, so no warning here") | From f6b0abc350f1e5d5c98e3c6696ea75702dec9443 Mon Sep 17 00:00:00 2001 From: Abdulrahman Alrahma Date: Sat, 7 Dec 2024 19:01:14 +0000 Subject: [PATCH 6/8] get is_star from checker instead of propagating thorugh visitor-trait --- .../src/checkers/ast/analyze/except_handler.rs | 11 +++-------- .../ruff_linter/src/checkers/ast/analyze/statement.rs | 3 +-- crates/ruff_linter/src/checkers/ast/mod.rs | 7 +++---- .../flake8_bugbear/rules/duplicate_exceptions.rs | 11 ++++++----- .../flake8_bugbear/rules/except_with_empty_tuple.rs | 11 ++++++----- .../rules/except_with_non_exception_classes.rs | 6 +++++- .../rules/raise_without_from_inside_except.rs | 7 ++++++- .../src/rules/pydoclint/rules/check_docstring.rs | 2 +- crates/ruff_linter/src/rules/tryceratops/helpers.rs | 2 +- crates/ruff_python_ast/src/visitor.rs | 6 +++--- .../tests/visitor.rs | 2 +- 11 files changed, 36 insertions(+), 32 deletions(-) diff --git a/crates/ruff_linter/src/checkers/ast/analyze/except_handler.rs b/crates/ruff_linter/src/checkers/ast/analyze/except_handler.rs index 46bc07e2fbb22..0c3d6372ab225 100644 --- a/crates/ruff_linter/src/checkers/ast/analyze/except_handler.rs +++ b/crates/ruff_linter/src/checkers/ast/analyze/except_handler.rs @@ -8,7 +8,7 @@ use crate::rules::{ }; /// Run lint rules over an [`ExceptHandler`] syntax node. -pub(crate) fn except_handler(except_handler: &ExceptHandler, checker: &mut Checker, is_star: bool) { +pub(crate) fn except_handler(except_handler: &ExceptHandler, checker: &mut Checker) { match except_handler { ExceptHandler::ExceptHandler(ast::ExceptHandlerExceptHandler { type_, @@ -31,7 +31,6 @@ pub(crate) fn except_handler(except_handler: &ExceptHandler, checker: &mut Check checker, name.as_deref(), body, - is_star, ); } if checker.enabled(Rule::BlindExcept) { @@ -61,14 +60,10 @@ pub(crate) fn except_handler(except_handler: &ExceptHandler, checker: &mut Check ); } if checker.enabled(Rule::ExceptWithEmptyTuple) { - flake8_bugbear::rules::except_with_empty_tuple(checker, except_handler, is_star); + flake8_bugbear::rules::except_with_empty_tuple(checker, except_handler); } if checker.enabled(Rule::ExceptWithNonExceptionClasses) { - flake8_bugbear::rules::except_with_non_exception_classes( - checker, - except_handler, - is_star, - ); + flake8_bugbear::rules::except_with_non_exception_classes(checker, except_handler); } if checker.enabled(Rule::BinaryOpException) { pylint::rules::binary_op_exception(checker, except_handler); diff --git a/crates/ruff_linter/src/checkers/ast/analyze/statement.rs b/crates/ruff_linter/src/checkers/ast/analyze/statement.rs index e20951c8724ac..73c86c2b33bee 100644 --- a/crates/ruff_linter/src/checkers/ast/analyze/statement.rs +++ b/crates/ruff_linter/src/checkers/ast/analyze/statement.rs @@ -1435,7 +1435,6 @@ pub(crate) fn statement(stmt: &Stmt, checker: &mut Checker) { handlers, orelse, finalbody, - is_star, .. }) => { if checker.enabled(Rule::TooManyNestedBlocks) { @@ -1460,7 +1459,7 @@ pub(crate) fn statement(stmt: &Stmt, checker: &mut Checker) { Rule::DuplicateHandlerException, Rule::DuplicateTryBlockException, ]) { - flake8_bugbear::rules::duplicate_exceptions(checker, handlers, *is_star); + flake8_bugbear::rules::duplicate_exceptions(checker, handlers); } if checker.enabled(Rule::RedundantTupleInExceptionHandler) { flake8_bugbear::rules::redundant_tuple_in_exception_handler(checker, handlers); diff --git a/crates/ruff_linter/src/checkers/ast/mod.rs b/crates/ruff_linter/src/checkers/ast/mod.rs index 122976d49048e..5fcf91b8e5bb0 100644 --- a/crates/ruff_linter/src/checkers/ast/mod.rs +++ b/crates/ruff_linter/src/checkers/ast/mod.rs @@ -892,7 +892,6 @@ impl<'a> Visitor<'a> for Checker<'a> { handlers, orelse, finalbody, - is_star, .. }, ) => { @@ -909,7 +908,7 @@ impl<'a> Visitor<'a> for Checker<'a> { for except_handler in handlers { self.semantic.push_branch(); - self.visit_except_handler(except_handler, *is_star); + self.visit_except_handler(except_handler); self.semantic.pop_branch(); } @@ -1565,7 +1564,7 @@ impl<'a> Visitor<'a> for Checker<'a> { self.semantic.pop_node(); } - fn visit_except_handler(&mut self, except_handler: &'a ExceptHandler, is_star: bool) { + fn visit_except_handler(&mut self, except_handler: &'a ExceptHandler) { let flags_snapshot = self.semantic.flags; self.semantic.flags |= SemanticModelFlags::EXCEPTION_HANDLER; @@ -1610,7 +1609,7 @@ impl<'a> Visitor<'a> for Checker<'a> { } // Step 4: Analysis - analyze::except_handler(except_handler, self, is_star); + analyze::except_handler(except_handler, self); self.semantic.flags = flags_snapshot; } diff --git a/crates/ruff_linter/src/rules/flake8_bugbear/rules/duplicate_exceptions.rs b/crates/ruff_linter/src/rules/flake8_bugbear/rules/duplicate_exceptions.rs index 16392eb22a8c5..3bc4fcdfeff3b 100644 --- a/crates/ruff_linter/src/rules/flake8_bugbear/rules/duplicate_exceptions.rs +++ b/crates/ruff_linter/src/rules/flake8_bugbear/rules/duplicate_exceptions.rs @@ -175,11 +175,7 @@ fn duplicate_handler_exceptions<'a>( } /// B025 -pub(crate) fn duplicate_exceptions( - checker: &mut Checker, - handlers: &[ExceptHandler], - is_star: bool, -) { +pub(crate) fn duplicate_exceptions(checker: &mut Checker, handlers: &[ExceptHandler]) { let mut seen: FxHashSet = FxHashSet::default(); let mut duplicates: FxHashMap> = FxHashMap::default(); for handler in handlers { @@ -213,6 +209,11 @@ pub(crate) fn duplicate_exceptions( } } + let is_star = checker + .semantic() + .current_statement() + .as_try_stmt() + .is_some_and(|try_stmt| try_stmt.is_star); if checker.enabled(Rule::DuplicateTryBlockException) { for (name, exprs) in duplicates { for expr in exprs { diff --git a/crates/ruff_linter/src/rules/flake8_bugbear/rules/except_with_empty_tuple.rs b/crates/ruff_linter/src/rules/flake8_bugbear/rules/except_with_empty_tuple.rs index 519ba1ac01de1..f52938a525efa 100644 --- a/crates/ruff_linter/src/rules/flake8_bugbear/rules/except_with_empty_tuple.rs +++ b/crates/ruff_linter/src/rules/flake8_bugbear/rules/except_with_empty_tuple.rs @@ -50,11 +50,7 @@ impl Violation for ExceptWithEmptyTuple { } /// B029 -pub(crate) fn except_with_empty_tuple( - checker: &mut Checker, - except_handler: &ExceptHandler, - is_star: bool, -) { +pub(crate) fn except_with_empty_tuple(checker: &mut Checker, except_handler: &ExceptHandler) { let ExceptHandler::ExceptHandler(ast::ExceptHandlerExceptHandler { type_, .. }) = except_handler; let Some(type_) = type_ else { @@ -63,6 +59,11 @@ pub(crate) fn except_with_empty_tuple( let Expr::Tuple(ast::ExprTuple { elts, .. }) = type_.as_ref() else { return; }; + let is_star = checker + .semantic() + .current_statement() + .as_try_stmt() + .is_some_and(|try_stmt| try_stmt.is_star); if elts.is_empty() { checker.diagnostics.push(Diagnostic::new( ExceptWithEmptyTuple { is_star }, diff --git a/crates/ruff_linter/src/rules/flake8_bugbear/rules/except_with_non_exception_classes.rs b/crates/ruff_linter/src/rules/flake8_bugbear/rules/except_with_non_exception_classes.rs index 8b32d80b14750..13f9f544fa00f 100644 --- a/crates/ruff_linter/src/rules/flake8_bugbear/rules/except_with_non_exception_classes.rs +++ b/crates/ruff_linter/src/rules/flake8_bugbear/rules/except_with_non_exception_classes.rs @@ -56,7 +56,6 @@ impl Violation for ExceptWithNonExceptionClasses { pub(crate) fn except_with_non_exception_classes( checker: &mut Checker, except_handler: &ExceptHandler, - is_star: bool, ) { let ExceptHandler::ExceptHandler(ast::ExceptHandlerExceptHandler { type_, .. }) = except_handler; @@ -68,6 +67,11 @@ pub(crate) fn except_with_non_exception_classes( expr, Expr::Subscript(_) | Expr::Attribute(_) | Expr::Name(_) | Expr::Call(_), ) { + let is_star = checker + .semantic() + .current_statement() + .as_try_stmt() + .is_some_and(|try_stmt| try_stmt.is_star); checker.diagnostics.push(Diagnostic::new( ExceptWithNonExceptionClasses { is_star }, expr.range(), diff --git a/crates/ruff_linter/src/rules/flake8_bugbear/rules/raise_without_from_inside_except.rs b/crates/ruff_linter/src/rules/flake8_bugbear/rules/raise_without_from_inside_except.rs index c2794292bb52d..b989606154da0 100644 --- a/crates/ruff_linter/src/rules/flake8_bugbear/rules/raise_without_from_inside_except.rs +++ b/crates/ruff_linter/src/rules/flake8_bugbear/rules/raise_without_from_inside_except.rs @@ -71,7 +71,6 @@ pub(crate) fn raise_without_from_inside_except( checker: &mut Checker, name: Option<&str>, body: &[Stmt], - is_star: bool, ) { let raises = { let mut visitor = RaiseStatementVisitor::default(); @@ -101,6 +100,12 @@ pub(crate) fn raise_without_from_inside_except( } } + let is_star = checker + .semantic() + .current_statement() + .as_try_stmt() + .is_some_and(|try_stmt| try_stmt.is_star); + checker.diagnostics.push(Diagnostic::new( RaiseWithoutFromInsideExcept { is_star }, range, diff --git a/crates/ruff_linter/src/rules/pydoclint/rules/check_docstring.rs b/crates/ruff_linter/src/rules/pydoclint/rules/check_docstring.rs index 35f5dab59e3da..b315d4cbc4183 100644 --- a/crates/ruff_linter/src/rules/pydoclint/rules/check_docstring.rs +++ b/crates/ruff_linter/src/rules/pydoclint/rules/check_docstring.rs @@ -617,7 +617,7 @@ impl<'a> BodyVisitor<'a> { } impl<'a> Visitor<'a> for BodyVisitor<'a> { - fn visit_except_handler(&mut self, handler: &'a ast::ExceptHandler, _is_star: bool) { + fn visit_except_handler(&mut self, handler: &'a ast::ExceptHandler) { let ast::ExceptHandler::ExceptHandler(handler_inner) = handler; self.currently_suspended_exceptions = handler_inner.type_.as_deref(); visitor::walk_except_handler(self, handler); diff --git a/crates/ruff_linter/src/rules/tryceratops/helpers.rs b/crates/ruff_linter/src/rules/tryceratops/helpers.rs index 6e8ff9b7b9b4c..f38ca0b16c50e 100644 --- a/crates/ruff_linter/src/rules/tryceratops/helpers.rs +++ b/crates/ruff_linter/src/rules/tryceratops/helpers.rs @@ -53,7 +53,7 @@ impl<'b> Visitor<'b> for LoggerCandidateVisitor<'_, 'b> { visitor::walk_expr(self, expr); } - fn visit_except_handler(&mut self, _except_handler: &'b ExceptHandler, _is_star: bool) { + fn visit_except_handler(&mut self, _except_handler: &'b ExceptHandler) { // Don't recurse into exception handlers, since we'll re-run the visitor on any such // handlers. } diff --git a/crates/ruff_python_ast/src/visitor.rs b/crates/ruff_python_ast/src/visitor.rs index 4f37ee3a03e1c..0f16af535461c 100644 --- a/crates/ruff_python_ast/src/visitor.rs +++ b/crates/ruff_python_ast/src/visitor.rs @@ -51,7 +51,7 @@ pub trait Visitor<'a> { fn visit_comprehension(&mut self, comprehension: &'a Comprehension) { walk_comprehension(self, comprehension); } - fn visit_except_handler(&mut self, except_handler: &'a ExceptHandler, _is_star: bool) { + fn visit_except_handler(&mut self, except_handler: &'a ExceptHandler) { walk_except_handler(self, except_handler); } fn visit_arguments(&mut self, arguments: &'a Arguments) { @@ -286,12 +286,12 @@ pub fn walk_stmt<'a, V: Visitor<'a> + ?Sized>(visitor: &mut V, stmt: &'a Stmt) { handlers, orelse, finalbody, - is_star, + is_star: _, range: _, }) => { visitor.visit_body(body); for except_handler in handlers { - visitor.visit_except_handler(except_handler, *is_star); + visitor.visit_except_handler(except_handler); } visitor.visit_body(orelse); visitor.visit_body(finalbody); diff --git a/crates/ruff_python_ast_integration_tests/tests/visitor.rs b/crates/ruff_python_ast_integration_tests/tests/visitor.rs index c1a11c98f7bd0..128d0c3f12184 100644 --- a/crates/ruff_python_ast_integration_tests/tests/visitor.rs +++ b/crates/ruff_python_ast_integration_tests/tests/visitor.rs @@ -247,7 +247,7 @@ impl Visitor<'_> for RecordVisitor { self.exit_node(); } - fn visit_except_handler(&mut self, except_handler: &ExceptHandler, _is_star: bool) { + fn visit_except_handler(&mut self, except_handler: &ExceptHandler) { self.enter_node(except_handler); walk_except_handler(self, except_handler); self.exit_node(); From 5cd29d9a8ee887eb5c884d264eb8d6f33be4e35e Mon Sep 17 00:00:00 2001 From: Micha Reiser Date: Sun, 8 Dec 2024 18:29:47 +0100 Subject: [PATCH 7/8] Move `is_star` computation into diagnostic-branch --- .../flake8_bugbear/rules/duplicate_exceptions.rs | 10 +++++----- .../flake8_bugbear/rules/except_with_empty_tuple.rs | 11 ++++++----- 2 files changed, 11 insertions(+), 10 deletions(-) diff --git a/crates/ruff_linter/src/rules/flake8_bugbear/rules/duplicate_exceptions.rs b/crates/ruff_linter/src/rules/flake8_bugbear/rules/duplicate_exceptions.rs index 3bc4fcdfeff3b..976fe26d31810 100644 --- a/crates/ruff_linter/src/rules/flake8_bugbear/rules/duplicate_exceptions.rs +++ b/crates/ruff_linter/src/rules/flake8_bugbear/rules/duplicate_exceptions.rs @@ -209,14 +209,14 @@ pub(crate) fn duplicate_exceptions(checker: &mut Checker, handlers: &[ExceptHand } } - let is_star = checker - .semantic() - .current_statement() - .as_try_stmt() - .is_some_and(|try_stmt| try_stmt.is_star); if checker.enabled(Rule::DuplicateTryBlockException) { for (name, exprs) in duplicates { for expr in exprs { + let is_star = checker + .semantic() + .current_statement() + .as_try_stmt() + .is_some_and(|try_stmt| try_stmt.is_star); checker.diagnostics.push(Diagnostic::new( DuplicateTryBlockException { name: name.segments().join("."), diff --git a/crates/ruff_linter/src/rules/flake8_bugbear/rules/except_with_empty_tuple.rs b/crates/ruff_linter/src/rules/flake8_bugbear/rules/except_with_empty_tuple.rs index f52938a525efa..0cded14b3b3f5 100644 --- a/crates/ruff_linter/src/rules/flake8_bugbear/rules/except_with_empty_tuple.rs +++ b/crates/ruff_linter/src/rules/flake8_bugbear/rules/except_with_empty_tuple.rs @@ -59,12 +59,13 @@ pub(crate) fn except_with_empty_tuple(checker: &mut Checker, except_handler: &Ex let Expr::Tuple(ast::ExprTuple { elts, .. }) = type_.as_ref() else { return; }; - let is_star = checker - .semantic() - .current_statement() - .as_try_stmt() - .is_some_and(|try_stmt| try_stmt.is_star); + if elts.is_empty() { + let is_star = checker + .semantic() + .current_statement() + .as_try_stmt() + .is_some_and(|try_stmt| try_stmt.is_star); checker.diagnostics.push(Diagnostic::new( ExceptWithEmptyTuple { is_star }, except_handler.range(), From 173f0a88a57a595196a54b7f63a298d8fb2ff0f6 Mon Sep 17 00:00:00 2001 From: Micha Reiser Date: Sun, 8 Dec 2024 18:32:44 +0100 Subject: [PATCH 8/8] Rustfmt --- .../src/rules/flake8_bugbear/rules/except_with_empty_tuple.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/ruff_linter/src/rules/flake8_bugbear/rules/except_with_empty_tuple.rs b/crates/ruff_linter/src/rules/flake8_bugbear/rules/except_with_empty_tuple.rs index 0cded14b3b3f5..d43fc889e211b 100644 --- a/crates/ruff_linter/src/rules/flake8_bugbear/rules/except_with_empty_tuple.rs +++ b/crates/ruff_linter/src/rules/flake8_bugbear/rules/except_with_empty_tuple.rs @@ -59,7 +59,7 @@ pub(crate) fn except_with_empty_tuple(checker: &mut Checker, except_handler: &Ex let Expr::Tuple(ast::ExprTuple { elts, .. }) = type_.as_ref() else { return; }; - + if elts.is_empty() { let is_star = checker .semantic()