-
Notifications
You must be signed in to change notification settings - Fork 1.2k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
## Summary This review contains a new rule for handling `useless exception statements` (`PLW0133`). Is it based on the following pylint's rule: [W0133](https://pylint.readthedocs.io/en/latest/user_guide/messages/warning/pointless-exception-statement.html) Note: this rule does not cover the case if an error is a custom exception class. See: [Rule request](#10145) ## Test Plan ```bash cargo test & manually ```
- Loading branch information
1 parent
cbafae0
commit 8e0a70c
Showing
8 changed files
with
418 additions
and
1 deletion.
There are no files selected for viewing
116 changes: 116 additions & 0 deletions
116
crates/ruff_linter/resources/test/fixtures/pylint/useless_exception_statement.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,116 @@ | ||
# Test case 1: Useless exception statement | ||
from abc import ABC, abstractmethod | ||
from contextlib import suppress | ||
|
||
|
||
def func(): | ||
AssertionError("This is an assertion error") # PLW0133 | ||
|
||
|
||
# Test case 2: Useless exception statement in try-except block | ||
def func(): | ||
try: | ||
Exception("This is an exception") # PLW0133 | ||
except Exception as err: | ||
pass | ||
|
||
|
||
# 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 func(): | ||
class Class: | ||
def __init__(self): | ||
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() | ||
|
||
|
||
# Test case 6: Useless exception statement in while loop | ||
def func(): | ||
while True: | ||
KeyError("This is an exception") # PLW0133 | ||
|
||
|
||
# Test case 7: Useless exception statement in abstract class | ||
def func(): | ||
class Class(ABC): | ||
@abstractmethod | ||
def method(self): | ||
NotImplementedError("This is an exception") # PLW0133 | ||
|
||
|
||
# Test case 8: Useless exception statement inside context manager | ||
def func(): | ||
with suppress(AttributeError): | ||
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 | ||
|
||
|
||
# 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 func(): | ||
if True: | ||
raise ValueError("This is an exception") # OK | ||
|
||
|
||
# Test case 3: Used exception statement in class | ||
def func(): | ||
class Class: | ||
def __init__(self): | ||
raise TypeError("This is an exception") # OK | ||
|
||
|
||
# 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 func(): | ||
{i: TypeError("This is an exception") for i in range(10)} # OK | ||
|
||
|
||
# Test case 6: Exception statement used in function | ||
def func(): | ||
def inner(): | ||
raise IndexError("This is an exception") # OK | ||
|
||
inner() | ||
|
||
|
||
# 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 func(): | ||
with suppress(AttributeError): | ||
raise AttributeError("This is an exception") # OK |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
95 changes: 95 additions & 0 deletions
95
crates/ruff_linter/src/rules/pylint/rules/useless_exception_statement.rs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,95 @@ | ||
use ruff_diagnostics::{Diagnostic, Edit, Fix, FixAvailability, Violation}; | ||
use ruff_macros::{derive_message_formats, violation}; | ||
use ruff_python_ast::{self as ast, Expr}; | ||
use ruff_python_semantic::SemanticModel; | ||
use ruff_text_size::Ranged; | ||
|
||
use crate::checkers::ast::Checker; | ||
|
||
/// ## What it does | ||
/// Checks for an exception that is not raised. | ||
/// | ||
/// ## Why is this bad? | ||
/// 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 | ||
/// ValueError("...") | ||
/// ``` | ||
/// | ||
/// Use instead: | ||
/// ```python | ||
/// 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; | ||
|
||
impl Violation for UselessExceptionStatement { | ||
const FIX_AVAILABILITY: FixAvailability = FixAvailability::Sometimes; | ||
|
||
#[derive_message_formats] | ||
fn message(&self) -> String { | ||
format!("Missing `raise` statement on exception") | ||
} | ||
|
||
fn fix_title(&self) -> Option<String> { | ||
Some(format!("Add `raise` keyword")) | ||
} | ||
} | ||
|
||
/// PLW0133 | ||
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(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); | ||
} | ||
} | ||
|
||
/// Returns `true` if the given expression is a builtin exception. | ||
/// | ||
/// See: <https://docs.python.org/3/library/exceptions.html#exception-hierarchy> | ||
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" | ||
] | ||
) | ||
}); | ||
} |
Oops, something went wrong.