MCPcopy Create free account
hub / github.com/QodeXcli/QodeX / analyzeFunction

Function analyzeFunction

src/context/data-flow.ts:94–163  ·  view source on GitHub ↗
(fnNode: any)

Source from the content-addressed store, hash-verified

92 const results: FunctionDataFlow[] = [];
93
94 const analyzeFunction = (fnNode: any) => {
95 const declared = new Set<string>();
96 collectParams(fnNode, content, declared);
97
98 // First pass: gather locally-declared names so they don't count as free.
99 const gatherDecls = (n: any) => {
100 if (!n) return;
101 if (LOCAL_DECL_RE.test(n.type)) {
102 const nameNode = n.childForFieldName?.('name') ?? n.childForFieldName?.('left');
103 if (nameNode && (nameNode.type === 'identifier')) {
104 declared.add(content.slice(nameNode.startIndex, nameNode.endIndex));
105 }
106 // variable_declarator with destructuring / multiple
107 for (let i = 0; i < (n.childCount ?? 0); i++) {
108 const c = n.child(i);
109 if (c?.type === 'identifier' && i === 0) {
110 declared.add(content.slice(c.startIndex, c.endIndex));
111 }
112 }
113 }
114 // nested function params are declared in their own scope but we keep it
115 // simple: treat them as declared so we don't over-report.
116 if (FUNC_NODE_RE.test(n.type) && n !== fnNode) {
117 collectParams(n, content, declared);
118 }
119 for (let i = 0; i < (n.childCount ?? 0); i++) gatherDecls(n.child(i));
120 };
121 const body = fnNode.childForFieldName?.('body') ?? fnNode;
122 gatherDecls(body);
123
124 // Second pass: count free identifiers (not declared, not a property key).
125 const freq = new Map<string, number>();
126 const walk = (n: any) => {
127 if (!n) return;
128 if (n.type === 'identifier') {
129 const name = content.slice(n.startIndex, n.endIndex);
130 // Allow single-char identifiers — they can be real function refs (g, _, $).
131 // Only skip empty strings and pure noise: single underscores are common
132 // throwaway vars, but we keep everything else including single letters.
133 if (name.length >= 1 && name !== '_' && !declared.has(name) && !BUILTINS.has(name)) {
134 freq.set(name, (freq.get(name) ?? 0) + 1);
135 }
136 } else if (n.type === 'member_expression') {
137 // obj.property — only walk the OBJECT side (left); skip the property name (right).
138 // Field names: object (left side), property (right side = what we skip).
139 const obj = n.childForFieldName?.('object');
140 if (obj) walk(obj);
141 // Don't descend into property — it's always a reference to a key, not a variable.
142 } else if (n.type === 'pair' || n.type === 'shorthand_property_identifier_pattern') {
143 // Object literal { key: val } — skip the key, walk the value.
144 const val = n.childForFieldName?.('value');
145 if (val) walk(val);
146 } else {
147 for (let i = 0; i < (n.childCount ?? 0); i++) walk(n.child(i));
148 }
149 };
150 walk(body);
151

Callers 1

findFnsFunction · 0.85

Calls 5

collectParamsFunction · 0.85
gatherDeclsFunction · 0.85
funcNameFunction · 0.85
walkFunction · 0.70
pushMethod · 0.45

Tested by

no test coverage detected