mirror of
https://github.com/astral-sh/ruff.git
synced 2025-08-18 01:20:40 +00:00

Implements SIM113 from #998 Added tests Limitations - No fix yet - Only flag cases where index variable immediately precede `for` loop @charliermarsh please review and let me know any improvements --------- Co-authored-by: Charlie Marsh <charlie.r.marsh@gmail.com>
93 lines
3 KiB
Rust
93 lines
3 KiB
Rust
//! Utilities for manually traversing a Python AST.
|
|
use crate::{self as ast, ExceptHandler, Stmt, Suite};
|
|
|
|
/// Given a [`Stmt`] and its parent, return the [`Suite`] that contains the [`Stmt`].
|
|
pub fn suite<'a>(stmt: &'a Stmt, parent: &'a Stmt) -> Option<&'a Suite> {
|
|
// TODO: refactor this to work without a parent, ie when `stmt` is at the top level
|
|
match parent {
|
|
Stmt::FunctionDef(ast::StmtFunctionDef { body, .. }) => Some(body),
|
|
Stmt::ClassDef(ast::StmtClassDef { body, .. }) => Some(body),
|
|
Stmt::For(ast::StmtFor { body, orelse, .. }) => {
|
|
if body.contains(stmt) {
|
|
Some(body)
|
|
} else if orelse.contains(stmt) {
|
|
Some(orelse)
|
|
} else {
|
|
None
|
|
}
|
|
}
|
|
Stmt::While(ast::StmtWhile { body, orelse, .. }) => {
|
|
if body.contains(stmt) {
|
|
Some(body)
|
|
} else if orelse.contains(stmt) {
|
|
Some(orelse)
|
|
} else {
|
|
None
|
|
}
|
|
}
|
|
Stmt::If(ast::StmtIf {
|
|
body,
|
|
elif_else_clauses,
|
|
..
|
|
}) => {
|
|
if body.contains(stmt) {
|
|
Some(body)
|
|
} else {
|
|
elif_else_clauses
|
|
.iter()
|
|
.map(|elif_else_clause| &elif_else_clause.body)
|
|
.find(|body| body.contains(stmt))
|
|
}
|
|
}
|
|
Stmt::With(ast::StmtWith { body, .. }) => Some(body),
|
|
Stmt::Match(ast::StmtMatch { cases, .. }) => cases
|
|
.iter()
|
|
.map(|case| &case.body)
|
|
.find(|body| body.contains(stmt)),
|
|
Stmt::Try(ast::StmtTry {
|
|
body,
|
|
handlers,
|
|
orelse,
|
|
finalbody,
|
|
..
|
|
}) => {
|
|
if body.contains(stmt) {
|
|
Some(body)
|
|
} else if orelse.contains(stmt) {
|
|
Some(orelse)
|
|
} else if finalbody.contains(stmt) {
|
|
Some(finalbody)
|
|
} else {
|
|
handlers
|
|
.iter()
|
|
.filter_map(ExceptHandler::as_except_handler)
|
|
.map(|handler| &handler.body)
|
|
.find(|body| body.contains(stmt))
|
|
}
|
|
}
|
|
_ => None,
|
|
}
|
|
}
|
|
|
|
/// Given a [`Stmt`] and its containing [`Suite`], return the next [`Stmt`] in the [`Suite`].
|
|
pub fn next_sibling<'a>(stmt: &'a Stmt, suite: &'a Suite) -> Option<&'a Stmt> {
|
|
let mut iter = suite.iter();
|
|
while let Some(sibling) = iter.next() {
|
|
if sibling == stmt {
|
|
return iter.next();
|
|
}
|
|
}
|
|
None
|
|
}
|
|
|
|
/// Given a [`Stmt`] and its containing [`Suite`], return the previous [`Stmt`] in the [`Suite`].
|
|
pub fn prev_sibling<'a>(stmt: &'a Stmt, suite: &'a Suite) -> Option<&'a Stmt> {
|
|
let mut prev = None;
|
|
for sibling in suite {
|
|
if sibling == stmt {
|
|
return prev;
|
|
}
|
|
prev = Some(sibling);
|
|
}
|
|
None
|
|
}
|