Ignore PERF203 if try contains loop control flow statements (#6536)

This commit is contained in:
Evan Rittenhouse
2023-08-14 15:47:37 -05:00
committed by GitHub
parent a3bf6d9cb7
commit 1a52b548e7
2 changed files with 60 additions and 3 deletions

View File

@@ -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")

View File

@@ -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),
}
}
}