mirror of
https://github.com/astral-sh/ruff.git
synced 2025-09-01 08:07:47 +00:00
Avoid iterating over body twice (#2439)
This commit is contained in:
parent
7f44ffb55c
commit
1ea88ea56b
4 changed files with 68 additions and 30 deletions
|
@ -115,3 +115,13 @@ def f():
|
||||||
else:
|
else:
|
||||||
return True
|
return True
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
def f():
|
||||||
|
x = 1
|
||||||
|
|
||||||
|
# SIM110
|
||||||
|
for x in iterable:
|
||||||
|
if check(x):
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
|
|
@ -87,6 +87,9 @@ pub struct Checker<'a> {
|
||||||
deferred_functions: Vec<(&'a Stmt, DeferralContext<'a>, VisibleScope)>,
|
deferred_functions: Vec<(&'a Stmt, DeferralContext<'a>, VisibleScope)>,
|
||||||
deferred_lambdas: Vec<(&'a Expr, DeferralContext<'a>)>,
|
deferred_lambdas: Vec<(&'a Expr, DeferralContext<'a>)>,
|
||||||
deferred_assignments: Vec<DeferralContext<'a>>,
|
deferred_assignments: Vec<DeferralContext<'a>>,
|
||||||
|
// Body iteration; used to peek at siblings.
|
||||||
|
body: &'a [Stmt],
|
||||||
|
body_index: usize,
|
||||||
// Internal, derivative state.
|
// Internal, derivative state.
|
||||||
visible_scope: VisibleScope,
|
visible_scope: VisibleScope,
|
||||||
in_annotation: bool,
|
in_annotation: bool,
|
||||||
|
@ -145,6 +148,9 @@ impl<'a> Checker<'a> {
|
||||||
deferred_functions: vec![],
|
deferred_functions: vec![],
|
||||||
deferred_lambdas: vec![],
|
deferred_lambdas: vec![],
|
||||||
deferred_assignments: vec![],
|
deferred_assignments: vec![],
|
||||||
|
// Body iteration.
|
||||||
|
body: &[],
|
||||||
|
body_index: 0,
|
||||||
// Internal, derivative state.
|
// Internal, derivative state.
|
||||||
visible_scope: VisibleScope {
|
visible_scope: VisibleScope {
|
||||||
modifier: Modifier::Module,
|
modifier: Modifier::Module,
|
||||||
|
@ -1590,7 +1596,11 @@ where
|
||||||
if self.settings.rules.enabled(&Rule::ConvertLoopToAny)
|
if self.settings.rules.enabled(&Rule::ConvertLoopToAny)
|
||||||
|| self.settings.rules.enabled(&Rule::ConvertLoopToAll)
|
|| self.settings.rules.enabled(&Rule::ConvertLoopToAll)
|
||||||
{
|
{
|
||||||
flake8_simplify::rules::convert_for_loop_to_any_all(self, stmt, None);
|
flake8_simplify::rules::convert_for_loop_to_any_all(
|
||||||
|
self,
|
||||||
|
stmt,
|
||||||
|
self.current_sibling_stmt(),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
if self.settings.rules.enabled(&Rule::KeyInDict) {
|
if self.settings.rules.enabled(&Rule::KeyInDict) {
|
||||||
flake8_simplify::rules::key_in_dict_for(self, target, iter);
|
flake8_simplify::rules::key_in_dict_for(self, target, iter);
|
||||||
|
@ -3694,19 +3704,18 @@ where
|
||||||
flake8_pie::rules::no_unnecessary_pass(self, body);
|
flake8_pie::rules::no_unnecessary_pass(self, body);
|
||||||
}
|
}
|
||||||
|
|
||||||
if self.settings.rules.enabled(&Rule::ConvertLoopToAny)
|
let prev_body = self.body;
|
||||||
|| self.settings.rules.enabled(&Rule::ConvertLoopToAll)
|
let prev_body_index = self.body_index;
|
||||||
{
|
self.body = body;
|
||||||
for (stmt, sibling) in body.iter().tuple_windows() {
|
self.body_index = 0;
|
||||||
if matches!(stmt.node, StmtKind::For { .. })
|
|
||||||
&& matches!(sibling.node, StmtKind::Return { .. })
|
for stmt in body {
|
||||||
{
|
self.visit_stmt(stmt);
|
||||||
flake8_simplify::rules::convert_for_loop_to_any_all(self, stmt, Some(sibling));
|
self.body_index += 1;
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
visitor::walk_body(self, body);
|
self.body = prev_body;
|
||||||
|
self.body_index = prev_body_index;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3795,6 +3804,11 @@ impl<'a> Checker<'a> {
|
||||||
self.exprs.iter().rev().nth(2)
|
self.exprs.iter().rev().nth(2)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Return the `Stmt` that immediately follows the current `Stmt`, if any.
|
||||||
|
pub fn current_sibling_stmt(&self) -> Option<&'a Stmt> {
|
||||||
|
self.body.get(self.body_index + 1)
|
||||||
|
}
|
||||||
|
|
||||||
pub fn current_scope(&self) -> &Scope {
|
pub fn current_scope(&self) -> &Scope {
|
||||||
&self.scopes[*(self.scope_stack.last().expect("No current scope found"))]
|
&self.scopes[*(self.scope_stack.last().expect("No current scope found"))]
|
||||||
}
|
}
|
||||||
|
@ -5219,9 +5233,7 @@ pub fn check_ast(
|
||||||
};
|
};
|
||||||
|
|
||||||
// Iterate over the AST.
|
// Iterate over the AST.
|
||||||
for stmt in python_ast {
|
checker.visit_body(python_ast);
|
||||||
checker.visit_stmt(stmt);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check any deferred statements.
|
// Check any deferred statements.
|
||||||
checker.check_deferred_functions();
|
checker.check_deferred_functions();
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
use rustpython_ast::{
|
use rustpython_ast::{
|
||||||
Comprehension, Constant, Expr, ExprContext, ExprKind, Stmt, StmtKind, Unaryop,
|
Comprehension, Constant, Expr, ExprContext, ExprKind, Location, Stmt, StmtKind, Unaryop,
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::ast::helpers::{create_expr, create_stmt, unparse_stmt};
|
use crate::ast::helpers::{create_expr, create_stmt, unparse_stmt};
|
||||||
|
@ -16,6 +16,7 @@ struct Loop<'a> {
|
||||||
test: &'a Expr,
|
test: &'a Expr,
|
||||||
target: &'a Expr,
|
target: &'a Expr,
|
||||||
iter: &'a Expr,
|
iter: &'a Expr,
|
||||||
|
terminal: Location,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Extract the returned boolean values a `StmtKind::For` with an `else` body.
|
/// Extract the returned boolean values a `StmtKind::For` with an `else` body.
|
||||||
|
@ -78,6 +79,7 @@ fn return_values_for_else(stmt: &Stmt) -> Option<Loop> {
|
||||||
test: nested_test,
|
test: nested_test,
|
||||||
target,
|
target,
|
||||||
iter,
|
iter,
|
||||||
|
terminal: stmt.end_location.unwrap(),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -142,6 +144,7 @@ fn return_values_for_siblings<'a>(stmt: &'a Stmt, sibling: &'a Stmt) -> Option<L
|
||||||
test: nested_test,
|
test: nested_test,
|
||||||
target,
|
target,
|
||||||
iter,
|
iter,
|
||||||
|
terminal: sibling.end_location.unwrap(),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -172,12 +175,12 @@ fn return_stmt(id: &str, test: &Expr, target: &Expr, iter: &Expr, stylist: &Styl
|
||||||
|
|
||||||
/// SIM110, SIM111
|
/// SIM110, SIM111
|
||||||
pub fn convert_for_loop_to_any_all(checker: &mut Checker, stmt: &Stmt, sibling: Option<&Stmt>) {
|
pub fn convert_for_loop_to_any_all(checker: &mut Checker, stmt: &Stmt, sibling: Option<&Stmt>) {
|
||||||
if let Some(loop_info) = match sibling {
|
// There are two cases to consider:
|
||||||
// Ex) `for` loop with an `else: return True` or `else: return False`.
|
// - `for` loop with an `else: return True` or `else: return False`.
|
||||||
None => return_values_for_else(stmt),
|
// - `for` loop followed by `return True` or `return False`
|
||||||
// Ex) `for` loop followed by `return True` or `return False`
|
if let Some(loop_info) = return_values_for_else(stmt)
|
||||||
Some(sibling) => return_values_for_siblings(stmt, sibling),
|
.or_else(|| sibling.and_then(|sibling| return_values_for_siblings(stmt, sibling)))
|
||||||
} {
|
{
|
||||||
if loop_info.return_value && !loop_info.next_return_value {
|
if loop_info.return_value && !loop_info.next_return_value {
|
||||||
if checker.settings.rules.enabled(&Rule::ConvertLoopToAny) {
|
if checker.settings.rules.enabled(&Rule::ConvertLoopToAny) {
|
||||||
let contents = return_stmt(
|
let contents = return_stmt(
|
||||||
|
@ -203,10 +206,7 @@ pub fn convert_for_loop_to_any_all(checker: &mut Checker, stmt: &Stmt, sibling:
|
||||||
diagnostic.amend(Fix::replacement(
|
diagnostic.amend(Fix::replacement(
|
||||||
contents,
|
contents,
|
||||||
stmt.location,
|
stmt.location,
|
||||||
match sibling {
|
loop_info.terminal,
|
||||||
None => stmt.end_location.unwrap(),
|
|
||||||
Some(sibling) => sibling.end_location.unwrap(),
|
|
||||||
},
|
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
checker.diagnostics.push(diagnostic);
|
checker.diagnostics.push(diagnostic);
|
||||||
|
@ -253,10 +253,7 @@ pub fn convert_for_loop_to_any_all(checker: &mut Checker, stmt: &Stmt, sibling:
|
||||||
diagnostic.amend(Fix::replacement(
|
diagnostic.amend(Fix::replacement(
|
||||||
contents,
|
contents,
|
||||||
stmt.location,
|
stmt.location,
|
||||||
match sibling {
|
loop_info.terminal,
|
||||||
None => stmt.end_location.unwrap(),
|
|
||||||
Some(sibling) => sibling.end_location.unwrap(),
|
|
||||||
},
|
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
checker.diagnostics.push(diagnostic);
|
checker.diagnostics.push(diagnostic);
|
||||||
|
|
|
@ -59,4 +59,23 @@ expression: diagnostics
|
||||||
row: 77
|
row: 77
|
||||||
column: 20
|
column: 20
|
||||||
parent: ~
|
parent: ~
|
||||||
|
- kind:
|
||||||
|
ConvertLoopToAny:
|
||||||
|
any: return any(check(x) for x in iterable)
|
||||||
|
location:
|
||||||
|
row: 124
|
||||||
|
column: 4
|
||||||
|
end_location:
|
||||||
|
row: 126
|
||||||
|
column: 23
|
||||||
|
fix:
|
||||||
|
content:
|
||||||
|
- return any(check(x) for x in iterable)
|
||||||
|
location:
|
||||||
|
row: 124
|
||||||
|
column: 4
|
||||||
|
end_location:
|
||||||
|
row: 127
|
||||||
|
column: 16
|
||||||
|
parent: ~
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue