Skip to main content

gobject_linter/rules/
type_style.rs

1use std::sync::LazyLock;
2
3use gobject_ast::model::{
4    FileModel, Parameter, StructField, TopLevelItem, TypeDefItem, TypeInfo, TypedefTarget,
5};
6
7use crate::{
8    ast_context::AstContext,
9    config::Config,
10    rules::{ConfigOption, Fix, Rule, Violation},
11};
12
13pub struct TypeStyle;
14
15impl Rule for TypeStyle {
16    fn name(&self) -> &'static str {
17        "type_style"
18    }
19
20    fn description(&self) -> &'static str {
21        "Enforce consistent use of GLib or C standard type aliases"
22    }
23
24    fn category(&self) -> crate::rules::Category {
25        crate::rules::Category::Style
26    }
27
28    fn fixable(&self) -> bool {
29        true
30    }
31
32    fn config_options(&self) -> &'static [ConfigOption] {
33        static OPTIONS: LazyLock<Vec<ConfigOption>> = LazyLock::new(|| {
34            vec![ConfigOption {
35                name: "style",
36                option_type: "string",
37                default_value: "\"glib\"",
38                example_value: "\"c\"",
39                description: "Type style to enforce: \"glib\" (prefer gint, gchar, …) or \"c\" (prefer int, char, int32_t, …)",
40            }]
41        });
42
43        &OPTIONS
44    }
45
46    fn check_all(
47        &self,
48        ast_context: &AstContext,
49        config: &Config,
50        violations: &mut Vec<Violation>,
51    ) {
52        let style = config
53            .get_rule_config(self.name())
54            .and_then(|rc| rc.options.get("style"))
55            .and_then(|v| v.as_str())
56            .unwrap_or("glib");
57
58        for (_, file) in ast_context.iter_all_files() {
59            for item in file.iter_all_items() {
60                self.check_item(item, file, style, violations);
61            }
62        }
63    }
64}
65
66impl TypeStyle {
67    fn check_item(
68        &self,
69        item: &TopLevelItem,
70        file: &FileModel,
71        style: &str,
72        violations: &mut Vec<Violation>,
73    ) {
74        match item {
75            TopLevelItem::FunctionDeclaration(decl) => {
76                self.check_type(&decl.return_type, file, style, violations);
77                self.check_params(&decl.parameters, file, style, violations);
78            }
79            TopLevelItem::FunctionDefinition(def) => {
80                self.check_type(&def.return_type, file, style, violations);
81                self.check_params(&def.parameters, file, style, violations);
82                for var in def.iter_local_declarations() {
83                    self.check_type(&var.type_info, file, style, violations);
84                }
85            }
86            TopLevelItem::TypeDefinition(typedef_item) => {
87                self.check_typedef(typedef_item, file, style, violations);
88            }
89            TopLevelItem::Declaration(decl) => {
90                self.check_type(&decl.type_info, file, style, violations);
91            }
92            _ => {}
93        }
94    }
95
96    fn check_typedef(
97        &self,
98        item: &TypeDefItem,
99        file: &FileModel,
100        style: &str,
101        violations: &mut Vec<Violation>,
102    ) {
103        match item {
104            TypeDefItem::Typedef {
105                target,
106                struct_fields,
107                ..
108            } => {
109                match target {
110                    TypedefTarget::Type(type_info) => {
111                        self.check_type(type_info, file, style, violations);
112                    }
113                    TypedefTarget::Callback {
114                        return_type,
115                        parameters,
116                    } => {
117                        self.check_type(return_type, file, style, violations);
118                        self.check_params(parameters, file, style, violations);
119                    }
120                }
121                self.check_fields(struct_fields, file, style, violations);
122            }
123            TypeDefItem::Struct { fields, .. } => {
124                self.check_fields(fields, file, style, violations);
125            }
126            TypeDefItem::Enum(_) => {}
127        }
128    }
129
130    fn check_fields(
131        &self,
132        fields: &[StructField],
133        file: &FileModel,
134        style: &str,
135        violations: &mut Vec<Violation>,
136    ) {
137        for field in fields {
138            field.walk(&mut |f| {
139                self.check_type(&f.field_type, file, style, violations);
140            });
141        }
142    }
143
144    fn check_params(
145        &self,
146        parameters: &[Parameter],
147        file: &FileModel,
148        style: &str,
149        violations: &mut Vec<Violation>,
150    ) {
151        for param in parameters {
152            if let Parameter::Regular { type_info, .. } = param {
153                self.check_type(type_info, file, style, violations);
154            }
155        }
156    }
157
158    fn check_type(
159        &self,
160        type_info: &TypeInfo,
161        file: &FileModel,
162        style: &str,
163        violations: &mut Vec<Violation>,
164    ) {
165        let Some(basic) = type_info.as_basic() else {
166            return;
167        };
168
169        let canonical = if style == "c" {
170            basic.canonical_c(&type_info.base_type)
171        } else {
172            basic.canonical_glib()
173        };
174
175        let Some(canonical) = canonical else { return };
176
177        if type_info.base_type == canonical {
178            return;
179        }
180
181        let loc = &type_info.location;
182        let source_text = loc.as_str().unwrap_or("");
183        let new_text = source_text.replacen(&type_info.base_type, canonical, 1);
184        let fix = Fix::new(loc.start_byte, loc.end_byte, new_text);
185
186        violations.push(self.violation_with_fix_at(
187            &file.path,
188            &type_info.location,
189            format!("use `{}` instead of `{}`", canonical, type_info.base_type),
190            fix,
191        ));
192    }
193}