Skip to main content

gobject_linter/rules/
missing_export_macro.rs

1use gobject_ast::model::{FileModel, FunctionDeclItem, GObjectType};
2
3use crate::{
4    ast_context::AstContext,
5    config::Config,
6    rules::{Category, Rule, Violation},
7};
8
9pub struct MissingExportMacro;
10
11impl Rule for MissingExportMacro {
12    fn name(&self) -> &'static str {
13        "missing_export_macro"
14    }
15
16    fn description(&self) -> &'static str {
17        "Detect public API functions and types without export macros"
18    }
19
20    fn long_description(&self) -> Option<&'static str> {
21        Some(
22            "Detects functions and types in public headers that lack export macros.\n\
23             Public API should be marked with appropriate export macros (e.g., G_MODULE_EXPORT, \
24             CLUTTER_EXPORT, META_EXPORT) to ensure proper symbol visibility.\n\
25             Public API should be marked with appropriate export macros (e.g., G_MODULE_EXPORT, \
26             CLUTTER_EXPORT, META_EXPORT) to ensure proper symbol visibility.",
27        )
28    }
29
30    fn category(&self) -> Category {
31        Category::Correctness
32    }
33
34    fn requires_meson(&self) -> bool {
35        true
36    }
37
38    fn opt_in(&self) -> bool {
39        true
40    }
41
42    fn opt_in_reason(&self) -> Option<&'static str> {
43        Some(
44            "May produce false positives as the parser can misidentify export macros in some codebases",
45        )
46    }
47
48    fn check_func_decl(
49        &self,
50        ast_context: &AstContext,
51        _config: &Config,
52        func: &FunctionDeclItem,
53        file: &FileModel,
54        violations: &mut Vec<Violation>,
55    ) {
56        if !ast_context.is_public_header(&file.path).unwrap_or(false) {
57            return;
58        }
59
60        if func.is_static {
61            return;
62        }
63
64        if func.export_macros.is_empty() {
65            violations.push(self.violation_at(
66                &file.path,
67                &func.location,
68                format!(
69                    "Public function '{}' in header is missing an export macro (e.g., G_MODULE_EXPORT, *_EXPORT)",
70                    func.name
71                ),
72            ));
73        }
74    }
75
76    fn check_gobject_type(
77        &self,
78        ast_context: &AstContext,
79        _config: &Config,
80        gobject_type: &GObjectType,
81        file: &FileModel,
82        violations: &mut Vec<Violation>,
83    ) {
84        if !ast_context.is_public_header(&file.path).unwrap_or(false) {
85            return;
86        }
87
88        if !gobject_type.kind.is_declare() {
89            return;
90        }
91
92        if gobject_type.export_macros.is_empty() {
93            violations.push(self.violation_at(
94                &file.path,
95                &gobject_type.location,
96                format!(
97                    "'{}' is missing an export macro (e.g., G_MODULE_EXPORT, *_EXPORT)",
98                    gobject_type.type_name
99                ),
100            ));
101        }
102    }
103}