aidl_parser/
traverse.rs

1use std::ops::ControlFlow;
2
3use crate::ast;
4use crate::symbol::{ConstOwner, Symbol};
5
6/// Determine the depth of traversal functions
7#[derive(Clone, Copy)]
8pub enum SymbolFilter {
9    /// Only extract the top-level item
10    ItemsOnly,
11    /// Extract the top-level and its direct children (e.g.: parcelable + fields)
12    ItemsAndItemElements,
13    /// Extract all symbols (incl. types)
14    All,
15}
16
17/// Traverse the AST and provide the symbols to the given closure
18///
19/// This function works like the visitor pattern.
20pub fn walk_symbols<'a, F: FnMut(Symbol<'a>)>(ast: &'a ast::Aidl, filter: SymbolFilter, mut f: F) {
21    walk_symbols_with_control_flow(ast, filter, |smb| -> ControlFlow<()> {
22        f(smb);
23        ControlFlow::Continue(())
24    });
25}
26
27/// Traverse the AST and filter the symbols based on the given predicate.
28///
29/// For each symbol, the predicate is called and the symbol will be
30/// added to the returned vector when the return value is true.
31///
32/// See also: [`walk_symbols`]
33pub fn filter_symbols<'a, F>(ast: &'a ast::Aidl, filter: SymbolFilter, mut f: F) -> Vec<Symbol<'a>>
34where
35    F: FnMut(&Symbol<'a>) -> bool,
36{
37    let mut v = Vec::new();
38    walk_symbols(ast, filter, |symbol| {
39        if f(&symbol) {
40            v.push(symbol);
41        }
42    });
43
44    v
45}
46
47/// Look for a symbol inside the AST based on the given predicate.
48///
49/// Return the first symbol for which the predicate returns true, or `None`
50/// if no matching symbol has been found.
51///
52/// See also: [`walk_symbols`]
53pub fn find_symbol<'a, F>(ast: &'a ast::Aidl, filter: SymbolFilter, mut f: F) -> Option<Symbol<'a>>
54where
55    F: FnMut(&Symbol<'a>) -> bool,
56{
57    let res = walk_symbols_with_control_flow(ast, filter, |smb| -> ControlFlow<Symbol<'a>> {
58        if f(&smb) {
59            ControlFlow::Break(smb)
60        } else {
61            ControlFlow::Continue(())
62        }
63    });
64
65    match res {
66        ControlFlow::Continue(_) => None,
67        ControlFlow::Break(smb) => Some(smb),
68    }
69}
70
71/// Look for a symbol at a given position.
72///
73/// Return the first symbol whose range includes the given position, or `None`
74/// if no matching symbol has been found.
75///
76/// See also: [`find_symbol`]
77pub fn find_symbol_at_line_col(
78    ast: &ast::Aidl,
79    filter: SymbolFilter,
80    line_col: (usize, usize),
81) -> Option<Symbol> {
82    find_symbol(ast, filter, |smb| range_contains(smb.get_range(), line_col))
83}
84
85#[allow(clippy::needless_borrow)] // because of false-positives when invoking macros...
86fn walk_symbols_with_control_flow<'a, V, F>(
87    ast: &'a ast::Aidl,
88    filter: SymbolFilter,
89    mut f: F,
90) -> ControlFlow<V>
91where
92    F: FnMut(Symbol<'a>) -> ControlFlow<V>,
93{
94    macro_rules! visit_type_helper {
95        ($t:expr, $f:ident) => {
96            if $t.kind == ast::TypeKind::Array {
97                // For arrays, start with the array element type, then on the array itself
98                $t.generic_types
99                    .iter()
100                    .try_for_each(|t| $f(Symbol::Type(t)))?;
101                $f(Symbol::Type($t))?;
102            } else {
103                // For other types, start with the main type and then its generic types
104                $f(Symbol::Type($t))?;
105                $t.generic_types
106                    .iter()
107                    .try_for_each(|t| $f(Symbol::Type(t)))?;
108            }
109        };
110    }
111
112    if let SymbolFilter::All = filter {
113        f(Symbol::Package(&ast.package));
114
115        for import in &ast.imports {
116            f(Symbol::Import(import))?;
117        }
118    }
119
120    match ast.item {
121        ast::Item::Interface(ref i) => {
122            f(Symbol::Interface(i, &ast.package))?;
123            if let SymbolFilter::ItemsOnly = filter {
124                return ControlFlow::Continue(());
125            }
126
127            i.elements.iter().try_for_each(|el| match el {
128                ast::InterfaceElement::Method(m) => {
129                    f(Symbol::Method(m, i))?;
130                    if let SymbolFilter::All = filter {
131                        visit_type_helper!(&m.return_type, f);
132                        m.args.iter().try_for_each(|arg| {
133                            f(Symbol::Arg(arg, m))?;
134                            visit_type_helper!(&arg.arg_type, f);
135                            ControlFlow::Continue(())
136                        })?;
137                    }
138                    ControlFlow::Continue(())
139                }
140                ast::InterfaceElement::Const(c) => {
141                    f(Symbol::Const(c, ConstOwner::Interface(i)))?;
142                    if let SymbolFilter::All = filter {
143                        visit_type_helper!(&c.const_type, f);
144                    }
145                    ControlFlow::Continue(())
146                }
147            })?;
148        }
149        ast::Item::Parcelable(ref p) => {
150            f(Symbol::Parcelable(p, &ast.package))?;
151            if let SymbolFilter::ItemsOnly = filter {
152                return ControlFlow::Continue(());
153            }
154
155            p.elements.iter().try_for_each(|el| match el {
156                ast::ParcelableElement::Field(fi) => {
157                    f(Symbol::Field(fi, p))?;
158                    if let SymbolFilter::All = filter {
159                        visit_type_helper!(&fi.field_type, f);
160                    }
161
162                    ControlFlow::Continue(())
163                }
164                ast::ParcelableElement::Const(c) => {
165                    f(Symbol::Const(c, ConstOwner::Parcelable(p)))?;
166                    if let SymbolFilter::All = filter {
167                        visit_type_helper!(&c.const_type, f);
168                    }
169                    ControlFlow::Continue(())
170                }
171            })?;
172        }
173        ast::Item::Enum(ref e) => {
174            f(Symbol::Enum(e, &ast.package))?;
175            if let SymbolFilter::ItemsOnly = filter {
176                return ControlFlow::Continue(());
177            }
178
179            e.elements.iter().try_for_each(|el| {
180                f(Symbol::EnumElement(el, e))?;
181                ControlFlow::Continue(())
182            })?;
183        }
184    }
185
186    ControlFlow::Continue(())
187}
188
189fn range_contains(range: &ast::Range, line_col: (usize, usize)) -> bool {
190    if range.start.line_col.0 > line_col.0 {
191        return false;
192    }
193
194    if range.start.line_col.0 == line_col.0 && range.start.line_col.1 > line_col.1 {
195        return false;
196    }
197
198    if range.end.line_col.0 < line_col.0 {
199        return false;
200    }
201
202    if range.end.line_col.0 == line_col.0 && range.end.line_col.1 < line_col.1 {
203        return false;
204    }
205
206    true
207}
208
209/// Traverse the AST and provide the types to the given closure
210pub fn walk_types<F: FnMut(&ast::Type)>(ast: &ast::Aidl, mut f: F) {
211    let mut visit_type_helper = move |type_: &ast::Type| {
212        if type_.kind == ast::TypeKind::Array {
213            // For arrays, start with the array element type, then on the array itself
214            type_.generic_types.iter().for_each(&mut f);
215            f(type_);
216        } else {
217            // For other types, start with the main type and then its generic types
218            f(type_);
219            type_.generic_types.iter().for_each(&mut f);
220        }
221    };
222
223    match ast.item {
224        ast::Item::Interface(ref i) => {
225            i.elements.iter().for_each(|el| match el {
226                ast::InterfaceElement::Method(m) => {
227                    visit_type_helper(&m.return_type);
228                    m.args.iter().for_each(|arg| {
229                        visit_type_helper(&arg.arg_type);
230                    })
231                }
232                ast::InterfaceElement::Const(c) => {
233                    visit_type_helper(&c.const_type);
234                }
235            });
236        }
237        ast::Item::Parcelable(ref p) => {
238            p.elements.iter().for_each(|el| match el {
239                ast::ParcelableElement::Field(fi) => {
240                    visit_type_helper(&fi.field_type);
241                }
242                ast::ParcelableElement::Const(c) => {
243                    visit_type_helper(&c.const_type);
244                }
245            });
246        }
247        ast::Item::Enum(_) => (),
248    }
249}
250
251pub(crate) fn walk_types_mut<F: FnMut(&mut ast::Type)>(ast: &mut ast::Aidl, mut f: F) {
252    let mut visit_type_helper = move |type_: &mut ast::Type| {
253        f(type_);
254        type_.generic_types.iter_mut().for_each(&mut f);
255    };
256
257    match ast.item {
258        ast::Item::Interface(ref mut i) => {
259            i.elements.iter_mut().for_each(|el| match el {
260                ast::InterfaceElement::Method(m) => {
261                    visit_type_helper(&mut m.return_type);
262                    m.args.iter_mut().for_each(|arg| {
263                        visit_type_helper(&mut arg.arg_type);
264                    })
265                }
266                ast::InterfaceElement::Const(c) => {
267                    visit_type_helper(&mut c.const_type);
268                }
269            });
270        }
271        ast::Item::Parcelable(ref mut p) => {
272            p.elements.iter_mut().for_each(|el| match el {
273                ast::ParcelableElement::Field(fi) => {
274                    visit_type_helper(&mut fi.field_type);
275                }
276                ast::ParcelableElement::Const(c) => {
277                    visit_type_helper(&mut c.const_type);
278                }
279            });
280        }
281        ast::Item::Enum(_) => (),
282    }
283}
284
285/// Traverse the AST and provide the methods to the given closure
286pub fn walk_methods<'a, F: FnMut(&'a ast::Method)>(ast: &'a ast::Aidl, mut f: F) {
287    match ast.item {
288        ast::Item::Interface(ref i) => {
289            i.elements.iter().for_each(|el| match el {
290                ast::InterfaceElement::Method(m) => f(m),
291                ast::InterfaceElement::Const(_) => (),
292            });
293        }
294        ast::Item::Parcelable(_) => (),
295        ast::Item::Enum(_) => (),
296    }
297}
298
299/// Traverse the AST and provide the method arguments to the given closure
300pub fn walk_args<'a, F: FnMut(&'a ast::Method, &'a ast::Arg)>(ast: &'a ast::Aidl, mut f: F) {
301    match ast.item {
302        ast::Item::Interface(ref i) => {
303            i.elements.iter().for_each(|el| match el {
304                ast::InterfaceElement::Method(m) => m.args.iter().for_each(|arg| {
305                    f(m, arg);
306                }),
307                ast::InterfaceElement::Const(_) => (),
308            });
309        }
310        ast::Item::Parcelable(_) => (),
311        ast::Item::Enum(_) => (),
312    }
313}