Skip to main content

gobject_linter/rules/
use_g_new.rs

1use 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        // Need exactly 1 argument
51        if call.arguments.len() != 1 {
52            return;
53        }
54
55        // Check if argument is sizeof(Type)
56        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        // Extract the type - only works for simple types/identifiers
64        let Some(type_name) = sizeof_expr.type_name() else {
65            // Complex expression, not a simple type - skip
66            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}