Skip to main content

gobject_ast/model/statement/
mod.rs

1mod break_stmt;
2mod compound_stmt;
3mod continue_stmt;
4mod for_stmt;
5mod goto_stmt;
6mod if_stmt;
7mod labeled_stmt;
8mod return_stmt;
9mod switch_stmt;
10mod variable_decl;
11mod while_stmt;
12
13pub use break_stmt::BreakStatement;
14pub use compound_stmt::CompoundStatement;
15pub use continue_stmt::ContinueStatement;
16pub use for_stmt::{ForInit, ForStatement};
17pub use goto_stmt::GotoStatement;
18pub use if_stmt::IfStatement;
19pub use labeled_stmt::LabeledStatement;
20pub use return_stmt::ReturnStatement;
21use serde::Serialize;
22pub use switch_stmt::{CaseLabel, SwitchCase, SwitchStatement};
23pub use variable_decl::VariableDecl;
24pub use while_stmt::{DoWhileStatement, WhileStatement};
25
26use crate::model::{
27    Assignment, CallExpression, Comment, Expression, PreprocessorDirective, SourceLocation,
28};
29
30#[derive(Debug, Clone, Serialize)]
31#[serde(rename_all = "snake_case")]
32pub enum Statement {
33    Declaration(Box<VariableDecl>),
34    Expression(Box<Expression>),
35    If(IfStatement),
36    Return(ReturnStatement),
37    Goto(GotoStatement),
38    Labeled(LabeledStatement),
39    Compound(CompoundStatement),
40    Switch(SwitchStatement),
41    For(Box<ForStatement>),
42    While(WhileStatement),
43    DoWhile(DoWhileStatement),
44    Break(BreakStatement),
45    Continue(ContinueStatement),
46    Preprocessor(PreprocessorDirective),
47    Comment(Comment),
48}
49
50impl Statement {
51    /// Recursively visit all nested statements. The closure receives a
52    /// `&'s Statement` tied to `self`'s lifetime, so references extracted
53    /// inside the closure can be stored in an outer `Vec<&'s T>`.
54    pub fn walk<'s, F>(&'s self, f: &mut F)
55    where
56        F: FnMut(&'s Self),
57    {
58        f(self);
59        match self {
60            Self::If(if_stmt) => {
61                for stmt in &if_stmt.then_body {
62                    stmt.walk(f);
63                }
64                if let Some(else_body) = &if_stmt.else_body {
65                    for stmt in else_body {
66                        stmt.walk(f);
67                    }
68                }
69            }
70            Self::Compound(compound) => {
71                for stmt in &compound.statements {
72                    stmt.walk(f);
73                }
74            }
75            Self::Labeled(labeled) => {
76                labeled.statement.walk(f);
77            }
78            Self::Switch(switch) => {
79                for case in &switch.cases {
80                    for stmt in &case.body {
81                        stmt.walk(f);
82                    }
83                }
84            }
85            Self::For(for_stmt) => {
86                for stmt in &for_stmt.body {
87                    stmt.walk(f);
88                }
89            }
90            Self::While(while_stmt) => {
91                for stmt in &while_stmt.body {
92                    stmt.walk(f);
93                }
94            }
95            Self::DoWhile(do_while) => {
96                for stmt in &do_while.body {
97                    stmt.walk(f);
98                }
99            }
100            Self::Declaration(_)
101            | Self::Expression(_)
102            | Self::Return(_)
103            | Self::Goto(_)
104            | Self::Break(_)
105            | Self::Continue(_)
106            | Self::Preprocessor(_)
107            | Self::Comment(_) => {}
108        }
109    }
110
111    /// Call `f` with each immediate child block (a `&[Statement]` slice) of
112    /// this statement. Covers all container variants exhaustively; leaf
113    /// variants produce no calls. Use this instead of a manual `match` when
114    /// you need to apply a slice-level operation (e.g. pair detection) to
115    /// every nested body.
116    pub fn for_each_child_block<F>(&self, mut f: F)
117    where
118        F: FnMut(&[Self]),
119    {
120        match self {
121            Self::If(if_stmt) => {
122                f(&if_stmt.then_body);
123                if let Some(else_body) = &if_stmt.else_body {
124                    f(else_body);
125                }
126            }
127            Self::Compound(c) => f(&c.statements),
128            Self::Labeled(l) => f(std::slice::from_ref(&l.statement)),
129            Self::For(for_stmt) => f(&for_stmt.body),
130            Self::While(w) => f(&w.body),
131            Self::DoWhile(d) => f(&d.body),
132            Self::Switch(sw) => {
133                for case in &sw.cases {
134                    f(&case.body);
135                }
136            }
137            Self::Declaration(_)
138            | Self::Expression(_)
139            | Self::Return(_)
140            | Self::Goto(_)
141            | Self::Break(_)
142            | Self::Continue(_)
143            | Self::Preprocessor(_)
144            | Self::Comment(_) => {}
145        }
146    }
147
148    /// Visit all direct expressions contained in this statement
149    /// (non-recursive). Includes conditions, initialisers, and all other
150    /// expressions that are immediate children of this statement node.
151    /// Zero-allocation alternative to collecting into a Vec.
152    pub fn visit_expressions<'s>(&'s self, f: &mut impl FnMut(&'s Expression)) {
153        match self {
154            Self::Expression(expr_stmt) => f(expr_stmt),
155            Self::Return(ret) => {
156                if let Some(v) = &ret.value {
157                    f(v);
158                }
159            }
160            Self::Declaration(decl) => {
161                if let Some(init) = &decl.initializer {
162                    f(init);
163                }
164                if let Some(size) = &decl.array_size {
165                    f(size);
166                }
167            }
168            Self::If(if_stmt) => f(&if_stmt.condition),
169            Self::Switch(switch) => {
170                f(&switch.condition);
171                for case in &switch.cases {
172                    if let Some(label_expr) = &case.label.value {
173                        f(label_expr);
174                    }
175                }
176            }
177            Self::For(for_stmt) => {
178                match &for_stmt.initializer {
179                    Some(ForInit::Expr(init)) => f(init),
180                    Some(ForInit::Decl(decl)) => {
181                        if let Some(init) = &decl.initializer {
182                            f(init);
183                        }
184                    }
185                    None => {}
186                }
187                if let Some(cond) = &for_stmt.condition {
188                    f(cond);
189                }
190                if let Some(update) = &for_stmt.update {
191                    f(update);
192                }
193            }
194            Self::While(while_stmt) => f(&while_stmt.condition),
195            Self::DoWhile(do_while) => f(&do_while.condition),
196            Self::Goto(_)
197            | Self::Labeled(_)
198            | Self::Compound(_)
199            | Self::Break(_)
200            | Self::Continue(_)
201            | Self::Preprocessor(_)
202            | Self::Comment(_) => {}
203        }
204    }
205
206    pub fn location(&self) -> &SourceLocation {
207        match self {
208            Self::Declaration(d) => &d.location,
209            Self::Expression(e) => e.location(),
210            Self::If(i) => &i.location,
211            Self::Return(r) => &r.location,
212            Self::Goto(g) => &g.location,
213            Self::Labeled(l) => &l.location,
214            Self::Compound(c) => &c.location,
215            Self::Switch(s) => &s.location,
216            Self::For(f) => &f.location,
217            Self::While(w) => &w.location,
218            Self::DoWhile(d) => &d.location,
219            Self::Break(b) => &b.location,
220            Self::Continue(c) => &c.location,
221            Self::Preprocessor(p) => p.location(),
222            Self::Comment(c) => &c.location,
223        }
224    }
225
226    /// Recursively walk all expressions in this statement tree.
227    /// Visits the direct expression of each statement (not sub-expressions —
228    /// call `Expression::walk` on the result if you need nested expressions).
229    pub fn walk_expressions<'s, F>(&'s self, f: &mut F)
230    where
231        F: FnMut(&'s Expression),
232    {
233        self.walk(&mut |s| {
234            s.visit_expressions(f);
235        });
236    }
237
238    /// Iterator over all switch statements in this statement tree (recursive)
239    pub fn iter_switches<'s>(&'s self) -> impl Iterator<Item = &'s SwitchStatement> + 's {
240        let mut results: Vec<&'s SwitchStatement> = Vec::new();
241        self.walk(&mut |s| {
242            if let Self::Switch(sw) = s {
243                results.push(sw);
244            }
245        });
246        results.into_iter()
247    }
248
249    /// Iterator over all if statements in this statement tree (recursive)
250    pub fn iter_if_statements<'s>(&'s self) -> impl Iterator<Item = &'s IfStatement> + 's {
251        let mut results: Vec<&'s IfStatement> = Vec::new();
252        self.walk(&mut |s| {
253            if let Self::If(if_stmt) = s {
254                results.push(if_stmt);
255            }
256        });
257        results.into_iter()
258    }
259
260    /// Iterator over all variable declarations in this statement tree
261    /// (recursive)
262    pub fn iter_declarations<'s>(&'s self) -> impl Iterator<Item = &'s VariableDecl> + 's {
263        let mut results: Vec<&'s VariableDecl> = Vec::new();
264        self.walk(&mut |s| {
265            if let Self::Declaration(decl) = s {
266                results.push(decl);
267            }
268            if let Self::For(for_stmt) = s
269                && let Some(ForInit::Decl(decl)) = &for_stmt.initializer
270            {
271                results.push(decl);
272            }
273        });
274        results.into_iter()
275    }
276
277    /// Iterator over all return statements in this statement tree (recursive)
278    pub fn iter_returns<'s>(&'s self) -> impl Iterator<Item = &'s ReturnStatement> + 's {
279        let mut results: Vec<&'s ReturnStatement> = Vec::new();
280        self.walk(&mut |s| {
281            if let Self::Return(ret) = s {
282                results.push(ret);
283            }
284        });
285        results.into_iter()
286    }
287
288    /// Iterator over all top-level assignment statements in this statement tree
289    /// (recursive). Only yields assignments that are the entire expression
290    /// statement, not assignments nested inside other expressions.
291    pub fn iter_assignments<'s>(&'s self) -> impl Iterator<Item = &'s Assignment> + 's {
292        let mut results: Vec<&'s Assignment> = Vec::new();
293        self.walk(&mut |s| {
294            if let Self::Expression(expr_stmt) = s
295                && let Expression::Assignment(assign) = expr_stmt.as_ref()
296            {
297                results.push(assign);
298            }
299        });
300        results.into_iter()
301    }
302
303    /// Iterator over all call expressions in this statement tree (recursive).
304    /// Includes calls at all nesting levels within expressions (e.g. nested
305    /// arguments).
306    pub fn iter_calls<'s>(&'s self) -> impl Iterator<Item = &'s CallExpression> + 's {
307        // Two-step: first collect top-level expressions (avoids nested-closure
308        // invariance issue with &mut Vec), then walk each for nested calls.
309        let mut exprs: Vec<&'s Expression> = Vec::new();
310        self.walk_expressions(&mut |expr| exprs.push(expr));
311
312        let mut results: Vec<&'s CallExpression> = Vec::new();
313        for expr in exprs {
314            expr.walk(&mut |e| {
315                if let Expression::Call(call) = e {
316                    results.push(call);
317                }
318            });
319        }
320        results.into_iter()
321    }
322
323    /// Extract the call expression if this is an expression statement with a
324    /// call
325    pub fn extract_call(&self) -> Option<&CallExpression> {
326        if let Self::Expression(expr_stmt) = self
327            && let Expression::Call(call) = expr_stmt.as_ref()
328        {
329            return Some(call);
330        }
331        None
332    }
333
334    /// Check if this statement assigns a value matching the predicate to the
335    /// target variable
336    pub fn is_assignment_to<F>(&self, target_var: &str, value_check: F) -> bool
337    where
338        F: Fn(&Expression) -> bool,
339    {
340        if let Self::Expression(expr_stmt) = self
341            && let Expression::Assignment(assign) = expr_stmt.as_ref()
342        {
343            let lhs_text = assign.lhs.location().as_str().unwrap_or("");
344            return lhs_text.trim() == target_var.trim() && value_check(&assign.rhs);
345        }
346        false
347    }
348
349    /// Extract the assignment expression if this is an assignment statement
350    pub fn extract_assignment(&self) -> Option<&Assignment> {
351        if let Self::Expression(expr_stmt) = self
352            && let Expression::Assignment(assign) = expr_stmt.as_ref()
353        {
354            return Some(assign);
355        }
356        None
357    }
358
359    /// Check if this statement assigns NULL to the target variable
360    pub fn is_null_assignment_to(&self, var_name: &str) -> bool {
361        self.is_assignment_to(var_name, Expression::is_null)
362    }
363
364    fn non_comments(stmts: &[Self]) -> Vec<&Self> {
365        stmts
366            .iter()
367            .filter(|s| !matches!(s, Self::Comment(_)))
368            .collect()
369    }
370
371    /// Iterate over consecutive pairs of statements (skipping comments)
372    pub fn for_each_pair<F>(statements: &[Self], mut f: F)
373    where
374        F: FnMut(&Self, &Self),
375    {
376        let nc = Self::non_comments(statements);
377        for w in nc.windows(2) {
378            f(w[0], w[1]);
379        }
380    }
381
382    /// Iterate over consecutive triples of statements (skipping comments)
383    pub fn for_each_triple<F>(statements: &[Self], mut f: F)
384    where
385        F: FnMut(&Self, &Self, &Self),
386    {
387        let nc = Self::non_comments(statements);
388        for w in nc.windows(3) {
389            f(w[0], w[1], w[2]);
390        }
391    }
392
393    /// Recursively visit consecutive pairs of statements at every nesting level
394    /// (if bodies, else bodies, compound blocks, loops, switch cases, labeled
395    /// statements). Comments are skipped so they don't break pair detection.
396    pub fn walk_pairs<F>(stmts: &[Self], f: &mut F)
397    where
398        F: FnMut(&Self, &Self),
399    {
400        let nc = Self::non_comments(stmts);
401        for w in nc.windows(2) {
402            f(w[0], w[1]);
403        }
404        for stmt in stmts {
405            match stmt {
406                Self::If(if_stmt) => {
407                    Self::walk_pairs(&if_stmt.then_body, f);
408                    if let Some(else_body) = &if_stmt.else_body {
409                        Self::walk_pairs(else_body, f);
410                    }
411                }
412                Self::Compound(c) => Self::walk_pairs(&c.statements, f),
413                Self::Labeled(l) => Self::walk_pairs(std::slice::from_ref(&l.statement), f),
414                Self::For(for_stmt) => Self::walk_pairs(&for_stmt.body, f),
415                Self::While(w) => Self::walk_pairs(&w.body, f),
416                Self::DoWhile(d) => Self::walk_pairs(&d.body, f),
417                Self::Switch(sw) => {
418                    for case in &sw.cases {
419                        Self::walk_pairs(&case.body, f);
420                    }
421                }
422                Self::Declaration(_)
423                | Self::Expression(_)
424                | Self::Return(_)
425                | Self::Goto(_)
426                | Self::Break(_)
427                | Self::Continue(_)
428                | Self::Preprocessor(_)
429                | Self::Comment(_) => {}
430            }
431        }
432    }
433
434    /// Recursively visit consecutive triples of statements at every nesting
435    /// level. Comments are skipped so they don't break triple detection.
436    pub fn walk_triples<F>(stmts: &[Self], f: &mut F)
437    where
438        F: FnMut(&Self, &Self, &Self),
439    {
440        let nc = Self::non_comments(stmts);
441        for w in nc.windows(3) {
442            f(w[0], w[1], w[2]);
443        }
444        for stmt in stmts {
445            match stmt {
446                Self::If(if_stmt) => {
447                    Self::walk_triples(&if_stmt.then_body, f);
448                    if let Some(else_body) = &if_stmt.else_body {
449                        Self::walk_triples(else_body, f);
450                    }
451                }
452                Self::Compound(c) => Self::walk_triples(&c.statements, f),
453                Self::Labeled(l) => Self::walk_triples(std::slice::from_ref(&l.statement), f),
454                Self::For(for_stmt) => Self::walk_triples(&for_stmt.body, f),
455                Self::While(w) => Self::walk_triples(&w.body, f),
456                Self::DoWhile(d) => Self::walk_triples(&d.body, f),
457                Self::Switch(sw) => {
458                    for case in &sw.cases {
459                        Self::walk_triples(&case.body, f);
460                    }
461                }
462                Self::Declaration(_)
463                | Self::Expression(_)
464                | Self::Return(_)
465                | Self::Goto(_)
466                | Self::Break(_)
467                | Self::Continue(_)
468                | Self::Preprocessor(_)
469                | Self::Comment(_) => {}
470            }
471        }
472    }
473}