gobject_linter/rules/
use_g_string_free_and_steal.rs1use 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 let Some(second_expr) = call.get_arg(1) else {
60 return;
61 };
62
63 if !second_expr.is_falsy() {
64 return;
65 }
66
67 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 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}