Pre-order walk, collecting the leftmost identifier of every call/invocation/macro node, in source order, then hashing them.
(root: Node<'_>, source: &[u8])
| 247 | /// Pre-order walk, collecting the leftmost identifier of every |
| 248 | /// call/invocation/macro node, in source order, then hashing them. |
| 249 | fn hash_call_sequence(root: Node<'_>, source: &[u8]) -> String { |
| 250 | let mut calls: Vec<String> = Vec::new(); |
| 251 | let mut stack: Vec<Node<'_>> = vec![root]; |
| 252 | while let Some(node) = stack.pop() { |
| 253 | let kind = node.kind(); |
| 254 | if is_call_kind(kind) { |
| 255 | if let Some(name) = leftmost_callable_name(node, source) { |
| 256 | calls.push(name); |
| 257 | } |
| 258 | } |
| 259 | let mut cursor = node.walk(); |
| 260 | if cursor.goto_first_child() { |
| 261 | let mut children: Vec<Node<'_>> = Vec::new(); |
| 262 | loop { |
| 263 | children.push(cursor.node()); |
| 264 | if !cursor.goto_next_sibling() { |
| 265 | break; |
| 266 | } |
| 267 | } |
| 268 | for child in children.into_iter().rev() { |
| 269 | stack.push(child); |
| 270 | } |
| 271 | } |
| 272 | } |
| 273 | |
| 274 | let mut hasher = Sha256::new(); |
| 275 | for name in &calls { |
| 276 | hasher.update(name.as_bytes()); |
| 277 | hasher.update([0x1f]); |
| 278 | } |
| 279 | short_hex(hasher.finalize().as_slice()) |
| 280 | } |
| 281 | |
| 282 | fn is_call_kind(kind: &str) -> bool { |
| 283 | const MARKERS: [&str; 4] = ["call", "invocation", "macro", "apply"]; |
no test coverage detected