Skip to main content

normalize_languages/
hlsl.rs

1//! HLSL (High-Level Shading Language) support.
2
3use crate::{ContainerBody, Import, Language, LanguageSymbols};
4use tree_sitter::Node;
5
6/// HLSL language support.
7pub struct Hlsl;
8
9impl Language for Hlsl {
10    fn name(&self) -> &'static str {
11        "HLSL"
12    }
13    fn extensions(&self) -> &'static [&'static str] {
14        &["hlsl", "hlsli", "fx", "fxh", "cginc"]
15    }
16    fn grammar_name(&self) -> &'static str {
17        "hlsl"
18    }
19
20    fn as_symbols(&self) -> Option<&dyn LanguageSymbols> {
21        Some(self)
22    }
23
24    fn extract_imports(&self, node: &Node, content: &str) -> Vec<Import> {
25        if node.kind() != "preproc_include" {
26            return Vec::new();
27        }
28
29        let text = &content[node.byte_range()];
30        let line = node.start_position().row + 1;
31
32        // #include "file.hlsl" or #include <file.hlsl>
33        let module = text
34            .split('"')
35            .nth(1)
36            .or_else(|| text.split('<').nth(1).and_then(|s| s.split('>').next()))
37            .map(|s| s.to_string());
38
39        if let Some(module) = module {
40            return vec![Import {
41                module,
42                names: Vec::new(),
43                alias: None,
44                is_wildcard: false,
45                is_relative: text.contains('"'),
46                line,
47            }];
48        }
49
50        Vec::new()
51    }
52
53    fn format_import(&self, import: &Import, _names: Option<&[&str]>) -> String {
54        // HLSL: #include "file.hlsl" or #include <file.hlsl>
55        if import.is_relative {
56            format!("#include \"{}\"", import.module)
57        } else {
58            format!("#include <{}>", import.module)
59        }
60    }
61
62    fn container_body<'a>(&self, node: &'a Node<'a>) -> Option<Node<'a>> {
63        node.child_by_field_name("body")
64    }
65
66    fn analyze_container_body(
67        &self,
68        body_node: &Node,
69        content: &str,
70        inner_indent: &str,
71    ) -> Option<ContainerBody> {
72        crate::body::analyze_brace_body(body_node, content, inner_indent)
73    }
74
75    fn node_name<'a>(&self, node: &Node, content: &'a str) -> Option<&'a str> {
76        node.child_by_field_name("declarator")
77            .and_then(|d| d.child_by_field_name("declarator"))
78            .map(|n| &content[n.byte_range()])
79            .or_else(|| {
80                node.child_by_field_name("name")
81                    .map(|n| &content[n.byte_range()])
82            })
83    }
84}
85
86impl LanguageSymbols for Hlsl {}
87
88#[cfg(test)]
89mod tests {
90    use super::*;
91    use crate::validate_unused_kinds_audit;
92
93    #[test]
94    fn unused_node_kinds_audit() {
95        #[rustfmt::skip]
96        let documented_unused: &[&str] = &[
97            "abstract_function_declarator", "access_specifier", "alias_declaration",
98            "alignas_qualifier", "alignof_expression", "assignment_expression",
99            "attribute_declaration", "attribute_specifier", "attributed_statement",
100            "base_class_clause", "binary_expression", "bitfield_clause", "break_statement",
101            "call_expression", "cast_expression", "catch_clause",
102            "class_specifier", "co_await_expression", "co_return_statement", "co_yield_statement",
103            "comma_expression", "compound_literal_expression", "concept_definition",
104            "condition_clause", "consteval_block_declaration", "continue_statement",
105            "declaration", "declaration_list", "decltype", "default_method_clause",
106            "delete_expression", "delete_method_clause", "dependent_type", "destructor_name",
107            "discard_statement", "do_statement", "else_clause", "enum_specifier", "enumerator",
108            "enumerator_list", "expansion_statement", "explicit_function_specifier",
109            "explicit_object_parameter_declaration", "export_declaration", "expression_statement",
110            "extension_expression", "field_declaration", "field_declaration_list",
111            "field_expression", "field_identifier", "fold_expression", "for_range_loop",
112            "friend_declaration", "function_declarator", "generic_expression",
113            "global_module_fragment_declaration", "gnu_asm_expression", "gnu_asm_qualifier",
114            "goto_statement", "identifier", "import_declaration", "init_statement",
115            "labeled_statement", "lambda_capture_initializer", "lambda_capture_specifier",
116            "lambda_declarator", "lambda_default_capture", "lambda_expression", "lambda_specifier",
117            "linkage_specification", "module_declaration", "module_name", "module_partition",
118            "ms_based_modifier", "ms_call_modifier", "ms_declspec_modifier", "ms_pointer_modifier",
119            "ms_restrict_modifier", "ms_signed_ptr_modifier", "ms_unaligned_ptr_modifier",
120            "ms_unsigned_ptr_modifier", "namespace_alias_definition", "namespace_definition",
121            "namespace_identifier", "nested_namespace_specifier", "new_expression", "noexcept",
122            "offsetof_expression", "operator_cast", "operator_name", "optional_parameter_declaration",
123            "optional_type_parameter_declaration", "parameter_declaration", "parenthesized_expression",
124            "placeholder_type_specifier", "pointer_expression", "pointer_type_declarator",
125            "preproc_elif", "preproc_elifdef", "preproc_else", "preproc_function_def",
126            "preproc_if", "preproc_ifdef", "primitive_type", "private_module_fragment_declaration",
127            "pure_virtual_clause", "qualified_identifier", "qualifiers", "ref_qualifier",
128            "reflect_expression", "requires_clause", "requires_expression", "return_statement",
129            "seh_except_clause", "seh_finally_clause", "seh_leave_statement", "seh_try_statement",
130            "sized_type_specifier", "sizeof_expression", "splice_expression", "splice_specifier",
131            "splice_type_specifier", "statement_identifier", "static_assert_declaration",
132            "storage_class_specifier", "structured_binding_declarator", "subscript_expression",
133            "template_declaration", "template_function", "template_method",
134            "template_template_parameter_declaration", "template_type", "throw_specifier",
135            "throw_statement", "trailing_return_type", "try_statement", "type_definition",
136            "type_descriptor", "type_identifier", "type_parameter_declaration", "type_qualifier",
137            "type_requirement", "unary_expression", "union_specifier", "update_expression",
138            "using_declaration", "variadic_parameter_declaration",
139            "variadic_type_parameter_declaration", "virtual_specifier",
140            // control flow — not extracted as symbols
141            "conditional_expression",
142            "case_statement",
143            "for_statement",
144            "compound_statement",
145            "if_statement",
146            "switch_statement",
147            "while_statement",
148        ];
149        validate_unused_kinds_audit(&Hlsl, documented_unused)
150            .expect("HLSL unused node kinds audit failed");
151    }
152}