diff --git a/crates/ruff_linter/resources/test/fixtures/flake8_blind_except/BLE.py b/crates/ruff_linter/resources/test/fixtures/flake8_blind_except/BLE.py index 5bfeaef08c..32c0dc87e2 100644 --- a/crates/ruff_linter/resources/test/fixtures/flake8_blind_except/BLE.py +++ b/crates/ruff_linter/resources/test/fixtures/flake8_blind_except/BLE.py @@ -162,3 +162,86 @@ except Exception: exception("An error occurred") else: exception("An error occurred") + +# Test tuple exceptions +try: + pass +except (Exception,): + pass + +try: + pass +except (Exception, ValueError): + pass + +try: + pass +except (ValueError, Exception): + pass + +try: + pass +except (ValueError, Exception) as e: + print(e) + +try: + pass +except (BaseException, TypeError): + pass + +try: + pass +except (TypeError, BaseException): + pass + +try: + pass +except (Exception, BaseException): + pass + +try: + pass +except (BaseException, Exception): + pass + +# Test nested tuples +try: + pass +except ((Exception, ValueError), TypeError): + pass + +try: + pass +except (ValueError, (BaseException, TypeError)): + pass + +# Test valid tuple exceptions (should not trigger) +try: + pass +except (ValueError, TypeError): + pass + +try: + pass +except (OSError, FileNotFoundError): + pass + +try: + pass +except (OSError, FileNotFoundError) as e: + print(e) + +try: + pass +except (Exception, ValueError): + critical("...", exc_info=True) + +try: + pass +except (Exception, ValueError): + raise + +try: + pass +except (Exception, ValueError) as e: + raise e diff --git a/crates/ruff_linter/src/rules/flake8_blind_except/rules/blind_except.rs b/crates/ruff_linter/src/rules/flake8_blind_except/rules/blind_except.rs index 9def259746..6698873eb7 100644 --- a/crates/ruff_linter/src/rules/flake8_blind_except/rules/blind_except.rs +++ b/crates/ruff_linter/src/rules/flake8_blind_except/rules/blind_except.rs @@ -75,6 +75,22 @@ impl Violation for BlindExcept { } } +fn contains_blind_exception<'a>( + semantic: &'a SemanticModel, + expr: &'a Expr, +) -> Option<(&'a str, ruff_text_size::TextRange)> { + match expr { + Expr::Tuple(ast::ExprTuple { elts, .. }) => elts + .iter() + .find_map(|elt| contains_blind_exception(semantic, elt)), + _ => { + let builtin_exception_type = semantic.resolve_builtin_symbol(expr)?; + matches!(builtin_exception_type, "BaseException" | "Exception") + .then(|| (builtin_exception_type, expr.range())) + } + } +} + /// BLE001 pub(crate) fn blind_except( checker: &Checker, @@ -87,12 +103,9 @@ pub(crate) fn blind_except( }; let semantic = checker.semantic(); - let Some(builtin_exception_type) = semantic.resolve_builtin_symbol(type_) else { + let Some((builtin_exception_type, range)) = contains_blind_exception(semantic, type_) else { return; }; - if !matches!(builtin_exception_type, "BaseException" | "Exception") { - return; - } // If the exception is re-raised, don't flag an error. let mut visitor = ReraiseVisitor::new(name); @@ -110,9 +123,9 @@ pub(crate) fn blind_except( checker.report_diagnostic( BlindExcept { - name: builtin_exception_type.to_string(), + name: builtin_exception_type.into(), }, - type_.range(), + range, ); } diff --git a/crates/ruff_linter/src/rules/flake8_blind_except/snapshots/ruff_linter__rules__flake8_blind_except__tests__BLE001_BLE.py.snap b/crates/ruff_linter/src/rules/flake8_blind_except/snapshots/ruff_linter__rules__flake8_blind_except__tests__BLE001_BLE.py.snap index 575b116b67..8e0f48c034 100644 --- a/crates/ruff_linter/src/rules/flake8_blind_except/snapshots/ruff_linter__rules__flake8_blind_except__tests__BLE001_BLE.py.snap +++ b/crates/ruff_linter/src/rules/flake8_blind_except/snapshots/ruff_linter__rules__flake8_blind_except__tests__BLE001_BLE.py.snap @@ -147,3 +147,93 @@ BLE.py:131:8: BLE001 Do not catch blind exception: `Exception` | ^^^^^^^^^ BLE001 132 | critical("...", exc_info=None) | + +BLE.py:169:9: BLE001 Do not catch blind exception: `Exception` + | +167 | try: +168 | pass +169 | except (Exception,): + | ^^^^^^^^^ BLE001 +170 | pass + | + +BLE.py:174:9: BLE001 Do not catch blind exception: `Exception` + | +172 | try: +173 | pass +174 | except (Exception, ValueError): + | ^^^^^^^^^ BLE001 +175 | pass + | + +BLE.py:179:21: BLE001 Do not catch blind exception: `Exception` + | +177 | try: +178 | pass +179 | except (ValueError, Exception): + | ^^^^^^^^^ BLE001 +180 | pass + | + +BLE.py:184:21: BLE001 Do not catch blind exception: `Exception` + | +182 | try: +183 | pass +184 | except (ValueError, Exception) as e: + | ^^^^^^^^^ BLE001 +185 | print(e) + | + +BLE.py:189:9: BLE001 Do not catch blind exception: `BaseException` + | +187 | try: +188 | pass +189 | except (BaseException, TypeError): + | ^^^^^^^^^^^^^ BLE001 +190 | pass + | + +BLE.py:194:20: BLE001 Do not catch blind exception: `BaseException` + | +192 | try: +193 | pass +194 | except (TypeError, BaseException): + | ^^^^^^^^^^^^^ BLE001 +195 | pass + | + +BLE.py:199:9: BLE001 Do not catch blind exception: `Exception` + | +197 | try: +198 | pass +199 | except (Exception, BaseException): + | ^^^^^^^^^ BLE001 +200 | pass + | + +BLE.py:204:9: BLE001 Do not catch blind exception: `BaseException` + | +202 | try: +203 | pass +204 | except (BaseException, Exception): + | ^^^^^^^^^^^^^ BLE001 +205 | pass + | + +BLE.py:210:10: BLE001 Do not catch blind exception: `Exception` + | +208 | try: +209 | pass +210 | except ((Exception, ValueError), TypeError): + | ^^^^^^^^^ BLE001 +211 | pass + | + +BLE.py:215:22: BLE001 Do not catch blind exception: `BaseException` + | +213 | try: +214 | pass +215 | except (ValueError, (BaseException, TypeError)): + | ^^^^^^^^^^^^^ BLE001 +216 | pass + |