1use gobject_ast::model::{CallExpression, FileModel, FunctionDefItem};
2
3use crate::{
4 ast_context::AstContext,
5 config::Config,
6 rules::{Fix, Rule, Violation},
7};
8
9pub struct UseExplicitDefaultFlags;
10
11const FLAG_REPLACEMENTS: &[(&str, usize, &str)] = &[
14 ("g_application_new", 1, "G_APPLICATION_DEFAULT_FLAGS"),
16 ("gtk_widget_class_add_binding", 2, "GDK_NO_MODIFIER_MASK"),
18 (
19 "gtk_widget_class_add_binding_signal",
20 2,
21 "GDK_NO_MODIFIER_MASK",
22 ),
23 (
24 "gtk_widget_class_add_binding_action",
25 2,
26 "GDK_NO_MODIFIER_MASK",
27 ),
28 ("gtk_shortcut_new", 1, "GDK_NO_MODIFIER_MASK"),
30 ("g_dbus_connection_new", 1, "G_DBUS_CONNECTION_FLAGS_NONE"),
32 (
33 "g_dbus_connection_new_for_address",
34 1,
35 "G_DBUS_CONNECTION_FLAGS_NONE",
36 ),
37 ("g_dbus_proxy_new", 2, "G_DBUS_PROXY_FLAGS_NONE"),
38 ("g_dbus_proxy_new_for_bus", 2, "G_DBUS_PROXY_FLAGS_NONE"),
39 ("g_file_query_info", 2, "G_FILE_QUERY_INFO_NONE"),
41 ("g_file_query_info_async", 2, "G_FILE_QUERY_INFO_NONE"),
42 ("g_file_enumerate_children", 1, "G_FILE_QUERY_INFO_NONE"),
43 (
44 "g_file_enumerate_children_async",
45 1,
46 "G_FILE_QUERY_INFO_NONE",
47 ),
48 ("g_subprocess_new", 0, "G_SUBPROCESS_FLAGS_NONE"),
50 ("g_subprocess_launcher_new", 0, "G_SUBPROCESS_FLAGS_NONE"),
51 (
53 "g_settings_new_with_backend_and_path",
54 3,
55 "G_SETTINGS_BIND_DEFAULT",
56 ),
57 ("gtk_application_new", 1, "G_APPLICATION_DEFAULT_FLAGS"),
59 ("adw_application_new", 1, "G_APPLICATION_DEFAULT_FLAGS"),
61 ("gtk_icon_theme_lookup_icon", 6, "GTK_ICON_LOOKUP_NONE"),
63 ("gtk_icon_theme_lookup_by_gicon", 5, "GTK_ICON_LOOKUP_NONE"),
64 ("gtk_drop_target_async_new", 1, "GDK_ACTION_NONE"),
66 ("gtk_drop_target_new", 1, "GDK_ACTION_NONE"),
67];
68
69impl Rule for UseExplicitDefaultFlags {
70 fn name(&self) -> &'static str {
71 "use_explicit_default_flags"
72 }
73
74 fn description(&self) -> &'static str {
75 "Use explicit default flag constants (e.g., G_APPLICATION_DEFAULT_FLAGS) instead of 0"
76 }
77
78 fn category(&self) -> crate::rules::Category {
79 crate::rules::Category::Style
80 }
81
82 fn fixable(&self) -> bool {
83 true
84 }
85
86 fn check_func_impl(
87 &self,
88 _ast_context: &AstContext,
89 _config: &Config,
90 func: &FunctionDefItem,
91 file: &FileModel,
92 violations: &mut Vec<Violation>,
93 ) {
94 let function_names: Vec<&str> = FLAG_REPLACEMENTS.iter().map(|(name, ..)| *name).collect();
96
97 for call in func.find_calls(&function_names) {
98 self.check_call(file, call, violations);
99 }
100 }
101}
102
103impl UseExplicitDefaultFlags {
104 fn check_call(&self, file: &FileModel, call: &CallExpression, violations: &mut Vec<Violation>) {
105 for &(target_func, arg_pos, replacement_const) in FLAG_REPLACEMENTS {
107 if call.is_function(target_func) {
108 if let Some(arg_expr) = call.get_arg(arg_pos)
109 && arg_expr.is_zero()
110 {
111 let fix = Fix::new(
112 arg_expr.location().start_byte,
113 arg_expr.location().end_byte,
114 replacement_const.to_string(),
115 );
116
117 violations.push(self.violation_with_fix_at(
118 &file.path,
119 &call.location,
120 format!(
121 "Use {} instead of 0 for {}() flags parameter",
122 replacement_const, target_func
123 ),
124 fix,
125 ));
126 }
127 break;
128 }
129 }
130 }
131}