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>,
8 #[serde(skip_serializing_if = "Vec::is_empty")]
9 pub arguments: Vec<Box<Expression>>,
10 pub location: SourceLocation,
11}
12
13impl CallExpression {
14 pub fn function_name(&self) -> &str {
18 self.function.location().as_str().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) -> bool {
31 self.function_name().contains(pattern)
32 }
33
34 pub fn function_ends_with(&self, pattern: &str) -> bool {
36 self.function_name().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> {
50 self.arguments.get(index).map(std::convert::AsRef::as_ref)
51 }
52
53 pub fn get_arg_text(&self, index: usize) -> Option<&str> {
55 self.arguments.get(index)?.location().as_str()
56 }
57
58 pub fn has_arg_matching<F>(&self, index: usize, predicate: F) -> bool
61 where
62 F: FnOnce(&Expression) -> bool,
63 {
64 self.get_arg(index).is_some_and(predicate)
65 }
66
67 pub fn arg_contains_variable(&self, index: usize, var_name: &str) -> bool {
71 self.has_arg_matching(index, |expr| {
72 expr.extract_variable_name()
73 .is_some_and(|name| name == var_name)
74 })
75 }
76
77 pub fn is_likely_macro(&self) -> bool {
80 let name = self.function_name();
81 name.chars().all(|c| c.is_uppercase() || c == '_') || name.ends_with('_')
82 }
83
84 pub fn extract_string_from_arg(&self, index: usize) -> Option<String> {
88 self.arguments.get(index)?.extract_string_value()
89 }
90
91 pub fn is_allocation_call(&self) -> bool {
94 if let Some(name) = self.function_name_str() {
95 matches!(
96 name,
97 "g_object_new"
98 | "g_object_new_with_properties"
99 | "g_type_create_instance"
100 | "g_new"
101 | "g_new0"
102 | "g_try_new"
103 | "g_try_new0"
104 | "g_malloc"
105 | "g_malloc0"
106 | "g_strdup"
107 | "g_strndup"
108 | "g_file_new_for_path"
109 | "g_file_new_for_uri"
110 | "g_file_new_tmp"
111 | "g_variant_new"
112 | "g_variant_ref_sink"
113 | "g_bytes_new"
114 | "g_bytes_new_take"
115 | "g_hash_table_new"
116 | "g_hash_table_new_full"
117 | "g_array_new"
118 | "g_ptr_array_new"
119 | "g_error_new"
120 | "g_error_new_literal"
121 ) || name.ends_with("_new")
122 || name.ends_with("_get_instance")
123 || name.contains("_new_")
124 || name.contains("_create")
125 } else {
126 false
127 }
128 }
129
130 pub fn is_cleanup_call(&self) -> bool {
133 if let Some(name) = self.function_name_str() {
134 matches!(
135 name,
136 "g_object_unref"
137 | "g_clear_object"
138 | "g_clear_pointer"
139 | "g_error_free"
140 | "g_clear_error"
141 | "g_free"
142 | "g_clear_handle_id"
143 | "g_clear_signal_handler"
144 | "g_list_free"
145 | "g_list_free_full"
146 | "g_slist_free"
147 | "g_slist_free_full"
148 | "g_hash_table_unref"
149 | "g_hash_table_destroy"
150 | "g_bytes_unref"
151 | "g_variant_unref"
152 | "g_array_unref"
153 | "g_array_free"
154 | "g_ptr_array_unref"
155 | "g_ptr_array_free"
156 ) || name.ends_with("_unref")
157 || name.ends_with("_free")
158 || name.ends_with("_destroy")
159 } else {
160 false
161 }
162 }
163}