@grafema/core 0.2.5-beta → 0.2.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +61 -23
- package/dist/DiscoveryManager.d.ts +59 -0
- package/dist/DiscoveryManager.d.ts.map +1 -0
- package/dist/DiscoveryManager.js +249 -0
- package/dist/DiscoveryManager.js.map +1 -0
- package/dist/GraphInitializer.d.ts +44 -0
- package/dist/GraphInitializer.d.ts.map +1 -0
- package/dist/GraphInitializer.js +121 -0
- package/dist/GraphInitializer.js.map +1 -0
- package/dist/GuaranteeChecker.d.ts +35 -0
- package/dist/GuaranteeChecker.d.ts.map +1 -0
- package/dist/GuaranteeChecker.js +81 -0
- package/dist/GuaranteeChecker.js.map +1 -0
- package/dist/Orchestrator.d.ts +34 -151
- package/dist/Orchestrator.d.ts.map +1 -1
- package/dist/Orchestrator.js +173 -741
- package/dist/Orchestrator.js.map +1 -1
- package/dist/OrchestratorTypes.d.ts +115 -0
- package/dist/OrchestratorTypes.d.ts.map +1 -0
- package/dist/OrchestratorTypes.js +6 -0
- package/dist/OrchestratorTypes.js.map +1 -0
- package/dist/ParallelAnalysisRunner.d.ts +43 -0
- package/dist/ParallelAnalysisRunner.d.ts.map +1 -0
- package/dist/ParallelAnalysisRunner.js +161 -0
- package/dist/ParallelAnalysisRunner.js.map +1 -0
- package/dist/PhaseRunner.d.ts +94 -0
- package/dist/PhaseRunner.d.ts.map +1 -0
- package/dist/PhaseRunner.js +332 -0
- package/dist/PhaseRunner.js.map +1 -0
- package/dist/config/ConfigLoader.d.ts +51 -1
- package/dist/config/ConfigLoader.d.ts.map +1 -1
- package/dist/config/ConfigLoader.js +121 -1
- package/dist/config/ConfigLoader.js.map +1 -1
- package/dist/config/index.d.ts +1 -1
- package/dist/config/index.d.ts.map +1 -1
- package/dist/config/index.js +1 -1
- package/dist/config/index.js.map +1 -1
- package/dist/core/ASTWorker.d.ts +2 -0
- package/dist/core/ASTWorker.d.ts.map +1 -1
- package/dist/core/ASTWorker.js +6 -2
- package/dist/core/ASTWorker.js.map +1 -1
- package/dist/core/FileOverview.d.ts +124 -0
- package/dist/core/FileOverview.d.ts.map +1 -0
- package/dist/core/FileOverview.js +257 -0
- package/dist/core/FileOverview.js.map +1 -0
- package/dist/core/GraphFreshnessChecker.d.ts +1 -1
- package/dist/core/GraphFreshnessChecker.d.ts.map +1 -1
- package/dist/core/GraphFreshnessChecker.js +7 -5
- package/dist/core/GraphFreshnessChecker.js.map +1 -1
- package/dist/core/GuaranteeManager.d.ts +13 -0
- package/dist/core/GuaranteeManager.d.ts.map +1 -1
- package/dist/core/GuaranteeManager.js +63 -2
- package/dist/core/GuaranteeManager.js.map +1 -1
- package/dist/core/IncrementalReanalyzer.d.ts.map +1 -1
- package/dist/core/IncrementalReanalyzer.js +6 -3
- package/dist/core/IncrementalReanalyzer.js.map +1 -1
- package/dist/core/NodeFactory.d.ts +81 -415
- package/dist/core/NodeFactory.d.ts.map +1 -1
- package/dist/core/NodeFactory.js +139 -336
- package/dist/core/NodeFactory.js.map +1 -1
- package/dist/core/ResourceRegistry.d.ts +17 -0
- package/dist/core/ResourceRegistry.d.ts.map +1 -0
- package/dist/core/ResourceRegistry.js +32 -0
- package/dist/core/ResourceRegistry.js.map +1 -0
- package/dist/core/ScopeTracker.d.ts +12 -0
- package/dist/core/ScopeTracker.d.ts.map +1 -1
- package/dist/core/ScopeTracker.js +24 -0
- package/dist/core/ScopeTracker.js.map +1 -1
- package/dist/core/SemanticId.d.ts +69 -0
- package/dist/core/SemanticId.d.ts.map +1 -1
- package/dist/core/SemanticId.js +134 -0
- package/dist/core/SemanticId.js.map +1 -1
- package/dist/core/brandNodeInternal.d.ts +14 -0
- package/dist/core/brandNodeInternal.d.ts.map +1 -0
- package/dist/core/brandNodeInternal.js +4 -0
- package/dist/core/brandNodeInternal.js.map +1 -0
- package/dist/core/buildDependencyGraph.d.ts +36 -0
- package/dist/core/buildDependencyGraph.d.ts.map +1 -0
- package/dist/core/buildDependencyGraph.js +78 -0
- package/dist/core/buildDependencyGraph.js.map +1 -0
- package/dist/core/factories/CoreFactory.d.ts +223 -0
- package/dist/core/factories/CoreFactory.d.ts.map +1 -0
- package/dist/core/factories/CoreFactory.js +127 -0
- package/dist/core/factories/CoreFactory.js.map +1 -0
- package/dist/core/factories/DatabaseFactory.d.ts +29 -0
- package/dist/core/factories/DatabaseFactory.d.ts.map +1 -0
- package/dist/core/factories/DatabaseFactory.js +25 -0
- package/dist/core/factories/DatabaseFactory.js.map +1 -0
- package/dist/core/factories/ExternalFactory.d.ts +11 -0
- package/dist/core/factories/ExternalFactory.d.ts.map +1 -0
- package/dist/core/factories/ExternalFactory.js +16 -0
- package/dist/core/factories/ExternalFactory.js.map +1 -0
- package/dist/core/factories/HttpFactory.d.ts +22 -0
- package/dist/core/factories/HttpFactory.d.ts.map +1 -0
- package/dist/core/factories/HttpFactory.js +32 -0
- package/dist/core/factories/HttpFactory.js.map +1 -0
- package/dist/core/factories/ReactFactory.d.ts +14 -0
- package/dist/core/factories/ReactFactory.d.ts.map +1 -0
- package/dist/core/factories/ReactFactory.js +13 -0
- package/dist/core/factories/ReactFactory.js.map +1 -0
- package/dist/core/factories/RustFactory.d.ts +62 -0
- package/dist/core/factories/RustFactory.d.ts.map +1 -0
- package/dist/core/factories/RustFactory.js +32 -0
- package/dist/core/factories/RustFactory.js.map +1 -0
- package/dist/core/factories/ServiceFactory.d.ts +12 -0
- package/dist/core/factories/ServiceFactory.d.ts.map +1 -0
- package/dist/core/factories/ServiceFactory.js +22 -0
- package/dist/core/factories/ServiceFactory.js.map +1 -0
- package/dist/core/factories/SocketFactory.d.ts +31 -0
- package/dist/core/factories/SocketFactory.d.ts.map +1 -0
- package/dist/core/factories/SocketFactory.js +35 -0
- package/dist/core/factories/SocketFactory.js.map +1 -0
- package/dist/core/nodes/DatabaseNode.d.ts +85 -0
- package/dist/core/nodes/DatabaseNode.d.ts.map +1 -0
- package/dist/core/nodes/DatabaseNode.js +118 -0
- package/dist/core/nodes/DatabaseNode.js.map +1 -0
- package/dist/core/nodes/ExpressMiddlewareNode.d.ts +47 -0
- package/dist/core/nodes/ExpressMiddlewareNode.d.ts.map +1 -0
- package/dist/core/nodes/ExpressMiddlewareNode.js +63 -0
- package/dist/core/nodes/ExpressMiddlewareNode.js.map +1 -0
- package/dist/core/nodes/ExpressMountNode.d.ts +44 -0
- package/dist/core/nodes/ExpressMountNode.d.ts.map +1 -0
- package/dist/core/nodes/ExpressMountNode.js +61 -0
- package/dist/core/nodes/ExpressMountNode.js.map +1 -0
- package/dist/core/nodes/ExternalApiNode.d.ts +29 -0
- package/dist/core/nodes/ExternalApiNode.d.ts.map +1 -0
- package/dist/core/nodes/ExternalApiNode.js +41 -0
- package/dist/core/nodes/ExternalApiNode.js.map +1 -0
- package/dist/core/nodes/ExternalFunctionNode.d.ts +40 -0
- package/dist/core/nodes/ExternalFunctionNode.d.ts.map +1 -0
- package/dist/core/nodes/ExternalFunctionNode.js +54 -0
- package/dist/core/nodes/ExternalFunctionNode.js.map +1 -0
- package/dist/core/nodes/FetchRequestNode.d.ts +54 -0
- package/dist/core/nodes/FetchRequestNode.d.ts.map +1 -0
- package/dist/core/nodes/FetchRequestNode.js +67 -0
- package/dist/core/nodes/FetchRequestNode.js.map +1 -0
- package/dist/core/nodes/HttpRouteNode.d.ts +58 -0
- package/dist/core/nodes/HttpRouteNode.d.ts.map +1 -0
- package/dist/core/nodes/HttpRouteNode.js +72 -0
- package/dist/core/nodes/HttpRouteNode.js.map +1 -0
- package/dist/core/nodes/NodeKind.d.ts +1 -0
- package/dist/core/nodes/NodeKind.d.ts.map +1 -1
- package/dist/core/nodes/NodeKind.js +1 -0
- package/dist/core/nodes/NodeKind.js.map +1 -1
- package/dist/core/nodes/ReactNode.d.ts +53 -0
- package/dist/core/nodes/ReactNode.d.ts.map +1 -0
- package/dist/core/nodes/ReactNode.js +70 -0
- package/dist/core/nodes/ReactNode.js.map +1 -0
- package/dist/core/nodes/RustCallNode.d.ts +46 -0
- package/dist/core/nodes/RustCallNode.d.ts.map +1 -0
- package/dist/core/nodes/RustCallNode.js +62 -0
- package/dist/core/nodes/RustCallNode.js.map +1 -0
- package/dist/core/nodes/RustFunctionNode.d.ts +58 -0
- package/dist/core/nodes/RustFunctionNode.d.ts.map +1 -0
- package/dist/core/nodes/RustFunctionNode.js +67 -0
- package/dist/core/nodes/RustFunctionNode.js.map +1 -0
- package/dist/core/nodes/RustImplNode.d.ts +35 -0
- package/dist/core/nodes/RustImplNode.d.ts.map +1 -0
- package/dist/core/nodes/RustImplNode.js +55 -0
- package/dist/core/nodes/RustImplNode.js.map +1 -0
- package/dist/core/nodes/RustMethodNode.d.ts +64 -0
- package/dist/core/nodes/RustMethodNode.d.ts.map +1 -0
- package/dist/core/nodes/RustMethodNode.js +76 -0
- package/dist/core/nodes/RustMethodNode.js.map +1 -0
- package/dist/core/nodes/RustModuleNode.d.ts +40 -0
- package/dist/core/nodes/RustModuleNode.d.ts.map +1 -0
- package/dist/core/nodes/RustModuleNode.js +57 -0
- package/dist/core/nodes/RustModuleNode.js.map +1 -0
- package/dist/core/nodes/RustStructNode.d.ts +38 -0
- package/dist/core/nodes/RustStructNode.d.ts.map +1 -0
- package/dist/core/nodes/RustStructNode.js +54 -0
- package/dist/core/nodes/RustStructNode.js.map +1 -0
- package/dist/core/nodes/RustTraitNode.d.ts +40 -0
- package/dist/core/nodes/RustTraitNode.d.ts.map +1 -0
- package/dist/core/nodes/RustTraitNode.js +52 -0
- package/dist/core/nodes/RustTraitNode.js.map +1 -0
- package/dist/core/nodes/ServiceLayerNode.d.ts +85 -0
- package/dist/core/nodes/ServiceLayerNode.d.ts.map +1 -0
- package/dist/core/nodes/ServiceLayerNode.js +122 -0
- package/dist/core/nodes/ServiceLayerNode.js.map +1 -0
- package/dist/core/nodes/SocketIONode.d.ts +71 -0
- package/dist/core/nodes/SocketIONode.d.ts.map +1 -0
- package/dist/core/nodes/SocketIONode.js +111 -0
- package/dist/core/nodes/SocketIONode.js.map +1 -0
- package/dist/core/nodes/SocketNode.d.ts +87 -0
- package/dist/core/nodes/SocketNode.d.ts.map +1 -0
- package/dist/core/nodes/SocketNode.js +124 -0
- package/dist/core/nodes/SocketNode.js.map +1 -0
- package/dist/core/nodes/TypeNode.d.ts +26 -1
- package/dist/core/nodes/TypeNode.d.ts.map +1 -1
- package/dist/core/nodes/TypeNode.js +21 -3
- package/dist/core/nodes/TypeNode.js.map +1 -1
- package/dist/core/nodes/TypeParameterNode.d.ts +44 -0
- package/dist/core/nodes/TypeParameterNode.d.ts.map +1 -0
- package/dist/core/nodes/TypeParameterNode.js +64 -0
- package/dist/core/nodes/TypeParameterNode.js.map +1 -0
- package/dist/core/nodes/index.d.ts +19 -0
- package/dist/core/nodes/index.d.ts.map +1 -1
- package/dist/core/nodes/index.js +26 -0
- package/dist/core/nodes/index.js.map +1 -1
- package/dist/index.d.ts +33 -6
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +30 -3
- package/dist/index.js.map +1 -1
- package/dist/plugins/InfraAnalyzer.d.ts +110 -0
- package/dist/plugins/InfraAnalyzer.d.ts.map +1 -0
- package/dist/plugins/InfraAnalyzer.js +170 -0
- package/dist/plugins/InfraAnalyzer.js.map +1 -0
- package/dist/plugins/analysis/DatabaseAnalyzer.d.ts.map +1 -1
- package/dist/plugins/analysis/DatabaseAnalyzer.js +18 -15
- package/dist/plugins/analysis/DatabaseAnalyzer.js.map +1 -1
- package/dist/plugins/analysis/ExpressAnalyzer.d.ts.map +1 -1
- package/dist/plugins/analysis/ExpressAnalyzer.js +27 -26
- package/dist/plugins/analysis/ExpressAnalyzer.js.map +1 -1
- package/dist/plugins/analysis/ExpressResponseAnalyzer.d.ts.map +1 -1
- package/dist/plugins/analysis/ExpressResponseAnalyzer.js +5 -3
- package/dist/plugins/analysis/ExpressResponseAnalyzer.js.map +1 -1
- package/dist/plugins/analysis/ExpressRouteAnalyzer.d.ts.map +1 -1
- package/dist/plugins/analysis/ExpressRouteAnalyzer.js +36 -39
- package/dist/plugins/analysis/ExpressRouteAnalyzer.js.map +1 -1
- package/dist/plugins/analysis/FetchAnalyzer.d.ts.map +1 -1
- package/dist/plugins/analysis/FetchAnalyzer.js +23 -39
- package/dist/plugins/analysis/FetchAnalyzer.js.map +1 -1
- package/dist/plugins/analysis/IncrementalAnalysisPlugin.d.ts.map +1 -1
- package/dist/plugins/analysis/IncrementalAnalysisPlugin.js +3 -2
- package/dist/plugins/analysis/IncrementalAnalysisPlugin.js.map +1 -1
- package/dist/plugins/analysis/JSASTAnalyzer.d.ts +23 -85
- package/dist/plugins/analysis/JSASTAnalyzer.d.ts.map +1 -1
- package/dist/plugins/analysis/JSASTAnalyzer.js +351 -1887
- package/dist/plugins/analysis/JSASTAnalyzer.js.map +1 -1
- package/dist/plugins/analysis/NestJSRouteAnalyzer.d.ts +28 -0
- package/dist/plugins/analysis/NestJSRouteAnalyzer.d.ts.map +1 -0
- package/dist/plugins/analysis/NestJSRouteAnalyzer.js +196 -0
- package/dist/plugins/analysis/NestJSRouteAnalyzer.js.map +1 -0
- package/dist/plugins/analysis/ReactAnalyzer.d.ts +1 -61
- package/dist/plugins/analysis/ReactAnalyzer.d.ts.map +1 -1
- package/dist/plugins/analysis/ReactAnalyzer.js +24 -915
- package/dist/plugins/analysis/ReactAnalyzer.js.map +1 -1
- package/dist/plugins/analysis/RustAnalyzer.d.ts.map +1 -1
- package/dist/plugins/analysis/RustAnalyzer.js +31 -66
- package/dist/plugins/analysis/RustAnalyzer.js.map +1 -1
- package/dist/plugins/analysis/SQLiteAnalyzer.d.ts.map +1 -1
- package/dist/plugins/analysis/SQLiteAnalyzer.js +13 -6
- package/dist/plugins/analysis/SQLiteAnalyzer.js.map +1 -1
- package/dist/plugins/analysis/ServiceLayerAnalyzer.d.ts.map +1 -1
- package/dist/plugins/analysis/ServiceLayerAnalyzer.js +10 -7
- package/dist/plugins/analysis/ServiceLayerAnalyzer.js.map +1 -1
- package/dist/plugins/analysis/SocketAnalyzer.d.ts +81 -0
- package/dist/plugins/analysis/SocketAnalyzer.d.ts.map +1 -0
- package/dist/plugins/analysis/SocketAnalyzer.js +475 -0
- package/dist/plugins/analysis/SocketAnalyzer.js.map +1 -0
- package/dist/plugins/analysis/SocketIOAnalyzer.d.ts.map +1 -1
- package/dist/plugins/analysis/SocketIOAnalyzer.js +13 -18
- package/dist/plugins/analysis/SocketIOAnalyzer.js.map +1 -1
- package/dist/plugins/analysis/SystemDbAnalyzer.d.ts.map +1 -1
- package/dist/plugins/analysis/SystemDbAnalyzer.js +8 -5
- package/dist/plugins/analysis/SystemDbAnalyzer.js.map +1 -1
- package/dist/plugins/analysis/ast/CollisionResolver.d.ts +65 -0
- package/dist/plugins/analysis/ast/CollisionResolver.d.ts.map +1 -0
- package/dist/plugins/analysis/ast/CollisionResolver.js +106 -0
- package/dist/plugins/analysis/ast/CollisionResolver.js.map +1 -0
- package/dist/plugins/analysis/ast/FunctionBodyContext.d.ts +124 -0
- package/dist/plugins/analysis/ast/FunctionBodyContext.d.ts.map +1 -0
- package/dist/plugins/analysis/ast/FunctionBodyContext.js +151 -0
- package/dist/plugins/analysis/ast/FunctionBodyContext.js.map +1 -0
- package/dist/plugins/analysis/ast/GraphBuilder.d.ts +26 -261
- package/dist/plugins/analysis/ast/GraphBuilder.d.ts.map +1 -1
- package/dist/plugins/analysis/ast/GraphBuilder.js +251 -2494
- package/dist/plugins/analysis/ast/GraphBuilder.js.map +1 -1
- package/dist/plugins/analysis/ast/IdGenerator.d.ts +42 -0
- package/dist/plugins/analysis/ast/IdGenerator.d.ts.map +1 -1
- package/dist/plugins/analysis/ast/IdGenerator.js +61 -1
- package/dist/plugins/analysis/ast/IdGenerator.js.map +1 -1
- package/dist/plugins/analysis/ast/builders/AssignmentBuilder.d.ts +15 -0
- package/dist/plugins/analysis/ast/builders/AssignmentBuilder.d.ts.map +1 -0
- package/dist/plugins/analysis/ast/builders/AssignmentBuilder.js +274 -0
- package/dist/plugins/analysis/ast/builders/AssignmentBuilder.js.map +1 -0
- package/dist/plugins/analysis/ast/builders/CallFlowBuilder.d.ts +22 -0
- package/dist/plugins/analysis/ast/builders/CallFlowBuilder.d.ts.map +1 -0
- package/dist/plugins/analysis/ast/builders/CallFlowBuilder.js +178 -0
- package/dist/plugins/analysis/ast/builders/CallFlowBuilder.js.map +1 -0
- package/dist/plugins/analysis/ast/builders/ControlFlowBuilder.d.ts +76 -0
- package/dist/plugins/analysis/ast/builders/ControlFlowBuilder.d.ts.map +1 -0
- package/dist/plugins/analysis/ast/builders/ControlFlowBuilder.js +387 -0
- package/dist/plugins/analysis/ast/builders/ControlFlowBuilder.js.map +1 -0
- package/dist/plugins/analysis/ast/builders/CoreBuilder.d.ts +38 -0
- package/dist/plugins/analysis/ast/builders/CoreBuilder.d.ts.map +1 -0
- package/dist/plugins/analysis/ast/builders/CoreBuilder.js +240 -0
- package/dist/plugins/analysis/ast/builders/CoreBuilder.js.map +1 -0
- package/dist/plugins/analysis/ast/builders/ModuleRuntimeBuilder.d.ts +53 -0
- package/dist/plugins/analysis/ast/builders/ModuleRuntimeBuilder.d.ts.map +1 -0
- package/dist/plugins/analysis/ast/builders/ModuleRuntimeBuilder.js +355 -0
- package/dist/plugins/analysis/ast/builders/ModuleRuntimeBuilder.js.map +1 -0
- package/dist/plugins/analysis/ast/builders/MutationBuilder.d.ts +46 -0
- package/dist/plugins/analysis/ast/builders/MutationBuilder.d.ts.map +1 -0
- package/dist/plugins/analysis/ast/builders/MutationBuilder.js +264 -0
- package/dist/plugins/analysis/ast/builders/MutationBuilder.js.map +1 -0
- package/dist/plugins/analysis/ast/builders/ReturnBuilder.d.ts +23 -0
- package/dist/plugins/analysis/ast/builders/ReturnBuilder.d.ts.map +1 -0
- package/dist/plugins/analysis/ast/builders/ReturnBuilder.js +206 -0
- package/dist/plugins/analysis/ast/builders/ReturnBuilder.js.map +1 -0
- package/dist/plugins/analysis/ast/builders/TypeSystemBuilder.d.ts +64 -0
- package/dist/plugins/analysis/ast/builders/TypeSystemBuilder.d.ts.map +1 -0
- package/dist/plugins/analysis/ast/builders/TypeSystemBuilder.js +370 -0
- package/dist/plugins/analysis/ast/builders/TypeSystemBuilder.js.map +1 -0
- package/dist/plugins/analysis/ast/builders/UpdateExpressionBuilder.d.ts +46 -0
- package/dist/plugins/analysis/ast/builders/UpdateExpressionBuilder.d.ts.map +1 -0
- package/dist/plugins/analysis/ast/builders/UpdateExpressionBuilder.js +191 -0
- package/dist/plugins/analysis/ast/builders/UpdateExpressionBuilder.js.map +1 -0
- package/dist/plugins/analysis/ast/builders/YieldBuilder.d.ts +30 -0
- package/dist/plugins/analysis/ast/builders/YieldBuilder.d.ts.map +1 -0
- package/dist/plugins/analysis/ast/builders/YieldBuilder.js +214 -0
- package/dist/plugins/analysis/ast/builders/YieldBuilder.js.map +1 -0
- package/dist/plugins/analysis/ast/builders/index.d.ts +12 -0
- package/dist/plugins/analysis/ast/builders/index.d.ts.map +1 -0
- package/dist/plugins/analysis/ast/builders/index.js +11 -0
- package/dist/plugins/analysis/ast/builders/index.js.map +1 -0
- package/dist/plugins/analysis/ast/builders/types.d.ts +30 -0
- package/dist/plugins/analysis/ast/builders/types.d.ts.map +1 -0
- package/dist/plugins/analysis/ast/builders/types.js +8 -0
- package/dist/plugins/analysis/ast/builders/types.js.map +1 -0
- package/dist/plugins/analysis/ast/handlers/AnalyzerDelegate.d.ts +50 -0
- package/dist/plugins/analysis/ast/handlers/AnalyzerDelegate.d.ts.map +1 -0
- package/dist/plugins/analysis/ast/handlers/AnalyzerDelegate.js +2 -0
- package/dist/plugins/analysis/ast/handlers/AnalyzerDelegate.js.map +1 -0
- package/dist/plugins/analysis/ast/handlers/BranchHandler.d.ts +18 -0
- package/dist/plugins/analysis/ast/handlers/BranchHandler.d.ts.map +1 -0
- package/dist/plugins/analysis/ast/handlers/BranchHandler.js +244 -0
- package/dist/plugins/analysis/ast/handlers/BranchHandler.js.map +1 -0
- package/dist/plugins/analysis/ast/handlers/CallExpressionHandler.d.ts +7 -0
- package/dist/plugins/analysis/ast/handlers/CallExpressionHandler.d.ts.map +1 -0
- package/dist/plugins/analysis/ast/handlers/CallExpressionHandler.js +295 -0
- package/dist/plugins/analysis/ast/handlers/CallExpressionHandler.js.map +1 -0
- package/dist/plugins/analysis/ast/handlers/FunctionBodyHandler.d.ts +22 -0
- package/dist/plugins/analysis/ast/handlers/FunctionBodyHandler.d.ts.map +1 -0
- package/dist/plugins/analysis/ast/handlers/FunctionBodyHandler.js +9 -0
- package/dist/plugins/analysis/ast/handlers/FunctionBodyHandler.js.map +1 -0
- package/dist/plugins/analysis/ast/handlers/LoopHandler.d.ts +13 -0
- package/dist/plugins/analysis/ast/handlers/LoopHandler.d.ts.map +1 -0
- package/dist/plugins/analysis/ast/handlers/LoopHandler.js +207 -0
- package/dist/plugins/analysis/ast/handlers/LoopHandler.js.map +1 -0
- package/dist/plugins/analysis/ast/handlers/NestedFunctionHandler.d.ts +13 -0
- package/dist/plugins/analysis/ast/handlers/NestedFunctionHandler.d.ts.map +1 -0
- package/dist/plugins/analysis/ast/handlers/NestedFunctionHandler.js +174 -0
- package/dist/plugins/analysis/ast/handlers/NestedFunctionHandler.js.map +1 -0
- package/dist/plugins/analysis/ast/handlers/NewExpressionHandler.d.ts +12 -0
- package/dist/plugins/analysis/ast/handlers/NewExpressionHandler.d.ts.map +1 -0
- package/dist/plugins/analysis/ast/handlers/NewExpressionHandler.js +135 -0
- package/dist/plugins/analysis/ast/handlers/NewExpressionHandler.js.map +1 -0
- package/dist/plugins/analysis/ast/handlers/PropertyAccessHandler.d.ts +13 -0
- package/dist/plugins/analysis/ast/handlers/PropertyAccessHandler.d.ts.map +1 -0
- package/dist/plugins/analysis/ast/handlers/PropertyAccessHandler.js +71 -0
- package/dist/plugins/analysis/ast/handlers/PropertyAccessHandler.js.map +1 -0
- package/dist/plugins/analysis/ast/handlers/ReturnYieldHandler.d.ts +12 -0
- package/dist/plugins/analysis/ast/handlers/ReturnYieldHandler.d.ts.map +1 -0
- package/dist/plugins/analysis/ast/handlers/ReturnYieldHandler.js +135 -0
- package/dist/plugins/analysis/ast/handlers/ReturnYieldHandler.js.map +1 -0
- package/dist/plugins/analysis/ast/handlers/ThrowHandler.d.ts +12 -0
- package/dist/plugins/analysis/ast/handlers/ThrowHandler.d.ts.map +1 -0
- package/dist/plugins/analysis/ast/handlers/ThrowHandler.js +82 -0
- package/dist/plugins/analysis/ast/handlers/ThrowHandler.js.map +1 -0
- package/dist/plugins/analysis/ast/handlers/TryCatchHandler.d.ts +14 -0
- package/dist/plugins/analysis/ast/handlers/TryCatchHandler.d.ts.map +1 -0
- package/dist/plugins/analysis/ast/handlers/TryCatchHandler.js +220 -0
- package/dist/plugins/analysis/ast/handlers/TryCatchHandler.js.map +1 -0
- package/dist/plugins/analysis/ast/handlers/VariableHandler.d.ts +12 -0
- package/dist/plugins/analysis/ast/handlers/VariableHandler.d.ts.map +1 -0
- package/dist/plugins/analysis/ast/handlers/VariableHandler.js +57 -0
- package/dist/plugins/analysis/ast/handlers/VariableHandler.js.map +1 -0
- package/dist/plugins/analysis/ast/handlers/index.d.ts +13 -0
- package/dist/plugins/analysis/ast/handlers/index.d.ts.map +1 -0
- package/dist/plugins/analysis/ast/handlers/index.js +12 -0
- package/dist/plugins/analysis/ast/handlers/index.js.map +1 -0
- package/dist/plugins/analysis/ast/types.d.ts +57 -6
- package/dist/plugins/analysis/ast/types.d.ts.map +1 -1
- package/dist/plugins/analysis/ast/utils/createParameterNodes.d.ts +5 -4
- package/dist/plugins/analysis/ast/utils/createParameterNodes.d.ts.map +1 -1
- package/dist/plugins/analysis/ast/utils/createParameterNodes.js +94 -13
- package/dist/plugins/analysis/ast/utils/createParameterNodes.js.map +1 -1
- package/dist/plugins/analysis/ast/utils/extractNamesFromPattern.d.ts +81 -0
- package/dist/plugins/analysis/ast/utils/extractNamesFromPattern.d.ts.map +1 -0
- package/dist/plugins/analysis/ast/utils/extractNamesFromPattern.js +140 -0
- package/dist/plugins/analysis/ast/utils/extractNamesFromPattern.js.map +1 -0
- package/dist/plugins/analysis/ast/utils/getExpressionValue.d.ts +22 -0
- package/dist/plugins/analysis/ast/utils/getExpressionValue.d.ts.map +1 -0
- package/dist/plugins/analysis/ast/utils/getExpressionValue.js +35 -0
- package/dist/plugins/analysis/ast/utils/getExpressionValue.js.map +1 -0
- package/dist/plugins/analysis/ast/utils/getMemberExpressionName.d.ts +25 -0
- package/dist/plugins/analysis/ast/utils/getMemberExpressionName.d.ts.map +1 -0
- package/dist/plugins/analysis/ast/utils/getMemberExpressionName.js +21 -0
- package/dist/plugins/analysis/ast/utils/getMemberExpressionName.js.map +1 -0
- package/dist/plugins/analysis/ast/utils/index.d.ts +2 -0
- package/dist/plugins/analysis/ast/utils/index.d.ts.map +1 -1
- package/dist/plugins/analysis/ast/utils/index.js +2 -0
- package/dist/plugins/analysis/ast/utils/index.js.map +1 -1
- package/dist/plugins/analysis/ast/visitors/ArgumentExtractor.d.ts +23 -0
- package/dist/plugins/analysis/ast/visitors/ArgumentExtractor.d.ts.map +1 -0
- package/dist/plugins/analysis/ast/visitors/ArgumentExtractor.js +241 -0
- package/dist/plugins/analysis/ast/visitors/ArgumentExtractor.js.map +1 -0
- package/dist/plugins/analysis/ast/visitors/ArrayElementExtractor.d.ts +20 -0
- package/dist/plugins/analysis/ast/visitors/ArrayElementExtractor.d.ts.map +1 -0
- package/dist/plugins/analysis/ast/visitors/ArrayElementExtractor.js +110 -0
- package/dist/plugins/analysis/ast/visitors/ArrayElementExtractor.js.map +1 -0
- package/dist/plugins/analysis/ast/visitors/CallExpressionVisitor.d.ts +15 -142
- package/dist/plugins/analysis/ast/visitors/CallExpressionVisitor.d.ts.map +1 -1
- package/dist/plugins/analysis/ast/visitors/CallExpressionVisitor.js +304 -937
- package/dist/plugins/analysis/ast/visitors/CallExpressionVisitor.js.map +1 -1
- package/dist/plugins/analysis/ast/visitors/ClassVisitor.d.ts.map +1 -1
- package/dist/plugins/analysis/ast/visitors/ClassVisitor.js +26 -11
- package/dist/plugins/analysis/ast/visitors/ClassVisitor.js.map +1 -1
- package/dist/plugins/analysis/ast/visitors/FunctionVisitor.d.ts.map +1 -1
- package/dist/plugins/analysis/ast/visitors/FunctionVisitor.js +21 -6
- package/dist/plugins/analysis/ast/visitors/FunctionVisitor.js.map +1 -1
- package/dist/plugins/analysis/ast/visitors/MutationDetector.d.ts +25 -0
- package/dist/plugins/analysis/ast/visitors/MutationDetector.d.ts.map +1 -0
- package/dist/plugins/analysis/ast/visitors/MutationDetector.js +181 -0
- package/dist/plugins/analysis/ast/visitors/MutationDetector.js.map +1 -0
- package/dist/plugins/analysis/ast/visitors/ObjectPropertyExtractor.d.ts +20 -0
- package/dist/plugins/analysis/ast/visitors/ObjectPropertyExtractor.d.ts.map +1 -0
- package/dist/plugins/analysis/ast/visitors/ObjectPropertyExtractor.js +155 -0
- package/dist/plugins/analysis/ast/visitors/ObjectPropertyExtractor.js.map +1 -0
- package/dist/plugins/analysis/ast/visitors/PropertyAccessVisitor.d.ts +9 -1
- package/dist/plugins/analysis/ast/visitors/PropertyAccessVisitor.d.ts.map +1 -1
- package/dist/plugins/analysis/ast/visitors/PropertyAccessVisitor.js +51 -3
- package/dist/plugins/analysis/ast/visitors/PropertyAccessVisitor.js.map +1 -1
- package/dist/plugins/analysis/ast/visitors/TypeScriptVisitor.d.ts +20 -0
- package/dist/plugins/analysis/ast/visitors/TypeScriptVisitor.d.ts.map +1 -1
- package/dist/plugins/analysis/ast/visitors/TypeScriptVisitor.js +188 -12
- package/dist/plugins/analysis/ast/visitors/TypeScriptVisitor.js.map +1 -1
- package/dist/plugins/analysis/ast/visitors/VariableVisitor.d.ts.map +1 -1
- package/dist/plugins/analysis/ast/visitors/VariableVisitor.js +6 -4
- package/dist/plugins/analysis/ast/visitors/VariableVisitor.js.map +1 -1
- package/dist/plugins/analysis/ast/visitors/call-expression-helpers.d.ts +19 -0
- package/dist/plugins/analysis/ast/visitors/call-expression-helpers.d.ts.map +1 -0
- package/dist/plugins/analysis/ast/visitors/call-expression-helpers.js +57 -0
- package/dist/plugins/analysis/ast/visitors/call-expression-helpers.js.map +1 -0
- package/dist/plugins/analysis/ast/visitors/call-expression-types.d.ts +168 -0
- package/dist/plugins/analysis/ast/visitors/call-expression-types.d.ts.map +1 -0
- package/dist/plugins/analysis/ast/visitors/call-expression-types.js +7 -0
- package/dist/plugins/analysis/ast/visitors/call-expression-types.js.map +1 -0
- package/dist/plugins/analysis/react-internal/browser-api.d.ts +20 -0
- package/dist/plugins/analysis/react-internal/browser-api.d.ts.map +1 -0
- package/dist/plugins/analysis/react-internal/browser-api.js +140 -0
- package/dist/plugins/analysis/react-internal/browser-api.js.map +1 -0
- package/dist/plugins/analysis/react-internal/hooks.d.ts +31 -0
- package/dist/plugins/analysis/react-internal/hooks.d.ts.map +1 -0
- package/dist/plugins/analysis/react-internal/hooks.js +465 -0
- package/dist/plugins/analysis/react-internal/hooks.js.map +1 -0
- package/dist/plugins/analysis/react-internal/jsx.d.ts +43 -0
- package/dist/plugins/analysis/react-internal/jsx.d.ts.map +1 -0
- package/dist/plugins/analysis/react-internal/jsx.js +231 -0
- package/dist/plugins/analysis/react-internal/jsx.js.map +1 -0
- package/dist/plugins/analysis/react-internal/types.d.ts +116 -0
- package/dist/plugins/analysis/react-internal/types.d.ts.map +1 -0
- package/dist/plugins/analysis/react-internal/types.js +83 -0
- package/dist/plugins/analysis/react-internal/types.js.map +1 -0
- package/dist/plugins/discovery/MonorepoServiceDiscovery.d.ts.map +1 -1
- package/dist/plugins/discovery/MonorepoServiceDiscovery.js +6 -13
- package/dist/plugins/discovery/MonorepoServiceDiscovery.js.map +1 -1
- package/dist/plugins/enrichment/AliasTracker.d.ts.map +1 -1
- package/dist/plugins/enrichment/AliasTracker.js +3 -1
- package/dist/plugins/enrichment/AliasTracker.js.map +1 -1
- package/dist/plugins/enrichment/ArgumentParameterLinker.d.ts.map +1 -1
- package/dist/plugins/enrichment/ArgumentParameterLinker.js +3 -1
- package/dist/plugins/enrichment/ArgumentParameterLinker.js.map +1 -1
- package/dist/plugins/enrichment/CallbackCallResolver.d.ts +42 -0
- package/dist/plugins/enrichment/CallbackCallResolver.d.ts.map +1 -0
- package/dist/plugins/enrichment/CallbackCallResolver.js +311 -0
- package/dist/plugins/enrichment/CallbackCallResolver.js.map +1 -0
- package/dist/plugins/enrichment/ClosureCaptureEnricher.d.ts.map +1 -1
- package/dist/plugins/enrichment/ClosureCaptureEnricher.js +3 -1
- package/dist/plugins/enrichment/ClosureCaptureEnricher.js.map +1 -1
- package/dist/plugins/enrichment/ConfigRoutingMapBuilder.d.ts +17 -0
- package/dist/plugins/enrichment/ConfigRoutingMapBuilder.d.ts.map +1 -0
- package/dist/plugins/enrichment/ConfigRoutingMapBuilder.js +55 -0
- package/dist/plugins/enrichment/ConfigRoutingMapBuilder.js.map +1 -0
- package/dist/plugins/enrichment/ExpressHandlerLinker.d.ts.map +1 -1
- package/dist/plugins/enrichment/ExpressHandlerLinker.js +3 -1
- package/dist/plugins/enrichment/ExpressHandlerLinker.js.map +1 -1
- package/dist/plugins/enrichment/ExternalCallResolver.d.ts.map +1 -1
- package/dist/plugins/enrichment/ExternalCallResolver.js +5 -8
- package/dist/plugins/enrichment/ExternalCallResolver.js.map +1 -1
- package/dist/plugins/enrichment/FunctionCallResolver.d.ts.map +1 -1
- package/dist/plugins/enrichment/FunctionCallResolver.js +6 -9
- package/dist/plugins/enrichment/FunctionCallResolver.js.map +1 -1
- package/dist/plugins/enrichment/HTTPConnectionEnricher.d.ts.map +1 -1
- package/dist/plugins/enrichment/HTTPConnectionEnricher.js +3 -1
- package/dist/plugins/enrichment/HTTPConnectionEnricher.js.map +1 -1
- package/dist/plugins/enrichment/ImportExportLinker.d.ts.map +1 -1
- package/dist/plugins/enrichment/ImportExportLinker.js +5 -3
- package/dist/plugins/enrichment/ImportExportLinker.js.map +1 -1
- package/dist/plugins/enrichment/InstanceOfResolver.d.ts.map +1 -1
- package/dist/plugins/enrichment/InstanceOfResolver.js +3 -1
- package/dist/plugins/enrichment/InstanceOfResolver.js.map +1 -1
- package/dist/plugins/enrichment/MethodCallResolver.d.ts +17 -68
- package/dist/plugins/enrichment/MethodCallResolver.d.ts.map +1 -1
- package/dist/plugins/enrichment/MethodCallResolver.js +42 -517
- package/dist/plugins/enrichment/MethodCallResolver.js.map +1 -1
- package/dist/plugins/enrichment/MountPointResolver.d.ts.map +1 -1
- package/dist/plugins/enrichment/MountPointResolver.js +9 -2
- package/dist/plugins/enrichment/MountPointResolver.js.map +1 -1
- package/dist/plugins/enrichment/NodejsBuiltinsResolver.d.ts.map +1 -1
- package/dist/plugins/enrichment/NodejsBuiltinsResolver.js +7 -16
- package/dist/plugins/enrichment/NodejsBuiltinsResolver.js.map +1 -1
- package/dist/plugins/enrichment/PrefixEvaluator.d.ts.map +1 -1
- package/dist/plugins/enrichment/PrefixEvaluator.js +6 -2
- package/dist/plugins/enrichment/PrefixEvaluator.js.map +1 -1
- package/dist/plugins/enrichment/RejectionPropagationEnricher.d.ts.map +1 -1
- package/dist/plugins/enrichment/RejectionPropagationEnricher.js +3 -1
- package/dist/plugins/enrichment/RejectionPropagationEnricher.js.map +1 -1
- package/dist/plugins/enrichment/RustFFIEnricher.d.ts.map +1 -1
- package/dist/plugins/enrichment/RustFFIEnricher.js +3 -1
- package/dist/plugins/enrichment/RustFFIEnricher.js.map +1 -1
- package/dist/plugins/enrichment/ServiceConnectionEnricher.d.ts +76 -0
- package/dist/plugins/enrichment/ServiceConnectionEnricher.d.ts.map +1 -0
- package/dist/plugins/enrichment/ServiceConnectionEnricher.js +355 -0
- package/dist/plugins/enrichment/ServiceConnectionEnricher.js.map +1 -0
- package/dist/plugins/enrichment/SocketConnectionEnricher.d.ts +42 -0
- package/dist/plugins/enrichment/SocketConnectionEnricher.d.ts.map +1 -0
- package/dist/plugins/enrichment/SocketConnectionEnricher.js +166 -0
- package/dist/plugins/enrichment/SocketConnectionEnricher.js.map +1 -0
- package/dist/plugins/enrichment/ValueDomainAnalyzer.d.ts.map +1 -1
- package/dist/plugins/enrichment/ValueDomainAnalyzer.js +3 -1
- package/dist/plugins/enrichment/ValueDomainAnalyzer.js.map +1 -1
- package/dist/plugins/enrichment/method-call/MethodCallData.d.ts +68 -0
- package/dist/plugins/enrichment/method-call/MethodCallData.d.ts.map +1 -0
- package/dist/plugins/enrichment/method-call/MethodCallData.js +227 -0
- package/dist/plugins/enrichment/method-call/MethodCallData.js.map +1 -0
- package/dist/plugins/enrichment/method-call/MethodCallDetectors.d.ts +21 -0
- package/dist/plugins/enrichment/method-call/MethodCallDetectors.d.ts.map +1 -0
- package/dist/plugins/enrichment/method-call/MethodCallDetectors.js +52 -0
- package/dist/plugins/enrichment/method-call/MethodCallDetectors.js.map +1 -0
- package/dist/plugins/enrichment/method-call/MethodCallErrorAnalysis.d.ts +22 -0
- package/dist/plugins/enrichment/method-call/MethodCallErrorAnalysis.d.ts.map +1 -0
- package/dist/plugins/enrichment/method-call/MethodCallErrorAnalysis.js +105 -0
- package/dist/plugins/enrichment/method-call/MethodCallErrorAnalysis.js.map +1 -0
- package/dist/plugins/enrichment/method-call/MethodCallIndexers.d.ts +19 -0
- package/dist/plugins/enrichment/method-call/MethodCallIndexers.d.ts.map +1 -0
- package/dist/plugins/enrichment/method-call/MethodCallIndexers.js +63 -0
- package/dist/plugins/enrichment/method-call/MethodCallIndexers.js.map +1 -0
- package/dist/plugins/enrichment/method-call/MethodCallResolution.d.ts +30 -0
- package/dist/plugins/enrichment/method-call/MethodCallResolution.d.ts.map +1 -0
- package/dist/plugins/enrichment/method-call/MethodCallResolution.js +138 -0
- package/dist/plugins/enrichment/method-call/MethodCallResolution.js.map +1 -0
- package/dist/plugins/indexing/IncrementalModuleIndexer.d.ts.map +1 -1
- package/dist/plugins/indexing/IncrementalModuleIndexer.js +2 -8
- package/dist/plugins/indexing/IncrementalModuleIndexer.js.map +1 -1
- package/dist/plugins/indexing/JSModuleIndexer.d.ts.map +1 -1
- package/dist/plugins/indexing/JSModuleIndexer.js +13 -20
- package/dist/plugins/indexing/JSModuleIndexer.js.map +1 -1
- package/dist/plugins/indexing/RustModuleIndexer.d.ts.map +1 -1
- package/dist/plugins/indexing/RustModuleIndexer.js +4 -8
- package/dist/plugins/indexing/RustModuleIndexer.js.map +1 -1
- package/dist/plugins/validation/AwaitInLoopValidator.d.ts +24 -0
- package/dist/plugins/validation/AwaitInLoopValidator.d.ts.map +1 -0
- package/dist/plugins/validation/AwaitInLoopValidator.js +69 -0
- package/dist/plugins/validation/AwaitInLoopValidator.js.map +1 -0
- package/dist/plugins/validation/PackageCoverageValidator.d.ts +33 -0
- package/dist/plugins/validation/PackageCoverageValidator.d.ts.map +1 -0
- package/dist/plugins/validation/PackageCoverageValidator.js +149 -0
- package/dist/plugins/validation/PackageCoverageValidator.js.map +1 -0
- package/dist/plugins/validation/UnconnectedRouteValidator.d.ts +18 -0
- package/dist/plugins/validation/UnconnectedRouteValidator.d.ts.map +1 -0
- package/dist/plugins/validation/UnconnectedRouteValidator.js +68 -0
- package/dist/plugins/validation/UnconnectedRouteValidator.js.map +1 -0
- package/dist/queries/NodeContext.d.ts +81 -0
- package/dist/queries/NodeContext.d.ts.map +1 -0
- package/dist/queries/NodeContext.js +193 -0
- package/dist/queries/NodeContext.js.map +1 -0
- package/dist/queries/findCallsInFunction.d.ts.map +1 -1
- package/dist/queries/findCallsInFunction.js +10 -2
- package/dist/queries/findCallsInFunction.js.map +1 -1
- package/dist/queries/findContainingFunction.d.ts +3 -2
- package/dist/queries/findContainingFunction.d.ts.map +1 -1
- package/dist/queries/findContainingFunction.js +13 -3
- package/dist/queries/findContainingFunction.js.map +1 -1
- package/dist/queries/index.d.ts +2 -0
- package/dist/queries/index.d.ts.map +1 -1
- package/dist/queries/index.js +1 -0
- package/dist/queries/index.js.map +1 -1
- package/dist/resources/InfraResourceMapImpl.d.ts +31 -0
- package/dist/resources/InfraResourceMapImpl.d.ts.map +1 -0
- package/dist/resources/InfraResourceMapImpl.js +110 -0
- package/dist/resources/InfraResourceMapImpl.js.map +1 -0
- package/dist/resources/RoutingMapImpl.d.ts +33 -0
- package/dist/resources/RoutingMapImpl.d.ts.map +1 -0
- package/dist/resources/RoutingMapImpl.js +115 -0
- package/dist/resources/RoutingMapImpl.js.map +1 -0
- package/dist/storage/backends/RFDBServerBackend.d.ts +35 -6
- package/dist/storage/backends/RFDBServerBackend.d.ts.map +1 -1
- package/dist/storage/backends/RFDBServerBackend.js +102 -70
- package/dist/storage/backends/RFDBServerBackend.js.map +1 -1
- package/dist/utils/findRfdbBinary.d.ts +3 -2
- package/dist/utils/findRfdbBinary.d.ts.map +1 -1
- package/dist/utils/findRfdbBinary.js +22 -7
- package/dist/utils/findRfdbBinary.js.map +1 -1
- package/dist/utils/moduleResolution.d.ts.map +1 -1
- package/dist/utils/moduleResolution.js +26 -1
- package/dist/utils/moduleResolution.js.map +1 -1
- package/dist/utils/resolveNodeFile.d.ts +13 -0
- package/dist/utils/resolveNodeFile.d.ts.map +1 -0
- package/dist/utils/resolveNodeFile.js +18 -0
- package/dist/utils/resolveNodeFile.js.map +1 -0
- package/dist/version.d.ts +11 -0
- package/dist/version.d.ts.map +1 -0
- package/dist/version.js +26 -0
- package/dist/version.js.map +1 -0
- package/package.json +3 -3
- package/src/DiscoveryManager.ts +279 -0
- package/src/GraphInitializer.ts +131 -0
- package/src/GuaranteeChecker.ts +90 -0
- package/src/Orchestrator.ts +222 -963
- package/src/OrchestratorTypes.ts +122 -0
- package/src/ParallelAnalysisRunner.ts +188 -0
- package/src/PhaseRunner.ts +450 -0
- package/src/config/ConfigLoader.ts +176 -2
- package/src/config/index.ts +2 -0
- package/src/core/ASTWorker.ts +9 -2
- package/src/core/FileOverview.ts +374 -0
- package/src/core/GraphFreshnessChecker.ts +7 -5
- package/src/core/GuaranteeManager.ts +70 -2
- package/src/core/IncrementalReanalyzer.ts +6 -3
- package/src/core/NodeFactory.ts +173 -652
- package/src/core/ResourceRegistry.ts +39 -0
- package/src/core/ScopeTracker.ts +23 -0
- package/src/core/SemanticId.ts +183 -0
- package/src/core/brandNodeInternal.ts +16 -0
- package/src/core/buildDependencyGraph.ts +98 -0
- package/src/core/factories/CoreFactory.ts +489 -0
- package/src/core/factories/DatabaseFactory.ts +63 -0
- package/src/core/factories/ExternalFactory.ts +23 -0
- package/src/core/factories/HttpFactory.ts +57 -0
- package/src/core/factories/ReactFactory.ts +15 -0
- package/src/core/factories/RustFactory.ts +128 -0
- package/src/core/factories/ServiceFactory.ts +27 -0
- package/src/core/factories/SocketFactory.ts +94 -0
- package/src/core/nodes/DatabaseNode.ts +175 -0
- package/src/core/nodes/ExpressMiddlewareNode.ts +98 -0
- package/src/core/nodes/ExpressMountNode.ts +94 -0
- package/src/core/nodes/ExternalApiNode.ts +53 -0
- package/src/core/nodes/ExternalFunctionNode.ts +77 -0
- package/src/core/nodes/FetchRequestNode.ts +105 -0
- package/src/core/nodes/HttpRouteNode.ts +113 -0
- package/src/core/nodes/NodeKind.ts +1 -0
- package/src/core/nodes/ReactNode.ts +78 -0
- package/src/core/nodes/RustCallNode.ts +96 -0
- package/src/core/nodes/RustFunctionNode.ts +112 -0
- package/src/core/nodes/RustImplNode.ts +78 -0
- package/src/core/nodes/RustMethodNode.ts +125 -0
- package/src/core/nodes/RustModuleNode.ts +84 -0
- package/src/core/nodes/RustStructNode.ts +80 -0
- package/src/core/nodes/RustTraitNode.ts +82 -0
- package/src/core/nodes/ServiceLayerNode.ts +183 -0
- package/src/core/nodes/SocketIONode.ts +177 -0
- package/src/core/nodes/SocketNode.ts +206 -0
- package/src/core/nodes/TypeNode.ts +46 -3
- package/src/core/nodes/TypeParameterNode.ts +91 -0
- package/src/core/nodes/index.ts +57 -0
- package/src/index.ts +60 -4
- package/src/plugins/InfraAnalyzer.ts +208 -0
- package/src/plugins/analysis/DatabaseAnalyzer.ts +27 -17
- package/src/plugins/analysis/ExpressAnalyzer.ts +51 -38
- package/src/plugins/analysis/ExpressResponseAnalyzer.ts +15 -12
- package/src/plugins/analysis/ExpressRouteAnalyzer.ts +56 -56
- package/src/plugins/analysis/FetchAnalyzer.ts +42 -52
- package/src/plugins/analysis/IncrementalAnalysisPlugin.ts +3 -2
- package/src/plugins/analysis/JSASTAnalyzer.ts +391 -2304
- package/src/plugins/analysis/NestJSRouteAnalyzer.ts +241 -0
- package/src/plugins/analysis/ReactAnalyzer.ts +33 -1085
- package/src/plugins/analysis/RustAnalyzer.ts +112 -116
- package/src/plugins/analysis/SQLiteAnalyzer.ts +23 -9
- package/src/plugins/analysis/ServiceLayerAnalyzer.ts +32 -10
- package/src/plugins/analysis/SocketAnalyzer.ts +601 -0
- package/src/plugins/analysis/SocketIOAnalyzer.ts +25 -34
- package/src/plugins/analysis/SystemDbAnalyzer.ts +15 -12
- package/src/plugins/analysis/ast/CollisionResolver.ts +137 -0
- package/src/plugins/analysis/ast/FunctionBodyContext.ts +291 -0
- package/src/plugins/analysis/ast/GraphBuilder.ts +274 -3180
- package/src/plugins/analysis/ast/IdGenerator.ts +81 -1
- package/src/plugins/analysis/ast/builders/AssignmentBuilder.ts +407 -0
- package/src/plugins/analysis/ast/builders/CallFlowBuilder.ts +255 -0
- package/src/plugins/analysis/ast/builders/ControlFlowBuilder.ts +470 -0
- package/src/plugins/analysis/ast/builders/CoreBuilder.ts +306 -0
- package/src/plugins/analysis/ast/builders/ModuleRuntimeBuilder.ts +452 -0
- package/src/plugins/analysis/ast/builders/MutationBuilder.ts +372 -0
- package/src/plugins/analysis/ast/builders/ReturnBuilder.ts +279 -0
- package/src/plugins/analysis/ast/builders/TypeSystemBuilder.ts +475 -0
- package/src/plugins/analysis/ast/builders/UpdateExpressionBuilder.ts +262 -0
- package/src/plugins/analysis/ast/builders/YieldBuilder.ts +287 -0
- package/src/plugins/analysis/ast/builders/index.ts +11 -0
- package/src/plugins/analysis/ast/builders/types.ts +65 -0
- package/src/plugins/analysis/ast/handlers/AnalyzerDelegate.ts +183 -0
- package/src/plugins/analysis/ast/handlers/BranchHandler.ts +313 -0
- package/src/plugins/analysis/ast/handlers/CallExpressionHandler.ts +347 -0
- package/src/plugins/analysis/ast/handlers/FunctionBodyHandler.ts +24 -0
- package/src/plugins/analysis/ast/handlers/LoopHandler.ts +240 -0
- package/src/plugins/analysis/ast/handlers/NestedFunctionHandler.ts +201 -0
- package/src/plugins/analysis/ast/handlers/NewExpressionHandler.ts +159 -0
- package/src/plugins/analysis/ast/handlers/PropertyAccessHandler.ts +112 -0
- package/src/plugins/analysis/ast/handlers/ReturnYieldHandler.ts +166 -0
- package/src/plugins/analysis/ast/handlers/ThrowHandler.ts +101 -0
- package/src/plugins/analysis/ast/handlers/TryCatchHandler.ts +262 -0
- package/src/plugins/analysis/ast/handlers/VariableHandler.ts +93 -0
- package/src/plugins/analysis/ast/handlers/index.ts +12 -0
- package/src/plugins/analysis/ast/types.ts +68 -9
- package/src/plugins/analysis/ast/utils/createParameterNodes.ts +118 -13
- package/src/plugins/analysis/ast/utils/extractNamesFromPattern.ts +166 -0
- package/src/plugins/analysis/ast/utils/getExpressionValue.ts +34 -0
- package/src/plugins/analysis/ast/utils/getMemberExpressionName.ts +33 -0
- package/src/plugins/analysis/ast/utils/index.ts +2 -0
- package/src/plugins/analysis/ast/visitors/ArgumentExtractor.ts +307 -0
- package/src/plugins/analysis/ast/visitors/ArrayElementExtractor.ts +172 -0
- package/src/plugins/analysis/ast/visitors/CallExpressionVisitor.ts +425 -1374
- package/src/plugins/analysis/ast/visitors/ClassVisitor.ts +43 -12
- package/src/plugins/analysis/ast/visitors/FunctionVisitor.ts +39 -8
- package/src/plugins/analysis/ast/visitors/MutationDetector.ts +211 -0
- package/src/plugins/analysis/ast/visitors/ObjectPropertyExtractor.ts +217 -0
- package/src/plugins/analysis/ast/visitors/PropertyAccessVisitor.ts +69 -4
- package/src/plugins/analysis/ast/visitors/TypeScriptVisitor.ts +232 -13
- package/src/plugins/analysis/ast/visitors/VariableVisitor.ts +8 -11
- package/src/plugins/analysis/ast/visitors/call-expression-helpers.ts +65 -0
- package/src/plugins/analysis/ast/visitors/call-expression-types.ts +179 -0
- package/src/plugins/analysis/react-internal/browser-api.ts +168 -0
- package/src/plugins/analysis/react-internal/hooks.ts +517 -0
- package/src/plugins/analysis/react-internal/jsx.ts +279 -0
- package/src/plugins/analysis/react-internal/types.ts +183 -0
- package/src/plugins/discovery/MonorepoServiceDiscovery.ts +6 -14
- package/src/plugins/enrichment/AliasTracker.ts +3 -1
- package/src/plugins/enrichment/ArgumentParameterLinker.ts +3 -1
- package/src/plugins/enrichment/CallbackCallResolver.ts +398 -0
- package/src/plugins/enrichment/ClosureCaptureEnricher.ts +3 -1
- package/src/plugins/enrichment/ConfigRoutingMapBuilder.ts +67 -0
- package/src/plugins/enrichment/ExpressHandlerLinker.ts +3 -1
- package/src/plugins/enrichment/ExternalCallResolver.ts +5 -8
- package/src/plugins/enrichment/FunctionCallResolver.ts +6 -9
- package/src/plugins/enrichment/HTTPConnectionEnricher.ts +3 -1
- package/src/plugins/enrichment/ImportExportLinker.ts +5 -3
- package/src/plugins/enrichment/InstanceOfResolver.ts +3 -1
- package/src/plugins/enrichment/MethodCallResolver.ts +48 -659
- package/src/plugins/enrichment/MountPointResolver.ts +9 -2
- package/src/plugins/enrichment/NodejsBuiltinsResolver.ts +13 -18
- package/src/plugins/enrichment/PrefixEvaluator.ts +6 -2
- package/src/plugins/enrichment/RejectionPropagationEnricher.ts +3 -1
- package/src/plugins/enrichment/RustFFIEnricher.ts +3 -1
- package/src/plugins/enrichment/ServiceConnectionEnricher.ts +472 -0
- package/src/plugins/enrichment/SocketConnectionEnricher.ts +228 -0
- package/src/plugins/enrichment/ValueDomainAnalyzer.ts +3 -1
- package/src/plugins/enrichment/method-call/MethodCallData.ts +299 -0
- package/src/plugins/enrichment/method-call/MethodCallDetectors.ts +70 -0
- package/src/plugins/enrichment/method-call/MethodCallErrorAnalysis.ts +131 -0
- package/src/plugins/enrichment/method-call/MethodCallIndexers.ts +83 -0
- package/src/plugins/enrichment/method-call/MethodCallResolution.ts +181 -0
- package/src/plugins/indexing/IncrementalModuleIndexer.ts +5 -10
- package/src/plugins/indexing/JSModuleIndexer.ts +17 -21
- package/src/plugins/indexing/RustModuleIndexer.ts +14 -13
- package/src/plugins/validation/AwaitInLoopValidator.ts +91 -0
- package/src/plugins/validation/PackageCoverageValidator.ts +181 -0
- package/src/plugins/validation/UnconnectedRouteValidator.ts +93 -0
- package/src/queries/NodeContext.ts +277 -0
- package/src/queries/findCallsInFunction.ts +11 -2
- package/src/queries/findContainingFunction.ts +14 -3
- package/src/queries/index.ts +13 -0
- package/src/resources/InfraResourceMapImpl.ts +119 -0
- package/src/resources/RoutingMapImpl.ts +133 -0
- package/src/storage/backends/RFDBServerBackend.ts +106 -77
- package/src/utils/findRfdbBinary.ts +22 -7
- package/src/utils/moduleResolution.ts +28 -1
- package/src/utils/resolveNodeFile.ts +18 -0
- package/src/version.ts +28 -0
|
@@ -8,7 +8,7 @@ import { createHash } from 'crypto';
|
|
|
8
8
|
import { basename } from 'path';
|
|
9
9
|
import { parse } from '@babel/parser';
|
|
10
10
|
import traverseModule from '@babel/traverse';
|
|
11
|
-
import type { NodePath, TraverseOptions } from '@babel/traverse';
|
|
11
|
+
import type { NodePath, TraverseOptions, Visitor } from '@babel/traverse';
|
|
12
12
|
import * as t from '@babel/types';
|
|
13
13
|
|
|
14
14
|
// Type for CJS/ESM interop - @babel/traverse exports a function but @types defines it as namespace
|
|
@@ -54,11 +54,15 @@ import { getLine, getColumn } from './ast/utils/location.js';
|
|
|
54
54
|
import { Profiler } from '../../core/Profiler.js';
|
|
55
55
|
import { ScopeTracker } from '../../core/ScopeTracker.js';
|
|
56
56
|
import { computeSemanticId } from '../../core/SemanticId.js';
|
|
57
|
+
import { IdGenerator } from './ast/IdGenerator.js';
|
|
58
|
+
import { CollisionResolver } from './ast/CollisionResolver.js';
|
|
57
59
|
import { ExpressionNode } from '../../core/nodes/ExpressionNode.js';
|
|
58
60
|
import { ConstructorCallNode } from '../../core/nodes/ConstructorCallNode.js';
|
|
59
61
|
import { ObjectLiteralNode } from '../../core/nodes/ObjectLiteralNode.js';
|
|
60
62
|
import { ArrayLiteralNode } from '../../core/nodes/ArrayLiteralNode.js';
|
|
61
63
|
import { NodeFactory } from '../../core/NodeFactory.js';
|
|
64
|
+
import { brandNodeInternal } from '../../core/brandNodeInternal.js';
|
|
65
|
+
import { resolveNodeFile } from '../../utils/resolveNodeFile.js';
|
|
62
66
|
import type { PluginContext, PluginResult, PluginMetadata, GraphBackend } from '@grafema/types';
|
|
63
67
|
import type {
|
|
64
68
|
ModuleNode,
|
|
@@ -106,11 +110,29 @@ import type {
|
|
|
106
110
|
RejectionPatternInfo,
|
|
107
111
|
CatchesFromInfo,
|
|
108
112
|
PropertyAccessInfo,
|
|
113
|
+
TypeParameterInfo,
|
|
109
114
|
CounterRef,
|
|
110
115
|
ProcessedNodes,
|
|
111
116
|
ASTCollections,
|
|
112
117
|
ExtractedVariable,
|
|
113
118
|
} from './ast/types.js';
|
|
119
|
+
import { extractNamesFromPattern } from './ast/utils/extractNamesFromPattern.js';
|
|
120
|
+
import { createFunctionBodyContext } from './ast/FunctionBodyContext.js';
|
|
121
|
+
import type { FunctionBodyContext } from './ast/FunctionBodyContext.js';
|
|
122
|
+
import {
|
|
123
|
+
VariableHandler,
|
|
124
|
+
ReturnYieldHandler,
|
|
125
|
+
ThrowHandler,
|
|
126
|
+
NestedFunctionHandler,
|
|
127
|
+
PropertyAccessHandler,
|
|
128
|
+
NewExpressionHandler,
|
|
129
|
+
CallExpressionHandler,
|
|
130
|
+
LoopHandler,
|
|
131
|
+
TryCatchHandler,
|
|
132
|
+
BranchHandler,
|
|
133
|
+
} from './ast/handlers/index.js';
|
|
134
|
+
import type { AnalyzerDelegate } from './ast/handlers/index.js';
|
|
135
|
+
import type { FunctionBodyHandler } from './ast/handlers/index.js';
|
|
114
136
|
|
|
115
137
|
// === LOCAL TYPES ===
|
|
116
138
|
|
|
@@ -152,6 +174,8 @@ interface Collections {
|
|
|
152
174
|
typeAliases: TypeAliasInfo[];
|
|
153
175
|
enums: EnumDeclarationInfo[];
|
|
154
176
|
decorators: DecoratorInfo[];
|
|
177
|
+
// Type parameter tracking for generics (REG-303)
|
|
178
|
+
typeParameters: TypeParameterInfo[];
|
|
155
179
|
// Object/Array literal tracking
|
|
156
180
|
objectLiterals: ObjectLiteralInfo[];
|
|
157
181
|
objectProperties: ObjectPropertyInfo[];
|
|
@@ -201,35 +225,6 @@ interface Collections {
|
|
|
201
225
|
[key: string]: unknown;
|
|
202
226
|
}
|
|
203
227
|
|
|
204
|
-
/**
|
|
205
|
-
* Tracks try/catch/finally scope transitions during traversal.
|
|
206
|
-
* Used by createTryStatementHandler and createBlockStatementHandler.
|
|
207
|
-
*/
|
|
208
|
-
interface TryScopeInfo {
|
|
209
|
-
tryScopeId: string;
|
|
210
|
-
catchScopeId: string | null;
|
|
211
|
-
finallyScopeId: string | null;
|
|
212
|
-
currentBlock: 'try' | 'catch' | 'finally';
|
|
213
|
-
// Phase 4: Control flow node IDs
|
|
214
|
-
tryBlockId: string;
|
|
215
|
-
catchBlockId: string | null;
|
|
216
|
-
finallyBlockId: string | null;
|
|
217
|
-
}
|
|
218
|
-
|
|
219
|
-
/**
|
|
220
|
-
* Tracks if/else scope transitions during traversal.
|
|
221
|
-
* Used by createIfStatementHandler and createBlockStatementHandler.
|
|
222
|
-
* Phase 3: Extended to include branchId for control flow BRANCH nodes.
|
|
223
|
-
*/
|
|
224
|
-
interface IfElseScopeInfo {
|
|
225
|
-
inElse: boolean;
|
|
226
|
-
hasElse: boolean;
|
|
227
|
-
ifScopeId: string;
|
|
228
|
-
elseScopeId: string | null;
|
|
229
|
-
// Phase 3: Control flow BRANCH node ID
|
|
230
|
-
branchId: string;
|
|
231
|
-
}
|
|
232
|
-
|
|
233
228
|
interface AnalysisManifest {
|
|
234
229
|
projectPath: string;
|
|
235
230
|
[key: string]: unknown;
|
|
@@ -267,14 +262,14 @@ export class JSASTAnalyzer extends Plugin {
|
|
|
267
262
|
'CALL', 'IMPORT', 'EXPORT', 'LITERAL', 'EXTERNAL_MODULE',
|
|
268
263
|
'net:stdio', 'net:request', 'event:listener', 'http:request',
|
|
269
264
|
// TypeScript-specific nodes
|
|
270
|
-
'INTERFACE', 'TYPE', 'ENUM', 'DECORATOR'
|
|
265
|
+
'INTERFACE', 'TYPE', 'ENUM', 'DECORATOR', 'TYPE_PARAMETER'
|
|
271
266
|
],
|
|
272
267
|
edges: [
|
|
273
268
|
'CONTAINS', 'DECLARES', 'CALLS', 'HAS_SCOPE', 'CAPTURES', 'MODIFIES',
|
|
274
269
|
'WRITES_TO', 'IMPORTS', 'INSTANCE_OF', 'HANDLED_BY', 'HAS_CALLBACK',
|
|
275
270
|
'PASSES_ARGUMENT', 'MAKES_REQUEST', 'IMPORTS_FROM', 'EXPORTS_TO', 'ASSIGNED_FROM',
|
|
276
271
|
// TypeScript-specific edges
|
|
277
|
-
'IMPLEMENTS', 'EXTENDS', 'DECORATED_BY',
|
|
272
|
+
'IMPLEMENTS', 'EXTENDS', 'DECORATED_BY', 'HAS_TYPE_PARAMETER',
|
|
278
273
|
// Promise data flow
|
|
279
274
|
'RESOLVES_TO'
|
|
280
275
|
]
|
|
@@ -295,9 +290,9 @@ export class JSASTAnalyzer extends Plugin {
|
|
|
295
290
|
/**
|
|
296
291
|
* Вычисляет хеш содержимого файла
|
|
297
292
|
*/
|
|
298
|
-
calculateFileHash(filePath: string): string | null {
|
|
293
|
+
calculateFileHash(filePath: string, projectPath: string = ''): string | null {
|
|
299
294
|
try {
|
|
300
|
-
const content = readFileSync(filePath, 'utf-8');
|
|
295
|
+
const content = readFileSync(resolveNodeFile(filePath, projectPath), 'utf-8');
|
|
301
296
|
return createHash('sha256').update(content).digest('hex');
|
|
302
297
|
} catch {
|
|
303
298
|
return null;
|
|
@@ -307,7 +302,7 @@ export class JSASTAnalyzer extends Plugin {
|
|
|
307
302
|
/**
|
|
308
303
|
* Проверяет нужно ли анализировать модуль (сравнивает хеши)
|
|
309
304
|
*/
|
|
310
|
-
async shouldAnalyzeModule(module: ModuleNode, graph: GraphBackend, forceAnalysis: boolean): Promise<boolean> {
|
|
305
|
+
async shouldAnalyzeModule(module: ModuleNode, graph: GraphBackend, forceAnalysis: boolean, projectPath: string = ""): Promise<boolean> {
|
|
311
306
|
if (forceAnalysis) {
|
|
312
307
|
return true;
|
|
313
308
|
}
|
|
@@ -316,19 +311,19 @@ export class JSASTAnalyzer extends Plugin {
|
|
|
316
311
|
return true;
|
|
317
312
|
}
|
|
318
313
|
|
|
319
|
-
const currentHash = this.calculateFileHash(module.file);
|
|
314
|
+
const currentHash = this.calculateFileHash(module.file, projectPath);
|
|
320
315
|
if (!currentHash) {
|
|
321
316
|
return true;
|
|
322
317
|
}
|
|
323
318
|
|
|
324
319
|
if (currentHash !== module.contentHash) {
|
|
325
|
-
await graph.addNode({
|
|
320
|
+
await graph.addNode(brandNodeInternal({
|
|
326
321
|
id: module.id,
|
|
327
|
-
type: 'MODULE',
|
|
322
|
+
type: 'MODULE' as const,
|
|
328
323
|
name: module.name,
|
|
329
324
|
file: module.file,
|
|
330
325
|
contentHash: currentHash
|
|
331
|
-
});
|
|
326
|
+
}));
|
|
332
327
|
return true;
|
|
333
328
|
}
|
|
334
329
|
|
|
@@ -365,7 +360,7 @@ export class JSASTAnalyzer extends Plugin {
|
|
|
365
360
|
continue;
|
|
366
361
|
}
|
|
367
362
|
|
|
368
|
-
if (await this.shouldAnalyzeModule(module, graph, forceAnalysis)) {
|
|
363
|
+
if (await this.shouldAnalyzeModule(module, graph, forceAnalysis, projectPath)) {
|
|
369
364
|
modulesToAnalyze.push(module);
|
|
370
365
|
} else {
|
|
371
366
|
skippedCount++;
|
|
@@ -501,7 +496,7 @@ export class JSASTAnalyzer extends Plugin {
|
|
|
501
496
|
// Convert ModuleNode to ASTModuleInfo format
|
|
502
497
|
const moduleInfos: ASTModuleInfo[] = modules.map(m => ({
|
|
503
498
|
id: m.id,
|
|
504
|
-
file: m.file,
|
|
499
|
+
file: resolveNodeFile(m.file, projectPath),
|
|
505
500
|
name: m.name
|
|
506
501
|
}));
|
|
507
502
|
|
|
@@ -546,7 +541,7 @@ export class JSASTAnalyzer extends Plugin {
|
|
|
546
541
|
context.onProgress({
|
|
547
542
|
phase: 'analysis',
|
|
548
543
|
currentPlugin: 'JSASTAnalyzer',
|
|
549
|
-
message: `Processed ${result.module.
|
|
544
|
+
message: `Processed ${result.module.name}`,
|
|
550
545
|
totalFiles: modules.length,
|
|
551
546
|
processedFiles: results.indexOf(result) + 1
|
|
552
547
|
});
|
|
@@ -567,68 +562,13 @@ export class JSASTAnalyzer extends Plugin {
|
|
|
567
562
|
/**
|
|
568
563
|
* Extract variable names from destructuring patterns
|
|
569
564
|
* Uses t.isX() type guards to avoid casts
|
|
565
|
+
*
|
|
566
|
+
* REG-399: Delegated to extractNamesFromPattern utility for code reuse with parameters.
|
|
567
|
+
* This method maintains the same API for backward compatibility.
|
|
570
568
|
*/
|
|
571
569
|
extractVariableNamesFromPattern(pattern: t.Node | null | undefined, variables: ExtractedVariable[] = [], propertyPath: string[] = []): ExtractedVariable[] {
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
if (t.isIdentifier(pattern)) {
|
|
575
|
-
variables.push({
|
|
576
|
-
name: pattern.name,
|
|
577
|
-
loc: pattern.loc?.start ? { start: pattern.loc.start } : { start: { line: 0, column: 0 } },
|
|
578
|
-
propertyPath: propertyPath.length > 0 ? [...propertyPath] : undefined
|
|
579
|
-
});
|
|
580
|
-
} else if (t.isObjectPattern(pattern)) {
|
|
581
|
-
pattern.properties.forEach((prop) => {
|
|
582
|
-
if (t.isRestElement(prop)) {
|
|
583
|
-
const restVars = this.extractVariableNamesFromPattern(prop.argument, [], []);
|
|
584
|
-
restVars.forEach(v => {
|
|
585
|
-
v.isRest = true;
|
|
586
|
-
v.propertyPath = propertyPath.length > 0 ? [...propertyPath] : undefined;
|
|
587
|
-
variables.push(v);
|
|
588
|
-
});
|
|
589
|
-
} else if (t.isObjectProperty(prop) && prop.value) {
|
|
590
|
-
const key = t.isIdentifier(prop.key) ? prop.key.name :
|
|
591
|
-
(t.isStringLiteral(prop.key) || t.isNumericLiteral(prop.key) ? String(prop.key.value) : null);
|
|
592
|
-
|
|
593
|
-
if (key !== null) {
|
|
594
|
-
const newPath = [...propertyPath, key];
|
|
595
|
-
this.extractVariableNamesFromPattern(prop.value, variables, newPath);
|
|
596
|
-
} else {
|
|
597
|
-
this.extractVariableNamesFromPattern(prop.value, variables, propertyPath);
|
|
598
|
-
}
|
|
599
|
-
}
|
|
600
|
-
});
|
|
601
|
-
} else if (t.isArrayPattern(pattern)) {
|
|
602
|
-
pattern.elements.forEach((element, index) => {
|
|
603
|
-
if (element) {
|
|
604
|
-
if (t.isRestElement(element)) {
|
|
605
|
-
const restVars = this.extractVariableNamesFromPattern(element.argument, [], []);
|
|
606
|
-
restVars.forEach(v => {
|
|
607
|
-
v.isRest = true;
|
|
608
|
-
v.arrayIndex = index;
|
|
609
|
-
v.propertyPath = propertyPath.length > 0 ? [...propertyPath] : undefined;
|
|
610
|
-
variables.push(v);
|
|
611
|
-
});
|
|
612
|
-
} else {
|
|
613
|
-
const extracted = this.extractVariableNamesFromPattern(element, [], propertyPath);
|
|
614
|
-
extracted.forEach(v => {
|
|
615
|
-
v.arrayIndex = index;
|
|
616
|
-
variables.push(v);
|
|
617
|
-
});
|
|
618
|
-
}
|
|
619
|
-
}
|
|
620
|
-
});
|
|
621
|
-
} else if (t.isRestElement(pattern)) {
|
|
622
|
-
const restVars = this.extractVariableNamesFromPattern(pattern.argument, [], propertyPath);
|
|
623
|
-
restVars.forEach(v => {
|
|
624
|
-
v.isRest = true;
|
|
625
|
-
variables.push(v);
|
|
626
|
-
});
|
|
627
|
-
} else if (t.isAssignmentPattern(pattern)) {
|
|
628
|
-
this.extractVariableNamesFromPattern(pattern.left, variables, propertyPath);
|
|
629
|
-
}
|
|
630
|
-
|
|
631
|
-
return variables;
|
|
570
|
+
// Delegate to the extracted utility function
|
|
571
|
+
return extractNamesFromPattern(pattern, variables, propertyPath);
|
|
632
572
|
}
|
|
633
573
|
|
|
634
574
|
/**
|
|
@@ -725,57 +665,15 @@ export class JSASTAnalyzer extends Plugin {
|
|
|
725
665
|
}
|
|
726
666
|
|
|
727
667
|
// 3. MemberExpression call (e.g., arr.map())
|
|
668
|
+
// Uses coordinate-based lookup to reference the standard CALL node created by CallExpressionVisitor
|
|
728
669
|
if (initExpression.type === 'CallExpression' && initExpression.callee.type === 'MemberExpression') {
|
|
729
|
-
const callee = initExpression.callee;
|
|
730
|
-
const objectName = callee.object.type === 'Identifier' ? callee.object.name : (callee.object.type === 'ThisExpression' ? 'this' : 'unknown');
|
|
731
|
-
const methodName = callee.property.type === 'Identifier' ? callee.property.name : 'unknown';
|
|
732
|
-
|
|
733
|
-
const fullName = `${objectName}.${methodName}`;
|
|
734
|
-
const methodCallId = `CALL#${fullName}#${module.file}#${getLine(initExpression)}:${getColumn(initExpression)}:inline`;
|
|
735
|
-
|
|
736
|
-
const existing = variableAssignments.find(a => a.sourceId === methodCallId);
|
|
737
|
-
if (!existing) {
|
|
738
|
-
const extractedArgs: unknown[] = [];
|
|
739
|
-
initExpression.arguments.forEach((arg, index) => {
|
|
740
|
-
if (arg.type !== 'SpreadElement') {
|
|
741
|
-
const argLiteralValue = ExpressionEvaluator.extractLiteralValue(arg);
|
|
742
|
-
if (argLiteralValue !== null) {
|
|
743
|
-
const literalId = `LITERAL#arg${index}#${module.file}#${getLine(initExpression)}:${getColumn(initExpression)}:${literalCounterRef.value++}`;
|
|
744
|
-
literals.push({
|
|
745
|
-
id: literalId,
|
|
746
|
-
type: 'LITERAL',
|
|
747
|
-
value: argLiteralValue,
|
|
748
|
-
valueType: typeof argLiteralValue,
|
|
749
|
-
file: module.file,
|
|
750
|
-
line: arg.loc?.start.line || getLine(initExpression),
|
|
751
|
-
column: arg.loc?.start.column || getColumn(initExpression),
|
|
752
|
-
parentCallId: methodCallId,
|
|
753
|
-
argIndex: index
|
|
754
|
-
});
|
|
755
|
-
extractedArgs.push(argLiteralValue);
|
|
756
|
-
} else {
|
|
757
|
-
extractedArgs.push(undefined);
|
|
758
|
-
}
|
|
759
|
-
}
|
|
760
|
-
});
|
|
761
|
-
|
|
762
|
-
literals.push({
|
|
763
|
-
id: methodCallId,
|
|
764
|
-
type: 'CALL',
|
|
765
|
-
name: fullName,
|
|
766
|
-
object: objectName,
|
|
767
|
-
method: methodName,
|
|
768
|
-
file: module.file,
|
|
769
|
-
arguments: extractedArgs,
|
|
770
|
-
line: getLine(initExpression),
|
|
771
|
-
column: getColumn(initExpression)
|
|
772
|
-
});
|
|
773
|
-
}
|
|
774
|
-
|
|
775
670
|
variableAssignments.push({
|
|
776
671
|
variableId,
|
|
777
|
-
|
|
778
|
-
|
|
672
|
+
sourceType: 'METHOD_CALL',
|
|
673
|
+
sourceLine: getLine(initExpression),
|
|
674
|
+
sourceColumn: getColumn(initExpression),
|
|
675
|
+
sourceFile: module.file,
|
|
676
|
+
line: line
|
|
779
677
|
});
|
|
780
678
|
return;
|
|
781
679
|
}
|
|
@@ -1417,13 +1315,13 @@ export class JSASTAnalyzer extends Plugin {
|
|
|
1417
1315
|
|
|
1418
1316
|
try {
|
|
1419
1317
|
this.profiler.start('file_read');
|
|
1420
|
-
const code = readFileSync(module.file, 'utf-8');
|
|
1318
|
+
const code = readFileSync(resolveNodeFile(module.file, projectPath), 'utf-8');
|
|
1421
1319
|
this.profiler.end('file_read');
|
|
1422
1320
|
|
|
1423
1321
|
this.profiler.start('babel_parse');
|
|
1424
1322
|
const ast = parse(code, {
|
|
1425
1323
|
sourceType: 'module',
|
|
1426
|
-
plugins: ['jsx', 'typescript']
|
|
1324
|
+
plugins: ['jsx', 'typescript', 'decorators-legacy']
|
|
1427
1325
|
});
|
|
1428
1326
|
this.profiler.end('babel_parse');
|
|
1429
1327
|
|
|
@@ -1431,6 +1329,9 @@ export class JSASTAnalyzer extends Plugin {
|
|
|
1431
1329
|
// Use basename for shorter, more readable semantic IDs
|
|
1432
1330
|
const scopeTracker = new ScopeTracker(basename(module.file));
|
|
1433
1331
|
|
|
1332
|
+
// REG-464: Shared IdGenerator for v2 collision resolution across visitors
|
|
1333
|
+
const sharedIdGenerator = new IdGenerator(scopeTracker);
|
|
1334
|
+
|
|
1434
1335
|
const functions: FunctionInfo[] = [];
|
|
1435
1336
|
const parameters: ParameterInfo[] = [];
|
|
1436
1337
|
const scopes: ScopeInfo[] = [];
|
|
@@ -1458,6 +1359,8 @@ export class JSASTAnalyzer extends Plugin {
|
|
|
1458
1359
|
const typeAliases: TypeAliasInfo[] = [];
|
|
1459
1360
|
const enums: EnumDeclarationInfo[] = [];
|
|
1460
1361
|
const decorators: DecoratorInfo[] = [];
|
|
1362
|
+
// Type parameter tracking for generics (REG-303)
|
|
1363
|
+
const typeParameters: TypeParameterInfo[] = [];
|
|
1461
1364
|
// Object/Array literal tracking for data flow
|
|
1462
1365
|
const objectLiterals: ObjectLiteralInfo[] = [];
|
|
1463
1366
|
const objectProperties: ObjectPropertyInfo[] = [];
|
|
@@ -1545,6 +1448,8 @@ export class JSASTAnalyzer extends Plugin {
|
|
|
1545
1448
|
httpRequests, literals, variableAssignments,
|
|
1546
1449
|
// TypeScript-specific collections
|
|
1547
1450
|
interfaces, typeAliases, enums, decorators,
|
|
1451
|
+
// Type parameter tracking for generics (REG-303)
|
|
1452
|
+
typeParameters,
|
|
1548
1453
|
// Object/Array literal tracking
|
|
1549
1454
|
objectLiterals, objectProperties, arrayLiterals, arrayElements,
|
|
1550
1455
|
// Array mutation tracking
|
|
@@ -1759,10 +1664,30 @@ export class JSASTAnalyzer extends Plugin {
|
|
|
1759
1664
|
|
|
1760
1665
|
// Call expressions
|
|
1761
1666
|
this.profiler.start('traverse_calls');
|
|
1762
|
-
const callExpressionVisitor = new CallExpressionVisitor(module, allCollections, scopeTracker);
|
|
1667
|
+
const callExpressionVisitor = new CallExpressionVisitor(module, allCollections, scopeTracker, sharedIdGenerator);
|
|
1763
1668
|
traverse(ast, callExpressionVisitor.getHandlers());
|
|
1764
1669
|
this.profiler.end('traverse_calls');
|
|
1765
1670
|
|
|
1671
|
+
// REG-297: Detect top-level await expressions
|
|
1672
|
+
this.profiler.start('traverse_top_level_await');
|
|
1673
|
+
let hasTopLevelAwait = false;
|
|
1674
|
+
traverse(ast, {
|
|
1675
|
+
AwaitExpression(awaitPath: NodePath<t.AwaitExpression>) {
|
|
1676
|
+
if (!awaitPath.getFunctionParent()) {
|
|
1677
|
+
hasTopLevelAwait = true;
|
|
1678
|
+
awaitPath.stop();
|
|
1679
|
+
}
|
|
1680
|
+
},
|
|
1681
|
+
// for-await-of uses ForOfStatement.await, not AwaitExpression
|
|
1682
|
+
ForOfStatement(forOfPath: NodePath<t.ForOfStatement>) {
|
|
1683
|
+
if (forOfPath.node.await && !forOfPath.getFunctionParent()) {
|
|
1684
|
+
hasTopLevelAwait = true;
|
|
1685
|
+
forOfPath.stop();
|
|
1686
|
+
}
|
|
1687
|
+
}
|
|
1688
|
+
});
|
|
1689
|
+
this.profiler.end('traverse_top_level_await');
|
|
1690
|
+
|
|
1766
1691
|
// Property access expressions (REG-395)
|
|
1767
1692
|
this.profiler.start('traverse_property_access');
|
|
1768
1693
|
const propertyAccessVisitor = new PropertyAccessVisitor(module, allCollections, scopeTracker);
|
|
@@ -1897,6 +1822,39 @@ export class JSASTAnalyzer extends Plugin {
|
|
|
1897
1822
|
});
|
|
1898
1823
|
this.profiler.end('traverse_ifs');
|
|
1899
1824
|
|
|
1825
|
+
// REG-464: Resolve v2 ID collisions after all visitors complete
|
|
1826
|
+
const pendingNodes = sharedIdGenerator.getPendingNodes();
|
|
1827
|
+
if (pendingNodes.length > 0) {
|
|
1828
|
+
// Capture pre-resolution IDs to update callArguments afterward
|
|
1829
|
+
const preResolutionIds = new Map<{ id: string }, string>();
|
|
1830
|
+
for (const pn of pendingNodes) {
|
|
1831
|
+
preResolutionIds.set(pn.collectionRef, pn.collectionRef.id);
|
|
1832
|
+
}
|
|
1833
|
+
|
|
1834
|
+
const collisionResolver = new CollisionResolver();
|
|
1835
|
+
collisionResolver.resolve(pendingNodes);
|
|
1836
|
+
|
|
1837
|
+
// Update callArgument.callId references that became stale after resolution
|
|
1838
|
+
const idRemapping = new Map<string, string>();
|
|
1839
|
+
for (const pn of pendingNodes) {
|
|
1840
|
+
const oldId = preResolutionIds.get(pn.collectionRef)!;
|
|
1841
|
+
if (oldId !== pn.collectionRef.id) {
|
|
1842
|
+
idRemapping.set(oldId, pn.collectionRef.id);
|
|
1843
|
+
}
|
|
1844
|
+
}
|
|
1845
|
+
if (idRemapping.size > 0) {
|
|
1846
|
+
const callArgs = allCollections.callArguments as Array<{ callId: string }> | undefined;
|
|
1847
|
+
if (callArgs) {
|
|
1848
|
+
for (const arg of callArgs) {
|
|
1849
|
+
const resolved = idRemapping.get(arg.callId);
|
|
1850
|
+
if (resolved) {
|
|
1851
|
+
arg.callId = resolved;
|
|
1852
|
+
}
|
|
1853
|
+
}
|
|
1854
|
+
}
|
|
1855
|
+
}
|
|
1856
|
+
}
|
|
1857
|
+
|
|
1900
1858
|
// Build graph
|
|
1901
1859
|
this.profiler.start('graph_build');
|
|
1902
1860
|
const result = await this.graphBuilder.build(module, graph, projectPath, {
|
|
@@ -1932,6 +1890,8 @@ export class JSASTAnalyzer extends Plugin {
|
|
|
1932
1890
|
typeAliases,
|
|
1933
1891
|
enums,
|
|
1934
1892
|
decorators,
|
|
1893
|
+
// Type parameter tracking for generics (REG-303)
|
|
1894
|
+
typeParameters,
|
|
1935
1895
|
// Array mutation tracking
|
|
1936
1896
|
arrayMutations,
|
|
1937
1897
|
// Object mutation tracking
|
|
@@ -1958,7 +1918,9 @@ export class JSASTAnalyzer extends Plugin {
|
|
|
1958
1918
|
? allCollections.catchesFromInfos as CatchesFromInfo[]
|
|
1959
1919
|
: catchesFromInfos,
|
|
1960
1920
|
// Property access tracking (REG-395)
|
|
1961
|
-
propertyAccesses: allCollections.propertyAccesses || propertyAccesses
|
|
1921
|
+
propertyAccesses: allCollections.propertyAccesses || propertyAccesses,
|
|
1922
|
+
// REG-297: Top-level await tracking
|
|
1923
|
+
hasTopLevelAwait
|
|
1962
1924
|
});
|
|
1963
1925
|
this.profiler.end('graph_build');
|
|
1964
1926
|
|
|
@@ -2205,506 +2167,6 @@ export class JSASTAnalyzer extends Plugin {
|
|
|
2205
2167
|
});
|
|
2206
2168
|
}
|
|
2207
2169
|
|
|
2208
|
-
private createLoopScopeHandler(
|
|
2209
|
-
trackerScopeType: string,
|
|
2210
|
-
scopeType: string,
|
|
2211
|
-
loopType: 'for' | 'for-in' | 'for-of' | 'while' | 'do-while',
|
|
2212
|
-
parentScopeId: string,
|
|
2213
|
-
module: VisitorModule,
|
|
2214
|
-
scopes: ScopeInfo[],
|
|
2215
|
-
loops: LoopInfo[],
|
|
2216
|
-
scopeCounterRef: CounterRef,
|
|
2217
|
-
loopCounterRef: CounterRef,
|
|
2218
|
-
scopeTracker: ScopeTracker | undefined,
|
|
2219
|
-
scopeIdStack?: string[],
|
|
2220
|
-
controlFlowState?: { loopCount: number }
|
|
2221
|
-
): { enter: (path: NodePath<t.Loop>) => void; exit: () => void } {
|
|
2222
|
-
return {
|
|
2223
|
-
enter: (path: NodePath<t.Loop>) => {
|
|
2224
|
-
const node = path.node;
|
|
2225
|
-
|
|
2226
|
-
// Phase 6 (REG-267): Increment loop count for cyclomatic complexity
|
|
2227
|
-
if (controlFlowState) {
|
|
2228
|
-
controlFlowState.loopCount++;
|
|
2229
|
-
}
|
|
2230
|
-
|
|
2231
|
-
// 1. Create LOOP node
|
|
2232
|
-
const loopCounter = loopCounterRef.value++;
|
|
2233
|
-
const legacyLoopId = `${module.file}:LOOP:${loopType}:${getLine(node)}:${loopCounter}`;
|
|
2234
|
-
const loopId = scopeTracker
|
|
2235
|
-
? computeSemanticId('LOOP', loopType, scopeTracker.getContext(), { discriminator: loopCounter })
|
|
2236
|
-
: legacyLoopId;
|
|
2237
|
-
|
|
2238
|
-
// 2. Extract iteration target for for-in/for-of
|
|
2239
|
-
let iteratesOverName: string | undefined;
|
|
2240
|
-
let iteratesOverLine: number | undefined;
|
|
2241
|
-
let iteratesOverColumn: number | undefined;
|
|
2242
|
-
|
|
2243
|
-
if (loopType === 'for-in' || loopType === 'for-of') {
|
|
2244
|
-
const loopNode = node as t.ForInStatement | t.ForOfStatement;
|
|
2245
|
-
if (t.isIdentifier(loopNode.right)) {
|
|
2246
|
-
iteratesOverName = loopNode.right.name;
|
|
2247
|
-
iteratesOverLine = getLine(loopNode.right);
|
|
2248
|
-
iteratesOverColumn = getColumn(loopNode.right);
|
|
2249
|
-
} else if (t.isMemberExpression(loopNode.right)) {
|
|
2250
|
-
iteratesOverName = this.memberExpressionToString(loopNode.right);
|
|
2251
|
-
iteratesOverLine = getLine(loopNode.right);
|
|
2252
|
-
iteratesOverColumn = getColumn(loopNode.right);
|
|
2253
|
-
}
|
|
2254
|
-
}
|
|
2255
|
-
|
|
2256
|
-
// 2b. Extract init/test/update for classic for loops and test for while/do-while (REG-282)
|
|
2257
|
-
let initVariableName: string | undefined;
|
|
2258
|
-
let initLine: number | undefined;
|
|
2259
|
-
|
|
2260
|
-
let testExpressionId: string | undefined;
|
|
2261
|
-
let testExpressionType: string | undefined;
|
|
2262
|
-
let testLine: number | undefined;
|
|
2263
|
-
let testColumn: number | undefined;
|
|
2264
|
-
|
|
2265
|
-
let updateExpressionId: string | undefined;
|
|
2266
|
-
let updateExpressionType: string | undefined;
|
|
2267
|
-
let updateLine: number | undefined;
|
|
2268
|
-
let updateColumn: number | undefined;
|
|
2269
|
-
|
|
2270
|
-
if (loopType === 'for') {
|
|
2271
|
-
const forNode = node as t.ForStatement;
|
|
2272
|
-
|
|
2273
|
-
// Extract init: let i = 0
|
|
2274
|
-
if (forNode.init) {
|
|
2275
|
-
initLine = getLine(forNode.init);
|
|
2276
|
-
if (t.isVariableDeclaration(forNode.init)) {
|
|
2277
|
-
// Get name of first declared variable
|
|
2278
|
-
const firstDeclarator = forNode.init.declarations[0];
|
|
2279
|
-
if (t.isIdentifier(firstDeclarator.id)) {
|
|
2280
|
-
initVariableName = firstDeclarator.id.name;
|
|
2281
|
-
}
|
|
2282
|
-
}
|
|
2283
|
-
}
|
|
2284
|
-
|
|
2285
|
-
// Extract test: i < 10
|
|
2286
|
-
if (forNode.test) {
|
|
2287
|
-
testLine = getLine(forNode.test);
|
|
2288
|
-
testColumn = getColumn(forNode.test);
|
|
2289
|
-
testExpressionType = forNode.test.type;
|
|
2290
|
-
testExpressionId = ExpressionNode.generateId(forNode.test.type, module.file, testLine, testColumn);
|
|
2291
|
-
}
|
|
2292
|
-
|
|
2293
|
-
// Extract update: i++
|
|
2294
|
-
if (forNode.update) {
|
|
2295
|
-
updateLine = getLine(forNode.update);
|
|
2296
|
-
updateColumn = getColumn(forNode.update);
|
|
2297
|
-
updateExpressionType = forNode.update.type;
|
|
2298
|
-
updateExpressionId = ExpressionNode.generateId(forNode.update.type, module.file, updateLine, updateColumn);
|
|
2299
|
-
}
|
|
2300
|
-
}
|
|
2301
|
-
|
|
2302
|
-
// Extract test condition for while and do-while loops
|
|
2303
|
-
if (loopType === 'while' || loopType === 'do-while') {
|
|
2304
|
-
const condLoop = node as t.WhileStatement | t.DoWhileStatement;
|
|
2305
|
-
if (condLoop.test) {
|
|
2306
|
-
testLine = getLine(condLoop.test);
|
|
2307
|
-
testColumn = getColumn(condLoop.test);
|
|
2308
|
-
testExpressionType = condLoop.test.type;
|
|
2309
|
-
testExpressionId = ExpressionNode.generateId(condLoop.test.type, module.file, testLine, testColumn);
|
|
2310
|
-
}
|
|
2311
|
-
}
|
|
2312
|
-
|
|
2313
|
-
// Extract async flag for for-await-of (REG-284)
|
|
2314
|
-
let isAsync: boolean | undefined;
|
|
2315
|
-
if (loopType === 'for-of') {
|
|
2316
|
-
const forOfNode = node as t.ForOfStatement;
|
|
2317
|
-
isAsync = forOfNode.await === true ? true : undefined;
|
|
2318
|
-
}
|
|
2319
|
-
|
|
2320
|
-
// 3. Determine actual parent - use stack for nested loops, otherwise original parentScopeId
|
|
2321
|
-
const actualParentScopeId = (scopeIdStack && scopeIdStack.length > 0)
|
|
2322
|
-
? scopeIdStack[scopeIdStack.length - 1]
|
|
2323
|
-
: parentScopeId;
|
|
2324
|
-
|
|
2325
|
-
// 3.5. Extract condition expression for while/do-while/for loops (REG-280)
|
|
2326
|
-
// Note: for-in and for-of don't have test expressions (they use ITERATES_OVER instead)
|
|
2327
|
-
let conditionExpressionId: string | undefined;
|
|
2328
|
-
let conditionExpressionType: string | undefined;
|
|
2329
|
-
let conditionLine: number | undefined;
|
|
2330
|
-
let conditionColumn: number | undefined;
|
|
2331
|
-
|
|
2332
|
-
if (loopType === 'while' || loopType === 'do-while') {
|
|
2333
|
-
const testNode = (node as t.WhileStatement | t.DoWhileStatement).test;
|
|
2334
|
-
if (testNode) {
|
|
2335
|
-
const condResult = this.extractDiscriminantExpression(testNode, module);
|
|
2336
|
-
conditionExpressionId = condResult.id;
|
|
2337
|
-
conditionExpressionType = condResult.expressionType;
|
|
2338
|
-
conditionLine = condResult.line;
|
|
2339
|
-
conditionColumn = condResult.column;
|
|
2340
|
-
}
|
|
2341
|
-
} else if (loopType === 'for') {
|
|
2342
|
-
const forNode = node as t.ForStatement;
|
|
2343
|
-
// for loop test may be null (infinite loop: for(;;))
|
|
2344
|
-
if (forNode.test) {
|
|
2345
|
-
const condResult = this.extractDiscriminantExpression(forNode.test, module);
|
|
2346
|
-
conditionExpressionId = condResult.id;
|
|
2347
|
-
conditionExpressionType = condResult.expressionType;
|
|
2348
|
-
conditionLine = condResult.line;
|
|
2349
|
-
conditionColumn = condResult.column;
|
|
2350
|
-
}
|
|
2351
|
-
}
|
|
2352
|
-
|
|
2353
|
-
// 4. Push LOOP info
|
|
2354
|
-
loops.push({
|
|
2355
|
-
id: loopId,
|
|
2356
|
-
semanticId: loopId,
|
|
2357
|
-
type: 'LOOP',
|
|
2358
|
-
loopType,
|
|
2359
|
-
file: module.file,
|
|
2360
|
-
line: getLine(node),
|
|
2361
|
-
column: getColumn(node),
|
|
2362
|
-
parentScopeId: actualParentScopeId,
|
|
2363
|
-
iteratesOverName,
|
|
2364
|
-
iteratesOverLine,
|
|
2365
|
-
iteratesOverColumn,
|
|
2366
|
-
conditionExpressionId,
|
|
2367
|
-
conditionExpressionType,
|
|
2368
|
-
conditionLine,
|
|
2369
|
-
conditionColumn,
|
|
2370
|
-
// REG-282: init/test/update for classic for loops
|
|
2371
|
-
initVariableName,
|
|
2372
|
-
initLine,
|
|
2373
|
-
testExpressionId,
|
|
2374
|
-
testExpressionType,
|
|
2375
|
-
testLine,
|
|
2376
|
-
testColumn,
|
|
2377
|
-
updateExpressionId,
|
|
2378
|
-
updateExpressionType,
|
|
2379
|
-
updateLine,
|
|
2380
|
-
updateColumn,
|
|
2381
|
-
// REG-284: async flag for for-await-of
|
|
2382
|
-
async: isAsync
|
|
2383
|
-
});
|
|
2384
|
-
|
|
2385
|
-
// 5. Create body SCOPE (backward compatibility)
|
|
2386
|
-
const scopeId = `SCOPE#${scopeType}#${module.file}#${getLine(node)}:${scopeCounterRef.value++}`;
|
|
2387
|
-
const semanticId = this.generateSemanticId(scopeType, scopeTracker);
|
|
2388
|
-
scopes.push({
|
|
2389
|
-
id: scopeId,
|
|
2390
|
-
type: 'SCOPE',
|
|
2391
|
-
scopeType,
|
|
2392
|
-
semanticId,
|
|
2393
|
-
file: module.file,
|
|
2394
|
-
line: getLine(node),
|
|
2395
|
-
parentScopeId: loopId // Parent is LOOP, not original parentScopeId
|
|
2396
|
-
});
|
|
2397
|
-
|
|
2398
|
-
// 6. Push body SCOPE to scopeIdStack (for CONTAINS edges to nested items)
|
|
2399
|
-
// The body scope is the container for nested loops, not the LOOP itself
|
|
2400
|
-
if (scopeIdStack) {
|
|
2401
|
-
scopeIdStack.push(scopeId);
|
|
2402
|
-
}
|
|
2403
|
-
|
|
2404
|
-
// Enter scope for semantic ID generation
|
|
2405
|
-
if (scopeTracker) {
|
|
2406
|
-
scopeTracker.enterCountedScope(trackerScopeType);
|
|
2407
|
-
}
|
|
2408
|
-
},
|
|
2409
|
-
exit: () => {
|
|
2410
|
-
// Pop loop scope from stack
|
|
2411
|
-
if (scopeIdStack) {
|
|
2412
|
-
scopeIdStack.pop();
|
|
2413
|
-
}
|
|
2414
|
-
|
|
2415
|
-
// Exit scope
|
|
2416
|
-
if (scopeTracker) {
|
|
2417
|
-
scopeTracker.exitScope();
|
|
2418
|
-
}
|
|
2419
|
-
}
|
|
2420
|
-
};
|
|
2421
|
-
}
|
|
2422
|
-
|
|
2423
|
-
/**
|
|
2424
|
-
* Factory method to create TryStatement handler.
|
|
2425
|
-
* Creates TRY_BLOCK, CATCH_BLOCK, FINALLY_BLOCK nodes and body SCOPEs.
|
|
2426
|
-
* Does NOT use skip() - allows normal traversal for CallExpression/NewExpression visitors.
|
|
2427
|
-
*
|
|
2428
|
-
* Phase 4 (REG-267): Creates control flow nodes with HAS_CATCH and HAS_FINALLY edges.
|
|
2429
|
-
*
|
|
2430
|
-
* @param parentScopeId - Parent scope ID for the scope nodes
|
|
2431
|
-
* @param module - Module context
|
|
2432
|
-
* @param scopes - Collection to push scope nodes to
|
|
2433
|
-
* @param tryBlocks - Collection to push TRY_BLOCK nodes to
|
|
2434
|
-
* @param catchBlocks - Collection to push CATCH_BLOCK nodes to
|
|
2435
|
-
* @param finallyBlocks - Collection to push FINALLY_BLOCK nodes to
|
|
2436
|
-
* @param scopeCounterRef - Counter for unique scope IDs
|
|
2437
|
-
* @param tryBlockCounterRef - Counter for unique TRY_BLOCK IDs
|
|
2438
|
-
* @param catchBlockCounterRef - Counter for unique CATCH_BLOCK IDs
|
|
2439
|
-
* @param finallyBlockCounterRef - Counter for unique FINALLY_BLOCK IDs
|
|
2440
|
-
* @param scopeTracker - Tracker for semantic ID generation
|
|
2441
|
-
* @param tryScopeMap - Map to track try/catch/finally scope transitions
|
|
2442
|
-
* @param scopeIdStack - Stack for tracking current scope ID for CONTAINS edges
|
|
2443
|
-
*/
|
|
2444
|
-
private createTryStatementHandler(
|
|
2445
|
-
parentScopeId: string,
|
|
2446
|
-
module: VisitorModule,
|
|
2447
|
-
scopes: ScopeInfo[],
|
|
2448
|
-
tryBlocks: TryBlockInfo[],
|
|
2449
|
-
catchBlocks: CatchBlockInfo[],
|
|
2450
|
-
finallyBlocks: FinallyBlockInfo[],
|
|
2451
|
-
scopeCounterRef: CounterRef,
|
|
2452
|
-
tryBlockCounterRef: CounterRef,
|
|
2453
|
-
catchBlockCounterRef: CounterRef,
|
|
2454
|
-
finallyBlockCounterRef: CounterRef,
|
|
2455
|
-
scopeTracker: ScopeTracker | undefined,
|
|
2456
|
-
tryScopeMap: Map<t.TryStatement, TryScopeInfo>,
|
|
2457
|
-
scopeIdStack?: string[],
|
|
2458
|
-
controlFlowState?: { hasTryCatch: boolean; tryBlockDepth: number }
|
|
2459
|
-
): { enter: (tryPath: NodePath<t.TryStatement>) => void; exit: (tryPath: NodePath<t.TryStatement>) => void } {
|
|
2460
|
-
return {
|
|
2461
|
-
enter: (tryPath: NodePath<t.TryStatement>) => {
|
|
2462
|
-
const tryNode = tryPath.node;
|
|
2463
|
-
|
|
2464
|
-
// Phase 6 (REG-267): Mark that this function has try/catch
|
|
2465
|
-
if (controlFlowState) {
|
|
2466
|
-
controlFlowState.hasTryCatch = true;
|
|
2467
|
-
// REG-311: Increment try block depth for O(1) isInsideTry detection
|
|
2468
|
-
controlFlowState.tryBlockDepth++;
|
|
2469
|
-
}
|
|
2470
|
-
|
|
2471
|
-
// Determine actual parent - use stack for nested structures, otherwise original parentScopeId
|
|
2472
|
-
const actualParentScopeId = (scopeIdStack && scopeIdStack.length > 0)
|
|
2473
|
-
? scopeIdStack[scopeIdStack.length - 1]
|
|
2474
|
-
: parentScopeId;
|
|
2475
|
-
|
|
2476
|
-
// 1. Create TRY_BLOCK node
|
|
2477
|
-
const tryBlockCounter = tryBlockCounterRef.value++;
|
|
2478
|
-
const legacyTryBlockId = `${module.file}:TRY_BLOCK:${getLine(tryNode)}:${tryBlockCounter}`;
|
|
2479
|
-
const tryBlockId = scopeTracker
|
|
2480
|
-
? computeSemanticId('TRY_BLOCK', 'try', scopeTracker.getContext(), { discriminator: tryBlockCounter })
|
|
2481
|
-
: legacyTryBlockId;
|
|
2482
|
-
|
|
2483
|
-
tryBlocks.push({
|
|
2484
|
-
id: tryBlockId,
|
|
2485
|
-
semanticId: tryBlockId,
|
|
2486
|
-
type: 'TRY_BLOCK',
|
|
2487
|
-
file: module.file,
|
|
2488
|
-
line: getLine(tryNode),
|
|
2489
|
-
column: getColumn(tryNode),
|
|
2490
|
-
parentScopeId: actualParentScopeId
|
|
2491
|
-
});
|
|
2492
|
-
|
|
2493
|
-
// 2. Create try-body SCOPE (backward compatibility)
|
|
2494
|
-
// Parent is now TRY_BLOCK, not original parentScopeId
|
|
2495
|
-
const tryScopeId = `SCOPE#try-block#${module.file}#${getLine(tryNode)}:${scopeCounterRef.value++}`;
|
|
2496
|
-
const trySemanticId = this.generateSemanticId('try-block', scopeTracker);
|
|
2497
|
-
scopes.push({
|
|
2498
|
-
id: tryScopeId,
|
|
2499
|
-
type: 'SCOPE',
|
|
2500
|
-
scopeType: 'try-block',
|
|
2501
|
-
semanticId: trySemanticId,
|
|
2502
|
-
file: module.file,
|
|
2503
|
-
line: getLine(tryNode),
|
|
2504
|
-
parentScopeId: tryBlockId // Parent is TRY_BLOCK
|
|
2505
|
-
});
|
|
2506
|
-
|
|
2507
|
-
// 3. Create CATCH_BLOCK and catch-body SCOPE if handler exists
|
|
2508
|
-
let catchBlockId: string | null = null;
|
|
2509
|
-
let catchScopeId: string | null = null;
|
|
2510
|
-
if (tryNode.handler) {
|
|
2511
|
-
const catchClause = tryNode.handler;
|
|
2512
|
-
const catchBlockCounter = catchBlockCounterRef.value++;
|
|
2513
|
-
const legacyCatchBlockId = `${module.file}:CATCH_BLOCK:${getLine(catchClause)}:${catchBlockCounter}`;
|
|
2514
|
-
catchBlockId = scopeTracker
|
|
2515
|
-
? computeSemanticId('CATCH_BLOCK', 'catch', scopeTracker.getContext(), { discriminator: catchBlockCounter })
|
|
2516
|
-
: legacyCatchBlockId;
|
|
2517
|
-
|
|
2518
|
-
// Extract parameter name if present
|
|
2519
|
-
let parameterName: string | undefined;
|
|
2520
|
-
if (catchClause.param && t.isIdentifier(catchClause.param)) {
|
|
2521
|
-
parameterName = catchClause.param.name;
|
|
2522
|
-
}
|
|
2523
|
-
|
|
2524
|
-
catchBlocks.push({
|
|
2525
|
-
id: catchBlockId,
|
|
2526
|
-
semanticId: catchBlockId,
|
|
2527
|
-
type: 'CATCH_BLOCK',
|
|
2528
|
-
file: module.file,
|
|
2529
|
-
line: getLine(catchClause),
|
|
2530
|
-
column: getColumn(catchClause),
|
|
2531
|
-
parentScopeId,
|
|
2532
|
-
parentTryBlockId: tryBlockId,
|
|
2533
|
-
parameterName
|
|
2534
|
-
});
|
|
2535
|
-
|
|
2536
|
-
// Create catch-body SCOPE (backward compatibility)
|
|
2537
|
-
catchScopeId = `SCOPE#catch-block#${module.file}#${getLine(catchClause)}:${scopeCounterRef.value++}`;
|
|
2538
|
-
const catchSemanticId = this.generateSemanticId('catch-block', scopeTracker);
|
|
2539
|
-
scopes.push({
|
|
2540
|
-
id: catchScopeId,
|
|
2541
|
-
type: 'SCOPE',
|
|
2542
|
-
scopeType: 'catch-block',
|
|
2543
|
-
semanticId: catchSemanticId,
|
|
2544
|
-
file: module.file,
|
|
2545
|
-
line: getLine(catchClause),
|
|
2546
|
-
parentScopeId: catchBlockId // Parent is CATCH_BLOCK
|
|
2547
|
-
});
|
|
2548
|
-
}
|
|
2549
|
-
|
|
2550
|
-
// 4. Create FINALLY_BLOCK and finally-body SCOPE if finalizer exists
|
|
2551
|
-
let finallyBlockId: string | null = null;
|
|
2552
|
-
let finallyScopeId: string | null = null;
|
|
2553
|
-
if (tryNode.finalizer) {
|
|
2554
|
-
const finallyBlockCounter = finallyBlockCounterRef.value++;
|
|
2555
|
-
const legacyFinallyBlockId = `${module.file}:FINALLY_BLOCK:${getLine(tryNode.finalizer)}:${finallyBlockCounter}`;
|
|
2556
|
-
finallyBlockId = scopeTracker
|
|
2557
|
-
? computeSemanticId('FINALLY_BLOCK', 'finally', scopeTracker.getContext(), { discriminator: finallyBlockCounter })
|
|
2558
|
-
: legacyFinallyBlockId;
|
|
2559
|
-
|
|
2560
|
-
finallyBlocks.push({
|
|
2561
|
-
id: finallyBlockId,
|
|
2562
|
-
semanticId: finallyBlockId,
|
|
2563
|
-
type: 'FINALLY_BLOCK',
|
|
2564
|
-
file: module.file,
|
|
2565
|
-
line: getLine(tryNode.finalizer),
|
|
2566
|
-
column: getColumn(tryNode.finalizer),
|
|
2567
|
-
parentScopeId,
|
|
2568
|
-
parentTryBlockId: tryBlockId
|
|
2569
|
-
});
|
|
2570
|
-
|
|
2571
|
-
// Create finally-body SCOPE (backward compatibility)
|
|
2572
|
-
finallyScopeId = `SCOPE#finally-block#${module.file}#${getLine(tryNode.finalizer)}:${scopeCounterRef.value++}`;
|
|
2573
|
-
const finallySemanticId = this.generateSemanticId('finally-block', scopeTracker);
|
|
2574
|
-
scopes.push({
|
|
2575
|
-
id: finallyScopeId,
|
|
2576
|
-
type: 'SCOPE',
|
|
2577
|
-
scopeType: 'finally-block',
|
|
2578
|
-
semanticId: finallySemanticId,
|
|
2579
|
-
file: module.file,
|
|
2580
|
-
line: getLine(tryNode.finalizer),
|
|
2581
|
-
parentScopeId: finallyBlockId // Parent is FINALLY_BLOCK
|
|
2582
|
-
});
|
|
2583
|
-
}
|
|
2584
|
-
|
|
2585
|
-
// 5. Push try scope onto stack for CONTAINS edges
|
|
2586
|
-
if (scopeIdStack) {
|
|
2587
|
-
scopeIdStack.push(tryScopeId);
|
|
2588
|
-
}
|
|
2589
|
-
|
|
2590
|
-
// Enter try scope for semantic ID generation
|
|
2591
|
-
if (scopeTracker) {
|
|
2592
|
-
scopeTracker.enterCountedScope('try');
|
|
2593
|
-
}
|
|
2594
|
-
|
|
2595
|
-
// 6. Store scope info for catch/finally transitions
|
|
2596
|
-
tryScopeMap.set(tryNode, {
|
|
2597
|
-
tryScopeId,
|
|
2598
|
-
catchScopeId,
|
|
2599
|
-
finallyScopeId,
|
|
2600
|
-
currentBlock: 'try',
|
|
2601
|
-
tryBlockId,
|
|
2602
|
-
catchBlockId,
|
|
2603
|
-
finallyBlockId
|
|
2604
|
-
});
|
|
2605
|
-
},
|
|
2606
|
-
exit: (tryPath: NodePath<t.TryStatement>) => {
|
|
2607
|
-
const tryNode = tryPath.node;
|
|
2608
|
-
const _scopeInfo = tryScopeMap.get(tryNode);
|
|
2609
|
-
|
|
2610
|
-
// REG-311: Only decrement try block depth if we're still in 'try' block
|
|
2611
|
-
// (not transitioned to catch/finally, where we already decremented)
|
|
2612
|
-
if (controlFlowState && _scopeInfo?.currentBlock === 'try') {
|
|
2613
|
-
controlFlowState.tryBlockDepth--;
|
|
2614
|
-
}
|
|
2615
|
-
|
|
2616
|
-
// Pop the current scope from stack (could be try, catch, or finally)
|
|
2617
|
-
if (scopeIdStack) {
|
|
2618
|
-
scopeIdStack.pop();
|
|
2619
|
-
}
|
|
2620
|
-
|
|
2621
|
-
// Exit the current scope
|
|
2622
|
-
if (scopeTracker) {
|
|
2623
|
-
scopeTracker.exitScope();
|
|
2624
|
-
}
|
|
2625
|
-
|
|
2626
|
-
// Clean up
|
|
2627
|
-
tryScopeMap.delete(tryNode);
|
|
2628
|
-
}
|
|
2629
|
-
};
|
|
2630
|
-
}
|
|
2631
|
-
|
|
2632
|
-
/**
|
|
2633
|
-
* Factory method to create CatchClause handler.
|
|
2634
|
-
* Handles scope transition from try to catch and processes catch parameter.
|
|
2635
|
-
*
|
|
2636
|
-
* @param module - Module context
|
|
2637
|
-
* @param variableDeclarations - Collection to push variable declarations to
|
|
2638
|
-
* @param varDeclCounterRef - Counter for unique variable declaration IDs
|
|
2639
|
-
* @param scopeTracker - Tracker for semantic ID generation
|
|
2640
|
-
* @param tryScopeMap - Map to track try/catch/finally scope transitions
|
|
2641
|
-
* @param scopeIdStack - Stack for tracking current scope ID for CONTAINS edges
|
|
2642
|
-
*/
|
|
2643
|
-
private createCatchClauseHandler(
|
|
2644
|
-
module: VisitorModule,
|
|
2645
|
-
variableDeclarations: VariableDeclarationInfo[],
|
|
2646
|
-
varDeclCounterRef: CounterRef,
|
|
2647
|
-
scopeTracker: ScopeTracker | undefined,
|
|
2648
|
-
tryScopeMap: Map<t.TryStatement, TryScopeInfo>,
|
|
2649
|
-
scopeIdStack?: string[],
|
|
2650
|
-
controlFlowState?: { hasTryCatch: boolean; tryBlockDepth: number }
|
|
2651
|
-
): { enter: (catchPath: NodePath<t.CatchClause>) => void } {
|
|
2652
|
-
return {
|
|
2653
|
-
enter: (catchPath: NodePath<t.CatchClause>) => {
|
|
2654
|
-
const catchNode = catchPath.node;
|
|
2655
|
-
const parent = catchPath.parent;
|
|
2656
|
-
|
|
2657
|
-
if (!t.isTryStatement(parent)) return;
|
|
2658
|
-
|
|
2659
|
-
const scopeInfo = tryScopeMap.get(parent);
|
|
2660
|
-
if (!scopeInfo || !scopeInfo.catchScopeId) return;
|
|
2661
|
-
|
|
2662
|
-
// Transition from try scope to catch scope
|
|
2663
|
-
if (scopeInfo.currentBlock === 'try') {
|
|
2664
|
-
// Pop try scope, push catch scope
|
|
2665
|
-
if (scopeIdStack) {
|
|
2666
|
-
scopeIdStack.pop();
|
|
2667
|
-
scopeIdStack.push(scopeInfo.catchScopeId);
|
|
2668
|
-
}
|
|
2669
|
-
|
|
2670
|
-
// Exit try scope, enter catch scope for semantic ID
|
|
2671
|
-
if (scopeTracker) {
|
|
2672
|
-
scopeTracker.exitScope();
|
|
2673
|
-
scopeTracker.enterCountedScope('catch');
|
|
2674
|
-
}
|
|
2675
|
-
|
|
2676
|
-
// REG-311: Decrement tryBlockDepth when leaving try block for catch
|
|
2677
|
-
// Calls in catch block should NOT have isInsideTry=true
|
|
2678
|
-
if (controlFlowState) {
|
|
2679
|
-
controlFlowState.tryBlockDepth--;
|
|
2680
|
-
}
|
|
2681
|
-
|
|
2682
|
-
scopeInfo.currentBlock = 'catch';
|
|
2683
|
-
}
|
|
2684
|
-
|
|
2685
|
-
// Handle catch parameter (e.g., catch (e) or catch ({ message }))
|
|
2686
|
-
if (catchNode.param) {
|
|
2687
|
-
const errorVarInfo = this.extractVariableNamesFromPattern(catchNode.param);
|
|
2688
|
-
|
|
2689
|
-
errorVarInfo.forEach(varInfo => {
|
|
2690
|
-
const legacyId = `VARIABLE#${varInfo.name}#${module.file}#${varInfo.loc.start.line}:${varInfo.loc.start.column}:${varDeclCounterRef.value++}`;
|
|
2691
|
-
const varId = scopeTracker
|
|
2692
|
-
? computeSemanticId('VARIABLE', varInfo.name, scopeTracker.getContext())
|
|
2693
|
-
: legacyId;
|
|
2694
|
-
|
|
2695
|
-
variableDeclarations.push({
|
|
2696
|
-
id: varId,
|
|
2697
|
-
type: 'VARIABLE',
|
|
2698
|
-
name: varInfo.name,
|
|
2699
|
-
file: module.file,
|
|
2700
|
-
line: varInfo.loc.start.line,
|
|
2701
|
-
parentScopeId: scopeInfo.catchScopeId!
|
|
2702
|
-
});
|
|
2703
|
-
});
|
|
2704
|
-
}
|
|
2705
|
-
}
|
|
2706
|
-
};
|
|
2707
|
-
}
|
|
2708
2170
|
|
|
2709
2171
|
/**
|
|
2710
2172
|
* Handles SwitchStatement nodes.
|
|
@@ -3219,1635 +2681,144 @@ export class JSASTAnalyzer extends Plugin {
|
|
|
3219
2681
|
};
|
|
3220
2682
|
}
|
|
3221
2683
|
|
|
2684
|
+
|
|
3222
2685
|
/**
|
|
3223
|
-
*
|
|
3224
|
-
*
|
|
3225
|
-
* Tracks if/else scope transitions via ifElseScopeMap.
|
|
3226
|
-
*
|
|
3227
|
-
* Phase 3 (REG-267): Creates BRANCH node with branchType='if' and
|
|
3228
|
-
* HAS_CONSEQUENT/HAS_ALTERNATE edges to body SCOPEs.
|
|
2686
|
+
* Анализирует тело функции и извлекает переменные, вызовы, условные блоки.
|
|
2687
|
+
* Uses ScopeTracker from collections for semantic ID generation.
|
|
3229
2688
|
*
|
|
3230
|
-
*
|
|
3231
|
-
*
|
|
3232
|
-
*
|
|
3233
|
-
* @param branches - Collection to push BRANCH nodes to
|
|
3234
|
-
* @param ifScopeCounterRef - Counter for unique if scope IDs
|
|
3235
|
-
* @param branchCounterRef - Counter for unique BRANCH IDs
|
|
3236
|
-
* @param scopeTracker - Tracker for semantic ID generation
|
|
3237
|
-
* @param sourceCode - Source code for extracting condition text
|
|
3238
|
-
* @param ifElseScopeMap - Map to track if/else scope transitions
|
|
3239
|
-
* @param scopeIdStack - Stack for tracking current scope ID for CONTAINS edges
|
|
2689
|
+
* REG-422: Delegates traversal to extracted handler classes.
|
|
2690
|
+
* Local state is encapsulated in FunctionBodyContext; each handler
|
|
2691
|
+
* contributes a Visitor fragment that is merged into a single traversal.
|
|
3240
2692
|
*/
|
|
3241
|
-
|
|
2693
|
+
analyzeFunctionBody(
|
|
2694
|
+
funcPath: NodePath<t.Function | t.StaticBlock>,
|
|
3242
2695
|
parentScopeId: string,
|
|
3243
2696
|
module: VisitorModule,
|
|
3244
|
-
|
|
3245
|
-
|
|
3246
|
-
|
|
3247
|
-
|
|
3248
|
-
|
|
3249
|
-
|
|
3250
|
-
|
|
3251
|
-
|
|
3252
|
-
|
|
3253
|
-
|
|
3254
|
-
|
|
3255
|
-
|
|
3256
|
-
|
|
3257
|
-
|
|
3258
|
-
|
|
3259
|
-
|
|
3260
|
-
|
|
3261
|
-
|
|
3262
|
-
|
|
3263
|
-
|
|
3264
|
-
|
|
3265
|
-
|
|
3266
|
-
|
|
2697
|
+
collections: VisitorCollections
|
|
2698
|
+
): void {
|
|
2699
|
+
// 1. Create context (replaces ~260 lines of local var declarations)
|
|
2700
|
+
const ctx = createFunctionBodyContext(
|
|
2701
|
+
funcPath, parentScopeId, module, collections,
|
|
2702
|
+
(collections.functions ?? []) as FunctionInfo[],
|
|
2703
|
+
extractNamesFromPattern
|
|
2704
|
+
);
|
|
2705
|
+
|
|
2706
|
+
// 2. Handle implicit return for THIS arrow function if it has an expression body
|
|
2707
|
+
// e.g., `const double = x => x * 2;` — this needs this.extractReturnExpressionInfo,
|
|
2708
|
+
// so it stays here rather than in a handler.
|
|
2709
|
+
if (t.isArrowFunctionExpression(ctx.funcNode) && !t.isBlockStatement(ctx.funcNode.body) && ctx.currentFunctionId) {
|
|
2710
|
+
const bodyExpr = ctx.funcNode.body;
|
|
2711
|
+
const exprInfo = this.extractReturnExpressionInfo(
|
|
2712
|
+
bodyExpr, module, ctx.literals, ctx.literalCounterRef, ctx.funcLine, ctx.funcColumn, 'implicit_return'
|
|
2713
|
+
);
|
|
2714
|
+
ctx.returnStatements.push({
|
|
2715
|
+
parentFunctionId: ctx.currentFunctionId,
|
|
2716
|
+
file: module.file,
|
|
2717
|
+
line: getLine(bodyExpr),
|
|
2718
|
+
column: getColumn(bodyExpr),
|
|
2719
|
+
returnValueType: 'NONE',
|
|
2720
|
+
isImplicitReturn: true,
|
|
2721
|
+
...exprInfo,
|
|
2722
|
+
});
|
|
2723
|
+
}
|
|
3267
2724
|
|
|
3268
|
-
|
|
3269
|
-
|
|
3270
|
-
|
|
3271
|
-
|
|
3272
|
-
|
|
3273
|
-
|
|
3274
|
-
|
|
3275
|
-
|
|
3276
|
-
|
|
3277
|
-
|
|
3278
|
-
|
|
3279
|
-
|
|
3280
|
-
|
|
3281
|
-
|
|
3282
|
-
|
|
3283
|
-
|
|
3284
|
-
|
|
3285
|
-
|
|
3286
|
-
|
|
3287
|
-
|
|
3288
|
-
|
|
3289
|
-
|
|
2725
|
+
// 3. Create handlers and merge their visitors into a single traversal
|
|
2726
|
+
// Cast to AnalyzerDelegate — the interface declares the same methods that exist
|
|
2727
|
+
// on this class as private. The cast is safe because the shape matches exactly.
|
|
2728
|
+
const delegate = this as unknown as AnalyzerDelegate;
|
|
2729
|
+
const handlers: FunctionBodyHandler[] = [
|
|
2730
|
+
new VariableHandler(ctx, delegate),
|
|
2731
|
+
new ReturnYieldHandler(ctx, delegate),
|
|
2732
|
+
new ThrowHandler(ctx, delegate),
|
|
2733
|
+
new NestedFunctionHandler(ctx, delegate),
|
|
2734
|
+
new PropertyAccessHandler(ctx, delegate),
|
|
2735
|
+
new NewExpressionHandler(ctx, delegate),
|
|
2736
|
+
new CallExpressionHandler(ctx, delegate),
|
|
2737
|
+
new LoopHandler(ctx, delegate),
|
|
2738
|
+
new TryCatchHandler(ctx, delegate),
|
|
2739
|
+
new BranchHandler(ctx, delegate),
|
|
2740
|
+
];
|
|
2741
|
+
|
|
2742
|
+
const mergedVisitor: Visitor = {};
|
|
2743
|
+
for (const handler of handlers) {
|
|
2744
|
+
Object.assign(mergedVisitor, handler.getHandlers());
|
|
2745
|
+
}
|
|
2746
|
+
|
|
2747
|
+
// 4. Single traversal over the function body
|
|
2748
|
+
funcPath.traverse(mergedVisitor);
|
|
2749
|
+
|
|
2750
|
+
// 5. Post-traverse: collect CATCHES_FROM info for try/catch blocks
|
|
2751
|
+
if (ctx.functionPath) {
|
|
2752
|
+
this.collectCatchesFromInfo(
|
|
2753
|
+
ctx.functionPath,
|
|
2754
|
+
ctx.catchBlocks,
|
|
2755
|
+
ctx.callSites,
|
|
2756
|
+
ctx.methodCalls,
|
|
2757
|
+
ctx.constructorCalls,
|
|
2758
|
+
ctx.catchesFromInfos,
|
|
2759
|
+
module
|
|
2760
|
+
);
|
|
2761
|
+
}
|
|
3290
2762
|
|
|
3291
|
-
|
|
3292
|
-
|
|
3293
|
-
|
|
3294
|
-
const branchId = scopeTracker
|
|
3295
|
-
? computeSemanticId('BRANCH', 'if', scopeTracker.getContext(), { discriminator: branchCounter })
|
|
3296
|
-
: legacyBranchId;
|
|
3297
|
-
|
|
3298
|
-
// 2. Extract condition expression info for HAS_CONDITION edge
|
|
3299
|
-
const conditionResult = this.extractDiscriminantExpression(ifNode.test, module);
|
|
3300
|
-
|
|
3301
|
-
// For else-if, get the parent branch ID
|
|
3302
|
-
const isAlternateOfBranchId = isElseIf
|
|
3303
|
-
? ifElseScopeMap.get(ifPath.parent as t.IfStatement)?.branchId
|
|
3304
|
-
: undefined;
|
|
3305
|
-
|
|
3306
|
-
branches.push({
|
|
3307
|
-
id: branchId,
|
|
3308
|
-
semanticId: branchId,
|
|
3309
|
-
type: 'BRANCH',
|
|
3310
|
-
branchType: 'if',
|
|
3311
|
-
file: module.file,
|
|
3312
|
-
line: getLine(ifNode),
|
|
3313
|
-
parentScopeId: actualParentScopeId,
|
|
3314
|
-
discriminantExpressionId: conditionResult.id,
|
|
3315
|
-
discriminantExpressionType: conditionResult.expressionType,
|
|
3316
|
-
discriminantLine: conditionResult.line,
|
|
3317
|
-
discriminantColumn: conditionResult.column,
|
|
3318
|
-
isAlternateOfBranchId
|
|
3319
|
-
});
|
|
2763
|
+
// 6. Post-traverse: Attach control flow metadata to the function node
|
|
2764
|
+
this.attachControlFlowMetadata(ctx);
|
|
2765
|
+
}
|
|
3320
2766
|
|
|
3321
|
-
|
|
3322
|
-
|
|
3323
|
-
|
|
3324
|
-
|
|
3325
|
-
|
|
3326
|
-
|
|
3327
|
-
|
|
3328
|
-
|
|
3329
|
-
|
|
3330
|
-
|
|
3331
|
-
|
|
3332
|
-
|
|
3333
|
-
|
|
3334
|
-
|
|
3335
|
-
|
|
3336
|
-
|
|
3337
|
-
|
|
3338
|
-
|
|
3339
|
-
|
|
3340
|
-
|
|
3341
|
-
|
|
3342
|
-
|
|
2767
|
+
/**
|
|
2768
|
+
* Attach control flow metadata (cyclomatic complexity, error tracking, HOF bindings)
|
|
2769
|
+
* to the matching function node after traversal completes.
|
|
2770
|
+
*/
|
|
2771
|
+
private attachControlFlowMetadata(ctx: FunctionBodyContext): void {
|
|
2772
|
+
if (!ctx.matchingFunction) return;
|
|
2773
|
+
|
|
2774
|
+
const cyclomaticComplexity = 1 +
|
|
2775
|
+
ctx.controlFlowState.branchCount +
|
|
2776
|
+
ctx.controlFlowState.loopCount +
|
|
2777
|
+
ctx.controlFlowState.caseCount +
|
|
2778
|
+
ctx.controlFlowState.logicalOpCount;
|
|
2779
|
+
|
|
2780
|
+
// REG-311: Collect rejection info for this function
|
|
2781
|
+
const functionRejectionPatterns = ctx.rejectionPatterns.filter(p => p.functionId === ctx.matchingFunction!.id);
|
|
2782
|
+
const asyncPatterns = functionRejectionPatterns.filter(p => p.isAsync);
|
|
2783
|
+
const syncPatterns = functionRejectionPatterns.filter(p => !p.isAsync);
|
|
2784
|
+
const canReject = asyncPatterns.length > 0;
|
|
2785
|
+
const hasAsyncThrow = asyncPatterns.some(p => p.rejectionType === 'async_throw');
|
|
2786
|
+
const rejectedBuiltinErrors = [...new Set(
|
|
2787
|
+
asyncPatterns
|
|
2788
|
+
.filter(p => p.errorClassName !== null)
|
|
2789
|
+
.map(p => p.errorClassName!)
|
|
2790
|
+
)];
|
|
2791
|
+
// REG-286: Sync throw error tracking
|
|
2792
|
+
const thrownBuiltinErrors = [...new Set(
|
|
2793
|
+
syncPatterns
|
|
2794
|
+
.filter(p => p.errorClassName !== null)
|
|
2795
|
+
.map(p => p.errorClassName!)
|
|
2796
|
+
)];
|
|
2797
|
+
|
|
2798
|
+
ctx.matchingFunction.controlFlow = {
|
|
2799
|
+
hasBranches: ctx.controlFlowState.branchCount > 0,
|
|
2800
|
+
hasLoops: ctx.controlFlowState.loopCount > 0,
|
|
2801
|
+
hasTryCatch: ctx.controlFlowState.hasTryCatch,
|
|
2802
|
+
hasEarlyReturn: ctx.controlFlowState.hasEarlyReturn,
|
|
2803
|
+
hasThrow: ctx.controlFlowState.hasThrow,
|
|
2804
|
+
cyclomaticComplexity,
|
|
2805
|
+
// REG-311: Async error tracking
|
|
2806
|
+
canReject,
|
|
2807
|
+
hasAsyncThrow,
|
|
2808
|
+
rejectedBuiltinErrors: rejectedBuiltinErrors.length > 0 ? rejectedBuiltinErrors : undefined,
|
|
2809
|
+
// REG-286: Sync throw tracking
|
|
2810
|
+
thrownBuiltinErrors: thrownBuiltinErrors.length > 0 ? thrownBuiltinErrors : undefined
|
|
2811
|
+
};
|
|
3343
2812
|
|
|
3344
|
-
|
|
3345
|
-
|
|
3346
|
-
|
|
3347
|
-
|
|
3348
|
-
|
|
3349
|
-
|
|
3350
|
-
|
|
3351
|
-
|
|
3352
|
-
|
|
3353
|
-
|
|
3354
|
-
// 5. Handle else branch if present
|
|
3355
|
-
let elseScopeId: string | null = null;
|
|
3356
|
-
if (ifNode.alternate && !t.isIfStatement(ifNode.alternate)) {
|
|
3357
|
-
// Only create else scope for actual else block, not else-if
|
|
3358
|
-
const elseCounterId = ifScopeCounterRef.value++;
|
|
3359
|
-
elseScopeId = `SCOPE#else#${module.file}#${getLine(ifNode.alternate)}:${getColumn(ifNode.alternate)}:${elseCounterId}`;
|
|
3360
|
-
|
|
3361
|
-
const negatedConstraints = constraints.length > 0 ? ConditionParser.negate(constraints) : undefined;
|
|
3362
|
-
const elseSemanticId = this.generateSemanticId('else_statement', scopeTracker);
|
|
3363
|
-
|
|
3364
|
-
scopes.push({
|
|
3365
|
-
id: elseScopeId,
|
|
3366
|
-
type: 'SCOPE',
|
|
3367
|
-
scopeType: 'else_statement',
|
|
3368
|
-
name: `else:${getLine(ifNode.alternate)}:${getColumn(ifNode.alternate)}:${elseCounterId}`,
|
|
3369
|
-
semanticId: elseSemanticId,
|
|
3370
|
-
conditional: true,
|
|
3371
|
-
constraints: negatedConstraints,
|
|
3372
|
-
file: module.file,
|
|
3373
|
-
line: getLine(ifNode.alternate),
|
|
3374
|
-
parentScopeId: branchId // Parent is BRANCH, not original parentScopeId
|
|
3375
|
-
});
|
|
3376
|
-
|
|
3377
|
-
// Store info to switch to else scope when we enter alternate
|
|
3378
|
-
ifElseScopeMap.set(ifNode, { inElse: false, hasElse: true, ifScopeId, elseScopeId, branchId });
|
|
3379
|
-
} else {
|
|
3380
|
-
ifElseScopeMap.set(ifNode, { inElse: false, hasElse: false, ifScopeId, elseScopeId: null, branchId });
|
|
3381
|
-
}
|
|
3382
|
-
},
|
|
3383
|
-
exit: (ifPath: NodePath<t.IfStatement>) => {
|
|
3384
|
-
const ifNode = ifPath.node;
|
|
3385
|
-
|
|
3386
|
-
// Pop scope from stack (either if or else, depending on what we're exiting)
|
|
3387
|
-
if (scopeIdStack) {
|
|
3388
|
-
scopeIdStack.pop();
|
|
3389
|
-
}
|
|
3390
|
-
|
|
3391
|
-
// Exit the current scope (either if or else)
|
|
3392
|
-
if (scopeTracker) {
|
|
3393
|
-
scopeTracker.exitScope();
|
|
3394
|
-
}
|
|
3395
|
-
|
|
3396
|
-
// If we were in else, we already exited else scope
|
|
3397
|
-
// If we only had if, we exit if scope (done above)
|
|
3398
|
-
ifElseScopeMap.delete(ifNode);
|
|
3399
|
-
}
|
|
3400
|
-
};
|
|
3401
|
-
}
|
|
3402
|
-
|
|
3403
|
-
/**
|
|
3404
|
-
* Factory method to create ConditionalExpression (ternary) handler.
|
|
3405
|
-
* Creates BRANCH nodes with branchType='ternary' and increments branchCount for cyclomatic complexity.
|
|
3406
|
-
*
|
|
3407
|
-
* Key difference from IfStatement: ternary has EXPRESSIONS as branches, not SCOPE blocks.
|
|
3408
|
-
* We store consequentExpressionId and alternateExpressionId in BranchInfo for HAS_CONSEQUENT/HAS_ALTERNATE edges.
|
|
3409
|
-
*
|
|
3410
|
-
* @param parentScopeId - Parent scope ID for the BRANCH node
|
|
3411
|
-
* @param module - Module context
|
|
3412
|
-
* @param branches - Collection to push BRANCH nodes to
|
|
3413
|
-
* @param branchCounterRef - Counter for unique BRANCH IDs
|
|
3414
|
-
* @param scopeTracker - Tracker for semantic ID generation
|
|
3415
|
-
* @param scopeIdStack - Stack for tracking current scope ID for CONTAINS edges
|
|
3416
|
-
* @param controlFlowState - State for tracking control flow metrics (complexity)
|
|
3417
|
-
* @param countLogicalOperators - Function to count logical operators in condition
|
|
3418
|
-
*/
|
|
3419
|
-
private createConditionalExpressionHandler(
|
|
3420
|
-
parentScopeId: string,
|
|
3421
|
-
module: VisitorModule,
|
|
3422
|
-
branches: BranchInfo[],
|
|
3423
|
-
branchCounterRef: CounterRef,
|
|
3424
|
-
scopeTracker: ScopeTracker | undefined,
|
|
3425
|
-
scopeIdStack?: string[],
|
|
3426
|
-
controlFlowState?: { branchCount: number; logicalOpCount: number },
|
|
3427
|
-
countLogicalOperators?: (node: t.Expression) => number
|
|
3428
|
-
): (condPath: NodePath<t.ConditionalExpression>) => void {
|
|
3429
|
-
return (condPath: NodePath<t.ConditionalExpression>) => {
|
|
3430
|
-
const condNode = condPath.node;
|
|
3431
|
-
|
|
3432
|
-
// Increment branch count for cyclomatic complexity
|
|
3433
|
-
if (controlFlowState) {
|
|
3434
|
-
controlFlowState.branchCount++;
|
|
3435
|
-
// Count logical operators in the test condition (e.g., a && b ? x : y)
|
|
3436
|
-
if (countLogicalOperators) {
|
|
3437
|
-
controlFlowState.logicalOpCount += countLogicalOperators(condNode.test);
|
|
3438
|
-
}
|
|
3439
|
-
}
|
|
3440
|
-
|
|
3441
|
-
// Determine parent scope from stack or fallback
|
|
3442
|
-
const actualParentScopeId = (scopeIdStack && scopeIdStack.length > 0)
|
|
3443
|
-
? scopeIdStack[scopeIdStack.length - 1]
|
|
3444
|
-
: parentScopeId;
|
|
3445
|
-
|
|
3446
|
-
// Create BRANCH node with branchType='ternary'
|
|
3447
|
-
const branchCounter = branchCounterRef.value++;
|
|
3448
|
-
const legacyBranchId = `${module.file}:BRANCH:ternary:${getLine(condNode)}:${branchCounter}`;
|
|
3449
|
-
const branchId = scopeTracker
|
|
3450
|
-
? computeSemanticId('BRANCH', 'ternary', scopeTracker.getContext(), { discriminator: branchCounter })
|
|
3451
|
-
: legacyBranchId;
|
|
3452
|
-
|
|
3453
|
-
// Extract condition expression info for HAS_CONDITION edge
|
|
3454
|
-
const conditionResult = this.extractDiscriminantExpression(condNode.test, module);
|
|
3455
|
-
|
|
3456
|
-
// Generate expression IDs for consequent and alternate
|
|
3457
|
-
const consequentLine = getLine(condNode.consequent);
|
|
3458
|
-
const consequentColumn = getColumn(condNode.consequent);
|
|
3459
|
-
const consequentExpressionId = ExpressionNode.generateId(
|
|
3460
|
-
condNode.consequent.type,
|
|
3461
|
-
module.file,
|
|
3462
|
-
consequentLine,
|
|
3463
|
-
consequentColumn
|
|
3464
|
-
);
|
|
3465
|
-
|
|
3466
|
-
const alternateLine = getLine(condNode.alternate);
|
|
3467
|
-
const alternateColumn = getColumn(condNode.alternate);
|
|
3468
|
-
const alternateExpressionId = ExpressionNode.generateId(
|
|
3469
|
-
condNode.alternate.type,
|
|
3470
|
-
module.file,
|
|
3471
|
-
alternateLine,
|
|
3472
|
-
alternateColumn
|
|
3473
|
-
);
|
|
3474
|
-
|
|
3475
|
-
branches.push({
|
|
3476
|
-
id: branchId,
|
|
3477
|
-
semanticId: branchId,
|
|
3478
|
-
type: 'BRANCH',
|
|
3479
|
-
branchType: 'ternary',
|
|
3480
|
-
file: module.file,
|
|
3481
|
-
line: getLine(condNode),
|
|
3482
|
-
parentScopeId: actualParentScopeId,
|
|
3483
|
-
discriminantExpressionId: conditionResult.id,
|
|
3484
|
-
discriminantExpressionType: conditionResult.expressionType,
|
|
3485
|
-
discriminantLine: conditionResult.line,
|
|
3486
|
-
discriminantColumn: conditionResult.column,
|
|
3487
|
-
consequentExpressionId,
|
|
3488
|
-
alternateExpressionId
|
|
3489
|
-
});
|
|
3490
|
-
};
|
|
3491
|
-
}
|
|
3492
|
-
|
|
3493
|
-
/**
|
|
3494
|
-
* Factory method to create BlockStatement handler for tracking if/else and try/finally transitions.
|
|
3495
|
-
* When entering an else block, switches scope from if to else.
|
|
3496
|
-
* When entering a finally block, switches scope from try/catch to finally.
|
|
3497
|
-
*
|
|
3498
|
-
* @param scopeTracker - Tracker for semantic ID generation
|
|
3499
|
-
* @param ifElseScopeMap - Map to track if/else scope transitions
|
|
3500
|
-
* @param tryScopeMap - Map to track try/catch/finally scope transitions
|
|
3501
|
-
* @param scopeIdStack - Stack for tracking current scope ID for CONTAINS edges
|
|
3502
|
-
*/
|
|
3503
|
-
private createBlockStatementHandler(
|
|
3504
|
-
scopeTracker: ScopeTracker | undefined,
|
|
3505
|
-
ifElseScopeMap: Map<t.IfStatement, IfElseScopeInfo>,
|
|
3506
|
-
tryScopeMap: Map<t.TryStatement, TryScopeInfo>,
|
|
3507
|
-
scopeIdStack?: string[]
|
|
3508
|
-
): { enter: (blockPath: NodePath<t.BlockStatement>) => void } {
|
|
3509
|
-
return {
|
|
3510
|
-
enter: (blockPath: NodePath<t.BlockStatement>) => {
|
|
3511
|
-
const parent = blockPath.parent;
|
|
3512
|
-
|
|
3513
|
-
// Check if this block is the alternate of an IfStatement
|
|
3514
|
-
if (t.isIfStatement(parent) && parent.alternate === blockPath.node) {
|
|
3515
|
-
const scopeInfo = ifElseScopeMap.get(parent);
|
|
3516
|
-
if (scopeInfo && scopeInfo.hasElse && !scopeInfo.inElse) {
|
|
3517
|
-
// Swap if-scope for else-scope on the stack
|
|
3518
|
-
if (scopeIdStack && scopeInfo.elseScopeId) {
|
|
3519
|
-
scopeIdStack.pop(); // Remove if-scope
|
|
3520
|
-
scopeIdStack.push(scopeInfo.elseScopeId); // Push else-scope
|
|
3521
|
-
}
|
|
3522
|
-
|
|
3523
|
-
// Exit if scope, enter else scope for semantic ID tracking
|
|
3524
|
-
if (scopeTracker) {
|
|
3525
|
-
scopeTracker.exitScope();
|
|
3526
|
-
scopeTracker.enterCountedScope('else');
|
|
3527
|
-
}
|
|
3528
|
-
scopeInfo.inElse = true;
|
|
3529
|
-
}
|
|
3530
|
-
}
|
|
3531
|
-
|
|
3532
|
-
// Check if this block is the finalizer of a TryStatement
|
|
3533
|
-
if (t.isTryStatement(parent) && parent.finalizer === blockPath.node) {
|
|
3534
|
-
const scopeInfo = tryScopeMap.get(parent);
|
|
3535
|
-
if (scopeInfo && scopeInfo.finallyScopeId && scopeInfo.currentBlock !== 'finally') {
|
|
3536
|
-
// Pop current scope (try or catch), push finally scope
|
|
3537
|
-
if (scopeIdStack) {
|
|
3538
|
-
scopeIdStack.pop();
|
|
3539
|
-
scopeIdStack.push(scopeInfo.finallyScopeId);
|
|
3540
|
-
}
|
|
3541
|
-
|
|
3542
|
-
// Exit current scope, enter finally scope for semantic ID tracking
|
|
3543
|
-
if (scopeTracker) {
|
|
3544
|
-
scopeTracker.exitScope();
|
|
3545
|
-
scopeTracker.enterCountedScope('finally');
|
|
3546
|
-
}
|
|
3547
|
-
scopeInfo.currentBlock = 'finally';
|
|
3548
|
-
}
|
|
3549
|
-
}
|
|
3550
|
-
}
|
|
3551
|
-
};
|
|
3552
|
-
}
|
|
3553
|
-
|
|
3554
|
-
/**
|
|
3555
|
-
* Анализирует тело функции и извлекает переменные, вызовы, условные блоки.
|
|
3556
|
-
* Uses ScopeTracker from collections for semantic ID generation.
|
|
3557
|
-
*/
|
|
3558
|
-
analyzeFunctionBody(
|
|
3559
|
-
funcPath: NodePath<t.Function | t.StaticBlock>,
|
|
3560
|
-
parentScopeId: string,
|
|
3561
|
-
module: VisitorModule,
|
|
3562
|
-
collections: VisitorCollections
|
|
3563
|
-
): void {
|
|
3564
|
-
// Extract with defaults for optional properties
|
|
3565
|
-
const functions = (collections.functions ?? []) as FunctionInfo[];
|
|
3566
|
-
const scopes = (collections.scopes ?? []) as ScopeInfo[];
|
|
3567
|
-
const variableDeclarations = (collections.variableDeclarations ?? []) as VariableDeclarationInfo[];
|
|
3568
|
-
const callSites = (collections.callSites ?? []) as CallSiteInfo[];
|
|
3569
|
-
const methodCalls = (collections.methodCalls ?? []) as MethodCallInfo[];
|
|
3570
|
-
const _eventListeners = (collections.eventListeners ?? []) as EventListenerInfo[];
|
|
3571
|
-
const _methodCallbacks = (collections.methodCallbacks ?? []) as MethodCallbackInfo[];
|
|
3572
|
-
const classInstantiations = (collections.classInstantiations ?? []) as ClassInstantiationInfo[];
|
|
3573
|
-
const constructorCalls = (collections.constructorCalls ?? []) as ConstructorCallInfo[];
|
|
3574
|
-
const _httpRequests = (collections.httpRequests ?? []) as HttpRequestInfo[];
|
|
3575
|
-
const literals = (collections.literals ?? []) as LiteralInfo[];
|
|
3576
|
-
const variableAssignments = (collections.variableAssignments ?? []) as VariableAssignmentInfo[];
|
|
3577
|
-
const ifScopeCounterRef = (collections.ifScopeCounterRef ?? { value: 0 }) as CounterRef;
|
|
3578
|
-
const scopeCounterRef = (collections.scopeCounterRef ?? { value: 0 }) as CounterRef;
|
|
3579
|
-
const varDeclCounterRef = (collections.varDeclCounterRef ?? { value: 0 }) as CounterRef;
|
|
3580
|
-
const callSiteCounterRef = (collections.callSiteCounterRef ?? { value: 0 }) as CounterRef;
|
|
3581
|
-
const functionCounterRef = (collections.functionCounterRef ?? { value: 0 }) as CounterRef;
|
|
3582
|
-
const _httpRequestCounterRef = (collections.httpRequestCounterRef ?? { value: 0 }) as CounterRef;
|
|
3583
|
-
const literalCounterRef = (collections.literalCounterRef ?? { value: 0 }) as CounterRef;
|
|
3584
|
-
const _anonymousFunctionCounterRef = (collections.anonymousFunctionCounterRef ?? { value: 0 }) as CounterRef;
|
|
3585
|
-
const scopeTracker = collections.scopeTracker as ScopeTracker | undefined;
|
|
3586
|
-
// Object literal tracking (REG-328)
|
|
3587
|
-
if (!collections.objectLiterals) {
|
|
3588
|
-
collections.objectLiterals = [];
|
|
3589
|
-
}
|
|
3590
|
-
if (!collections.objectProperties) {
|
|
3591
|
-
collections.objectProperties = [];
|
|
3592
|
-
}
|
|
3593
|
-
if (!collections.objectLiteralCounterRef) {
|
|
3594
|
-
collections.objectLiteralCounterRef = { value: 0 };
|
|
3595
|
-
}
|
|
3596
|
-
const objectLiterals = collections.objectLiterals as ObjectLiteralInfo[];
|
|
3597
|
-
const objectProperties = collections.objectProperties as ObjectPropertyInfo[];
|
|
3598
|
-
const objectLiteralCounterRef = collections.objectLiteralCounterRef as CounterRef;
|
|
3599
|
-
const returnStatements = (collections.returnStatements ?? []) as ReturnStatementInfo[];
|
|
3600
|
-
// Initialize yieldExpressions if not exist to ensure nested function calls share same array
|
|
3601
|
-
if (!collections.yieldExpressions) {
|
|
3602
|
-
collections.yieldExpressions = [];
|
|
3603
|
-
}
|
|
3604
|
-
const yieldExpressions = collections.yieldExpressions as YieldExpressionInfo[];
|
|
3605
|
-
const _parameters = (collections.parameters ?? []) as ParameterInfo[];
|
|
3606
|
-
// Control flow collections (Phase 2: LOOP nodes)
|
|
3607
|
-
// Initialize if not exist to ensure nested function calls share same arrays
|
|
3608
|
-
if (!collections.loops) {
|
|
3609
|
-
collections.loops = [];
|
|
3610
|
-
}
|
|
3611
|
-
if (!collections.loopCounterRef) {
|
|
3612
|
-
collections.loopCounterRef = { value: 0 };
|
|
3613
|
-
}
|
|
3614
|
-
const loops = collections.loops as LoopInfo[];
|
|
3615
|
-
const loopCounterRef = collections.loopCounterRef as CounterRef;
|
|
3616
|
-
const updateExpressions = (collections.updateExpressions ?? []) as UpdateExpressionInfo[];
|
|
3617
|
-
const processedNodes = collections.processedNodes ?? {
|
|
3618
|
-
functions: new Set<string>(),
|
|
3619
|
-
classes: new Set<string>(),
|
|
3620
|
-
imports: new Set<string>(),
|
|
3621
|
-
exports: new Set<string>(),
|
|
3622
|
-
variables: new Set<string>(),
|
|
3623
|
-
callSites: new Set<string>(),
|
|
3624
|
-
methodCalls: new Set<string>(),
|
|
3625
|
-
varDecls: new Set<string>(),
|
|
3626
|
-
eventListeners: new Set<string>()
|
|
3627
|
-
};
|
|
3628
|
-
|
|
3629
|
-
const parentScopeVariables = new Set<{ name: string; id: string; scopeId: string }>();
|
|
3630
|
-
|
|
3631
|
-
const processedCallSites = processedNodes.callSites;
|
|
3632
|
-
const _processedVarDecls = processedNodes.varDecls;
|
|
3633
|
-
const processedMethodCalls = processedNodes.methodCalls;
|
|
3634
|
-
const _processedEventListeners = processedNodes.eventListeners;
|
|
3635
|
-
|
|
3636
|
-
// Track if/else scope transitions (Phase 3: extended with branchId)
|
|
3637
|
-
const ifElseScopeMap = new Map<t.IfStatement, IfElseScopeInfo>();
|
|
3638
|
-
|
|
3639
|
-
// Ensure branches and branchCounterRef are initialized (used by IfStatement and SwitchStatement)
|
|
3640
|
-
if (!collections.branches) {
|
|
3641
|
-
collections.branches = [];
|
|
3642
|
-
}
|
|
3643
|
-
if (!collections.branchCounterRef) {
|
|
3644
|
-
collections.branchCounterRef = { value: 0 };
|
|
3645
|
-
}
|
|
3646
|
-
const branches = collections.branches as BranchInfo[];
|
|
3647
|
-
const branchCounterRef = collections.branchCounterRef as CounterRef;
|
|
3648
|
-
|
|
3649
|
-
// Phase 4: Initialize try/catch/finally collections and counters
|
|
3650
|
-
if (!collections.tryBlocks) {
|
|
3651
|
-
collections.tryBlocks = [];
|
|
3652
|
-
}
|
|
3653
|
-
if (!collections.catchBlocks) {
|
|
3654
|
-
collections.catchBlocks = [];
|
|
3655
|
-
}
|
|
3656
|
-
if (!collections.finallyBlocks) {
|
|
3657
|
-
collections.finallyBlocks = [];
|
|
3658
|
-
}
|
|
3659
|
-
if (!collections.tryBlockCounterRef) {
|
|
3660
|
-
collections.tryBlockCounterRef = { value: 0 };
|
|
3661
|
-
}
|
|
3662
|
-
if (!collections.catchBlockCounterRef) {
|
|
3663
|
-
collections.catchBlockCounterRef = { value: 0 };
|
|
3664
|
-
}
|
|
3665
|
-
if (!collections.finallyBlockCounterRef) {
|
|
3666
|
-
collections.finallyBlockCounterRef = { value: 0 };
|
|
3667
|
-
}
|
|
3668
|
-
const tryBlocks = collections.tryBlocks as TryBlockInfo[];
|
|
3669
|
-
const catchBlocks = collections.catchBlocks as CatchBlockInfo[];
|
|
3670
|
-
const finallyBlocks = collections.finallyBlocks as FinallyBlockInfo[];
|
|
3671
|
-
const tryBlockCounterRef = collections.tryBlockCounterRef as CounterRef;
|
|
3672
|
-
const catchBlockCounterRef = collections.catchBlockCounterRef as CounterRef;
|
|
3673
|
-
const finallyBlockCounterRef = collections.finallyBlockCounterRef as CounterRef;
|
|
3674
|
-
|
|
3675
|
-
// Track try/catch/finally scope transitions
|
|
3676
|
-
const tryScopeMap = new Map<t.TryStatement, TryScopeInfo>();
|
|
3677
|
-
|
|
3678
|
-
// REG-334: Use shared Promise executor contexts from collections.
|
|
3679
|
-
// These are populated by module-level NewExpression handler and function-level NewExpression handler.
|
|
3680
|
-
if (!collections.promiseExecutorContexts) {
|
|
3681
|
-
collections.promiseExecutorContexts = new Map<string, PromiseExecutorContext>();
|
|
3682
|
-
}
|
|
3683
|
-
const promiseExecutorContexts = collections.promiseExecutorContexts as Map<string, PromiseExecutorContext>;
|
|
3684
|
-
|
|
3685
|
-
// Initialize promiseResolutions array if not exists
|
|
3686
|
-
if (!collections.promiseResolutions) {
|
|
3687
|
-
collections.promiseResolutions = [];
|
|
3688
|
-
}
|
|
3689
|
-
const promiseResolutions = collections.promiseResolutions as PromiseResolutionInfo[];
|
|
3690
|
-
|
|
3691
|
-
// REG-311: Initialize rejectionPatterns and catchesFromInfos collections
|
|
3692
|
-
if (!collections.rejectionPatterns) {
|
|
3693
|
-
collections.rejectionPatterns = [];
|
|
3694
|
-
}
|
|
3695
|
-
if (!collections.catchesFromInfos) {
|
|
3696
|
-
collections.catchesFromInfos = [];
|
|
3697
|
-
}
|
|
3698
|
-
const rejectionPatterns = collections.rejectionPatterns as RejectionPatternInfo[];
|
|
3699
|
-
const catchesFromInfos = collections.catchesFromInfos as CatchesFromInfo[];
|
|
3700
|
-
|
|
3701
|
-
// Dynamic scope ID stack for CONTAINS edges
|
|
3702
|
-
// Starts with the function body scope, gets updated as we enter/exit conditional scopes
|
|
3703
|
-
const scopeIdStack: string[] = [parentScopeId];
|
|
3704
|
-
const getCurrentScopeId = (): string => scopeIdStack[scopeIdStack.length - 1];
|
|
3705
|
-
|
|
3706
|
-
// Determine the ID of the function we're analyzing for RETURNS edges
|
|
3707
|
-
// Find by matching file/line/column in functions collection (it was just added by the visitor)
|
|
3708
|
-
// REG-271: Skip for StaticBlock (static blocks don't have RETURNS edges or control flow metadata)
|
|
3709
|
-
const funcNode = funcPath.node;
|
|
3710
|
-
const functionNode = t.isFunction(funcNode) ? funcNode : null;
|
|
3711
|
-
const functionPath = functionNode ? (funcPath as NodePath<t.Function>) : null;
|
|
3712
|
-
const funcLine = getLine(funcNode);
|
|
3713
|
-
const funcColumn = getColumn(funcNode);
|
|
3714
|
-
let currentFunctionId: string | null = null;
|
|
3715
|
-
|
|
3716
|
-
// StaticBlock is not a function - skip function matching for RETURNS edges
|
|
3717
|
-
// For StaticBlock, matchingFunction will be undefined
|
|
3718
|
-
const matchingFunction = funcNode.type !== 'StaticBlock'
|
|
3719
|
-
? functions.find(f =>
|
|
3720
|
-
f.file === module.file &&
|
|
3721
|
-
f.line === funcLine &&
|
|
3722
|
-
(f.column === undefined || f.column === funcColumn)
|
|
3723
|
-
)
|
|
3724
|
-
: undefined;
|
|
3725
|
-
|
|
3726
|
-
if (matchingFunction) {
|
|
3727
|
-
currentFunctionId = matchingFunction.id;
|
|
3728
|
-
}
|
|
3729
|
-
|
|
3730
|
-
// Phase 6 (REG-267): Control flow tracking state for cyclomatic complexity
|
|
3731
|
-
const controlFlowState = {
|
|
3732
|
-
branchCount: 0, // if/switch statements
|
|
3733
|
-
loopCount: 0, // for/while/do-while/for-in/for-of
|
|
3734
|
-
caseCount: 0, // switch cases (excluding default)
|
|
3735
|
-
logicalOpCount: 0, // && and || in conditions
|
|
3736
|
-
hasTryCatch: false,
|
|
3737
|
-
hasEarlyReturn: false,
|
|
3738
|
-
hasThrow: false,
|
|
3739
|
-
returnCount: 0, // Track total return count for early return detection
|
|
3740
|
-
totalStatements: 0, // Track if there are statements after returns
|
|
3741
|
-
// REG-311: Try block depth counter for O(1) isInsideTry detection
|
|
3742
|
-
tryBlockDepth: 0
|
|
3743
|
-
};
|
|
3744
|
-
|
|
3745
|
-
// Handle implicit return for THIS arrow function if it has an expression body
|
|
3746
|
-
// e.g., `const double = x => x * 2;` - the function we're analyzing IS an arrow with expression body
|
|
3747
|
-
if (t.isArrowFunctionExpression(funcNode) && !t.isBlockStatement(funcNode.body) && currentFunctionId) {
|
|
3748
|
-
const bodyExpr = funcNode.body;
|
|
3749
|
-
const bodyLine = getLine(bodyExpr);
|
|
3750
|
-
const bodyColumn = getColumn(bodyExpr);
|
|
3751
|
-
|
|
3752
|
-
// Extract expression-specific info using shared method
|
|
3753
|
-
const exprInfo = this.extractReturnExpressionInfo(
|
|
3754
|
-
bodyExpr, module, literals, literalCounterRef, funcLine, funcColumn, 'implicit_return'
|
|
3755
|
-
);
|
|
3756
|
-
|
|
3757
|
-
const returnInfo: ReturnStatementInfo = {
|
|
3758
|
-
parentFunctionId: currentFunctionId,
|
|
3759
|
-
file: module.file,
|
|
3760
|
-
line: bodyLine,
|
|
3761
|
-
column: bodyColumn,
|
|
3762
|
-
returnValueType: 'NONE',
|
|
3763
|
-
isImplicitReturn: true,
|
|
3764
|
-
...exprInfo,
|
|
3765
|
-
};
|
|
3766
|
-
|
|
3767
|
-
returnStatements.push(returnInfo);
|
|
3768
|
-
}
|
|
3769
|
-
|
|
3770
|
-
funcPath.traverse({
|
|
3771
|
-
VariableDeclaration: (varPath: NodePath<t.VariableDeclaration>) => {
|
|
3772
|
-
this.handleVariableDeclaration(
|
|
3773
|
-
varPath,
|
|
3774
|
-
getCurrentScopeId(),
|
|
3775
|
-
module,
|
|
3776
|
-
variableDeclarations,
|
|
3777
|
-
classInstantiations,
|
|
3778
|
-
literals,
|
|
3779
|
-
variableAssignments,
|
|
3780
|
-
varDeclCounterRef,
|
|
3781
|
-
literalCounterRef,
|
|
3782
|
-
scopeTracker,
|
|
3783
|
-
parentScopeVariables,
|
|
3784
|
-
objectLiterals,
|
|
3785
|
-
objectProperties,
|
|
3786
|
-
objectLiteralCounterRef
|
|
3787
|
-
);
|
|
3788
|
-
},
|
|
3789
|
-
|
|
3790
|
-
// Detect indexed array assignments: arr[i] = value
|
|
3791
|
-
AssignmentExpression: (assignPath: NodePath<t.AssignmentExpression>) => {
|
|
3792
|
-
const assignNode = assignPath.node;
|
|
3793
|
-
|
|
3794
|
-
// === VARIABLE REASSIGNMENT (REG-290) ===
|
|
3795
|
-
// Check if LHS is simple identifier (not obj.prop, not arr[i])
|
|
3796
|
-
// Must be checked FIRST before array/object mutation handlers
|
|
3797
|
-
if (assignNode.left.type === 'Identifier') {
|
|
3798
|
-
// Initialize collection if not exists
|
|
3799
|
-
if (!collections.variableReassignments) {
|
|
3800
|
-
collections.variableReassignments = [];
|
|
3801
|
-
}
|
|
3802
|
-
const variableReassignments = collections.variableReassignments as VariableReassignmentInfo[];
|
|
3803
|
-
|
|
3804
|
-
this.detectVariableReassignment(assignNode, module, variableReassignments, scopeTracker);
|
|
3805
|
-
}
|
|
3806
|
-
// === END VARIABLE REASSIGNMENT ===
|
|
3807
|
-
|
|
3808
|
-
// Initialize collection if not exists
|
|
3809
|
-
if (!collections.arrayMutations) {
|
|
3810
|
-
collections.arrayMutations = [];
|
|
3811
|
-
}
|
|
3812
|
-
const arrayMutations = collections.arrayMutations as ArrayMutationInfo[];
|
|
3813
|
-
|
|
3814
|
-
// Check for indexed array assignment: arr[i] = value
|
|
3815
|
-
this.detectIndexedArrayAssignment(assignNode, module, arrayMutations, scopeTracker, collections);
|
|
3816
|
-
|
|
3817
|
-
// Initialize object mutations collection if not exists
|
|
3818
|
-
if (!collections.objectMutations) {
|
|
3819
|
-
collections.objectMutations = [];
|
|
3820
|
-
}
|
|
3821
|
-
const objectMutations = collections.objectMutations as ObjectMutationInfo[];
|
|
3822
|
-
|
|
3823
|
-
// Check for object property assignment: obj.prop = value
|
|
3824
|
-
this.detectObjectPropertyAssignment(assignNode, module, objectMutations, scopeTracker);
|
|
3825
|
-
},
|
|
3826
|
-
|
|
3827
|
-
// Handle return statements for RETURNS edges
|
|
3828
|
-
ReturnStatement: (returnPath: NodePath<t.ReturnStatement>) => {
|
|
3829
|
-
// Skip if we couldn't determine the function ID
|
|
3830
|
-
if (!currentFunctionId) {
|
|
3831
|
-
return;
|
|
3832
|
-
}
|
|
3833
|
-
|
|
3834
|
-
// Skip if this return is inside a nested function (not the function we're analyzing)
|
|
3835
|
-
// Check if there's a function ancestor BETWEEN us and funcNode
|
|
3836
|
-
// Stop checking once we reach funcNode - parents above funcNode are outside scope
|
|
3837
|
-
let parent: NodePath | null = returnPath.parentPath;
|
|
3838
|
-
let isInsideConditional = false;
|
|
3839
|
-
while (parent) {
|
|
3840
|
-
// If we've reached funcNode, we're done checking - this return belongs to funcNode
|
|
3841
|
-
if (parent.node === funcNode) {
|
|
3842
|
-
break;
|
|
3843
|
-
}
|
|
3844
|
-
if (t.isFunction(parent.node)) {
|
|
3845
|
-
// Found a function between returnPath and funcNode - this return is inside a nested function
|
|
3846
|
-
return;
|
|
3847
|
-
}
|
|
3848
|
-
// Track if return is inside a conditional block (if/else, switch case, loop, try/catch)
|
|
3849
|
-
if (t.isIfStatement(parent.node) ||
|
|
3850
|
-
t.isSwitchCase(parent.node) ||
|
|
3851
|
-
t.isLoop(parent.node) ||
|
|
3852
|
-
t.isTryStatement(parent.node) ||
|
|
3853
|
-
t.isCatchClause(parent.node)) {
|
|
3854
|
-
isInsideConditional = true;
|
|
3855
|
-
}
|
|
3856
|
-
parent = parent.parentPath;
|
|
3857
|
-
}
|
|
3858
|
-
|
|
3859
|
-
// Phase 6 (REG-267): Track return count and early return detection
|
|
3860
|
-
controlFlowState.returnCount++;
|
|
3861
|
-
|
|
3862
|
-
// A return is "early" if it's inside a conditional structure
|
|
3863
|
-
// (More returns after this one indicate the function doesn't always end here)
|
|
3864
|
-
if (isInsideConditional) {
|
|
3865
|
-
controlFlowState.hasEarlyReturn = true;
|
|
3866
|
-
}
|
|
3867
|
-
|
|
3868
|
-
const returnNode = returnPath.node;
|
|
3869
|
-
const returnLine = getLine(returnNode);
|
|
3870
|
-
const returnColumn = getColumn(returnNode);
|
|
3871
|
-
|
|
3872
|
-
// Handle bare return; (no value)
|
|
3873
|
-
if (!returnNode.argument) {
|
|
3874
|
-
// Skip - no data flow value
|
|
3875
|
-
return;
|
|
3876
|
-
}
|
|
3877
|
-
|
|
3878
|
-
const arg = returnNode.argument;
|
|
3879
|
-
|
|
3880
|
-
// Extract expression-specific info using shared method
|
|
3881
|
-
const exprInfo = this.extractReturnExpressionInfo(
|
|
3882
|
-
arg, module, literals, literalCounterRef, returnLine, returnColumn, 'return'
|
|
3883
|
-
);
|
|
3884
|
-
|
|
3885
|
-
const returnInfo: ReturnStatementInfo = {
|
|
3886
|
-
parentFunctionId: currentFunctionId,
|
|
3887
|
-
file: module.file,
|
|
3888
|
-
line: returnLine,
|
|
3889
|
-
column: returnColumn,
|
|
3890
|
-
returnValueType: 'NONE',
|
|
3891
|
-
...exprInfo,
|
|
3892
|
-
};
|
|
3893
|
-
|
|
3894
|
-
returnStatements.push(returnInfo);
|
|
3895
|
-
},
|
|
3896
|
-
|
|
3897
|
-
// Phase 6 (REG-267): Track throw statements for control flow metadata
|
|
3898
|
-
// REG-311: Also detect async_throw rejection patterns
|
|
3899
|
-
ThrowStatement: (throwPath: NodePath<t.ThrowStatement>) => {
|
|
3900
|
-
// Skip if this throw is inside a nested function (not the function we're analyzing)
|
|
3901
|
-
let parent: NodePath | null = throwPath.parentPath;
|
|
3902
|
-
while (parent) {
|
|
3903
|
-
if (t.isFunction(parent.node) && parent.node !== funcNode) {
|
|
3904
|
-
// This throw is inside a nested function - skip it
|
|
3905
|
-
return;
|
|
3906
|
-
}
|
|
3907
|
-
parent = parent.parentPath;
|
|
3908
|
-
}
|
|
3909
|
-
|
|
3910
|
-
controlFlowState.hasThrow = true;
|
|
3911
|
-
|
|
3912
|
-
// REG-311: Track rejection patterns for async functions
|
|
3913
|
-
const isAsyncFunction = functionNode?.async === true;
|
|
3914
|
-
if (isAsyncFunction && currentFunctionId && functionNode && functionPath) {
|
|
3915
|
-
const throwNode = throwPath.node;
|
|
3916
|
-
const arg = throwNode.argument;
|
|
3917
|
-
const throwLine = getLine(throwNode);
|
|
3918
|
-
const throwColumn = getColumn(throwNode);
|
|
3919
|
-
|
|
3920
|
-
// Case 1: throw new Error() or throw new CustomError()
|
|
3921
|
-
if (arg && t.isNewExpression(arg) && t.isIdentifier(arg.callee)) {
|
|
3922
|
-
rejectionPatterns.push({
|
|
3923
|
-
functionId: currentFunctionId,
|
|
3924
|
-
errorClassName: arg.callee.name,
|
|
3925
|
-
rejectionType: 'async_throw',
|
|
3926
|
-
file: module.file,
|
|
3927
|
-
line: throwLine,
|
|
3928
|
-
column: throwColumn
|
|
3929
|
-
});
|
|
3930
|
-
}
|
|
3931
|
-
// Case 2: throw identifier - needs micro-trace
|
|
3932
|
-
else if (arg && t.isIdentifier(arg)) {
|
|
3933
|
-
const varName = arg.name;
|
|
3934
|
-
|
|
3935
|
-
// Check if it's a parameter
|
|
3936
|
-
const isParameter = functionNode.params.some(param =>
|
|
3937
|
-
t.isIdentifier(param) && param.name === varName
|
|
3938
|
-
);
|
|
3939
|
-
|
|
3940
|
-
if (isParameter) {
|
|
3941
|
-
// Parameter forwarding - can't resolve statically
|
|
3942
|
-
rejectionPatterns.push({
|
|
3943
|
-
functionId: currentFunctionId,
|
|
3944
|
-
errorClassName: null,
|
|
3945
|
-
rejectionType: 'variable_parameter',
|
|
3946
|
-
file: module.file,
|
|
3947
|
-
line: throwLine,
|
|
3948
|
-
column: throwColumn,
|
|
3949
|
-
sourceVariableName: varName
|
|
3950
|
-
});
|
|
3951
|
-
} else {
|
|
3952
|
-
// Try micro-trace
|
|
3953
|
-
const { errorClassName, tracePath } = this.microTraceToErrorClass(
|
|
3954
|
-
varName,
|
|
3955
|
-
functionPath,
|
|
3956
|
-
variableDeclarations
|
|
3957
|
-
);
|
|
3958
|
-
|
|
3959
|
-
rejectionPatterns.push({
|
|
3960
|
-
functionId: currentFunctionId,
|
|
3961
|
-
errorClassName,
|
|
3962
|
-
rejectionType: errorClassName ? 'variable_traced' : 'variable_unknown',
|
|
3963
|
-
file: module.file,
|
|
3964
|
-
line: throwLine,
|
|
3965
|
-
column: throwColumn,
|
|
3966
|
-
sourceVariableName: varName,
|
|
3967
|
-
tracePath
|
|
3968
|
-
});
|
|
3969
|
-
}
|
|
3970
|
-
}
|
|
3971
|
-
}
|
|
3972
|
-
},
|
|
3973
|
-
|
|
3974
|
-
// Handle yield expressions for YIELDS/DELEGATES_TO edges (REG-270)
|
|
3975
|
-
YieldExpression: (yieldPath: NodePath<t.YieldExpression>) => {
|
|
3976
|
-
// Skip if we couldn't determine the function ID
|
|
3977
|
-
if (!currentFunctionId) {
|
|
3978
|
-
return;
|
|
3979
|
-
}
|
|
3980
|
-
|
|
3981
|
-
// Skip if this yield is inside a nested function (not the function we're analyzing)
|
|
3982
|
-
// Check if there's a function ancestor BETWEEN us and funcNode
|
|
3983
|
-
let parent: NodePath | null = yieldPath.parentPath;
|
|
3984
|
-
while (parent) {
|
|
3985
|
-
// If we've reached funcNode, we're done checking - this yield belongs to funcNode
|
|
3986
|
-
if (parent.node === funcNode) {
|
|
3987
|
-
break;
|
|
3988
|
-
}
|
|
3989
|
-
if (t.isFunction(parent.node)) {
|
|
3990
|
-
// Found a function between yieldPath and funcNode - this yield is inside a nested function
|
|
3991
|
-
return;
|
|
3992
|
-
}
|
|
3993
|
-
parent = parent.parentPath;
|
|
3994
|
-
}
|
|
3995
|
-
|
|
3996
|
-
const yieldNode = yieldPath.node;
|
|
3997
|
-
const yieldLine = getLine(yieldNode);
|
|
3998
|
-
const yieldColumn = getColumn(yieldNode);
|
|
3999
|
-
const isDelegate = yieldNode.delegate ?? false;
|
|
4000
|
-
|
|
4001
|
-
// Handle bare yield; (no value) - only valid for non-delegate yield
|
|
4002
|
-
if (!yieldNode.argument && !isDelegate) {
|
|
4003
|
-
// Skip - no data flow value
|
|
4004
|
-
return;
|
|
4005
|
-
}
|
|
4006
|
-
|
|
4007
|
-
// For yield* without argument (syntax error in practice, but handle gracefully)
|
|
4008
|
-
if (!yieldNode.argument) {
|
|
4009
|
-
return;
|
|
4010
|
-
}
|
|
4011
|
-
|
|
4012
|
-
const arg = yieldNode.argument;
|
|
4013
|
-
|
|
4014
|
-
// Extract expression-specific info using shared method
|
|
4015
|
-
// Note: We reuse extractReturnExpressionInfo since yield values have identical semantics
|
|
4016
|
-
const exprInfo = this.extractReturnExpressionInfo(
|
|
4017
|
-
arg, module, literals, literalCounterRef, yieldLine, yieldColumn, 'yield'
|
|
4018
|
-
);
|
|
4019
|
-
|
|
4020
|
-
// Map ReturnStatementInfo fields to YieldExpressionInfo fields
|
|
4021
|
-
const yieldInfo: YieldExpressionInfo = {
|
|
4022
|
-
parentFunctionId: currentFunctionId,
|
|
4023
|
-
file: module.file,
|
|
4024
|
-
line: yieldLine,
|
|
4025
|
-
column: yieldColumn,
|
|
4026
|
-
isDelegate,
|
|
4027
|
-
yieldValueType: exprInfo.returnValueType ?? 'NONE',
|
|
4028
|
-
yieldValueName: exprInfo.returnValueName,
|
|
4029
|
-
yieldValueId: exprInfo.returnValueId,
|
|
4030
|
-
yieldValueLine: exprInfo.returnValueLine,
|
|
4031
|
-
yieldValueColumn: exprInfo.returnValueColumn,
|
|
4032
|
-
yieldValueCallName: exprInfo.returnValueCallName,
|
|
4033
|
-
expressionType: exprInfo.expressionType,
|
|
4034
|
-
operator: exprInfo.operator,
|
|
4035
|
-
leftSourceName: exprInfo.leftSourceName,
|
|
4036
|
-
rightSourceName: exprInfo.rightSourceName,
|
|
4037
|
-
consequentSourceName: exprInfo.consequentSourceName,
|
|
4038
|
-
alternateSourceName: exprInfo.alternateSourceName,
|
|
4039
|
-
object: exprInfo.object,
|
|
4040
|
-
property: exprInfo.property,
|
|
4041
|
-
computed: exprInfo.computed,
|
|
4042
|
-
objectSourceName: exprInfo.objectSourceName,
|
|
4043
|
-
expressionSourceNames: exprInfo.expressionSourceNames,
|
|
4044
|
-
unaryArgSourceName: exprInfo.unaryArgSourceName,
|
|
4045
|
-
};
|
|
4046
|
-
|
|
4047
|
-
yieldExpressions.push(yieldInfo);
|
|
4048
|
-
},
|
|
4049
|
-
|
|
4050
|
-
ForStatement: this.createLoopScopeHandler('for', 'for-loop', 'for', parentScopeId, module, scopes, loops, scopeCounterRef, loopCounterRef, scopeTracker, scopeIdStack, controlFlowState),
|
|
4051
|
-
ForInStatement: this.createLoopScopeHandler('for-in', 'for-in-loop', 'for-in', parentScopeId, module, scopes, loops, scopeCounterRef, loopCounterRef, scopeTracker, scopeIdStack, controlFlowState),
|
|
4052
|
-
ForOfStatement: this.createLoopScopeHandler('for-of', 'for-of-loop', 'for-of', parentScopeId, module, scopes, loops, scopeCounterRef, loopCounterRef, scopeTracker, scopeIdStack, controlFlowState),
|
|
4053
|
-
WhileStatement: this.createLoopScopeHandler('while', 'while-loop', 'while', parentScopeId, module, scopes, loops, scopeCounterRef, loopCounterRef, scopeTracker, scopeIdStack, controlFlowState),
|
|
4054
|
-
DoWhileStatement: this.createLoopScopeHandler('do-while', 'do-while-loop', 'do-while', parentScopeId, module, scopes, loops, scopeCounterRef, loopCounterRef, scopeTracker, scopeIdStack, controlFlowState),
|
|
4055
|
-
|
|
4056
|
-
// Phase 4 (REG-267): Now creates TRY_BLOCK, CATCH_BLOCK, FINALLY_BLOCK nodes
|
|
4057
|
-
TryStatement: this.createTryStatementHandler(
|
|
4058
|
-
parentScopeId,
|
|
4059
|
-
module,
|
|
4060
|
-
scopes,
|
|
4061
|
-
tryBlocks,
|
|
4062
|
-
catchBlocks,
|
|
4063
|
-
finallyBlocks,
|
|
4064
|
-
scopeCounterRef,
|
|
4065
|
-
tryBlockCounterRef,
|
|
4066
|
-
catchBlockCounterRef,
|
|
4067
|
-
finallyBlockCounterRef,
|
|
4068
|
-
scopeTracker,
|
|
4069
|
-
tryScopeMap,
|
|
4070
|
-
scopeIdStack,
|
|
4071
|
-
controlFlowState
|
|
4072
|
-
),
|
|
4073
|
-
|
|
4074
|
-
CatchClause: this.createCatchClauseHandler(
|
|
4075
|
-
module,
|
|
4076
|
-
variableDeclarations,
|
|
4077
|
-
varDeclCounterRef,
|
|
4078
|
-
scopeTracker,
|
|
4079
|
-
tryScopeMap,
|
|
4080
|
-
scopeIdStack,
|
|
4081
|
-
controlFlowState
|
|
4082
|
-
),
|
|
4083
|
-
|
|
4084
|
-
SwitchStatement: (switchPath: NodePath<t.SwitchStatement>) => {
|
|
4085
|
-
this.handleSwitchStatement(
|
|
4086
|
-
switchPath,
|
|
4087
|
-
parentScopeId,
|
|
4088
|
-
module,
|
|
4089
|
-
collections,
|
|
4090
|
-
scopeTracker,
|
|
4091
|
-
controlFlowState
|
|
4092
|
-
);
|
|
4093
|
-
},
|
|
4094
|
-
|
|
4095
|
-
FunctionDeclaration: (funcDeclPath: NodePath<t.FunctionDeclaration>) => {
|
|
4096
|
-
const node = funcDeclPath.node;
|
|
4097
|
-
const funcName = node.id ? node.id.name : this.generateAnonymousName(scopeTracker);
|
|
4098
|
-
// Use semantic ID as primary ID when scopeTracker available
|
|
4099
|
-
const legacyId = `FUNCTION#${funcName}#${module.file}#${getLine(node)}:${getColumn(node)}:${functionCounterRef.value++}`;
|
|
4100
|
-
const functionId = scopeTracker
|
|
4101
|
-
? computeSemanticId('FUNCTION', funcName, scopeTracker.getContext())
|
|
4102
|
-
: legacyId;
|
|
4103
|
-
|
|
4104
|
-
functions.push({
|
|
4105
|
-
id: functionId,
|
|
4106
|
-
type: 'FUNCTION',
|
|
4107
|
-
name: funcName,
|
|
4108
|
-
file: module.file,
|
|
4109
|
-
line: getLine(node),
|
|
4110
|
-
column: getColumn(node),
|
|
4111
|
-
async: node.async || false,
|
|
4112
|
-
generator: node.generator || false,
|
|
4113
|
-
parentScopeId
|
|
4114
|
-
});
|
|
4115
|
-
|
|
4116
|
-
const nestedScopeId = `SCOPE#${funcName}:body#${module.file}#${getLine(node)}`;
|
|
4117
|
-
const closureSemanticId = this.generateSemanticId('closure', scopeTracker);
|
|
4118
|
-
scopes.push({
|
|
4119
|
-
id: nestedScopeId,
|
|
4120
|
-
type: 'SCOPE',
|
|
4121
|
-
scopeType: 'closure',
|
|
4122
|
-
name: `${funcName}:body`,
|
|
4123
|
-
semanticId: closureSemanticId,
|
|
4124
|
-
conditional: false,
|
|
4125
|
-
file: module.file,
|
|
4126
|
-
line: getLine(node),
|
|
4127
|
-
parentFunctionId: functionId,
|
|
4128
|
-
capturesFrom: parentScopeId
|
|
4129
|
-
});
|
|
4130
|
-
|
|
4131
|
-
// Enter nested function scope for semantic ID generation
|
|
4132
|
-
if (scopeTracker) {
|
|
4133
|
-
scopeTracker.enterScope(funcName, 'function');
|
|
4134
|
-
}
|
|
4135
|
-
this.analyzeFunctionBody(funcDeclPath, nestedScopeId, module, collections);
|
|
4136
|
-
if (scopeTracker) {
|
|
4137
|
-
scopeTracker.exitScope();
|
|
4138
|
-
}
|
|
4139
|
-
funcDeclPath.skip();
|
|
4140
|
-
},
|
|
4141
|
-
|
|
4142
|
-
FunctionExpression: (funcPath: NodePath<t.FunctionExpression>) => {
|
|
4143
|
-
const node = funcPath.node;
|
|
4144
|
-
const funcName = node.id ? node.id.name : this.generateAnonymousName(scopeTracker);
|
|
4145
|
-
// Use semantic ID as primary ID when scopeTracker available
|
|
4146
|
-
const legacyId = `FUNCTION#${funcName}#${module.file}#${getLine(node)}:${getColumn(node)}:${functionCounterRef.value++}`;
|
|
4147
|
-
const functionId = scopeTracker
|
|
4148
|
-
? computeSemanticId('FUNCTION', funcName, scopeTracker.getContext())
|
|
4149
|
-
: legacyId;
|
|
4150
|
-
|
|
4151
|
-
functions.push({
|
|
4152
|
-
id: functionId,
|
|
4153
|
-
type: 'FUNCTION',
|
|
4154
|
-
name: funcName,
|
|
4155
|
-
file: module.file,
|
|
4156
|
-
line: getLine(node),
|
|
4157
|
-
column: getColumn(node),
|
|
4158
|
-
async: node.async || false,
|
|
4159
|
-
generator: node.generator || false,
|
|
4160
|
-
parentScopeId
|
|
4161
|
-
});
|
|
4162
|
-
|
|
4163
|
-
const nestedScopeId = `SCOPE#${funcName}:body#${module.file}#${getLine(node)}`;
|
|
4164
|
-
const closureSemanticId = this.generateSemanticId('closure', scopeTracker);
|
|
4165
|
-
scopes.push({
|
|
4166
|
-
id: nestedScopeId,
|
|
4167
|
-
type: 'SCOPE',
|
|
4168
|
-
scopeType: 'closure',
|
|
4169
|
-
name: `${funcName}:body`,
|
|
4170
|
-
semanticId: closureSemanticId,
|
|
4171
|
-
conditional: false,
|
|
4172
|
-
file: module.file,
|
|
4173
|
-
line: getLine(node),
|
|
4174
|
-
parentFunctionId: functionId,
|
|
4175
|
-
capturesFrom: parentScopeId
|
|
4176
|
-
});
|
|
4177
|
-
|
|
4178
|
-
// Enter nested function scope for semantic ID generation
|
|
4179
|
-
if (scopeTracker) {
|
|
4180
|
-
scopeTracker.enterScope(funcName, 'function');
|
|
4181
|
-
}
|
|
4182
|
-
this.analyzeFunctionBody(funcPath, nestedScopeId, module, collections);
|
|
4183
|
-
if (scopeTracker) {
|
|
4184
|
-
scopeTracker.exitScope();
|
|
4185
|
-
}
|
|
4186
|
-
funcPath.skip();
|
|
4187
|
-
},
|
|
4188
|
-
|
|
4189
|
-
ArrowFunctionExpression: (arrowPath: NodePath<t.ArrowFunctionExpression>) => {
|
|
4190
|
-
const node = arrowPath.node;
|
|
4191
|
-
const line = getLine(node);
|
|
4192
|
-
const column = getColumn(node);
|
|
4193
|
-
|
|
4194
|
-
// Определяем имя (anonymous если не присвоено переменной)
|
|
4195
|
-
const parent = arrowPath.parent;
|
|
4196
|
-
let funcName: string;
|
|
4197
|
-
if (t.isVariableDeclarator(parent) && t.isIdentifier(parent.id)) {
|
|
4198
|
-
funcName = parent.id.name;
|
|
4199
|
-
} else {
|
|
4200
|
-
// Используем scope-level счётчик для стабильного semanticId
|
|
4201
|
-
funcName = this.generateAnonymousName(scopeTracker);
|
|
4202
|
-
}
|
|
4203
|
-
|
|
4204
|
-
// Use semantic ID as primary ID when scopeTracker available
|
|
4205
|
-
const legacyId = `FUNCTION#${funcName}:${line}:${column}:${functionCounterRef.value++}`;
|
|
4206
|
-
const functionId = scopeTracker
|
|
4207
|
-
? computeSemanticId('FUNCTION', funcName, scopeTracker.getContext())
|
|
4208
|
-
: legacyId;
|
|
4209
|
-
|
|
4210
|
-
functions.push({
|
|
4211
|
-
id: functionId,
|
|
4212
|
-
type: 'FUNCTION',
|
|
4213
|
-
name: funcName,
|
|
4214
|
-
file: module.file,
|
|
4215
|
-
line,
|
|
4216
|
-
column,
|
|
4217
|
-
async: node.async || false,
|
|
4218
|
-
arrowFunction: true,
|
|
4219
|
-
parentScopeId
|
|
4220
|
-
});
|
|
4221
|
-
|
|
4222
|
-
if (node.body.type === 'BlockStatement') {
|
|
4223
|
-
const nestedScopeId = `SCOPE#${funcName}:body#${module.file}#${line}`;
|
|
4224
|
-
const arrowSemanticId = this.generateSemanticId('arrow_body', scopeTracker);
|
|
4225
|
-
scopes.push({
|
|
4226
|
-
id: nestedScopeId,
|
|
4227
|
-
type: 'SCOPE',
|
|
4228
|
-
scopeType: 'arrow_body',
|
|
4229
|
-
name: `${funcName}:body`,
|
|
4230
|
-
semanticId: arrowSemanticId,
|
|
4231
|
-
conditional: false,
|
|
4232
|
-
file: module.file,
|
|
4233
|
-
line,
|
|
4234
|
-
parentFunctionId: functionId,
|
|
4235
|
-
capturesFrom: parentScopeId
|
|
4236
|
-
});
|
|
4237
|
-
|
|
4238
|
-
// Enter arrow function scope for semantic ID generation
|
|
4239
|
-
if (scopeTracker) {
|
|
4240
|
-
scopeTracker.enterScope(funcName, 'arrow');
|
|
4241
|
-
}
|
|
4242
|
-
this.analyzeFunctionBody(arrowPath, nestedScopeId, module, collections);
|
|
4243
|
-
if (scopeTracker) {
|
|
4244
|
-
scopeTracker.exitScope();
|
|
4245
|
-
}
|
|
4246
|
-
} else {
|
|
4247
|
-
// Arrow function with expression body (implicit return)
|
|
4248
|
-
// e.g., x => x * 2, () => 42
|
|
4249
|
-
const bodyExpr = node.body;
|
|
4250
|
-
const bodyLine = getLine(bodyExpr);
|
|
4251
|
-
const bodyColumn = getColumn(bodyExpr);
|
|
4252
|
-
|
|
4253
|
-
// Extract expression-specific info using shared method
|
|
4254
|
-
const exprInfo = this.extractReturnExpressionInfo(
|
|
4255
|
-
bodyExpr, module, literals, literalCounterRef, line, column, 'implicit_return'
|
|
4256
|
-
);
|
|
4257
|
-
|
|
4258
|
-
const returnInfo: ReturnStatementInfo = {
|
|
4259
|
-
parentFunctionId: functionId,
|
|
4260
|
-
file: module.file,
|
|
4261
|
-
line: bodyLine,
|
|
4262
|
-
column: bodyColumn,
|
|
4263
|
-
returnValueType: 'NONE',
|
|
4264
|
-
isImplicitReturn: true,
|
|
4265
|
-
...exprInfo,
|
|
4266
|
-
};
|
|
4267
|
-
|
|
4268
|
-
returnStatements.push(returnInfo);
|
|
4269
|
-
}
|
|
4270
|
-
|
|
4271
|
-
arrowPath.skip();
|
|
4272
|
-
},
|
|
4273
|
-
|
|
4274
|
-
UpdateExpression: (updatePath: NodePath<t.UpdateExpression>) => {
|
|
4275
|
-
const updateNode = updatePath.node;
|
|
4276
|
-
|
|
4277
|
-
// REG-288/REG-312: Collect update expression info for graph building
|
|
4278
|
-
this.collectUpdateExpression(updateNode, module, updateExpressions, getCurrentScopeId(), scopeTracker);
|
|
4279
|
-
|
|
4280
|
-
// Legacy behavior: update scope.modifies for IDENTIFIER targets
|
|
4281
|
-
if (updateNode.argument.type === 'Identifier') {
|
|
4282
|
-
const varName = updateNode.argument.name;
|
|
4283
|
-
|
|
4284
|
-
// Find variable by name - could be from parent scope or declarations
|
|
4285
|
-
const fromParentScope = Array.from(parentScopeVariables).find(v => v.name === varName);
|
|
4286
|
-
const fromDeclarations = variableDeclarations.find(v => v.name === varName);
|
|
4287
|
-
const variable = fromParentScope ?? fromDeclarations;
|
|
4288
|
-
|
|
4289
|
-
if (variable) {
|
|
4290
|
-
const scope = scopes.find(s => s.id === parentScopeId);
|
|
4291
|
-
if (scope) {
|
|
4292
|
-
if (!scope.modifies) scope.modifies = [];
|
|
4293
|
-
scope.modifies.push({
|
|
4294
|
-
variableId: variable.id,
|
|
4295
|
-
variableName: varName,
|
|
4296
|
-
line: getLine(updateNode)
|
|
4297
|
-
});
|
|
4298
|
-
}
|
|
4299
|
-
}
|
|
4300
|
-
}
|
|
4301
|
-
},
|
|
4302
|
-
|
|
4303
|
-
// IF statements - создаём условные scope и обходим содержимое для CALL узлов
|
|
4304
|
-
// Phase 3 (REG-267): Now creates BRANCH nodes with branchType='if'
|
|
4305
|
-
IfStatement: this.createIfStatementHandler(
|
|
4306
|
-
parentScopeId,
|
|
4307
|
-
module,
|
|
4308
|
-
scopes,
|
|
4309
|
-
branches,
|
|
4310
|
-
ifScopeCounterRef,
|
|
4311
|
-
branchCounterRef,
|
|
4312
|
-
scopeTracker,
|
|
4313
|
-
collections.code ?? '',
|
|
4314
|
-
ifElseScopeMap,
|
|
4315
|
-
scopeIdStack,
|
|
4316
|
-
controlFlowState,
|
|
4317
|
-
this.countLogicalOperators.bind(this)
|
|
4318
|
-
),
|
|
4319
|
-
|
|
4320
|
-
// Ternary expressions (REG-287): Creates BRANCH nodes with branchType='ternary'
|
|
4321
|
-
ConditionalExpression: this.createConditionalExpressionHandler(
|
|
4322
|
-
parentScopeId,
|
|
4323
|
-
module,
|
|
4324
|
-
branches,
|
|
4325
|
-
branchCounterRef,
|
|
4326
|
-
scopeTracker,
|
|
4327
|
-
scopeIdStack,
|
|
4328
|
-
controlFlowState,
|
|
4329
|
-
this.countLogicalOperators.bind(this)
|
|
4330
|
-
),
|
|
4331
|
-
|
|
4332
|
-
// Track when we enter the alternate (else) block of an IfStatement
|
|
4333
|
-
BlockStatement: this.createBlockStatementHandler(scopeTracker, ifElseScopeMap, tryScopeMap, scopeIdStack),
|
|
4334
|
-
|
|
4335
|
-
// Function call expressions
|
|
4336
|
-
CallExpression: (callPath: NodePath<t.CallExpression>) => {
|
|
4337
|
-
// REG-311: Detect isAwaited (parent is AwaitExpression)
|
|
4338
|
-
const parent = callPath.parentPath;
|
|
4339
|
-
const isAwaited = parent?.isAwaitExpression() ?? false;
|
|
4340
|
-
|
|
4341
|
-
// REG-311: Detect isInsideTry (O(1) via depth counter)
|
|
4342
|
-
const isInsideTry = controlFlowState.tryBlockDepth > 0;
|
|
4343
|
-
|
|
4344
|
-
this.handleCallExpression(
|
|
4345
|
-
callPath.node,
|
|
4346
|
-
processedCallSites,
|
|
4347
|
-
processedMethodCalls,
|
|
4348
|
-
callSites,
|
|
4349
|
-
methodCalls,
|
|
4350
|
-
module,
|
|
4351
|
-
callSiteCounterRef,
|
|
4352
|
-
scopeTracker,
|
|
4353
|
-
getCurrentScopeId(),
|
|
4354
|
-
collections,
|
|
4355
|
-
isAwaited,
|
|
4356
|
-
isInsideTry
|
|
4357
|
-
);
|
|
4358
|
-
|
|
4359
|
-
// REG-334: Check for resolve/reject calls inside Promise executors
|
|
4360
|
-
const callNode = callPath.node;
|
|
4361
|
-
if (t.isIdentifier(callNode.callee)) {
|
|
4362
|
-
const calleeName = callNode.callee.name;
|
|
4363
|
-
|
|
4364
|
-
// Walk up function parents to find Promise executor context
|
|
4365
|
-
// This handles nested callbacks like: new Promise((resolve) => { db.query((err, data) => { resolve(data); }); });
|
|
4366
|
-
let funcParent = callPath.getFunctionParent();
|
|
4367
|
-
while (funcParent) {
|
|
4368
|
-
const funcNode = funcParent.node;
|
|
4369
|
-
const funcKey = `${funcNode.start}:${funcNode.end}`;
|
|
4370
|
-
const context = promiseExecutorContexts.get(funcKey);
|
|
4371
|
-
|
|
4372
|
-
if (context) {
|
|
4373
|
-
const isResolve = calleeName === context.resolveName;
|
|
4374
|
-
const isReject = calleeName === context.rejectName;
|
|
4375
|
-
|
|
4376
|
-
if (isResolve || isReject) {
|
|
4377
|
-
// Find the CALL node ID for this resolve/reject call
|
|
4378
|
-
// It was just added by handleCallExpression
|
|
4379
|
-
const callLine = getLine(callNode);
|
|
4380
|
-
const callColumn = getColumn(callNode);
|
|
4381
|
-
|
|
4382
|
-
// Find matching call site that was just added
|
|
4383
|
-
const resolveCall = callSites.find(cs =>
|
|
4384
|
-
cs.name === calleeName &&
|
|
4385
|
-
cs.file === module.file &&
|
|
4386
|
-
cs.line === callLine &&
|
|
4387
|
-
cs.column === callColumn
|
|
4388
|
-
);
|
|
4389
|
-
|
|
4390
|
-
if (resolveCall) {
|
|
4391
|
-
promiseResolutions.push({
|
|
4392
|
-
callId: resolveCall.id,
|
|
4393
|
-
constructorCallId: context.constructorCallId,
|
|
4394
|
-
isReject,
|
|
4395
|
-
file: module.file,
|
|
4396
|
-
line: callLine
|
|
4397
|
-
});
|
|
4398
|
-
|
|
4399
|
-
// REG-334: Collect arguments for resolve/reject calls
|
|
4400
|
-
// This enables traceValues to follow PASSES_ARGUMENT edges
|
|
4401
|
-
if (!collections.callArguments) {
|
|
4402
|
-
collections.callArguments = [];
|
|
4403
|
-
}
|
|
4404
|
-
const callArgumentsArr = collections.callArguments as CallArgumentInfo[];
|
|
4405
|
-
|
|
4406
|
-
// Process arguments (typically just one: resolve(value))
|
|
4407
|
-
callNode.arguments.forEach((arg, argIndex) => {
|
|
4408
|
-
const argInfo: CallArgumentInfo = {
|
|
4409
|
-
callId: resolveCall.id,
|
|
4410
|
-
argIndex,
|
|
4411
|
-
file: module.file,
|
|
4412
|
-
line: getLine(arg),
|
|
4413
|
-
column: getColumn(arg)
|
|
4414
|
-
};
|
|
4415
|
-
|
|
4416
|
-
// Handle different argument types
|
|
4417
|
-
if (t.isIdentifier(arg)) {
|
|
4418
|
-
argInfo.targetType = 'VARIABLE';
|
|
4419
|
-
argInfo.targetName = arg.name;
|
|
4420
|
-
} else if (t.isLiteral(arg) && !t.isTemplateLiteral(arg)) {
|
|
4421
|
-
// Create LITERAL node for the argument value
|
|
4422
|
-
const literalValue = ExpressionEvaluator.extractLiteralValue(arg as t.Literal);
|
|
4423
|
-
if (literalValue !== null) {
|
|
4424
|
-
const argLine = getLine(arg);
|
|
4425
|
-
const argColumn = getColumn(arg);
|
|
4426
|
-
const literalId = `LITERAL#arg${argIndex}#${module.file}#${argLine}:${argColumn}:${literalCounterRef.value++}`;
|
|
4427
|
-
literals.push({
|
|
4428
|
-
id: literalId,
|
|
4429
|
-
type: 'LITERAL',
|
|
4430
|
-
value: literalValue,
|
|
4431
|
-
valueType: typeof literalValue,
|
|
4432
|
-
file: module.file,
|
|
4433
|
-
line: argLine,
|
|
4434
|
-
column: argColumn,
|
|
4435
|
-
parentCallId: resolveCall.id,
|
|
4436
|
-
argIndex
|
|
4437
|
-
});
|
|
4438
|
-
argInfo.targetType = 'LITERAL';
|
|
4439
|
-
argInfo.targetId = literalId;
|
|
4440
|
-
argInfo.literalValue = literalValue;
|
|
4441
|
-
}
|
|
4442
|
-
} else if (t.isCallExpression(arg)) {
|
|
4443
|
-
argInfo.targetType = 'CALL';
|
|
4444
|
-
argInfo.nestedCallLine = getLine(arg);
|
|
4445
|
-
argInfo.nestedCallColumn = getColumn(arg);
|
|
4446
|
-
} else {
|
|
4447
|
-
argInfo.targetType = 'EXPRESSION';
|
|
4448
|
-
argInfo.expressionType = arg.type;
|
|
4449
|
-
}
|
|
4450
|
-
|
|
4451
|
-
callArgumentsArr.push(argInfo);
|
|
4452
|
-
});
|
|
4453
|
-
}
|
|
4454
|
-
|
|
4455
|
-
break; // Found context, stop searching
|
|
4456
|
-
}
|
|
4457
|
-
}
|
|
4458
|
-
|
|
4459
|
-
funcParent = funcParent.getFunctionParent();
|
|
4460
|
-
}
|
|
4461
|
-
|
|
4462
|
-
// REG-311: Detect executor_reject pattern - reject(new Error()) inside Promise executor
|
|
4463
|
-
// Walk up to find Promise executor context and check if this is reject call with NewExpression arg
|
|
4464
|
-
funcParent = callPath.getFunctionParent();
|
|
4465
|
-
while (funcParent && currentFunctionId) {
|
|
4466
|
-
const funcNode = funcParent.node;
|
|
4467
|
-
const funcKey = `${funcNode.start}:${funcNode.end}`;
|
|
4468
|
-
const context = promiseExecutorContexts.get(funcKey);
|
|
4469
|
-
|
|
4470
|
-
if (context && calleeName === context.rejectName && callNode.arguments.length > 0) {
|
|
4471
|
-
// REG-311: Use the creator function's ID (the function that created the Promise),
|
|
4472
|
-
// not the executor's ID
|
|
4473
|
-
const targetFunctionId = context.creatorFunctionId || currentFunctionId;
|
|
4474
|
-
const arg = callNode.arguments[0];
|
|
4475
|
-
const callLine = getLine(callNode);
|
|
4476
|
-
const callColumn = getColumn(callNode);
|
|
4477
|
-
|
|
4478
|
-
// Case 1: reject(new Error())
|
|
4479
|
-
if (t.isNewExpression(arg) && t.isIdentifier(arg.callee)) {
|
|
4480
|
-
rejectionPatterns.push({
|
|
4481
|
-
functionId: targetFunctionId,
|
|
4482
|
-
errorClassName: arg.callee.name,
|
|
4483
|
-
rejectionType: 'executor_reject',
|
|
4484
|
-
file: module.file,
|
|
4485
|
-
line: callLine,
|
|
4486
|
-
column: callColumn
|
|
4487
|
-
});
|
|
4488
|
-
}
|
|
4489
|
-
// Case 2: reject(err) where err is variable
|
|
4490
|
-
else if (t.isIdentifier(arg)) {
|
|
4491
|
-
const varName = arg.name;
|
|
4492
|
-
// Check if it's a parameter of ANY containing function (executor, outer, etc.)
|
|
4493
|
-
// Walk up the function chain to find if varName is a parameter
|
|
4494
|
-
let isParameter = false;
|
|
4495
|
-
let checkParent: NodePath<t.Node> | null = funcParent;
|
|
4496
|
-
while (checkParent) {
|
|
4497
|
-
if (t.isFunction(checkParent.node)) {
|
|
4498
|
-
if (checkParent.node.params.some(p =>
|
|
4499
|
-
t.isIdentifier(p) && p.name === varName
|
|
4500
|
-
)) {
|
|
4501
|
-
isParameter = true;
|
|
4502
|
-
break;
|
|
4503
|
-
}
|
|
4504
|
-
}
|
|
4505
|
-
checkParent = checkParent.getFunctionParent();
|
|
4506
|
-
}
|
|
4507
|
-
|
|
4508
|
-
if (isParameter) {
|
|
4509
|
-
rejectionPatterns.push({
|
|
4510
|
-
functionId: targetFunctionId,
|
|
4511
|
-
errorClassName: null,
|
|
4512
|
-
rejectionType: 'variable_parameter',
|
|
4513
|
-
file: module.file,
|
|
4514
|
-
line: callLine,
|
|
4515
|
-
column: callColumn,
|
|
4516
|
-
sourceVariableName: varName
|
|
4517
|
-
});
|
|
4518
|
-
} else {
|
|
4519
|
-
// Try micro-trace
|
|
4520
|
-
const { errorClassName, tracePath } = this.microTraceToErrorClass(
|
|
4521
|
-
varName,
|
|
4522
|
-
funcParent as NodePath<t.Function>,
|
|
4523
|
-
variableDeclarations
|
|
4524
|
-
);
|
|
4525
|
-
|
|
4526
|
-
rejectionPatterns.push({
|
|
4527
|
-
functionId: targetFunctionId,
|
|
4528
|
-
errorClassName,
|
|
4529
|
-
rejectionType: errorClassName ? 'variable_traced' : 'variable_unknown',
|
|
4530
|
-
file: module.file,
|
|
4531
|
-
line: callLine,
|
|
4532
|
-
column: callColumn,
|
|
4533
|
-
sourceVariableName: varName,
|
|
4534
|
-
tracePath
|
|
4535
|
-
});
|
|
4536
|
-
}
|
|
4537
|
-
}
|
|
4538
|
-
break;
|
|
4539
|
-
}
|
|
4540
|
-
funcParent = funcParent.getFunctionParent();
|
|
4541
|
-
}
|
|
4542
|
-
}
|
|
4543
|
-
|
|
4544
|
-
// REG-311: Detect Promise.reject(new Error()) pattern
|
|
4545
|
-
if (t.isMemberExpression(callNode.callee) && currentFunctionId) {
|
|
4546
|
-
const memberCallee = callNode.callee;
|
|
4547
|
-
if (t.isIdentifier(memberCallee.object) &&
|
|
4548
|
-
memberCallee.object.name === 'Promise' &&
|
|
4549
|
-
t.isIdentifier(memberCallee.property) &&
|
|
4550
|
-
memberCallee.property.name === 'reject' &&
|
|
4551
|
-
callNode.arguments.length > 0) {
|
|
4552
|
-
const arg = callNode.arguments[0];
|
|
4553
|
-
const callLine = getLine(callNode);
|
|
4554
|
-
const callColumn = getColumn(callNode);
|
|
4555
|
-
|
|
4556
|
-
// Case 1: Promise.reject(new Error())
|
|
4557
|
-
if (t.isNewExpression(arg) && t.isIdentifier(arg.callee)) {
|
|
4558
|
-
rejectionPatterns.push({
|
|
4559
|
-
functionId: currentFunctionId,
|
|
4560
|
-
errorClassName: arg.callee.name,
|
|
4561
|
-
rejectionType: 'promise_reject',
|
|
4562
|
-
file: module.file,
|
|
4563
|
-
line: callLine,
|
|
4564
|
-
column: callColumn
|
|
4565
|
-
});
|
|
4566
|
-
}
|
|
4567
|
-
// Case 2: Promise.reject(err) where err is variable
|
|
4568
|
-
else if (t.isIdentifier(arg)) {
|
|
4569
|
-
const varName = arg.name;
|
|
4570
|
-
// Check if it's a parameter of containing function
|
|
4571
|
-
const isParameter = functionNode
|
|
4572
|
-
? functionNode.params.some(param => t.isIdentifier(param) && param.name === varName)
|
|
4573
|
-
: false;
|
|
4574
|
-
|
|
4575
|
-
if (isParameter) {
|
|
4576
|
-
rejectionPatterns.push({
|
|
4577
|
-
functionId: currentFunctionId,
|
|
4578
|
-
errorClassName: null,
|
|
4579
|
-
rejectionType: 'variable_parameter',
|
|
4580
|
-
file: module.file,
|
|
4581
|
-
line: callLine,
|
|
4582
|
-
column: callColumn,
|
|
4583
|
-
sourceVariableName: varName
|
|
4584
|
-
});
|
|
4585
|
-
} else {
|
|
4586
|
-
// Try micro-trace
|
|
4587
|
-
if (!functionPath) {
|
|
4588
|
-
rejectionPatterns.push({
|
|
4589
|
-
functionId: currentFunctionId,
|
|
4590
|
-
errorClassName: null,
|
|
4591
|
-
rejectionType: 'variable_unknown',
|
|
4592
|
-
file: module.file,
|
|
4593
|
-
line: callLine,
|
|
4594
|
-
column: callColumn,
|
|
4595
|
-
sourceVariableName: varName,
|
|
4596
|
-
tracePath: [varName]
|
|
4597
|
-
});
|
|
4598
|
-
return;
|
|
4599
|
-
}
|
|
4600
|
-
|
|
4601
|
-
const { errorClassName, tracePath } = this.microTraceToErrorClass(
|
|
4602
|
-
varName,
|
|
4603
|
-
functionPath,
|
|
4604
|
-
variableDeclarations
|
|
4605
|
-
);
|
|
4606
|
-
|
|
4607
|
-
rejectionPatterns.push({
|
|
4608
|
-
functionId: currentFunctionId,
|
|
4609
|
-
errorClassName,
|
|
4610
|
-
rejectionType: errorClassName ? 'variable_traced' : 'variable_unknown',
|
|
4611
|
-
file: module.file,
|
|
4612
|
-
line: callLine,
|
|
4613
|
-
column: callColumn,
|
|
4614
|
-
sourceVariableName: varName,
|
|
4615
|
-
tracePath
|
|
4616
|
-
});
|
|
4617
|
-
}
|
|
4618
|
-
}
|
|
4619
|
-
}
|
|
4620
|
-
}
|
|
4621
|
-
},
|
|
4622
|
-
|
|
4623
|
-
// NewExpression (constructor calls)
|
|
4624
|
-
NewExpression: (newPath: NodePath<t.NewExpression>) => {
|
|
4625
|
-
const newNode = newPath.node;
|
|
4626
|
-
const nodeKey = `new:${newNode.start}:${newNode.end}`;
|
|
4627
|
-
|
|
4628
|
-
// Determine className from callee
|
|
4629
|
-
let className: string | null = null;
|
|
4630
|
-
if (newNode.callee.type === 'Identifier') {
|
|
4631
|
-
className = newNode.callee.name;
|
|
4632
|
-
} else if (newNode.callee.type === 'MemberExpression' && newNode.callee.property.type === 'Identifier') {
|
|
4633
|
-
className = newNode.callee.property.name;
|
|
4634
|
-
}
|
|
4635
|
-
|
|
4636
|
-
// Create CONSTRUCTOR_CALL node (always, for all NewExpressions)
|
|
4637
|
-
if (className) {
|
|
4638
|
-
const constructorKey = `constructor:${nodeKey}`;
|
|
4639
|
-
if (!processedCallSites.has(constructorKey)) {
|
|
4640
|
-
processedCallSites.add(constructorKey);
|
|
4641
|
-
|
|
4642
|
-
const line = getLine(newNode);
|
|
4643
|
-
const column = getColumn(newNode);
|
|
4644
|
-
const constructorCallId = ConstructorCallNode.generateId(className, module.file, line, column);
|
|
4645
|
-
const isBuiltin = ConstructorCallNode.isBuiltinConstructor(className);
|
|
4646
|
-
|
|
4647
|
-
constructorCalls.push({
|
|
4648
|
-
id: constructorCallId,
|
|
4649
|
-
type: 'CONSTRUCTOR_CALL',
|
|
4650
|
-
className,
|
|
4651
|
-
isBuiltin,
|
|
4652
|
-
file: module.file,
|
|
4653
|
-
line,
|
|
4654
|
-
column
|
|
4655
|
-
});
|
|
4656
|
-
|
|
4657
|
-
// REG-334: If this is Promise constructor with executor callback,
|
|
4658
|
-
// register the context for resolve/reject detection
|
|
4659
|
-
if (className === 'Promise' && newNode.arguments.length > 0) {
|
|
4660
|
-
const executorArg = newNode.arguments[0];
|
|
4661
|
-
|
|
4662
|
-
// Only handle inline function expressions (not variable references)
|
|
4663
|
-
if (t.isArrowFunctionExpression(executorArg) || t.isFunctionExpression(executorArg)) {
|
|
4664
|
-
// Extract resolve/reject parameter names
|
|
4665
|
-
let resolveName: string | undefined;
|
|
4666
|
-
let rejectName: string | undefined;
|
|
4667
|
-
|
|
4668
|
-
if (executorArg.params.length > 0 && t.isIdentifier(executorArg.params[0])) {
|
|
4669
|
-
resolveName = executorArg.params[0].name;
|
|
4670
|
-
}
|
|
4671
|
-
if (executorArg.params.length > 1 && t.isIdentifier(executorArg.params[1])) {
|
|
4672
|
-
rejectName = executorArg.params[1].name;
|
|
4673
|
-
}
|
|
4674
|
-
|
|
4675
|
-
if (resolveName) {
|
|
4676
|
-
// Key by function node position to allow nested Promise detection
|
|
4677
|
-
const funcKey = `${executorArg.start}:${executorArg.end}`;
|
|
4678
|
-
promiseExecutorContexts.set(funcKey, {
|
|
4679
|
-
constructorCallId,
|
|
4680
|
-
resolveName,
|
|
4681
|
-
rejectName,
|
|
4682
|
-
file: module.file,
|
|
4683
|
-
line,
|
|
4684
|
-
// REG-311: Store the ID of the function that creates the Promise
|
|
4685
|
-
creatorFunctionId: currentFunctionId || undefined
|
|
4686
|
-
});
|
|
4687
|
-
}
|
|
4688
|
-
}
|
|
4689
|
-
}
|
|
4690
|
-
}
|
|
4691
|
-
}
|
|
4692
|
-
|
|
4693
|
-
// Handle simple constructor: new Foo()
|
|
4694
|
-
if (newNode.callee.type === 'Identifier') {
|
|
4695
|
-
if (processedCallSites.has(nodeKey)) {
|
|
4696
|
-
return;
|
|
4697
|
-
}
|
|
4698
|
-
processedCallSites.add(nodeKey);
|
|
4699
|
-
|
|
4700
|
-
// Generate semantic ID (primary) or legacy ID (fallback)
|
|
4701
|
-
const constructorName = newNode.callee.name;
|
|
4702
|
-
const legacyId = `CALL#new:${constructorName}#${module.file}#${getLine(newNode)}:${getColumn(newNode)}:${callSiteCounterRef.value++}`;
|
|
4703
|
-
|
|
4704
|
-
let newCallId = legacyId;
|
|
4705
|
-
if (scopeTracker) {
|
|
4706
|
-
const discriminator = scopeTracker.getItemCounter(`CALL:new:${constructorName}`);
|
|
4707
|
-
newCallId = computeSemanticId('CALL', `new:${constructorName}`, scopeTracker.getContext(), { discriminator });
|
|
4708
|
-
}
|
|
4709
|
-
|
|
4710
|
-
callSites.push({
|
|
4711
|
-
id: newCallId,
|
|
4712
|
-
type: 'CALL',
|
|
4713
|
-
name: constructorName,
|
|
4714
|
-
file: module.file,
|
|
4715
|
-
line: getLine(newNode),
|
|
4716
|
-
parentScopeId: getCurrentScopeId(),
|
|
4717
|
-
targetFunctionName: constructorName,
|
|
4718
|
-
isNew: true
|
|
4719
|
-
});
|
|
4720
|
-
}
|
|
4721
|
-
// Handle namespaced constructor: new ns.Constructor()
|
|
4722
|
-
else if (newNode.callee.type === 'MemberExpression') {
|
|
4723
|
-
const memberCallee = newNode.callee;
|
|
4724
|
-
const object = memberCallee.object;
|
|
4725
|
-
const property = memberCallee.property;
|
|
4726
|
-
|
|
4727
|
-
if (object.type === 'Identifier' && property.type === 'Identifier') {
|
|
4728
|
-
if (processedMethodCalls.has(nodeKey)) {
|
|
4729
|
-
return;
|
|
4730
|
-
}
|
|
4731
|
-
processedMethodCalls.add(nodeKey);
|
|
4732
|
-
|
|
4733
|
-
const objectName = object.name;
|
|
4734
|
-
const constructorName = property.name;
|
|
4735
|
-
const fullName = `${objectName}.${constructorName}`;
|
|
4736
|
-
|
|
4737
|
-
// Generate semantic ID for method-style constructor call
|
|
4738
|
-
const legacyId = `CALL#new:${fullName}#${module.file}#${getLine(newNode)}:${getColumn(newNode)}:${callSiteCounterRef.value++}`;
|
|
4739
|
-
|
|
4740
|
-
let newMethodCallId = legacyId;
|
|
4741
|
-
if (scopeTracker) {
|
|
4742
|
-
const discriminator = scopeTracker.getItemCounter(`CALL:new:${fullName}`);
|
|
4743
|
-
newMethodCallId = computeSemanticId('CALL', `new:${fullName}`, scopeTracker.getContext(), { discriminator });
|
|
4744
|
-
}
|
|
4745
|
-
|
|
4746
|
-
methodCalls.push({
|
|
4747
|
-
id: newMethodCallId,
|
|
4748
|
-
type: 'CALL',
|
|
4749
|
-
name: fullName,
|
|
4750
|
-
object: objectName,
|
|
4751
|
-
method: constructorName,
|
|
4752
|
-
file: module.file,
|
|
4753
|
-
line: getLine(newNode),
|
|
4754
|
-
column: getColumn(newNode),
|
|
4755
|
-
parentScopeId: getCurrentScopeId(),
|
|
4756
|
-
isNew: true
|
|
4757
|
-
});
|
|
4758
|
-
}
|
|
4759
|
-
}
|
|
4760
|
-
},
|
|
4761
|
-
|
|
4762
|
-
// Property access expressions (REG-395)
|
|
4763
|
-
// Shared handler for both MemberExpression and OptionalMemberExpression
|
|
4764
|
-
MemberExpression: (memberPath: NodePath<t.MemberExpression>) => {
|
|
4765
|
-
// Initialize collections if needed
|
|
4766
|
-
if (!collections.propertyAccesses) {
|
|
4767
|
-
collections.propertyAccesses = [];
|
|
4768
|
-
}
|
|
4769
|
-
if (!collections.propertyAccessCounterRef) {
|
|
4770
|
-
collections.propertyAccessCounterRef = { value: 0 };
|
|
4771
|
-
}
|
|
4772
|
-
|
|
4773
|
-
PropertyAccessVisitor.extractPropertyAccesses(
|
|
4774
|
-
memberPath,
|
|
4775
|
-
memberPath.node,
|
|
4776
|
-
module,
|
|
4777
|
-
collections.propertyAccesses as PropertyAccessInfo[],
|
|
4778
|
-
collections.propertyAccessCounterRef as CounterRef,
|
|
4779
|
-
scopeTracker,
|
|
4780
|
-
currentFunctionId || getCurrentScopeId()
|
|
4781
|
-
);
|
|
4782
|
-
},
|
|
4783
|
-
// OptionalMemberExpression: obj?.prop (same logic as MemberExpression)
|
|
4784
|
-
OptionalMemberExpression: (memberPath: NodePath) => {
|
|
4785
|
-
// Initialize collections if needed
|
|
4786
|
-
if (!collections.propertyAccesses) {
|
|
4787
|
-
collections.propertyAccesses = [];
|
|
4788
|
-
}
|
|
4789
|
-
if (!collections.propertyAccessCounterRef) {
|
|
4790
|
-
collections.propertyAccessCounterRef = { value: 0 };
|
|
4791
|
-
}
|
|
4792
|
-
|
|
4793
|
-
PropertyAccessVisitor.extractPropertyAccesses(
|
|
4794
|
-
memberPath,
|
|
4795
|
-
memberPath.node as t.MemberExpression,
|
|
4796
|
-
module,
|
|
4797
|
-
collections.propertyAccesses as PropertyAccessInfo[],
|
|
4798
|
-
collections.propertyAccessCounterRef as CounterRef,
|
|
4799
|
-
scopeTracker,
|
|
4800
|
-
currentFunctionId || getCurrentScopeId()
|
|
4801
|
-
);
|
|
4802
|
-
}
|
|
4803
|
-
});
|
|
4804
|
-
|
|
4805
|
-
// REG-311: Second pass - collect CATCHES_FROM info for try/catch blocks
|
|
4806
|
-
// This links catch blocks to exception sources in their corresponding try blocks
|
|
4807
|
-
if (functionPath) {
|
|
4808
|
-
this.collectCatchesFromInfo(
|
|
4809
|
-
functionPath,
|
|
4810
|
-
catchBlocks,
|
|
4811
|
-
callSites,
|
|
4812
|
-
methodCalls,
|
|
4813
|
-
constructorCalls,
|
|
4814
|
-
catchesFromInfos,
|
|
4815
|
-
module
|
|
4816
|
-
);
|
|
4817
|
-
}
|
|
4818
|
-
|
|
4819
|
-
// Phase 6 (REG-267): Attach control flow metadata to the function node
|
|
4820
|
-
if (matchingFunction) {
|
|
4821
|
-
const cyclomaticComplexity = 1 +
|
|
4822
|
-
controlFlowState.branchCount +
|
|
4823
|
-
controlFlowState.loopCount +
|
|
4824
|
-
controlFlowState.caseCount +
|
|
4825
|
-
controlFlowState.logicalOpCount;
|
|
4826
|
-
|
|
4827
|
-
// REG-311: Collect rejection info for this function
|
|
4828
|
-
const functionRejectionPatterns = rejectionPatterns.filter(p => p.functionId === matchingFunction.id);
|
|
4829
|
-
const canReject = functionRejectionPatterns.length > 0;
|
|
4830
|
-
const hasAsyncThrow = functionRejectionPatterns.some(p => p.rejectionType === 'async_throw');
|
|
4831
|
-
const rejectedBuiltinErrors = [...new Set(
|
|
4832
|
-
functionRejectionPatterns
|
|
4833
|
-
.filter(p => p.errorClassName !== null)
|
|
4834
|
-
.map(p => p.errorClassName!)
|
|
4835
|
-
)];
|
|
4836
|
-
|
|
4837
|
-
matchingFunction.controlFlow = {
|
|
4838
|
-
hasBranches: controlFlowState.branchCount > 0,
|
|
4839
|
-
hasLoops: controlFlowState.loopCount > 0,
|
|
4840
|
-
hasTryCatch: controlFlowState.hasTryCatch,
|
|
4841
|
-
hasEarlyReturn: controlFlowState.hasEarlyReturn,
|
|
4842
|
-
hasThrow: controlFlowState.hasThrow,
|
|
4843
|
-
cyclomaticComplexity,
|
|
4844
|
-
// REG-311: Async error tracking
|
|
4845
|
-
canReject,
|
|
4846
|
-
hasAsyncThrow,
|
|
4847
|
-
rejectedBuiltinErrors: rejectedBuiltinErrors.length > 0 ? rejectedBuiltinErrors : undefined
|
|
4848
|
-
};
|
|
4849
|
-
}
|
|
4850
|
-
}
|
|
2813
|
+
// REG-401: Store invoked parameter indexes for user-defined HOF detection
|
|
2814
|
+
if (ctx.invokedParamIndexes.size > 0) {
|
|
2815
|
+
ctx.matchingFunction.invokesParamIndexes = [...ctx.invokedParamIndexes];
|
|
2816
|
+
}
|
|
2817
|
+
// REG-417: Store property paths for destructured param bindings
|
|
2818
|
+
if (ctx.invokesParamBindings.length > 0) {
|
|
2819
|
+
ctx.matchingFunction.invokesParamBindings = ctx.invokesParamBindings;
|
|
2820
|
+
}
|
|
2821
|
+
}
|
|
4851
2822
|
|
|
4852
2823
|
/**
|
|
4853
2824
|
* Handle CallExpression nodes: direct function calls (greet(), main())
|
|
@@ -4885,7 +2856,8 @@ export class JSASTAnalyzer extends Plugin {
|
|
|
4885
2856
|
parentScopeId: string,
|
|
4886
2857
|
collections: VisitorCollections,
|
|
4887
2858
|
isAwaited: boolean = false,
|
|
4888
|
-
isInsideTry: boolean = false
|
|
2859
|
+
isInsideTry: boolean = false,
|
|
2860
|
+
isInsideLoop: boolean = false
|
|
4889
2861
|
): void {
|
|
4890
2862
|
// Handle direct function calls (greet(), main())
|
|
4891
2863
|
if (callNode.callee.type === 'Identifier') {
|
|
@@ -4916,7 +2888,9 @@ export class JSASTAnalyzer extends Plugin {
|
|
|
4916
2888
|
targetFunctionName: calleeName,
|
|
4917
2889
|
// REG-311: Async error tracking metadata
|
|
4918
2890
|
isAwaited,
|
|
4919
|
-
isInsideTry
|
|
2891
|
+
isInsideTry,
|
|
2892
|
+
// REG-298: Await-in-loop detection
|
|
2893
|
+
...(isAwaited && isInsideLoop ? { isInsideLoop } : {})
|
|
4920
2894
|
});
|
|
4921
2895
|
}
|
|
4922
2896
|
// Handle method calls (obj.method(), data.process())
|
|
@@ -4961,9 +2935,16 @@ export class JSASTAnalyzer extends Plugin {
|
|
|
4961
2935
|
// REG-311: Async error tracking metadata
|
|
4962
2936
|
isAwaited,
|
|
4963
2937
|
isInsideTry,
|
|
2938
|
+
// REG-298: Await-in-loop detection
|
|
2939
|
+
...(isAwaited && isInsideLoop ? { isInsideLoop } : {}),
|
|
4964
2940
|
isMethodCall: true
|
|
4965
2941
|
});
|
|
4966
2942
|
|
|
2943
|
+
// REG-400: Extract arguments for method calls (enables callback resolution)
|
|
2944
|
+
if (callNode.arguments.length > 0) {
|
|
2945
|
+
this.extractMethodCallArguments(callNode, methodCallId, module, collections);
|
|
2946
|
+
}
|
|
2947
|
+
|
|
4967
2948
|
// Check for array mutation methods (push, unshift, splice)
|
|
4968
2949
|
const ARRAY_MUTATION_METHODS = ['push', 'unshift', 'splice'];
|
|
4969
2950
|
if (ARRAY_MUTATION_METHODS.includes(methodName)) {
|
|
@@ -5065,12 +3046,112 @@ export class JSASTAnalyzer extends Plugin {
|
|
|
5065
3046
|
parentScopeId,
|
|
5066
3047
|
isMethodCall: true
|
|
5067
3048
|
});
|
|
3049
|
+
|
|
3050
|
+
// REG-400: Extract arguments for nested method calls (enables callback resolution)
|
|
3051
|
+
if (callNode.arguments.length > 0) {
|
|
3052
|
+
this.extractMethodCallArguments(callNode, methodCallId, module, collections);
|
|
3053
|
+
}
|
|
5068
3054
|
}
|
|
5069
3055
|
}
|
|
5070
3056
|
}
|
|
5071
3057
|
}
|
|
5072
3058
|
}
|
|
5073
3059
|
|
|
3060
|
+
/**
|
|
3061
|
+
* REG-400: Extract arguments from method call nodes inside function bodies.
|
|
3062
|
+
* Populates callArguments collection so GraphBuilder.bufferArgumentEdges can
|
|
3063
|
+
* create PASSES_ARGUMENT and callback CALLS edges.
|
|
3064
|
+
*
|
|
3065
|
+
* This mirrors CallExpressionVisitor.extractArguments but is simplified —
|
|
3066
|
+
* handles Identifier, Literal, CallExpression, and Expression types.
|
|
3067
|
+
*/
|
|
3068
|
+
private extractMethodCallArguments(
|
|
3069
|
+
callNode: t.CallExpression,
|
|
3070
|
+
methodCallId: string,
|
|
3071
|
+
module: VisitorModule,
|
|
3072
|
+
collections: VisitorCollections
|
|
3073
|
+
): void {
|
|
3074
|
+
if (!collections.callArguments) {
|
|
3075
|
+
collections.callArguments = [];
|
|
3076
|
+
}
|
|
3077
|
+
const callArguments = collections.callArguments as CallArgumentInfo[];
|
|
3078
|
+
const literals = (collections.literals ?? []) as LiteralInfo[];
|
|
3079
|
+
const literalCounterRef = (collections.literalCounterRef ?? { value: 0 }) as CounterRef;
|
|
3080
|
+
|
|
3081
|
+
callNode.arguments.forEach((arg, argIndex) => {
|
|
3082
|
+
const argInfo: CallArgumentInfo = {
|
|
3083
|
+
callId: methodCallId,
|
|
3084
|
+
argIndex,
|
|
3085
|
+
file: module.file,
|
|
3086
|
+
line: getLine(arg),
|
|
3087
|
+
column: getColumn(arg)
|
|
3088
|
+
};
|
|
3089
|
+
|
|
3090
|
+
if (t.isSpreadElement(arg)) {
|
|
3091
|
+
const spreadArg = arg.argument;
|
|
3092
|
+
if (t.isIdentifier(spreadArg)) {
|
|
3093
|
+
argInfo.targetType = 'VARIABLE';
|
|
3094
|
+
argInfo.targetName = spreadArg.name;
|
|
3095
|
+
argInfo.isSpread = true;
|
|
3096
|
+
}
|
|
3097
|
+
} else if (t.isIdentifier(arg)) {
|
|
3098
|
+
argInfo.targetType = 'VARIABLE';
|
|
3099
|
+
argInfo.targetName = arg.name;
|
|
3100
|
+
} else if (t.isLiteral(arg) && !t.isTemplateLiteral(arg)) {
|
|
3101
|
+
const literalValue = ExpressionEvaluator.extractLiteralValue(arg as t.Literal);
|
|
3102
|
+
if (literalValue !== null) {
|
|
3103
|
+
const argLine = getLine(arg);
|
|
3104
|
+
const argColumn = getColumn(arg);
|
|
3105
|
+
const literalId = `LITERAL#arg${argIndex}#${module.file}#${argLine}:${argColumn}:${literalCounterRef.value++}`;
|
|
3106
|
+
literals.push({
|
|
3107
|
+
id: literalId,
|
|
3108
|
+
type: 'LITERAL',
|
|
3109
|
+
value: literalValue,
|
|
3110
|
+
valueType: typeof literalValue,
|
|
3111
|
+
file: module.file,
|
|
3112
|
+
line: argLine,
|
|
3113
|
+
column: argColumn,
|
|
3114
|
+
parentCallId: methodCallId,
|
|
3115
|
+
argIndex
|
|
3116
|
+
});
|
|
3117
|
+
argInfo.targetType = 'LITERAL';
|
|
3118
|
+
argInfo.targetId = literalId;
|
|
3119
|
+
argInfo.literalValue = literalValue;
|
|
3120
|
+
}
|
|
3121
|
+
} else if (t.isArrowFunctionExpression(arg) || t.isFunctionExpression(arg)) {
|
|
3122
|
+
argInfo.targetType = 'FUNCTION';
|
|
3123
|
+
argInfo.functionLine = getLine(arg);
|
|
3124
|
+
argInfo.functionColumn = getColumn(arg);
|
|
3125
|
+
} else if (t.isCallExpression(arg)) {
|
|
3126
|
+
argInfo.targetType = 'CALL';
|
|
3127
|
+
argInfo.nestedCallLine = getLine(arg);
|
|
3128
|
+
argInfo.nestedCallColumn = getColumn(arg);
|
|
3129
|
+
// REG-402: MemberExpression arguments (this.handler, obj.method)
|
|
3130
|
+
} else if (t.isMemberExpression(arg)) {
|
|
3131
|
+
argInfo.targetType = 'EXPRESSION';
|
|
3132
|
+
argInfo.expressionType = 'MemberExpression';
|
|
3133
|
+
if (t.isIdentifier(arg.object)) {
|
|
3134
|
+
argInfo.objectName = arg.object.name;
|
|
3135
|
+
} else if (t.isThisExpression(arg.object)) {
|
|
3136
|
+
argInfo.objectName = 'this';
|
|
3137
|
+
// Store enclosing class name for direct lookup in GraphBuilder
|
|
3138
|
+
const scopeTracker = collections.scopeTracker as ScopeTracker | undefined;
|
|
3139
|
+
if (scopeTracker) {
|
|
3140
|
+
argInfo.enclosingClassName = scopeTracker.getEnclosingScope('CLASS');
|
|
3141
|
+
}
|
|
3142
|
+
}
|
|
3143
|
+
if (!arg.computed && t.isIdentifier(arg.property)) {
|
|
3144
|
+
argInfo.propertyName = arg.property.name;
|
|
3145
|
+
}
|
|
3146
|
+
} else {
|
|
3147
|
+
argInfo.targetType = 'EXPRESSION';
|
|
3148
|
+
argInfo.expressionType = arg.type;
|
|
3149
|
+
}
|
|
3150
|
+
|
|
3151
|
+
callArguments.push(argInfo);
|
|
3152
|
+
});
|
|
3153
|
+
}
|
|
3154
|
+
|
|
5074
3155
|
/**
|
|
5075
3156
|
* REG-311: Micro-trace - follow variable assignments within function to find error source.
|
|
5076
3157
|
* Used to resolve reject(err) or throw err where err is a variable.
|
|
@@ -5461,34 +3542,12 @@ export class JSASTAnalyzer extends Plugin {
|
|
|
5461
3542
|
valueType: 'EXPRESSION'
|
|
5462
3543
|
};
|
|
5463
3544
|
|
|
5464
|
-
// Determine value type and create value nodes for non-variable types
|
|
5465
|
-
|
|
5466
|
-
|
|
5467
|
-
|
|
5468
|
-
|
|
5469
|
-
|
|
5470
|
-
const valueColumn = value.loc?.start.column ?? column;
|
|
5471
|
-
// Create LITERAL node if collections available
|
|
5472
|
-
if (collections?.literals && collections.literalCounterRef) {
|
|
5473
|
-
const literalCounterRef = collections.literalCounterRef as CounterRef;
|
|
5474
|
-
const literalId = `LITERAL#indexed#${module.file}#${valueLine}:${valueColumn}:${literalCounterRef.value++}`;
|
|
5475
|
-
(collections.literals as LiteralInfo[]).push({
|
|
5476
|
-
id: literalId,
|
|
5477
|
-
type: 'LITERAL',
|
|
5478
|
-
value: literalValue,
|
|
5479
|
-
valueType: typeof literalValue,
|
|
5480
|
-
file: module.file,
|
|
5481
|
-
line: valueLine,
|
|
5482
|
-
column: valueColumn,
|
|
5483
|
-
parentCallId: undefined,
|
|
5484
|
-
argIndex: 0
|
|
5485
|
-
} as LiteralInfo);
|
|
5486
|
-
argInfo.valueNodeId = literalId;
|
|
5487
|
-
}
|
|
5488
|
-
} else if (value.type === 'Identifier') {
|
|
5489
|
-
argInfo.valueType = 'VARIABLE';
|
|
5490
|
-
argInfo.valueName = value.name;
|
|
5491
|
-
} else if (value.type === 'ObjectExpression') {
|
|
3545
|
+
// Determine value type and create value nodes for non-variable types
|
|
3546
|
+
// IMPORTANT: Check ObjectExpression/ArrayExpression BEFORE extractLiteralValue
|
|
3547
|
+
// to match the order in detectArrayMutation and extractArguments (REG-396).
|
|
3548
|
+
// extractLiteralValue returns objects/arrays with all-literal properties as
|
|
3549
|
+
// literal values, but we want OBJECT_LITERAL/ARRAY_LITERAL nodes instead.
|
|
3550
|
+
if (value.type === 'ObjectExpression') {
|
|
5492
3551
|
argInfo.valueType = 'OBJECT_LITERAL';
|
|
5493
3552
|
const valueLine = value.loc?.start.line ?? line;
|
|
5494
3553
|
const valueColumn = value.loc?.start.column ?? column;
|
|
@@ -5518,10 +3577,38 @@ export class JSASTAnalyzer extends Plugin {
|
|
|
5518
3577
|
(collections.arrayLiterals as ArrayLiteralInfo[]).push(arrayNode as unknown as ArrayLiteralInfo);
|
|
5519
3578
|
argInfo.valueNodeId = arrayNode.id;
|
|
5520
3579
|
}
|
|
3580
|
+
} else if (value.type === 'Identifier') {
|
|
3581
|
+
argInfo.valueType = 'VARIABLE';
|
|
3582
|
+
argInfo.valueName = value.name;
|
|
5521
3583
|
} else if (value.type === 'CallExpression') {
|
|
5522
3584
|
argInfo.valueType = 'CALL';
|
|
5523
3585
|
argInfo.callLine = value.loc?.start.line;
|
|
5524
3586
|
argInfo.callColumn = value.loc?.start.column;
|
|
3587
|
+
} else {
|
|
3588
|
+
const literalValue = ExpressionEvaluator.extractLiteralValue(value);
|
|
3589
|
+
if (literalValue !== null) {
|
|
3590
|
+
argInfo.valueType = 'LITERAL';
|
|
3591
|
+
argInfo.literalValue = literalValue;
|
|
3592
|
+
const valueLine = value.loc?.start.line ?? line;
|
|
3593
|
+
const valueColumn = value.loc?.start.column ?? column;
|
|
3594
|
+
// Create LITERAL node if collections available
|
|
3595
|
+
if (collections?.literals && collections.literalCounterRef) {
|
|
3596
|
+
const literalCounterRef = collections.literalCounterRef as CounterRef;
|
|
3597
|
+
const literalId = `LITERAL#indexed#${module.file}#${valueLine}:${valueColumn}:${literalCounterRef.value++}`;
|
|
3598
|
+
(collections.literals as LiteralInfo[]).push({
|
|
3599
|
+
id: literalId,
|
|
3600
|
+
type: 'LITERAL',
|
|
3601
|
+
value: literalValue,
|
|
3602
|
+
valueType: typeof literalValue,
|
|
3603
|
+
file: module.file,
|
|
3604
|
+
line: valueLine,
|
|
3605
|
+
column: valueColumn,
|
|
3606
|
+
parentCallId: undefined,
|
|
3607
|
+
argIndex: 0
|
|
3608
|
+
} as LiteralInfo);
|
|
3609
|
+
argInfo.valueNodeId = literalId;
|
|
3610
|
+
}
|
|
3611
|
+
}
|
|
5525
3612
|
}
|
|
5526
3613
|
|
|
5527
3614
|
// Capture scope path for scope-aware lookup (REG-309)
|