Skip to main content

gobject_linter/rules/
use_g_gnuc_flag_enum.rs

1use gobject_ast::model::{EnumInfo, FileModel};
2
3use crate::{
4    ast_context::AstContext,
5    config::Config,
6    rules::{Fix, Rule, Violation},
7};
8
9pub struct UseGGnucFlagEnum;
10
11impl Rule for UseGGnucFlagEnum {
12    fn name(&self) -> &'static str {
13        "use_g_gnuc_flag_enum"
14    }
15
16    fn description(&self) -> &'static str {
17        "Use G_GNUC_FLAG_ENUM for enums that represent bit flags"
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, 87))
30    }
31
32    fn check_enum(
33        &self,
34        _ast_context: &AstContext,
35        _config: &Config,
36        enum_info: &EnumInfo,
37        file: &FileModel,
38        violations: &mut Vec<Violation>,
39    ) {
40        let Some(ref enum_name) = enum_info.name else {
41            return;
42        };
43
44        if !enum_info.is_flags_enum() {
45            return;
46        }
47
48        if enum_info.has_attribute("G_GNUC_FLAG_ENUM") {
49            return;
50        }
51
52        let fix = self.generate_fix(enum_info, enum_name);
53
54        violations.push(self.violation_with_fix_at(
55            &file.path,
56            &enum_info.location,
57            format!(
58                "Enum '{}' appears to be a flags enum but is missing G_GNUC_FLAG_ENUM attribute",
59                enum_name
60            ),
61            fix,
62        ));
63    }
64}
65
66impl UseGGnucFlagEnum {
67    fn generate_fix(&self, enum_info: &EnumInfo, enum_name: &str) -> Fix {
68        let typedef_text = enum_info.location.as_str().unwrap_or("");
69
70        if let Some(closing_brace_pos) = typedef_text.rfind('}') {
71            let after_brace = &typedef_text[closing_brace_pos + 1..];
72
73            if let Some(name_offset) = after_brace.find(enum_name) {
74                let insert_pos =
75                    enum_info.location.start_byte + closing_brace_pos + 1 + name_offset;
76
77                return Fix::new(insert_pos, insert_pos, "G_GNUC_FLAG_ENUM ".to_string());
78            }
79        }
80
81        Fix::new(
82            enum_info.location.end_byte - 1,
83            enum_info.location.end_byte - 1,
84            " G_GNUC_FLAG_ENUM".to_string(),
85        )
86    }
87}