normalize-languages 0.3.2

Tree-sitter language support and dynamic grammar loading
Documentation
//! HLSL (High-Level Shading Language) support.

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

/// HLSL language support.
pub struct Hlsl;

impl Language for Hlsl {
    fn name(&self) -> &'static str {
        "HLSL"
    }
    fn extensions(&self) -> &'static [&'static str] {
        &["hlsl", "hlsli", "fx", "fxh", "cginc"]
    }
    fn grammar_name(&self) -> &'static str {
        "hlsl"
    }

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

    fn extract_imports(&self, node: &Node, content: &str) -> Vec<Import> {
        if node.kind() != "preproc_include" {
            return Vec::new();
        }

        let text = &content[node.byte_range()];
        let line = node.start_position().row + 1;

        // #include "file.hlsl" or #include <file.hlsl>
        let module = text
            .split('"')
            .nth(1)
            .or_else(|| text.split('<').nth(1).and_then(|s| s.split('>').next()))
            .map(|s| s.to_string());

        if let Some(module) = module {
            return vec![Import {
                module,
                names: Vec::new(),
                alias: None,
                is_wildcard: false,
                is_relative: text.contains('"'),
                line,
            }];
        }

        Vec::new()
    }

    fn format_import(&self, import: &Import, _names: Option<&[&str]>) -> String {
        // HLSL: #include "file.hlsl" or #include <file.hlsl>
        if import.is_relative {
            format!("#include \"{}\"", import.module)
        } else {
            format!("#include <{}>", import.module)
        }
    }

    fn container_body<'a>(&self, node: &'a Node<'a>) -> Option<Node<'a>> {
        node.child_by_field_name("body")
    }

    fn analyze_container_body(
        &self,
        body_node: &Node,
        content: &str,
        inner_indent: &str,
    ) -> Option<ContainerBody> {
        crate::body::analyze_brace_body(body_node, content, inner_indent)
    }

    fn node_name<'a>(&self, node: &Node, content: &'a str) -> Option<&'a str> {
        node.child_by_field_name("declarator")
            .and_then(|d| d.child_by_field_name("declarator"))
            .map(|n| &content[n.byte_range()])
            .or_else(|| {
                node.child_by_field_name("name")
                    .map(|n| &content[n.byte_range()])
            })
    }
}

impl LanguageSymbols for Hlsl {}

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

    #[test]
    fn unused_node_kinds_audit() {
        #[rustfmt::skip]
        let documented_unused: &[&str] = &[
            "abstract_function_declarator", "access_specifier", "alias_declaration",
            "alignas_qualifier", "alignof_expression", "assignment_expression",
            "attribute_declaration", "attribute_specifier", "attributed_statement",
            "base_class_clause", "binary_expression", "bitfield_clause", "break_statement",
            "call_expression", "cast_expression", "catch_clause",
            "class_specifier", "co_await_expression", "co_return_statement", "co_yield_statement",
            "comma_expression", "compound_literal_expression", "concept_definition",
            "condition_clause", "consteval_block_declaration", "continue_statement",
            "declaration", "declaration_list", "decltype", "default_method_clause",
            "delete_expression", "delete_method_clause", "dependent_type", "destructor_name",
            "discard_statement", "do_statement", "else_clause", "enum_specifier", "enumerator",
            "enumerator_list", "expansion_statement", "explicit_function_specifier",
            "explicit_object_parameter_declaration", "export_declaration", "expression_statement",
            "extension_expression", "field_declaration", "field_declaration_list",
            "field_expression", "field_identifier", "fold_expression", "for_range_loop",
            "friend_declaration", "function_declarator", "generic_expression",
            "global_module_fragment_declaration", "gnu_asm_expression", "gnu_asm_qualifier",
            "goto_statement", "identifier", "import_declaration", "init_statement",
            "labeled_statement", "lambda_capture_initializer", "lambda_capture_specifier",
            "lambda_declarator", "lambda_default_capture", "lambda_expression", "lambda_specifier",
            "linkage_specification", "module_declaration", "module_name", "module_partition",
            "ms_based_modifier", "ms_call_modifier", "ms_declspec_modifier", "ms_pointer_modifier",
            "ms_restrict_modifier", "ms_signed_ptr_modifier", "ms_unaligned_ptr_modifier",
            "ms_unsigned_ptr_modifier", "namespace_alias_definition", "namespace_definition",
            "namespace_identifier", "nested_namespace_specifier", "new_expression", "noexcept",
            "offsetof_expression", "operator_cast", "operator_name", "optional_parameter_declaration",
            "optional_type_parameter_declaration", "parameter_declaration", "parenthesized_expression",
            "placeholder_type_specifier", "pointer_expression", "pointer_type_declarator",
            "preproc_elif", "preproc_elifdef", "preproc_else", "preproc_function_def",
            "preproc_if", "preproc_ifdef", "primitive_type", "private_module_fragment_declaration",
            "pure_virtual_clause", "qualified_identifier", "qualifiers", "ref_qualifier",
            "reflect_expression", "requires_clause", "requires_expression", "return_statement",
            "seh_except_clause", "seh_finally_clause", "seh_leave_statement", "seh_try_statement",
            "sized_type_specifier", "sizeof_expression", "splice_expression", "splice_specifier",
            "splice_type_specifier", "statement_identifier", "static_assert_declaration",
            "storage_class_specifier", "structured_binding_declarator", "subscript_expression",
            "template_declaration", "template_function", "template_method",
            "template_template_parameter_declaration", "template_type", "throw_specifier",
            "throw_statement", "trailing_return_type", "try_statement", "type_definition",
            "type_descriptor", "type_identifier", "type_parameter_declaration", "type_qualifier",
            "type_requirement", "unary_expression", "union_specifier", "update_expression",
            "using_declaration", "variadic_parameter_declaration",
            "variadic_type_parameter_declaration", "virtual_specifier",
            // control flow — not extracted as symbols
            "conditional_expression",
            "case_statement",
            "for_statement",
            "compound_statement",
            "if_statement",
            "switch_statement",
            "while_statement",
        ];
        validate_unused_kinds_audit(&Hlsl, documented_unused)
            .expect("HLSL unused node kinds audit failed");
    }
}