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
82
83
84
85
86
87
use std::hash::{DefaultHasher, Hash, Hasher};

use crate::tree::Tree;

/// Computes a Merkle hash for every node: (kind_id, label, child hashes
/// in order), indexed like the tree's arena.
///
/// Spans are deliberately excluded so the hash is position-independent —
/// identical code at different places (or in different trees) collides,
/// which is exactly what anchoring the diff on unique equal subtrees
/// needs.
pub(crate) fn compute(tree: &Tree) -> Vec<u64> {
    let mut hashes = vec![0u64; tree.nodes().count()];
    // Arena order is pre-order, so every child's index is greater than
    // its parent's; walking indices in reverse hashes children first.
    let ids: Vec<_> = tree.nodes().collect();
    for &id in ids.iter().rev() {
        let mut hasher = DefaultHasher::new();
        tree.kind_id(id).hash(&mut hasher);
        tree.label(id).hash(&mut hasher);
        for &child in tree.children(id) {
            // In-bounds by construction: children share the arena the
            // hashes vec was sized from, and the reverse walk computed
            // their hashes already.
            #[allow(clippy::indexing_slicing)]
            hashes[child.index()].hash(&mut hasher);
        }
        #[allow(clippy::indexing_slicing)]
        {
            hashes[id.index()] = hasher.finish();
        }
    }
    hashes
}

#[cfg(test)]
mod tests {
    use crate::error::Error;
    use crate::lang::Lang;
    use crate::tree::Tree;

    fn parse_rust(src: &str) -> Result<Tree, Error> {
        let lang = Lang::by_name("rust").ok_or(Error::UnknownLanguage {
            path: "rust".into(),
        })?;
        Tree::parse(src, lang)
    }

    #[test]
    fn identical_subtrees_hash_equal_across_positions() -> Result<(), Error> {
        let t = parse_rust("fn a() { x(); }\nfn b() { x(); }")?;
        let calls: Vec<_> = t
            .nodes()
            .filter(|&n| t.kind(n) == "call_expression")
            .collect();
        assert_eq!(calls.len(), 2);
        assert_eq!(
            calls.first().map(|&n| t.hash(n)),
            calls.last().map(|&n| t.hash(n))
        );
        Ok(())
    }

    #[test]
    fn equal_sources_hash_equal_across_trees() -> Result<(), Error> {
        let a = parse_rust("fn main() { x(); }")?;
        let b = parse_rust("fn main() { x(); }")?;
        assert_eq!(a.hash(a.root()), b.hash(b.root()));
        Ok(())
    }

    #[test]
    fn different_labels_hash_differently() -> Result<(), Error> {
        let a = parse_rust("fn main() { x(); }")?;
        let b = parse_rust("fn main() { y(); }")?;
        assert_ne!(a.hash(a.root()), b.hash(b.root()));
        Ok(())
    }

    #[test]
    fn different_structure_hashes_differently() -> Result<(), Error> {
        let a = parse_rust("fn main() { x(); }")?;
        let b = parse_rust("fn main() { x(); x(); }")?;
        assert_ne!(a.hash(a.root()), b.hash(b.root()));
        Ok(())
    }
}