Skip to main content

gobject_ast/model/expression/
mod.rs

1mod alloc_call;
2mod assignment;
3mod binary;
4mod call;
5mod cast;
6mod conditional;
7mod field_access;
8mod identifier;
9mod initializer_list;
10mod literal;
11mod offsetof;
12mod sizeof;
13mod subscript;
14mod unary;
15mod update;
16
17use std::collections::HashMap;
18
19pub use alloc_call::AllocCallExpression;
20pub use assignment::Assignment;
21pub use binary::BinaryExpression;
22pub use call::CallExpression;
23pub use cast::CastExpression;
24pub use conditional::ConditionalExpression;
25pub use field_access::FieldAccessExpression;
26pub use identifier::IdentifierExpression;
27pub use initializer_list::{Designator, InitializerItem, InitializerListExpression};
28pub use literal::{
29    BooleanExpression, CharLiteralExpression, CommentExpression, GenericExpression, NullExpression,
30    NumberLiteralExpression, StringLiteralExpression,
31};
32pub use offsetof::{OffsetField, OffsetOfExpression};
33use serde::Serialize;
34pub use sizeof::{SizeofExpression, SizeofOperand};
35pub use subscript::SubscriptExpression;
36pub use unary::UnaryExpression;
37pub use update::UpdateExpression;
38
39use crate::model::{DefineValue, SourceLocation};
40
41#[derive(Debug, Clone, Serialize)]
42#[serde(rename_all = "snake_case")]
43pub enum Expression {
44    Call(CallExpression),
45    AllocCall(AllocCallExpression),
46    Assignment(Assignment),
47    Binary(BinaryExpression),
48    Unary(UnaryExpression),
49    Identifier(IdentifierExpression),
50    FieldAccess(FieldAccessExpression),
51    StringLiteral(StringLiteralExpression),
52    NumberLiteral(NumberLiteralExpression),
53    Null(NullExpression),
54    Boolean(BooleanExpression),
55    Cast(CastExpression),
56    Conditional(ConditionalExpression),
57    Sizeof(SizeofExpression),
58    Subscript(SubscriptExpression),
59    InitializerList(InitializerListExpression),
60    CharLiteral(CharLiteralExpression),
61    Update(UpdateExpression),
62    Comment(CommentExpression),
63    OffsetOf(OffsetOfExpression),
64    Generic(GenericExpression),
65}
66
67impl Expression {
68    pub fn location(&self) -> &SourceLocation {
69        match self {
70            Self::Call(c) => &c.location,
71            Self::AllocCall(a) => &a.location,
72            Self::Assignment(a) => &a.location,
73            Self::Binary(b) => &b.location,
74            Self::Unary(u) => &u.location,
75            Self::Identifier(i) => &i.location,
76            Self::FieldAccess(f) => &f.location,
77            Self::StringLiteral(s) => &s.location,
78            Self::NumberLiteral(n) => &n.location,
79            Self::Null(n) => &n.location,
80            Self::Boolean(b) => &b.location,
81            Self::Cast(c) => &c.location,
82            Self::Conditional(c) => &c.location,
83            Self::Sizeof(s) => &s.location,
84            Self::Subscript(s) => &s.location,
85            Self::InitializerList(i) => &i.location,
86            Self::CharLiteral(c) => &c.location,
87            Self::Update(u) => &u.location,
88            Self::Comment(c) => &c.location,
89            Self::OffsetOf(o) => &o.location,
90            Self::Generic(g) => &g.location,
91        }
92    }
93
94    pub fn is_falsy(&self) -> bool {
95        match self {
96            Self::Boolean(b) => !b.value,
97            Self::NumberLiteral(n) => n.value == "0",
98            Self::Identifier(id) => id.name == "FALSE",
99            _ => false,
100        }
101    }
102
103    pub fn is_truthy(&self) -> bool {
104        match self {
105            Self::Boolean(b) => b.value,
106            Self::NumberLiteral(n) => n.value == "1",
107            Self::Identifier(id) => id.name == "TRUE",
108            _ => false,
109        }
110    }
111
112    /// Recursively walk all nested expressions. The closure receives a
113    /// `&'s Expression` tied to `self`'s lifetime, so references extracted
114    /// inside the closure can be stored in an outer `Vec<&'s T>`.
115    pub fn walk<'s, F>(&'s self, f: &mut F)
116    where
117        F: FnMut(&'s Self),
118    {
119        f(self);
120        match self {
121            Self::Call(call) => {
122                call.function.walk(f);
123                for arg in &call.arguments {
124                    arg.walk(f);
125                }
126            }
127            Self::AllocCall(alloc) => {
128                alloc.function.walk(f);
129                for arg in &alloc.arguments {
130                    arg.walk(f);
131                }
132            }
133            Self::Assignment(assign) => {
134                assign.lhs.walk(f);
135                assign.rhs.walk(f);
136            }
137            Self::Unary(unary) => {
138                unary.operand.walk(f);
139            }
140            Self::Binary(binary) => {
141                binary.left.walk(f);
142                binary.right.walk(f);
143            }
144            Self::Cast(cast) => {
145                cast.operand.walk(f);
146            }
147            Self::Conditional(cond) => {
148                cond.condition.walk(f);
149                cond.then_expr.walk(f);
150                cond.else_expr.walk(f);
151            }
152            Self::Subscript(subscript) => {
153                subscript.array.walk(f);
154                subscript.index.walk(f);
155            }
156            Self::Update(update) => {
157                update.operand.walk(f);
158            }
159            Self::FieldAccess(field) => {
160                field.base.walk(f);
161            }
162            Self::InitializerList(init) => {
163                for item in &init.items {
164                    if let Some(Designator::Subscript(idx)) = &item.designator {
165                        idx.walk(f);
166                    }
167                    item.value.walk(f);
168                }
169            }
170            Self::Identifier(_)
171            | Self::StringLiteral(_)
172            | Self::NumberLiteral(_)
173            | Self::Null(_)
174            | Self::Boolean(_)
175            | Self::Sizeof(_)
176            | Self::CharLiteral(_)
177            | Self::Comment(_)
178            | Self::OffsetOf(_)
179            | Self::Generic(_) => {}
180        }
181    }
182
183    /// Extract variable name from simple expressions (Identifier or
184    /// FieldAccess)
185    pub fn extract_variable_name(&self) -> Option<&str> {
186        match self {
187            Self::Identifier(_) | Self::FieldAccess(_) => self.location().as_str(),
188            _ => None,
189        }
190    }
191
192    /// Extract the identifier name, unwrapping macro calls and casts.
193    /// `G_OBJECT(self)` → `"self"`, `(GSourceFunc) callback` → `"callback"`
194    pub fn extract_identifier_name(&self) -> Option<&str> {
195        match self {
196            Self::Identifier(id) => Some(&id.name),
197            Self::Call(call) => call.get_arg(0)?.extract_identifier_name(),
198            Self::Cast(cast) => cast.operand.extract_identifier_name(),
199            _ => None,
200        }
201    }
202
203    /// Check if this expression is NULL
204    /// Handles both Expression::Null and the identifier "NULL" (common in C
205    /// code)
206    pub fn is_null(&self) -> bool {
207        matches!(self, Self::Null(_)) || matches!(self, Self::Identifier(id) if id.name == "NULL")
208    }
209
210    /// Check if this expression is the number 0
211    pub fn is_zero(&self) -> bool {
212        matches!(self, Self::NumberLiteral(n) if n.value.trim() == "0")
213    }
214
215    /// Check if this expression is a string literal
216    pub fn is_string_literal(&self) -> bool {
217        matches!(self, Self::StringLiteral(_))
218    }
219
220    /// Extract string literal value, unwrapping macro calls like I_("string")
221    /// Returns the string without quotes
222    pub fn extract_string_value(&self) -> Option<String> {
223        match self {
224            Self::StringLiteral(lit) => Some(lit.value.trim_matches('"').to_string()),
225            Self::Call(call) => call.get_arg(0)?.extract_string_value(),
226            _ => None,
227        }
228    }
229
230    /// Like `extract_string_value`, but also resolves identifiers through a
231    /// define map (e.g. `MY_PROP_NAME` → the string from `#define MY_PROP_NAME
232    /// "foo"`).
233    pub fn resolve_string_value(&self, defines: &HashMap<String, DefineValue>) -> Option<String> {
234        if let Some(s) = self.extract_string_value() {
235            return Some(s);
236        }
237        match self {
238            Self::Identifier(id) => match defines.get(&id.name)? {
239                DefineValue::StringLiteral(s) => Some(s.clone()),
240                _ => None,
241            },
242            Self::Call(call) => {
243                let arg = call.get_arg(0)?;
244                if let Self::Identifier(id) = arg {
245                    match defines.get(&id.name)? {
246                        DefineValue::StringLiteral(s) => Some(s.clone()),
247                        _ => None,
248                    }
249                } else {
250                    None
251                }
252            }
253            _ => None,
254        }
255    }
256
257    /// Check if this is a string literal or a macro wrapping a string literal
258    pub fn is_string_or_macro_string(&self) -> bool {
259        self.extract_string_value().is_some()
260    }
261
262    /// Check if this expression contains an identifier with the given name
263    /// Recursively searches through the entire expression tree
264    pub fn contains_identifier(&self, name: &str) -> bool {
265        let mut found = false;
266        self.walk(&mut |e| {
267            if let Self::Identifier(id) = e
268                && id.name == name
269            {
270                found = true;
271            }
272        });
273        found
274    }
275
276    /// Collect all identifiers in this expression
277    /// Returns a list of all identifier names found in the expression tree
278    pub fn collect_identifiers(&self) -> Vec<String> {
279        let mut identifiers = Vec::new();
280        self.walk(&mut |e| {
281            if let Self::Identifier(id) = e {
282                identifiers.push(id.name.clone());
283            }
284        });
285        identifiers
286    }
287
288    /// Check if this expression is a call to the specified function
289    pub fn is_call_to(&self, function_name: &str) -> bool {
290        matches!(self, Self::Call(call) if call.is_function(function_name))
291    }
292
293    /// Check if this expression is a call to any of the specified functions
294    pub fn is_call_to_any(&self, function_names: &[&str]) -> bool {
295        matches!(self, Self::Call(call) if call.function_name_str().is_some_and(|name| function_names.contains(&name)))
296    }
297}