* Visit a node and extract information
(node: SyntaxNode)
| 934 | * Visit a node and extract information |
| 935 | */ |
| 936 | private visitNode(node: SyntaxNode): void { |
| 937 | if (!this.extractor) return; |
| 938 | |
| 939 | const nodeType = node.type; |
| 940 | let skipChildren = false; |
| 941 | |
| 942 | // Language-specific custom visitor hook |
| 943 | if (this.extractor.visitNode) { |
| 944 | const ctx = this.makeExtractorContext(); |
| 945 | const handled = this.extractor.visitNode(node, ctx); |
| 946 | if (handled) { |
| 947 | // The hook consumed this subtree, so the walkers below never descend |
| 948 | // into it — scan it for function-as-value candidates (#756). Scala's |
| 949 | // hook handles val/var definitions (`val table = Seq(targetCb)`), for |
| 950 | // example. The scan is capture-only and halts at nested functions. |
| 951 | this.scanFnRefSubtree(node, 0); |
| 952 | return; |
| 953 | } |
| 954 | } |
| 955 | |
| 956 | // Pascal-specific AST handling |
| 957 | if (this.language === 'pascal') { |
| 958 | skipChildren = this.visitPascalNode(node); |
| 959 | if (skipChildren) return; |
| 960 | } |
| 961 | |
| 962 | // C++ namespace blocks: carry the namespace name as a qualifiedName prefix |
| 963 | // while walking the body, so `namespace flash { void compute_attn(); }` |
| 964 | // indexes compute_attn with qualifiedName `flash::compute_attn` and a |
| 965 | // namespace-qualified call (`flash::compute_attn(...)`) resolves by exact |
| 966 | // qualified match instead of never resolving — C++ namespaces previously |
| 967 | // left no trace in qualifiedNames at all, so every `ns::fn()` call site |
| 968 | // was a permanently dead edge (surfaced by #387 flow validation on |
| 969 | // flash-attention/cutlass, whose kernel dispatch is namespace-qualified). |
| 970 | // C++17 nested forms (`namespace a::b {`) prefix as written. An anonymous |
| 971 | // namespace falls through to the generic walk — its contents stay bare, |
| 972 | // matching how call sites spell them. |
| 973 | if (this.language === 'cpp' && nodeType === 'namespace_definition') { |
| 974 | const nameNode = getChildByField(node, 'name'); |
| 975 | const nsName = nameNode ? getNodeText(nameNode, this.source) : ''; |
| 976 | if (nsName) { |
| 977 | this.namespacePrefix.push(nsName); |
| 978 | for (let i = 0; i < node.namedChildCount; i++) { |
| 979 | const child = node.namedChild(i); |
| 980 | if (child) this.visitNode(child); |
| 981 | } |
| 982 | this.namespacePrefix.pop(); |
| 983 | return; |
| 984 | } |
| 985 | } |
| 986 | |
| 987 | // Function-as-value capture (#756) — independent of the dispatch ladder |
| 988 | // below (the captured container types have no other handler there), so it |
| 989 | // can never shadow or be shadowed by an extraction branch. |
| 990 | this.maybeCaptureFnRefs(node, nodeType); |
| 991 | |
| 992 | // Check for function declarations |
| 993 | // For Python/Ruby, function_definition inside a class should be treated as method |
no test coverage detected