gobject_linter/rules/
use_g_new.rs1use gobject_ast::model::{CallExpression, Expression, FileModel, FunctionDefItem};
2
3use crate::{
4 ast_context::AstContext,
5 config::Config,
6 rules::{Fix, Rule, Violation},
7};
8
9pub struct UseGNew;
10
11impl Rule for UseGNew {
12 fn name(&self) -> &'static str {
13 "use_g_new"
14 }
15
16 fn description(&self) -> &'static str {
17 "Suggest g_new/g_new0 instead of g_malloc/g_malloc0 with sizeof for type safety"
18 }
19
20 fn category(&self) -> crate::rules::Category {
21 crate::rules::Category::Complexity
22 }
23
24 fn fixable(&self) -> bool {
25 true
26 }
27
28 fn check_func_impl(
29 &self,
30 _ast_context: &AstContext,
31 config: &Config,
32 func: &FunctionDefItem,
33 file: &FileModel,
34 violations: &mut Vec<Violation>,
35 ) {
36 for call in func.find_calls(&["g_malloc", "g_malloc0"]) {
37 self.check_call(file, call, config, violations);
38 }
39 }
40}
41
42impl UseGNew {
43 fn check_call(
44 &self,
45 file: &FileModel,
46 call: &CallExpression,
47 config: &Config,
48 violations: &mut Vec<Violation>,
49 ) {
50 if call.arguments.len() != 1 {
52 return;
53 }
54
55 let Some(arg_expr) = call.get_arg(0) else {
57 return;
58 };
59 let Expression::Sizeof(sizeof_expr) = arg_expr else {
60 return;
61 };
62
63 let Some(type_name) = sizeof_expr.type_name() else {
65 return;
67 };
68
69 let func_name = call.function_name();
70 let suggested_func = if call.is_function("g_malloc0") {
71 "g_new0"
72 } else {
73 "g_new"
74 };
75
76 let replacement = config.style.format_call(suggested_func, &[type_name, "1"]);
77 let message = format!(
78 "Use {} instead of {}(sizeof({})) for type safety",
79 replacement, func_name, type_name
80 );
81 let fix = Fix::new(
82 call.location.start_byte,
83 call.location.end_byte,
84 replacement,
85 );
86
87 violations.push(self.violation_with_fix_at(&file.path, &call.location, message, fix));
88 }
89}