gobject_linter/rules/
property_enum_coverage.rs1use gobject_ast::model::{EnumInfo, FileModel};
2
3use crate::{
4 ast_context::AstContext,
5 config::Config,
6 rules::{Rule, Violation},
7};
8
9pub struct PropertyEnumCoverage;
10
11impl Rule for PropertyEnumCoverage {
12 fn name(&self) -> &'static str {
13 "property_enum_coverage"
14 }
15
16 fn description(&self) -> &'static str {
17 "Ensure all property enum values have corresponding g_param_spec or g_object_class_override_property"
18 }
19
20 fn category(&self) -> crate::rules::Category {
21 crate::rules::Category::Correctness
22 }
23
24 fn check_enum(
25 &self,
26 _ast_context: &AstContext,
27 _config: &Config,
28 enum_info: &EnumInfo,
29 file: &FileModel,
30 violations: &mut Vec<Violation>,
31 ) {
32 if !enum_info.is_property_enum() {
33 return;
34 }
35
36 let property_values: Vec<&str> = enum_info
37 .values
38 .iter()
39 .filter(|v| !v.is_prop_0() && !v.is_prop_last())
40 .map(|v| v.name.as_str())
41 .collect();
42
43 if property_values.is_empty() {
44 return;
45 }
46
47 let Some(gobject_type) = file.find_gobject_type_for_property_enum(enum_info) else {
48 return;
49 };
50
51 let installed_properties: Vec<_> = gobject_type
52 .properties
53 .iter()
54 .filter_map(|assignment| assignment.get_installed_enum_value())
55 .collect();
56
57 for prop_name in property_values {
58 if !installed_properties.iter().any(|p| p == &prop_name) {
59 violations.push(self.violation(
60 &file.path,
61 enum_info.location.line,
62 1,
63 format!(
64 "Property enum value '{}' is declared but never installed in {}",
65 prop_name,
66 gobject_type.class_init_function_name()
67 ),
68 ));
69 }
70 }
71 }
72}