gobject-linter 0.1.2

A fast tree-sitter-based linter for GObject/C code
Documentation
use gobject_ast::model::{EnumInfo, FileModel};

use crate::{
    ast_context::AstContext,
    config::Config,
    rules::{Rule, Violation},
};

pub struct SignalEnumCoverage;

impl Rule for SignalEnumCoverage {
    fn name(&self) -> &'static str {
        "signal_enum_coverage"
    }

    fn description(&self) -> &'static str {
        "Ensure all signal enum values have corresponding g_signal_new calls"
    }

    fn category(&self) -> crate::rules::Category {
        crate::rules::Category::Correctness
    }

    fn check_enum(
        &self,
        _ast_context: &AstContext,
        _config: &Config,
        enum_info: &EnumInfo,
        file: &FileModel,
        violations: &mut Vec<Violation>,
    ) {
        if !enum_info.is_signal_enum() {
            return;
        }

        let signal_values: Vec<&str> = enum_info
            .values
            .iter()
            .filter(|v| !v.is_signal_last())
            .map(|v| v.name.as_str())
            .collect();

        if signal_values.is_empty() {
            return;
        }

        let Some(gobject_type) = file.find_gobject_type_for_signal_enum(enum_info) else {
            return;
        };

        let installed: std::collections::HashSet<&str> = gobject_type
            .signals
            .iter()
            .filter_map(|s| s.enum_value.as_deref())
            .collect();

        for signal_name in &signal_values {
            if !installed.contains(signal_name) {
                violations.push(self.violation(
                    &file.path,
                    enum_info.location.line,
                    1,
                    format!(
                        "Signal enum value '{}' is declared but never installed in {}",
                        signal_name,
                        gobject_type.class_init_function_name()
                    ),
                ));
            }
        }
    }
}