Skip to main content

gobject_linter/rules/
use_g_string_free_and_steal.rs

1use gobject_ast::model::{CallExpression, FileModel, FunctionDefItem};
2
3use crate::{
4    ast_context::AstContext,
5    config::Config,
6    rules::{Fix, Rule, Violation},
7};
8
9pub struct UseGStringFreeAndSteal;
10
11impl Rule for UseGStringFreeAndSteal {
12    fn name(&self) -> &'static str {
13        "use_g_string_free_and_steal"
14    }
15
16    fn description(&self) -> &'static str {
17        "Suggest g_string_free_and_steal instead of g_string_free (..., FALSE) for better readability"
18    }
19
20    fn category(&self) -> crate::rules::Category {
21        crate::rules::Category::Style
22    }
23
24    fn fixable(&self) -> bool {
25        true
26    }
27
28    fn min_glib_version(&self) -> Option<(u32, u32)> {
29        Some((2, 76))
30    }
31
32    fn check_func_impl(
33        &self,
34        _ast_context: &AstContext,
35        config: &Config,
36        func: &FunctionDefItem,
37        file: &FileModel,
38        violations: &mut Vec<Violation>,
39    ) {
40        for call in func.find_calls(&["g_string_free"]) {
41            self.check_call(file, call, config, violations);
42        }
43    }
44}
45
46impl UseGStringFreeAndSteal {
47    fn check_call(
48        &self,
49        file: &FileModel,
50        call: &CallExpression,
51        config: &Config,
52        violations: &mut Vec<Violation>,
53    ) {
54        if call.arguments.len() != 2 {
55            return;
56        }
57
58        // Check if second argument is FALSE/false/0
59        let Some(second_expr) = call.get_arg(1) else {
60            return;
61        };
62
63        if !second_expr.is_falsy() {
64            return;
65        }
66
67        // Get argument text for the fix
68        let Some(first_text) = call.get_arg_text(0) else {
69            return;
70        };
71        let Some(second_text) = call.get_arg_text(1) else {
72            return;
73        };
74
75        // Build replacement
76        let replacement = config
77            .style
78            .format_call("g_string_free_and_steal", &[first_text]);
79        let message = format!(
80            "Use {} instead of g_string_free({}, {}) for readability",
81            replacement, first_text, second_text
82        );
83        let fix = Fix::new(
84            call.location.start_byte,
85            call.location.end_byte,
86            replacement,
87        );
88
89        violations.push(self.violation_with_fix_at(&file.path, &call.location, message, fix));
90    }
91}