Skip to main content

gobject_linter/rules/
use_explicit_default_flags.rs

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
11/// Mapping of (function_name, arg_position, replacement_constant)
12/// arg_position is 0-indexed
13const FLAG_REPLACEMENTS: &[(&str, usize, &str)] = &[
14    // GApplication
15    ("g_application_new", 1, "G_APPLICATION_DEFAULT_FLAGS"),
16    // GTK bindings
17    ("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    // GtkShortcut
29    ("gtk_shortcut_new", 1, "GDK_NO_MODIFIER_MASK"),
30    // GDBus
31    ("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    // GFile
40    ("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    // GSubprocess
49    ("g_subprocess_new", 0, "G_SUBPROCESS_FLAGS_NONE"),
50    ("g_subprocess_launcher_new", 0, "G_SUBPROCESS_FLAGS_NONE"),
51    // GSettings
52    (
53        "g_settings_new_with_backend_and_path",
54        3,
55        "G_SETTINGS_BIND_DEFAULT",
56    ),
57    // GtkApplication
58    ("gtk_application_new", 1, "G_APPLICATION_DEFAULT_FLAGS"),
59    // AdwApplication (libadwaita)
60    ("adw_application_new", 1, "G_APPLICATION_DEFAULT_FLAGS"),
61    // GtkIconTheme (GTK 4.18+)
62    ("gtk_icon_theme_lookup_icon", 6, "GTK_ICON_LOOKUP_NONE"),
63    ("gtk_icon_theme_lookup_by_gicon", 5, "GTK_ICON_LOOKUP_NONE"),
64    // GtkDropTargetAsync (GTK 4.20+)
65    ("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        // Collect all function names from FLAG_REPLACEMENTS
95        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        // Find the matching replacement rule
106        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}