|
1 | 1 | //! Utilities for manually traversing a Python AST. |
2 | | -use crate::{self as ast, ExceptHandler, Stmt, Suite}; |
| 2 | +use crate::{self as ast, AnyNodeRef, ExceptHandler, Stmt}; |
3 | 3 |
|
4 | | -/// Given a [`Stmt`] and its parent, return the [`Suite`] that contains the [`Stmt`]. |
5 | | -pub fn suite<'a>(stmt: &'a Stmt, parent: &'a Stmt) -> Option<&'a Suite> { |
| 4 | +/// Given a [`Stmt`] and its parent, return the [`ast::Suite`] that contains the [`Stmt`]. |
| 5 | +pub fn suite<'a>(stmt: &'a Stmt, parent: &'a Stmt) -> Option<EnclosingSuite<'a>> { |
6 | 6 | // TODO: refactor this to work without a parent, ie when `stmt` is at the top level |
7 | 7 | match parent { |
8 | | - Stmt::FunctionDef(ast::StmtFunctionDef { body, .. }) => Some(body), |
9 | | - Stmt::ClassDef(ast::StmtClassDef { body, .. }) => Some(body), |
10 | | - Stmt::For(ast::StmtFor { body, orelse, .. }) => { |
11 | | - if body.contains(stmt) { |
12 | | - Some(body) |
13 | | - } else if orelse.contains(stmt) { |
14 | | - Some(orelse) |
15 | | - } else { |
16 | | - None |
17 | | - } |
18 | | - } |
19 | | - Stmt::While(ast::StmtWhile { body, orelse, .. }) => { |
20 | | - if body.contains(stmt) { |
21 | | - Some(body) |
22 | | - } else if orelse.contains(stmt) { |
23 | | - Some(orelse) |
24 | | - } else { |
25 | | - None |
26 | | - } |
27 | | - } |
| 8 | + Stmt::FunctionDef(ast::StmtFunctionDef { body, .. }) => EnclosingSuite::new(body, stmt), |
| 9 | + Stmt::ClassDef(ast::StmtClassDef { body, .. }) => EnclosingSuite::new(body, stmt), |
| 10 | + Stmt::For(ast::StmtFor { body, orelse, .. }) => [body, orelse] |
| 11 | + .iter() |
| 12 | + .find_map(|suite| EnclosingSuite::new(suite, stmt)), |
| 13 | + Stmt::While(ast::StmtWhile { body, orelse, .. }) => [body, orelse] |
| 14 | + .iter() |
| 15 | + .find_map(|suite| EnclosingSuite::new(suite, stmt)), |
28 | 16 | Stmt::If(ast::StmtIf { |
29 | 17 | body, |
30 | 18 | elif_else_clauses, |
31 | 19 | .. |
32 | | - }) => { |
33 | | - if body.contains(stmt) { |
34 | | - Some(body) |
35 | | - } else { |
36 | | - elif_else_clauses |
37 | | - .iter() |
38 | | - .map(|elif_else_clause| &elif_else_clause.body) |
39 | | - .find(|body| body.contains(stmt)) |
40 | | - } |
41 | | - } |
42 | | - Stmt::With(ast::StmtWith { body, .. }) => Some(body), |
| 20 | + }) => [body] |
| 21 | + .into_iter() |
| 22 | + .chain(elif_else_clauses.iter().map(|clause| &clause.body)) |
| 23 | + .find_map(|suite| EnclosingSuite::new(suite, stmt)), |
| 24 | + Stmt::With(ast::StmtWith { body, .. }) => EnclosingSuite::new(body, stmt), |
43 | 25 | Stmt::Match(ast::StmtMatch { cases, .. }) => cases |
44 | 26 | .iter() |
45 | 27 | .map(|case| &case.body) |
46 | | - .find(|body| body.contains(stmt)), |
| 28 | + .find_map(|body| EnclosingSuite::new(body, stmt)), |
47 | 29 | Stmt::Try(ast::StmtTry { |
48 | 30 | body, |
49 | 31 | handlers, |
50 | 32 | orelse, |
51 | 33 | finalbody, |
52 | 34 | .. |
53 | | - }) => { |
54 | | - if body.contains(stmt) { |
55 | | - Some(body) |
56 | | - } else if orelse.contains(stmt) { |
57 | | - Some(orelse) |
58 | | - } else if finalbody.contains(stmt) { |
59 | | - Some(finalbody) |
60 | | - } else { |
| 35 | + }) => [body, orelse, finalbody] |
| 36 | + .into_iter() |
| 37 | + .chain( |
61 | 38 | handlers |
62 | 39 | .iter() |
63 | 40 | .filter_map(ExceptHandler::as_except_handler) |
64 | | - .map(|handler| &handler.body) |
65 | | - .find(|body| body.contains(stmt)) |
66 | | - } |
67 | | - } |
| 41 | + .map(|handler| &handler.body), |
| 42 | + ) |
| 43 | + .find_map(|suite| EnclosingSuite::new(suite, stmt)), |
68 | 44 | _ => None, |
69 | 45 | } |
70 | 46 | } |
71 | 47 |
|
72 | | -/// Given a [`Stmt`] and its containing [`Suite`], return the next [`Stmt`] in the [`Suite`]. |
73 | | -pub fn next_sibling<'a>(stmt: &'a Stmt, suite: &'a Suite) -> Option<&'a Stmt> { |
74 | | - let mut iter = suite.iter(); |
75 | | - while let Some(sibling) = iter.next() { |
76 | | - if sibling == stmt { |
77 | | - return iter.next(); |
78 | | - } |
| 48 | +pub struct EnclosingSuite<'a> { |
| 49 | + suite: &'a [Stmt], |
| 50 | + position: usize, |
| 51 | +} |
| 52 | + |
| 53 | +impl<'a> EnclosingSuite<'a> { |
| 54 | + pub fn new(suite: &'a [Stmt], stmt: &'a Stmt) -> Option<Self> { |
| 55 | + let position = suite |
| 56 | + .iter() |
| 57 | + .position(|sibling| AnyNodeRef::ptr_eq(sibling.into(), stmt.into()))?; |
| 58 | + |
| 59 | + Some(EnclosingSuite { suite, position }) |
| 60 | + } |
| 61 | + |
| 62 | + pub fn next_sibling(&self) -> Option<&'a Stmt> { |
| 63 | + self.suite.get(self.position + 1) |
| 64 | + } |
| 65 | + |
| 66 | + pub fn next_siblings(&self) -> &'a [Stmt] { |
| 67 | + self.suite.get(self.position + 1..).unwrap_or_default() |
| 68 | + } |
| 69 | + |
| 70 | + pub fn previous_sibling(&self) -> Option<&'a Stmt> { |
| 71 | + self.suite.get(self.position.checked_sub(1)?) |
| 72 | + } |
| 73 | +} |
| 74 | + |
| 75 | +impl std::ops::Deref for EnclosingSuite<'_> { |
| 76 | + type Target = [Stmt]; |
| 77 | + |
| 78 | + fn deref(&self) -> &Self::Target { |
| 79 | + self.suite |
79 | 80 | } |
80 | | - None |
81 | 81 | } |
0 commit comments