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

Function analyzeDataFlow

src/context/data-flow.ts:83–177  ·  view source on GitHub ↗
(rel: string, content: string)

Source from the content-addressed store, hash-verified

81 * Best-effort: returns [] on parse failure / unsupported language.
82 */
83export async function analyzeDataFlow(rel: string, content: string): Promise<FunctionDataFlow[]> {
84 const lang = detectLanguage(rel);
85 if (!lang) return [];
86 let parser;
87 try { parser = await getParser(lang); } catch { return []; }
88 if (!parser) return [];
89 let tree;
90 try { tree = parser.parser.parse(content); } catch { return []; }
91
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);

Callers 3

executeMethod · 0.85
dependenciesOfFunction · 0.85

Calls 3

detectLanguageFunction · 0.85
getParserFunction · 0.85
findFnsFunction · 0.85

Tested by

no test coverage detected