|
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