Skip to main content

gobject_ast/model/expression/
mod.rs

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