* Visit a node and extract information
(node: SyntaxNode)
| 860 | * Visit a node and extract information |
| 861 | */ |
| 862 | private visitNode(node: SyntaxNode): void { |
| 863 | if (!this.extractor) return; |
| 864 | |
| 865 | const nodeType = node.type; |
| 866 | let skipChildren = false; |
| 867 | |
| 868 | // Language-specific custom visitor hook |
| 869 | if (this.extractor.visitNode) { |
| 870 | const ctx = this.makeExtractorContext(); |
| 871 | const handled = this.extractor.visitNode(node, ctx); |
| 872 | if (handled) { |
| 873 | // The hook consumed this subtree, so the walkers below never descend |
| 874 | // into it — scan it for function-as-value candidates (#756). Scala's |
| 875 | // hook handles val/var definitions (`val table = Seq(targetCb)`), for |
| 876 | // example. The scan is capture-only and halts at nested functions. |
| 877 | this.scanFnRefSubtree(node, 0); |
| 878 | return; |
| 879 | } |
| 880 | } |
| 881 | |
| 882 | // Pascal-specific AST handling |
| 883 | if (this.language === 'pascal') { |
| 884 | skipChildren = this.visitPascalNode(node); |
| 885 | if (skipChildren) return; |
| 886 | } |
| 887 | |
| 888 | // Function-as-value capture (#756) — independent of the dispatch ladder |
| 889 | // below (the captured container types have no other handler there), so it |
| 890 | // can never shadow or be shadowed by an extraction branch. |
| 891 | this.maybeCaptureFnRefs(node, nodeType); |
| 892 | |
| 893 | // Check for function declarations |
| 894 | // For Python/Ruby, function_definition inside a class should be treated as method |
| 895 | if (this.extractor.functionTypes.includes(nodeType)) { |
| 896 | if (this.isInsideClassLikeNode() && this.extractor.methodTypes.includes(nodeType)) { |
| 897 | // Inside a class - treat as method |
| 898 | this.extractMethod(node); |
| 899 | skipChildren = true; // extractMethod visits children via visitFunctionBody |
| 900 | } else { |
| 901 | this.extractFunction(node); |
| 902 | skipChildren = true; // extractFunction visits children via visitFunctionBody |
| 903 | } |
| 904 | } |
| 905 | // Check for class declarations |
| 906 | else if (this.extractor.classTypes.includes(nodeType)) { |
| 907 | // Some languages reuse class_declaration for structs/enums (e.g. Swift) |
| 908 | const classification = this.extractor.classifyClassNode?.(node) ?? 'class'; |
| 909 | if (classification === 'struct') { |
| 910 | this.extractStruct(node); |
| 911 | } else if (classification === 'enum') { |
| 912 | this.extractEnum(node); |
| 913 | } else if (classification === 'interface') { |
| 914 | this.extractInterface(node); |
| 915 | } else if (classification === 'trait') { |
| 916 | this.extractClass(node, 'trait'); |
| 917 | } else { |
| 918 | this.extractClass(node); |
| 919 | } |
no test coverage detected