MCPcopy Create free account
hub / github.com/ScriptedAlchemy/tracedecay / count_complexity

Function count_complexity

src/extraction/complexity.rs:62–169  ·  view source on GitHub ↗

Counts complexity metrics by iterating over all descendants of `node`. Uses an explicit stack instead of recursion (NASA Power of 10, Rule 1). The nesting depth tracks how many nesting-type ancestors enclose each node. `source` is needed to extract method/macro names for unchecked-call and assertion detection. Pass an empty slice to skip name-based matching.

(
    node: TsNode<'_>,
    config: &ComplexityConfig,
    source: &[u8],
)

Source from the content-addressed store, hash-verified

60/// `source` is needed to extract method/macro names for unchecked-call and
61/// assertion detection. Pass an empty slice to skip name-based matching.
62pub fn count_complexity(
63 node: TsNode<'_>,
64 config: &ComplexityConfig,
65 source: &[u8],
66) -> ComplexityMetrics {
67 const MAX_ITERATIONS: usize = 500_000;
68 debug_assert!(
69 !config.branch_types.is_empty() || !config.loop_types.is_empty(),
70 "count_complexity called with config that has no branch or loop types"
71 );
72 debug_assert!(
73 node.child_count() > 0,
74 "count_complexity called on a node with no children"
75 );
76 let mut metrics = ComplexityMetrics::default();
77
78 // Stack: (tree-sitter node, current nesting depth)
79 let mut stack: Vec<(TsNode<'_>, u32)> = Vec::new();
80
81 // Seed with direct children of the function node. Earlier revisions used
82 // `node.child(i)` in a `for i in 0..N` loop — tree-sitter's `child(i)`
83 // is O(i) because it walks sibling links from the first child, so the
84 // seed loop alone was O(N²) for high-fanout nodes (e.g. the giant
85 // `switch` in `kernel/bpf/verifier.c` with thousands of cases). Use a
86 // cursor for O(1) per step.
87 push_children(&mut stack, node, 0);
88
89 let mut iterations: usize = 0;
90
91 while let Some((current, depth)) = stack.pop() {
92 iterations += 1;
93 if iterations >= MAX_ITERATIONS {
94 break;
95 }
96
97 let kind = current.kind();
98
99 // Classify the node.
100 if config.branch_types.contains(&kind) {
101 metrics.branches += 1;
102 }
103 if config.loop_types.contains(&kind) {
104 metrics.loops += 1;
105 }
106 if config.return_types.contains(&kind) {
107 metrics.returns += 1;
108 }
109
110 // Unsafe blocks.
111 if config.unsafe_types.contains(&kind) {
112 metrics.unsafe_blocks += 1;
113 }
114
115 // Unchecked operator types (e.g. non_null_assertion_expression, `!!`).
116 if config.unchecked_types.contains(&kind) {
117 metrics.unchecked_calls += 1;
118 }
119

Callers 15

visit_let_bindingMethod · 0.85
visit_sub_definitionMethod · 0.85
visit_functionMethod · 0.85
visit_bindingMethod · 0.85
visit_methodMethod · 0.85
visit_constructorMethod · 0.85
visit_functionMethod · 0.85
visit_testMethod · 0.85

Calls 7

push_childrenFunction · 0.85
extract_call_nameFunction · 0.85
extract_macro_nameFunction · 0.85
kindMethod · 0.80
containsMethod · 0.45
is_emptyMethod · 0.45
as_strMethod · 0.45

Tested by 2

visit_test_callFunction · 0.68
rust_fn_complexityFunction · 0.68