Skip to main content

normalize_languages/
rescript.rs

1//! ReScript language support.
2
3use crate::{ContainerBody, Import, Language, LanguageSymbols};
4use tree_sitter::Node;
5
6/// ReScript language support.
7pub struct ReScript;
8
9impl Language for ReScript {
10    fn name(&self) -> &'static str {
11        "ReScript"
12    }
13    fn extensions(&self) -> &'static [&'static str] {
14        &["res", "resi"]
15    }
16    fn grammar_name(&self) -> &'static str {
17        "rescript"
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() != "open_statement" {
26            return Vec::new();
27        }
28
29        let text = &content[node.byte_range()];
30        vec![Import {
31            module: text.trim().to_string(),
32            names: Vec::new(),
33            alias: None,
34            is_wildcard: true,
35            is_relative: false,
36            line: node.start_position().row + 1,
37        }]
38    }
39
40    fn format_import(&self, import: &Import, _names: Option<&[&str]>) -> String {
41        // ReScript: open Module
42        format!("open {}", import.module)
43    }
44
45    fn is_test_symbol(&self, symbol: &crate::Symbol) -> bool {
46        let name = symbol.name.as_str();
47        match symbol.kind {
48            crate::SymbolKind::Function | crate::SymbolKind::Method => name.starts_with("test_"),
49            crate::SymbolKind::Module => name == "tests" || name == "test",
50            _ => false,
51        }
52    }
53
54    fn container_body<'a>(&self, node: &'a Node<'a>) -> Option<Node<'a>> {
55        node.child_by_field_name("body")
56    }
57
58    fn analyze_container_body(
59        &self,
60        body_node: &Node,
61        content: &str,
62        inner_indent: &str,
63    ) -> Option<ContainerBody> {
64        crate::body::analyze_brace_body(body_node, content, inner_indent)
65    }
66}
67
68impl LanguageSymbols for ReScript {}
69
70#[cfg(test)]
71mod tests {
72    use super::*;
73    use crate::validate_unused_kinds_audit;
74
75    #[test]
76    fn unused_node_kinds_audit() {
77        #[rustfmt::skip]
78        let documented_unused: &[&str] = &[
79            // Expression nodes
80            "try_expression", "ternary_expression", "while_expression", "for_expression",
81            "call_expression", "pipe_expression", "sequence_expression", "await_expression",
82            "coercion_expression", "lazy_expression", "assert_expression",
83            "parenthesized_expression", "unary_expression", "binary_expression",
84            "subscript_expression", "member_expression", "mutation_expression",
85            "extension_expression",
86            // Type nodes
87            "type_identifier", "type_identifier_path", "unit_type", "generic_type",
88            "function_type", "polyvar_type", "polymorphic_type", "tuple_type",
89            "record_type", "record_type_field", "object_type", "variant_type",
90            "abstract_type", "type_arguments", "type_parameters", "type_constraint",
91            "type_annotation", "type_spread", "constrain_type",
92            "as_aliasing_type", "function_type_parameters",
93            // Module nodes
94            "parenthesized_module_expression", "module_type_constraint", "module_type_annotation",
95            "module_type_of", "constrain_module", "module_identifier", "module_identifier_path",
96            "module_pack", "module_unpack",
97            // Declaration nodes
98            "let_declaration", "exception_declaration", "variant_declaration",
99            "polyvar_declaration", "include_statement",
100            // JSX
101            "jsx_expression", "jsx_identifier", "nested_jsx_identifier",
102            // Pattern matching
103            "exception_pattern", "polyvar_type_pattern",
104            // Identifiers
105            "value_identifier_path", "variant_identifier",
106            "nested_variant_identifier", "polyvar_identifier", "property_identifier",
107            "extension_identifier", "decorator_identifier",
108            // Clauses
109            "else_clause", "else_if_clause",
110            // Other
111            "function", "expression_statement", "formal_parameters",
112            // control flow — not extracted as symbols
113            "if_expression",
114            "block",
115            "switch_expression",
116            "open_statement",
117            "switch_match",
118        ];
119        validate_unused_kinds_audit(&ReScript, documented_unused)
120            .expect("ReScript unused node kinds audit failed");
121    }
122}