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