1use crate::{ContainerBody, Import, Language, LanguageSymbols};
4use tree_sitter::Node;
5
6pub 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 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 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 "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}