gobject_ast/model/expression/
call.rs1use serde::Serialize;
2
3use crate::model::{Expression, SourceLocation};
4
5#[derive(Debug, Clone, Serialize)]
6pub struct CallExpression {
7 pub function: Box<Expression>, #[serde(skip_serializing_if = "Vec::is_empty")]
9 pub arguments: Vec<Argument>,
10 pub location: SourceLocation,
11}
12
13impl CallExpression {
14 pub fn function_name<'a>(&self, source: &'a [u8]) -> &'a str {
18 self.function.location().as_str(source).unwrap_or("")
19 }
20
21 pub fn is_function(&self, name: &str) -> bool {
23 match &*self.function {
24 Expression::Identifier(id) => id.name == name,
25 _ => false,
26 }
27 }
28
29 pub fn function_contains(&self, pattern: &str, source: &[u8]) -> bool {
31 self.function_name(source).contains(pattern)
32 }
33
34 pub fn function_ends_with(&self, pattern: &str, source: &[u8]) -> bool {
36 self.function_name(source).ends_with(pattern)
37 }
38
39 pub fn function_name_str(&self) -> Option<&str> {
42 match &*self.function {
43 Expression::Identifier(id) => Some(&id.name),
44 _ => None,
45 }
46 }
47
48 pub fn get_arg(&self, index: usize) -> Option<&Expression> {
51 match self.arguments.get(index)? {
52 Argument::Expression(expr) => Some(expr.as_ref()),
53 }
54 }
55
56 pub fn get_arg_text<'a>(&self, index: usize, source: &'a [u8]) -> Option<&'a str> {
58 match self.arguments.get(index)? {
59 Argument::Expression(expr) => expr.to_source_string(source),
60 }
61 }
62
63 pub fn has_arg_matching<F>(&self, index: usize, predicate: F) -> bool
66 where
67 F: FnOnce(&Expression) -> bool,
68 {
69 self.get_arg(index).is_some_and(predicate)
70 }
71
72 pub fn arg_contains_variable(&self, index: usize, var_name: &str, source: &[u8]) -> bool {
76 self.has_arg_matching(index, |expr| {
77 expr.extract_variable_name(source)
78 .is_some_and(|name| name == var_name)
79 })
80 }
81
82 pub fn is_likely_macro(&self, source: &[u8]) -> bool {
85 let name = self.function_name(source);
86 name.chars().all(|c| c.is_uppercase() || c == '_') || name.ends_with('_')
87 }
88
89 pub fn extract_string_from_arg(&self, index: usize) -> Option<String> {
93 let Argument::Expression(expr) = self.arguments.get(index)?;
94 expr.extract_string_value()
95 }
96
97 pub fn is_allocation_call(&self) -> bool {
100 if let Some(name) = self.function_name_str() {
101 matches!(
102 name,
103 "g_object_new"
104 | "g_object_new_with_properties"
105 | "g_type_create_instance"
106 | "g_new"
107 | "g_new0"
108 | "g_try_new"
109 | "g_try_new0"
110 | "g_malloc"
111 | "g_malloc0"
112 | "g_strdup"
113 | "g_strndup"
114 | "g_file_new_for_path"
115 | "g_file_new_for_uri"
116 | "g_file_new_tmp"
117 | "g_variant_new"
118 | "g_variant_ref_sink"
119 | "g_bytes_new"
120 | "g_bytes_new_take"
121 | "g_hash_table_new"
122 | "g_hash_table_new_full"
123 | "g_array_new"
124 | "g_ptr_array_new"
125 | "g_error_new"
126 | "g_error_new_literal"
127 ) || name.ends_with("_new")
128 || name.ends_with("_get_instance")
129 || name.contains("_new_")
130 || name.contains("_create")
131 } else {
132 false
133 }
134 }
135
136 pub fn is_cleanup_call(&self) -> bool {
139 if let Some(name) = self.function_name_str() {
140 matches!(
141 name,
142 "g_object_unref"
143 | "g_clear_object"
144 | "g_clear_pointer"
145 | "g_error_free"
146 | "g_clear_error"
147 | "g_free"
148 | "g_clear_handle_id"
149 | "g_clear_signal_handler"
150 | "g_list_free"
151 | "g_list_free_full"
152 | "g_slist_free"
153 | "g_slist_free_full"
154 | "g_hash_table_unref"
155 | "g_hash_table_destroy"
156 | "g_bytes_unref"
157 | "g_variant_unref"
158 | "g_array_unref"
159 | "g_array_free"
160 | "g_ptr_array_unref"
161 | "g_ptr_array_free"
162 ) || name.ends_with("_unref")
163 || name.ends_with("_free")
164 || name.ends_with("_destroy")
165 } else {
166 false
167 }
168 }
169}
170
171#[derive(Debug, Clone, Serialize)]
172#[serde(untagged)]
173pub enum Argument {
174 Expression(Box<Expression>),
175}
176
177impl Argument {
178 pub fn to_source_string<'a>(&self, source: &'a [u8]) -> Option<&'a str> {
180 match self {
181 Self::Expression(expr) => expr.to_source_string(source),
182 }
183 }
184
185 pub fn is_string_or_macro_string(&self) -> bool {
187 let Self::Expression(expr) = self;
188 expr.is_string_or_macro_string()
189 }
190
191 pub fn is_null(&self) -> bool {
193 let Self::Expression(expr) = self;
194 expr.is_null()
195 }
196
197 pub fn extract_string_value(&self) -> Option<String> {
199 let Self::Expression(expr) = self;
200 expr.extract_string_value()
201 }
202}