Skip to main content

normalize_languages/
nix.rs

1//! Nix language support.
2
3use crate::{Import, Language, LanguageSymbols};
4use tree_sitter::Node;
5
6/// Nix language support.
7pub struct Nix;
8
9impl Language for Nix {
10    fn name(&self) -> &'static str {
11        "Nix"
12    }
13    fn extensions(&self) -> &'static [&'static str] {
14        &["nix"]
15    }
16    fn grammar_name(&self) -> &'static str {
17        "nix"
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() != "apply_expression" {
26            return Vec::new();
27        }
28
29        let text = &content[node.byte_range()];
30        if !text.starts_with("import ") {
31            return Vec::new();
32        }
33
34        // Extract path after "import"
35        let rest = text.strip_prefix("import ").unwrap_or("").trim();
36        let module = rest.split_whitespace().next().unwrap_or(rest).to_string();
37
38        vec![Import {
39            module,
40            names: Vec::new(),
41            alias: None,
42            is_wildcard: false,
43            is_relative: rest.starts_with('.'),
44            line: node.start_position().row + 1,
45        }]
46    }
47
48    fn format_import(&self, import: &Import, _names: Option<&[&str]>) -> String {
49        // Nix: import ./path.nix
50        format!("import {}", import.module)
51    }
52
53    fn is_test_symbol(&self, symbol: &crate::Symbol) -> bool {
54        let name = symbol.name.as_str();
55        match symbol.kind {
56            crate::SymbolKind::Function | crate::SymbolKind::Method => name.starts_with("test_"),
57            crate::SymbolKind::Module => name == "tests" || name == "test",
58            _ => false,
59        }
60    }
61
62    fn node_name<'a>(&self, node: &Node, content: &'a str) -> Option<&'a str> {
63        node.child_by_field_name("attrpath")
64            .map(|n| &content[n.byte_range()])
65    }
66}
67
68impl LanguageSymbols for Nix {}
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            "assert_expression", "binary_expression", "float_expression",
80            "formal", "formals", "has_attr_expression", "hpath_expression",
81            "identifier", "indented_string_expression", "integer_expression",
82            "list_expression", "let_attrset_expression", "parenthesized_expression",
83            "path_expression", "select_expression", "spath_expression",
84            "string_expression", "unary_expression", "uri_expression",
85            "variable_expression",
86            // Control flow / application — not definition constructs
87            "apply_expression", "if_expression", "with_expression",
88            // structural node, not extracted as symbols
89            "let_expression",
90            "attrset_expression",
91            "rec_attrset_expression",
92            "function_expression",
93        ];
94        validate_unused_kinds_audit(&Nix, documented_unused)
95            .expect("Nix unused node kinds audit failed");
96    }
97}