(n: any)
| 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 |
no test coverage detected