Skip to main content

normalize_languages/
powershell.rs

1//! PowerShell language support.
2
3use crate::{ContainerBody, Import, Language, LanguageSymbols};
4use tree_sitter::Node;
5
6/// PowerShell language support.
7pub struct PowerShell;
8
9impl Language for PowerShell {
10    fn name(&self) -> &'static str {
11        "PowerShell"
12    }
13    fn extensions(&self) -> &'static [&'static str] {
14        &["ps1", "psm1", "psd1"]
15    }
16    fn grammar_name(&self) -> &'static str {
17        "powershell"
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() != "pipeline" {
26            return Vec::new();
27        }
28
29        let text = &content[node.byte_range()];
30        let line = node.start_position().row + 1;
31
32        // Import-Module ModuleName
33        if let Some(rest) = text.strip_prefix("Import-Module ") {
34            let module = rest.split_whitespace().next().map(|s| s.to_string());
35            if let Some(module) = module {
36                return vec![Import {
37                    module,
38                    names: Vec::new(),
39                    alias: None,
40                    is_wildcard: true,
41                    is_relative: false,
42                    line,
43                }];
44            }
45        }
46
47        Vec::new()
48    }
49
50    fn format_import(&self, import: &Import, _names: Option<&[&str]>) -> String {
51        // PowerShell: Import-Module or using module
52        format!("Import-Module {}", import.module)
53    }
54
55    fn is_test_symbol(&self, symbol: &crate::Symbol) -> bool {
56        let name = symbol.name.as_str();
57        match symbol.kind {
58            crate::SymbolKind::Function | crate::SymbolKind::Method => name.starts_with("test_"),
59            crate::SymbolKind::Module => name == "tests" || name == "test",
60            _ => false,
61        }
62    }
63
64    fn container_body<'a>(&self, node: &'a Node<'a>) -> Option<Node<'a>> {
65        node.child_by_field_name("body")
66    }
67
68    fn analyze_container_body(
69        &self,
70        body_node: &Node,
71        content: &str,
72        inner_indent: &str,
73    ) -> Option<ContainerBody> {
74        crate::body::analyze_brace_body(body_node, content, inner_indent)
75    }
76}
77
78impl LanguageSymbols for PowerShell {}
79
80#[cfg(test)]
81mod tests {
82    use super::*;
83    use crate::validate_unused_kinds_audit;
84
85    #[test]
86    fn unused_node_kinds_audit() {
87        #[rustfmt::skip]
88        let documented_unused: &[&str] = &[
89            "additive_argument_expression", "additive_expression", "argument_expression",
90            "argument_expression_list", "array_expression", "array_literal_expression",
91            "array_type_name", "assignement_operator", "assignment_expression",
92            "bitwise_argument_expression", "bitwise_expression", "block_name", "cast_expression",
93            "catch_clauses", "catch_type_list", "class_attribute", "class_method_definition",
94            "class_method_parameter", "class_method_parameter_list", "class_property_definition",
95            "command_invokation_operator", "comparison_argument_expression",
96            "comparison_expression", "comparison_operator", "data_statement", "do_statement",
97            "else_clause", "elseif_clauses", "empty_statement", "enum_member",
98            "expression_with_unary_operator", "file_redirection_operator", "finally_clause",
99            "flow_control_statement", "for_condition", "for_initializer", "for_iterator",
100            "foreach_command", "foreach_parameter", "format_argument_expression",
101            "format_expression", "format_operator",
102            "function_parameter_declaration", "generic_type_arguments", "generic_type_name",
103            "hash_entry", "hash_literal_body", "hash_literal_expression",
104            "inlinescript_statement", "invokation_expression", "invokation_foreach_expression",
105            "key_expression", "label_expression", "left_assignment_expression",
106            "logical_argument_expression", "logical_expression", "merging_redirection_operator",
107            "multiplicative_argument_expression", "multiplicative_expression", "named_block",
108            "named_block_list", "parallel_statement", "param_block", "parenthesized_expression",
109            "post_decrement_expression", "post_increment_expression", "pre_decrement_expression",
110            "pre_increment_expression", "range_argument_expression", "range_expression",
111            "script_block_body", "script_block_expression", "sequence_statement",
112            "statement_block", "statement_list", "sub_expression", "switch_body",
113            "switch_clause", "switch_clause_condition", "switch_clauses", "trap_statement",
114            "type_identifier", "type_literal", "type_name", "type_spec", "unary_expression",
115            "while_condition",
116            // control flow — not extracted as symbols
117            "if_statement",
118            "try_statement",
119            "catch_clause",
120            "while_statement",
121            "switch_statement",
122            "for_statement",
123            "elseif_clause",
124            "foreach_statement",
125            "script_block",
126        ];
127        validate_unused_kinds_audit(&PowerShell, documented_unused)
128            .expect("PowerShell unused node kinds audit failed");
129    }
130}