From ca54ea1d57105ee1e202103ca03150bc71c52635 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mikko=20Lepp=C3=A4nen?= Date: Thu, 29 Feb 2024 18:31:14 +0200 Subject: [PATCH 1/5] rule(PLW0133): Implement useless exception statement --- .../pylint/useless_exception_statement.py | 110 ++++++++++++ .../src/checkers/ast/analyze/statement.rs | 3 + crates/ruff_linter/src/codes.rs | 1 + crates/ruff_linter/src/rules/pylint/mod.rs | 4 + .../ruff_linter/src/rules/pylint/rules/mod.rs | 2 + .../rules/useless_exception_statement.rs | 110 ++++++++++++ ...LW0133_useless_exception_statement.py.snap | 164 ++++++++++++++++++ ruff.schema.json | 1 + 8 files changed, 395 insertions(+) create mode 100644 crates/ruff_linter/resources/test/fixtures/pylint/useless_exception_statement.py create mode 100644 crates/ruff_linter/src/rules/pylint/rules/useless_exception_statement.rs create mode 100644 crates/ruff_linter/src/rules/pylint/snapshots/ruff_linter__rules__pylint__tests__PLW0133_useless_exception_statement.py.snap diff --git a/crates/ruff_linter/resources/test/fixtures/pylint/useless_exception_statement.py b/crates/ruff_linter/resources/test/fixtures/pylint/useless_exception_statement.py new file mode 100644 index 0000000000000..9ed511ca0726b --- /dev/null +++ b/crates/ruff_linter/resources/test/fixtures/pylint/useless_exception_statement.py @@ -0,0 +1,110 @@ + +# Violation test cases: PLW0133 + +# Test case 1: Useless exception statement +from abc import ABC, abstractmethod +from contextlib import suppress + + +def test(): + AssertionError("This is an assertion error") # PLW0133 + +# Test case 2: Useless exception statement in try-except block + +def test(): + try: + Exception("This is an exception") # PLW0133 + except Exception as e: + pass # PL + +# Test case 3: Useless exception statement in if statement + +def test(): + if True: + RuntimeError("This is an exception") # PLW0133 + +# Test case 4: Useless exception statement in class + +def test(): + class Test: + def __init__(self): + TypeError("This is an exception") # PLW0133 + +# Test case 5: Useless exception statement in function + +def test(): + def f(): + IndexError("This is an exception") # PLW0133 + f() + +# Test case 6: Useless exception statement in while loop + +def test(): + while True: + KeyError("This is an exception") # PLW0133 + +# Test case 7: Useless exception statement in abstract class + +def test(): + class Test(ABC): + @abstractmethod + def test(self): + NotImplementedError("This is an exception") # PLW0133 + + +# Test case 8: Useless exception statement inside context manager + +def test(): + with suppress(AttributeError): + AttributeError("This is an exception") # PLW0133 + + +# Non-violation test cases: PLW0133 + +# Test case 1: Used exception statement in try-except block + +def test(): + raise Exception("This is an exception") # OK + +# Test case 2: Used exception statement in if statement + +def test(): + if True: + raise ValueError("This is an exception") # OK + +# Test case 3: Used exception statement in class + +def test(): + class Test: + def __init__(self): + raise TypeError("This is an exception") # OK + +# Test case 4: Exception statement used in list comprehension + +def test(): + [ValueError("This is an exception") for i in range(10)] # OK + + +# Test case 5: Exception statement used when initializing a dictionary + +def test(): + {i: TypeError("This is an exception") for i in range(10)} # OK + +# Test case 6: Exception statement used in function + +def test(): + def f(): + raise IndexError("This is an exception") # OK + f() + +# Test case 7: Exception statement used in variable assignment + +def test(): + e = KeyError("This is an exception") # OK + + +# Test case 8: Exception statement inside context manager + +def test(): + with suppress(AttributeError): + raise AttributeError("This is an exception") # OK \ No newline at end of file diff --git a/crates/ruff_linter/src/checkers/ast/analyze/statement.rs b/crates/ruff_linter/src/checkers/ast/analyze/statement.rs index d8147fe2db1f1..9d224e4f00111 100644 --- a/crates/ruff_linter/src/checkers/ast/analyze/statement.rs +++ b/crates/ruff_linter/src/checkers/ast/analyze/statement.rs @@ -1632,6 +1632,9 @@ pub(crate) fn statement(stmt: &Stmt, checker: &mut Checker) { if checker.enabled(Rule::RepeatedAppend) { refurb::rules::repeated_append(checker, stmt); } + if checker.enabled(Rule::UselessExceptionStatement) { + pylint::rules::useless_exception_statement(checker, value); + } } _ => {} } diff --git a/crates/ruff_linter/src/codes.rs b/crates/ruff_linter/src/codes.rs index d313c6816b800..fd22c0bd2da88 100644 --- a/crates/ruff_linter/src/codes.rs +++ b/crates/ruff_linter/src/codes.rs @@ -292,6 +292,7 @@ pub fn code_to_rule(linter: Linter, code: &str) -> Option<(RuleGroup, Rule)> { (Pylint, "W0127") => (RuleGroup::Stable, rules::pylint::rules::SelfAssigningVariable), (Pylint, "W0129") => (RuleGroup::Stable, rules::pylint::rules::AssertOnStringLiteral), (Pylint, "W0131") => (RuleGroup::Stable, rules::pylint::rules::NamedExprWithoutContext), + (Pylint, "W0133") => (RuleGroup::Preview, rules::pylint::rules::UselessExceptionStatement), (Pylint, "W0245") => (RuleGroup::Preview, rules::pylint::rules::SuperWithoutBrackets), (Pylint, "W0406") => (RuleGroup::Stable, rules::pylint::rules::ImportSelf), (Pylint, "W0602") => (RuleGroup::Stable, rules::pylint::rules::GlobalVariableNotAssigned), diff --git a/crates/ruff_linter/src/rules/pylint/mod.rs b/crates/ruff_linter/src/rules/pylint/mod.rs index e9599b963aa70..2d813d1bc9d28 100644 --- a/crates/ruff_linter/src/rules/pylint/mod.rs +++ b/crates/ruff_linter/src/rules/pylint/mod.rs @@ -176,6 +176,10 @@ mod tests { Rule::UnnecessaryDictIndexLookup, Path::new("unnecessary_dict_index_lookup.py") )] + #[test_case( + Rule::UselessExceptionStatement, + Path::new("useless_exception_statement.py") + )] fn rules(rule_code: Rule, path: &Path) -> Result<()> { let snapshot = format!("{}_{}", rule_code.noqa_code(), path.to_string_lossy()); let diagnostics = test_path( diff --git a/crates/ruff_linter/src/rules/pylint/rules/mod.rs b/crates/ruff_linter/src/rules/pylint/rules/mod.rs index 9963339844a84..2d341e8f6c544 100644 --- a/crates/ruff_linter/src/rules/pylint/rules/mod.rs +++ b/crates/ruff_linter/src/rules/pylint/rules/mod.rs @@ -78,6 +78,7 @@ pub(crate) use unnecessary_lambda::*; pub(crate) use unnecessary_list_index_lookup::*; pub(crate) use unspecified_encoding::*; pub(crate) use useless_else_on_loop::*; +pub(crate) use useless_exception_statement::*; pub(crate) use useless_import_alias::*; pub(crate) use useless_return::*; pub(crate) use useless_with_lock::*; @@ -164,6 +165,7 @@ mod unnecessary_lambda; mod unnecessary_list_index_lookup; mod unspecified_encoding; mod useless_else_on_loop; +mod useless_exception_statement; mod useless_import_alias; mod useless_return; mod useless_with_lock; diff --git a/crates/ruff_linter/src/rules/pylint/rules/useless_exception_statement.rs b/crates/ruff_linter/src/rules/pylint/rules/useless_exception_statement.rs new file mode 100644 index 0000000000000..45c67e19386af --- /dev/null +++ b/crates/ruff_linter/src/rules/pylint/rules/useless_exception_statement.rs @@ -0,0 +1,110 @@ +use ast::{ExprCall, StmtRaise}; +use ruff_diagnostics::{Diagnostic, Edit, Fix, FixAvailability, Violation}; +use ruff_macros::{derive_message_formats, violation}; +use ruff_python_ast::{self as ast, Expr, Stmt}; +use ruff_text_size::{Ranged, TextRange}; + +use crate::checkers::ast::Checker; + +/// ## What it does +/// Checks for a missing `raise` statement for an exception. It's unnecessary to +/// use an exception without raising it. This rule checks for the absence of a +/// `raise` statement for an exception. +/// +/// ## Why is this bad? +/// It's unnecessary to use an exception without raising it. This can lead to +/// confusion and unexpected behavior. It's better to raise the exception to +/// indicate that an error has occurred. +/// +/// ## Example +/// ```python +/// Exception("exception should be raised") +/// ``` +/// +/// Use instead: +/// ```python +/// raise Exception("exception should be raised") +/// ``` + +// Python exception hierarchy: https://docs.python.org/3/library/exceptions.html#exception-hierarchy +const PY_BUILTIN_EXCEPTIONS: [&str; 21] = [ + "SystemExit", + "Exception", + "ArithmeticError", + "AssertionError", + "AttributeError", + "BufferError", + "EOFError", + "ImportError", + "LookupError", + "IndexError", + "KeyError", + "MemoryError", + "NameError", + "ReferenceError", + "RuntimeError", + "NotImplementedError", + "StopIteration", + "SyntaxError", + "SystemError", + "TypeError", + "ValueError", +]; + +#[violation] +pub struct UselessExceptionStatement; + +impl Violation for UselessExceptionStatement { + const FIX_AVAILABILITY: FixAvailability = FixAvailability::Sometimes; + + #[derive_message_formats] + fn message(&self) -> String { + format!("Missing `raise` statement for exception; add `raise` statement to exception") + } + + fn fix_title(&self) -> Option { + Some(format!("Add `raise` statement to exception")) + } +} + +/// PLW0133 +pub(crate) fn useless_exception_statement(checker: &mut Checker, expr: &Expr) { + let Expr::Call(ExprCall { func, .. }) = expr else { + return; + }; + + if !is_builtin_exception(checker, func) { + return; + } + + let mut diagnostic = Diagnostic::new(UselessExceptionStatement {}, expr.range()); + + diagnostic.set_fix(Fix::safe_edit(Edit::range_replacement( + checker + .generator() + .stmt(&fix_useless_exception_statement(expr)), + expr.range(), + ))); + checker.diagnostics.push(diagnostic); +} + +fn is_builtin_exception(checker: &mut Checker, exc: &Expr) -> bool { + return checker + .semantic() + .resolve_call_path(exc) + .is_some_and(|call_path| { + PY_BUILTIN_EXCEPTIONS.contains(call_path.as_slice().get(1).unwrap_or(&"")) + }); +} + +/// Generate a [`Fix`] to replace useless builtin exception `raise exception`. +/// +/// For example: +/// - Given `ValueError("incorrect value")`, generate `raise ValueError("incorrect value")`. +fn fix_useless_exception_statement(expr: &Expr) -> Stmt { + Stmt::Raise(StmtRaise { + range: TextRange::default(), + exc: Some(Box::new(expr.clone())), + cause: None, + }) +} diff --git a/crates/ruff_linter/src/rules/pylint/snapshots/ruff_linter__rules__pylint__tests__PLW0133_useless_exception_statement.py.snap b/crates/ruff_linter/src/rules/pylint/snapshots/ruff_linter__rules__pylint__tests__PLW0133_useless_exception_statement.py.snap new file mode 100644 index 0000000000000..33efccafad04b --- /dev/null +++ b/crates/ruff_linter/src/rules/pylint/snapshots/ruff_linter__rules__pylint__tests__PLW0133_useless_exception_statement.py.snap @@ -0,0 +1,164 @@ +--- +source: crates/ruff_linter/src/rules/pylint/mod.rs +--- +useless_exception_statement.py:10:5: PLW0133 [*] Missing `raise` statement for exception; add `raise` statement to exception + | + 9 | def test(): +10 | AssertionError("This is an assertion error") # PLW0133 + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ PLW0133 +11 | +12 | # Test case 2: Useless exception statement in try-except block + | + = help: Add `raise` statement to exception + +ℹ Safe fix +7 7 | +8 8 | +9 9 | def test(): +10 |- AssertionError("This is an assertion error") # PLW0133 + 10 |+ raise AssertionError("This is an assertion error") # PLW0133 +11 11 | +12 12 | # Test case 2: Useless exception statement in try-except block +13 13 | + +useless_exception_statement.py:16:9: PLW0133 [*] Missing `raise` statement for exception; add `raise` statement to exception + | +14 | def test(): +15 | try: +16 | Exception("This is an exception") # PLW0133 + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ PLW0133 +17 | except Exception as e: +18 | pass # PL + | + = help: Add `raise` statement to exception + +ℹ Safe fix +13 13 | +14 14 | def test(): +15 15 | try: +16 |- Exception("This is an exception") # PLW0133 + 16 |+ raise Exception("This is an exception") # PLW0133 +17 17 | except Exception as e: +18 18 | pass # PL +19 19 | + +useless_exception_statement.py:24:9: PLW0133 [*] Missing `raise` statement for exception; add `raise` statement to exception + | +22 | def test(): +23 | if True: +24 | RuntimeError("This is an exception") # PLW0133 + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ PLW0133 +25 | +26 | # Test case 4: Useless exception statement in class + | + = help: Add `raise` statement to exception + +ℹ Safe fix +21 21 | +22 22 | def test(): +23 23 | if True: +24 |- RuntimeError("This is an exception") # PLW0133 + 24 |+ raise RuntimeError("This is an exception") # PLW0133 +25 25 | +26 26 | # Test case 4: Useless exception statement in class +27 27 | + +useless_exception_statement.py:31:13: PLW0133 [*] Missing `raise` statement for exception; add `raise` statement to exception + | +29 | class Test: +30 | def __init__(self): +31 | TypeError("This is an exception") # PLW0133 + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ PLW0133 +32 | +33 | # Test case 5: Useless exception statement in function + | + = help: Add `raise` statement to exception + +ℹ Safe fix +28 28 | def test(): +29 29 | class Test: +30 30 | def __init__(self): +31 |- TypeError("This is an exception") # PLW0133 + 31 |+ raise TypeError("This is an exception") # PLW0133 +32 32 | +33 33 | # Test case 5: Useless exception statement in function +34 34 | + +useless_exception_statement.py:37:9: PLW0133 [*] Missing `raise` statement for exception; add `raise` statement to exception + | +35 | def test(): +36 | def f(): +37 | IndexError("This is an exception") # PLW0133 + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ PLW0133 +38 | f() + | + = help: Add `raise` statement to exception + +ℹ Safe fix +34 34 | +35 35 | def test(): +36 36 | def f(): +37 |- IndexError("This is an exception") # PLW0133 + 37 |+ raise IndexError("This is an exception") # PLW0133 +38 38 | f() +39 39 | +40 40 | # Test case 6: Useless exception statement in while loop + +useless_exception_statement.py:44:9: PLW0133 [*] Missing `raise` statement for exception; add `raise` statement to exception + | +42 | def test(): +43 | while True: +44 | KeyError("This is an exception") # PLW0133 + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ PLW0133 +45 | +46 | # Test case 7: Useless exception statement in abstract class + | + = help: Add `raise` statement to exception + +ℹ Safe fix +41 41 | +42 42 | def test(): +43 43 | while True: +44 |- KeyError("This is an exception") # PLW0133 + 44 |+ raise KeyError("This is an exception") # PLW0133 +45 45 | +46 46 | # Test case 7: Useless exception statement in abstract class +47 47 | + +useless_exception_statement.py:52:13: PLW0133 [*] Missing `raise` statement for exception; add `raise` statement to exception + | +50 | @abstractmethod +51 | def test(self): +52 | NotImplementedError("This is an exception") # PLW0133 + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ PLW0133 + | + = help: Add `raise` statement to exception + +ℹ Safe fix +49 49 | class Test(ABC): +50 50 | @abstractmethod +51 51 | def test(self): +52 |- NotImplementedError("This is an exception") # PLW0133 + 52 |+ raise NotImplementedError("This is an exception") # PLW0133 +53 53 | +54 54 | +55 55 | # Test case 8: Useless exception statement inside context manager + +useless_exception_statement.py:59:9: PLW0133 [*] Missing `raise` statement for exception; add `raise` statement to exception + | +57 | def test(): +58 | with suppress(AttributeError): +59 | AttributeError("This is an exception") # PLW0133 + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ PLW0133 + | + = help: Add `raise` statement to exception + +ℹ Safe fix +56 56 | +57 57 | def test(): +58 58 | with suppress(AttributeError): +59 |- AttributeError("This is an exception") # PLW0133 + 59 |+ raise AttributeError("This is an exception") # PLW0133 +60 60 | +61 61 | +62 62 | # Non-violation test cases: PLW0133 diff --git a/ruff.schema.json b/ruff.schema.json index 8e17f813722a0..13df0a59bd1dc 100644 --- a/ruff.schema.json +++ b/ruff.schema.json @@ -3312,6 +3312,7 @@ "PLW0129", "PLW013", "PLW0131", + "PLW0133", "PLW02", "PLW024", "PLW0245", From 5394d3a63f2c96031f7763e815c5d4f22a1cb18d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mikko=20Lepp=C3=A4nen?= Date: Thu, 29 Feb 2024 19:40:15 +0200 Subject: [PATCH 2/5] Fix: rule doc was not generated --- .../rules/useless_exception_statement.rs | 41 +++++++++---------- 1 file changed, 20 insertions(+), 21 deletions(-) diff --git a/crates/ruff_linter/src/rules/pylint/rules/useless_exception_statement.rs b/crates/ruff_linter/src/rules/pylint/rules/useless_exception_statement.rs index 45c67e19386af..2775eccf6f9f1 100644 --- a/crates/ruff_linter/src/rules/pylint/rules/useless_exception_statement.rs +++ b/crates/ruff_linter/src/rules/pylint/rules/useless_exception_statement.rs @@ -6,27 +6,7 @@ use ruff_text_size::{Ranged, TextRange}; use crate::checkers::ast::Checker; -/// ## What it does -/// Checks for a missing `raise` statement for an exception. It's unnecessary to -/// use an exception without raising it. This rule checks for the absence of a -/// `raise` statement for an exception. -/// -/// ## Why is this bad? -/// It's unnecessary to use an exception without raising it. This can lead to -/// confusion and unexpected behavior. It's better to raise the exception to -/// indicate that an error has occurred. -/// -/// ## Example -/// ```python -/// Exception("exception should be raised") -/// ``` -/// -/// Use instead: -/// ```python -/// raise Exception("exception should be raised") -/// ``` - -// Python exception hierarchy: https://docs.python.org/3/library/exceptions.html#exception-hierarchy +/// Python exception hierarchy: https://docs.python.org/3/library/exceptions.html#exception-hierarchy const PY_BUILTIN_EXCEPTIONS: [&str; 21] = [ "SystemExit", "Exception", @@ -51,6 +31,25 @@ const PY_BUILTIN_EXCEPTIONS: [&str; 21] = [ "ValueError", ]; +/// ## What it does +/// Checks for a missing `raise` statement for an exception. It's unnecessary to +/// use an exception without raising it. This rule checks for the absence of a +/// `raise` statement for an exception. +/// +/// ## Why is this bad? +/// It's unnecessary to use an exception without raising it. This can lead to +/// confusion and unexpected behavior. It's better to raise the exception to +/// indicate that an error has occurred. +/// +/// ## Example +/// ```python +/// Exception("exception should be raised") +/// ``` +/// +/// Use instead: +/// ```python +/// raise Exception("exception should be raised") +/// ``` #[violation] pub struct UselessExceptionStatement; From c05cb912642a1386bec0c10876808ee920ebc655 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mikko=20Lepp=C3=A4nen?= Date: Thu, 29 Feb 2024 19:43:56 +0200 Subject: [PATCH 3/5] Fix: minor clippy issue related to url handling --- .../src/rules/pylint/rules/useless_exception_statement.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/ruff_linter/src/rules/pylint/rules/useless_exception_statement.rs b/crates/ruff_linter/src/rules/pylint/rules/useless_exception_statement.rs index 2775eccf6f9f1..010eb0f358649 100644 --- a/crates/ruff_linter/src/rules/pylint/rules/useless_exception_statement.rs +++ b/crates/ruff_linter/src/rules/pylint/rules/useless_exception_statement.rs @@ -6,7 +6,7 @@ use ruff_text_size::{Ranged, TextRange}; use crate::checkers::ast::Checker; -/// Python exception hierarchy: https://docs.python.org/3/library/exceptions.html#exception-hierarchy +/// - [Python exception hierarchy](https://docs.python.org/3/library/exceptions.html#exception-hierarchy) const PY_BUILTIN_EXCEPTIONS: [&str; 21] = [ "SystemExit", "Exception", From a69fd7f189decea27d2b66e103c149cdcd26288f Mon Sep 17 00:00:00 2001 From: Charlie Marsh Date: Thu, 29 Feb 2024 21:10:28 -0500 Subject: [PATCH 4/5] Reformat fixture --- .../pylint/useless_exception_statement.py | 104 ++++----- ...LW0133_useless_exception_statement.py.snap | 213 +++++++++--------- 2 files changed, 153 insertions(+), 164 deletions(-) diff --git a/crates/ruff_linter/resources/test/fixtures/pylint/useless_exception_statement.py b/crates/ruff_linter/resources/test/fixtures/pylint/useless_exception_statement.py index 9ed511ca0726b..ac07600bef36f 100644 --- a/crates/ruff_linter/resources/test/fixtures/pylint/useless_exception_statement.py +++ b/crates/ruff_linter/resources/test/fixtures/pylint/useless_exception_statement.py @@ -1,110 +1,106 @@ - -# Violation test cases: PLW0133 - # Test case 1: Useless exception statement from abc import ABC, abstractmethod from contextlib import suppress -def test(): +def func(): AssertionError("This is an assertion error") # PLW0133 -# Test case 2: Useless exception statement in try-except block -def test(): +# Test case 2: Useless exception statement in try-except block +def func(): try: - Exception("This is an exception") # PLW0133 - except Exception as e: - pass # PL + Exception("This is an exception") # PLW0133 + except Exception as err: + pass -# Test case 3: Useless exception statement in if statement -def test(): +# Test case 3: Useless exception statement in if statement +def func(): if True: RuntimeError("This is an exception") # PLW0133 -# Test case 4: Useless exception statement in class -def test(): - class Test: +# Test case 4: Useless exception statement in class +def func(): + class Class: def __init__(self): - TypeError("This is an exception") # PLW0133 + TypeError("This is an exception") # PLW0133 + # Test case 5: Useless exception statement in function +def func(): + def inner(): + IndexError("This is an exception") # PLW0133 + + inner() -def test(): - def f(): - IndexError("This is an exception") # PLW0133 - f() # Test case 6: Useless exception statement in while loop - -def test(): +def func(): while True: - KeyError("This is an exception") # PLW0133 + KeyError("This is an exception") # PLW0133 + # Test case 7: Useless exception statement in abstract class - -def test(): - class Test(ABC): +def func(): + class Class(ABC): @abstractmethod - def test(self): - NotImplementedError("This is an exception") # PLW0133 + def method(self): + NotImplementedError("This is an exception") # PLW0133 # Test case 8: Useless exception statement inside context manager - -def test(): +def func(): with suppress(AttributeError): - AttributeError("This is an exception") # PLW0133 + AttributeError("This is an exception") # PLW0133 # Non-violation test cases: PLW0133 - -# Test case 1: Used exception statement in try-except block -def test(): + +# Test case 1: Used exception statement in try-except block +def func(): raise Exception("This is an exception") # OK -# Test case 2: Used exception statement in if statement -def test(): +# Test case 2: Used exception statement in if statement +def func(): if True: raise ValueError("This is an exception") # OK - + + # Test case 3: Used exception statement in class - -def test(): - class Test: +def func(): + class Class: def __init__(self): raise TypeError("This is an exception") # OK - -# Test case 4: Exception statement used in list comprehension -def test(): + +# Test case 4: Exception statement used in list comprehension +def func(): [ValueError("This is an exception") for i in range(10)] # OK # Test case 5: Exception statement used when initializing a dictionary - -def test(): +def func(): {i: TypeError("This is an exception") for i in range(10)} # OK + # Test case 6: Exception statement used in function - -def test(): - def f(): +def func(): + def inner(): raise IndexError("This is an exception") # OK - f() -# Test case 7: Exception statement used in variable assignment + inner() + -def test(): - e = KeyError("This is an exception") # OK +# Test case 7: Exception statement used in variable assignment +def func(): + err = KeyError("This is an exception") # OK # Test case 8: Exception statement inside context manager - -def test(): +def func(): with suppress(AttributeError): - raise AttributeError("This is an exception") # OK \ No newline at end of file + raise AttributeError("This is an exception") # OK diff --git a/crates/ruff_linter/src/rules/pylint/snapshots/ruff_linter__rules__pylint__tests__PLW0133_useless_exception_statement.py.snap b/crates/ruff_linter/src/rules/pylint/snapshots/ruff_linter__rules__pylint__tests__PLW0133_useless_exception_statement.py.snap index 33efccafad04b..bbcf4faaf9547 100644 --- a/crates/ruff_linter/src/rules/pylint/snapshots/ruff_linter__rules__pylint__tests__PLW0133_useless_exception_statement.py.snap +++ b/crates/ruff_linter/src/rules/pylint/snapshots/ruff_linter__rules__pylint__tests__PLW0133_useless_exception_statement.py.snap @@ -1,164 +1,157 @@ --- source: crates/ruff_linter/src/rules/pylint/mod.rs --- -useless_exception_statement.py:10:5: PLW0133 [*] Missing `raise` statement for exception; add `raise` statement to exception - | - 9 | def test(): -10 | AssertionError("This is an assertion error") # PLW0133 - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ PLW0133 -11 | -12 | # Test case 2: Useless exception statement in try-except block - | - = help: Add `raise` statement to exception +useless_exception_statement.py:7:5: PLW0133 [*] Missing `raise` statement for exception; add `raise` statement to exception + | +6 | def func(): +7 | AssertionError("This is an assertion error") # PLW0133 + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ PLW0133 + | + = help: Add `raise` statement to exception ℹ Safe fix -7 7 | -8 8 | -9 9 | def test(): -10 |- AssertionError("This is an assertion error") # PLW0133 - 10 |+ raise AssertionError("This is an assertion error") # PLW0133 -11 11 | -12 12 | # Test case 2: Useless exception statement in try-except block -13 13 | +4 4 | +5 5 | +6 6 | def func(): +7 |- AssertionError("This is an assertion error") # PLW0133 + 7 |+ raise AssertionError("This is an assertion error") # PLW0133 +8 8 | +9 9 | +10 10 | # Test case 2: Useless exception statement in try-except block -useless_exception_statement.py:16:9: PLW0133 [*] Missing `raise` statement for exception; add `raise` statement to exception +useless_exception_statement.py:13:9: PLW0133 [*] Missing `raise` statement for exception; add `raise` statement to exception | -14 | def test(): -15 | try: -16 | Exception("This is an exception") # PLW0133 +11 | def func(): +12 | try: +13 | Exception("This is an exception") # PLW0133 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ PLW0133 -17 | except Exception as e: -18 | pass # PL +14 | except Exception as err: +15 | pass | = help: Add `raise` statement to exception ℹ Safe fix -13 13 | -14 14 | def test(): -15 15 | try: -16 |- Exception("This is an exception") # PLW0133 - 16 |+ raise Exception("This is an exception") # PLW0133 -17 17 | except Exception as e: -18 18 | pass # PL -19 19 | +10 10 | # Test case 2: Useless exception statement in try-except block +11 11 | def func(): +12 12 | try: +13 |- Exception("This is an exception") # PLW0133 + 13 |+ raise Exception("This is an exception") # PLW0133 +14 14 | except Exception as err: +15 15 | pass +16 16 | -useless_exception_statement.py:24:9: PLW0133 [*] Missing `raise` statement for exception; add `raise` statement to exception +useless_exception_statement.py:21:9: PLW0133 [*] Missing `raise` statement for exception; add `raise` statement to exception | -22 | def test(): -23 | if True: -24 | RuntimeError("This is an exception") # PLW0133 +19 | def func(): +20 | if True: +21 | RuntimeError("This is an exception") # PLW0133 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ PLW0133 -25 | -26 | # Test case 4: Useless exception statement in class | = help: Add `raise` statement to exception ℹ Safe fix -21 21 | -22 22 | def test(): -23 23 | if True: -24 |- RuntimeError("This is an exception") # PLW0133 - 24 |+ raise RuntimeError("This is an exception") # PLW0133 -25 25 | -26 26 | # Test case 4: Useless exception statement in class -27 27 | +18 18 | # Test case 3: Useless exception statement in if statement +19 19 | def func(): +20 20 | if True: +21 |- RuntimeError("This is an exception") # PLW0133 + 21 |+ raise RuntimeError("This is an exception") # PLW0133 +22 22 | +23 23 | +24 24 | # Test case 4: Useless exception statement in class -useless_exception_statement.py:31:13: PLW0133 [*] Missing `raise` statement for exception; add `raise` statement to exception +useless_exception_statement.py:28:13: PLW0133 [*] Missing `raise` statement for exception; add `raise` statement to exception | -29 | class Test: -30 | def __init__(self): -31 | TypeError("This is an exception") # PLW0133 +26 | class Class: +27 | def __init__(self): +28 | TypeError("This is an exception") # PLW0133 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ PLW0133 -32 | -33 | # Test case 5: Useless exception statement in function | = help: Add `raise` statement to exception ℹ Safe fix -28 28 | def test(): -29 29 | class Test: -30 30 | def __init__(self): -31 |- TypeError("This is an exception") # PLW0133 - 31 |+ raise TypeError("This is an exception") # PLW0133 -32 32 | -33 33 | # Test case 5: Useless exception statement in function -34 34 | +25 25 | def func(): +26 26 | class Class: +27 27 | def __init__(self): +28 |- TypeError("This is an exception") # PLW0133 + 28 |+ raise TypeError("This is an exception") # PLW0133 +29 29 | +30 30 | +31 31 | # Test case 5: Useless exception statement in function -useless_exception_statement.py:37:9: PLW0133 [*] Missing `raise` statement for exception; add `raise` statement to exception +useless_exception_statement.py:34:9: PLW0133 [*] Missing `raise` statement for exception; add `raise` statement to exception | -35 | def test(): -36 | def f(): -37 | IndexError("This is an exception") # PLW0133 +32 | def func(): +33 | def inner(): +34 | IndexError("This is an exception") # PLW0133 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ PLW0133 -38 | f() +35 | +36 | inner() | = help: Add `raise` statement to exception ℹ Safe fix -34 34 | -35 35 | def test(): -36 36 | def f(): -37 |- IndexError("This is an exception") # PLW0133 - 37 |+ raise IndexError("This is an exception") # PLW0133 -38 38 | f() -39 39 | -40 40 | # Test case 6: Useless exception statement in while loop +31 31 | # Test case 5: Useless exception statement in function +32 32 | def func(): +33 33 | def inner(): +34 |- IndexError("This is an exception") # PLW0133 + 34 |+ raise IndexError("This is an exception") # PLW0133 +35 35 | +36 36 | inner() +37 37 | -useless_exception_statement.py:44:9: PLW0133 [*] Missing `raise` statement for exception; add `raise` statement to exception +useless_exception_statement.py:42:9: PLW0133 [*] Missing `raise` statement for exception; add `raise` statement to exception | -42 | def test(): -43 | while True: -44 | KeyError("This is an exception") # PLW0133 +40 | def func(): +41 | while True: +42 | KeyError("This is an exception") # PLW0133 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ PLW0133 -45 | -46 | # Test case 7: Useless exception statement in abstract class | = help: Add `raise` statement to exception ℹ Safe fix -41 41 | -42 42 | def test(): -43 43 | while True: -44 |- KeyError("This is an exception") # PLW0133 - 44 |+ raise KeyError("This is an exception") # PLW0133 -45 45 | -46 46 | # Test case 7: Useless exception statement in abstract class -47 47 | +39 39 | # Test case 6: Useless exception statement in while loop +40 40 | def func(): +41 41 | while True: +42 |- KeyError("This is an exception") # PLW0133 + 42 |+ raise KeyError("This is an exception") # PLW0133 +43 43 | +44 44 | +45 45 | # Test case 7: Useless exception statement in abstract class -useless_exception_statement.py:52:13: PLW0133 [*] Missing `raise` statement for exception; add `raise` statement to exception +useless_exception_statement.py:50:13: PLW0133 [*] Missing `raise` statement for exception; add `raise` statement to exception | -50 | @abstractmethod -51 | def test(self): -52 | NotImplementedError("This is an exception") # PLW0133 +48 | @abstractmethod +49 | def method(self): +50 | NotImplementedError("This is an exception") # PLW0133 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ PLW0133 | = help: Add `raise` statement to exception ℹ Safe fix -49 49 | class Test(ABC): -50 50 | @abstractmethod -51 51 | def test(self): -52 |- NotImplementedError("This is an exception") # PLW0133 - 52 |+ raise NotImplementedError("This is an exception") # PLW0133 -53 53 | -54 54 | -55 55 | # Test case 8: Useless exception statement inside context manager +47 47 | class Class(ABC): +48 48 | @abstractmethod +49 49 | def method(self): +50 |- NotImplementedError("This is an exception") # PLW0133 + 50 |+ raise NotImplementedError("This is an exception") # PLW0133 +51 51 | +52 52 | +53 53 | # Test case 8: Useless exception statement inside context manager -useless_exception_statement.py:59:9: PLW0133 [*] Missing `raise` statement for exception; add `raise` statement to exception +useless_exception_statement.py:56:9: PLW0133 [*] Missing `raise` statement for exception; add `raise` statement to exception | -57 | def test(): -58 | with suppress(AttributeError): -59 | AttributeError("This is an exception") # PLW0133 +54 | def func(): +55 | with suppress(AttributeError): +56 | AttributeError("This is an exception") # PLW0133 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ PLW0133 | = help: Add `raise` statement to exception ℹ Safe fix -56 56 | -57 57 | def test(): -58 58 | with suppress(AttributeError): -59 |- AttributeError("This is an exception") # PLW0133 - 59 |+ raise AttributeError("This is an exception") # PLW0133 -60 60 | -61 61 | -62 62 | # Non-violation test cases: PLW0133 +53 53 | # Test case 8: Useless exception statement inside context manager +54 54 | def func(): +55 55 | with suppress(AttributeError): +56 |- AttributeError("This is an exception") # PLW0133 + 56 |+ raise AttributeError("This is an exception") # PLW0133 +57 57 | +58 58 | +59 59 | # Non-violation test cases: PLW0133 From 554e405315d39df667c60d1ad0d9e7bb8d0f8ce2 Mon Sep 17 00:00:00 2001 From: Charlie Marsh Date: Thu, 29 Feb 2024 21:27:05 -0500 Subject: [PATCH 5/5] Use statement --- .../pylint/useless_exception_statement.py | 10 ++ .../src/checkers/ast/analyze/statement.rs | 4 +- .../rules/useless_exception_statement.rs | 126 ++++++++---------- ...LW0133_useless_exception_statement.py.snap | 88 ++++++++---- 4 files changed, 131 insertions(+), 97 deletions(-) diff --git a/crates/ruff_linter/resources/test/fixtures/pylint/useless_exception_statement.py b/crates/ruff_linter/resources/test/fixtures/pylint/useless_exception_statement.py index ac07600bef36f..c3ca0b90d2513 100644 --- a/crates/ruff_linter/resources/test/fixtures/pylint/useless_exception_statement.py +++ b/crates/ruff_linter/resources/test/fixtures/pylint/useless_exception_statement.py @@ -56,6 +56,16 @@ def func(): AttributeError("This is an exception") # PLW0133 +# Test case 9: Useless exception statement in parentheses +def func(): + (RuntimeError("This is an exception")) # PLW0133 + + +# Test case 10: Useless exception statement in continuation +def func(): + x = 1; (RuntimeError("This is an exception")); y = 2 # PLW0133 + + # Non-violation test cases: PLW0133 diff --git a/crates/ruff_linter/src/checkers/ast/analyze/statement.rs b/crates/ruff_linter/src/checkers/ast/analyze/statement.rs index 9d224e4f00111..f8867e68cf5c3 100644 --- a/crates/ruff_linter/src/checkers/ast/analyze/statement.rs +++ b/crates/ruff_linter/src/checkers/ast/analyze/statement.rs @@ -1609,7 +1609,7 @@ pub(crate) fn statement(stmt: &Stmt, checker: &mut Checker) { refurb::rules::delete_full_slice(checker, delete); } } - Stmt::Expr(ast::StmtExpr { value, range: _ }) => { + Stmt::Expr(expr @ ast::StmtExpr { value, range: _ }) => { if checker.enabled(Rule::UselessComparison) { flake8_bugbear::rules::useless_comparison(checker, value); } @@ -1633,7 +1633,7 @@ pub(crate) fn statement(stmt: &Stmt, checker: &mut Checker) { refurb::rules::repeated_append(checker, stmt); } if checker.enabled(Rule::UselessExceptionStatement) { - pylint::rules::useless_exception_statement(checker, value); + pylint::rules::useless_exception_statement(checker, expr); } } _ => {} diff --git a/crates/ruff_linter/src/rules/pylint/rules/useless_exception_statement.rs b/crates/ruff_linter/src/rules/pylint/rules/useless_exception_statement.rs index 010eb0f358649..02066cd04b0d8 100644 --- a/crates/ruff_linter/src/rules/pylint/rules/useless_exception_statement.rs +++ b/crates/ruff_linter/src/rules/pylint/rules/useless_exception_statement.rs @@ -1,55 +1,32 @@ -use ast::{ExprCall, StmtRaise}; use ruff_diagnostics::{Diagnostic, Edit, Fix, FixAvailability, Violation}; use ruff_macros::{derive_message_formats, violation}; -use ruff_python_ast::{self as ast, Expr, Stmt}; -use ruff_text_size::{Ranged, TextRange}; +use ruff_python_ast::{self as ast, Expr}; +use ruff_python_semantic::SemanticModel; +use ruff_text_size::Ranged; use crate::checkers::ast::Checker; -/// - [Python exception hierarchy](https://docs.python.org/3/library/exceptions.html#exception-hierarchy) -const PY_BUILTIN_EXCEPTIONS: [&str; 21] = [ - "SystemExit", - "Exception", - "ArithmeticError", - "AssertionError", - "AttributeError", - "BufferError", - "EOFError", - "ImportError", - "LookupError", - "IndexError", - "KeyError", - "MemoryError", - "NameError", - "ReferenceError", - "RuntimeError", - "NotImplementedError", - "StopIteration", - "SyntaxError", - "SystemError", - "TypeError", - "ValueError", -]; - /// ## What it does -/// Checks for a missing `raise` statement for an exception. It's unnecessary to -/// use an exception without raising it. This rule checks for the absence of a -/// `raise` statement for an exception. +/// Checks for an exception that is not raised. /// /// ## Why is this bad? -/// It's unnecessary to use an exception without raising it. This can lead to -/// confusion and unexpected behavior. It's better to raise the exception to -/// indicate that an error has occurred. +/// It's unnecessary to create an exception without raising it. For example, +/// `ValueError("...")` on its own will have no effect (unlike +/// `raise ValueError("...")`) and is likely a mistake. /// /// ## Example /// ```python -/// Exception("exception should be raised") +/// ValueError("...") /// ``` /// /// Use instead: /// ```python -/// raise Exception("exception should be raised") +/// raise ValueError("...") /// ``` +/// +/// ## Fix safety +/// This rule's fix is marked as unsafe, as converting a useless exception +/// statement to a `raise` statement will change the program's behavior. #[violation] pub struct UselessExceptionStatement; @@ -58,52 +35,61 @@ impl Violation for UselessExceptionStatement { #[derive_message_formats] fn message(&self) -> String { - format!("Missing `raise` statement for exception; add `raise` statement to exception") + format!("Missing `raise` statement on exception") } fn fix_title(&self) -> Option { - Some(format!("Add `raise` statement to exception")) + Some(format!("Add `raise` keyword")) } } /// PLW0133 -pub(crate) fn useless_exception_statement(checker: &mut Checker, expr: &Expr) { - let Expr::Call(ExprCall { func, .. }) = expr else { +pub(crate) fn useless_exception_statement(checker: &mut Checker, expr: &ast::StmtExpr) { + let Expr::Call(ast::ExprCall { func, .. }) = expr.value.as_ref() else { return; }; - if !is_builtin_exception(checker, func) { - return; + if is_builtin_exception(func, checker.semantic()) { + let mut diagnostic = Diagnostic::new(UselessExceptionStatement, expr.range()); + diagnostic.set_fix(Fix::unsafe_edit(Edit::insertion( + "raise ".to_string(), + expr.start(), + ))); + checker.diagnostics.push(diagnostic); } - - let mut diagnostic = Diagnostic::new(UselessExceptionStatement {}, expr.range()); - - diagnostic.set_fix(Fix::safe_edit(Edit::range_replacement( - checker - .generator() - .stmt(&fix_useless_exception_statement(expr)), - expr.range(), - ))); - checker.diagnostics.push(diagnostic); -} - -fn is_builtin_exception(checker: &mut Checker, exc: &Expr) -> bool { - return checker - .semantic() - .resolve_call_path(exc) - .is_some_and(|call_path| { - PY_BUILTIN_EXCEPTIONS.contains(call_path.as_slice().get(1).unwrap_or(&"")) - }); } -/// Generate a [`Fix`] to replace useless builtin exception `raise exception`. +/// Returns `true` if the given expression is a builtin exception. /// -/// For example: -/// - Given `ValueError("incorrect value")`, generate `raise ValueError("incorrect value")`. -fn fix_useless_exception_statement(expr: &Expr) -> Stmt { - Stmt::Raise(StmtRaise { - range: TextRange::default(), - exc: Some(Box::new(expr.clone())), - cause: None, - }) +/// See: +fn is_builtin_exception(expr: &Expr, semantic: &SemanticModel) -> bool { + return semantic.resolve_call_path(expr).is_some_and(|call_path| { + matches!( + call_path.as_slice(), + [ + "", + "SystemExit" + | "Exception" + | "ArithmeticError" + | "AssertionError" + | "AttributeError" + | "BufferError" + | "EOFError" + | "ImportError" + | "LookupError" + | "IndexError" + | "KeyError" + | "MemoryError" + | "NameError" + | "ReferenceError" + | "RuntimeError" + | "NotImplementedError" + | "StopIteration" + | "SyntaxError" + | "SystemError" + | "TypeError" + | "ValueError" + ] + ) + }); } diff --git a/crates/ruff_linter/src/rules/pylint/snapshots/ruff_linter__rules__pylint__tests__PLW0133_useless_exception_statement.py.snap b/crates/ruff_linter/src/rules/pylint/snapshots/ruff_linter__rules__pylint__tests__PLW0133_useless_exception_statement.py.snap index bbcf4faaf9547..91c485cfb4e8c 100644 --- a/crates/ruff_linter/src/rules/pylint/snapshots/ruff_linter__rules__pylint__tests__PLW0133_useless_exception_statement.py.snap +++ b/crates/ruff_linter/src/rules/pylint/snapshots/ruff_linter__rules__pylint__tests__PLW0133_useless_exception_statement.py.snap @@ -1,15 +1,15 @@ --- source: crates/ruff_linter/src/rules/pylint/mod.rs --- -useless_exception_statement.py:7:5: PLW0133 [*] Missing `raise` statement for exception; add `raise` statement to exception +useless_exception_statement.py:7:5: PLW0133 [*] Missing `raise` statement on exception | 6 | def func(): 7 | AssertionError("This is an assertion error") # PLW0133 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ PLW0133 | - = help: Add `raise` statement to exception + = help: Add `raise` keyword -ℹ Safe fix +ℹ Unsafe fix 4 4 | 5 5 | 6 6 | def func(): @@ -19,7 +19,7 @@ useless_exception_statement.py:7:5: PLW0133 [*] Missing `raise` statement for ex 9 9 | 10 10 | # Test case 2: Useless exception statement in try-except block -useless_exception_statement.py:13:9: PLW0133 [*] Missing `raise` statement for exception; add `raise` statement to exception +useless_exception_statement.py:13:9: PLW0133 [*] Missing `raise` statement on exception | 11 | def func(): 12 | try: @@ -28,9 +28,9 @@ useless_exception_statement.py:13:9: PLW0133 [*] Missing `raise` statement for e 14 | except Exception as err: 15 | pass | - = help: Add `raise` statement to exception + = help: Add `raise` keyword -ℹ Safe fix +ℹ Unsafe fix 10 10 | # Test case 2: Useless exception statement in try-except block 11 11 | def func(): 12 12 | try: @@ -40,16 +40,16 @@ useless_exception_statement.py:13:9: PLW0133 [*] Missing `raise` statement for e 15 15 | pass 16 16 | -useless_exception_statement.py:21:9: PLW0133 [*] Missing `raise` statement for exception; add `raise` statement to exception +useless_exception_statement.py:21:9: PLW0133 [*] Missing `raise` statement on exception | 19 | def func(): 20 | if True: 21 | RuntimeError("This is an exception") # PLW0133 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ PLW0133 | - = help: Add `raise` statement to exception + = help: Add `raise` keyword -ℹ Safe fix +ℹ Unsafe fix 18 18 | # Test case 3: Useless exception statement in if statement 19 19 | def func(): 20 20 | if True: @@ -59,16 +59,16 @@ useless_exception_statement.py:21:9: PLW0133 [*] Missing `raise` statement for e 23 23 | 24 24 | # Test case 4: Useless exception statement in class -useless_exception_statement.py:28:13: PLW0133 [*] Missing `raise` statement for exception; add `raise` statement to exception +useless_exception_statement.py:28:13: PLW0133 [*] Missing `raise` statement on exception | 26 | class Class: 27 | def __init__(self): 28 | TypeError("This is an exception") # PLW0133 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ PLW0133 | - = help: Add `raise` statement to exception + = help: Add `raise` keyword -ℹ Safe fix +ℹ Unsafe fix 25 25 | def func(): 26 26 | class Class: 27 27 | def __init__(self): @@ -78,7 +78,7 @@ useless_exception_statement.py:28:13: PLW0133 [*] Missing `raise` statement for 30 30 | 31 31 | # Test case 5: Useless exception statement in function -useless_exception_statement.py:34:9: PLW0133 [*] Missing `raise` statement for exception; add `raise` statement to exception +useless_exception_statement.py:34:9: PLW0133 [*] Missing `raise` statement on exception | 32 | def func(): 33 | def inner(): @@ -87,9 +87,9 @@ useless_exception_statement.py:34:9: PLW0133 [*] Missing `raise` statement for e 35 | 36 | inner() | - = help: Add `raise` statement to exception + = help: Add `raise` keyword -ℹ Safe fix +ℹ Unsafe fix 31 31 | # Test case 5: Useless exception statement in function 32 32 | def func(): 33 33 | def inner(): @@ -99,16 +99,16 @@ useless_exception_statement.py:34:9: PLW0133 [*] Missing `raise` statement for e 36 36 | inner() 37 37 | -useless_exception_statement.py:42:9: PLW0133 [*] Missing `raise` statement for exception; add `raise` statement to exception +useless_exception_statement.py:42:9: PLW0133 [*] Missing `raise` statement on exception | 40 | def func(): 41 | while True: 42 | KeyError("This is an exception") # PLW0133 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ PLW0133 | - = help: Add `raise` statement to exception + = help: Add `raise` keyword -ℹ Safe fix +ℹ Unsafe fix 39 39 | # Test case 6: Useless exception statement in while loop 40 40 | def func(): 41 41 | while True: @@ -118,16 +118,16 @@ useless_exception_statement.py:42:9: PLW0133 [*] Missing `raise` statement for e 44 44 | 45 45 | # Test case 7: Useless exception statement in abstract class -useless_exception_statement.py:50:13: PLW0133 [*] Missing `raise` statement for exception; add `raise` statement to exception +useless_exception_statement.py:50:13: PLW0133 [*] Missing `raise` statement on exception | 48 | @abstractmethod 49 | def method(self): 50 | NotImplementedError("This is an exception") # PLW0133 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ PLW0133 | - = help: Add `raise` statement to exception + = help: Add `raise` keyword -ℹ Safe fix +ℹ Unsafe fix 47 47 | class Class(ABC): 48 48 | @abstractmethod 49 49 | def method(self): @@ -137,16 +137,16 @@ useless_exception_statement.py:50:13: PLW0133 [*] Missing `raise` statement for 52 52 | 53 53 | # Test case 8: Useless exception statement inside context manager -useless_exception_statement.py:56:9: PLW0133 [*] Missing `raise` statement for exception; add `raise` statement to exception +useless_exception_statement.py:56:9: PLW0133 [*] Missing `raise` statement on exception | 54 | def func(): 55 | with suppress(AttributeError): 56 | AttributeError("This is an exception") # PLW0133 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ PLW0133 | - = help: Add `raise` statement to exception + = help: Add `raise` keyword -ℹ Safe fix +ℹ Unsafe fix 53 53 | # Test case 8: Useless exception statement inside context manager 54 54 | def func(): 55 55 | with suppress(AttributeError): @@ -154,4 +154,42 @@ useless_exception_statement.py:56:9: PLW0133 [*] Missing `raise` statement for e 56 |+ raise AttributeError("This is an exception") # PLW0133 57 57 | 58 58 | -59 59 | # Non-violation test cases: PLW0133 +59 59 | # Test case 9: Useless exception statement in parentheses + +useless_exception_statement.py:61:5: PLW0133 [*] Missing `raise` statement on exception + | +59 | # Test case 9: Useless exception statement in parentheses +60 | def func(): +61 | (RuntimeError("This is an exception")) # PLW0133 + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ PLW0133 + | + = help: Add `raise` keyword + +ℹ Unsafe fix +58 58 | +59 59 | # Test case 9: Useless exception statement in parentheses +60 60 | def func(): +61 |- (RuntimeError("This is an exception")) # PLW0133 + 61 |+ raise (RuntimeError("This is an exception")) # PLW0133 +62 62 | +63 63 | +64 64 | # Test case 10: Useless exception statement in continuation + +useless_exception_statement.py:66:12: PLW0133 [*] Missing `raise` statement on exception + | +64 | # Test case 10: Useless exception statement in continuation +65 | def func(): +66 | x = 1; (RuntimeError("This is an exception")); y = 2 # PLW0133 + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ PLW0133 + | + = help: Add `raise` keyword + +ℹ Unsafe fix +63 63 | +64 64 | # Test case 10: Useless exception statement in continuation +65 65 | def func(): +66 |- x = 1; (RuntimeError("This is an exception")); y = 2 # PLW0133 + 66 |+ x = 1; raise (RuntimeError("This is an exception")); y = 2 # PLW0133 +67 67 | +68 68 | +69 69 | # Non-violation test cases: PLW0133