1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
use rnix::{
    ast::Ident,
    NixLanguage, SyntaxKind,
};
use rowan::{api::SyntaxNode, ast::AstNode};

/// This string in a Nix comment above an unused declaration shall
/// force us to skip it.
///
/// ```nix
/// let
///   # deadnix: skip
///   skeletonsInTheBasement =
/// ```
const PRAGMA_SKIP: &str = "deadnix: skip";

/// A Nix variable binding
#[derive(Debug, Clone)]
pub struct Binding {
    /// Variable name
    pub name: Ident,
    /// Syntax node of declaration itself
    pub decl_node: SyntaxNode<NixLanguage>,
    mortal: bool,
}

impl Binding {
    /// Create a new Binding
    pub fn new(
        name: Ident,
        decl_node: SyntaxNode<NixLanguage>,
        mortal: bool,
    ) -> Self {
        Binding {
            name,
            decl_node,
            mortal,
        }
    }

    /// Can die?
    ///
    /// Not mortal are `rec { ... }`, and lambda args that already
    /// start with `_`.
    pub fn is_mortal(&self) -> bool {
        self.mortal
    }

    /// Does the name start with `_`, signifying an anonymous
    /// variable?
    pub fn starts_with_underscore(&self) -> bool {
        self.name.syntax().text().char_at(0.into()) == Some('_')
    }

    /// Searches through tokens backwards for `PRAGMA_SKIP` until at
    /// least two linebreaks are seen
    pub fn has_pragma_skip(&self) -> bool {
        let mut line_breaks = 0;
        let mut token = self.decl_node.first_token().unwrap();
        while let Some(prev) = token.prev_token() {
            token = prev;

            match token.kind() {
                SyntaxKind::TOKEN_WHITESPACE => {
                    line_breaks += token.text().matches('\n').count();
                    if line_breaks > 1 {
                        break;
                    }
                }

                SyntaxKind::TOKEN_COMMENT if token.text().contains(PRAGMA_SKIP) =>
                    return true,

                _ => {}
            }
        }

        // No PRAGMA_SKIP found
        false
    }
}