gobject_linter/rules/
use_g_gnuc_flag_enum.rs1use 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}