Ignore PERF203 if try contains loop control flow statements (#6536)
This commit is contained in:
@@ -21,3 +21,29 @@ while i < 10:
|
||||
print("error")
|
||||
|
||||
i += 1
|
||||
|
||||
# OK - no other way to write this
|
||||
for i in range(10):
|
||||
try:
|
||||
print(f"{i}")
|
||||
break
|
||||
except:
|
||||
print("error")
|
||||
|
||||
# OK - no other way to write this
|
||||
for i in range(10):
|
||||
try:
|
||||
print(f"{i}")
|
||||
continue
|
||||
except:
|
||||
print("error")
|
||||
|
||||
|
||||
# OK - no other way to write this
|
||||
for i in range(10):
|
||||
try:
|
||||
print(f"{i}")
|
||||
if i > 0:
|
||||
break
|
||||
except:
|
||||
print("error")
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
use ruff_python_ast::{self as ast, Ranged, Stmt};
|
||||
|
||||
use ruff_diagnostics::{Diagnostic, Violation};
|
||||
use ruff_macros::{derive_message_formats, violation};
|
||||
use ruff_python_ast::statement_visitor::{walk_stmt, StatementVisitor};
|
||||
use ruff_python_ast::{self as ast, Ranged, Stmt};
|
||||
|
||||
use crate::checkers::ast::Checker;
|
||||
use crate::settings::types::PythonVersion;
|
||||
@@ -35,6 +35,7 @@ use crate::settings::types::PythonVersion;
|
||||
/// int_numbers.append(int(num))
|
||||
/// except ValueError as e:
|
||||
/// print(f"Couldn't convert to integer: {e}")
|
||||
/// break
|
||||
/// ```
|
||||
///
|
||||
/// Use instead:
|
||||
@@ -67,7 +68,7 @@ pub(crate) fn try_except_in_loop(checker: &mut Checker, body: &[Stmt]) {
|
||||
return;
|
||||
}
|
||||
|
||||
let [Stmt::Try(ast::StmtTry { handlers, .. })] = body else {
|
||||
let [Stmt::Try(ast::StmtTry { handlers, body, .. })] = body else {
|
||||
return;
|
||||
};
|
||||
|
||||
@@ -75,7 +76,37 @@ pub(crate) fn try_except_in_loop(checker: &mut Checker, body: &[Stmt]) {
|
||||
return;
|
||||
};
|
||||
|
||||
// Avoid flagging `try`-`except` blocks that contain `break` or `continue`,
|
||||
// which rely on the exception handling mechanism.
|
||||
if has_break_or_continue(body) {
|
||||
return;
|
||||
}
|
||||
|
||||
checker
|
||||
.diagnostics
|
||||
.push(Diagnostic::new(TryExceptInLoop, handler.range()));
|
||||
}
|
||||
|
||||
/// Returns `true` if a `break` or `continue` statement is present in `body`.
|
||||
fn has_break_or_continue(body: &[Stmt]) -> bool {
|
||||
let mut visitor = LoopControlFlowVisitor::default();
|
||||
visitor.visit_body(body);
|
||||
visitor.has_break_or_continue
|
||||
}
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
struct LoopControlFlowVisitor {
|
||||
has_break_or_continue: bool,
|
||||
}
|
||||
|
||||
impl StatementVisitor<'_> for LoopControlFlowVisitor {
|
||||
fn visit_stmt(&mut self, stmt: &Stmt) {
|
||||
match stmt {
|
||||
Stmt::Break(_) | Stmt::Continue(_) => self.has_break_or_continue = true,
|
||||
Stmt::FunctionDef(_) | Stmt::ClassDef(_) => {
|
||||
// Don't recurse.
|
||||
}
|
||||
_ => walk_stmt(self, stmt),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user