Avoid iterating over body twice (#2439)

This commit is contained in:
Charlie Marsh 2023-02-01 08:12:36 -05:00 committed by GitHub
parent 7f44ffb55c
commit 1ea88ea56b
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 68 additions and 30 deletions

View file

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

View file

@ -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();

View file

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

View file

@ -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: ~