@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
|
@@ -32,11 +32,18 @@ import { getLine, getColumn } from './ast/utils/location.js';
|
|
|
32
32
|
import { Profiler } from '../../core/Profiler.js';
|
|
33
33
|
import { ScopeTracker } from '../../core/ScopeTracker.js';
|
|
34
34
|
import { computeSemanticId } from '../../core/SemanticId.js';
|
|
35
|
+
import { IdGenerator } from './ast/IdGenerator.js';
|
|
36
|
+
import { CollisionResolver } from './ast/CollisionResolver.js';
|
|
35
37
|
import { ExpressionNode } from '../../core/nodes/ExpressionNode.js';
|
|
36
38
|
import { ConstructorCallNode } from '../../core/nodes/ConstructorCallNode.js';
|
|
37
39
|
import { ObjectLiteralNode } from '../../core/nodes/ObjectLiteralNode.js';
|
|
38
40
|
import { ArrayLiteralNode } from '../../core/nodes/ArrayLiteralNode.js';
|
|
39
41
|
import { NodeFactory } from '../../core/NodeFactory.js';
|
|
42
|
+
import { brandNodeInternal } from '../../core/brandNodeInternal.js';
|
|
43
|
+
import { resolveNodeFile } from '../../utils/resolveNodeFile.js';
|
|
44
|
+
import { extractNamesFromPattern } from './ast/utils/extractNamesFromPattern.js';
|
|
45
|
+
import { createFunctionBodyContext } from './ast/FunctionBodyContext.js';
|
|
46
|
+
import { VariableHandler, ReturnYieldHandler, ThrowHandler, NestedFunctionHandler, PropertyAccessHandler, NewExpressionHandler, CallExpressionHandler, LoopHandler, TryCatchHandler, BranchHandler, } from './ast/handlers/index.js';
|
|
40
47
|
export class JSASTAnalyzer extends Plugin {
|
|
41
48
|
graphBuilder;
|
|
42
49
|
analyzedModules;
|
|
@@ -57,14 +64,14 @@ export class JSASTAnalyzer extends Plugin {
|
|
|
57
64
|
'CALL', 'IMPORT', 'EXPORT', 'LITERAL', 'EXTERNAL_MODULE',
|
|
58
65
|
'net:stdio', 'net:request', 'event:listener', 'http:request',
|
|
59
66
|
// TypeScript-specific nodes
|
|
60
|
-
'INTERFACE', 'TYPE', 'ENUM', 'DECORATOR'
|
|
67
|
+
'INTERFACE', 'TYPE', 'ENUM', 'DECORATOR', 'TYPE_PARAMETER'
|
|
61
68
|
],
|
|
62
69
|
edges: [
|
|
63
70
|
'CONTAINS', 'DECLARES', 'CALLS', 'HAS_SCOPE', 'CAPTURES', 'MODIFIES',
|
|
64
71
|
'WRITES_TO', 'IMPORTS', 'INSTANCE_OF', 'HANDLED_BY', 'HAS_CALLBACK',
|
|
65
72
|
'PASSES_ARGUMENT', 'MAKES_REQUEST', 'IMPORTS_FROM', 'EXPORTS_TO', 'ASSIGNED_FROM',
|
|
66
73
|
// TypeScript-specific edges
|
|
67
|
-
'IMPLEMENTS', 'EXTENDS', 'DECORATED_BY',
|
|
74
|
+
'IMPLEMENTS', 'EXTENDS', 'DECORATED_BY', 'HAS_TYPE_PARAMETER',
|
|
68
75
|
// Promise data flow
|
|
69
76
|
'RESOLVES_TO'
|
|
70
77
|
]
|
|
@@ -84,9 +91,9 @@ export class JSASTAnalyzer extends Plugin {
|
|
|
84
91
|
/**
|
|
85
92
|
* Вычисляет хеш содержимого файла
|
|
86
93
|
*/
|
|
87
|
-
calculateFileHash(filePath) {
|
|
94
|
+
calculateFileHash(filePath, projectPath = '') {
|
|
88
95
|
try {
|
|
89
|
-
const content = readFileSync(filePath, 'utf-8');
|
|
96
|
+
const content = readFileSync(resolveNodeFile(filePath, projectPath), 'utf-8');
|
|
90
97
|
return createHash('sha256').update(content).digest('hex');
|
|
91
98
|
}
|
|
92
99
|
catch {
|
|
@@ -96,25 +103,25 @@ export class JSASTAnalyzer extends Plugin {
|
|
|
96
103
|
/**
|
|
97
104
|
* Проверяет нужно ли анализировать модуль (сравнивает хеши)
|
|
98
105
|
*/
|
|
99
|
-
async shouldAnalyzeModule(module, graph, forceAnalysis) {
|
|
106
|
+
async shouldAnalyzeModule(module, graph, forceAnalysis, projectPath = "") {
|
|
100
107
|
if (forceAnalysis) {
|
|
101
108
|
return true;
|
|
102
109
|
}
|
|
103
110
|
if (!module.contentHash) {
|
|
104
111
|
return true;
|
|
105
112
|
}
|
|
106
|
-
const currentHash = this.calculateFileHash(module.file);
|
|
113
|
+
const currentHash = this.calculateFileHash(module.file, projectPath);
|
|
107
114
|
if (!currentHash) {
|
|
108
115
|
return true;
|
|
109
116
|
}
|
|
110
117
|
if (currentHash !== module.contentHash) {
|
|
111
|
-
await graph.addNode({
|
|
118
|
+
await graph.addNode(brandNodeInternal({
|
|
112
119
|
id: module.id,
|
|
113
120
|
type: 'MODULE',
|
|
114
121
|
name: module.name,
|
|
115
122
|
file: module.file,
|
|
116
123
|
contentHash: currentHash
|
|
117
|
-
});
|
|
124
|
+
}));
|
|
118
125
|
return true;
|
|
119
126
|
}
|
|
120
127
|
// Hash matches - check if module was actually analyzed (has FUNCTION nodes)
|
|
@@ -143,7 +150,7 @@ export class JSASTAnalyzer extends Plugin {
|
|
|
143
150
|
skippedCount++;
|
|
144
151
|
continue;
|
|
145
152
|
}
|
|
146
|
-
if (await this.shouldAnalyzeModule(module, graph, forceAnalysis)) {
|
|
153
|
+
if (await this.shouldAnalyzeModule(module, graph, forceAnalysis, projectPath)) {
|
|
147
154
|
modulesToAnalyze.push(module);
|
|
148
155
|
}
|
|
149
156
|
else {
|
|
@@ -248,7 +255,7 @@ export class JSASTAnalyzer extends Plugin {
|
|
|
248
255
|
// Convert ModuleNode to ASTModuleInfo format
|
|
249
256
|
const moduleInfos = modules.map(m => ({
|
|
250
257
|
id: m.id,
|
|
251
|
-
file: m.file,
|
|
258
|
+
file: resolveNodeFile(m.file, projectPath),
|
|
252
259
|
name: m.name
|
|
253
260
|
}));
|
|
254
261
|
// Parse all modules in parallel using worker threads
|
|
@@ -282,7 +289,7 @@ export class JSASTAnalyzer extends Plugin {
|
|
|
282
289
|
context.onProgress({
|
|
283
290
|
phase: 'analysis',
|
|
284
291
|
currentPlugin: 'JSASTAnalyzer',
|
|
285
|
-
message: `Processed ${result.module.
|
|
292
|
+
message: `Processed ${result.module.name}`,
|
|
286
293
|
totalFiles: modules.length,
|
|
287
294
|
processedFiles: results.indexOf(result) + 1
|
|
288
295
|
});
|
|
@@ -298,73 +305,13 @@ export class JSASTAnalyzer extends Plugin {
|
|
|
298
305
|
/**
|
|
299
306
|
* Extract variable names from destructuring patterns
|
|
300
307
|
* Uses t.isX() type guards to avoid casts
|
|
308
|
+
*
|
|
309
|
+
* REG-399: Delegated to extractNamesFromPattern utility for code reuse with parameters.
|
|
310
|
+
* This method maintains the same API for backward compatibility.
|
|
301
311
|
*/
|
|
302
312
|
extractVariableNamesFromPattern(pattern, variables = [], propertyPath = []) {
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
if (t.isIdentifier(pattern)) {
|
|
306
|
-
variables.push({
|
|
307
|
-
name: pattern.name,
|
|
308
|
-
loc: pattern.loc?.start ? { start: pattern.loc.start } : { start: { line: 0, column: 0 } },
|
|
309
|
-
propertyPath: propertyPath.length > 0 ? [...propertyPath] : undefined
|
|
310
|
-
});
|
|
311
|
-
}
|
|
312
|
-
else if (t.isObjectPattern(pattern)) {
|
|
313
|
-
pattern.properties.forEach((prop) => {
|
|
314
|
-
if (t.isRestElement(prop)) {
|
|
315
|
-
const restVars = this.extractVariableNamesFromPattern(prop.argument, [], []);
|
|
316
|
-
restVars.forEach(v => {
|
|
317
|
-
v.isRest = true;
|
|
318
|
-
v.propertyPath = propertyPath.length > 0 ? [...propertyPath] : undefined;
|
|
319
|
-
variables.push(v);
|
|
320
|
-
});
|
|
321
|
-
}
|
|
322
|
-
else if (t.isObjectProperty(prop) && prop.value) {
|
|
323
|
-
const key = t.isIdentifier(prop.key) ? prop.key.name :
|
|
324
|
-
(t.isStringLiteral(prop.key) || t.isNumericLiteral(prop.key) ? String(prop.key.value) : null);
|
|
325
|
-
if (key !== null) {
|
|
326
|
-
const newPath = [...propertyPath, key];
|
|
327
|
-
this.extractVariableNamesFromPattern(prop.value, variables, newPath);
|
|
328
|
-
}
|
|
329
|
-
else {
|
|
330
|
-
this.extractVariableNamesFromPattern(prop.value, variables, propertyPath);
|
|
331
|
-
}
|
|
332
|
-
}
|
|
333
|
-
});
|
|
334
|
-
}
|
|
335
|
-
else if (t.isArrayPattern(pattern)) {
|
|
336
|
-
pattern.elements.forEach((element, index) => {
|
|
337
|
-
if (element) {
|
|
338
|
-
if (t.isRestElement(element)) {
|
|
339
|
-
const restVars = this.extractVariableNamesFromPattern(element.argument, [], []);
|
|
340
|
-
restVars.forEach(v => {
|
|
341
|
-
v.isRest = true;
|
|
342
|
-
v.arrayIndex = index;
|
|
343
|
-
v.propertyPath = propertyPath.length > 0 ? [...propertyPath] : undefined;
|
|
344
|
-
variables.push(v);
|
|
345
|
-
});
|
|
346
|
-
}
|
|
347
|
-
else {
|
|
348
|
-
const extracted = this.extractVariableNamesFromPattern(element, [], propertyPath);
|
|
349
|
-
extracted.forEach(v => {
|
|
350
|
-
v.arrayIndex = index;
|
|
351
|
-
variables.push(v);
|
|
352
|
-
});
|
|
353
|
-
}
|
|
354
|
-
}
|
|
355
|
-
});
|
|
356
|
-
}
|
|
357
|
-
else if (t.isRestElement(pattern)) {
|
|
358
|
-
const restVars = this.extractVariableNamesFromPattern(pattern.argument, [], propertyPath);
|
|
359
|
-
restVars.forEach(v => {
|
|
360
|
-
v.isRest = true;
|
|
361
|
-
variables.push(v);
|
|
362
|
-
});
|
|
363
|
-
}
|
|
364
|
-
else if (t.isAssignmentPattern(pattern)) {
|
|
365
|
-
this.extractVariableNamesFromPattern(pattern.left, variables, propertyPath);
|
|
366
|
-
}
|
|
367
|
-
return variables;
|
|
313
|
+
// Delegate to the extracted utility function
|
|
314
|
+
return extractNamesFromPattern(pattern, variables, propertyPath);
|
|
368
315
|
}
|
|
369
316
|
/**
|
|
370
317
|
* Отслеживает присваивание переменной для data flow анализа
|
|
@@ -426,54 +373,15 @@ export class JSASTAnalyzer extends Plugin {
|
|
|
426
373
|
return;
|
|
427
374
|
}
|
|
428
375
|
// 3. MemberExpression call (e.g., arr.map())
|
|
376
|
+
// Uses coordinate-based lookup to reference the standard CALL node created by CallExpressionVisitor
|
|
429
377
|
if (initExpression.type === 'CallExpression' && initExpression.callee.type === 'MemberExpression') {
|
|
430
|
-
const callee = initExpression.callee;
|
|
431
|
-
const objectName = callee.object.type === 'Identifier' ? callee.object.name : (callee.object.type === 'ThisExpression' ? 'this' : 'unknown');
|
|
432
|
-
const methodName = callee.property.type === 'Identifier' ? callee.property.name : 'unknown';
|
|
433
|
-
const fullName = `${objectName}.${methodName}`;
|
|
434
|
-
const methodCallId = `CALL#${fullName}#${module.file}#${getLine(initExpression)}:${getColumn(initExpression)}:inline`;
|
|
435
|
-
const existing = variableAssignments.find(a => a.sourceId === methodCallId);
|
|
436
|
-
if (!existing) {
|
|
437
|
-
const extractedArgs = [];
|
|
438
|
-
initExpression.arguments.forEach((arg, index) => {
|
|
439
|
-
if (arg.type !== 'SpreadElement') {
|
|
440
|
-
const argLiteralValue = ExpressionEvaluator.extractLiteralValue(arg);
|
|
441
|
-
if (argLiteralValue !== null) {
|
|
442
|
-
const literalId = `LITERAL#arg${index}#${module.file}#${getLine(initExpression)}:${getColumn(initExpression)}:${literalCounterRef.value++}`;
|
|
443
|
-
literals.push({
|
|
444
|
-
id: literalId,
|
|
445
|
-
type: 'LITERAL',
|
|
446
|
-
value: argLiteralValue,
|
|
447
|
-
valueType: typeof argLiteralValue,
|
|
448
|
-
file: module.file,
|
|
449
|
-
line: arg.loc?.start.line || getLine(initExpression),
|
|
450
|
-
column: arg.loc?.start.column || getColumn(initExpression),
|
|
451
|
-
parentCallId: methodCallId,
|
|
452
|
-
argIndex: index
|
|
453
|
-
});
|
|
454
|
-
extractedArgs.push(argLiteralValue);
|
|
455
|
-
}
|
|
456
|
-
else {
|
|
457
|
-
extractedArgs.push(undefined);
|
|
458
|
-
}
|
|
459
|
-
}
|
|
460
|
-
});
|
|
461
|
-
literals.push({
|
|
462
|
-
id: methodCallId,
|
|
463
|
-
type: 'CALL',
|
|
464
|
-
name: fullName,
|
|
465
|
-
object: objectName,
|
|
466
|
-
method: methodName,
|
|
467
|
-
file: module.file,
|
|
468
|
-
arguments: extractedArgs,
|
|
469
|
-
line: getLine(initExpression),
|
|
470
|
-
column: getColumn(initExpression)
|
|
471
|
-
});
|
|
472
|
-
}
|
|
473
378
|
variableAssignments.push({
|
|
474
379
|
variableId,
|
|
475
|
-
|
|
476
|
-
|
|
380
|
+
sourceType: 'METHOD_CALL',
|
|
381
|
+
sourceLine: getLine(initExpression),
|
|
382
|
+
sourceColumn: getColumn(initExpression),
|
|
383
|
+
sourceFile: module.file,
|
|
384
|
+
line: line
|
|
477
385
|
});
|
|
478
386
|
return;
|
|
479
387
|
}
|
|
@@ -1002,17 +910,19 @@ export class JSASTAnalyzer extends Plugin {
|
|
|
1002
910
|
let edgesCreated = 0;
|
|
1003
911
|
try {
|
|
1004
912
|
this.profiler.start('file_read');
|
|
1005
|
-
const code = readFileSync(module.file, 'utf-8');
|
|
913
|
+
const code = readFileSync(resolveNodeFile(module.file, projectPath), 'utf-8');
|
|
1006
914
|
this.profiler.end('file_read');
|
|
1007
915
|
this.profiler.start('babel_parse');
|
|
1008
916
|
const ast = parse(code, {
|
|
1009
917
|
sourceType: 'module',
|
|
1010
|
-
plugins: ['jsx', 'typescript']
|
|
918
|
+
plugins: ['jsx', 'typescript', 'decorators-legacy']
|
|
1011
919
|
});
|
|
1012
920
|
this.profiler.end('babel_parse');
|
|
1013
921
|
// Create ScopeTracker for semantic ID generation
|
|
1014
922
|
// Use basename for shorter, more readable semantic IDs
|
|
1015
923
|
const scopeTracker = new ScopeTracker(basename(module.file));
|
|
924
|
+
// REG-464: Shared IdGenerator for v2 collision resolution across visitors
|
|
925
|
+
const sharedIdGenerator = new IdGenerator(scopeTracker);
|
|
1016
926
|
const functions = [];
|
|
1017
927
|
const parameters = [];
|
|
1018
928
|
const scopes = [];
|
|
@@ -1040,6 +950,8 @@ export class JSASTAnalyzer extends Plugin {
|
|
|
1040
950
|
const typeAliases = [];
|
|
1041
951
|
const enums = [];
|
|
1042
952
|
const decorators = [];
|
|
953
|
+
// Type parameter tracking for generics (REG-303)
|
|
954
|
+
const typeParameters = [];
|
|
1043
955
|
// Object/Array literal tracking for data flow
|
|
1044
956
|
const objectLiterals = [];
|
|
1045
957
|
const objectProperties = [];
|
|
@@ -1113,6 +1025,8 @@ export class JSASTAnalyzer extends Plugin {
|
|
|
1113
1025
|
httpRequests, literals, variableAssignments,
|
|
1114
1026
|
// TypeScript-specific collections
|
|
1115
1027
|
interfaces, typeAliases, enums, decorators,
|
|
1028
|
+
// Type parameter tracking for generics (REG-303)
|
|
1029
|
+
typeParameters,
|
|
1116
1030
|
// Object/Array literal tracking
|
|
1117
1031
|
objectLiterals, objectProperties, arrayLiterals, arrayElements,
|
|
1118
1032
|
// Array mutation tracking
|
|
@@ -1301,9 +1215,28 @@ export class JSASTAnalyzer extends Plugin {
|
|
|
1301
1215
|
this.profiler.end('traverse_callbacks');
|
|
1302
1216
|
// Call expressions
|
|
1303
1217
|
this.profiler.start('traverse_calls');
|
|
1304
|
-
const callExpressionVisitor = new CallExpressionVisitor(module, allCollections, scopeTracker);
|
|
1218
|
+
const callExpressionVisitor = new CallExpressionVisitor(module, allCollections, scopeTracker, sharedIdGenerator);
|
|
1305
1219
|
traverse(ast, callExpressionVisitor.getHandlers());
|
|
1306
1220
|
this.profiler.end('traverse_calls');
|
|
1221
|
+
// REG-297: Detect top-level await expressions
|
|
1222
|
+
this.profiler.start('traverse_top_level_await');
|
|
1223
|
+
let hasTopLevelAwait = false;
|
|
1224
|
+
traverse(ast, {
|
|
1225
|
+
AwaitExpression(awaitPath) {
|
|
1226
|
+
if (!awaitPath.getFunctionParent()) {
|
|
1227
|
+
hasTopLevelAwait = true;
|
|
1228
|
+
awaitPath.stop();
|
|
1229
|
+
}
|
|
1230
|
+
},
|
|
1231
|
+
// for-await-of uses ForOfStatement.await, not AwaitExpression
|
|
1232
|
+
ForOfStatement(forOfPath) {
|
|
1233
|
+
if (forOfPath.node.await && !forOfPath.getFunctionParent()) {
|
|
1234
|
+
hasTopLevelAwait = true;
|
|
1235
|
+
forOfPath.stop();
|
|
1236
|
+
}
|
|
1237
|
+
}
|
|
1238
|
+
});
|
|
1239
|
+
this.profiler.end('traverse_top_level_await');
|
|
1307
1240
|
// Property access expressions (REG-395)
|
|
1308
1241
|
this.profiler.start('traverse_property_access');
|
|
1309
1242
|
const propertyAccessVisitor = new PropertyAccessVisitor(module, allCollections, scopeTracker);
|
|
@@ -1424,6 +1357,36 @@ export class JSASTAnalyzer extends Plugin {
|
|
|
1424
1357
|
}
|
|
1425
1358
|
});
|
|
1426
1359
|
this.profiler.end('traverse_ifs');
|
|
1360
|
+
// REG-464: Resolve v2 ID collisions after all visitors complete
|
|
1361
|
+
const pendingNodes = sharedIdGenerator.getPendingNodes();
|
|
1362
|
+
if (pendingNodes.length > 0) {
|
|
1363
|
+
// Capture pre-resolution IDs to update callArguments afterward
|
|
1364
|
+
const preResolutionIds = new Map();
|
|
1365
|
+
for (const pn of pendingNodes) {
|
|
1366
|
+
preResolutionIds.set(pn.collectionRef, pn.collectionRef.id);
|
|
1367
|
+
}
|
|
1368
|
+
const collisionResolver = new CollisionResolver();
|
|
1369
|
+
collisionResolver.resolve(pendingNodes);
|
|
1370
|
+
// Update callArgument.callId references that became stale after resolution
|
|
1371
|
+
const idRemapping = new Map();
|
|
1372
|
+
for (const pn of pendingNodes) {
|
|
1373
|
+
const oldId = preResolutionIds.get(pn.collectionRef);
|
|
1374
|
+
if (oldId !== pn.collectionRef.id) {
|
|
1375
|
+
idRemapping.set(oldId, pn.collectionRef.id);
|
|
1376
|
+
}
|
|
1377
|
+
}
|
|
1378
|
+
if (idRemapping.size > 0) {
|
|
1379
|
+
const callArgs = allCollections.callArguments;
|
|
1380
|
+
if (callArgs) {
|
|
1381
|
+
for (const arg of callArgs) {
|
|
1382
|
+
const resolved = idRemapping.get(arg.callId);
|
|
1383
|
+
if (resolved) {
|
|
1384
|
+
arg.callId = resolved;
|
|
1385
|
+
}
|
|
1386
|
+
}
|
|
1387
|
+
}
|
|
1388
|
+
}
|
|
1389
|
+
}
|
|
1427
1390
|
// Build graph
|
|
1428
1391
|
this.profiler.start('graph_build');
|
|
1429
1392
|
const result = await this.graphBuilder.build(module, graph, projectPath, {
|
|
@@ -1459,6 +1422,8 @@ export class JSASTAnalyzer extends Plugin {
|
|
|
1459
1422
|
typeAliases,
|
|
1460
1423
|
enums,
|
|
1461
1424
|
decorators,
|
|
1425
|
+
// Type parameter tracking for generics (REG-303)
|
|
1426
|
+
typeParameters,
|
|
1462
1427
|
// Array mutation tracking
|
|
1463
1428
|
arrayMutations,
|
|
1464
1429
|
// Object mutation tracking
|
|
@@ -1485,7 +1450,9 @@ export class JSASTAnalyzer extends Plugin {
|
|
|
1485
1450
|
? allCollections.catchesFromInfos
|
|
1486
1451
|
: catchesFromInfos,
|
|
1487
1452
|
// Property access tracking (REG-395)
|
|
1488
|
-
propertyAccesses: allCollections.propertyAccesses || propertyAccesses
|
|
1453
|
+
propertyAccesses: allCollections.propertyAccesses || propertyAccesses,
|
|
1454
|
+
// REG-297: Top-level await tracking
|
|
1455
|
+
hasTopLevelAwait
|
|
1489
1456
|
});
|
|
1490
1457
|
this.profiler.end('graph_build');
|
|
1491
1458
|
nodesCreated = result.nodes;
|
|
@@ -1663,423 +1630,6 @@ export class JSASTAnalyzer extends Plugin {
|
|
|
1663
1630
|
}
|
|
1664
1631
|
});
|
|
1665
1632
|
}
|
|
1666
|
-
createLoopScopeHandler(trackerScopeType, scopeType, loopType, parentScopeId, module, scopes, loops, scopeCounterRef, loopCounterRef, scopeTracker, scopeIdStack, controlFlowState) {
|
|
1667
|
-
return {
|
|
1668
|
-
enter: (path) => {
|
|
1669
|
-
const node = path.node;
|
|
1670
|
-
// Phase 6 (REG-267): Increment loop count for cyclomatic complexity
|
|
1671
|
-
if (controlFlowState) {
|
|
1672
|
-
controlFlowState.loopCount++;
|
|
1673
|
-
}
|
|
1674
|
-
// 1. Create LOOP node
|
|
1675
|
-
const loopCounter = loopCounterRef.value++;
|
|
1676
|
-
const legacyLoopId = `${module.file}:LOOP:${loopType}:${getLine(node)}:${loopCounter}`;
|
|
1677
|
-
const loopId = scopeTracker
|
|
1678
|
-
? computeSemanticId('LOOP', loopType, scopeTracker.getContext(), { discriminator: loopCounter })
|
|
1679
|
-
: legacyLoopId;
|
|
1680
|
-
// 2. Extract iteration target for for-in/for-of
|
|
1681
|
-
let iteratesOverName;
|
|
1682
|
-
let iteratesOverLine;
|
|
1683
|
-
let iteratesOverColumn;
|
|
1684
|
-
if (loopType === 'for-in' || loopType === 'for-of') {
|
|
1685
|
-
const loopNode = node;
|
|
1686
|
-
if (t.isIdentifier(loopNode.right)) {
|
|
1687
|
-
iteratesOverName = loopNode.right.name;
|
|
1688
|
-
iteratesOverLine = getLine(loopNode.right);
|
|
1689
|
-
iteratesOverColumn = getColumn(loopNode.right);
|
|
1690
|
-
}
|
|
1691
|
-
else if (t.isMemberExpression(loopNode.right)) {
|
|
1692
|
-
iteratesOverName = this.memberExpressionToString(loopNode.right);
|
|
1693
|
-
iteratesOverLine = getLine(loopNode.right);
|
|
1694
|
-
iteratesOverColumn = getColumn(loopNode.right);
|
|
1695
|
-
}
|
|
1696
|
-
}
|
|
1697
|
-
// 2b. Extract init/test/update for classic for loops and test for while/do-while (REG-282)
|
|
1698
|
-
let initVariableName;
|
|
1699
|
-
let initLine;
|
|
1700
|
-
let testExpressionId;
|
|
1701
|
-
let testExpressionType;
|
|
1702
|
-
let testLine;
|
|
1703
|
-
let testColumn;
|
|
1704
|
-
let updateExpressionId;
|
|
1705
|
-
let updateExpressionType;
|
|
1706
|
-
let updateLine;
|
|
1707
|
-
let updateColumn;
|
|
1708
|
-
if (loopType === 'for') {
|
|
1709
|
-
const forNode = node;
|
|
1710
|
-
// Extract init: let i = 0
|
|
1711
|
-
if (forNode.init) {
|
|
1712
|
-
initLine = getLine(forNode.init);
|
|
1713
|
-
if (t.isVariableDeclaration(forNode.init)) {
|
|
1714
|
-
// Get name of first declared variable
|
|
1715
|
-
const firstDeclarator = forNode.init.declarations[0];
|
|
1716
|
-
if (t.isIdentifier(firstDeclarator.id)) {
|
|
1717
|
-
initVariableName = firstDeclarator.id.name;
|
|
1718
|
-
}
|
|
1719
|
-
}
|
|
1720
|
-
}
|
|
1721
|
-
// Extract test: i < 10
|
|
1722
|
-
if (forNode.test) {
|
|
1723
|
-
testLine = getLine(forNode.test);
|
|
1724
|
-
testColumn = getColumn(forNode.test);
|
|
1725
|
-
testExpressionType = forNode.test.type;
|
|
1726
|
-
testExpressionId = ExpressionNode.generateId(forNode.test.type, module.file, testLine, testColumn);
|
|
1727
|
-
}
|
|
1728
|
-
// Extract update: i++
|
|
1729
|
-
if (forNode.update) {
|
|
1730
|
-
updateLine = getLine(forNode.update);
|
|
1731
|
-
updateColumn = getColumn(forNode.update);
|
|
1732
|
-
updateExpressionType = forNode.update.type;
|
|
1733
|
-
updateExpressionId = ExpressionNode.generateId(forNode.update.type, module.file, updateLine, updateColumn);
|
|
1734
|
-
}
|
|
1735
|
-
}
|
|
1736
|
-
// Extract test condition for while and do-while loops
|
|
1737
|
-
if (loopType === 'while' || loopType === 'do-while') {
|
|
1738
|
-
const condLoop = node;
|
|
1739
|
-
if (condLoop.test) {
|
|
1740
|
-
testLine = getLine(condLoop.test);
|
|
1741
|
-
testColumn = getColumn(condLoop.test);
|
|
1742
|
-
testExpressionType = condLoop.test.type;
|
|
1743
|
-
testExpressionId = ExpressionNode.generateId(condLoop.test.type, module.file, testLine, testColumn);
|
|
1744
|
-
}
|
|
1745
|
-
}
|
|
1746
|
-
// Extract async flag for for-await-of (REG-284)
|
|
1747
|
-
let isAsync;
|
|
1748
|
-
if (loopType === 'for-of') {
|
|
1749
|
-
const forOfNode = node;
|
|
1750
|
-
isAsync = forOfNode.await === true ? true : undefined;
|
|
1751
|
-
}
|
|
1752
|
-
// 3. Determine actual parent - use stack for nested loops, otherwise original parentScopeId
|
|
1753
|
-
const actualParentScopeId = (scopeIdStack && scopeIdStack.length > 0)
|
|
1754
|
-
? scopeIdStack[scopeIdStack.length - 1]
|
|
1755
|
-
: parentScopeId;
|
|
1756
|
-
// 3.5. Extract condition expression for while/do-while/for loops (REG-280)
|
|
1757
|
-
// Note: for-in and for-of don't have test expressions (they use ITERATES_OVER instead)
|
|
1758
|
-
let conditionExpressionId;
|
|
1759
|
-
let conditionExpressionType;
|
|
1760
|
-
let conditionLine;
|
|
1761
|
-
let conditionColumn;
|
|
1762
|
-
if (loopType === 'while' || loopType === 'do-while') {
|
|
1763
|
-
const testNode = node.test;
|
|
1764
|
-
if (testNode) {
|
|
1765
|
-
const condResult = this.extractDiscriminantExpression(testNode, module);
|
|
1766
|
-
conditionExpressionId = condResult.id;
|
|
1767
|
-
conditionExpressionType = condResult.expressionType;
|
|
1768
|
-
conditionLine = condResult.line;
|
|
1769
|
-
conditionColumn = condResult.column;
|
|
1770
|
-
}
|
|
1771
|
-
}
|
|
1772
|
-
else if (loopType === 'for') {
|
|
1773
|
-
const forNode = node;
|
|
1774
|
-
// for loop test may be null (infinite loop: for(;;))
|
|
1775
|
-
if (forNode.test) {
|
|
1776
|
-
const condResult = this.extractDiscriminantExpression(forNode.test, module);
|
|
1777
|
-
conditionExpressionId = condResult.id;
|
|
1778
|
-
conditionExpressionType = condResult.expressionType;
|
|
1779
|
-
conditionLine = condResult.line;
|
|
1780
|
-
conditionColumn = condResult.column;
|
|
1781
|
-
}
|
|
1782
|
-
}
|
|
1783
|
-
// 4. Push LOOP info
|
|
1784
|
-
loops.push({
|
|
1785
|
-
id: loopId,
|
|
1786
|
-
semanticId: loopId,
|
|
1787
|
-
type: 'LOOP',
|
|
1788
|
-
loopType,
|
|
1789
|
-
file: module.file,
|
|
1790
|
-
line: getLine(node),
|
|
1791
|
-
column: getColumn(node),
|
|
1792
|
-
parentScopeId: actualParentScopeId,
|
|
1793
|
-
iteratesOverName,
|
|
1794
|
-
iteratesOverLine,
|
|
1795
|
-
iteratesOverColumn,
|
|
1796
|
-
conditionExpressionId,
|
|
1797
|
-
conditionExpressionType,
|
|
1798
|
-
conditionLine,
|
|
1799
|
-
conditionColumn,
|
|
1800
|
-
// REG-282: init/test/update for classic for loops
|
|
1801
|
-
initVariableName,
|
|
1802
|
-
initLine,
|
|
1803
|
-
testExpressionId,
|
|
1804
|
-
testExpressionType,
|
|
1805
|
-
testLine,
|
|
1806
|
-
testColumn,
|
|
1807
|
-
updateExpressionId,
|
|
1808
|
-
updateExpressionType,
|
|
1809
|
-
updateLine,
|
|
1810
|
-
updateColumn,
|
|
1811
|
-
// REG-284: async flag for for-await-of
|
|
1812
|
-
async: isAsync
|
|
1813
|
-
});
|
|
1814
|
-
// 5. Create body SCOPE (backward compatibility)
|
|
1815
|
-
const scopeId = `SCOPE#${scopeType}#${module.file}#${getLine(node)}:${scopeCounterRef.value++}`;
|
|
1816
|
-
const semanticId = this.generateSemanticId(scopeType, scopeTracker);
|
|
1817
|
-
scopes.push({
|
|
1818
|
-
id: scopeId,
|
|
1819
|
-
type: 'SCOPE',
|
|
1820
|
-
scopeType,
|
|
1821
|
-
semanticId,
|
|
1822
|
-
file: module.file,
|
|
1823
|
-
line: getLine(node),
|
|
1824
|
-
parentScopeId: loopId // Parent is LOOP, not original parentScopeId
|
|
1825
|
-
});
|
|
1826
|
-
// 6. Push body SCOPE to scopeIdStack (for CONTAINS edges to nested items)
|
|
1827
|
-
// The body scope is the container for nested loops, not the LOOP itself
|
|
1828
|
-
if (scopeIdStack) {
|
|
1829
|
-
scopeIdStack.push(scopeId);
|
|
1830
|
-
}
|
|
1831
|
-
// Enter scope for semantic ID generation
|
|
1832
|
-
if (scopeTracker) {
|
|
1833
|
-
scopeTracker.enterCountedScope(trackerScopeType);
|
|
1834
|
-
}
|
|
1835
|
-
},
|
|
1836
|
-
exit: () => {
|
|
1837
|
-
// Pop loop scope from stack
|
|
1838
|
-
if (scopeIdStack) {
|
|
1839
|
-
scopeIdStack.pop();
|
|
1840
|
-
}
|
|
1841
|
-
// Exit scope
|
|
1842
|
-
if (scopeTracker) {
|
|
1843
|
-
scopeTracker.exitScope();
|
|
1844
|
-
}
|
|
1845
|
-
}
|
|
1846
|
-
};
|
|
1847
|
-
}
|
|
1848
|
-
/**
|
|
1849
|
-
* Factory method to create TryStatement handler.
|
|
1850
|
-
* Creates TRY_BLOCK, CATCH_BLOCK, FINALLY_BLOCK nodes and body SCOPEs.
|
|
1851
|
-
* Does NOT use skip() - allows normal traversal for CallExpression/NewExpression visitors.
|
|
1852
|
-
*
|
|
1853
|
-
* Phase 4 (REG-267): Creates control flow nodes with HAS_CATCH and HAS_FINALLY edges.
|
|
1854
|
-
*
|
|
1855
|
-
* @param parentScopeId - Parent scope ID for the scope nodes
|
|
1856
|
-
* @param module - Module context
|
|
1857
|
-
* @param scopes - Collection to push scope nodes to
|
|
1858
|
-
* @param tryBlocks - Collection to push TRY_BLOCK nodes to
|
|
1859
|
-
* @param catchBlocks - Collection to push CATCH_BLOCK nodes to
|
|
1860
|
-
* @param finallyBlocks - Collection to push FINALLY_BLOCK nodes to
|
|
1861
|
-
* @param scopeCounterRef - Counter for unique scope IDs
|
|
1862
|
-
* @param tryBlockCounterRef - Counter for unique TRY_BLOCK IDs
|
|
1863
|
-
* @param catchBlockCounterRef - Counter for unique CATCH_BLOCK IDs
|
|
1864
|
-
* @param finallyBlockCounterRef - Counter for unique FINALLY_BLOCK IDs
|
|
1865
|
-
* @param scopeTracker - Tracker for semantic ID generation
|
|
1866
|
-
* @param tryScopeMap - Map to track try/catch/finally scope transitions
|
|
1867
|
-
* @param scopeIdStack - Stack for tracking current scope ID for CONTAINS edges
|
|
1868
|
-
*/
|
|
1869
|
-
createTryStatementHandler(parentScopeId, module, scopes, tryBlocks, catchBlocks, finallyBlocks, scopeCounterRef, tryBlockCounterRef, catchBlockCounterRef, finallyBlockCounterRef, scopeTracker, tryScopeMap, scopeIdStack, controlFlowState) {
|
|
1870
|
-
return {
|
|
1871
|
-
enter: (tryPath) => {
|
|
1872
|
-
const tryNode = tryPath.node;
|
|
1873
|
-
// Phase 6 (REG-267): Mark that this function has try/catch
|
|
1874
|
-
if (controlFlowState) {
|
|
1875
|
-
controlFlowState.hasTryCatch = true;
|
|
1876
|
-
// REG-311: Increment try block depth for O(1) isInsideTry detection
|
|
1877
|
-
controlFlowState.tryBlockDepth++;
|
|
1878
|
-
}
|
|
1879
|
-
// Determine actual parent - use stack for nested structures, otherwise original parentScopeId
|
|
1880
|
-
const actualParentScopeId = (scopeIdStack && scopeIdStack.length > 0)
|
|
1881
|
-
? scopeIdStack[scopeIdStack.length - 1]
|
|
1882
|
-
: parentScopeId;
|
|
1883
|
-
// 1. Create TRY_BLOCK node
|
|
1884
|
-
const tryBlockCounter = tryBlockCounterRef.value++;
|
|
1885
|
-
const legacyTryBlockId = `${module.file}:TRY_BLOCK:${getLine(tryNode)}:${tryBlockCounter}`;
|
|
1886
|
-
const tryBlockId = scopeTracker
|
|
1887
|
-
? computeSemanticId('TRY_BLOCK', 'try', scopeTracker.getContext(), { discriminator: tryBlockCounter })
|
|
1888
|
-
: legacyTryBlockId;
|
|
1889
|
-
tryBlocks.push({
|
|
1890
|
-
id: tryBlockId,
|
|
1891
|
-
semanticId: tryBlockId,
|
|
1892
|
-
type: 'TRY_BLOCK',
|
|
1893
|
-
file: module.file,
|
|
1894
|
-
line: getLine(tryNode),
|
|
1895
|
-
column: getColumn(tryNode),
|
|
1896
|
-
parentScopeId: actualParentScopeId
|
|
1897
|
-
});
|
|
1898
|
-
// 2. Create try-body SCOPE (backward compatibility)
|
|
1899
|
-
// Parent is now TRY_BLOCK, not original parentScopeId
|
|
1900
|
-
const tryScopeId = `SCOPE#try-block#${module.file}#${getLine(tryNode)}:${scopeCounterRef.value++}`;
|
|
1901
|
-
const trySemanticId = this.generateSemanticId('try-block', scopeTracker);
|
|
1902
|
-
scopes.push({
|
|
1903
|
-
id: tryScopeId,
|
|
1904
|
-
type: 'SCOPE',
|
|
1905
|
-
scopeType: 'try-block',
|
|
1906
|
-
semanticId: trySemanticId,
|
|
1907
|
-
file: module.file,
|
|
1908
|
-
line: getLine(tryNode),
|
|
1909
|
-
parentScopeId: tryBlockId // Parent is TRY_BLOCK
|
|
1910
|
-
});
|
|
1911
|
-
// 3. Create CATCH_BLOCK and catch-body SCOPE if handler exists
|
|
1912
|
-
let catchBlockId = null;
|
|
1913
|
-
let catchScopeId = null;
|
|
1914
|
-
if (tryNode.handler) {
|
|
1915
|
-
const catchClause = tryNode.handler;
|
|
1916
|
-
const catchBlockCounter = catchBlockCounterRef.value++;
|
|
1917
|
-
const legacyCatchBlockId = `${module.file}:CATCH_BLOCK:${getLine(catchClause)}:${catchBlockCounter}`;
|
|
1918
|
-
catchBlockId = scopeTracker
|
|
1919
|
-
? computeSemanticId('CATCH_BLOCK', 'catch', scopeTracker.getContext(), { discriminator: catchBlockCounter })
|
|
1920
|
-
: legacyCatchBlockId;
|
|
1921
|
-
// Extract parameter name if present
|
|
1922
|
-
let parameterName;
|
|
1923
|
-
if (catchClause.param && t.isIdentifier(catchClause.param)) {
|
|
1924
|
-
parameterName = catchClause.param.name;
|
|
1925
|
-
}
|
|
1926
|
-
catchBlocks.push({
|
|
1927
|
-
id: catchBlockId,
|
|
1928
|
-
semanticId: catchBlockId,
|
|
1929
|
-
type: 'CATCH_BLOCK',
|
|
1930
|
-
file: module.file,
|
|
1931
|
-
line: getLine(catchClause),
|
|
1932
|
-
column: getColumn(catchClause),
|
|
1933
|
-
parentScopeId,
|
|
1934
|
-
parentTryBlockId: tryBlockId,
|
|
1935
|
-
parameterName
|
|
1936
|
-
});
|
|
1937
|
-
// Create catch-body SCOPE (backward compatibility)
|
|
1938
|
-
catchScopeId = `SCOPE#catch-block#${module.file}#${getLine(catchClause)}:${scopeCounterRef.value++}`;
|
|
1939
|
-
const catchSemanticId = this.generateSemanticId('catch-block', scopeTracker);
|
|
1940
|
-
scopes.push({
|
|
1941
|
-
id: catchScopeId,
|
|
1942
|
-
type: 'SCOPE',
|
|
1943
|
-
scopeType: 'catch-block',
|
|
1944
|
-
semanticId: catchSemanticId,
|
|
1945
|
-
file: module.file,
|
|
1946
|
-
line: getLine(catchClause),
|
|
1947
|
-
parentScopeId: catchBlockId // Parent is CATCH_BLOCK
|
|
1948
|
-
});
|
|
1949
|
-
}
|
|
1950
|
-
// 4. Create FINALLY_BLOCK and finally-body SCOPE if finalizer exists
|
|
1951
|
-
let finallyBlockId = null;
|
|
1952
|
-
let finallyScopeId = null;
|
|
1953
|
-
if (tryNode.finalizer) {
|
|
1954
|
-
const finallyBlockCounter = finallyBlockCounterRef.value++;
|
|
1955
|
-
const legacyFinallyBlockId = `${module.file}:FINALLY_BLOCK:${getLine(tryNode.finalizer)}:${finallyBlockCounter}`;
|
|
1956
|
-
finallyBlockId = scopeTracker
|
|
1957
|
-
? computeSemanticId('FINALLY_BLOCK', 'finally', scopeTracker.getContext(), { discriminator: finallyBlockCounter })
|
|
1958
|
-
: legacyFinallyBlockId;
|
|
1959
|
-
finallyBlocks.push({
|
|
1960
|
-
id: finallyBlockId,
|
|
1961
|
-
semanticId: finallyBlockId,
|
|
1962
|
-
type: 'FINALLY_BLOCK',
|
|
1963
|
-
file: module.file,
|
|
1964
|
-
line: getLine(tryNode.finalizer),
|
|
1965
|
-
column: getColumn(tryNode.finalizer),
|
|
1966
|
-
parentScopeId,
|
|
1967
|
-
parentTryBlockId: tryBlockId
|
|
1968
|
-
});
|
|
1969
|
-
// Create finally-body SCOPE (backward compatibility)
|
|
1970
|
-
finallyScopeId = `SCOPE#finally-block#${module.file}#${getLine(tryNode.finalizer)}:${scopeCounterRef.value++}`;
|
|
1971
|
-
const finallySemanticId = this.generateSemanticId('finally-block', scopeTracker);
|
|
1972
|
-
scopes.push({
|
|
1973
|
-
id: finallyScopeId,
|
|
1974
|
-
type: 'SCOPE',
|
|
1975
|
-
scopeType: 'finally-block',
|
|
1976
|
-
semanticId: finallySemanticId,
|
|
1977
|
-
file: module.file,
|
|
1978
|
-
line: getLine(tryNode.finalizer),
|
|
1979
|
-
parentScopeId: finallyBlockId // Parent is FINALLY_BLOCK
|
|
1980
|
-
});
|
|
1981
|
-
}
|
|
1982
|
-
// 5. Push try scope onto stack for CONTAINS edges
|
|
1983
|
-
if (scopeIdStack) {
|
|
1984
|
-
scopeIdStack.push(tryScopeId);
|
|
1985
|
-
}
|
|
1986
|
-
// Enter try scope for semantic ID generation
|
|
1987
|
-
if (scopeTracker) {
|
|
1988
|
-
scopeTracker.enterCountedScope('try');
|
|
1989
|
-
}
|
|
1990
|
-
// 6. Store scope info for catch/finally transitions
|
|
1991
|
-
tryScopeMap.set(tryNode, {
|
|
1992
|
-
tryScopeId,
|
|
1993
|
-
catchScopeId,
|
|
1994
|
-
finallyScopeId,
|
|
1995
|
-
currentBlock: 'try',
|
|
1996
|
-
tryBlockId,
|
|
1997
|
-
catchBlockId,
|
|
1998
|
-
finallyBlockId
|
|
1999
|
-
});
|
|
2000
|
-
},
|
|
2001
|
-
exit: (tryPath) => {
|
|
2002
|
-
const tryNode = tryPath.node;
|
|
2003
|
-
const _scopeInfo = tryScopeMap.get(tryNode);
|
|
2004
|
-
// REG-311: Only decrement try block depth if we're still in 'try' block
|
|
2005
|
-
// (not transitioned to catch/finally, where we already decremented)
|
|
2006
|
-
if (controlFlowState && _scopeInfo?.currentBlock === 'try') {
|
|
2007
|
-
controlFlowState.tryBlockDepth--;
|
|
2008
|
-
}
|
|
2009
|
-
// Pop the current scope from stack (could be try, catch, or finally)
|
|
2010
|
-
if (scopeIdStack) {
|
|
2011
|
-
scopeIdStack.pop();
|
|
2012
|
-
}
|
|
2013
|
-
// Exit the current scope
|
|
2014
|
-
if (scopeTracker) {
|
|
2015
|
-
scopeTracker.exitScope();
|
|
2016
|
-
}
|
|
2017
|
-
// Clean up
|
|
2018
|
-
tryScopeMap.delete(tryNode);
|
|
2019
|
-
}
|
|
2020
|
-
};
|
|
2021
|
-
}
|
|
2022
|
-
/**
|
|
2023
|
-
* Factory method to create CatchClause handler.
|
|
2024
|
-
* Handles scope transition from try to catch and processes catch parameter.
|
|
2025
|
-
*
|
|
2026
|
-
* @param module - Module context
|
|
2027
|
-
* @param variableDeclarations - Collection to push variable declarations to
|
|
2028
|
-
* @param varDeclCounterRef - Counter for unique variable declaration IDs
|
|
2029
|
-
* @param scopeTracker - Tracker for semantic ID generation
|
|
2030
|
-
* @param tryScopeMap - Map to track try/catch/finally scope transitions
|
|
2031
|
-
* @param scopeIdStack - Stack for tracking current scope ID for CONTAINS edges
|
|
2032
|
-
*/
|
|
2033
|
-
createCatchClauseHandler(module, variableDeclarations, varDeclCounterRef, scopeTracker, tryScopeMap, scopeIdStack, controlFlowState) {
|
|
2034
|
-
return {
|
|
2035
|
-
enter: (catchPath) => {
|
|
2036
|
-
const catchNode = catchPath.node;
|
|
2037
|
-
const parent = catchPath.parent;
|
|
2038
|
-
if (!t.isTryStatement(parent))
|
|
2039
|
-
return;
|
|
2040
|
-
const scopeInfo = tryScopeMap.get(parent);
|
|
2041
|
-
if (!scopeInfo || !scopeInfo.catchScopeId)
|
|
2042
|
-
return;
|
|
2043
|
-
// Transition from try scope to catch scope
|
|
2044
|
-
if (scopeInfo.currentBlock === 'try') {
|
|
2045
|
-
// Pop try scope, push catch scope
|
|
2046
|
-
if (scopeIdStack) {
|
|
2047
|
-
scopeIdStack.pop();
|
|
2048
|
-
scopeIdStack.push(scopeInfo.catchScopeId);
|
|
2049
|
-
}
|
|
2050
|
-
// Exit try scope, enter catch scope for semantic ID
|
|
2051
|
-
if (scopeTracker) {
|
|
2052
|
-
scopeTracker.exitScope();
|
|
2053
|
-
scopeTracker.enterCountedScope('catch');
|
|
2054
|
-
}
|
|
2055
|
-
// REG-311: Decrement tryBlockDepth when leaving try block for catch
|
|
2056
|
-
// Calls in catch block should NOT have isInsideTry=true
|
|
2057
|
-
if (controlFlowState) {
|
|
2058
|
-
controlFlowState.tryBlockDepth--;
|
|
2059
|
-
}
|
|
2060
|
-
scopeInfo.currentBlock = 'catch';
|
|
2061
|
-
}
|
|
2062
|
-
// Handle catch parameter (e.g., catch (e) or catch ({ message }))
|
|
2063
|
-
if (catchNode.param) {
|
|
2064
|
-
const errorVarInfo = this.extractVariableNamesFromPattern(catchNode.param);
|
|
2065
|
-
errorVarInfo.forEach(varInfo => {
|
|
2066
|
-
const legacyId = `VARIABLE#${varInfo.name}#${module.file}#${varInfo.loc.start.line}:${varInfo.loc.start.column}:${varDeclCounterRef.value++}`;
|
|
2067
|
-
const varId = scopeTracker
|
|
2068
|
-
? computeSemanticId('VARIABLE', varInfo.name, scopeTracker.getContext())
|
|
2069
|
-
: legacyId;
|
|
2070
|
-
variableDeclarations.push({
|
|
2071
|
-
id: varId,
|
|
2072
|
-
type: 'VARIABLE',
|
|
2073
|
-
name: varInfo.name,
|
|
2074
|
-
file: module.file,
|
|
2075
|
-
line: varInfo.loc.start.line,
|
|
2076
|
-
parentScopeId: scopeInfo.catchScopeId
|
|
2077
|
-
});
|
|
2078
|
-
});
|
|
2079
|
-
}
|
|
2080
|
-
}
|
|
2081
|
-
};
|
|
2082
|
-
}
|
|
2083
1633
|
/**
|
|
2084
1634
|
* Handles SwitchStatement nodes.
|
|
2085
1635
|
* Creates BRANCH node for switch, CASE nodes for each case clause,
|
|
@@ -2533,1331 +2083,134 @@ export class JSASTAnalyzer extends Plugin {
|
|
|
2533
2083
|
};
|
|
2534
2084
|
}
|
|
2535
2085
|
/**
|
|
2536
|
-
*
|
|
2537
|
-
*
|
|
2538
|
-
* Tracks if/else scope transitions via ifElseScopeMap.
|
|
2539
|
-
*
|
|
2540
|
-
* Phase 3 (REG-267): Creates BRANCH node with branchType='if' and
|
|
2541
|
-
* HAS_CONSEQUENT/HAS_ALTERNATE edges to body SCOPEs.
|
|
2542
|
-
*
|
|
2543
|
-
* @param parentScopeId - Parent scope ID for the scope nodes
|
|
2544
|
-
* @param module - Module context
|
|
2545
|
-
* @param scopes - Collection to push scope nodes to
|
|
2546
|
-
* @param branches - Collection to push BRANCH nodes to
|
|
2547
|
-
* @param ifScopeCounterRef - Counter for unique if scope IDs
|
|
2548
|
-
* @param branchCounterRef - Counter for unique BRANCH IDs
|
|
2549
|
-
* @param scopeTracker - Tracker for semantic ID generation
|
|
2550
|
-
* @param sourceCode - Source code for extracting condition text
|
|
2551
|
-
* @param ifElseScopeMap - Map to track if/else scope transitions
|
|
2552
|
-
* @param scopeIdStack - Stack for tracking current scope ID for CONTAINS edges
|
|
2553
|
-
*/
|
|
2554
|
-
createIfStatementHandler(parentScopeId, module, scopes, branches, ifScopeCounterRef, branchCounterRef, scopeTracker, sourceCode, ifElseScopeMap, scopeIdStack, controlFlowState, countLogicalOperators) {
|
|
2555
|
-
return {
|
|
2556
|
-
enter: (ifPath) => {
|
|
2557
|
-
const ifNode = ifPath.node;
|
|
2558
|
-
const condition = sourceCode.substring(ifNode.test.start, ifNode.test.end) || 'condition';
|
|
2559
|
-
// Phase 6 (REG-267): Increment branch count and count logical operators
|
|
2560
|
-
if (controlFlowState) {
|
|
2561
|
-
controlFlowState.branchCount++;
|
|
2562
|
-
if (countLogicalOperators) {
|
|
2563
|
-
controlFlowState.logicalOpCount += countLogicalOperators(ifNode.test);
|
|
2564
|
-
}
|
|
2565
|
-
}
|
|
2566
|
-
// Check if this if-statement is an else-if (alternate of parent IfStatement)
|
|
2567
|
-
const isElseIf = t.isIfStatement(ifPath.parent) && ifPath.parentKey === 'alternate';
|
|
2568
|
-
// Determine actual parent scope
|
|
2569
|
-
let actualParentScopeId;
|
|
2570
|
-
if (isElseIf) {
|
|
2571
|
-
// For else-if, parent should be the outer BRANCH (stored in ifElseScopeMap)
|
|
2572
|
-
const parentIfInfo = ifElseScopeMap.get(ifPath.parent);
|
|
2573
|
-
if (parentIfInfo) {
|
|
2574
|
-
actualParentScopeId = parentIfInfo.branchId;
|
|
2575
|
-
}
|
|
2576
|
-
else {
|
|
2577
|
-
// Fallback to stack
|
|
2578
|
-
actualParentScopeId = (scopeIdStack && scopeIdStack.length > 0)
|
|
2579
|
-
? scopeIdStack[scopeIdStack.length - 1]
|
|
2580
|
-
: parentScopeId;
|
|
2581
|
-
}
|
|
2582
|
-
}
|
|
2583
|
-
else {
|
|
2584
|
-
// For regular if statements, use stack or original parentScopeId
|
|
2585
|
-
actualParentScopeId = (scopeIdStack && scopeIdStack.length > 0)
|
|
2586
|
-
? scopeIdStack[scopeIdStack.length - 1]
|
|
2587
|
-
: parentScopeId;
|
|
2588
|
-
}
|
|
2589
|
-
// 1. Create BRANCH node for if statement
|
|
2590
|
-
const branchCounter = branchCounterRef.value++;
|
|
2591
|
-
const legacyBranchId = `${module.file}:BRANCH:if:${getLine(ifNode)}:${branchCounter}`;
|
|
2592
|
-
const branchId = scopeTracker
|
|
2593
|
-
? computeSemanticId('BRANCH', 'if', scopeTracker.getContext(), { discriminator: branchCounter })
|
|
2594
|
-
: legacyBranchId;
|
|
2595
|
-
// 2. Extract condition expression info for HAS_CONDITION edge
|
|
2596
|
-
const conditionResult = this.extractDiscriminantExpression(ifNode.test, module);
|
|
2597
|
-
// For else-if, get the parent branch ID
|
|
2598
|
-
const isAlternateOfBranchId = isElseIf
|
|
2599
|
-
? ifElseScopeMap.get(ifPath.parent)?.branchId
|
|
2600
|
-
: undefined;
|
|
2601
|
-
branches.push({
|
|
2602
|
-
id: branchId,
|
|
2603
|
-
semanticId: branchId,
|
|
2604
|
-
type: 'BRANCH',
|
|
2605
|
-
branchType: 'if',
|
|
2606
|
-
file: module.file,
|
|
2607
|
-
line: getLine(ifNode),
|
|
2608
|
-
parentScopeId: actualParentScopeId,
|
|
2609
|
-
discriminantExpressionId: conditionResult.id,
|
|
2610
|
-
discriminantExpressionType: conditionResult.expressionType,
|
|
2611
|
-
discriminantLine: conditionResult.line,
|
|
2612
|
-
discriminantColumn: conditionResult.column,
|
|
2613
|
-
isAlternateOfBranchId
|
|
2614
|
-
});
|
|
2615
|
-
// 3. Create if-body SCOPE (backward compatibility)
|
|
2616
|
-
// Parent is now BRANCH, not original parentScopeId
|
|
2617
|
-
const counterId = ifScopeCounterRef.value++;
|
|
2618
|
-
const ifScopeId = `SCOPE#if#${module.file}#${getLine(ifNode)}:${getColumn(ifNode)}:${counterId}`;
|
|
2619
|
-
// Parse condition to extract constraints
|
|
2620
|
-
const constraints = ConditionParser.parse(ifNode.test);
|
|
2621
|
-
const ifSemanticId = this.generateSemanticId('if_statement', scopeTracker);
|
|
2622
|
-
scopes.push({
|
|
2623
|
-
id: ifScopeId,
|
|
2624
|
-
type: 'SCOPE',
|
|
2625
|
-
scopeType: 'if_statement',
|
|
2626
|
-
name: `if:${getLine(ifNode)}:${getColumn(ifNode)}:${counterId}`,
|
|
2627
|
-
semanticId: ifSemanticId,
|
|
2628
|
-
conditional: true,
|
|
2629
|
-
condition,
|
|
2630
|
-
constraints: constraints.length > 0 ? constraints : undefined,
|
|
2631
|
-
file: module.file,
|
|
2632
|
-
line: getLine(ifNode),
|
|
2633
|
-
parentScopeId: branchId // Parent is BRANCH, not original parentScopeId
|
|
2634
|
-
});
|
|
2635
|
-
// 4. Push if scope onto stack for CONTAINS edges
|
|
2636
|
-
if (scopeIdStack) {
|
|
2637
|
-
scopeIdStack.push(ifScopeId);
|
|
2638
|
-
}
|
|
2639
|
-
// Enter scope for semantic ID generation
|
|
2640
|
-
if (scopeTracker) {
|
|
2641
|
-
scopeTracker.enterCountedScope('if');
|
|
2642
|
-
}
|
|
2643
|
-
// 5. Handle else branch if present
|
|
2644
|
-
let elseScopeId = null;
|
|
2645
|
-
if (ifNode.alternate && !t.isIfStatement(ifNode.alternate)) {
|
|
2646
|
-
// Only create else scope for actual else block, not else-if
|
|
2647
|
-
const elseCounterId = ifScopeCounterRef.value++;
|
|
2648
|
-
elseScopeId = `SCOPE#else#${module.file}#${getLine(ifNode.alternate)}:${getColumn(ifNode.alternate)}:${elseCounterId}`;
|
|
2649
|
-
const negatedConstraints = constraints.length > 0 ? ConditionParser.negate(constraints) : undefined;
|
|
2650
|
-
const elseSemanticId = this.generateSemanticId('else_statement', scopeTracker);
|
|
2651
|
-
scopes.push({
|
|
2652
|
-
id: elseScopeId,
|
|
2653
|
-
type: 'SCOPE',
|
|
2654
|
-
scopeType: 'else_statement',
|
|
2655
|
-
name: `else:${getLine(ifNode.alternate)}:${getColumn(ifNode.alternate)}:${elseCounterId}`,
|
|
2656
|
-
semanticId: elseSemanticId,
|
|
2657
|
-
conditional: true,
|
|
2658
|
-
constraints: negatedConstraints,
|
|
2659
|
-
file: module.file,
|
|
2660
|
-
line: getLine(ifNode.alternate),
|
|
2661
|
-
parentScopeId: branchId // Parent is BRANCH, not original parentScopeId
|
|
2662
|
-
});
|
|
2663
|
-
// Store info to switch to else scope when we enter alternate
|
|
2664
|
-
ifElseScopeMap.set(ifNode, { inElse: false, hasElse: true, ifScopeId, elseScopeId, branchId });
|
|
2665
|
-
}
|
|
2666
|
-
else {
|
|
2667
|
-
ifElseScopeMap.set(ifNode, { inElse: false, hasElse: false, ifScopeId, elseScopeId: null, branchId });
|
|
2668
|
-
}
|
|
2669
|
-
},
|
|
2670
|
-
exit: (ifPath) => {
|
|
2671
|
-
const ifNode = ifPath.node;
|
|
2672
|
-
// Pop scope from stack (either if or else, depending on what we're exiting)
|
|
2673
|
-
if (scopeIdStack) {
|
|
2674
|
-
scopeIdStack.pop();
|
|
2675
|
-
}
|
|
2676
|
-
// Exit the current scope (either if or else)
|
|
2677
|
-
if (scopeTracker) {
|
|
2678
|
-
scopeTracker.exitScope();
|
|
2679
|
-
}
|
|
2680
|
-
// If we were in else, we already exited else scope
|
|
2681
|
-
// If we only had if, we exit if scope (done above)
|
|
2682
|
-
ifElseScopeMap.delete(ifNode);
|
|
2683
|
-
}
|
|
2684
|
-
};
|
|
2685
|
-
}
|
|
2686
|
-
/**
|
|
2687
|
-
* Factory method to create ConditionalExpression (ternary) handler.
|
|
2688
|
-
* Creates BRANCH nodes with branchType='ternary' and increments branchCount for cyclomatic complexity.
|
|
2689
|
-
*
|
|
2690
|
-
* Key difference from IfStatement: ternary has EXPRESSIONS as branches, not SCOPE blocks.
|
|
2691
|
-
* We store consequentExpressionId and alternateExpressionId in BranchInfo for HAS_CONSEQUENT/HAS_ALTERNATE edges.
|
|
2086
|
+
* Анализирует тело функции и извлекает переменные, вызовы, условные блоки.
|
|
2087
|
+
* Uses ScopeTracker from collections for semantic ID generation.
|
|
2692
2088
|
*
|
|
2693
|
-
*
|
|
2694
|
-
*
|
|
2695
|
-
*
|
|
2696
|
-
* @param branchCounterRef - Counter for unique BRANCH IDs
|
|
2697
|
-
* @param scopeTracker - Tracker for semantic ID generation
|
|
2698
|
-
* @param scopeIdStack - Stack for tracking current scope ID for CONTAINS edges
|
|
2699
|
-
* @param controlFlowState - State for tracking control flow metrics (complexity)
|
|
2700
|
-
* @param countLogicalOperators - Function to count logical operators in condition
|
|
2089
|
+
* REG-422: Delegates traversal to extracted handler classes.
|
|
2090
|
+
* Local state is encapsulated in FunctionBodyContext; each handler
|
|
2091
|
+
* contributes a Visitor fragment that is merged into a single traversal.
|
|
2701
2092
|
*/
|
|
2702
|
-
|
|
2703
|
-
|
|
2704
|
-
|
|
2705
|
-
|
|
2706
|
-
|
|
2707
|
-
|
|
2708
|
-
|
|
2709
|
-
|
|
2710
|
-
|
|
2711
|
-
|
|
2712
|
-
|
|
2713
|
-
// Determine parent scope from stack or fallback
|
|
2714
|
-
const actualParentScopeId = (scopeIdStack && scopeIdStack.length > 0)
|
|
2715
|
-
? scopeIdStack[scopeIdStack.length - 1]
|
|
2716
|
-
: parentScopeId;
|
|
2717
|
-
// Create BRANCH node with branchType='ternary'
|
|
2718
|
-
const branchCounter = branchCounterRef.value++;
|
|
2719
|
-
const legacyBranchId = `${module.file}:BRANCH:ternary:${getLine(condNode)}:${branchCounter}`;
|
|
2720
|
-
const branchId = scopeTracker
|
|
2721
|
-
? computeSemanticId('BRANCH', 'ternary', scopeTracker.getContext(), { discriminator: branchCounter })
|
|
2722
|
-
: legacyBranchId;
|
|
2723
|
-
// Extract condition expression info for HAS_CONDITION edge
|
|
2724
|
-
const conditionResult = this.extractDiscriminantExpression(condNode.test, module);
|
|
2725
|
-
// Generate expression IDs for consequent and alternate
|
|
2726
|
-
const consequentLine = getLine(condNode.consequent);
|
|
2727
|
-
const consequentColumn = getColumn(condNode.consequent);
|
|
2728
|
-
const consequentExpressionId = ExpressionNode.generateId(condNode.consequent.type, module.file, consequentLine, consequentColumn);
|
|
2729
|
-
const alternateLine = getLine(condNode.alternate);
|
|
2730
|
-
const alternateColumn = getColumn(condNode.alternate);
|
|
2731
|
-
const alternateExpressionId = ExpressionNode.generateId(condNode.alternate.type, module.file, alternateLine, alternateColumn);
|
|
2732
|
-
branches.push({
|
|
2733
|
-
id: branchId,
|
|
2734
|
-
semanticId: branchId,
|
|
2735
|
-
type: 'BRANCH',
|
|
2736
|
-
branchType: 'ternary',
|
|
2093
|
+
analyzeFunctionBody(funcPath, parentScopeId, module, collections) {
|
|
2094
|
+
// 1. Create context (replaces ~260 lines of local var declarations)
|
|
2095
|
+
const ctx = createFunctionBodyContext(funcPath, parentScopeId, module, collections, (collections.functions ?? []), extractNamesFromPattern);
|
|
2096
|
+
// 2. Handle implicit return for THIS arrow function if it has an expression body
|
|
2097
|
+
// e.g., `const double = x => x * 2;` — this needs this.extractReturnExpressionInfo,
|
|
2098
|
+
// so it stays here rather than in a handler.
|
|
2099
|
+
if (t.isArrowFunctionExpression(ctx.funcNode) && !t.isBlockStatement(ctx.funcNode.body) && ctx.currentFunctionId) {
|
|
2100
|
+
const bodyExpr = ctx.funcNode.body;
|
|
2101
|
+
const exprInfo = this.extractReturnExpressionInfo(bodyExpr, module, ctx.literals, ctx.literalCounterRef, ctx.funcLine, ctx.funcColumn, 'implicit_return');
|
|
2102
|
+
ctx.returnStatements.push({
|
|
2103
|
+
parentFunctionId: ctx.currentFunctionId,
|
|
2737
2104
|
file: module.file,
|
|
2738
|
-
line: getLine(
|
|
2739
|
-
|
|
2740
|
-
|
|
2741
|
-
|
|
2742
|
-
|
|
2743
|
-
discriminantColumn: conditionResult.column,
|
|
2744
|
-
consequentExpressionId,
|
|
2745
|
-
alternateExpressionId
|
|
2105
|
+
line: getLine(bodyExpr),
|
|
2106
|
+
column: getColumn(bodyExpr),
|
|
2107
|
+
returnValueType: 'NONE',
|
|
2108
|
+
isImplicitReturn: true,
|
|
2109
|
+
...exprInfo,
|
|
2746
2110
|
});
|
|
2747
|
-
}
|
|
2111
|
+
}
|
|
2112
|
+
// 3. Create handlers and merge their visitors into a single traversal
|
|
2113
|
+
// Cast to AnalyzerDelegate — the interface declares the same methods that exist
|
|
2114
|
+
// on this class as private. The cast is safe because the shape matches exactly.
|
|
2115
|
+
const delegate = this;
|
|
2116
|
+
const handlers = [
|
|
2117
|
+
new VariableHandler(ctx, delegate),
|
|
2118
|
+
new ReturnYieldHandler(ctx, delegate),
|
|
2119
|
+
new ThrowHandler(ctx, delegate),
|
|
2120
|
+
new NestedFunctionHandler(ctx, delegate),
|
|
2121
|
+
new PropertyAccessHandler(ctx, delegate),
|
|
2122
|
+
new NewExpressionHandler(ctx, delegate),
|
|
2123
|
+
new CallExpressionHandler(ctx, delegate),
|
|
2124
|
+
new LoopHandler(ctx, delegate),
|
|
2125
|
+
new TryCatchHandler(ctx, delegate),
|
|
2126
|
+
new BranchHandler(ctx, delegate),
|
|
2127
|
+
];
|
|
2128
|
+
const mergedVisitor = {};
|
|
2129
|
+
for (const handler of handlers) {
|
|
2130
|
+
Object.assign(mergedVisitor, handler.getHandlers());
|
|
2131
|
+
}
|
|
2132
|
+
// 4. Single traversal over the function body
|
|
2133
|
+
funcPath.traverse(mergedVisitor);
|
|
2134
|
+
// 5. Post-traverse: collect CATCHES_FROM info for try/catch blocks
|
|
2135
|
+
if (ctx.functionPath) {
|
|
2136
|
+
this.collectCatchesFromInfo(ctx.functionPath, ctx.catchBlocks, ctx.callSites, ctx.methodCalls, ctx.constructorCalls, ctx.catchesFromInfos, module);
|
|
2137
|
+
}
|
|
2138
|
+
// 6. Post-traverse: Attach control flow metadata to the function node
|
|
2139
|
+
this.attachControlFlowMetadata(ctx);
|
|
2748
2140
|
}
|
|
2749
2141
|
/**
|
|
2750
|
-
*
|
|
2751
|
-
*
|
|
2752
|
-
* When entering a finally block, switches scope from try/catch to finally.
|
|
2753
|
-
*
|
|
2754
|
-
* @param scopeTracker - Tracker for semantic ID generation
|
|
2755
|
-
* @param ifElseScopeMap - Map to track if/else scope transitions
|
|
2756
|
-
* @param tryScopeMap - Map to track try/catch/finally scope transitions
|
|
2757
|
-
* @param scopeIdStack - Stack for tracking current scope ID for CONTAINS edges
|
|
2142
|
+
* Attach control flow metadata (cyclomatic complexity, error tracking, HOF bindings)
|
|
2143
|
+
* to the matching function node after traversal completes.
|
|
2758
2144
|
*/
|
|
2759
|
-
|
|
2760
|
-
|
|
2761
|
-
|
|
2762
|
-
|
|
2763
|
-
|
|
2764
|
-
|
|
2765
|
-
|
|
2766
|
-
|
|
2767
|
-
|
|
2768
|
-
|
|
2769
|
-
|
|
2770
|
-
|
|
2771
|
-
|
|
2772
|
-
|
|
2773
|
-
|
|
2774
|
-
|
|
2775
|
-
|
|
2776
|
-
|
|
2777
|
-
|
|
2778
|
-
|
|
2779
|
-
|
|
2780
|
-
|
|
2781
|
-
|
|
2782
|
-
|
|
2783
|
-
|
|
2784
|
-
|
|
2785
|
-
|
|
2786
|
-
|
|
2787
|
-
|
|
2788
|
-
|
|
2789
|
-
|
|
2790
|
-
|
|
2791
|
-
|
|
2792
|
-
|
|
2793
|
-
}
|
|
2794
|
-
scopeInfo.currentBlock = 'finally';
|
|
2795
|
-
}
|
|
2796
|
-
}
|
|
2797
|
-
}
|
|
2145
|
+
attachControlFlowMetadata(ctx) {
|
|
2146
|
+
if (!ctx.matchingFunction)
|
|
2147
|
+
return;
|
|
2148
|
+
const cyclomaticComplexity = 1 +
|
|
2149
|
+
ctx.controlFlowState.branchCount +
|
|
2150
|
+
ctx.controlFlowState.loopCount +
|
|
2151
|
+
ctx.controlFlowState.caseCount +
|
|
2152
|
+
ctx.controlFlowState.logicalOpCount;
|
|
2153
|
+
// REG-311: Collect rejection info for this function
|
|
2154
|
+
const functionRejectionPatterns = ctx.rejectionPatterns.filter(p => p.functionId === ctx.matchingFunction.id);
|
|
2155
|
+
const asyncPatterns = functionRejectionPatterns.filter(p => p.isAsync);
|
|
2156
|
+
const syncPatterns = functionRejectionPatterns.filter(p => !p.isAsync);
|
|
2157
|
+
const canReject = asyncPatterns.length > 0;
|
|
2158
|
+
const hasAsyncThrow = asyncPatterns.some(p => p.rejectionType === 'async_throw');
|
|
2159
|
+
const rejectedBuiltinErrors = [...new Set(asyncPatterns
|
|
2160
|
+
.filter(p => p.errorClassName !== null)
|
|
2161
|
+
.map(p => p.errorClassName))];
|
|
2162
|
+
// REG-286: Sync throw error tracking
|
|
2163
|
+
const thrownBuiltinErrors = [...new Set(syncPatterns
|
|
2164
|
+
.filter(p => p.errorClassName !== null)
|
|
2165
|
+
.map(p => p.errorClassName))];
|
|
2166
|
+
ctx.matchingFunction.controlFlow = {
|
|
2167
|
+
hasBranches: ctx.controlFlowState.branchCount > 0,
|
|
2168
|
+
hasLoops: ctx.controlFlowState.loopCount > 0,
|
|
2169
|
+
hasTryCatch: ctx.controlFlowState.hasTryCatch,
|
|
2170
|
+
hasEarlyReturn: ctx.controlFlowState.hasEarlyReturn,
|
|
2171
|
+
hasThrow: ctx.controlFlowState.hasThrow,
|
|
2172
|
+
cyclomaticComplexity,
|
|
2173
|
+
// REG-311: Async error tracking
|
|
2174
|
+
canReject,
|
|
2175
|
+
hasAsyncThrow,
|
|
2176
|
+
rejectedBuiltinErrors: rejectedBuiltinErrors.length > 0 ? rejectedBuiltinErrors : undefined,
|
|
2177
|
+
// REG-286: Sync throw tracking
|
|
2178
|
+
thrownBuiltinErrors: thrownBuiltinErrors.length > 0 ? thrownBuiltinErrors : undefined
|
|
2798
2179
|
};
|
|
2180
|
+
// REG-401: Store invoked parameter indexes for user-defined HOF detection
|
|
2181
|
+
if (ctx.invokedParamIndexes.size > 0) {
|
|
2182
|
+
ctx.matchingFunction.invokesParamIndexes = [...ctx.invokedParamIndexes];
|
|
2183
|
+
}
|
|
2184
|
+
// REG-417: Store property paths for destructured param bindings
|
|
2185
|
+
if (ctx.invokesParamBindings.length > 0) {
|
|
2186
|
+
ctx.matchingFunction.invokesParamBindings = ctx.invokesParamBindings;
|
|
2187
|
+
}
|
|
2799
2188
|
}
|
|
2800
2189
|
/**
|
|
2801
|
-
*
|
|
2802
|
-
*
|
|
2190
|
+
* Handle CallExpression nodes: direct function calls (greet(), main())
|
|
2191
|
+
* and method calls (obj.method(), data.process()).
|
|
2192
|
+
*
|
|
2193
|
+
* Handles:
|
|
2194
|
+
* - Direct function calls (Identifier callee) → callSites collection
|
|
2195
|
+
* - Method calls (MemberExpression callee) → methodCalls collection
|
|
2196
|
+
* - Array mutation detection (push, unshift, splice)
|
|
2197
|
+
* - Object.assign() detection
|
|
2198
|
+
* - REG-311: isAwaited and isInsideTry metadata on CALL nodes
|
|
2199
|
+
*
|
|
2200
|
+
* @param callNode - The call expression AST node
|
|
2201
|
+
* @param processedCallSites - Set of already processed call site keys to avoid duplicates
|
|
2202
|
+
* @param processedMethodCalls - Set of already processed method call keys to avoid duplicates
|
|
2203
|
+
* @param callSites - Collection for direct function calls
|
|
2204
|
+
* @param methodCalls - Collection for method calls
|
|
2205
|
+
* @param module - Current module being analyzed
|
|
2206
|
+
* @param callSiteCounterRef - Counter for legacy ID generation
|
|
2207
|
+
* @param scopeTracker - Optional scope tracker for semantic ID generation
|
|
2208
|
+
* @param parentScopeId - ID of the parent scope containing this call
|
|
2209
|
+
* @param collections - Full collections object for array/object mutations
|
|
2210
|
+
* @param isAwaited - REG-311: true if wrapped in await expression
|
|
2211
|
+
* @param isInsideTry - REG-311: true if inside try block
|
|
2803
2212
|
*/
|
|
2804
|
-
|
|
2805
|
-
// Extract with defaults for optional properties
|
|
2806
|
-
const functions = (collections.functions ?? []);
|
|
2807
|
-
const scopes = (collections.scopes ?? []);
|
|
2808
|
-
const variableDeclarations = (collections.variableDeclarations ?? []);
|
|
2809
|
-
const callSites = (collections.callSites ?? []);
|
|
2810
|
-
const methodCalls = (collections.methodCalls ?? []);
|
|
2811
|
-
const _eventListeners = (collections.eventListeners ?? []);
|
|
2812
|
-
const _methodCallbacks = (collections.methodCallbacks ?? []);
|
|
2813
|
-
const classInstantiations = (collections.classInstantiations ?? []);
|
|
2814
|
-
const constructorCalls = (collections.constructorCalls ?? []);
|
|
2815
|
-
const _httpRequests = (collections.httpRequests ?? []);
|
|
2816
|
-
const literals = (collections.literals ?? []);
|
|
2817
|
-
const variableAssignments = (collections.variableAssignments ?? []);
|
|
2818
|
-
const ifScopeCounterRef = (collections.ifScopeCounterRef ?? { value: 0 });
|
|
2819
|
-
const scopeCounterRef = (collections.scopeCounterRef ?? { value: 0 });
|
|
2820
|
-
const varDeclCounterRef = (collections.varDeclCounterRef ?? { value: 0 });
|
|
2821
|
-
const callSiteCounterRef = (collections.callSiteCounterRef ?? { value: 0 });
|
|
2822
|
-
const functionCounterRef = (collections.functionCounterRef ?? { value: 0 });
|
|
2823
|
-
const _httpRequestCounterRef = (collections.httpRequestCounterRef ?? { value: 0 });
|
|
2824
|
-
const literalCounterRef = (collections.literalCounterRef ?? { value: 0 });
|
|
2825
|
-
const _anonymousFunctionCounterRef = (collections.anonymousFunctionCounterRef ?? { value: 0 });
|
|
2826
|
-
const scopeTracker = collections.scopeTracker;
|
|
2827
|
-
// Object literal tracking (REG-328)
|
|
2828
|
-
if (!collections.objectLiterals) {
|
|
2829
|
-
collections.objectLiterals = [];
|
|
2830
|
-
}
|
|
2831
|
-
if (!collections.objectProperties) {
|
|
2832
|
-
collections.objectProperties = [];
|
|
2833
|
-
}
|
|
2834
|
-
if (!collections.objectLiteralCounterRef) {
|
|
2835
|
-
collections.objectLiteralCounterRef = { value: 0 };
|
|
2836
|
-
}
|
|
2837
|
-
const objectLiterals = collections.objectLiterals;
|
|
2838
|
-
const objectProperties = collections.objectProperties;
|
|
2839
|
-
const objectLiteralCounterRef = collections.objectLiteralCounterRef;
|
|
2840
|
-
const returnStatements = (collections.returnStatements ?? []);
|
|
2841
|
-
// Initialize yieldExpressions if not exist to ensure nested function calls share same array
|
|
2842
|
-
if (!collections.yieldExpressions) {
|
|
2843
|
-
collections.yieldExpressions = [];
|
|
2844
|
-
}
|
|
2845
|
-
const yieldExpressions = collections.yieldExpressions;
|
|
2846
|
-
const _parameters = (collections.parameters ?? []);
|
|
2847
|
-
// Control flow collections (Phase 2: LOOP nodes)
|
|
2848
|
-
// Initialize if not exist to ensure nested function calls share same arrays
|
|
2849
|
-
if (!collections.loops) {
|
|
2850
|
-
collections.loops = [];
|
|
2851
|
-
}
|
|
2852
|
-
if (!collections.loopCounterRef) {
|
|
2853
|
-
collections.loopCounterRef = { value: 0 };
|
|
2854
|
-
}
|
|
2855
|
-
const loops = collections.loops;
|
|
2856
|
-
const loopCounterRef = collections.loopCounterRef;
|
|
2857
|
-
const updateExpressions = (collections.updateExpressions ?? []);
|
|
2858
|
-
const processedNodes = collections.processedNodes ?? {
|
|
2859
|
-
functions: new Set(),
|
|
2860
|
-
classes: new Set(),
|
|
2861
|
-
imports: new Set(),
|
|
2862
|
-
exports: new Set(),
|
|
2863
|
-
variables: new Set(),
|
|
2864
|
-
callSites: new Set(),
|
|
2865
|
-
methodCalls: new Set(),
|
|
2866
|
-
varDecls: new Set(),
|
|
2867
|
-
eventListeners: new Set()
|
|
2868
|
-
};
|
|
2869
|
-
const parentScopeVariables = new Set();
|
|
2870
|
-
const processedCallSites = processedNodes.callSites;
|
|
2871
|
-
const _processedVarDecls = processedNodes.varDecls;
|
|
2872
|
-
const processedMethodCalls = processedNodes.methodCalls;
|
|
2873
|
-
const _processedEventListeners = processedNodes.eventListeners;
|
|
2874
|
-
// Track if/else scope transitions (Phase 3: extended with branchId)
|
|
2875
|
-
const ifElseScopeMap = new Map();
|
|
2876
|
-
// Ensure branches and branchCounterRef are initialized (used by IfStatement and SwitchStatement)
|
|
2877
|
-
if (!collections.branches) {
|
|
2878
|
-
collections.branches = [];
|
|
2879
|
-
}
|
|
2880
|
-
if (!collections.branchCounterRef) {
|
|
2881
|
-
collections.branchCounterRef = { value: 0 };
|
|
2882
|
-
}
|
|
2883
|
-
const branches = collections.branches;
|
|
2884
|
-
const branchCounterRef = collections.branchCounterRef;
|
|
2885
|
-
// Phase 4: Initialize try/catch/finally collections and counters
|
|
2886
|
-
if (!collections.tryBlocks) {
|
|
2887
|
-
collections.tryBlocks = [];
|
|
2888
|
-
}
|
|
2889
|
-
if (!collections.catchBlocks) {
|
|
2890
|
-
collections.catchBlocks = [];
|
|
2891
|
-
}
|
|
2892
|
-
if (!collections.finallyBlocks) {
|
|
2893
|
-
collections.finallyBlocks = [];
|
|
2894
|
-
}
|
|
2895
|
-
if (!collections.tryBlockCounterRef) {
|
|
2896
|
-
collections.tryBlockCounterRef = { value: 0 };
|
|
2897
|
-
}
|
|
2898
|
-
if (!collections.catchBlockCounterRef) {
|
|
2899
|
-
collections.catchBlockCounterRef = { value: 0 };
|
|
2900
|
-
}
|
|
2901
|
-
if (!collections.finallyBlockCounterRef) {
|
|
2902
|
-
collections.finallyBlockCounterRef = { value: 0 };
|
|
2903
|
-
}
|
|
2904
|
-
const tryBlocks = collections.tryBlocks;
|
|
2905
|
-
const catchBlocks = collections.catchBlocks;
|
|
2906
|
-
const finallyBlocks = collections.finallyBlocks;
|
|
2907
|
-
const tryBlockCounterRef = collections.tryBlockCounterRef;
|
|
2908
|
-
const catchBlockCounterRef = collections.catchBlockCounterRef;
|
|
2909
|
-
const finallyBlockCounterRef = collections.finallyBlockCounterRef;
|
|
2910
|
-
// Track try/catch/finally scope transitions
|
|
2911
|
-
const tryScopeMap = new Map();
|
|
2912
|
-
// REG-334: Use shared Promise executor contexts from collections.
|
|
2913
|
-
// These are populated by module-level NewExpression handler and function-level NewExpression handler.
|
|
2914
|
-
if (!collections.promiseExecutorContexts) {
|
|
2915
|
-
collections.promiseExecutorContexts = new Map();
|
|
2916
|
-
}
|
|
2917
|
-
const promiseExecutorContexts = collections.promiseExecutorContexts;
|
|
2918
|
-
// Initialize promiseResolutions array if not exists
|
|
2919
|
-
if (!collections.promiseResolutions) {
|
|
2920
|
-
collections.promiseResolutions = [];
|
|
2921
|
-
}
|
|
2922
|
-
const promiseResolutions = collections.promiseResolutions;
|
|
2923
|
-
// REG-311: Initialize rejectionPatterns and catchesFromInfos collections
|
|
2924
|
-
if (!collections.rejectionPatterns) {
|
|
2925
|
-
collections.rejectionPatterns = [];
|
|
2926
|
-
}
|
|
2927
|
-
if (!collections.catchesFromInfos) {
|
|
2928
|
-
collections.catchesFromInfos = [];
|
|
2929
|
-
}
|
|
2930
|
-
const rejectionPatterns = collections.rejectionPatterns;
|
|
2931
|
-
const catchesFromInfos = collections.catchesFromInfos;
|
|
2932
|
-
// Dynamic scope ID stack for CONTAINS edges
|
|
2933
|
-
// Starts with the function body scope, gets updated as we enter/exit conditional scopes
|
|
2934
|
-
const scopeIdStack = [parentScopeId];
|
|
2935
|
-
const getCurrentScopeId = () => scopeIdStack[scopeIdStack.length - 1];
|
|
2936
|
-
// Determine the ID of the function we're analyzing for RETURNS edges
|
|
2937
|
-
// Find by matching file/line/column in functions collection (it was just added by the visitor)
|
|
2938
|
-
// REG-271: Skip for StaticBlock (static blocks don't have RETURNS edges or control flow metadata)
|
|
2939
|
-
const funcNode = funcPath.node;
|
|
2940
|
-
const functionNode = t.isFunction(funcNode) ? funcNode : null;
|
|
2941
|
-
const functionPath = functionNode ? funcPath : null;
|
|
2942
|
-
const funcLine = getLine(funcNode);
|
|
2943
|
-
const funcColumn = getColumn(funcNode);
|
|
2944
|
-
let currentFunctionId = null;
|
|
2945
|
-
// StaticBlock is not a function - skip function matching for RETURNS edges
|
|
2946
|
-
// For StaticBlock, matchingFunction will be undefined
|
|
2947
|
-
const matchingFunction = funcNode.type !== 'StaticBlock'
|
|
2948
|
-
? functions.find(f => f.file === module.file &&
|
|
2949
|
-
f.line === funcLine &&
|
|
2950
|
-
(f.column === undefined || f.column === funcColumn))
|
|
2951
|
-
: undefined;
|
|
2952
|
-
if (matchingFunction) {
|
|
2953
|
-
currentFunctionId = matchingFunction.id;
|
|
2954
|
-
}
|
|
2955
|
-
// Phase 6 (REG-267): Control flow tracking state for cyclomatic complexity
|
|
2956
|
-
const controlFlowState = {
|
|
2957
|
-
branchCount: 0, // if/switch statements
|
|
2958
|
-
loopCount: 0, // for/while/do-while/for-in/for-of
|
|
2959
|
-
caseCount: 0, // switch cases (excluding default)
|
|
2960
|
-
logicalOpCount: 0, // && and || in conditions
|
|
2961
|
-
hasTryCatch: false,
|
|
2962
|
-
hasEarlyReturn: false,
|
|
2963
|
-
hasThrow: false,
|
|
2964
|
-
returnCount: 0, // Track total return count for early return detection
|
|
2965
|
-
totalStatements: 0, // Track if there are statements after returns
|
|
2966
|
-
// REG-311: Try block depth counter for O(1) isInsideTry detection
|
|
2967
|
-
tryBlockDepth: 0
|
|
2968
|
-
};
|
|
2969
|
-
// Handle implicit return for THIS arrow function if it has an expression body
|
|
2970
|
-
// e.g., `const double = x => x * 2;` - the function we're analyzing IS an arrow with expression body
|
|
2971
|
-
if (t.isArrowFunctionExpression(funcNode) && !t.isBlockStatement(funcNode.body) && currentFunctionId) {
|
|
2972
|
-
const bodyExpr = funcNode.body;
|
|
2973
|
-
const bodyLine = getLine(bodyExpr);
|
|
2974
|
-
const bodyColumn = getColumn(bodyExpr);
|
|
2975
|
-
// Extract expression-specific info using shared method
|
|
2976
|
-
const exprInfo = this.extractReturnExpressionInfo(bodyExpr, module, literals, literalCounterRef, funcLine, funcColumn, 'implicit_return');
|
|
2977
|
-
const returnInfo = {
|
|
2978
|
-
parentFunctionId: currentFunctionId,
|
|
2979
|
-
file: module.file,
|
|
2980
|
-
line: bodyLine,
|
|
2981
|
-
column: bodyColumn,
|
|
2982
|
-
returnValueType: 'NONE',
|
|
2983
|
-
isImplicitReturn: true,
|
|
2984
|
-
...exprInfo,
|
|
2985
|
-
};
|
|
2986
|
-
returnStatements.push(returnInfo);
|
|
2987
|
-
}
|
|
2988
|
-
funcPath.traverse({
|
|
2989
|
-
VariableDeclaration: (varPath) => {
|
|
2990
|
-
this.handleVariableDeclaration(varPath, getCurrentScopeId(), module, variableDeclarations, classInstantiations, literals, variableAssignments, varDeclCounterRef, literalCounterRef, scopeTracker, parentScopeVariables, objectLiterals, objectProperties, objectLiteralCounterRef);
|
|
2991
|
-
},
|
|
2992
|
-
// Detect indexed array assignments: arr[i] = value
|
|
2993
|
-
AssignmentExpression: (assignPath) => {
|
|
2994
|
-
const assignNode = assignPath.node;
|
|
2995
|
-
// === VARIABLE REASSIGNMENT (REG-290) ===
|
|
2996
|
-
// Check if LHS is simple identifier (not obj.prop, not arr[i])
|
|
2997
|
-
// Must be checked FIRST before array/object mutation handlers
|
|
2998
|
-
if (assignNode.left.type === 'Identifier') {
|
|
2999
|
-
// Initialize collection if not exists
|
|
3000
|
-
if (!collections.variableReassignments) {
|
|
3001
|
-
collections.variableReassignments = [];
|
|
3002
|
-
}
|
|
3003
|
-
const variableReassignments = collections.variableReassignments;
|
|
3004
|
-
this.detectVariableReassignment(assignNode, module, variableReassignments, scopeTracker);
|
|
3005
|
-
}
|
|
3006
|
-
// === END VARIABLE REASSIGNMENT ===
|
|
3007
|
-
// Initialize collection if not exists
|
|
3008
|
-
if (!collections.arrayMutations) {
|
|
3009
|
-
collections.arrayMutations = [];
|
|
3010
|
-
}
|
|
3011
|
-
const arrayMutations = collections.arrayMutations;
|
|
3012
|
-
// Check for indexed array assignment: arr[i] = value
|
|
3013
|
-
this.detectIndexedArrayAssignment(assignNode, module, arrayMutations, scopeTracker, collections);
|
|
3014
|
-
// Initialize object mutations collection if not exists
|
|
3015
|
-
if (!collections.objectMutations) {
|
|
3016
|
-
collections.objectMutations = [];
|
|
3017
|
-
}
|
|
3018
|
-
const objectMutations = collections.objectMutations;
|
|
3019
|
-
// Check for object property assignment: obj.prop = value
|
|
3020
|
-
this.detectObjectPropertyAssignment(assignNode, module, objectMutations, scopeTracker);
|
|
3021
|
-
},
|
|
3022
|
-
// Handle return statements for RETURNS edges
|
|
3023
|
-
ReturnStatement: (returnPath) => {
|
|
3024
|
-
// Skip if we couldn't determine the function ID
|
|
3025
|
-
if (!currentFunctionId) {
|
|
3026
|
-
return;
|
|
3027
|
-
}
|
|
3028
|
-
// Skip if this return is inside a nested function (not the function we're analyzing)
|
|
3029
|
-
// Check if there's a function ancestor BETWEEN us and funcNode
|
|
3030
|
-
// Stop checking once we reach funcNode - parents above funcNode are outside scope
|
|
3031
|
-
let parent = returnPath.parentPath;
|
|
3032
|
-
let isInsideConditional = false;
|
|
3033
|
-
while (parent) {
|
|
3034
|
-
// If we've reached funcNode, we're done checking - this return belongs to funcNode
|
|
3035
|
-
if (parent.node === funcNode) {
|
|
3036
|
-
break;
|
|
3037
|
-
}
|
|
3038
|
-
if (t.isFunction(parent.node)) {
|
|
3039
|
-
// Found a function between returnPath and funcNode - this return is inside a nested function
|
|
3040
|
-
return;
|
|
3041
|
-
}
|
|
3042
|
-
// Track if return is inside a conditional block (if/else, switch case, loop, try/catch)
|
|
3043
|
-
if (t.isIfStatement(parent.node) ||
|
|
3044
|
-
t.isSwitchCase(parent.node) ||
|
|
3045
|
-
t.isLoop(parent.node) ||
|
|
3046
|
-
t.isTryStatement(parent.node) ||
|
|
3047
|
-
t.isCatchClause(parent.node)) {
|
|
3048
|
-
isInsideConditional = true;
|
|
3049
|
-
}
|
|
3050
|
-
parent = parent.parentPath;
|
|
3051
|
-
}
|
|
3052
|
-
// Phase 6 (REG-267): Track return count and early return detection
|
|
3053
|
-
controlFlowState.returnCount++;
|
|
3054
|
-
// A return is "early" if it's inside a conditional structure
|
|
3055
|
-
// (More returns after this one indicate the function doesn't always end here)
|
|
3056
|
-
if (isInsideConditional) {
|
|
3057
|
-
controlFlowState.hasEarlyReturn = true;
|
|
3058
|
-
}
|
|
3059
|
-
const returnNode = returnPath.node;
|
|
3060
|
-
const returnLine = getLine(returnNode);
|
|
3061
|
-
const returnColumn = getColumn(returnNode);
|
|
3062
|
-
// Handle bare return; (no value)
|
|
3063
|
-
if (!returnNode.argument) {
|
|
3064
|
-
// Skip - no data flow value
|
|
3065
|
-
return;
|
|
3066
|
-
}
|
|
3067
|
-
const arg = returnNode.argument;
|
|
3068
|
-
// Extract expression-specific info using shared method
|
|
3069
|
-
const exprInfo = this.extractReturnExpressionInfo(arg, module, literals, literalCounterRef, returnLine, returnColumn, 'return');
|
|
3070
|
-
const returnInfo = {
|
|
3071
|
-
parentFunctionId: currentFunctionId,
|
|
3072
|
-
file: module.file,
|
|
3073
|
-
line: returnLine,
|
|
3074
|
-
column: returnColumn,
|
|
3075
|
-
returnValueType: 'NONE',
|
|
3076
|
-
...exprInfo,
|
|
3077
|
-
};
|
|
3078
|
-
returnStatements.push(returnInfo);
|
|
3079
|
-
},
|
|
3080
|
-
// Phase 6 (REG-267): Track throw statements for control flow metadata
|
|
3081
|
-
// REG-311: Also detect async_throw rejection patterns
|
|
3082
|
-
ThrowStatement: (throwPath) => {
|
|
3083
|
-
// Skip if this throw is inside a nested function (not the function we're analyzing)
|
|
3084
|
-
let parent = throwPath.parentPath;
|
|
3085
|
-
while (parent) {
|
|
3086
|
-
if (t.isFunction(parent.node) && parent.node !== funcNode) {
|
|
3087
|
-
// This throw is inside a nested function - skip it
|
|
3088
|
-
return;
|
|
3089
|
-
}
|
|
3090
|
-
parent = parent.parentPath;
|
|
3091
|
-
}
|
|
3092
|
-
controlFlowState.hasThrow = true;
|
|
3093
|
-
// REG-311: Track rejection patterns for async functions
|
|
3094
|
-
const isAsyncFunction = functionNode?.async === true;
|
|
3095
|
-
if (isAsyncFunction && currentFunctionId && functionNode && functionPath) {
|
|
3096
|
-
const throwNode = throwPath.node;
|
|
3097
|
-
const arg = throwNode.argument;
|
|
3098
|
-
const throwLine = getLine(throwNode);
|
|
3099
|
-
const throwColumn = getColumn(throwNode);
|
|
3100
|
-
// Case 1: throw new Error() or throw new CustomError()
|
|
3101
|
-
if (arg && t.isNewExpression(arg) && t.isIdentifier(arg.callee)) {
|
|
3102
|
-
rejectionPatterns.push({
|
|
3103
|
-
functionId: currentFunctionId,
|
|
3104
|
-
errorClassName: arg.callee.name,
|
|
3105
|
-
rejectionType: 'async_throw',
|
|
3106
|
-
file: module.file,
|
|
3107
|
-
line: throwLine,
|
|
3108
|
-
column: throwColumn
|
|
3109
|
-
});
|
|
3110
|
-
}
|
|
3111
|
-
// Case 2: throw identifier - needs micro-trace
|
|
3112
|
-
else if (arg && t.isIdentifier(arg)) {
|
|
3113
|
-
const varName = arg.name;
|
|
3114
|
-
// Check if it's a parameter
|
|
3115
|
-
const isParameter = functionNode.params.some(param => t.isIdentifier(param) && param.name === varName);
|
|
3116
|
-
if (isParameter) {
|
|
3117
|
-
// Parameter forwarding - can't resolve statically
|
|
3118
|
-
rejectionPatterns.push({
|
|
3119
|
-
functionId: currentFunctionId,
|
|
3120
|
-
errorClassName: null,
|
|
3121
|
-
rejectionType: 'variable_parameter',
|
|
3122
|
-
file: module.file,
|
|
3123
|
-
line: throwLine,
|
|
3124
|
-
column: throwColumn,
|
|
3125
|
-
sourceVariableName: varName
|
|
3126
|
-
});
|
|
3127
|
-
}
|
|
3128
|
-
else {
|
|
3129
|
-
// Try micro-trace
|
|
3130
|
-
const { errorClassName, tracePath } = this.microTraceToErrorClass(varName, functionPath, variableDeclarations);
|
|
3131
|
-
rejectionPatterns.push({
|
|
3132
|
-
functionId: currentFunctionId,
|
|
3133
|
-
errorClassName,
|
|
3134
|
-
rejectionType: errorClassName ? 'variable_traced' : 'variable_unknown',
|
|
3135
|
-
file: module.file,
|
|
3136
|
-
line: throwLine,
|
|
3137
|
-
column: throwColumn,
|
|
3138
|
-
sourceVariableName: varName,
|
|
3139
|
-
tracePath
|
|
3140
|
-
});
|
|
3141
|
-
}
|
|
3142
|
-
}
|
|
3143
|
-
}
|
|
3144
|
-
},
|
|
3145
|
-
// Handle yield expressions for YIELDS/DELEGATES_TO edges (REG-270)
|
|
3146
|
-
YieldExpression: (yieldPath) => {
|
|
3147
|
-
// Skip if we couldn't determine the function ID
|
|
3148
|
-
if (!currentFunctionId) {
|
|
3149
|
-
return;
|
|
3150
|
-
}
|
|
3151
|
-
// Skip if this yield is inside a nested function (not the function we're analyzing)
|
|
3152
|
-
// Check if there's a function ancestor BETWEEN us and funcNode
|
|
3153
|
-
let parent = yieldPath.parentPath;
|
|
3154
|
-
while (parent) {
|
|
3155
|
-
// If we've reached funcNode, we're done checking - this yield belongs to funcNode
|
|
3156
|
-
if (parent.node === funcNode) {
|
|
3157
|
-
break;
|
|
3158
|
-
}
|
|
3159
|
-
if (t.isFunction(parent.node)) {
|
|
3160
|
-
// Found a function between yieldPath and funcNode - this yield is inside a nested function
|
|
3161
|
-
return;
|
|
3162
|
-
}
|
|
3163
|
-
parent = parent.parentPath;
|
|
3164
|
-
}
|
|
3165
|
-
const yieldNode = yieldPath.node;
|
|
3166
|
-
const yieldLine = getLine(yieldNode);
|
|
3167
|
-
const yieldColumn = getColumn(yieldNode);
|
|
3168
|
-
const isDelegate = yieldNode.delegate ?? false;
|
|
3169
|
-
// Handle bare yield; (no value) - only valid for non-delegate yield
|
|
3170
|
-
if (!yieldNode.argument && !isDelegate) {
|
|
3171
|
-
// Skip - no data flow value
|
|
3172
|
-
return;
|
|
3173
|
-
}
|
|
3174
|
-
// For yield* without argument (syntax error in practice, but handle gracefully)
|
|
3175
|
-
if (!yieldNode.argument) {
|
|
3176
|
-
return;
|
|
3177
|
-
}
|
|
3178
|
-
const arg = yieldNode.argument;
|
|
3179
|
-
// Extract expression-specific info using shared method
|
|
3180
|
-
// Note: We reuse extractReturnExpressionInfo since yield values have identical semantics
|
|
3181
|
-
const exprInfo = this.extractReturnExpressionInfo(arg, module, literals, literalCounterRef, yieldLine, yieldColumn, 'yield');
|
|
3182
|
-
// Map ReturnStatementInfo fields to YieldExpressionInfo fields
|
|
3183
|
-
const yieldInfo = {
|
|
3184
|
-
parentFunctionId: currentFunctionId,
|
|
3185
|
-
file: module.file,
|
|
3186
|
-
line: yieldLine,
|
|
3187
|
-
column: yieldColumn,
|
|
3188
|
-
isDelegate,
|
|
3189
|
-
yieldValueType: exprInfo.returnValueType ?? 'NONE',
|
|
3190
|
-
yieldValueName: exprInfo.returnValueName,
|
|
3191
|
-
yieldValueId: exprInfo.returnValueId,
|
|
3192
|
-
yieldValueLine: exprInfo.returnValueLine,
|
|
3193
|
-
yieldValueColumn: exprInfo.returnValueColumn,
|
|
3194
|
-
yieldValueCallName: exprInfo.returnValueCallName,
|
|
3195
|
-
expressionType: exprInfo.expressionType,
|
|
3196
|
-
operator: exprInfo.operator,
|
|
3197
|
-
leftSourceName: exprInfo.leftSourceName,
|
|
3198
|
-
rightSourceName: exprInfo.rightSourceName,
|
|
3199
|
-
consequentSourceName: exprInfo.consequentSourceName,
|
|
3200
|
-
alternateSourceName: exprInfo.alternateSourceName,
|
|
3201
|
-
object: exprInfo.object,
|
|
3202
|
-
property: exprInfo.property,
|
|
3203
|
-
computed: exprInfo.computed,
|
|
3204
|
-
objectSourceName: exprInfo.objectSourceName,
|
|
3205
|
-
expressionSourceNames: exprInfo.expressionSourceNames,
|
|
3206
|
-
unaryArgSourceName: exprInfo.unaryArgSourceName,
|
|
3207
|
-
};
|
|
3208
|
-
yieldExpressions.push(yieldInfo);
|
|
3209
|
-
},
|
|
3210
|
-
ForStatement: this.createLoopScopeHandler('for', 'for-loop', 'for', parentScopeId, module, scopes, loops, scopeCounterRef, loopCounterRef, scopeTracker, scopeIdStack, controlFlowState),
|
|
3211
|
-
ForInStatement: this.createLoopScopeHandler('for-in', 'for-in-loop', 'for-in', parentScopeId, module, scopes, loops, scopeCounterRef, loopCounterRef, scopeTracker, scopeIdStack, controlFlowState),
|
|
3212
|
-
ForOfStatement: this.createLoopScopeHandler('for-of', 'for-of-loop', 'for-of', parentScopeId, module, scopes, loops, scopeCounterRef, loopCounterRef, scopeTracker, scopeIdStack, controlFlowState),
|
|
3213
|
-
WhileStatement: this.createLoopScopeHandler('while', 'while-loop', 'while', parentScopeId, module, scopes, loops, scopeCounterRef, loopCounterRef, scopeTracker, scopeIdStack, controlFlowState),
|
|
3214
|
-
DoWhileStatement: this.createLoopScopeHandler('do-while', 'do-while-loop', 'do-while', parentScopeId, module, scopes, loops, scopeCounterRef, loopCounterRef, scopeTracker, scopeIdStack, controlFlowState),
|
|
3215
|
-
// Phase 4 (REG-267): Now creates TRY_BLOCK, CATCH_BLOCK, FINALLY_BLOCK nodes
|
|
3216
|
-
TryStatement: this.createTryStatementHandler(parentScopeId, module, scopes, tryBlocks, catchBlocks, finallyBlocks, scopeCounterRef, tryBlockCounterRef, catchBlockCounterRef, finallyBlockCounterRef, scopeTracker, tryScopeMap, scopeIdStack, controlFlowState),
|
|
3217
|
-
CatchClause: this.createCatchClauseHandler(module, variableDeclarations, varDeclCounterRef, scopeTracker, tryScopeMap, scopeIdStack, controlFlowState),
|
|
3218
|
-
SwitchStatement: (switchPath) => {
|
|
3219
|
-
this.handleSwitchStatement(switchPath, parentScopeId, module, collections, scopeTracker, controlFlowState);
|
|
3220
|
-
},
|
|
3221
|
-
FunctionDeclaration: (funcDeclPath) => {
|
|
3222
|
-
const node = funcDeclPath.node;
|
|
3223
|
-
const funcName = node.id ? node.id.name : this.generateAnonymousName(scopeTracker);
|
|
3224
|
-
// Use semantic ID as primary ID when scopeTracker available
|
|
3225
|
-
const legacyId = `FUNCTION#${funcName}#${module.file}#${getLine(node)}:${getColumn(node)}:${functionCounterRef.value++}`;
|
|
3226
|
-
const functionId = scopeTracker
|
|
3227
|
-
? computeSemanticId('FUNCTION', funcName, scopeTracker.getContext())
|
|
3228
|
-
: legacyId;
|
|
3229
|
-
functions.push({
|
|
3230
|
-
id: functionId,
|
|
3231
|
-
type: 'FUNCTION',
|
|
3232
|
-
name: funcName,
|
|
3233
|
-
file: module.file,
|
|
3234
|
-
line: getLine(node),
|
|
3235
|
-
column: getColumn(node),
|
|
3236
|
-
async: node.async || false,
|
|
3237
|
-
generator: node.generator || false,
|
|
3238
|
-
parentScopeId
|
|
3239
|
-
});
|
|
3240
|
-
const nestedScopeId = `SCOPE#${funcName}:body#${module.file}#${getLine(node)}`;
|
|
3241
|
-
const closureSemanticId = this.generateSemanticId('closure', scopeTracker);
|
|
3242
|
-
scopes.push({
|
|
3243
|
-
id: nestedScopeId,
|
|
3244
|
-
type: 'SCOPE',
|
|
3245
|
-
scopeType: 'closure',
|
|
3246
|
-
name: `${funcName}:body`,
|
|
3247
|
-
semanticId: closureSemanticId,
|
|
3248
|
-
conditional: false,
|
|
3249
|
-
file: module.file,
|
|
3250
|
-
line: getLine(node),
|
|
3251
|
-
parentFunctionId: functionId,
|
|
3252
|
-
capturesFrom: parentScopeId
|
|
3253
|
-
});
|
|
3254
|
-
// Enter nested function scope for semantic ID generation
|
|
3255
|
-
if (scopeTracker) {
|
|
3256
|
-
scopeTracker.enterScope(funcName, 'function');
|
|
3257
|
-
}
|
|
3258
|
-
this.analyzeFunctionBody(funcDeclPath, nestedScopeId, module, collections);
|
|
3259
|
-
if (scopeTracker) {
|
|
3260
|
-
scopeTracker.exitScope();
|
|
3261
|
-
}
|
|
3262
|
-
funcDeclPath.skip();
|
|
3263
|
-
},
|
|
3264
|
-
FunctionExpression: (funcPath) => {
|
|
3265
|
-
const node = funcPath.node;
|
|
3266
|
-
const funcName = node.id ? node.id.name : this.generateAnonymousName(scopeTracker);
|
|
3267
|
-
// Use semantic ID as primary ID when scopeTracker available
|
|
3268
|
-
const legacyId = `FUNCTION#${funcName}#${module.file}#${getLine(node)}:${getColumn(node)}:${functionCounterRef.value++}`;
|
|
3269
|
-
const functionId = scopeTracker
|
|
3270
|
-
? computeSemanticId('FUNCTION', funcName, scopeTracker.getContext())
|
|
3271
|
-
: legacyId;
|
|
3272
|
-
functions.push({
|
|
3273
|
-
id: functionId,
|
|
3274
|
-
type: 'FUNCTION',
|
|
3275
|
-
name: funcName,
|
|
3276
|
-
file: module.file,
|
|
3277
|
-
line: getLine(node),
|
|
3278
|
-
column: getColumn(node),
|
|
3279
|
-
async: node.async || false,
|
|
3280
|
-
generator: node.generator || false,
|
|
3281
|
-
parentScopeId
|
|
3282
|
-
});
|
|
3283
|
-
const nestedScopeId = `SCOPE#${funcName}:body#${module.file}#${getLine(node)}`;
|
|
3284
|
-
const closureSemanticId = this.generateSemanticId('closure', scopeTracker);
|
|
3285
|
-
scopes.push({
|
|
3286
|
-
id: nestedScopeId,
|
|
3287
|
-
type: 'SCOPE',
|
|
3288
|
-
scopeType: 'closure',
|
|
3289
|
-
name: `${funcName}:body`,
|
|
3290
|
-
semanticId: closureSemanticId,
|
|
3291
|
-
conditional: false,
|
|
3292
|
-
file: module.file,
|
|
3293
|
-
line: getLine(node),
|
|
3294
|
-
parentFunctionId: functionId,
|
|
3295
|
-
capturesFrom: parentScopeId
|
|
3296
|
-
});
|
|
3297
|
-
// Enter nested function scope for semantic ID generation
|
|
3298
|
-
if (scopeTracker) {
|
|
3299
|
-
scopeTracker.enterScope(funcName, 'function');
|
|
3300
|
-
}
|
|
3301
|
-
this.analyzeFunctionBody(funcPath, nestedScopeId, module, collections);
|
|
3302
|
-
if (scopeTracker) {
|
|
3303
|
-
scopeTracker.exitScope();
|
|
3304
|
-
}
|
|
3305
|
-
funcPath.skip();
|
|
3306
|
-
},
|
|
3307
|
-
ArrowFunctionExpression: (arrowPath) => {
|
|
3308
|
-
const node = arrowPath.node;
|
|
3309
|
-
const line = getLine(node);
|
|
3310
|
-
const column = getColumn(node);
|
|
3311
|
-
// Определяем имя (anonymous если не присвоено переменной)
|
|
3312
|
-
const parent = arrowPath.parent;
|
|
3313
|
-
let funcName;
|
|
3314
|
-
if (t.isVariableDeclarator(parent) && t.isIdentifier(parent.id)) {
|
|
3315
|
-
funcName = parent.id.name;
|
|
3316
|
-
}
|
|
3317
|
-
else {
|
|
3318
|
-
// Используем scope-level счётчик для стабильного semanticId
|
|
3319
|
-
funcName = this.generateAnonymousName(scopeTracker);
|
|
3320
|
-
}
|
|
3321
|
-
// Use semantic ID as primary ID when scopeTracker available
|
|
3322
|
-
const legacyId = `FUNCTION#${funcName}:${line}:${column}:${functionCounterRef.value++}`;
|
|
3323
|
-
const functionId = scopeTracker
|
|
3324
|
-
? computeSemanticId('FUNCTION', funcName, scopeTracker.getContext())
|
|
3325
|
-
: legacyId;
|
|
3326
|
-
functions.push({
|
|
3327
|
-
id: functionId,
|
|
3328
|
-
type: 'FUNCTION',
|
|
3329
|
-
name: funcName,
|
|
3330
|
-
file: module.file,
|
|
3331
|
-
line,
|
|
3332
|
-
column,
|
|
3333
|
-
async: node.async || false,
|
|
3334
|
-
arrowFunction: true,
|
|
3335
|
-
parentScopeId
|
|
3336
|
-
});
|
|
3337
|
-
if (node.body.type === 'BlockStatement') {
|
|
3338
|
-
const nestedScopeId = `SCOPE#${funcName}:body#${module.file}#${line}`;
|
|
3339
|
-
const arrowSemanticId = this.generateSemanticId('arrow_body', scopeTracker);
|
|
3340
|
-
scopes.push({
|
|
3341
|
-
id: nestedScopeId,
|
|
3342
|
-
type: 'SCOPE',
|
|
3343
|
-
scopeType: 'arrow_body',
|
|
3344
|
-
name: `${funcName}:body`,
|
|
3345
|
-
semanticId: arrowSemanticId,
|
|
3346
|
-
conditional: false,
|
|
3347
|
-
file: module.file,
|
|
3348
|
-
line,
|
|
3349
|
-
parentFunctionId: functionId,
|
|
3350
|
-
capturesFrom: parentScopeId
|
|
3351
|
-
});
|
|
3352
|
-
// Enter arrow function scope for semantic ID generation
|
|
3353
|
-
if (scopeTracker) {
|
|
3354
|
-
scopeTracker.enterScope(funcName, 'arrow');
|
|
3355
|
-
}
|
|
3356
|
-
this.analyzeFunctionBody(arrowPath, nestedScopeId, module, collections);
|
|
3357
|
-
if (scopeTracker) {
|
|
3358
|
-
scopeTracker.exitScope();
|
|
3359
|
-
}
|
|
3360
|
-
}
|
|
3361
|
-
else {
|
|
3362
|
-
// Arrow function with expression body (implicit return)
|
|
3363
|
-
// e.g., x => x * 2, () => 42
|
|
3364
|
-
const bodyExpr = node.body;
|
|
3365
|
-
const bodyLine = getLine(bodyExpr);
|
|
3366
|
-
const bodyColumn = getColumn(bodyExpr);
|
|
3367
|
-
// Extract expression-specific info using shared method
|
|
3368
|
-
const exprInfo = this.extractReturnExpressionInfo(bodyExpr, module, literals, literalCounterRef, line, column, 'implicit_return');
|
|
3369
|
-
const returnInfo = {
|
|
3370
|
-
parentFunctionId: functionId,
|
|
3371
|
-
file: module.file,
|
|
3372
|
-
line: bodyLine,
|
|
3373
|
-
column: bodyColumn,
|
|
3374
|
-
returnValueType: 'NONE',
|
|
3375
|
-
isImplicitReturn: true,
|
|
3376
|
-
...exprInfo,
|
|
3377
|
-
};
|
|
3378
|
-
returnStatements.push(returnInfo);
|
|
3379
|
-
}
|
|
3380
|
-
arrowPath.skip();
|
|
3381
|
-
},
|
|
3382
|
-
UpdateExpression: (updatePath) => {
|
|
3383
|
-
const updateNode = updatePath.node;
|
|
3384
|
-
// REG-288/REG-312: Collect update expression info for graph building
|
|
3385
|
-
this.collectUpdateExpression(updateNode, module, updateExpressions, getCurrentScopeId(), scopeTracker);
|
|
3386
|
-
// Legacy behavior: update scope.modifies for IDENTIFIER targets
|
|
3387
|
-
if (updateNode.argument.type === 'Identifier') {
|
|
3388
|
-
const varName = updateNode.argument.name;
|
|
3389
|
-
// Find variable by name - could be from parent scope or declarations
|
|
3390
|
-
const fromParentScope = Array.from(parentScopeVariables).find(v => v.name === varName);
|
|
3391
|
-
const fromDeclarations = variableDeclarations.find(v => v.name === varName);
|
|
3392
|
-
const variable = fromParentScope ?? fromDeclarations;
|
|
3393
|
-
if (variable) {
|
|
3394
|
-
const scope = scopes.find(s => s.id === parentScopeId);
|
|
3395
|
-
if (scope) {
|
|
3396
|
-
if (!scope.modifies)
|
|
3397
|
-
scope.modifies = [];
|
|
3398
|
-
scope.modifies.push({
|
|
3399
|
-
variableId: variable.id,
|
|
3400
|
-
variableName: varName,
|
|
3401
|
-
line: getLine(updateNode)
|
|
3402
|
-
});
|
|
3403
|
-
}
|
|
3404
|
-
}
|
|
3405
|
-
}
|
|
3406
|
-
},
|
|
3407
|
-
// IF statements - создаём условные scope и обходим содержимое для CALL узлов
|
|
3408
|
-
// Phase 3 (REG-267): Now creates BRANCH nodes with branchType='if'
|
|
3409
|
-
IfStatement: this.createIfStatementHandler(parentScopeId, module, scopes, branches, ifScopeCounterRef, branchCounterRef, scopeTracker, collections.code ?? '', ifElseScopeMap, scopeIdStack, controlFlowState, this.countLogicalOperators.bind(this)),
|
|
3410
|
-
// Ternary expressions (REG-287): Creates BRANCH nodes with branchType='ternary'
|
|
3411
|
-
ConditionalExpression: this.createConditionalExpressionHandler(parentScopeId, module, branches, branchCounterRef, scopeTracker, scopeIdStack, controlFlowState, this.countLogicalOperators.bind(this)),
|
|
3412
|
-
// Track when we enter the alternate (else) block of an IfStatement
|
|
3413
|
-
BlockStatement: this.createBlockStatementHandler(scopeTracker, ifElseScopeMap, tryScopeMap, scopeIdStack),
|
|
3414
|
-
// Function call expressions
|
|
3415
|
-
CallExpression: (callPath) => {
|
|
3416
|
-
// REG-311: Detect isAwaited (parent is AwaitExpression)
|
|
3417
|
-
const parent = callPath.parentPath;
|
|
3418
|
-
const isAwaited = parent?.isAwaitExpression() ?? false;
|
|
3419
|
-
// REG-311: Detect isInsideTry (O(1) via depth counter)
|
|
3420
|
-
const isInsideTry = controlFlowState.tryBlockDepth > 0;
|
|
3421
|
-
this.handleCallExpression(callPath.node, processedCallSites, processedMethodCalls, callSites, methodCalls, module, callSiteCounterRef, scopeTracker, getCurrentScopeId(), collections, isAwaited, isInsideTry);
|
|
3422
|
-
// REG-334: Check for resolve/reject calls inside Promise executors
|
|
3423
|
-
const callNode = callPath.node;
|
|
3424
|
-
if (t.isIdentifier(callNode.callee)) {
|
|
3425
|
-
const calleeName = callNode.callee.name;
|
|
3426
|
-
// Walk up function parents to find Promise executor context
|
|
3427
|
-
// This handles nested callbacks like: new Promise((resolve) => { db.query((err, data) => { resolve(data); }); });
|
|
3428
|
-
let funcParent = callPath.getFunctionParent();
|
|
3429
|
-
while (funcParent) {
|
|
3430
|
-
const funcNode = funcParent.node;
|
|
3431
|
-
const funcKey = `${funcNode.start}:${funcNode.end}`;
|
|
3432
|
-
const context = promiseExecutorContexts.get(funcKey);
|
|
3433
|
-
if (context) {
|
|
3434
|
-
const isResolve = calleeName === context.resolveName;
|
|
3435
|
-
const isReject = calleeName === context.rejectName;
|
|
3436
|
-
if (isResolve || isReject) {
|
|
3437
|
-
// Find the CALL node ID for this resolve/reject call
|
|
3438
|
-
// It was just added by handleCallExpression
|
|
3439
|
-
const callLine = getLine(callNode);
|
|
3440
|
-
const callColumn = getColumn(callNode);
|
|
3441
|
-
// Find matching call site that was just added
|
|
3442
|
-
const resolveCall = callSites.find(cs => cs.name === calleeName &&
|
|
3443
|
-
cs.file === module.file &&
|
|
3444
|
-
cs.line === callLine &&
|
|
3445
|
-
cs.column === callColumn);
|
|
3446
|
-
if (resolveCall) {
|
|
3447
|
-
promiseResolutions.push({
|
|
3448
|
-
callId: resolveCall.id,
|
|
3449
|
-
constructorCallId: context.constructorCallId,
|
|
3450
|
-
isReject,
|
|
3451
|
-
file: module.file,
|
|
3452
|
-
line: callLine
|
|
3453
|
-
});
|
|
3454
|
-
// REG-334: Collect arguments for resolve/reject calls
|
|
3455
|
-
// This enables traceValues to follow PASSES_ARGUMENT edges
|
|
3456
|
-
if (!collections.callArguments) {
|
|
3457
|
-
collections.callArguments = [];
|
|
3458
|
-
}
|
|
3459
|
-
const callArgumentsArr = collections.callArguments;
|
|
3460
|
-
// Process arguments (typically just one: resolve(value))
|
|
3461
|
-
callNode.arguments.forEach((arg, argIndex) => {
|
|
3462
|
-
const argInfo = {
|
|
3463
|
-
callId: resolveCall.id,
|
|
3464
|
-
argIndex,
|
|
3465
|
-
file: module.file,
|
|
3466
|
-
line: getLine(arg),
|
|
3467
|
-
column: getColumn(arg)
|
|
3468
|
-
};
|
|
3469
|
-
// Handle different argument types
|
|
3470
|
-
if (t.isIdentifier(arg)) {
|
|
3471
|
-
argInfo.targetType = 'VARIABLE';
|
|
3472
|
-
argInfo.targetName = arg.name;
|
|
3473
|
-
}
|
|
3474
|
-
else if (t.isLiteral(arg) && !t.isTemplateLiteral(arg)) {
|
|
3475
|
-
// Create LITERAL node for the argument value
|
|
3476
|
-
const literalValue = ExpressionEvaluator.extractLiteralValue(arg);
|
|
3477
|
-
if (literalValue !== null) {
|
|
3478
|
-
const argLine = getLine(arg);
|
|
3479
|
-
const argColumn = getColumn(arg);
|
|
3480
|
-
const literalId = `LITERAL#arg${argIndex}#${module.file}#${argLine}:${argColumn}:${literalCounterRef.value++}`;
|
|
3481
|
-
literals.push({
|
|
3482
|
-
id: literalId,
|
|
3483
|
-
type: 'LITERAL',
|
|
3484
|
-
value: literalValue,
|
|
3485
|
-
valueType: typeof literalValue,
|
|
3486
|
-
file: module.file,
|
|
3487
|
-
line: argLine,
|
|
3488
|
-
column: argColumn,
|
|
3489
|
-
parentCallId: resolveCall.id,
|
|
3490
|
-
argIndex
|
|
3491
|
-
});
|
|
3492
|
-
argInfo.targetType = 'LITERAL';
|
|
3493
|
-
argInfo.targetId = literalId;
|
|
3494
|
-
argInfo.literalValue = literalValue;
|
|
3495
|
-
}
|
|
3496
|
-
}
|
|
3497
|
-
else if (t.isCallExpression(arg)) {
|
|
3498
|
-
argInfo.targetType = 'CALL';
|
|
3499
|
-
argInfo.nestedCallLine = getLine(arg);
|
|
3500
|
-
argInfo.nestedCallColumn = getColumn(arg);
|
|
3501
|
-
}
|
|
3502
|
-
else {
|
|
3503
|
-
argInfo.targetType = 'EXPRESSION';
|
|
3504
|
-
argInfo.expressionType = arg.type;
|
|
3505
|
-
}
|
|
3506
|
-
callArgumentsArr.push(argInfo);
|
|
3507
|
-
});
|
|
3508
|
-
}
|
|
3509
|
-
break; // Found context, stop searching
|
|
3510
|
-
}
|
|
3511
|
-
}
|
|
3512
|
-
funcParent = funcParent.getFunctionParent();
|
|
3513
|
-
}
|
|
3514
|
-
// REG-311: Detect executor_reject pattern - reject(new Error()) inside Promise executor
|
|
3515
|
-
// Walk up to find Promise executor context and check if this is reject call with NewExpression arg
|
|
3516
|
-
funcParent = callPath.getFunctionParent();
|
|
3517
|
-
while (funcParent && currentFunctionId) {
|
|
3518
|
-
const funcNode = funcParent.node;
|
|
3519
|
-
const funcKey = `${funcNode.start}:${funcNode.end}`;
|
|
3520
|
-
const context = promiseExecutorContexts.get(funcKey);
|
|
3521
|
-
if (context && calleeName === context.rejectName && callNode.arguments.length > 0) {
|
|
3522
|
-
// REG-311: Use the creator function's ID (the function that created the Promise),
|
|
3523
|
-
// not the executor's ID
|
|
3524
|
-
const targetFunctionId = context.creatorFunctionId || currentFunctionId;
|
|
3525
|
-
const arg = callNode.arguments[0];
|
|
3526
|
-
const callLine = getLine(callNode);
|
|
3527
|
-
const callColumn = getColumn(callNode);
|
|
3528
|
-
// Case 1: reject(new Error())
|
|
3529
|
-
if (t.isNewExpression(arg) && t.isIdentifier(arg.callee)) {
|
|
3530
|
-
rejectionPatterns.push({
|
|
3531
|
-
functionId: targetFunctionId,
|
|
3532
|
-
errorClassName: arg.callee.name,
|
|
3533
|
-
rejectionType: 'executor_reject',
|
|
3534
|
-
file: module.file,
|
|
3535
|
-
line: callLine,
|
|
3536
|
-
column: callColumn
|
|
3537
|
-
});
|
|
3538
|
-
}
|
|
3539
|
-
// Case 2: reject(err) where err is variable
|
|
3540
|
-
else if (t.isIdentifier(arg)) {
|
|
3541
|
-
const varName = arg.name;
|
|
3542
|
-
// Check if it's a parameter of ANY containing function (executor, outer, etc.)
|
|
3543
|
-
// Walk up the function chain to find if varName is a parameter
|
|
3544
|
-
let isParameter = false;
|
|
3545
|
-
let checkParent = funcParent;
|
|
3546
|
-
while (checkParent) {
|
|
3547
|
-
if (t.isFunction(checkParent.node)) {
|
|
3548
|
-
if (checkParent.node.params.some(p => t.isIdentifier(p) && p.name === varName)) {
|
|
3549
|
-
isParameter = true;
|
|
3550
|
-
break;
|
|
3551
|
-
}
|
|
3552
|
-
}
|
|
3553
|
-
checkParent = checkParent.getFunctionParent();
|
|
3554
|
-
}
|
|
3555
|
-
if (isParameter) {
|
|
3556
|
-
rejectionPatterns.push({
|
|
3557
|
-
functionId: targetFunctionId,
|
|
3558
|
-
errorClassName: null,
|
|
3559
|
-
rejectionType: 'variable_parameter',
|
|
3560
|
-
file: module.file,
|
|
3561
|
-
line: callLine,
|
|
3562
|
-
column: callColumn,
|
|
3563
|
-
sourceVariableName: varName
|
|
3564
|
-
});
|
|
3565
|
-
}
|
|
3566
|
-
else {
|
|
3567
|
-
// Try micro-trace
|
|
3568
|
-
const { errorClassName, tracePath } = this.microTraceToErrorClass(varName, funcParent, variableDeclarations);
|
|
3569
|
-
rejectionPatterns.push({
|
|
3570
|
-
functionId: targetFunctionId,
|
|
3571
|
-
errorClassName,
|
|
3572
|
-
rejectionType: errorClassName ? 'variable_traced' : 'variable_unknown',
|
|
3573
|
-
file: module.file,
|
|
3574
|
-
line: callLine,
|
|
3575
|
-
column: callColumn,
|
|
3576
|
-
sourceVariableName: varName,
|
|
3577
|
-
tracePath
|
|
3578
|
-
});
|
|
3579
|
-
}
|
|
3580
|
-
}
|
|
3581
|
-
break;
|
|
3582
|
-
}
|
|
3583
|
-
funcParent = funcParent.getFunctionParent();
|
|
3584
|
-
}
|
|
3585
|
-
}
|
|
3586
|
-
// REG-311: Detect Promise.reject(new Error()) pattern
|
|
3587
|
-
if (t.isMemberExpression(callNode.callee) && currentFunctionId) {
|
|
3588
|
-
const memberCallee = callNode.callee;
|
|
3589
|
-
if (t.isIdentifier(memberCallee.object) &&
|
|
3590
|
-
memberCallee.object.name === 'Promise' &&
|
|
3591
|
-
t.isIdentifier(memberCallee.property) &&
|
|
3592
|
-
memberCallee.property.name === 'reject' &&
|
|
3593
|
-
callNode.arguments.length > 0) {
|
|
3594
|
-
const arg = callNode.arguments[0];
|
|
3595
|
-
const callLine = getLine(callNode);
|
|
3596
|
-
const callColumn = getColumn(callNode);
|
|
3597
|
-
// Case 1: Promise.reject(new Error())
|
|
3598
|
-
if (t.isNewExpression(arg) && t.isIdentifier(arg.callee)) {
|
|
3599
|
-
rejectionPatterns.push({
|
|
3600
|
-
functionId: currentFunctionId,
|
|
3601
|
-
errorClassName: arg.callee.name,
|
|
3602
|
-
rejectionType: 'promise_reject',
|
|
3603
|
-
file: module.file,
|
|
3604
|
-
line: callLine,
|
|
3605
|
-
column: callColumn
|
|
3606
|
-
});
|
|
3607
|
-
}
|
|
3608
|
-
// Case 2: Promise.reject(err) where err is variable
|
|
3609
|
-
else if (t.isIdentifier(arg)) {
|
|
3610
|
-
const varName = arg.name;
|
|
3611
|
-
// Check if it's a parameter of containing function
|
|
3612
|
-
const isParameter = functionNode
|
|
3613
|
-
? functionNode.params.some(param => t.isIdentifier(param) && param.name === varName)
|
|
3614
|
-
: false;
|
|
3615
|
-
if (isParameter) {
|
|
3616
|
-
rejectionPatterns.push({
|
|
3617
|
-
functionId: currentFunctionId,
|
|
3618
|
-
errorClassName: null,
|
|
3619
|
-
rejectionType: 'variable_parameter',
|
|
3620
|
-
file: module.file,
|
|
3621
|
-
line: callLine,
|
|
3622
|
-
column: callColumn,
|
|
3623
|
-
sourceVariableName: varName
|
|
3624
|
-
});
|
|
3625
|
-
}
|
|
3626
|
-
else {
|
|
3627
|
-
// Try micro-trace
|
|
3628
|
-
if (!functionPath) {
|
|
3629
|
-
rejectionPatterns.push({
|
|
3630
|
-
functionId: currentFunctionId,
|
|
3631
|
-
errorClassName: null,
|
|
3632
|
-
rejectionType: 'variable_unknown',
|
|
3633
|
-
file: module.file,
|
|
3634
|
-
line: callLine,
|
|
3635
|
-
column: callColumn,
|
|
3636
|
-
sourceVariableName: varName,
|
|
3637
|
-
tracePath: [varName]
|
|
3638
|
-
});
|
|
3639
|
-
return;
|
|
3640
|
-
}
|
|
3641
|
-
const { errorClassName, tracePath } = this.microTraceToErrorClass(varName, functionPath, variableDeclarations);
|
|
3642
|
-
rejectionPatterns.push({
|
|
3643
|
-
functionId: currentFunctionId,
|
|
3644
|
-
errorClassName,
|
|
3645
|
-
rejectionType: errorClassName ? 'variable_traced' : 'variable_unknown',
|
|
3646
|
-
file: module.file,
|
|
3647
|
-
line: callLine,
|
|
3648
|
-
column: callColumn,
|
|
3649
|
-
sourceVariableName: varName,
|
|
3650
|
-
tracePath
|
|
3651
|
-
});
|
|
3652
|
-
}
|
|
3653
|
-
}
|
|
3654
|
-
}
|
|
3655
|
-
}
|
|
3656
|
-
},
|
|
3657
|
-
// NewExpression (constructor calls)
|
|
3658
|
-
NewExpression: (newPath) => {
|
|
3659
|
-
const newNode = newPath.node;
|
|
3660
|
-
const nodeKey = `new:${newNode.start}:${newNode.end}`;
|
|
3661
|
-
// Determine className from callee
|
|
3662
|
-
let className = null;
|
|
3663
|
-
if (newNode.callee.type === 'Identifier') {
|
|
3664
|
-
className = newNode.callee.name;
|
|
3665
|
-
}
|
|
3666
|
-
else if (newNode.callee.type === 'MemberExpression' && newNode.callee.property.type === 'Identifier') {
|
|
3667
|
-
className = newNode.callee.property.name;
|
|
3668
|
-
}
|
|
3669
|
-
// Create CONSTRUCTOR_CALL node (always, for all NewExpressions)
|
|
3670
|
-
if (className) {
|
|
3671
|
-
const constructorKey = `constructor:${nodeKey}`;
|
|
3672
|
-
if (!processedCallSites.has(constructorKey)) {
|
|
3673
|
-
processedCallSites.add(constructorKey);
|
|
3674
|
-
const line = getLine(newNode);
|
|
3675
|
-
const column = getColumn(newNode);
|
|
3676
|
-
const constructorCallId = ConstructorCallNode.generateId(className, module.file, line, column);
|
|
3677
|
-
const isBuiltin = ConstructorCallNode.isBuiltinConstructor(className);
|
|
3678
|
-
constructorCalls.push({
|
|
3679
|
-
id: constructorCallId,
|
|
3680
|
-
type: 'CONSTRUCTOR_CALL',
|
|
3681
|
-
className,
|
|
3682
|
-
isBuiltin,
|
|
3683
|
-
file: module.file,
|
|
3684
|
-
line,
|
|
3685
|
-
column
|
|
3686
|
-
});
|
|
3687
|
-
// REG-334: If this is Promise constructor with executor callback,
|
|
3688
|
-
// register the context for resolve/reject detection
|
|
3689
|
-
if (className === 'Promise' && newNode.arguments.length > 0) {
|
|
3690
|
-
const executorArg = newNode.arguments[0];
|
|
3691
|
-
// Only handle inline function expressions (not variable references)
|
|
3692
|
-
if (t.isArrowFunctionExpression(executorArg) || t.isFunctionExpression(executorArg)) {
|
|
3693
|
-
// Extract resolve/reject parameter names
|
|
3694
|
-
let resolveName;
|
|
3695
|
-
let rejectName;
|
|
3696
|
-
if (executorArg.params.length > 0 && t.isIdentifier(executorArg.params[0])) {
|
|
3697
|
-
resolveName = executorArg.params[0].name;
|
|
3698
|
-
}
|
|
3699
|
-
if (executorArg.params.length > 1 && t.isIdentifier(executorArg.params[1])) {
|
|
3700
|
-
rejectName = executorArg.params[1].name;
|
|
3701
|
-
}
|
|
3702
|
-
if (resolveName) {
|
|
3703
|
-
// Key by function node position to allow nested Promise detection
|
|
3704
|
-
const funcKey = `${executorArg.start}:${executorArg.end}`;
|
|
3705
|
-
promiseExecutorContexts.set(funcKey, {
|
|
3706
|
-
constructorCallId,
|
|
3707
|
-
resolveName,
|
|
3708
|
-
rejectName,
|
|
3709
|
-
file: module.file,
|
|
3710
|
-
line,
|
|
3711
|
-
// REG-311: Store the ID of the function that creates the Promise
|
|
3712
|
-
creatorFunctionId: currentFunctionId || undefined
|
|
3713
|
-
});
|
|
3714
|
-
}
|
|
3715
|
-
}
|
|
3716
|
-
}
|
|
3717
|
-
}
|
|
3718
|
-
}
|
|
3719
|
-
// Handle simple constructor: new Foo()
|
|
3720
|
-
if (newNode.callee.type === 'Identifier') {
|
|
3721
|
-
if (processedCallSites.has(nodeKey)) {
|
|
3722
|
-
return;
|
|
3723
|
-
}
|
|
3724
|
-
processedCallSites.add(nodeKey);
|
|
3725
|
-
// Generate semantic ID (primary) or legacy ID (fallback)
|
|
3726
|
-
const constructorName = newNode.callee.name;
|
|
3727
|
-
const legacyId = `CALL#new:${constructorName}#${module.file}#${getLine(newNode)}:${getColumn(newNode)}:${callSiteCounterRef.value++}`;
|
|
3728
|
-
let newCallId = legacyId;
|
|
3729
|
-
if (scopeTracker) {
|
|
3730
|
-
const discriminator = scopeTracker.getItemCounter(`CALL:new:${constructorName}`);
|
|
3731
|
-
newCallId = computeSemanticId('CALL', `new:${constructorName}`, scopeTracker.getContext(), { discriminator });
|
|
3732
|
-
}
|
|
3733
|
-
callSites.push({
|
|
3734
|
-
id: newCallId,
|
|
3735
|
-
type: 'CALL',
|
|
3736
|
-
name: constructorName,
|
|
3737
|
-
file: module.file,
|
|
3738
|
-
line: getLine(newNode),
|
|
3739
|
-
parentScopeId: getCurrentScopeId(),
|
|
3740
|
-
targetFunctionName: constructorName,
|
|
3741
|
-
isNew: true
|
|
3742
|
-
});
|
|
3743
|
-
}
|
|
3744
|
-
// Handle namespaced constructor: new ns.Constructor()
|
|
3745
|
-
else if (newNode.callee.type === 'MemberExpression') {
|
|
3746
|
-
const memberCallee = newNode.callee;
|
|
3747
|
-
const object = memberCallee.object;
|
|
3748
|
-
const property = memberCallee.property;
|
|
3749
|
-
if (object.type === 'Identifier' && property.type === 'Identifier') {
|
|
3750
|
-
if (processedMethodCalls.has(nodeKey)) {
|
|
3751
|
-
return;
|
|
3752
|
-
}
|
|
3753
|
-
processedMethodCalls.add(nodeKey);
|
|
3754
|
-
const objectName = object.name;
|
|
3755
|
-
const constructorName = property.name;
|
|
3756
|
-
const fullName = `${objectName}.${constructorName}`;
|
|
3757
|
-
// Generate semantic ID for method-style constructor call
|
|
3758
|
-
const legacyId = `CALL#new:${fullName}#${module.file}#${getLine(newNode)}:${getColumn(newNode)}:${callSiteCounterRef.value++}`;
|
|
3759
|
-
let newMethodCallId = legacyId;
|
|
3760
|
-
if (scopeTracker) {
|
|
3761
|
-
const discriminator = scopeTracker.getItemCounter(`CALL:new:${fullName}`);
|
|
3762
|
-
newMethodCallId = computeSemanticId('CALL', `new:${fullName}`, scopeTracker.getContext(), { discriminator });
|
|
3763
|
-
}
|
|
3764
|
-
methodCalls.push({
|
|
3765
|
-
id: newMethodCallId,
|
|
3766
|
-
type: 'CALL',
|
|
3767
|
-
name: fullName,
|
|
3768
|
-
object: objectName,
|
|
3769
|
-
method: constructorName,
|
|
3770
|
-
file: module.file,
|
|
3771
|
-
line: getLine(newNode),
|
|
3772
|
-
column: getColumn(newNode),
|
|
3773
|
-
parentScopeId: getCurrentScopeId(),
|
|
3774
|
-
isNew: true
|
|
3775
|
-
});
|
|
3776
|
-
}
|
|
3777
|
-
}
|
|
3778
|
-
},
|
|
3779
|
-
// Property access expressions (REG-395)
|
|
3780
|
-
// Shared handler for both MemberExpression and OptionalMemberExpression
|
|
3781
|
-
MemberExpression: (memberPath) => {
|
|
3782
|
-
// Initialize collections if needed
|
|
3783
|
-
if (!collections.propertyAccesses) {
|
|
3784
|
-
collections.propertyAccesses = [];
|
|
3785
|
-
}
|
|
3786
|
-
if (!collections.propertyAccessCounterRef) {
|
|
3787
|
-
collections.propertyAccessCounterRef = { value: 0 };
|
|
3788
|
-
}
|
|
3789
|
-
PropertyAccessVisitor.extractPropertyAccesses(memberPath, memberPath.node, module, collections.propertyAccesses, collections.propertyAccessCounterRef, scopeTracker, currentFunctionId || getCurrentScopeId());
|
|
3790
|
-
},
|
|
3791
|
-
// OptionalMemberExpression: obj?.prop (same logic as MemberExpression)
|
|
3792
|
-
OptionalMemberExpression: (memberPath) => {
|
|
3793
|
-
// Initialize collections if needed
|
|
3794
|
-
if (!collections.propertyAccesses) {
|
|
3795
|
-
collections.propertyAccesses = [];
|
|
3796
|
-
}
|
|
3797
|
-
if (!collections.propertyAccessCounterRef) {
|
|
3798
|
-
collections.propertyAccessCounterRef = { value: 0 };
|
|
3799
|
-
}
|
|
3800
|
-
PropertyAccessVisitor.extractPropertyAccesses(memberPath, memberPath.node, module, collections.propertyAccesses, collections.propertyAccessCounterRef, scopeTracker, currentFunctionId || getCurrentScopeId());
|
|
3801
|
-
}
|
|
3802
|
-
});
|
|
3803
|
-
// REG-311: Second pass - collect CATCHES_FROM info for try/catch blocks
|
|
3804
|
-
// This links catch blocks to exception sources in their corresponding try blocks
|
|
3805
|
-
if (functionPath) {
|
|
3806
|
-
this.collectCatchesFromInfo(functionPath, catchBlocks, callSites, methodCalls, constructorCalls, catchesFromInfos, module);
|
|
3807
|
-
}
|
|
3808
|
-
// Phase 6 (REG-267): Attach control flow metadata to the function node
|
|
3809
|
-
if (matchingFunction) {
|
|
3810
|
-
const cyclomaticComplexity = 1 +
|
|
3811
|
-
controlFlowState.branchCount +
|
|
3812
|
-
controlFlowState.loopCount +
|
|
3813
|
-
controlFlowState.caseCount +
|
|
3814
|
-
controlFlowState.logicalOpCount;
|
|
3815
|
-
// REG-311: Collect rejection info for this function
|
|
3816
|
-
const functionRejectionPatterns = rejectionPatterns.filter(p => p.functionId === matchingFunction.id);
|
|
3817
|
-
const canReject = functionRejectionPatterns.length > 0;
|
|
3818
|
-
const hasAsyncThrow = functionRejectionPatterns.some(p => p.rejectionType === 'async_throw');
|
|
3819
|
-
const rejectedBuiltinErrors = [...new Set(functionRejectionPatterns
|
|
3820
|
-
.filter(p => p.errorClassName !== null)
|
|
3821
|
-
.map(p => p.errorClassName))];
|
|
3822
|
-
matchingFunction.controlFlow = {
|
|
3823
|
-
hasBranches: controlFlowState.branchCount > 0,
|
|
3824
|
-
hasLoops: controlFlowState.loopCount > 0,
|
|
3825
|
-
hasTryCatch: controlFlowState.hasTryCatch,
|
|
3826
|
-
hasEarlyReturn: controlFlowState.hasEarlyReturn,
|
|
3827
|
-
hasThrow: controlFlowState.hasThrow,
|
|
3828
|
-
cyclomaticComplexity,
|
|
3829
|
-
// REG-311: Async error tracking
|
|
3830
|
-
canReject,
|
|
3831
|
-
hasAsyncThrow,
|
|
3832
|
-
rejectedBuiltinErrors: rejectedBuiltinErrors.length > 0 ? rejectedBuiltinErrors : undefined
|
|
3833
|
-
};
|
|
3834
|
-
}
|
|
3835
|
-
}
|
|
3836
|
-
/**
|
|
3837
|
-
* Handle CallExpression nodes: direct function calls (greet(), main())
|
|
3838
|
-
* and method calls (obj.method(), data.process()).
|
|
3839
|
-
*
|
|
3840
|
-
* Handles:
|
|
3841
|
-
* - Direct function calls (Identifier callee) → callSites collection
|
|
3842
|
-
* - Method calls (MemberExpression callee) → methodCalls collection
|
|
3843
|
-
* - Array mutation detection (push, unshift, splice)
|
|
3844
|
-
* - Object.assign() detection
|
|
3845
|
-
* - REG-311: isAwaited and isInsideTry metadata on CALL nodes
|
|
3846
|
-
*
|
|
3847
|
-
* @param callNode - The call expression AST node
|
|
3848
|
-
* @param processedCallSites - Set of already processed call site keys to avoid duplicates
|
|
3849
|
-
* @param processedMethodCalls - Set of already processed method call keys to avoid duplicates
|
|
3850
|
-
* @param callSites - Collection for direct function calls
|
|
3851
|
-
* @param methodCalls - Collection for method calls
|
|
3852
|
-
* @param module - Current module being analyzed
|
|
3853
|
-
* @param callSiteCounterRef - Counter for legacy ID generation
|
|
3854
|
-
* @param scopeTracker - Optional scope tracker for semantic ID generation
|
|
3855
|
-
* @param parentScopeId - ID of the parent scope containing this call
|
|
3856
|
-
* @param collections - Full collections object for array/object mutations
|
|
3857
|
-
* @param isAwaited - REG-311: true if wrapped in await expression
|
|
3858
|
-
* @param isInsideTry - REG-311: true if inside try block
|
|
3859
|
-
*/
|
|
3860
|
-
handleCallExpression(callNode, processedCallSites, processedMethodCalls, callSites, methodCalls, module, callSiteCounterRef, scopeTracker, parentScopeId, collections, isAwaited = false, isInsideTry = false) {
|
|
2213
|
+
handleCallExpression(callNode, processedCallSites, processedMethodCalls, callSites, methodCalls, module, callSiteCounterRef, scopeTracker, parentScopeId, collections, isAwaited = false, isInsideTry = false, isInsideLoop = false) {
|
|
3861
2214
|
// Handle direct function calls (greet(), main())
|
|
3862
2215
|
if (callNode.callee.type === 'Identifier') {
|
|
3863
2216
|
const nodeKey = `${callNode.start}:${callNode.end}`;
|
|
@@ -3884,7 +2237,9 @@ export class JSASTAnalyzer extends Plugin {
|
|
|
3884
2237
|
targetFunctionName: calleeName,
|
|
3885
2238
|
// REG-311: Async error tracking metadata
|
|
3886
2239
|
isAwaited,
|
|
3887
|
-
isInsideTry
|
|
2240
|
+
isInsideTry,
|
|
2241
|
+
// REG-298: Await-in-loop detection
|
|
2242
|
+
...(isAwaited && isInsideLoop ? { isInsideLoop } : {})
|
|
3888
2243
|
});
|
|
3889
2244
|
}
|
|
3890
2245
|
// Handle method calls (obj.method(), data.process())
|
|
@@ -3924,8 +2279,14 @@ export class JSASTAnalyzer extends Plugin {
|
|
|
3924
2279
|
// REG-311: Async error tracking metadata
|
|
3925
2280
|
isAwaited,
|
|
3926
2281
|
isInsideTry,
|
|
2282
|
+
// REG-298: Await-in-loop detection
|
|
2283
|
+
...(isAwaited && isInsideLoop ? { isInsideLoop } : {}),
|
|
3927
2284
|
isMethodCall: true
|
|
3928
2285
|
});
|
|
2286
|
+
// REG-400: Extract arguments for method calls (enables callback resolution)
|
|
2287
|
+
if (callNode.arguments.length > 0) {
|
|
2288
|
+
this.extractMethodCallArguments(callNode, methodCallId, module, collections);
|
|
2289
|
+
}
|
|
3929
2290
|
// Check for array mutation methods (push, unshift, splice)
|
|
3930
2291
|
const ARRAY_MUTATION_METHODS = ['push', 'unshift', 'splice'];
|
|
3931
2292
|
if (ARRAY_MUTATION_METHODS.includes(methodName)) {
|
|
@@ -3998,11 +2359,108 @@ export class JSASTAnalyzer extends Plugin {
|
|
|
3998
2359
|
parentScopeId,
|
|
3999
2360
|
isMethodCall: true
|
|
4000
2361
|
});
|
|
2362
|
+
// REG-400: Extract arguments for nested method calls (enables callback resolution)
|
|
2363
|
+
if (callNode.arguments.length > 0) {
|
|
2364
|
+
this.extractMethodCallArguments(callNode, methodCallId, module, collections);
|
|
2365
|
+
}
|
|
4001
2366
|
}
|
|
4002
2367
|
}
|
|
4003
2368
|
}
|
|
4004
2369
|
}
|
|
4005
2370
|
}
|
|
2371
|
+
/**
|
|
2372
|
+
* REG-400: Extract arguments from method call nodes inside function bodies.
|
|
2373
|
+
* Populates callArguments collection so GraphBuilder.bufferArgumentEdges can
|
|
2374
|
+
* create PASSES_ARGUMENT and callback CALLS edges.
|
|
2375
|
+
*
|
|
2376
|
+
* This mirrors CallExpressionVisitor.extractArguments but is simplified —
|
|
2377
|
+
* handles Identifier, Literal, CallExpression, and Expression types.
|
|
2378
|
+
*/
|
|
2379
|
+
extractMethodCallArguments(callNode, methodCallId, module, collections) {
|
|
2380
|
+
if (!collections.callArguments) {
|
|
2381
|
+
collections.callArguments = [];
|
|
2382
|
+
}
|
|
2383
|
+
const callArguments = collections.callArguments;
|
|
2384
|
+
const literals = (collections.literals ?? []);
|
|
2385
|
+
const literalCounterRef = (collections.literalCounterRef ?? { value: 0 });
|
|
2386
|
+
callNode.arguments.forEach((arg, argIndex) => {
|
|
2387
|
+
const argInfo = {
|
|
2388
|
+
callId: methodCallId,
|
|
2389
|
+
argIndex,
|
|
2390
|
+
file: module.file,
|
|
2391
|
+
line: getLine(arg),
|
|
2392
|
+
column: getColumn(arg)
|
|
2393
|
+
};
|
|
2394
|
+
if (t.isSpreadElement(arg)) {
|
|
2395
|
+
const spreadArg = arg.argument;
|
|
2396
|
+
if (t.isIdentifier(spreadArg)) {
|
|
2397
|
+
argInfo.targetType = 'VARIABLE';
|
|
2398
|
+
argInfo.targetName = spreadArg.name;
|
|
2399
|
+
argInfo.isSpread = true;
|
|
2400
|
+
}
|
|
2401
|
+
}
|
|
2402
|
+
else if (t.isIdentifier(arg)) {
|
|
2403
|
+
argInfo.targetType = 'VARIABLE';
|
|
2404
|
+
argInfo.targetName = arg.name;
|
|
2405
|
+
}
|
|
2406
|
+
else if (t.isLiteral(arg) && !t.isTemplateLiteral(arg)) {
|
|
2407
|
+
const literalValue = ExpressionEvaluator.extractLiteralValue(arg);
|
|
2408
|
+
if (literalValue !== null) {
|
|
2409
|
+
const argLine = getLine(arg);
|
|
2410
|
+
const argColumn = getColumn(arg);
|
|
2411
|
+
const literalId = `LITERAL#arg${argIndex}#${module.file}#${argLine}:${argColumn}:${literalCounterRef.value++}`;
|
|
2412
|
+
literals.push({
|
|
2413
|
+
id: literalId,
|
|
2414
|
+
type: 'LITERAL',
|
|
2415
|
+
value: literalValue,
|
|
2416
|
+
valueType: typeof literalValue,
|
|
2417
|
+
file: module.file,
|
|
2418
|
+
line: argLine,
|
|
2419
|
+
column: argColumn,
|
|
2420
|
+
parentCallId: methodCallId,
|
|
2421
|
+
argIndex
|
|
2422
|
+
});
|
|
2423
|
+
argInfo.targetType = 'LITERAL';
|
|
2424
|
+
argInfo.targetId = literalId;
|
|
2425
|
+
argInfo.literalValue = literalValue;
|
|
2426
|
+
}
|
|
2427
|
+
}
|
|
2428
|
+
else if (t.isArrowFunctionExpression(arg) || t.isFunctionExpression(arg)) {
|
|
2429
|
+
argInfo.targetType = 'FUNCTION';
|
|
2430
|
+
argInfo.functionLine = getLine(arg);
|
|
2431
|
+
argInfo.functionColumn = getColumn(arg);
|
|
2432
|
+
}
|
|
2433
|
+
else if (t.isCallExpression(arg)) {
|
|
2434
|
+
argInfo.targetType = 'CALL';
|
|
2435
|
+
argInfo.nestedCallLine = getLine(arg);
|
|
2436
|
+
argInfo.nestedCallColumn = getColumn(arg);
|
|
2437
|
+
// REG-402: MemberExpression arguments (this.handler, obj.method)
|
|
2438
|
+
}
|
|
2439
|
+
else if (t.isMemberExpression(arg)) {
|
|
2440
|
+
argInfo.targetType = 'EXPRESSION';
|
|
2441
|
+
argInfo.expressionType = 'MemberExpression';
|
|
2442
|
+
if (t.isIdentifier(arg.object)) {
|
|
2443
|
+
argInfo.objectName = arg.object.name;
|
|
2444
|
+
}
|
|
2445
|
+
else if (t.isThisExpression(arg.object)) {
|
|
2446
|
+
argInfo.objectName = 'this';
|
|
2447
|
+
// Store enclosing class name for direct lookup in GraphBuilder
|
|
2448
|
+
const scopeTracker = collections.scopeTracker;
|
|
2449
|
+
if (scopeTracker) {
|
|
2450
|
+
argInfo.enclosingClassName = scopeTracker.getEnclosingScope('CLASS');
|
|
2451
|
+
}
|
|
2452
|
+
}
|
|
2453
|
+
if (!arg.computed && t.isIdentifier(arg.property)) {
|
|
2454
|
+
argInfo.propertyName = arg.property.name;
|
|
2455
|
+
}
|
|
2456
|
+
}
|
|
2457
|
+
else {
|
|
2458
|
+
argInfo.targetType = 'EXPRESSION';
|
|
2459
|
+
argInfo.expressionType = arg.type;
|
|
2460
|
+
}
|
|
2461
|
+
callArguments.push(argInfo);
|
|
2462
|
+
});
|
|
2463
|
+
}
|
|
4006
2464
|
/**
|
|
4007
2465
|
* REG-311: Micro-trace - follow variable assignments within function to find error source.
|
|
4008
2466
|
* Used to resolve reject(err) or throw err where err is a variable.
|
|
@@ -4322,36 +2780,12 @@ export class JSASTAnalyzer extends Plugin {
|
|
|
4322
2780
|
isSpread: false,
|
|
4323
2781
|
valueType: 'EXPRESSION'
|
|
4324
2782
|
};
|
|
4325
|
-
// Determine value type and create value nodes for non-variable types
|
|
4326
|
-
|
|
4327
|
-
|
|
4328
|
-
|
|
4329
|
-
|
|
4330
|
-
|
|
4331
|
-
const valueColumn = value.loc?.start.column ?? column;
|
|
4332
|
-
// Create LITERAL node if collections available
|
|
4333
|
-
if (collections?.literals && collections.literalCounterRef) {
|
|
4334
|
-
const literalCounterRef = collections.literalCounterRef;
|
|
4335
|
-
const literalId = `LITERAL#indexed#${module.file}#${valueLine}:${valueColumn}:${literalCounterRef.value++}`;
|
|
4336
|
-
collections.literals.push({
|
|
4337
|
-
id: literalId,
|
|
4338
|
-
type: 'LITERAL',
|
|
4339
|
-
value: literalValue,
|
|
4340
|
-
valueType: typeof literalValue,
|
|
4341
|
-
file: module.file,
|
|
4342
|
-
line: valueLine,
|
|
4343
|
-
column: valueColumn,
|
|
4344
|
-
parentCallId: undefined,
|
|
4345
|
-
argIndex: 0
|
|
4346
|
-
});
|
|
4347
|
-
argInfo.valueNodeId = literalId;
|
|
4348
|
-
}
|
|
4349
|
-
}
|
|
4350
|
-
else if (value.type === 'Identifier') {
|
|
4351
|
-
argInfo.valueType = 'VARIABLE';
|
|
4352
|
-
argInfo.valueName = value.name;
|
|
4353
|
-
}
|
|
4354
|
-
else if (value.type === 'ObjectExpression') {
|
|
2783
|
+
// Determine value type and create value nodes for non-variable types
|
|
2784
|
+
// IMPORTANT: Check ObjectExpression/ArrayExpression BEFORE extractLiteralValue
|
|
2785
|
+
// to match the order in detectArrayMutation and extractArguments (REG-396).
|
|
2786
|
+
// extractLiteralValue returns objects/arrays with all-literal properties as
|
|
2787
|
+
// literal values, but we want OBJECT_LITERAL/ARRAY_LITERAL nodes instead.
|
|
2788
|
+
if (value.type === 'ObjectExpression') {
|
|
4355
2789
|
argInfo.valueType = 'OBJECT_LITERAL';
|
|
4356
2790
|
const valueLine = value.loc?.start.line ?? line;
|
|
4357
2791
|
const valueColumn = value.loc?.start.column ?? column;
|
|
@@ -4379,11 +2813,41 @@ export class JSASTAnalyzer extends Plugin {
|
|
|
4379
2813
|
argInfo.valueNodeId = arrayNode.id;
|
|
4380
2814
|
}
|
|
4381
2815
|
}
|
|
2816
|
+
else if (value.type === 'Identifier') {
|
|
2817
|
+
argInfo.valueType = 'VARIABLE';
|
|
2818
|
+
argInfo.valueName = value.name;
|
|
2819
|
+
}
|
|
4382
2820
|
else if (value.type === 'CallExpression') {
|
|
4383
2821
|
argInfo.valueType = 'CALL';
|
|
4384
2822
|
argInfo.callLine = value.loc?.start.line;
|
|
4385
2823
|
argInfo.callColumn = value.loc?.start.column;
|
|
4386
2824
|
}
|
|
2825
|
+
else {
|
|
2826
|
+
const literalValue = ExpressionEvaluator.extractLiteralValue(value);
|
|
2827
|
+
if (literalValue !== null) {
|
|
2828
|
+
argInfo.valueType = 'LITERAL';
|
|
2829
|
+
argInfo.literalValue = literalValue;
|
|
2830
|
+
const valueLine = value.loc?.start.line ?? line;
|
|
2831
|
+
const valueColumn = value.loc?.start.column ?? column;
|
|
2832
|
+
// Create LITERAL node if collections available
|
|
2833
|
+
if (collections?.literals && collections.literalCounterRef) {
|
|
2834
|
+
const literalCounterRef = collections.literalCounterRef;
|
|
2835
|
+
const literalId = `LITERAL#indexed#${module.file}#${valueLine}:${valueColumn}:${literalCounterRef.value++}`;
|
|
2836
|
+
collections.literals.push({
|
|
2837
|
+
id: literalId,
|
|
2838
|
+
type: 'LITERAL',
|
|
2839
|
+
value: literalValue,
|
|
2840
|
+
valueType: typeof literalValue,
|
|
2841
|
+
file: module.file,
|
|
2842
|
+
line: valueLine,
|
|
2843
|
+
column: valueColumn,
|
|
2844
|
+
parentCallId: undefined,
|
|
2845
|
+
argIndex: 0
|
|
2846
|
+
});
|
|
2847
|
+
argInfo.valueNodeId = literalId;
|
|
2848
|
+
}
|
|
2849
|
+
}
|
|
2850
|
+
}
|
|
4387
2851
|
// Capture scope path for scope-aware lookup (REG-309)
|
|
4388
2852
|
const scopePath = scopeTracker?.getContext().scopePath ?? [];
|
|
4389
2853
|
arrayMutations.push({
|