diff --git a/crates/ruff_linter/resources/test/fixtures/flake8_raise/RSE102.py b/crates/ruff_linter/resources/test/fixtures/flake8_raise/RSE102.py index bba0e98b17caf..0d737c8ca4fb0 100644 --- a/crates/ruff_linter/resources/test/fixtures/flake8_raise/RSE102.py +++ b/crates/ruff_linter/resources/test/fixtures/flake8_raise/RSE102.py @@ -93,3 +93,15 @@ def func(): # OK raise func() + + +# OK +future = executor.submit(float, "a") +if future.exception(): + raise future.exception() + + +# RSE102 +future = executor.submit(float, "a") +if future.exception(): + raise future.Exception() diff --git a/crates/ruff_linter/src/rules/flake8_raise/rules/unnecessary_paren_on_raise_exception.rs b/crates/ruff_linter/src/rules/flake8_raise/rules/unnecessary_paren_on_raise_exception.rs index a056bbc755f8d..9c13127127101 100644 --- a/crates/ruff_linter/src/rules/flake8_raise/rules/unnecessary_paren_on_raise_exception.rs +++ b/crates/ruff_linter/src/rules/flake8_raise/rules/unnecessary_paren_on_raise_exception.rs @@ -76,15 +76,34 @@ pub(crate) fn unnecessary_paren_on_raise_exception(checker: &mut Checker, expr: None }; - // `ctypes.WinError()` is a function, not a class. It's part of the standard library, so - // we might as well get it right. - if exception_type.is_none() - && checker + if exception_type.is_none() { + // If the method name doesn't _look_ like a class (i.e., it's lowercase), it's + // probably a function call, not a class. + let identifier = match func.as_ref() { + Expr::Name(ast::ExprName { id, .. }) => Some(id.as_str()), + Expr::Attribute(ast::ExprAttribute { attr, .. }) => Some(attr.as_str()), + _ => None, + }; + if identifier.is_some_and(|identifier| { + identifier + .strip_prefix('_') + .unwrap_or(identifier) + .chars() + .next() + .is_some_and(char::is_lowercase) + }) { + return; + } + + // `ctypes.WinError()` is a function, not a class. It's part of the standard library, so + // we might as well get it right. + if checker .semantic() .resolve_call_path(func) .is_some_and(|call_path| matches!(call_path.as_slice(), ["ctypes", "WinError"])) - { - return; + { + return; + } } let mut diagnostic = Diagnostic::new(UnnecessaryParenOnRaiseException, arguments.range()); diff --git a/crates/ruff_linter/src/rules/flake8_raise/snapshots/ruff_linter__rules__flake8_raise__tests__unnecessary-paren-on-raise-exception_RSE102.py.snap b/crates/ruff_linter/src/rules/flake8_raise/snapshots/ruff_linter__rules__flake8_raise__tests__unnecessary-paren-on-raise-exception_RSE102.py.snap index d1d89829c673f..d0dd2b4084b53 100644 --- a/crates/ruff_linter/src/rules/flake8_raise/snapshots/ruff_linter__rules__flake8_raise__tests__unnecessary-paren-on-raise-exception_RSE102.py.snap +++ b/crates/ruff_linter/src/rules/flake8_raise/snapshots/ruff_linter__rules__flake8_raise__tests__unnecessary-paren-on-raise-exception_RSE102.py.snap @@ -281,4 +281,18 @@ RSE102.py:84:10: RSE102 [*] Unnecessary parentheses on raised exception 86 86 | # OK 87 87 | raise ctypes.WinError() +RSE102.py:107:27: RSE102 [*] Unnecessary parentheses on raised exception + | +105 | future = executor.submit(float, "a") +106 | if future.exception(): +107 | raise future.Exception() + | ^^ RSE102 + | + = help: Remove unnecessary parentheses +ℹ Unsafe fix +104 104 | # RSE102 +105 105 | future = executor.submit(float, "a") +106 106 | if future.exception(): +107 |- raise future.Exception() + 107 |+ raise future.Exception