gobject_linter/rules/
g_error_init.rs1use gobject_ast::model::{FileModel, FunctionDefItem, Statement, VariableDecl};
2
3use crate::{
4 ast_context::AstContext,
5 config::Config,
6 rules::{Fix, Rule, Violation},
7};
8
9pub struct GErrorInit;
10
11impl Rule for GErrorInit {
12 fn name(&self) -> &'static str {
13 "g_error_init"
14 }
15
16 fn description(&self) -> &'static str {
17 "Ensure GError* variables are initialized to NULL"
18 }
19
20 fn category(&self) -> crate::rules::Category {
21 crate::rules::Category::Correctness
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 self.check_block(&func.body_statements, file, violations);
37 }
38}
39
40impl GErrorInit {
41 fn check_block(&self, stmts: &[Statement], file: &FileModel, violations: &mut Vec<Violation>) {
42 for (i, stmt) in stmts.iter().enumerate() {
43 if let Statement::Declaration(decl) = stmt {
44 self.check_declaration(decl, &stmts[i + 1..], file, violations);
45 }
46 stmt.for_each_child_block(|block| {
47 self.check_block(block, file, violations);
48 });
49 }
50 }
51
52 fn check_declaration(
53 &self,
54 decl: &VariableDecl,
55 following: &[Statement],
56 file: &FileModel,
57 violations: &mut Vec<Violation>,
58 ) {
59 if !decl.type_info.is_base_type("GError") || !decl.type_info.is_pointer() {
60 return;
61 }
62
63 let is_initialized_to_null = match &decl.initializer {
64 None => false,
65 Some(expr) if expr.is_null() || expr.is_zero() => true,
66 Some(_) => return,
67 };
68
69 if is_initialized_to_null {
70 return;
71 }
72
73 if self.first_use_is_assignment(&decl.name, following) {
74 return;
75 }
76
77 let insert_pos = decl.location.end_byte - 1;
78
79 let fix = Fix::new(insert_pos, insert_pos, " = NULL".to_string());
80
81 violations.push(self.violation_with_fix_at(
82 &file.path,
83 &decl.location,
84 format!("GError *{} must be initialized to NULL", decl.name),
85 fix,
86 ));
87 }
88
89 fn first_use_is_assignment(&self, var: &str, stmts: &[Statement]) -> bool {
90 for stmt in stmts {
91 let mut references_var = false;
92 stmt.visit_expressions(&mut |expr| {
93 if expr.contains_identifier(var) {
94 references_var = true;
95 }
96 });
97 if !references_var {
98 continue;
99 }
100 return stmt.is_assignment_to(var, |_| true);
101 }
102 true
103 }
104}