Skip to main content

gobject_linter/rules/
matching_declare_define.rs

1use std::collections::HashMap;
2
3use gobject_ast::model::GObjectTypeKind;
4
5use crate::{
6    ast_context::AstContext,
7    config::Config,
8    rules::{Rule, Violation},
9};
10
11pub struct MatchingDeclareDefine;
12
13impl Rule for MatchingDeclareDefine {
14    fn name(&self) -> &'static str {
15        "matching_declare_define"
16    }
17
18    fn description(&self) -> &'static str {
19        "Ensure G_DECLARE_* and G_DEFINE_* macros are used consistently"
20    }
21
22    fn category(&self) -> crate::rules::Category {
23        crate::rules::Category::Pedantic
24    }
25
26    fn min_glib_version(&self) -> Option<(u32, u32)> {
27        Some((2, 70))
28    }
29
30    fn check_all(
31        &self,
32        ast_context: &AstContext,
33        _config: &Config,
34        violations: &mut Vec<Violation>,
35    ) {
36        // Build a map of type_name -> GObjectTypeKind from all files
37        let mut declared_types: HashMap<&str, &GObjectTypeKind> = HashMap::new();
38
39        // Scan all files for G_DECLARE_* macros (can be in headers or C files)
40        for (_path, file) in ast_context.iter_all_files() {
41            for gt in file.iter_all_gobject_types() {
42                if gt.kind.is_declare() {
43                    declared_types.insert(&gt.type_name, &gt.kind);
44                }
45            }
46        }
47
48        // Scan C files for mismatched G_DEFINE_* macros
49        for (path, file) in ast_context.iter_c_files() {
50            for gt in file.iter_all_gobject_types() {
51                if gt.kind.is_define() {
52                    // Check if there's a matching declaration
53                    if let Some(declare_kind) = declared_types.get(gt.type_name.as_str())
54                        && !declare_kind.is_compatible_with(&gt.kind)
55                    {
56                        violations.push(self.violation(
57                            path,
58                            gt.location.line,
59                            1,
60                            format!(
61                                "'{}' is declared with {} but defined with {}",
62                                gt.type_name,
63                                declare_kind.macro_name(),
64                                gt.kind.macro_name()
65                            ),
66                        ));
67                    }
68                }
69            }
70        }
71    }
72}