ruff/crates/ruff_python_ast/src/traversal.rs
Valeriy Savchenko 26d53c56a2
[refurb] Implement repeated-append rule (FURB113) (#6702)
## Summary

As an initial effort with replicating `refurb` rules (#1348 ), this PR
adds support for
[FURB113](https://github.com/dosisod/refurb/blob/master/refurb/checks/builtin/list_extend.py)
and adds a new category of checks.

## Test Plan

I included a new test + checked that all other tests pass.
2023-08-28 22:51:59 +00:00

81 lines
2.6 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
}