-
Notifications
You must be signed in to change notification settings - Fork 66
Expand file tree
/
Copy pathasttools.rs
More file actions
117 lines (112 loc) · 3.38 KB
/
asttools.rs
File metadata and controls
117 lines (112 loc) · 3.38 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
//! AST traversal and analysis utilities.
//!
//! This module provides helper functions and macros for traversing
//! and analyzing the AST tree structure.
use crate::node::Node;
/// Gets an ancestor at a specific level above the current node.
///
/// # Arguments
/// * `node` - The starting node
/// * `level` - How many levels up to traverse (0 returns the node itself)
///
/// # Returns
/// The ancestor node at the specified level, or None if the tree isn't deep enough.
///
/// # Example
/// ```ignore
/// // Get the grandparent (2 levels up)
/// if let Some(grandparent) = get_parent(&node, 2) {
/// println!("Grandparent kind: {}", grandparent.kind());
/// }
/// ```
pub fn get_parent<'a>(node: &Node<'a>, level: usize) -> Option<Node<'a>> {
let mut level = level;
let mut current = *node;
while level != 0 {
current = current.parent()?;
level -= 1;
}
Some(current)
}
/// Checks if a node has specific ancestors in sequence.
///
/// This macro checks if the node's ancestors match a specific pattern,
/// where the first pattern(s) are immediate ancestors and the last pattern
/// is the final ancestor to match.
///
/// # Example
/// ```ignore
/// // Check if node is inside a function inside a class
/// let is_method = has_ancestors!(node, Class | Struct, Function);
/// ```
#[macro_export]
macro_rules! has_ancestors {
($node:expr, $( $typs:pat_param )|*, $( $typ:pat_param ),+) => {{
let mut res = false;
loop {
let mut node = *$node;
$(
if let Some(parent) = node.parent() {
match parent.kind_id().into() {
$typ => {
node = parent;
},
_ => {
break;
}
}
} else {
break;
}
)*
if let Some(parent) = node.parent() {
match parent.kind_id().into() {
$( $typs )|+ => {
res = true;
},
_ => {}
}
}
break;
}
res
}};
}
/// Counts specific ancestors matching a pattern until a stop condition.
///
/// This macro traverses up the tree counting ancestors that match the given
/// patterns, stopping when it encounters an ancestor matching the stop pattern.
///
/// # Example
/// ```ignore
/// // Count nested if statements until we hit a function boundary
/// let nesting = count_specific_ancestors!(node, If | ElseIf, Function | Method);
/// ```
#[macro_export]
macro_rules! count_specific_ancestors {
($node:expr, $checker:ty, $( $typs:pat_param )|*, $( $stops:pat_param )|*) => {{
let mut count = 0;
let mut node = *$node;
while let Some(parent) = node.parent() {
match parent.kind_id().into() {
$( $typs )|* => {
if !<$checker>::is_else_if(&parent) {
count += 1;
}
},
$( $stops )|* => break,
_ => {}
}
node = parent;
}
count
}};
}
#[cfg(test)]
mod tests {
#[test]
fn test_get_parent_level_zero() {
// Level 0 should return the same node
// (actual test would need a real node)
}
}