normalize-languages 0.3.1

Tree-sitter language support and dynamic grammar loading
Documentation
//! Ada language support.

use crate::docstring::extract_preceding_prefix_comments;
use crate::{ContainerBody, Import, Language, LanguageSymbols};
use tree_sitter::Node;

/// Ada language support.
pub struct Ada;

impl Language for Ada {
    fn name(&self) -> &'static str {
        "Ada"
    }
    fn extensions(&self) -> &'static [&'static str] {
        &["ada", "adb", "ads"]
    }
    fn grammar_name(&self) -> &'static str {
        "ada"
    }

    fn as_symbols(&self) -> Option<&dyn LanguageSymbols> {
        Some(self)
    }

    fn extract_imports(&self, node: &Node, content: &str) -> Vec<Import> {
        match node.kind() {
            "with_clause" => {
                let text = &content[node.byte_range()];
                vec![Import {
                    module: text.trim().to_string(),
                    names: Vec::new(),
                    alias: None,
                    is_wildcard: false,
                    is_relative: false,
                    line: node.start_position().row + 1,
                }]
            }
            "use_clause" => {
                let text = &content[node.byte_range()];
                vec![Import {
                    module: text.trim().to_string(),
                    names: Vec::new(),
                    alias: None,
                    is_wildcard: true,
                    is_relative: false,
                    line: node.start_position().row + 1,
                }]
            }
            _ => Vec::new(),
        }
    }

    fn format_import(&self, import: &Import, _names: Option<&[&str]>) -> String {
        // Ada: with Package;
        format!("with {};", import.module)
    }

    fn is_test_symbol(&self, symbol: &crate::Symbol) -> bool {
        let name = symbol.name.as_str();
        match symbol.kind {
            crate::SymbolKind::Function | crate::SymbolKind::Method => name.starts_with("test_"),
            crate::SymbolKind::Module => name == "tests" || name == "test",
            _ => false,
        }
    }

    fn container_body<'a>(&self, node: &'a Node<'a>) -> Option<Node<'a>> {
        // Ada has no dedicated body field; use the container node itself
        Some(*node)
    }

    fn analyze_container_body(
        &self,
        body_node: &Node,
        content: &str,
        inner_indent: &str,
    ) -> Option<ContainerBody> {
        // Ada: "package Foo is ... end Foo;" — find `is` child, then `end`
        crate::body::analyze_is_begin_end_body(body_node, content, inner_indent)
    }

    fn extract_docstring(&self, node: &Node, content: &str) -> Option<String> {
        // Ada uses -- for line comments
        extract_preceding_prefix_comments(node, content, "--")
    }

    fn node_name<'a>(&self, node: &Node, content: &'a str) -> Option<&'a str> {
        if let Some(name_node) = node.child_by_field_name("name") {
            return Some(&content[name_node.byte_range()]);
        }
        let mut cursor = node.walk();
        for child in node.children(&mut cursor) {
            if child.kind() == "identifier" || child.kind() == "defining_identifier" {
                return Some(&content[child.byte_range()]);
            }
        }
        None
    }
}

impl LanguageSymbols for Ada {}

#[cfg(test)]
mod tests {
    use super::*;
    use crate::validate_unused_kinds_audit;

    #[test]
    fn unused_node_kinds_audit() {
        #[rustfmt::skip]
        let documented_unused: &[&str] = &[
            // Type definitions
            "access_definition", "access_to_object_definition", "access_to_subprogram_definition",
            "array_type_definition", "decimal_fixed_point_definition", "derived_type_definition",
            "enumeration_type_definition", "floating_point_definition", "formal_access_type_definition",
            "formal_array_type_definition", "formal_decimal_fixed_point_definition",
            "formal_derived_type_definition", "formal_discrete_type_definition",
            "formal_floating_point_definition", "formal_interface_type_definition",
            "formal_modular_type_definition", "formal_ordinary_fixed_point_definition",
            "formal_private_type_definition", "formal_signed_integer_type_definition",
            "interface_type_definition", "modular_type_definition", "ordinary_fixed_point_definition",
            "record_type_definition", "signed_integer_type_definition",
            // Declarations
            "body_stub", "component_declaration", "component_definition", "discriminant_specification",
            "discriminant_specification_list", "entry_declaration", "exception_declaration",
            "formal_abstract_subprogram_declaration", "formal_complete_type_declaration",
            "formal_concrete_subprogram_declaration", "formal_incomplete_type_declaration",
            "formal_object_declaration", "formal_package_declaration", "formal_subprogram_declaration",
            "generic_formal_part", "generic_renaming_declaration", "generic_subprogram_declaration",
            "null_procedure_declaration", "number_declaration", "object_declaration",
            "object_renaming_declaration", "package_renaming_declaration", "parameter_specification",
            "private_extension_declaration", "single_protected_declaration", "single_task_declaration",
            "subprogram_renaming_declaration", "subtype_declaration",
            // Protected and task types
            "protected_body", "protected_body_stub", "protected_definition", "protected_type_declaration",
            "task_body", "task_body_stub", "task_definition", "task_type_declaration",
            // Stubs
            "package_body_stub", "subprogram_body_stub",
            // Statements
            "abort_statement", "accept_statement", "assignment_statement", "case_statement_alternative",
            "delay_relative_statement", "delay_until_statement", "goto_statement", "null_statement",
            "procedure_call_statement", "raise_statement", "requeue_statement", "simple_return_statement",
            // Expressions
            "qualified_expression", "raise_expression",
            // Potentially useful - control flow
            "exception_handler", "if_statement", "exit_statement", "case_statement",
            // Representation clauses
            "at_clause", "attribute_definition_clause", "component_clause", "enumeration_aggregate",
            "enumeration_representation_clause", "mod_clause", "record_representation_clause",
            // Control flow and statements
            "asynchronous_select", "conditional_entry_call", "entry_body", "entry_barrier",
            "entry_call_alternative", "entry_index_specification", "extended_return_object_declaration",
            "extended_return_statement", "handled_sequence_of_statements", "loop_label",
            "loop_parameter_specification", "timed_entry_call",
            // Contracts and aspects
            "aspect_specification", "global_aspect_definition",
            // GNAT-specific
            "gnatprep_declarative_if_statement", "gnatprep_identifier", "gnatprep_if_statement",
            // Expressions and operators
            "binary_adding_operator", "choice_parameter_specification", "chunk_specification",
            "elsif_expression_item", "elsif_statement_item", "exception_choice", "exception_choice_list",
            "exception_renaming_declaration", "expression", "formal_part", "function_call",
            "general_access_modifier", "index_subtype_definition",
            "iterator_specification", "multiplying_operator", "quantifier",
            "real_range_specification", "record_definition", "reduction_specification",
            "relational_operator", "subpool_specification", "unary_adding_operator",
            // control flow — not extracted as symbols
            "declare_expression",
            "if_expression",
            "quantified_expression",
            "block_statement",
            "loop_statement",
            "case_expression",
            "with_clause",
            "case_expression_alternative",
            "use_clause",
        ];
        validate_unused_kinds_audit(&Ada, documented_unused)
            .expect("Ada unused node kinds audit failed");
    }
}