@grafema/core 0.2.4-beta → 0.2.5-beta
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/dist/Orchestrator.d.ts +37 -0
- package/dist/Orchestrator.d.ts.map +1 -1
- package/dist/Orchestrator.js +322 -35
- package/dist/Orchestrator.js.map +1 -0
- package/dist/api/GraphAPI.d.ts +1 -1
- package/dist/api/GraphAPI.d.ts.map +1 -1
- package/dist/api/GraphAPI.js +3 -1
- package/dist/api/GraphAPI.js.map +1 -0
- package/dist/api/GuaranteeAPI.d.ts.map +1 -1
- package/dist/api/GuaranteeAPI.js +4 -2
- package/dist/api/GuaranteeAPI.js.map +1 -0
- package/dist/config/ConfigLoader.d.ts +66 -0
- package/dist/config/ConfigLoader.d.ts.map +1 -1
- package/dist/config/ConfigLoader.js +77 -3
- package/dist/config/ConfigLoader.js.map +1 -0
- package/dist/config/index.d.ts +1 -1
- package/dist/config/index.d.ts.map +1 -1
- package/dist/config/index.js +2 -1
- package/dist/config/index.js.map +1 -0
- package/dist/core/ASTWorker.d.ts.map +1 -1
- package/dist/core/ASTWorker.js +6 -4
- package/dist/core/ASTWorker.js.map +1 -0
- package/dist/core/ASTWorkerPool.js +2 -1
- package/dist/core/ASTWorkerPool.js.map +1 -0
- package/dist/core/AnalysisQueue.js +1 -0
- package/dist/core/AnalysisQueue.js.map +1 -0
- package/dist/core/CoverageAnalyzer.d.ts.map +1 -1
- package/dist/core/CoverageAnalyzer.js +1 -0
- package/dist/core/CoverageAnalyzer.js.map +1 -0
- package/dist/core/FileExplainer.js +1 -0
- package/dist/core/FileExplainer.js.map +1 -0
- package/dist/core/FileNodeManager.d.ts.map +1 -1
- package/dist/core/FileNodeManager.js +4 -2
- package/dist/core/FileNodeManager.js.map +1 -0
- package/dist/core/GraphBackend.js +1 -0
- package/dist/core/GraphBackend.js.map +1 -0
- package/dist/core/GraphFreshnessChecker.js +1 -0
- package/dist/core/GraphFreshnessChecker.js.map +1 -0
- package/dist/core/GuaranteeManager.js +2 -1
- package/dist/core/GuaranteeManager.js.map +1 -0
- package/dist/core/HashUtils.js +1 -0
- package/dist/core/HashUtils.js.map +1 -0
- package/dist/core/IncrementalReanalyzer.d.ts.map +1 -1
- package/dist/core/IncrementalReanalyzer.js +7 -3
- package/dist/core/IncrementalReanalyzer.js.map +1 -0
- package/dist/core/ManifestStore.js +1 -0
- package/dist/core/ManifestStore.js.map +1 -0
- package/dist/core/NodeFactory.d.ts +54 -34
- package/dist/core/NodeFactory.d.ts.map +1 -1
- package/dist/core/NodeFactory.js +20 -1
- package/dist/core/NodeFactory.js.map +1 -0
- package/dist/core/NodeId.js +1 -0
- package/dist/core/NodeId.js.map +1 -0
- package/dist/core/PriorityQueue.js +1 -0
- package/dist/core/PriorityQueue.js.map +1 -0
- package/dist/core/Profiler.js +1 -0
- package/dist/core/Profiler.js.map +1 -0
- package/dist/core/ScopeTracker.js +1 -0
- package/dist/core/ScopeTracker.js.map +1 -0
- package/dist/core/SemanticId.js +1 -0
- package/dist/core/SemanticId.js.map +1 -0
- package/dist/core/Task.js +1 -0
- package/dist/core/Task.js.map +1 -0
- package/dist/core/TaskTypes.js +1 -0
- package/dist/core/TaskTypes.js.map +1 -0
- package/dist/core/VersionManager.js +1 -0
- package/dist/core/VersionManager.js.map +1 -0
- package/dist/core/WorkerPool.d.ts.map +1 -1
- package/dist/core/WorkerPool.js +3 -1
- package/dist/core/WorkerPool.js.map +1 -0
- package/dist/core/nodes/ArgumentExpressionNode.js +1 -0
- package/dist/core/nodes/ArgumentExpressionNode.js.map +1 -0
- package/dist/core/nodes/ArrayLiteralNode.js +1 -0
- package/dist/core/nodes/ArrayLiteralNode.js.map +1 -0
- package/dist/core/nodes/BranchNode.js +1 -0
- package/dist/core/nodes/BranchNode.js.map +1 -0
- package/dist/core/nodes/CallSiteNode.js +1 -0
- package/dist/core/nodes/CallSiteNode.js.map +1 -0
- package/dist/core/nodes/CaseNode.js +1 -0
- package/dist/core/nodes/CaseNode.js.map +1 -0
- package/dist/core/nodes/ClassNode.js +1 -0
- package/dist/core/nodes/ClassNode.js.map +1 -0
- package/dist/core/nodes/ConstantNode.js +1 -0
- package/dist/core/nodes/ConstantNode.js.map +1 -0
- package/dist/core/nodes/ConstructorCallNode.js +1 -0
- package/dist/core/nodes/ConstructorCallNode.js.map +1 -0
- package/dist/core/nodes/DatabaseQueryNode.js +1 -0
- package/dist/core/nodes/DatabaseQueryNode.js.map +1 -0
- package/dist/core/nodes/DecoratorNode.js +1 -0
- package/dist/core/nodes/DecoratorNode.js.map +1 -0
- package/dist/core/nodes/EntrypointNode.js +1 -0
- package/dist/core/nodes/EntrypointNode.js.map +1 -0
- package/dist/core/nodes/EnumNode.js +1 -0
- package/dist/core/nodes/EnumNode.js.map +1 -0
- package/dist/core/nodes/EventListenerNode.js +1 -0
- package/dist/core/nodes/EventListenerNode.js.map +1 -0
- package/dist/core/nodes/ExportNode.js +1 -0
- package/dist/core/nodes/ExportNode.js.map +1 -0
- package/dist/core/nodes/ExpressionNode.js +1 -0
- package/dist/core/nodes/ExpressionNode.js.map +1 -0
- package/dist/core/nodes/ExternalModuleNode.js +1 -0
- package/dist/core/nodes/ExternalModuleNode.js.map +1 -0
- package/dist/core/nodes/ExternalStdioNode.js +1 -0
- package/dist/core/nodes/ExternalStdioNode.js.map +1 -0
- package/dist/core/nodes/FunctionNode.js +1 -0
- package/dist/core/nodes/FunctionNode.js.map +1 -0
- package/dist/core/nodes/GuaranteeNode.js +1 -0
- package/dist/core/nodes/GuaranteeNode.js.map +1 -0
- package/dist/core/nodes/HttpRequestNode.js +1 -0
- package/dist/core/nodes/HttpRequestNode.js.map +1 -0
- package/dist/core/nodes/ImportNode.js +1 -0
- package/dist/core/nodes/ImportNode.js.map +1 -0
- package/dist/core/nodes/InterfaceNode.js +1 -0
- package/dist/core/nodes/InterfaceNode.js.map +1 -0
- package/dist/core/nodes/IssueNode.js +1 -0
- package/dist/core/nodes/IssueNode.js.map +1 -0
- package/dist/core/nodes/LiteralNode.js +1 -0
- package/dist/core/nodes/LiteralNode.js.map +1 -0
- package/dist/core/nodes/MethodCallNode.js +1 -0
- package/dist/core/nodes/MethodCallNode.js.map +1 -0
- package/dist/core/nodes/MethodNode.js +1 -0
- package/dist/core/nodes/MethodNode.js.map +1 -0
- package/dist/core/nodes/ModuleNode.js +1 -0
- package/dist/core/nodes/ModuleNode.js.map +1 -0
- package/dist/core/nodes/NetworkRequestNode.js +1 -0
- package/dist/core/nodes/NetworkRequestNode.js.map +1 -0
- package/dist/core/nodes/NodeKind.d.ts +5 -0
- package/dist/core/nodes/NodeKind.d.ts.map +1 -1
- package/dist/core/nodes/NodeKind.js +11 -0
- package/dist/core/nodes/NodeKind.js.map +1 -0
- package/dist/core/nodes/ObjectLiteralNode.js +1 -0
- package/dist/core/nodes/ObjectLiteralNode.js.map +1 -0
- package/dist/core/nodes/ParameterNode.js +1 -0
- package/dist/core/nodes/ParameterNode.js.map +1 -0
- package/dist/core/nodes/PluginNode.d.ts +69 -0
- package/dist/core/nodes/PluginNode.d.ts.map +1 -0
- package/dist/core/nodes/PluginNode.js +106 -0
- package/dist/core/nodes/PluginNode.js.map +1 -0
- package/dist/core/nodes/ScopeNode.js +1 -0
- package/dist/core/nodes/ScopeNode.js.map +1 -0
- package/dist/core/nodes/ServiceNode.js +1 -0
- package/dist/core/nodes/ServiceNode.js.map +1 -0
- package/dist/core/nodes/TypeNode.js +2 -1
- package/dist/core/nodes/TypeNode.js.map +1 -0
- package/dist/core/nodes/VariableDeclarationNode.js +1 -0
- package/dist/core/nodes/VariableDeclarationNode.js.map +1 -0
- package/dist/core/nodes/index.d.ts +2 -1
- package/dist/core/nodes/index.d.ts.map +1 -1
- package/dist/core/nodes/index.js +4 -1
- package/dist/core/nodes/index.js.map +1 -0
- package/dist/core/toposort.d.ts +38 -0
- package/dist/core/toposort.d.ts.map +1 -0
- package/dist/core/toposort.js +129 -0
- package/dist/core/toposort.js.map +1 -0
- package/dist/data/builtins/BuiltinRegistry.js +1 -0
- package/dist/data/builtins/BuiltinRegistry.js.map +1 -0
- package/dist/data/builtins/definitions.js +1 -0
- package/dist/data/builtins/definitions.js.map +1 -0
- package/dist/data/builtins/index.js +1 -0
- package/dist/data/builtins/index.js.map +1 -0
- package/dist/data/builtins/jsGlobals.js +1 -0
- package/dist/data/builtins/jsGlobals.js.map +1 -0
- package/dist/data/builtins/types.js +1 -0
- package/dist/data/builtins/types.js.map +1 -0
- package/dist/data/globals/definitions.js +1 -0
- package/dist/data/globals/definitions.js.map +1 -0
- package/dist/data/globals/index.js +1 -0
- package/dist/data/globals/index.js.map +1 -0
- package/dist/diagnostics/DiagnosticCollector.d.ts +5 -0
- package/dist/diagnostics/DiagnosticCollector.d.ts.map +1 -1
- package/dist/diagnostics/DiagnosticCollector.js +4 -0
- package/dist/diagnostics/DiagnosticCollector.js.map +1 -0
- package/dist/diagnostics/DiagnosticReporter.d.ts +23 -1
- package/dist/diagnostics/DiagnosticReporter.d.ts.map +1 -1
- package/dist/diagnostics/DiagnosticReporter.js +68 -15
- package/dist/diagnostics/DiagnosticReporter.js.map +1 -0
- package/dist/diagnostics/DiagnosticWriter.js +1 -0
- package/dist/diagnostics/DiagnosticWriter.js.map +1 -0
- package/dist/diagnostics/categories.d.ts +57 -0
- package/dist/diagnostics/categories.d.ts.map +1 -0
- package/dist/diagnostics/categories.js +71 -0
- package/dist/diagnostics/categories.js.map +1 -0
- package/dist/diagnostics/index.d.ts +3 -0
- package/dist/diagnostics/index.d.ts.map +1 -1
- package/dist/diagnostics/index.js +4 -0
- package/dist/diagnostics/index.js.map +1 -0
- package/dist/errors/GrafemaError.d.ts +39 -0
- package/dist/errors/GrafemaError.d.ts.map +1 -1
- package/dist/errors/GrafemaError.js +28 -0
- package/dist/errors/GrafemaError.js.map +1 -0
- package/dist/index.d.ts +21 -10
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +19 -7
- package/dist/index.js.map +1 -0
- package/dist/instructions/index.d.ts +8 -0
- package/dist/instructions/index.d.ts.map +1 -0
- package/dist/instructions/index.js +20 -0
- package/dist/instructions/index.js.map +1 -0
- package/dist/instructions/onboarding.md +121 -0
- package/dist/logging/Logger.d.ts +53 -3
- package/dist/logging/Logger.d.ts.map +1 -1
- package/dist/logging/Logger.js +144 -4
- package/dist/logging/Logger.js.map +1 -0
- package/dist/plugins/Plugin.js +1 -0
- package/dist/plugins/Plugin.js.map +1 -0
- package/dist/plugins/analysis/DatabaseAnalyzer.d.ts.map +1 -1
- package/dist/plugins/analysis/DatabaseAnalyzer.js +20 -14
- package/dist/plugins/analysis/DatabaseAnalyzer.js.map +1 -0
- package/dist/plugins/analysis/ExpressAnalyzer.d.ts.map +1 -1
- package/dist/plugins/analysis/ExpressAnalyzer.js +23 -16
- package/dist/plugins/analysis/ExpressAnalyzer.js.map +1 -0
- package/dist/plugins/analysis/ExpressResponseAnalyzer.d.ts +2 -1
- package/dist/plugins/analysis/ExpressResponseAnalyzer.d.ts.map +1 -1
- package/dist/plugins/analysis/ExpressResponseAnalyzer.js +53 -76
- package/dist/plugins/analysis/ExpressResponseAnalyzer.js.map +1 -0
- package/dist/plugins/analysis/ExpressRouteAnalyzer.d.ts.map +1 -1
- package/dist/plugins/analysis/ExpressRouteAnalyzer.js +47 -37
- package/dist/plugins/analysis/ExpressRouteAnalyzer.js.map +1 -0
- package/dist/plugins/analysis/FetchAnalyzer.d.ts +8 -14
- package/dist/plugins/analysis/FetchAnalyzer.d.ts.map +1 -1
- package/dist/plugins/analysis/FetchAnalyzer.js +144 -97
- package/dist/plugins/analysis/FetchAnalyzer.js.map +1 -0
- package/dist/plugins/analysis/IncrementalAnalysisPlugin.d.ts.map +1 -1
- package/dist/plugins/analysis/IncrementalAnalysisPlugin.js +3 -5
- package/dist/plugins/analysis/IncrementalAnalysisPlugin.js.map +1 -0
- package/dist/plugins/analysis/JSASTAnalyzer.d.ts +59 -2
- package/dist/plugins/analysis/JSASTAnalyzer.d.ts.map +1 -1
- package/dist/plugins/analysis/JSASTAnalyzer.js +926 -422
- package/dist/plugins/analysis/JSASTAnalyzer.js.map +1 -0
- package/dist/plugins/analysis/ReactAnalyzer.d.ts.map +1 -1
- package/dist/plugins/analysis/ReactAnalyzer.js +24 -19
- package/dist/plugins/analysis/ReactAnalyzer.js.map +1 -0
- package/dist/plugins/analysis/RustAnalyzer.d.ts.map +1 -1
- package/dist/plugins/analysis/RustAnalyzer.js +39 -26
- package/dist/plugins/analysis/RustAnalyzer.js.map +1 -0
- package/dist/plugins/analysis/SQLiteAnalyzer.d.ts.map +1 -1
- package/dist/plugins/analysis/SQLiteAnalyzer.js +14 -7
- package/dist/plugins/analysis/SQLiteAnalyzer.js.map +1 -0
- package/dist/plugins/analysis/ServiceLayerAnalyzer.d.ts.map +1 -1
- package/dist/plugins/analysis/ServiceLayerAnalyzer.js +33 -28
- package/dist/plugins/analysis/ServiceLayerAnalyzer.js.map +1 -0
- package/dist/plugins/analysis/SocketIOAnalyzer.d.ts.map +1 -1
- package/dist/plugins/analysis/SocketIOAnalyzer.js +24 -14
- package/dist/plugins/analysis/SocketIOAnalyzer.js.map +1 -0
- package/dist/plugins/analysis/SystemDbAnalyzer.d.ts.map +1 -1
- package/dist/plugins/analysis/SystemDbAnalyzer.js +19 -11
- package/dist/plugins/analysis/SystemDbAnalyzer.js.map +1 -0
- package/dist/plugins/analysis/ast/ConditionParser.js +1 -0
- package/dist/plugins/analysis/ast/ConditionParser.js.map +1 -0
- package/dist/plugins/analysis/ast/ExpressionEvaluator.d.ts.map +1 -1
- package/dist/plugins/analysis/ast/ExpressionEvaluator.js +1 -0
- package/dist/plugins/analysis/ast/ExpressionEvaluator.js.map +1 -0
- package/dist/plugins/analysis/ast/GraphBuilder.d.ts +58 -6
- package/dist/plugins/analysis/ast/GraphBuilder.d.ts.map +1 -1
- package/dist/plugins/analysis/ast/GraphBuilder.js +440 -56
- package/dist/plugins/analysis/ast/GraphBuilder.js.map +1 -0
- package/dist/plugins/analysis/ast/IdGenerator.js +1 -0
- package/dist/plugins/analysis/ast/IdGenerator.js.map +1 -0
- package/dist/plugins/analysis/ast/OxcAdapter.js +1 -0
- package/dist/plugins/analysis/ast/OxcAdapter.js.map +1 -0
- package/dist/plugins/analysis/ast/types.d.ts +152 -1
- package/dist/plugins/analysis/ast/types.d.ts.map +1 -1
- package/dist/plugins/analysis/ast/types.js +1 -0
- package/dist/plugins/analysis/ast/types.js.map +1 -0
- package/dist/plugins/analysis/ast/utils/babelTraverse.d.ts +27 -0
- package/dist/plugins/analysis/ast/utils/babelTraverse.d.ts.map +1 -0
- package/dist/plugins/analysis/ast/utils/babelTraverse.js +45 -0
- package/dist/plugins/analysis/ast/utils/babelTraverse.js.map +1 -0
- package/dist/plugins/analysis/ast/utils/createParameterNodes.d.ts +1 -1
- package/dist/plugins/analysis/ast/utils/createParameterNodes.d.ts.map +1 -1
- package/dist/plugins/analysis/ast/utils/createParameterNodes.js +1 -0
- package/dist/plugins/analysis/ast/utils/createParameterNodes.js.map +1 -0
- package/dist/plugins/analysis/ast/utils/index.js +1 -0
- package/dist/plugins/analysis/ast/utils/index.js.map +1 -0
- package/dist/plugins/analysis/ast/utils/location.js +1 -0
- package/dist/plugins/analysis/ast/utils/location.js.map +1 -0
- package/dist/plugins/analysis/ast/visitors/ASTVisitor.d.ts +3 -1
- package/dist/plugins/analysis/ast/visitors/ASTVisitor.d.ts.map +1 -1
- package/dist/plugins/analysis/ast/visitors/ASTVisitor.js +1 -0
- package/dist/plugins/analysis/ast/visitors/ASTVisitor.js.map +1 -0
- package/dist/plugins/analysis/ast/visitors/CallExpressionVisitor.d.ts +9 -2
- package/dist/plugins/analysis/ast/visitors/CallExpressionVisitor.d.ts.map +1 -1
- package/dist/plugins/analysis/ast/visitors/CallExpressionVisitor.js +145 -14
- package/dist/plugins/analysis/ast/visitors/CallExpressionVisitor.js.map +1 -0
- package/dist/plugins/analysis/ast/visitors/ClassVisitor.d.ts +1 -1
- package/dist/plugins/analysis/ast/visitors/ClassVisitor.d.ts.map +1 -1
- package/dist/plugins/analysis/ast/visitors/ClassVisitor.js +198 -0
- package/dist/plugins/analysis/ast/visitors/ClassVisitor.js.map +1 -0
- package/dist/plugins/analysis/ast/visitors/FunctionVisitor.d.ts +4 -3
- package/dist/plugins/analysis/ast/visitors/FunctionVisitor.d.ts.map +1 -1
- package/dist/plugins/analysis/ast/visitors/FunctionVisitor.js +5 -2
- package/dist/plugins/analysis/ast/visitors/FunctionVisitor.js.map +1 -0
- package/dist/plugins/analysis/ast/visitors/ImportExportVisitor.js +1 -0
- package/dist/plugins/analysis/ast/visitors/ImportExportVisitor.js.map +1 -0
- package/dist/plugins/analysis/ast/visitors/PropertyAccessVisitor.d.ts +83 -0
- package/dist/plugins/analysis/ast/visitors/PropertyAccessVisitor.d.ts.map +1 -0
- package/dist/plugins/analysis/ast/visitors/PropertyAccessVisitor.js +258 -0
- package/dist/plugins/analysis/ast/visitors/PropertyAccessVisitor.js.map +1 -0
- package/dist/plugins/analysis/ast/visitors/TypeScriptVisitor.d.ts +1 -1
- package/dist/plugins/analysis/ast/visitors/TypeScriptVisitor.d.ts.map +1 -1
- package/dist/plugins/analysis/ast/visitors/TypeScriptVisitor.js +1 -0
- package/dist/plugins/analysis/ast/visitors/TypeScriptVisitor.js.map +1 -0
- package/dist/plugins/analysis/ast/visitors/VariableVisitor.d.ts +1 -1
- package/dist/plugins/analysis/ast/visitors/VariableVisitor.d.ts.map +1 -1
- package/dist/plugins/analysis/ast/visitors/VariableVisitor.js +1 -0
- package/dist/plugins/analysis/ast/visitors/VariableVisitor.js.map +1 -0
- package/dist/plugins/analysis/ast/visitors/index.d.ts +1 -0
- package/dist/plugins/analysis/ast/visitors/index.d.ts.map +1 -1
- package/dist/plugins/analysis/ast/visitors/index.js +2 -0
- package/dist/plugins/analysis/ast/visitors/index.js.map +1 -0
- package/dist/plugins/discovery/DiscoveryPlugin.d.ts +42 -10
- package/dist/plugins/discovery/DiscoveryPlugin.d.ts.map +1 -1
- package/dist/plugins/discovery/DiscoveryPlugin.js +17 -1
- package/dist/plugins/discovery/DiscoveryPlugin.js.map +1 -0
- package/dist/plugins/discovery/MonorepoServiceDiscovery.d.ts.map +1 -1
- package/dist/plugins/discovery/MonorepoServiceDiscovery.js +3 -2
- package/dist/plugins/discovery/MonorepoServiceDiscovery.js.map +1 -0
- package/dist/plugins/discovery/SimpleProjectDiscovery.d.ts.map +1 -1
- package/dist/plugins/discovery/SimpleProjectDiscovery.js +3 -2
- package/dist/plugins/discovery/SimpleProjectDiscovery.js.map +1 -0
- package/dist/plugins/discovery/WorkspaceDiscovery.d.ts.map +1 -1
- package/dist/plugins/discovery/WorkspaceDiscovery.js +3 -2
- package/dist/plugins/discovery/WorkspaceDiscovery.js.map +1 -0
- package/dist/plugins/discovery/resolveSourceEntrypoint.js +1 -0
- package/dist/plugins/discovery/resolveSourceEntrypoint.js.map +1 -0
- package/dist/plugins/discovery/workspaces/detector.js +1 -0
- package/dist/plugins/discovery/workspaces/detector.js.map +1 -0
- package/dist/plugins/discovery/workspaces/globResolver.js +1 -0
- package/dist/plugins/discovery/workspaces/globResolver.js.map +1 -0
- package/dist/plugins/discovery/workspaces/index.js +1 -0
- package/dist/plugins/discovery/workspaces/index.js.map +1 -0
- package/dist/plugins/discovery/workspaces/parsers.js +1 -0
- package/dist/plugins/discovery/workspaces/parsers.js.map +1 -0
- package/dist/plugins/enrichment/AliasTracker.js +3 -2
- package/dist/plugins/enrichment/AliasTracker.js.map +1 -0
- package/dist/plugins/enrichment/ArgumentParameterLinker.d.ts.map +1 -1
- package/dist/plugins/enrichment/ArgumentParameterLinker.js +1 -1
- package/dist/plugins/enrichment/ArgumentParameterLinker.js.map +1 -0
- package/dist/plugins/enrichment/ClosureCaptureEnricher.d.ts.map +1 -1
- package/dist/plugins/enrichment/ClosureCaptureEnricher.js +1 -1
- package/dist/plugins/enrichment/ClosureCaptureEnricher.js.map +1 -0
- package/dist/plugins/enrichment/ExpressHandlerLinker.d.ts +21 -0
- package/dist/plugins/enrichment/ExpressHandlerLinker.d.ts.map +1 -0
- package/dist/plugins/enrichment/ExpressHandlerLinker.js +137 -0
- package/dist/plugins/enrichment/ExpressHandlerLinker.js.map +1 -0
- package/dist/plugins/enrichment/ExternalCallResolver.d.ts.map +1 -1
- package/dist/plugins/enrichment/ExternalCallResolver.js +1 -1
- package/dist/plugins/enrichment/ExternalCallResolver.js.map +1 -0
- package/dist/plugins/enrichment/FunctionCallResolver.d.ts +5 -0
- package/dist/plugins/enrichment/FunctionCallResolver.d.ts.map +1 -1
- package/dist/plugins/enrichment/FunctionCallResolver.js +11 -9
- package/dist/plugins/enrichment/FunctionCallResolver.js.map +1 -0
- package/dist/plugins/enrichment/HTTPConnectionEnricher.d.ts +2 -0
- package/dist/plugins/enrichment/HTTPConnectionEnricher.d.ts.map +1 -1
- package/dist/plugins/enrichment/HTTPConnectionEnricher.js +51 -13
- package/dist/plugins/enrichment/HTTPConnectionEnricher.js.map +1 -0
- package/dist/plugins/enrichment/ImportExportLinker.d.ts.map +1 -1
- package/dist/plugins/enrichment/ImportExportLinker.js +1 -1
- package/dist/plugins/enrichment/ImportExportLinker.js.map +1 -0
- package/dist/plugins/enrichment/InstanceOfResolver.js +3 -2
- package/dist/plugins/enrichment/InstanceOfResolver.js.map +1 -0
- package/dist/plugins/enrichment/MethodCallResolver.d.ts +39 -1
- package/dist/plugins/enrichment/MethodCallResolver.d.ts.map +1 -1
- package/dist/plugins/enrichment/MethodCallResolver.js +422 -11
- package/dist/plugins/enrichment/MethodCallResolver.js.map +1 -0
- package/dist/plugins/enrichment/MountPointResolver.d.ts +1 -1
- package/dist/plugins/enrichment/MountPointResolver.d.ts.map +1 -1
- package/dist/plugins/enrichment/MountPointResolver.js +7 -23
- package/dist/plugins/enrichment/MountPointResolver.js.map +1 -0
- package/dist/plugins/enrichment/NodejsBuiltinsResolver.d.ts.map +1 -1
- package/dist/plugins/enrichment/NodejsBuiltinsResolver.js +2 -2
- package/dist/plugins/enrichment/NodejsBuiltinsResolver.js.map +1 -0
- package/dist/plugins/enrichment/PrefixEvaluator.d.ts.map +1 -1
- package/dist/plugins/enrichment/PrefixEvaluator.js +5 -4
- package/dist/plugins/enrichment/PrefixEvaluator.js.map +1 -0
- package/dist/plugins/enrichment/RejectionPropagationEnricher.d.ts +30 -0
- package/dist/plugins/enrichment/RejectionPropagationEnricher.d.ts.map +1 -0
- package/dist/plugins/enrichment/RejectionPropagationEnricher.js +190 -0
- package/dist/plugins/enrichment/RejectionPropagationEnricher.js.map +1 -0
- package/dist/plugins/enrichment/RustFFIEnricher.d.ts.map +1 -1
- package/dist/plugins/enrichment/RustFFIEnricher.js +1 -1
- package/dist/plugins/enrichment/RustFFIEnricher.js.map +1 -0
- package/dist/plugins/enrichment/ValueDomainAnalyzer.d.ts +1 -1
- package/dist/plugins/enrichment/ValueDomainAnalyzer.d.ts.map +1 -1
- package/dist/plugins/enrichment/ValueDomainAnalyzer.js +4 -3
- package/dist/plugins/enrichment/ValueDomainAnalyzer.js.map +1 -0
- package/dist/plugins/indexing/IncrementalModuleIndexer.d.ts +3 -1
- package/dist/plugins/indexing/IncrementalModuleIndexer.d.ts.map +1 -1
- package/dist/plugins/indexing/IncrementalModuleIndexer.js +22 -29
- package/dist/plugins/indexing/IncrementalModuleIndexer.js.map +1 -0
- package/dist/plugins/indexing/JSModuleIndexer.d.ts +3 -1
- package/dist/plugins/indexing/JSModuleIndexer.d.ts.map +1 -1
- package/dist/plugins/indexing/JSModuleIndexer.js +22 -31
- package/dist/plugins/indexing/JSModuleIndexer.js.map +1 -0
- package/dist/plugins/indexing/RustModuleIndexer.d.ts.map +1 -1
- package/dist/plugins/indexing/RustModuleIndexer.js +11 -5
- package/dist/plugins/indexing/RustModuleIndexer.js.map +1 -0
- package/dist/plugins/validation/BrokenImportValidator.d.ts.map +1 -1
- package/dist/plugins/validation/BrokenImportValidator.js +3 -3
- package/dist/plugins/validation/BrokenImportValidator.js.map +1 -0
- package/dist/plugins/validation/CallResolverValidator.d.ts.map +1 -1
- package/dist/plugins/validation/CallResolverValidator.js +3 -3
- package/dist/plugins/validation/CallResolverValidator.js.map +1 -0
- package/dist/plugins/validation/DataFlowValidator.js +2 -1
- package/dist/plugins/validation/DataFlowValidator.js.map +1 -0
- package/dist/plugins/validation/EvalBanValidator.js +2 -1
- package/dist/plugins/validation/EvalBanValidator.js.map +1 -0
- package/dist/plugins/validation/GraphConnectivityValidator.d.ts.map +1 -1
- package/dist/plugins/validation/GraphConnectivityValidator.js +2 -1
- package/dist/plugins/validation/GraphConnectivityValidator.js.map +1 -0
- package/dist/plugins/validation/SQLInjectionValidator.d.ts.map +1 -1
- package/dist/plugins/validation/SQLInjectionValidator.js +4 -2
- package/dist/plugins/validation/SQLInjectionValidator.js.map +1 -0
- package/dist/plugins/validation/ShadowingDetector.js +2 -1
- package/dist/plugins/validation/ShadowingDetector.js.map +1 -0
- package/dist/plugins/validation/TypeScriptDeadCodeValidator.d.ts.map +1 -1
- package/dist/plugins/validation/TypeScriptDeadCodeValidator.js +3 -3
- package/dist/plugins/validation/TypeScriptDeadCodeValidator.js.map +1 -0
- package/dist/plugins/vcs/GitPlugin.d.ts.map +1 -1
- package/dist/plugins/vcs/GitPlugin.js +13 -6
- package/dist/plugins/vcs/GitPlugin.js.map +1 -0
- package/dist/plugins/vcs/VCSPlugin.js +1 -0
- package/dist/plugins/vcs/VCSPlugin.js.map +1 -0
- package/dist/plugins/vcs/index.js +1 -0
- package/dist/plugins/vcs/index.js.map +1 -0
- package/dist/queries/findCallsInFunction.d.ts +1 -1
- package/dist/queries/findCallsInFunction.d.ts.map +1 -1
- package/dist/queries/findCallsInFunction.js +3 -2
- package/dist/queries/findCallsInFunction.js.map +1 -0
- package/dist/queries/findContainingFunction.d.ts +1 -1
- package/dist/queries/findContainingFunction.d.ts.map +1 -1
- package/dist/queries/findContainingFunction.js +1 -0
- package/dist/queries/findContainingFunction.js.map +1 -0
- package/dist/queries/index.js +1 -0
- package/dist/queries/index.js.map +1 -0
- package/dist/queries/traceValues.d.ts.map +1 -1
- package/dist/queries/traceValues.js +1 -0
- package/dist/queries/traceValues.js.map +1 -0
- package/dist/queries/types.js +1 -0
- package/dist/queries/types.js.map +1 -0
- package/dist/schema/GraphSchemaExtractor.js +1 -0
- package/dist/schema/GraphSchemaExtractor.js.map +1 -0
- package/dist/schema/InterfaceSchemaExtractor.js +1 -0
- package/dist/schema/InterfaceSchemaExtractor.js.map +1 -0
- package/dist/schema/index.js +1 -0
- package/dist/schema/index.js.map +1 -0
- package/dist/storage/backends/RFDBServerBackend.d.ts +25 -1
- package/dist/storage/backends/RFDBServerBackend.d.ts.map +1 -1
- package/dist/storage/backends/RFDBServerBackend.js +100 -22
- package/dist/storage/backends/RFDBServerBackend.js.map +1 -0
- package/dist/storage/backends/typeValidation.d.ts.map +1 -1
- package/dist/storage/backends/typeValidation.js +3 -0
- package/dist/storage/backends/typeValidation.js.map +1 -0
- package/dist/utils/findRfdbBinary.js +1 -0
- package/dist/utils/findRfdbBinary.js.map +1 -0
- package/dist/utils/moduleResolution.d.ts +134 -0
- package/dist/utils/moduleResolution.d.ts.map +1 -0
- package/dist/utils/moduleResolution.js +164 -0
- package/dist/utils/moduleResolution.js.map +1 -0
- package/dist/validation/PathValidator.d.ts.map +1 -1
- package/dist/validation/PathValidator.js +1 -0
- package/dist/validation/PathValidator.js.map +1 -0
- package/package.json +4 -3
- package/src/Orchestrator.ts +371 -41
- package/src/api/GraphAPI.ts +4 -2
- package/src/api/GuaranteeAPI.ts +3 -2
- package/src/config/ConfigLoader.ts +121 -3
- package/src/config/index.ts +7 -1
- package/src/core/ASTWorker.ts +8 -20
- package/src/core/ASTWorkerPool.ts +1 -1
- package/src/core/CoverageAnalyzer.ts +2 -2
- package/src/core/FileNodeManager.ts +3 -2
- package/src/core/GuaranteeManager.ts +1 -1
- package/src/core/IncrementalReanalyzer.ts +6 -3
- package/src/core/NodeFactory.ts +34 -1
- package/src/core/WorkerPool.ts +2 -1
- package/src/core/nodes/NodeKind.ts +11 -0
- package/src/core/nodes/PluginNode.ts +144 -0
- package/src/core/nodes/TypeNode.ts +1 -1
- package/src/core/nodes/index.ts +4 -0
- package/src/core/toposort.ts +160 -0
- package/src/diagnostics/DiagnosticCollector.ts +8 -1
- package/src/diagnostics/DiagnosticReporter.ts +87 -16
- package/src/diagnostics/categories.ts +104 -0
- package/src/diagnostics/index.ts +14 -0
- package/src/errors/GrafemaError.ts +58 -0
- package/src/index.ts +53 -9
- package/src/instructions/index.ts +21 -0
- package/src/instructions/onboarding.md +121 -0
- package/src/logging/Logger.ts +155 -4
- package/src/plugins/analysis/DatabaseAnalyzer.ts +22 -15
- package/src/plugins/analysis/ExpressAnalyzer.ts +30 -18
- package/src/plugins/analysis/ExpressResponseAnalyzer.ts +72 -88
- package/src/plugins/analysis/ExpressRouteAnalyzer.ts +54 -39
- package/src/plugins/analysis/FetchAnalyzer.ts +165 -113
- package/src/plugins/analysis/IncrementalAnalysisPlugin.ts +6 -7
- package/src/plugins/analysis/JSASTAnalyzer.ts +1122 -487
- package/src/plugins/analysis/ReactAnalyzer.ts +27 -20
- package/src/plugins/analysis/RustAnalyzer.ts +41 -27
- package/src/plugins/analysis/SQLiteAnalyzer.ts +18 -8
- package/src/plugins/analysis/ServiceLayerAnalyzer.ts +38 -34
- package/src/plugins/analysis/SocketIOAnalyzer.ts +30 -15
- package/src/plugins/analysis/SystemDbAnalyzer.ts +24 -13
- package/src/plugins/analysis/ast/ExpressionEvaluator.ts +1 -3
- package/src/plugins/analysis/ast/GraphBuilder.ts +532 -66
- package/src/plugins/analysis/ast/types.ts +200 -2
- package/src/plugins/analysis/ast/utils/babelTraverse.ts +74 -0
- package/src/plugins/analysis/ast/utils/createParameterNodes.ts +1 -1
- package/src/plugins/analysis/ast/visitors/ASTVisitor.ts +7 -2
- package/src/plugins/analysis/ast/visitors/CallExpressionVisitor.ts +163 -15
- package/src/plugins/analysis/ast/visitors/ClassVisitor.ts +249 -3
- package/src/plugins/analysis/ast/visitors/FunctionVisitor.ts +9 -4
- package/src/plugins/analysis/ast/visitors/PropertyAccessVisitor.ts +342 -0
- package/src/plugins/analysis/ast/visitors/TypeScriptVisitor.ts +2 -3
- package/src/plugins/analysis/ast/visitors/VariableVisitor.ts +1 -27
- package/src/plugins/analysis/ast/visitors/index.ts +2 -0
- package/src/plugins/discovery/DiscoveryPlugin.ts +42 -11
- package/src/plugins/discovery/MonorepoServiceDiscovery.ts +2 -2
- package/src/plugins/discovery/SimpleProjectDiscovery.ts +2 -2
- package/src/plugins/discovery/WorkspaceDiscovery.ts +2 -2
- package/src/plugins/discovery/workspaces/globResolver.ts +1 -1
- package/src/plugins/enrichment/AliasTracker.ts +2 -2
- package/src/plugins/enrichment/ArgumentParameterLinker.ts +0 -1
- package/src/plugins/enrichment/ClosureCaptureEnricher.ts +0 -1
- package/src/plugins/enrichment/ExpressHandlerLinker.ts +178 -0
- package/src/plugins/enrichment/ExternalCallResolver.ts +0 -1
- package/src/plugins/enrichment/FunctionCallResolver.ts +10 -15
- package/src/plugins/enrichment/HTTPConnectionEnricher.ts +65 -14
- package/src/plugins/enrichment/ImportExportLinker.ts +0 -1
- package/src/plugins/enrichment/InstanceOfResolver.ts +2 -2
- package/src/plugins/enrichment/MethodCallResolver.ts +526 -12
- package/src/plugins/enrichment/MountPointResolver.ts +6 -24
- package/src/plugins/enrichment/NodejsBuiltinsResolver.ts +1 -2
- package/src/plugins/enrichment/PrefixEvaluator.ts +4 -4
- package/src/plugins/enrichment/RejectionPropagationEnricher.ts +253 -0
- package/src/plugins/enrichment/RustFFIEnricher.ts +0 -1
- package/src/plugins/enrichment/ValueDomainAnalyzer.ts +3 -3
- package/src/plugins/indexing/IncrementalModuleIndexer.ts +24 -30
- package/src/plugins/indexing/JSModuleIndexer.ts +21 -22
- package/src/plugins/indexing/RustModuleIndexer.ts +10 -5
- package/src/plugins/validation/BrokenImportValidator.ts +2 -3
- package/src/plugins/validation/CallResolverValidator.ts +2 -3
- package/src/plugins/validation/DataFlowValidator.ts +1 -1
- package/src/plugins/validation/EvalBanValidator.ts +1 -1
- package/src/plugins/validation/GraphConnectivityValidator.ts +1 -9
- package/src/plugins/validation/SQLInjectionValidator.ts +3 -2
- package/src/plugins/validation/ShadowingDetector.ts +1 -1
- package/src/plugins/validation/TypeScriptDeadCodeValidator.ts +2 -3
- package/src/plugins/vcs/GitPlugin.ts +12 -6
- package/src/queries/findCallsInFunction.ts +4 -4
- package/src/queries/findContainingFunction.ts +1 -1
- package/src/queries/traceValues.ts +0 -1
- package/src/storage/backends/RFDBServerBackend.ts +115 -25
- package/src/storage/backends/typeValidation.ts +2 -0
- package/src/utils/moduleResolution.ts +244 -0
- package/src/validation/PathValidator.ts +0 -8
- package/src/plugins/validation/NodeCreationValidator.ts +0 -554
|
@@ -22,7 +22,7 @@ function isAnalysisResult(value) {
|
|
|
22
22
|
import { Plugin, createSuccessResult, createErrorResult } from '../Plugin.js';
|
|
23
23
|
import { ExpressionEvaluator } from './ast/ExpressionEvaluator.js';
|
|
24
24
|
import { GraphBuilder } from './ast/GraphBuilder.js';
|
|
25
|
-
import { ImportExportVisitor, VariableVisitor, FunctionVisitor, ClassVisitor, CallExpressionVisitor, TypeScriptVisitor } from './ast/visitors/index.js';
|
|
25
|
+
import { ImportExportVisitor, VariableVisitor, FunctionVisitor, ClassVisitor, CallExpressionVisitor, TypeScriptVisitor, PropertyAccessVisitor } from './ast/visitors/index.js';
|
|
26
26
|
import { Task } from '../../core/Task.js';
|
|
27
27
|
import { PriorityQueue } from '../../core/PriorityQueue.js';
|
|
28
28
|
import { WorkerPool } from '../../core/WorkerPool.js';
|
|
@@ -35,6 +35,7 @@ import { computeSemanticId } from '../../core/SemanticId.js';
|
|
|
35
35
|
import { ExpressionNode } from '../../core/nodes/ExpressionNode.js';
|
|
36
36
|
import { ConstructorCallNode } from '../../core/nodes/ConstructorCallNode.js';
|
|
37
37
|
import { ObjectLiteralNode } from '../../core/nodes/ObjectLiteralNode.js';
|
|
38
|
+
import { ArrayLiteralNode } from '../../core/nodes/ArrayLiteralNode.js';
|
|
38
39
|
import { NodeFactory } from '../../core/NodeFactory.js';
|
|
39
40
|
export class JSASTAnalyzer extends Plugin {
|
|
40
41
|
graphBuilder;
|
|
@@ -50,7 +51,6 @@ export class JSASTAnalyzer extends Plugin {
|
|
|
50
51
|
return {
|
|
51
52
|
name: 'JSASTAnalyzer',
|
|
52
53
|
phase: 'ANALYSIS',
|
|
53
|
-
priority: 80,
|
|
54
54
|
creates: {
|
|
55
55
|
nodes: [
|
|
56
56
|
'FUNCTION', 'CLASS', 'METHOD', 'VARIABLE', 'CONSTANT', 'SCOPE',
|
|
@@ -69,7 +69,16 @@ export class JSASTAnalyzer extends Plugin {
|
|
|
69
69
|
'RESOLVES_TO'
|
|
70
70
|
]
|
|
71
71
|
},
|
|
72
|
-
dependencies: ['JSModuleIndexer']
|
|
72
|
+
dependencies: ['JSModuleIndexer'],
|
|
73
|
+
fields: [
|
|
74
|
+
{ name: 'object', fieldType: 'string', nodeTypes: ['CALL'] },
|
|
75
|
+
{ name: 'method', fieldType: 'string', nodeTypes: ['CALL'] },
|
|
76
|
+
{ name: 'async', fieldType: 'bool', nodeTypes: ['FUNCTION', 'METHOD'] },
|
|
77
|
+
{ name: 'scopeType', fieldType: 'string', nodeTypes: ['SCOPE'] },
|
|
78
|
+
{ name: 'importType', fieldType: 'string', nodeTypes: ['IMPORT'] },
|
|
79
|
+
{ name: 'exportType', fieldType: 'string', nodeTypes: ['EXPORT'] },
|
|
80
|
+
{ name: 'parentScopeId', fieldType: 'id', nodeTypes: ['FUNCTION', 'METHOD', 'SCOPE', 'VARIABLE'] },
|
|
81
|
+
]
|
|
73
82
|
};
|
|
74
83
|
}
|
|
75
84
|
/**
|
|
@@ -1050,6 +1059,13 @@ export class JSASTAnalyzer extends Plugin {
|
|
|
1050
1059
|
const promiseResolutions = [];
|
|
1051
1060
|
// Promise executor contexts (REG-334) - keyed by executor function's start:end position
|
|
1052
1061
|
const promiseExecutorContexts = new Map();
|
|
1062
|
+
// Yield expression tracking for YIELDS/DELEGATES_TO edges (REG-270)
|
|
1063
|
+
const yieldExpressions = [];
|
|
1064
|
+
// REG-311: Async error tracking
|
|
1065
|
+
const rejectionPatterns = [];
|
|
1066
|
+
const catchesFromInfos = [];
|
|
1067
|
+
// Property access tracking for PROPERTY_ACCESS nodes (REG-395)
|
|
1068
|
+
const propertyAccesses = [];
|
|
1053
1069
|
const ifScopeCounterRef = { value: 0 };
|
|
1054
1070
|
const scopeCounterRef = { value: 0 };
|
|
1055
1071
|
const varDeclCounterRef = { value: 0 };
|
|
@@ -1062,6 +1078,7 @@ export class JSASTAnalyzer extends Plugin {
|
|
|
1062
1078
|
const arrayLiteralCounterRef = { value: 0 };
|
|
1063
1079
|
const branchCounterRef = { value: 0 };
|
|
1064
1080
|
const caseCounterRef = { value: 0 };
|
|
1081
|
+
const propertyAccessCounterRef = { value: 0 };
|
|
1065
1082
|
const processedNodes = {
|
|
1066
1083
|
functions: new Set(),
|
|
1067
1084
|
classes: new Set(),
|
|
@@ -1111,6 +1128,14 @@ export class JSASTAnalyzer extends Plugin {
|
|
|
1111
1128
|
// Promise resolution tracking (REG-334)
|
|
1112
1129
|
promiseResolutions,
|
|
1113
1130
|
promiseExecutorContexts,
|
|
1131
|
+
// Yield expression tracking (REG-270)
|
|
1132
|
+
yieldExpressions,
|
|
1133
|
+
// REG-311: Async error tracking
|
|
1134
|
+
rejectionPatterns,
|
|
1135
|
+
catchesFromInfos,
|
|
1136
|
+
// Property access tracking (REG-395)
|
|
1137
|
+
propertyAccesses,
|
|
1138
|
+
propertyAccessCounterRef,
|
|
1114
1139
|
objectLiteralCounterRef, arrayLiteralCounterRef,
|
|
1115
1140
|
ifScopeCounterRef, scopeCounterRef, varDeclCounterRef,
|
|
1116
1141
|
callSiteCounterRef, functionCounterRef, httpRequestCounterRef,
|
|
@@ -1199,7 +1224,7 @@ export class JSASTAnalyzer extends Plugin {
|
|
|
1199
1224
|
}
|
|
1200
1225
|
// === END VARIABLE REASSIGNMENT ===
|
|
1201
1226
|
// Check for indexed array assignment at module level: arr[i] = value
|
|
1202
|
-
this.detectIndexedArrayAssignment(assignNode, module, arrayMutations, scopeTracker);
|
|
1227
|
+
this.detectIndexedArrayAssignment(assignNode, module, arrayMutations, scopeTracker, allCollections);
|
|
1203
1228
|
// Check for object property assignment at module level: obj.prop = value
|
|
1204
1229
|
this.detectObjectPropertyAssignment(assignNode, module, objectMutations, scopeTracker);
|
|
1205
1230
|
}
|
|
@@ -1279,6 +1304,11 @@ export class JSASTAnalyzer extends Plugin {
|
|
|
1279
1304
|
const callExpressionVisitor = new CallExpressionVisitor(module, allCollections, scopeTracker);
|
|
1280
1305
|
traverse(ast, callExpressionVisitor.getHandlers());
|
|
1281
1306
|
this.profiler.end('traverse_calls');
|
|
1307
|
+
// Property access expressions (REG-395)
|
|
1308
|
+
this.profiler.start('traverse_property_access');
|
|
1309
|
+
const propertyAccessVisitor = new PropertyAccessVisitor(module, allCollections, scopeTracker);
|
|
1310
|
+
traverse(ast, propertyAccessVisitor.getHandlers());
|
|
1311
|
+
this.profiler.end('traverse_property_access');
|
|
1282
1312
|
// Module-level NewExpression (constructor calls)
|
|
1283
1313
|
// This handles top-level code like `const x = new Date()` that's not inside a function
|
|
1284
1314
|
this.profiler.start('traverse_new');
|
|
@@ -1336,7 +1366,9 @@ export class JSASTAnalyzer extends Plugin {
|
|
|
1336
1366
|
resolveName,
|
|
1337
1367
|
rejectName,
|
|
1338
1368
|
file: module.file,
|
|
1339
|
-
line
|
|
1369
|
+
line,
|
|
1370
|
+
// REG-311: Module-level Promise has no creator function
|
|
1371
|
+
creatorFunctionId: undefined
|
|
1340
1372
|
});
|
|
1341
1373
|
}
|
|
1342
1374
|
}
|
|
@@ -1435,6 +1467,8 @@ export class JSASTAnalyzer extends Plugin {
|
|
|
1435
1467
|
variableReassignments,
|
|
1436
1468
|
// Return statement tracking
|
|
1437
1469
|
returnStatements,
|
|
1470
|
+
// Yield expression tracking (REG-270)
|
|
1471
|
+
yieldExpressions,
|
|
1438
1472
|
// Update expression tracking (REG-288, REG-312)
|
|
1439
1473
|
updateExpressions,
|
|
1440
1474
|
// Promise resolution tracking (REG-334)
|
|
@@ -1442,7 +1476,16 @@ export class JSASTAnalyzer extends Plugin {
|
|
|
1442
1476
|
// Object/Array literal tracking - use allCollections refs as visitors may have created new arrays
|
|
1443
1477
|
objectLiterals: allCollections.objectLiterals || objectLiterals,
|
|
1444
1478
|
objectProperties: allCollections.objectProperties || objectProperties,
|
|
1445
|
-
arrayLiterals: allCollections.arrayLiterals || arrayLiterals
|
|
1479
|
+
arrayLiterals: allCollections.arrayLiterals || arrayLiterals,
|
|
1480
|
+
// REG-311: Async error tracking
|
|
1481
|
+
rejectionPatterns: Array.isArray(allCollections.rejectionPatterns)
|
|
1482
|
+
? allCollections.rejectionPatterns
|
|
1483
|
+
: rejectionPatterns,
|
|
1484
|
+
catchesFromInfos: Array.isArray(allCollections.catchesFromInfos)
|
|
1485
|
+
? allCollections.catchesFromInfos
|
|
1486
|
+
: catchesFromInfos,
|
|
1487
|
+
// Property access tracking (REG-395)
|
|
1488
|
+
propertyAccesses: allCollections.propertyAccesses || propertyAccesses
|
|
1446
1489
|
});
|
|
1447
1490
|
this.profiler.end('graph_build');
|
|
1448
1491
|
nodesCreated = result.nodes;
|
|
@@ -1830,6 +1873,8 @@ export class JSASTAnalyzer extends Plugin {
|
|
|
1830
1873
|
// Phase 6 (REG-267): Mark that this function has try/catch
|
|
1831
1874
|
if (controlFlowState) {
|
|
1832
1875
|
controlFlowState.hasTryCatch = true;
|
|
1876
|
+
// REG-311: Increment try block depth for O(1) isInsideTry detection
|
|
1877
|
+
controlFlowState.tryBlockDepth++;
|
|
1833
1878
|
}
|
|
1834
1879
|
// Determine actual parent - use stack for nested structures, otherwise original parentScopeId
|
|
1835
1880
|
const actualParentScopeId = (scopeIdStack && scopeIdStack.length > 0)
|
|
@@ -1955,7 +2000,12 @@ export class JSASTAnalyzer extends Plugin {
|
|
|
1955
2000
|
},
|
|
1956
2001
|
exit: (tryPath) => {
|
|
1957
2002
|
const tryNode = tryPath.node;
|
|
1958
|
-
const
|
|
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
|
+
}
|
|
1959
2009
|
// Pop the current scope from stack (could be try, catch, or finally)
|
|
1960
2010
|
if (scopeIdStack) {
|
|
1961
2011
|
scopeIdStack.pop();
|
|
@@ -1980,7 +2030,7 @@ export class JSASTAnalyzer extends Plugin {
|
|
|
1980
2030
|
* @param tryScopeMap - Map to track try/catch/finally scope transitions
|
|
1981
2031
|
* @param scopeIdStack - Stack for tracking current scope ID for CONTAINS edges
|
|
1982
2032
|
*/
|
|
1983
|
-
createCatchClauseHandler(module, variableDeclarations, varDeclCounterRef, scopeTracker, tryScopeMap, scopeIdStack) {
|
|
2033
|
+
createCatchClauseHandler(module, variableDeclarations, varDeclCounterRef, scopeTracker, tryScopeMap, scopeIdStack, controlFlowState) {
|
|
1984
2034
|
return {
|
|
1985
2035
|
enter: (catchPath) => {
|
|
1986
2036
|
const catchNode = catchPath.node;
|
|
@@ -2002,6 +2052,11 @@ export class JSASTAnalyzer extends Plugin {
|
|
|
2002
2052
|
scopeTracker.exitScope();
|
|
2003
2053
|
scopeTracker.enterCountedScope('catch');
|
|
2004
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
|
+
}
|
|
2005
2060
|
scopeInfo.currentBlock = 'catch';
|
|
2006
2061
|
}
|
|
2007
2062
|
// Handle catch parameter (e.g., catch (e) or catch ({ message }))
|
|
@@ -2311,6 +2366,172 @@ export class JSASTAnalyzer extends Plugin {
|
|
|
2311
2366
|
}
|
|
2312
2367
|
return parts.join('.');
|
|
2313
2368
|
}
|
|
2369
|
+
/**
|
|
2370
|
+
* Extract return expression info from an expression node.
|
|
2371
|
+
* Used for both explicit return statements and implicit arrow returns.
|
|
2372
|
+
*
|
|
2373
|
+
* This method consolidates ~450 lines of duplicated expression handling code
|
|
2374
|
+
* from three locations:
|
|
2375
|
+
* 1. Top-level implicit arrow returns (arrow function expression body)
|
|
2376
|
+
* 2. ReturnStatement handler (explicit returns)
|
|
2377
|
+
* 3. Nested arrow function implicit returns
|
|
2378
|
+
*
|
|
2379
|
+
* @param expr - The expression being returned
|
|
2380
|
+
* @param module - Module info for file context
|
|
2381
|
+
* @param literals - Collection to add literal nodes to
|
|
2382
|
+
* @param literalCounterRef - Counter for generating unique literal IDs
|
|
2383
|
+
* @param baseLine - Line number for literal ID generation
|
|
2384
|
+
* @param baseColumn - Column number for literal ID generation
|
|
2385
|
+
* @param literalIdSuffix - 'return' or 'implicit_return'
|
|
2386
|
+
* @returns Partial ReturnStatementInfo with expression-specific fields
|
|
2387
|
+
*/
|
|
2388
|
+
extractReturnExpressionInfo(expr, module, literals, literalCounterRef, baseLine, baseColumn, literalIdSuffix = 'return') {
|
|
2389
|
+
const exprLine = getLine(expr);
|
|
2390
|
+
const exprColumn = getColumn(expr);
|
|
2391
|
+
// Identifier (variable reference)
|
|
2392
|
+
if (t.isIdentifier(expr)) {
|
|
2393
|
+
return {
|
|
2394
|
+
returnValueType: 'VARIABLE',
|
|
2395
|
+
returnValueName: expr.name,
|
|
2396
|
+
};
|
|
2397
|
+
}
|
|
2398
|
+
// TemplateLiteral must come BEFORE isLiteral (TemplateLiteral extends Literal)
|
|
2399
|
+
if (t.isTemplateLiteral(expr)) {
|
|
2400
|
+
const sourceNames = [];
|
|
2401
|
+
for (const embedded of expr.expressions) {
|
|
2402
|
+
if (t.isIdentifier(embedded)) {
|
|
2403
|
+
sourceNames.push(embedded.name);
|
|
2404
|
+
}
|
|
2405
|
+
}
|
|
2406
|
+
return {
|
|
2407
|
+
returnValueType: 'EXPRESSION',
|
|
2408
|
+
expressionType: 'TemplateLiteral',
|
|
2409
|
+
returnValueLine: exprLine,
|
|
2410
|
+
returnValueColumn: exprColumn,
|
|
2411
|
+
returnValueId: NodeFactory.generateExpressionId('TemplateLiteral', module.file, exprLine, exprColumn),
|
|
2412
|
+
...(sourceNames.length > 0 ? { expressionSourceNames: sourceNames } : {}),
|
|
2413
|
+
};
|
|
2414
|
+
}
|
|
2415
|
+
// Literal values (after TemplateLiteral check)
|
|
2416
|
+
if (t.isLiteral(expr)) {
|
|
2417
|
+
const literalId = `LITERAL#${literalIdSuffix}#${module.file}#${baseLine}:${baseColumn}:${literalCounterRef.value++}`;
|
|
2418
|
+
literals.push({
|
|
2419
|
+
id: literalId,
|
|
2420
|
+
type: 'LITERAL',
|
|
2421
|
+
value: ExpressionEvaluator.extractLiteralValue(expr),
|
|
2422
|
+
valueType: typeof ExpressionEvaluator.extractLiteralValue(expr),
|
|
2423
|
+
file: module.file,
|
|
2424
|
+
line: exprLine,
|
|
2425
|
+
column: exprColumn,
|
|
2426
|
+
});
|
|
2427
|
+
return {
|
|
2428
|
+
returnValueType: 'LITERAL',
|
|
2429
|
+
returnValueId: literalId,
|
|
2430
|
+
};
|
|
2431
|
+
}
|
|
2432
|
+
// Direct function call: return foo()
|
|
2433
|
+
if (t.isCallExpression(expr) && t.isIdentifier(expr.callee)) {
|
|
2434
|
+
return {
|
|
2435
|
+
returnValueType: 'CALL_SITE',
|
|
2436
|
+
returnValueLine: exprLine,
|
|
2437
|
+
returnValueColumn: exprColumn,
|
|
2438
|
+
returnValueCallName: expr.callee.name,
|
|
2439
|
+
};
|
|
2440
|
+
}
|
|
2441
|
+
// Method call: return obj.method()
|
|
2442
|
+
if (t.isCallExpression(expr) && t.isMemberExpression(expr.callee)) {
|
|
2443
|
+
return {
|
|
2444
|
+
returnValueType: 'METHOD_CALL',
|
|
2445
|
+
returnValueLine: exprLine,
|
|
2446
|
+
returnValueColumn: exprColumn,
|
|
2447
|
+
returnValueCallName: t.isIdentifier(expr.callee.property)
|
|
2448
|
+
? expr.callee.property.name
|
|
2449
|
+
: undefined,
|
|
2450
|
+
};
|
|
2451
|
+
}
|
|
2452
|
+
// BinaryExpression: return a + b
|
|
2453
|
+
if (t.isBinaryExpression(expr)) {
|
|
2454
|
+
return {
|
|
2455
|
+
returnValueType: 'EXPRESSION',
|
|
2456
|
+
expressionType: 'BinaryExpression',
|
|
2457
|
+
returnValueLine: exprLine,
|
|
2458
|
+
returnValueColumn: exprColumn,
|
|
2459
|
+
operator: expr.operator,
|
|
2460
|
+
returnValueId: NodeFactory.generateExpressionId('BinaryExpression', module.file, exprLine, exprColumn),
|
|
2461
|
+
leftSourceName: t.isIdentifier(expr.left) ? expr.left.name : undefined,
|
|
2462
|
+
rightSourceName: t.isIdentifier(expr.right) ? expr.right.name : undefined,
|
|
2463
|
+
};
|
|
2464
|
+
}
|
|
2465
|
+
// LogicalExpression: return a && b, return a || b
|
|
2466
|
+
if (t.isLogicalExpression(expr)) {
|
|
2467
|
+
return {
|
|
2468
|
+
returnValueType: 'EXPRESSION',
|
|
2469
|
+
expressionType: 'LogicalExpression',
|
|
2470
|
+
returnValueLine: exprLine,
|
|
2471
|
+
returnValueColumn: exprColumn,
|
|
2472
|
+
operator: expr.operator,
|
|
2473
|
+
returnValueId: NodeFactory.generateExpressionId('LogicalExpression', module.file, exprLine, exprColumn),
|
|
2474
|
+
leftSourceName: t.isIdentifier(expr.left) ? expr.left.name : undefined,
|
|
2475
|
+
rightSourceName: t.isIdentifier(expr.right) ? expr.right.name : undefined,
|
|
2476
|
+
};
|
|
2477
|
+
}
|
|
2478
|
+
// ConditionalExpression: return condition ? a : b
|
|
2479
|
+
if (t.isConditionalExpression(expr)) {
|
|
2480
|
+
return {
|
|
2481
|
+
returnValueType: 'EXPRESSION',
|
|
2482
|
+
expressionType: 'ConditionalExpression',
|
|
2483
|
+
returnValueLine: exprLine,
|
|
2484
|
+
returnValueColumn: exprColumn,
|
|
2485
|
+
returnValueId: NodeFactory.generateExpressionId('ConditionalExpression', module.file, exprLine, exprColumn),
|
|
2486
|
+
consequentSourceName: t.isIdentifier(expr.consequent) ? expr.consequent.name : undefined,
|
|
2487
|
+
alternateSourceName: t.isIdentifier(expr.alternate) ? expr.alternate.name : undefined,
|
|
2488
|
+
};
|
|
2489
|
+
}
|
|
2490
|
+
// UnaryExpression: return !x, return -x
|
|
2491
|
+
if (t.isUnaryExpression(expr)) {
|
|
2492
|
+
return {
|
|
2493
|
+
returnValueType: 'EXPRESSION',
|
|
2494
|
+
expressionType: 'UnaryExpression',
|
|
2495
|
+
returnValueLine: exprLine,
|
|
2496
|
+
returnValueColumn: exprColumn,
|
|
2497
|
+
operator: expr.operator,
|
|
2498
|
+
returnValueId: NodeFactory.generateExpressionId('UnaryExpression', module.file, exprLine, exprColumn),
|
|
2499
|
+
unaryArgSourceName: t.isIdentifier(expr.argument) ? expr.argument.name : undefined,
|
|
2500
|
+
};
|
|
2501
|
+
}
|
|
2502
|
+
// MemberExpression (property access): return obj.prop
|
|
2503
|
+
if (t.isMemberExpression(expr)) {
|
|
2504
|
+
return {
|
|
2505
|
+
returnValueType: 'EXPRESSION',
|
|
2506
|
+
expressionType: 'MemberExpression',
|
|
2507
|
+
returnValueLine: exprLine,
|
|
2508
|
+
returnValueColumn: exprColumn,
|
|
2509
|
+
returnValueId: NodeFactory.generateExpressionId('MemberExpression', module.file, exprLine, exprColumn),
|
|
2510
|
+
object: t.isIdentifier(expr.object) ? expr.object.name : undefined,
|
|
2511
|
+
objectSourceName: t.isIdentifier(expr.object) ? expr.object.name : undefined,
|
|
2512
|
+
property: t.isIdentifier(expr.property) ? expr.property.name : undefined,
|
|
2513
|
+
computed: expr.computed,
|
|
2514
|
+
};
|
|
2515
|
+
}
|
|
2516
|
+
// NewExpression: return new Foo()
|
|
2517
|
+
if (t.isNewExpression(expr)) {
|
|
2518
|
+
return {
|
|
2519
|
+
returnValueType: 'EXPRESSION',
|
|
2520
|
+
expressionType: 'NewExpression',
|
|
2521
|
+
returnValueLine: exprLine,
|
|
2522
|
+
returnValueColumn: exprColumn,
|
|
2523
|
+
returnValueId: NodeFactory.generateExpressionId('NewExpression', module.file, exprLine, exprColumn),
|
|
2524
|
+
};
|
|
2525
|
+
}
|
|
2526
|
+
// Fallback for other expression types
|
|
2527
|
+
return {
|
|
2528
|
+
returnValueType: 'EXPRESSION',
|
|
2529
|
+
expressionType: expr.type,
|
|
2530
|
+
returnValueLine: exprLine,
|
|
2531
|
+
returnValueColumn: exprColumn,
|
|
2532
|
+
returnValueId: NodeFactory.generateExpressionId(expr.type, module.file, exprLine, exprColumn),
|
|
2533
|
+
};
|
|
2534
|
+
}
|
|
2314
2535
|
/**
|
|
2315
2536
|
* Factory method to create IfStatement handler.
|
|
2316
2537
|
* Creates BRANCH node for if statement and SCOPE nodes for if/else bodies.
|
|
@@ -2587,11 +2808,11 @@ export class JSASTAnalyzer extends Plugin {
|
|
|
2587
2808
|
const variableDeclarations = (collections.variableDeclarations ?? []);
|
|
2588
2809
|
const callSites = (collections.callSites ?? []);
|
|
2589
2810
|
const methodCalls = (collections.methodCalls ?? []);
|
|
2590
|
-
const
|
|
2591
|
-
const
|
|
2811
|
+
const _eventListeners = (collections.eventListeners ?? []);
|
|
2812
|
+
const _methodCallbacks = (collections.methodCallbacks ?? []);
|
|
2592
2813
|
const classInstantiations = (collections.classInstantiations ?? []);
|
|
2593
2814
|
const constructorCalls = (collections.constructorCalls ?? []);
|
|
2594
|
-
const
|
|
2815
|
+
const _httpRequests = (collections.httpRequests ?? []);
|
|
2595
2816
|
const literals = (collections.literals ?? []);
|
|
2596
2817
|
const variableAssignments = (collections.variableAssignments ?? []);
|
|
2597
2818
|
const ifScopeCounterRef = (collections.ifScopeCounterRef ?? { value: 0 });
|
|
@@ -2599,9 +2820,9 @@ export class JSASTAnalyzer extends Plugin {
|
|
|
2599
2820
|
const varDeclCounterRef = (collections.varDeclCounterRef ?? { value: 0 });
|
|
2600
2821
|
const callSiteCounterRef = (collections.callSiteCounterRef ?? { value: 0 });
|
|
2601
2822
|
const functionCounterRef = (collections.functionCounterRef ?? { value: 0 });
|
|
2602
|
-
const
|
|
2823
|
+
const _httpRequestCounterRef = (collections.httpRequestCounterRef ?? { value: 0 });
|
|
2603
2824
|
const literalCounterRef = (collections.literalCounterRef ?? { value: 0 });
|
|
2604
|
-
const
|
|
2825
|
+
const _anonymousFunctionCounterRef = (collections.anonymousFunctionCounterRef ?? { value: 0 });
|
|
2605
2826
|
const scopeTracker = collections.scopeTracker;
|
|
2606
2827
|
// Object literal tracking (REG-328)
|
|
2607
2828
|
if (!collections.objectLiterals) {
|
|
@@ -2617,7 +2838,12 @@ export class JSASTAnalyzer extends Plugin {
|
|
|
2617
2838
|
const objectProperties = collections.objectProperties;
|
|
2618
2839
|
const objectLiteralCounterRef = collections.objectLiteralCounterRef;
|
|
2619
2840
|
const returnStatements = (collections.returnStatements ?? []);
|
|
2620
|
-
|
|
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 ?? []);
|
|
2621
2847
|
// Control flow collections (Phase 2: LOOP nodes)
|
|
2622
2848
|
// Initialize if not exist to ensure nested function calls share same arrays
|
|
2623
2849
|
if (!collections.loops) {
|
|
@@ -2642,9 +2868,9 @@ export class JSASTAnalyzer extends Plugin {
|
|
|
2642
2868
|
};
|
|
2643
2869
|
const parentScopeVariables = new Set();
|
|
2644
2870
|
const processedCallSites = processedNodes.callSites;
|
|
2645
|
-
const
|
|
2871
|
+
const _processedVarDecls = processedNodes.varDecls;
|
|
2646
2872
|
const processedMethodCalls = processedNodes.methodCalls;
|
|
2647
|
-
const
|
|
2873
|
+
const _processedEventListeners = processedNodes.eventListeners;
|
|
2648
2874
|
// Track if/else scope transitions (Phase 3: extended with branchId)
|
|
2649
2875
|
const ifElseScopeMap = new Map();
|
|
2650
2876
|
// Ensure branches and branchCounterRef are initialized (used by IfStatement and SwitchStatement)
|
|
@@ -2694,19 +2920,35 @@ export class JSASTAnalyzer extends Plugin {
|
|
|
2694
2920
|
collections.promiseResolutions = [];
|
|
2695
2921
|
}
|
|
2696
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;
|
|
2697
2932
|
// Dynamic scope ID stack for CONTAINS edges
|
|
2698
2933
|
// Starts with the function body scope, gets updated as we enter/exit conditional scopes
|
|
2699
2934
|
const scopeIdStack = [parentScopeId];
|
|
2700
2935
|
const getCurrentScopeId = () => scopeIdStack[scopeIdStack.length - 1];
|
|
2701
2936
|
// Determine the ID of the function we're analyzing for RETURNS edges
|
|
2702
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)
|
|
2703
2939
|
const funcNode = funcPath.node;
|
|
2940
|
+
const functionNode = t.isFunction(funcNode) ? funcNode : null;
|
|
2941
|
+
const functionPath = functionNode ? funcPath : null;
|
|
2704
2942
|
const funcLine = getLine(funcNode);
|
|
2705
2943
|
const funcColumn = getColumn(funcNode);
|
|
2706
2944
|
let currentFunctionId = null;
|
|
2707
|
-
|
|
2708
|
-
|
|
2709
|
-
|
|
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;
|
|
2710
2952
|
if (matchingFunction) {
|
|
2711
2953
|
currentFunctionId = matchingFunction.id;
|
|
2712
2954
|
}
|
|
@@ -2720,7 +2962,9 @@ export class JSASTAnalyzer extends Plugin {
|
|
|
2720
2962
|
hasEarlyReturn: false,
|
|
2721
2963
|
hasThrow: false,
|
|
2722
2964
|
returnCount: 0, // Track total return count for early return detection
|
|
2723
|
-
totalStatements: 0 // Track if there are statements after returns
|
|
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
|
|
2724
2968
|
};
|
|
2725
2969
|
// Handle implicit return for THIS arrow function if it has an expression body
|
|
2726
2970
|
// e.g., `const double = x => x * 2;` - the function we're analyzing IS an arrow with expression body
|
|
@@ -2728,130 +2972,17 @@ export class JSASTAnalyzer extends Plugin {
|
|
|
2728
2972
|
const bodyExpr = funcNode.body;
|
|
2729
2973
|
const bodyLine = getLine(bodyExpr);
|
|
2730
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');
|
|
2731
2977
|
const returnInfo = {
|
|
2732
2978
|
parentFunctionId: currentFunctionId,
|
|
2733
2979
|
file: module.file,
|
|
2734
2980
|
line: bodyLine,
|
|
2735
2981
|
column: bodyColumn,
|
|
2736
2982
|
returnValueType: 'NONE',
|
|
2737
|
-
isImplicitReturn: true
|
|
2983
|
+
isImplicitReturn: true,
|
|
2984
|
+
...exprInfo,
|
|
2738
2985
|
};
|
|
2739
|
-
// Apply type detection logic for the implicit return
|
|
2740
|
-
if (t.isIdentifier(bodyExpr)) {
|
|
2741
|
-
returnInfo.returnValueType = 'VARIABLE';
|
|
2742
|
-
returnInfo.returnValueName = bodyExpr.name;
|
|
2743
|
-
}
|
|
2744
|
-
// TemplateLiteral must come BEFORE isLiteral (TemplateLiteral extends Literal)
|
|
2745
|
-
else if (t.isTemplateLiteral(bodyExpr)) {
|
|
2746
|
-
returnInfo.returnValueType = 'EXPRESSION';
|
|
2747
|
-
returnInfo.expressionType = 'TemplateLiteral';
|
|
2748
|
-
returnInfo.returnValueLine = getLine(bodyExpr);
|
|
2749
|
-
returnInfo.returnValueColumn = getColumn(bodyExpr);
|
|
2750
|
-
returnInfo.returnValueId = NodeFactory.generateExpressionId('TemplateLiteral', module.file, getLine(bodyExpr), getColumn(bodyExpr));
|
|
2751
|
-
const sourceNames = [];
|
|
2752
|
-
for (const expr of bodyExpr.expressions) {
|
|
2753
|
-
if (t.isIdentifier(expr))
|
|
2754
|
-
sourceNames.push(expr.name);
|
|
2755
|
-
}
|
|
2756
|
-
if (sourceNames.length > 0)
|
|
2757
|
-
returnInfo.expressionSourceNames = sourceNames;
|
|
2758
|
-
}
|
|
2759
|
-
else if (t.isLiteral(bodyExpr)) {
|
|
2760
|
-
returnInfo.returnValueType = 'LITERAL';
|
|
2761
|
-
const literalId = `LITERAL#implicit_return#${module.file}#${funcLine}:${funcColumn}:${literalCounterRef.value++}`;
|
|
2762
|
-
returnInfo.returnValueId = literalId;
|
|
2763
|
-
literals.push({
|
|
2764
|
-
id: literalId,
|
|
2765
|
-
type: 'LITERAL',
|
|
2766
|
-
value: ExpressionEvaluator.extractLiteralValue(bodyExpr),
|
|
2767
|
-
valueType: typeof ExpressionEvaluator.extractLiteralValue(bodyExpr),
|
|
2768
|
-
file: module.file,
|
|
2769
|
-
line: bodyLine,
|
|
2770
|
-
column: bodyColumn
|
|
2771
|
-
});
|
|
2772
|
-
}
|
|
2773
|
-
else if (t.isCallExpression(bodyExpr) && t.isIdentifier(bodyExpr.callee)) {
|
|
2774
|
-
returnInfo.returnValueType = 'CALL_SITE';
|
|
2775
|
-
returnInfo.returnValueLine = getLine(bodyExpr);
|
|
2776
|
-
returnInfo.returnValueColumn = getColumn(bodyExpr);
|
|
2777
|
-
returnInfo.returnValueCallName = bodyExpr.callee.name;
|
|
2778
|
-
}
|
|
2779
|
-
else if (t.isCallExpression(bodyExpr) && t.isMemberExpression(bodyExpr.callee)) {
|
|
2780
|
-
returnInfo.returnValueType = 'METHOD_CALL';
|
|
2781
|
-
returnInfo.returnValueLine = getLine(bodyExpr);
|
|
2782
|
-
returnInfo.returnValueColumn = getColumn(bodyExpr);
|
|
2783
|
-
if (t.isIdentifier(bodyExpr.callee.property)) {
|
|
2784
|
-
returnInfo.returnValueCallName = bodyExpr.callee.property.name;
|
|
2785
|
-
}
|
|
2786
|
-
}
|
|
2787
|
-
// REG-276: Detailed EXPRESSION handling for implicit arrow returns
|
|
2788
|
-
else if (t.isBinaryExpression(bodyExpr)) {
|
|
2789
|
-
returnInfo.returnValueType = 'EXPRESSION';
|
|
2790
|
-
returnInfo.expressionType = 'BinaryExpression';
|
|
2791
|
-
returnInfo.returnValueLine = getLine(bodyExpr);
|
|
2792
|
-
returnInfo.returnValueColumn = getColumn(bodyExpr);
|
|
2793
|
-
returnInfo.operator = bodyExpr.operator;
|
|
2794
|
-
returnInfo.returnValueId = NodeFactory.generateExpressionId('BinaryExpression', module.file, getLine(bodyExpr), getColumn(bodyExpr));
|
|
2795
|
-
if (t.isIdentifier(bodyExpr.left))
|
|
2796
|
-
returnInfo.leftSourceName = bodyExpr.left.name;
|
|
2797
|
-
if (t.isIdentifier(bodyExpr.right))
|
|
2798
|
-
returnInfo.rightSourceName = bodyExpr.right.name;
|
|
2799
|
-
}
|
|
2800
|
-
else if (t.isLogicalExpression(bodyExpr)) {
|
|
2801
|
-
returnInfo.returnValueType = 'EXPRESSION';
|
|
2802
|
-
returnInfo.expressionType = 'LogicalExpression';
|
|
2803
|
-
returnInfo.returnValueLine = getLine(bodyExpr);
|
|
2804
|
-
returnInfo.returnValueColumn = getColumn(bodyExpr);
|
|
2805
|
-
returnInfo.operator = bodyExpr.operator;
|
|
2806
|
-
returnInfo.returnValueId = NodeFactory.generateExpressionId('LogicalExpression', module.file, getLine(bodyExpr), getColumn(bodyExpr));
|
|
2807
|
-
if (t.isIdentifier(bodyExpr.left))
|
|
2808
|
-
returnInfo.leftSourceName = bodyExpr.left.name;
|
|
2809
|
-
if (t.isIdentifier(bodyExpr.right))
|
|
2810
|
-
returnInfo.rightSourceName = bodyExpr.right.name;
|
|
2811
|
-
}
|
|
2812
|
-
else if (t.isConditionalExpression(bodyExpr)) {
|
|
2813
|
-
returnInfo.returnValueType = 'EXPRESSION';
|
|
2814
|
-
returnInfo.expressionType = 'ConditionalExpression';
|
|
2815
|
-
returnInfo.returnValueLine = getLine(bodyExpr);
|
|
2816
|
-
returnInfo.returnValueColumn = getColumn(bodyExpr);
|
|
2817
|
-
returnInfo.returnValueId = NodeFactory.generateExpressionId('ConditionalExpression', module.file, getLine(bodyExpr), getColumn(bodyExpr));
|
|
2818
|
-
if (t.isIdentifier(bodyExpr.consequent))
|
|
2819
|
-
returnInfo.consequentSourceName = bodyExpr.consequent.name;
|
|
2820
|
-
if (t.isIdentifier(bodyExpr.alternate))
|
|
2821
|
-
returnInfo.alternateSourceName = bodyExpr.alternate.name;
|
|
2822
|
-
}
|
|
2823
|
-
else if (t.isUnaryExpression(bodyExpr)) {
|
|
2824
|
-
returnInfo.returnValueType = 'EXPRESSION';
|
|
2825
|
-
returnInfo.expressionType = 'UnaryExpression';
|
|
2826
|
-
returnInfo.returnValueLine = getLine(bodyExpr);
|
|
2827
|
-
returnInfo.returnValueColumn = getColumn(bodyExpr);
|
|
2828
|
-
returnInfo.operator = bodyExpr.operator;
|
|
2829
|
-
returnInfo.returnValueId = NodeFactory.generateExpressionId('UnaryExpression', module.file, getLine(bodyExpr), getColumn(bodyExpr));
|
|
2830
|
-
if (t.isIdentifier(bodyExpr.argument))
|
|
2831
|
-
returnInfo.unaryArgSourceName = bodyExpr.argument.name;
|
|
2832
|
-
}
|
|
2833
|
-
else if (t.isMemberExpression(bodyExpr)) {
|
|
2834
|
-
returnInfo.returnValueType = 'EXPRESSION';
|
|
2835
|
-
returnInfo.expressionType = 'MemberExpression';
|
|
2836
|
-
returnInfo.returnValueLine = getLine(bodyExpr);
|
|
2837
|
-
returnInfo.returnValueColumn = getColumn(bodyExpr);
|
|
2838
|
-
returnInfo.returnValueId = NodeFactory.generateExpressionId('MemberExpression', module.file, getLine(bodyExpr), getColumn(bodyExpr));
|
|
2839
|
-
if (t.isIdentifier(bodyExpr.object)) {
|
|
2840
|
-
returnInfo.object = bodyExpr.object.name;
|
|
2841
|
-
returnInfo.objectSourceName = bodyExpr.object.name;
|
|
2842
|
-
}
|
|
2843
|
-
if (t.isIdentifier(bodyExpr.property))
|
|
2844
|
-
returnInfo.property = bodyExpr.property.name;
|
|
2845
|
-
returnInfo.computed = bodyExpr.computed;
|
|
2846
|
-
}
|
|
2847
|
-
else {
|
|
2848
|
-
// Fallback: any other expression type
|
|
2849
|
-
returnInfo.returnValueType = 'EXPRESSION';
|
|
2850
|
-
returnInfo.expressionType = bodyExpr.type;
|
|
2851
|
-
returnInfo.returnValueLine = getLine(bodyExpr);
|
|
2852
|
-
returnInfo.returnValueColumn = getColumn(bodyExpr);
|
|
2853
|
-
returnInfo.returnValueId = NodeFactory.generateExpressionId(bodyExpr.type, module.file, getLine(bodyExpr), getColumn(bodyExpr));
|
|
2854
|
-
}
|
|
2855
2986
|
returnStatements.push(returnInfo);
|
|
2856
2987
|
}
|
|
2857
2988
|
funcPath.traverse({
|
|
@@ -2879,7 +3010,7 @@ export class JSASTAnalyzer extends Plugin {
|
|
|
2879
3010
|
}
|
|
2880
3011
|
const arrayMutations = collections.arrayMutations;
|
|
2881
3012
|
// Check for indexed array assignment: arr[i] = value
|
|
2882
|
-
this.detectIndexedArrayAssignment(assignNode, module, arrayMutations, scopeTracker);
|
|
3013
|
+
this.detectIndexedArrayAssignment(assignNode, module, arrayMutations, scopeTracker, collections);
|
|
2883
3014
|
// Initialize object mutations collection if not exists
|
|
2884
3015
|
if (!collections.objectMutations) {
|
|
2885
3016
|
collections.objectMutations = [];
|
|
@@ -2934,168 +3065,20 @@ export class JSASTAnalyzer extends Plugin {
|
|
|
2934
3065
|
return;
|
|
2935
3066
|
}
|
|
2936
3067
|
const arg = returnNode.argument;
|
|
2937
|
-
//
|
|
3068
|
+
// Extract expression-specific info using shared method
|
|
3069
|
+
const exprInfo = this.extractReturnExpressionInfo(arg, module, literals, literalCounterRef, returnLine, returnColumn, 'return');
|
|
2938
3070
|
const returnInfo = {
|
|
2939
3071
|
parentFunctionId: currentFunctionId,
|
|
2940
3072
|
file: module.file,
|
|
2941
3073
|
line: returnLine,
|
|
2942
3074
|
column: returnColumn,
|
|
2943
|
-
returnValueType: 'NONE'
|
|
3075
|
+
returnValueType: 'NONE',
|
|
3076
|
+
...exprInfo,
|
|
2944
3077
|
};
|
|
2945
|
-
// Identifier (variable reference)
|
|
2946
|
-
if (t.isIdentifier(arg)) {
|
|
2947
|
-
returnInfo.returnValueType = 'VARIABLE';
|
|
2948
|
-
returnInfo.returnValueName = arg.name;
|
|
2949
|
-
}
|
|
2950
|
-
// TemplateLiteral must come BEFORE isLiteral (TemplateLiteral extends Literal)
|
|
2951
|
-
else if (t.isTemplateLiteral(arg)) {
|
|
2952
|
-
returnInfo.returnValueType = 'EXPRESSION';
|
|
2953
|
-
returnInfo.expressionType = 'TemplateLiteral';
|
|
2954
|
-
returnInfo.returnValueLine = getLine(arg);
|
|
2955
|
-
returnInfo.returnValueColumn = getColumn(arg);
|
|
2956
|
-
returnInfo.returnValueId = NodeFactory.generateExpressionId('TemplateLiteral', module.file, getLine(arg), getColumn(arg));
|
|
2957
|
-
// Extract all embedded expression identifiers
|
|
2958
|
-
const sourceNames = [];
|
|
2959
|
-
for (const expr of arg.expressions) {
|
|
2960
|
-
if (t.isIdentifier(expr)) {
|
|
2961
|
-
sourceNames.push(expr.name);
|
|
2962
|
-
}
|
|
2963
|
-
}
|
|
2964
|
-
if (sourceNames.length > 0) {
|
|
2965
|
-
returnInfo.expressionSourceNames = sourceNames;
|
|
2966
|
-
}
|
|
2967
|
-
}
|
|
2968
|
-
// Literal values (after TemplateLiteral check)
|
|
2969
|
-
else if (t.isLiteral(arg)) {
|
|
2970
|
-
returnInfo.returnValueType = 'LITERAL';
|
|
2971
|
-
// Create a LITERAL node ID for this return value
|
|
2972
|
-
const literalId = `LITERAL#return#${module.file}#${returnLine}:${returnColumn}:${literalCounterRef.value++}`;
|
|
2973
|
-
returnInfo.returnValueId = literalId;
|
|
2974
|
-
// Also add to literals collection for node creation
|
|
2975
|
-
literals.push({
|
|
2976
|
-
id: literalId,
|
|
2977
|
-
type: 'LITERAL',
|
|
2978
|
-
value: ExpressionEvaluator.extractLiteralValue(arg),
|
|
2979
|
-
valueType: typeof ExpressionEvaluator.extractLiteralValue(arg),
|
|
2980
|
-
file: module.file,
|
|
2981
|
-
line: returnLine,
|
|
2982
|
-
column: returnColumn
|
|
2983
|
-
});
|
|
2984
|
-
}
|
|
2985
|
-
// Direct function call: return foo()
|
|
2986
|
-
else if (t.isCallExpression(arg) && t.isIdentifier(arg.callee)) {
|
|
2987
|
-
returnInfo.returnValueType = 'CALL_SITE';
|
|
2988
|
-
returnInfo.returnValueLine = getLine(arg);
|
|
2989
|
-
returnInfo.returnValueColumn = getColumn(arg);
|
|
2990
|
-
returnInfo.returnValueCallName = arg.callee.name;
|
|
2991
|
-
}
|
|
2992
|
-
// Method call: return obj.method()
|
|
2993
|
-
else if (t.isCallExpression(arg) && t.isMemberExpression(arg.callee)) {
|
|
2994
|
-
returnInfo.returnValueType = 'METHOD_CALL';
|
|
2995
|
-
returnInfo.returnValueLine = getLine(arg);
|
|
2996
|
-
returnInfo.returnValueColumn = getColumn(arg);
|
|
2997
|
-
// Extract method name for lookup
|
|
2998
|
-
if (t.isIdentifier(arg.callee.property)) {
|
|
2999
|
-
returnInfo.returnValueCallName = arg.callee.property.name;
|
|
3000
|
-
}
|
|
3001
|
-
}
|
|
3002
|
-
// BinaryExpression: return a + b
|
|
3003
|
-
else if (t.isBinaryExpression(arg)) {
|
|
3004
|
-
returnInfo.returnValueType = 'EXPRESSION';
|
|
3005
|
-
returnInfo.expressionType = 'BinaryExpression';
|
|
3006
|
-
returnInfo.returnValueLine = getLine(arg);
|
|
3007
|
-
returnInfo.returnValueColumn = getColumn(arg);
|
|
3008
|
-
returnInfo.operator = arg.operator;
|
|
3009
|
-
// Generate stable ID for the EXPRESSION node
|
|
3010
|
-
returnInfo.returnValueId = NodeFactory.generateExpressionId('BinaryExpression', module.file, getLine(arg), getColumn(arg));
|
|
3011
|
-
// Extract left operand source
|
|
3012
|
-
if (t.isIdentifier(arg.left)) {
|
|
3013
|
-
returnInfo.leftSourceName = arg.left.name;
|
|
3014
|
-
}
|
|
3015
|
-
// Extract right operand source
|
|
3016
|
-
if (t.isIdentifier(arg.right)) {
|
|
3017
|
-
returnInfo.rightSourceName = arg.right.name;
|
|
3018
|
-
}
|
|
3019
|
-
}
|
|
3020
|
-
// LogicalExpression: return a && b, return a || b
|
|
3021
|
-
else if (t.isLogicalExpression(arg)) {
|
|
3022
|
-
returnInfo.returnValueType = 'EXPRESSION';
|
|
3023
|
-
returnInfo.expressionType = 'LogicalExpression';
|
|
3024
|
-
returnInfo.returnValueLine = getLine(arg);
|
|
3025
|
-
returnInfo.returnValueColumn = getColumn(arg);
|
|
3026
|
-
returnInfo.operator = arg.operator;
|
|
3027
|
-
returnInfo.returnValueId = NodeFactory.generateExpressionId('LogicalExpression', module.file, getLine(arg), getColumn(arg));
|
|
3028
|
-
if (t.isIdentifier(arg.left)) {
|
|
3029
|
-
returnInfo.leftSourceName = arg.left.name;
|
|
3030
|
-
}
|
|
3031
|
-
if (t.isIdentifier(arg.right)) {
|
|
3032
|
-
returnInfo.rightSourceName = arg.right.name;
|
|
3033
|
-
}
|
|
3034
|
-
}
|
|
3035
|
-
// ConditionalExpression: return condition ? a : b
|
|
3036
|
-
else if (t.isConditionalExpression(arg)) {
|
|
3037
|
-
returnInfo.returnValueType = 'EXPRESSION';
|
|
3038
|
-
returnInfo.expressionType = 'ConditionalExpression';
|
|
3039
|
-
returnInfo.returnValueLine = getLine(arg);
|
|
3040
|
-
returnInfo.returnValueColumn = getColumn(arg);
|
|
3041
|
-
returnInfo.returnValueId = NodeFactory.generateExpressionId('ConditionalExpression', module.file, getLine(arg), getColumn(arg));
|
|
3042
|
-
// Extract consequent (then branch) source
|
|
3043
|
-
if (t.isIdentifier(arg.consequent)) {
|
|
3044
|
-
returnInfo.consequentSourceName = arg.consequent.name;
|
|
3045
|
-
}
|
|
3046
|
-
// Extract alternate (else branch) source
|
|
3047
|
-
if (t.isIdentifier(arg.alternate)) {
|
|
3048
|
-
returnInfo.alternateSourceName = arg.alternate.name;
|
|
3049
|
-
}
|
|
3050
|
-
}
|
|
3051
|
-
// UnaryExpression: return !x, return -x
|
|
3052
|
-
else if (t.isUnaryExpression(arg)) {
|
|
3053
|
-
returnInfo.returnValueType = 'EXPRESSION';
|
|
3054
|
-
returnInfo.expressionType = 'UnaryExpression';
|
|
3055
|
-
returnInfo.returnValueLine = getLine(arg);
|
|
3056
|
-
returnInfo.returnValueColumn = getColumn(arg);
|
|
3057
|
-
returnInfo.operator = arg.operator;
|
|
3058
|
-
returnInfo.returnValueId = NodeFactory.generateExpressionId('UnaryExpression', module.file, getLine(arg), getColumn(arg));
|
|
3059
|
-
if (t.isIdentifier(arg.argument)) {
|
|
3060
|
-
returnInfo.unaryArgSourceName = arg.argument.name;
|
|
3061
|
-
}
|
|
3062
|
-
}
|
|
3063
|
-
// MemberExpression (property access): return obj.prop
|
|
3064
|
-
else if (t.isMemberExpression(arg)) {
|
|
3065
|
-
returnInfo.returnValueType = 'EXPRESSION';
|
|
3066
|
-
returnInfo.expressionType = 'MemberExpression';
|
|
3067
|
-
returnInfo.returnValueLine = getLine(arg);
|
|
3068
|
-
returnInfo.returnValueColumn = getColumn(arg);
|
|
3069
|
-
returnInfo.returnValueId = NodeFactory.generateExpressionId('MemberExpression', module.file, getLine(arg), getColumn(arg));
|
|
3070
|
-
// Extract object.property info
|
|
3071
|
-
if (t.isIdentifier(arg.object)) {
|
|
3072
|
-
returnInfo.object = arg.object.name;
|
|
3073
|
-
returnInfo.objectSourceName = arg.object.name;
|
|
3074
|
-
}
|
|
3075
|
-
if (t.isIdentifier(arg.property)) {
|
|
3076
|
-
returnInfo.property = arg.property.name;
|
|
3077
|
-
}
|
|
3078
|
-
returnInfo.computed = arg.computed;
|
|
3079
|
-
}
|
|
3080
|
-
// NewExpression: return new Foo()
|
|
3081
|
-
else if (t.isNewExpression(arg)) {
|
|
3082
|
-
returnInfo.returnValueType = 'EXPRESSION';
|
|
3083
|
-
returnInfo.expressionType = 'NewExpression';
|
|
3084
|
-
returnInfo.returnValueLine = getLine(arg);
|
|
3085
|
-
returnInfo.returnValueColumn = getColumn(arg);
|
|
3086
|
-
returnInfo.returnValueId = NodeFactory.generateExpressionId('NewExpression', module.file, getLine(arg), getColumn(arg));
|
|
3087
|
-
}
|
|
3088
|
-
// Fallback for other expression types
|
|
3089
|
-
else {
|
|
3090
|
-
returnInfo.returnValueType = 'EXPRESSION';
|
|
3091
|
-
returnInfo.expressionType = arg.type;
|
|
3092
|
-
returnInfo.returnValueLine = getLine(arg);
|
|
3093
|
-
returnInfo.returnValueColumn = getColumn(arg);
|
|
3094
|
-
returnInfo.returnValueId = NodeFactory.generateExpressionId(arg.type, module.file, getLine(arg), getColumn(arg));
|
|
3095
|
-
}
|
|
3096
3078
|
returnStatements.push(returnInfo);
|
|
3097
3079
|
},
|
|
3098
3080
|
// Phase 6 (REG-267): Track throw statements for control flow metadata
|
|
3081
|
+
// REG-311: Also detect async_throw rejection patterns
|
|
3099
3082
|
ThrowStatement: (throwPath) => {
|
|
3100
3083
|
// Skip if this throw is inside a nested function (not the function we're analyzing)
|
|
3101
3084
|
let parent = throwPath.parentPath;
|
|
@@ -3107,6 +3090,122 @@ export class JSASTAnalyzer extends Plugin {
|
|
|
3107
3090
|
parent = parent.parentPath;
|
|
3108
3091
|
}
|
|
3109
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);
|
|
3110
3209
|
},
|
|
3111
3210
|
ForStatement: this.createLoopScopeHandler('for', 'for-loop', 'for', parentScopeId, module, scopes, loops, scopeCounterRef, loopCounterRef, scopeTracker, scopeIdStack, controlFlowState),
|
|
3112
3211
|
ForInStatement: this.createLoopScopeHandler('for-in', 'for-in-loop', 'for-in', parentScopeId, module, scopes, loops, scopeCounterRef, loopCounterRef, scopeTracker, scopeIdStack, controlFlowState),
|
|
@@ -3115,10 +3214,53 @@ export class JSASTAnalyzer extends Plugin {
|
|
|
3115
3214
|
DoWhileStatement: this.createLoopScopeHandler('do-while', 'do-while-loop', 'do-while', parentScopeId, module, scopes, loops, scopeCounterRef, loopCounterRef, scopeTracker, scopeIdStack, controlFlowState),
|
|
3116
3215
|
// Phase 4 (REG-267): Now creates TRY_BLOCK, CATCH_BLOCK, FINALLY_BLOCK nodes
|
|
3117
3216
|
TryStatement: this.createTryStatementHandler(parentScopeId, module, scopes, tryBlocks, catchBlocks, finallyBlocks, scopeCounterRef, tryBlockCounterRef, catchBlockCounterRef, finallyBlockCounterRef, scopeTracker, tryScopeMap, scopeIdStack, controlFlowState),
|
|
3118
|
-
CatchClause: this.createCatchClauseHandler(module, variableDeclarations, varDeclCounterRef, scopeTracker, tryScopeMap, scopeIdStack),
|
|
3217
|
+
CatchClause: this.createCatchClauseHandler(module, variableDeclarations, varDeclCounterRef, scopeTracker, tryScopeMap, scopeIdStack, controlFlowState),
|
|
3119
3218
|
SwitchStatement: (switchPath) => {
|
|
3120
3219
|
this.handleSwitchStatement(switchPath, parentScopeId, module, collections, scopeTracker, controlFlowState);
|
|
3121
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
|
+
},
|
|
3122
3264
|
FunctionExpression: (funcPath) => {
|
|
3123
3265
|
const node = funcPath.node;
|
|
3124
3266
|
const funcName = node.id ? node.id.name : this.generateAnonymousName(scopeTracker);
|
|
@@ -3222,129 +3364,17 @@ export class JSASTAnalyzer extends Plugin {
|
|
|
3222
3364
|
const bodyExpr = node.body;
|
|
3223
3365
|
const bodyLine = getLine(bodyExpr);
|
|
3224
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');
|
|
3225
3369
|
const returnInfo = {
|
|
3226
3370
|
parentFunctionId: functionId,
|
|
3227
3371
|
file: module.file,
|
|
3228
3372
|
line: bodyLine,
|
|
3229
3373
|
column: bodyColumn,
|
|
3230
3374
|
returnValueType: 'NONE',
|
|
3231
|
-
isImplicitReturn: true
|
|
3375
|
+
isImplicitReturn: true,
|
|
3376
|
+
...exprInfo,
|
|
3232
3377
|
};
|
|
3233
|
-
// Apply same type detection logic as ReturnStatement handler
|
|
3234
|
-
if (t.isIdentifier(bodyExpr)) {
|
|
3235
|
-
returnInfo.returnValueType = 'VARIABLE';
|
|
3236
|
-
returnInfo.returnValueName = bodyExpr.name;
|
|
3237
|
-
}
|
|
3238
|
-
// TemplateLiteral must come BEFORE isLiteral (TemplateLiteral extends Literal)
|
|
3239
|
-
else if (t.isTemplateLiteral(bodyExpr)) {
|
|
3240
|
-
returnInfo.returnValueType = 'EXPRESSION';
|
|
3241
|
-
returnInfo.expressionType = 'TemplateLiteral';
|
|
3242
|
-
returnInfo.returnValueLine = getLine(bodyExpr);
|
|
3243
|
-
returnInfo.returnValueColumn = getColumn(bodyExpr);
|
|
3244
|
-
returnInfo.returnValueId = NodeFactory.generateExpressionId('TemplateLiteral', module.file, getLine(bodyExpr), getColumn(bodyExpr));
|
|
3245
|
-
const sourceNames = [];
|
|
3246
|
-
for (const expr of bodyExpr.expressions) {
|
|
3247
|
-
if (t.isIdentifier(expr))
|
|
3248
|
-
sourceNames.push(expr.name);
|
|
3249
|
-
}
|
|
3250
|
-
if (sourceNames.length > 0)
|
|
3251
|
-
returnInfo.expressionSourceNames = sourceNames;
|
|
3252
|
-
}
|
|
3253
|
-
else if (t.isLiteral(bodyExpr)) {
|
|
3254
|
-
returnInfo.returnValueType = 'LITERAL';
|
|
3255
|
-
const literalId = `LITERAL#implicit_return#${module.file}#${line}:${column}:${literalCounterRef.value++}`;
|
|
3256
|
-
returnInfo.returnValueId = literalId;
|
|
3257
|
-
literals.push({
|
|
3258
|
-
id: literalId,
|
|
3259
|
-
type: 'LITERAL',
|
|
3260
|
-
value: ExpressionEvaluator.extractLiteralValue(bodyExpr),
|
|
3261
|
-
valueType: typeof ExpressionEvaluator.extractLiteralValue(bodyExpr),
|
|
3262
|
-
file: module.file,
|
|
3263
|
-
line: bodyLine,
|
|
3264
|
-
column: bodyColumn
|
|
3265
|
-
});
|
|
3266
|
-
}
|
|
3267
|
-
else if (t.isCallExpression(bodyExpr) && t.isIdentifier(bodyExpr.callee)) {
|
|
3268
|
-
returnInfo.returnValueType = 'CALL_SITE';
|
|
3269
|
-
returnInfo.returnValueLine = getLine(bodyExpr);
|
|
3270
|
-
returnInfo.returnValueColumn = getColumn(bodyExpr);
|
|
3271
|
-
returnInfo.returnValueCallName = bodyExpr.callee.name;
|
|
3272
|
-
}
|
|
3273
|
-
else if (t.isCallExpression(bodyExpr) && t.isMemberExpression(bodyExpr.callee)) {
|
|
3274
|
-
returnInfo.returnValueType = 'METHOD_CALL';
|
|
3275
|
-
returnInfo.returnValueLine = getLine(bodyExpr);
|
|
3276
|
-
returnInfo.returnValueColumn = getColumn(bodyExpr);
|
|
3277
|
-
if (t.isIdentifier(bodyExpr.callee.property)) {
|
|
3278
|
-
returnInfo.returnValueCallName = bodyExpr.callee.property.name;
|
|
3279
|
-
}
|
|
3280
|
-
}
|
|
3281
|
-
// REG-276: Detailed EXPRESSION handling for nested implicit arrow returns
|
|
3282
|
-
else if (t.isBinaryExpression(bodyExpr)) {
|
|
3283
|
-
returnInfo.returnValueType = 'EXPRESSION';
|
|
3284
|
-
returnInfo.expressionType = 'BinaryExpression';
|
|
3285
|
-
returnInfo.returnValueLine = getLine(bodyExpr);
|
|
3286
|
-
returnInfo.returnValueColumn = getColumn(bodyExpr);
|
|
3287
|
-
returnInfo.operator = bodyExpr.operator;
|
|
3288
|
-
returnInfo.returnValueId = NodeFactory.generateExpressionId('BinaryExpression', module.file, getLine(bodyExpr), getColumn(bodyExpr));
|
|
3289
|
-
if (t.isIdentifier(bodyExpr.left))
|
|
3290
|
-
returnInfo.leftSourceName = bodyExpr.left.name;
|
|
3291
|
-
if (t.isIdentifier(bodyExpr.right))
|
|
3292
|
-
returnInfo.rightSourceName = bodyExpr.right.name;
|
|
3293
|
-
}
|
|
3294
|
-
else if (t.isLogicalExpression(bodyExpr)) {
|
|
3295
|
-
returnInfo.returnValueType = 'EXPRESSION';
|
|
3296
|
-
returnInfo.expressionType = 'LogicalExpression';
|
|
3297
|
-
returnInfo.returnValueLine = getLine(bodyExpr);
|
|
3298
|
-
returnInfo.returnValueColumn = getColumn(bodyExpr);
|
|
3299
|
-
returnInfo.operator = bodyExpr.operator;
|
|
3300
|
-
returnInfo.returnValueId = NodeFactory.generateExpressionId('LogicalExpression', module.file, getLine(bodyExpr), getColumn(bodyExpr));
|
|
3301
|
-
if (t.isIdentifier(bodyExpr.left))
|
|
3302
|
-
returnInfo.leftSourceName = bodyExpr.left.name;
|
|
3303
|
-
if (t.isIdentifier(bodyExpr.right))
|
|
3304
|
-
returnInfo.rightSourceName = bodyExpr.right.name;
|
|
3305
|
-
}
|
|
3306
|
-
else if (t.isConditionalExpression(bodyExpr)) {
|
|
3307
|
-
returnInfo.returnValueType = 'EXPRESSION';
|
|
3308
|
-
returnInfo.expressionType = 'ConditionalExpression';
|
|
3309
|
-
returnInfo.returnValueLine = getLine(bodyExpr);
|
|
3310
|
-
returnInfo.returnValueColumn = getColumn(bodyExpr);
|
|
3311
|
-
returnInfo.returnValueId = NodeFactory.generateExpressionId('ConditionalExpression', module.file, getLine(bodyExpr), getColumn(bodyExpr));
|
|
3312
|
-
if (t.isIdentifier(bodyExpr.consequent))
|
|
3313
|
-
returnInfo.consequentSourceName = bodyExpr.consequent.name;
|
|
3314
|
-
if (t.isIdentifier(bodyExpr.alternate))
|
|
3315
|
-
returnInfo.alternateSourceName = bodyExpr.alternate.name;
|
|
3316
|
-
}
|
|
3317
|
-
else if (t.isUnaryExpression(bodyExpr)) {
|
|
3318
|
-
returnInfo.returnValueType = 'EXPRESSION';
|
|
3319
|
-
returnInfo.expressionType = 'UnaryExpression';
|
|
3320
|
-
returnInfo.returnValueLine = getLine(bodyExpr);
|
|
3321
|
-
returnInfo.returnValueColumn = getColumn(bodyExpr);
|
|
3322
|
-
returnInfo.operator = bodyExpr.operator;
|
|
3323
|
-
returnInfo.returnValueId = NodeFactory.generateExpressionId('UnaryExpression', module.file, getLine(bodyExpr), getColumn(bodyExpr));
|
|
3324
|
-
if (t.isIdentifier(bodyExpr.argument))
|
|
3325
|
-
returnInfo.unaryArgSourceName = bodyExpr.argument.name;
|
|
3326
|
-
}
|
|
3327
|
-
else if (t.isMemberExpression(bodyExpr)) {
|
|
3328
|
-
returnInfo.returnValueType = 'EXPRESSION';
|
|
3329
|
-
returnInfo.expressionType = 'MemberExpression';
|
|
3330
|
-
returnInfo.returnValueLine = getLine(bodyExpr);
|
|
3331
|
-
returnInfo.returnValueColumn = getColumn(bodyExpr);
|
|
3332
|
-
returnInfo.returnValueId = NodeFactory.generateExpressionId('MemberExpression', module.file, getLine(bodyExpr), getColumn(bodyExpr));
|
|
3333
|
-
if (t.isIdentifier(bodyExpr.object)) {
|
|
3334
|
-
returnInfo.object = bodyExpr.object.name;
|
|
3335
|
-
returnInfo.objectSourceName = bodyExpr.object.name;
|
|
3336
|
-
}
|
|
3337
|
-
if (t.isIdentifier(bodyExpr.property))
|
|
3338
|
-
returnInfo.property = bodyExpr.property.name;
|
|
3339
|
-
returnInfo.computed = bodyExpr.computed;
|
|
3340
|
-
}
|
|
3341
|
-
else {
|
|
3342
|
-
returnInfo.returnValueType = 'EXPRESSION';
|
|
3343
|
-
returnInfo.expressionType = bodyExpr.type;
|
|
3344
|
-
returnInfo.returnValueLine = getLine(bodyExpr);
|
|
3345
|
-
returnInfo.returnValueColumn = getColumn(bodyExpr);
|
|
3346
|
-
returnInfo.returnValueId = NodeFactory.generateExpressionId(bodyExpr.type, module.file, getLine(bodyExpr), getColumn(bodyExpr));
|
|
3347
|
-
}
|
|
3348
3378
|
returnStatements.push(returnInfo);
|
|
3349
3379
|
}
|
|
3350
3380
|
arrowPath.skip();
|
|
@@ -3383,7 +3413,12 @@ export class JSASTAnalyzer extends Plugin {
|
|
|
3383
3413
|
BlockStatement: this.createBlockStatementHandler(scopeTracker, ifElseScopeMap, tryScopeMap, scopeIdStack),
|
|
3384
3414
|
// Function call expressions
|
|
3385
3415
|
CallExpression: (callPath) => {
|
|
3386
|
-
|
|
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);
|
|
3387
3422
|
// REG-334: Check for resolve/reject calls inside Promise executors
|
|
3388
3423
|
const callNode = callPath.node;
|
|
3389
3424
|
if (t.isIdentifier(callNode.callee)) {
|
|
@@ -3476,6 +3511,147 @@ export class JSASTAnalyzer extends Plugin {
|
|
|
3476
3511
|
}
|
|
3477
3512
|
funcParent = funcParent.getFunctionParent();
|
|
3478
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
|
+
}
|
|
3479
3655
|
}
|
|
3480
3656
|
},
|
|
3481
3657
|
// NewExpression (constructor calls)
|
|
@@ -3531,7 +3707,9 @@ export class JSASTAnalyzer extends Plugin {
|
|
|
3531
3707
|
resolveName,
|
|
3532
3708
|
rejectName,
|
|
3533
3709
|
file: module.file,
|
|
3534
|
-
line
|
|
3710
|
+
line,
|
|
3711
|
+
// REG-311: Store the ID of the function that creates the Promise
|
|
3712
|
+
creatorFunctionId: currentFunctionId || undefined
|
|
3535
3713
|
});
|
|
3536
3714
|
}
|
|
3537
3715
|
}
|
|
@@ -3597,8 +3775,36 @@ export class JSASTAnalyzer extends Plugin {
|
|
|
3597
3775
|
});
|
|
3598
3776
|
}
|
|
3599
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());
|
|
3600
3801
|
}
|
|
3601
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
|
+
}
|
|
3602
3808
|
// Phase 6 (REG-267): Attach control flow metadata to the function node
|
|
3603
3809
|
if (matchingFunction) {
|
|
3604
3810
|
const cyclomaticComplexity = 1 +
|
|
@@ -3606,13 +3812,24 @@ export class JSASTAnalyzer extends Plugin {
|
|
|
3606
3812
|
controlFlowState.loopCount +
|
|
3607
3813
|
controlFlowState.caseCount +
|
|
3608
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))];
|
|
3609
3822
|
matchingFunction.controlFlow = {
|
|
3610
3823
|
hasBranches: controlFlowState.branchCount > 0,
|
|
3611
3824
|
hasLoops: controlFlowState.loopCount > 0,
|
|
3612
3825
|
hasTryCatch: controlFlowState.hasTryCatch,
|
|
3613
3826
|
hasEarlyReturn: controlFlowState.hasEarlyReturn,
|
|
3614
3827
|
hasThrow: controlFlowState.hasThrow,
|
|
3615
|
-
cyclomaticComplexity
|
|
3828
|
+
cyclomaticComplexity,
|
|
3829
|
+
// REG-311: Async error tracking
|
|
3830
|
+
canReject,
|
|
3831
|
+
hasAsyncThrow,
|
|
3832
|
+
rejectedBuiltinErrors: rejectedBuiltinErrors.length > 0 ? rejectedBuiltinErrors : undefined
|
|
3616
3833
|
};
|
|
3617
3834
|
}
|
|
3618
3835
|
}
|
|
@@ -3625,6 +3842,7 @@ export class JSASTAnalyzer extends Plugin {
|
|
|
3625
3842
|
* - Method calls (MemberExpression callee) → methodCalls collection
|
|
3626
3843
|
* - Array mutation detection (push, unshift, splice)
|
|
3627
3844
|
* - Object.assign() detection
|
|
3845
|
+
* - REG-311: isAwaited and isInsideTry metadata on CALL nodes
|
|
3628
3846
|
*
|
|
3629
3847
|
* @param callNode - The call expression AST node
|
|
3630
3848
|
* @param processedCallSites - Set of already processed call site keys to avoid duplicates
|
|
@@ -3636,8 +3854,10 @@ export class JSASTAnalyzer extends Plugin {
|
|
|
3636
3854
|
* @param scopeTracker - Optional scope tracker for semantic ID generation
|
|
3637
3855
|
* @param parentScopeId - ID of the parent scope containing this call
|
|
3638
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
|
|
3639
3859
|
*/
|
|
3640
|
-
handleCallExpression(callNode, processedCallSites, processedMethodCalls, callSites, methodCalls, module, callSiteCounterRef, scopeTracker, parentScopeId, collections) {
|
|
3860
|
+
handleCallExpression(callNode, processedCallSites, processedMethodCalls, callSites, methodCalls, module, callSiteCounterRef, scopeTracker, parentScopeId, collections, isAwaited = false, isInsideTry = false) {
|
|
3641
3861
|
// Handle direct function calls (greet(), main())
|
|
3642
3862
|
if (callNode.callee.type === 'Identifier') {
|
|
3643
3863
|
const nodeKey = `${callNode.start}:${callNode.end}`;
|
|
@@ -3661,7 +3881,10 @@ export class JSASTAnalyzer extends Plugin {
|
|
|
3661
3881
|
line: getLine(callNode),
|
|
3662
3882
|
column: getColumn(callNode), // REG-223: Add column for coordinate-based lookup
|
|
3663
3883
|
parentScopeId,
|
|
3664
|
-
targetFunctionName: calleeName
|
|
3884
|
+
targetFunctionName: calleeName,
|
|
3885
|
+
// REG-311: Async error tracking metadata
|
|
3886
|
+
isAwaited,
|
|
3887
|
+
isInsideTry
|
|
3665
3888
|
});
|
|
3666
3889
|
}
|
|
3667
3890
|
// Handle method calls (obj.method(), data.process())
|
|
@@ -3697,7 +3920,11 @@ export class JSASTAnalyzer extends Plugin {
|
|
|
3697
3920
|
file: module.file,
|
|
3698
3921
|
line: getLine(callNode),
|
|
3699
3922
|
column: getColumn(callNode),
|
|
3700
|
-
parentScopeId
|
|
3923
|
+
parentScopeId,
|
|
3924
|
+
// REG-311: Async error tracking metadata
|
|
3925
|
+
isAwaited,
|
|
3926
|
+
isInsideTry,
|
|
3927
|
+
isMethodCall: true
|
|
3701
3928
|
});
|
|
3702
3929
|
// Check for array mutation methods (push, unshift, splice)
|
|
3703
3930
|
const ARRAY_MUTATION_METHODS = ['push', 'unshift', 'splice'];
|
|
@@ -3720,6 +3947,7 @@ export class JSASTAnalyzer extends Plugin {
|
|
|
3720
3947
|
}
|
|
3721
3948
|
}
|
|
3722
3949
|
// REG-117: Nested array mutations like obj.arr.push(item)
|
|
3950
|
+
// REG-395: General nested method calls like a.b.c() or obj.nested.method()
|
|
3723
3951
|
// object is MemberExpression, property is the method name
|
|
3724
3952
|
else if (object.type === 'MemberExpression' && property.type === 'Identifier') {
|
|
3725
3953
|
const nestedMember = object;
|
|
@@ -3745,8 +3973,238 @@ export class JSASTAnalyzer extends Plugin {
|
|
|
3745
3973
|
baseObjectName, propertyName);
|
|
3746
3974
|
}
|
|
3747
3975
|
}
|
|
3976
|
+
// REG-395: Create CALL node for nested method calls like a.b.c()
|
|
3977
|
+
const objectName = CallExpressionVisitor.extractMemberExpressionName(nestedMember);
|
|
3978
|
+
if (objectName) {
|
|
3979
|
+
const nodeKey = `${callNode.start}:${callNode.end}`;
|
|
3980
|
+
if (!processedMethodCalls.has(nodeKey)) {
|
|
3981
|
+
processedMethodCalls.add(nodeKey);
|
|
3982
|
+
const fullName = `${objectName}.${methodName}`;
|
|
3983
|
+
const legacyId = `CALL#${fullName}#${module.file}#${getLine(callNode)}:${getColumn(callNode)}:${callSiteCounterRef.value++}`;
|
|
3984
|
+
let methodCallId = legacyId;
|
|
3985
|
+
if (scopeTracker) {
|
|
3986
|
+
const discriminator = scopeTracker.getItemCounter(`CALL:${fullName}`);
|
|
3987
|
+
methodCallId = computeSemanticId('CALL', fullName, scopeTracker.getContext(), { discriminator });
|
|
3988
|
+
}
|
|
3989
|
+
methodCalls.push({
|
|
3990
|
+
id: methodCallId,
|
|
3991
|
+
type: 'CALL',
|
|
3992
|
+
name: fullName,
|
|
3993
|
+
object: objectName,
|
|
3994
|
+
method: methodName,
|
|
3995
|
+
file: module.file,
|
|
3996
|
+
line: getLine(callNode),
|
|
3997
|
+
column: getColumn(callNode),
|
|
3998
|
+
parentScopeId,
|
|
3999
|
+
isMethodCall: true
|
|
4000
|
+
});
|
|
4001
|
+
}
|
|
4002
|
+
}
|
|
4003
|
+
}
|
|
4004
|
+
}
|
|
4005
|
+
}
|
|
4006
|
+
/**
|
|
4007
|
+
* REG-311: Micro-trace - follow variable assignments within function to find error source.
|
|
4008
|
+
* Used to resolve reject(err) or throw err where err is a variable.
|
|
4009
|
+
*
|
|
4010
|
+
* Uses cycle detection via Set<variableName> to avoid infinite loops on circular assignments.
|
|
4011
|
+
*
|
|
4012
|
+
* @param variableName - Name of variable to trace
|
|
4013
|
+
* @param funcPath - NodePath of containing function for AST traversal
|
|
4014
|
+
* @param variableDeclarations - Variable declarations in current scope
|
|
4015
|
+
* @returns Error class name if traced to NewExpression, null otherwise, plus trace path
|
|
4016
|
+
*/
|
|
4017
|
+
microTraceToErrorClass(variableName, funcPath, _variableDeclarations) {
|
|
4018
|
+
const tracePath = [variableName];
|
|
4019
|
+
const visited = new Set(); // Cycle detection
|
|
4020
|
+
let currentName = variableName;
|
|
4021
|
+
const funcBody = funcPath.node.body;
|
|
4022
|
+
if (!t.isBlockStatement(funcBody)) {
|
|
4023
|
+
return { errorClassName: null, tracePath };
|
|
4024
|
+
}
|
|
4025
|
+
// Iterate until we find a NewExpression or can't trace further
|
|
4026
|
+
while (!visited.has(currentName)) {
|
|
4027
|
+
visited.add(currentName);
|
|
4028
|
+
let found = false;
|
|
4029
|
+
let foundNewExpression = null;
|
|
4030
|
+
let nextName = null;
|
|
4031
|
+
// Walk AST to find assignments: currentName = newValue
|
|
4032
|
+
funcPath.traverse({
|
|
4033
|
+
VariableDeclarator: (declPath) => {
|
|
4034
|
+
if (found || foundNewExpression)
|
|
4035
|
+
return;
|
|
4036
|
+
if (t.isIdentifier(declPath.node.id) && declPath.node.id.name === currentName) {
|
|
4037
|
+
const init = declPath.node.init;
|
|
4038
|
+
if (init) {
|
|
4039
|
+
// Case 1: const err = new Error()
|
|
4040
|
+
if (t.isNewExpression(init) && t.isIdentifier(init.callee)) {
|
|
4041
|
+
tracePath.push(`new ${init.callee.name}()`);
|
|
4042
|
+
foundNewExpression = init.callee.name;
|
|
4043
|
+
found = true;
|
|
4044
|
+
return;
|
|
4045
|
+
}
|
|
4046
|
+
// Case 2: const err = otherVar (chain)
|
|
4047
|
+
if (t.isIdentifier(init)) {
|
|
4048
|
+
tracePath.push(init.name);
|
|
4049
|
+
nextName = init.name;
|
|
4050
|
+
found = true;
|
|
4051
|
+
return;
|
|
4052
|
+
}
|
|
4053
|
+
}
|
|
4054
|
+
}
|
|
4055
|
+
},
|
|
4056
|
+
AssignmentExpression: (assignPath) => {
|
|
4057
|
+
if (found || foundNewExpression)
|
|
4058
|
+
return;
|
|
4059
|
+
const left = assignPath.node.left;
|
|
4060
|
+
const right = assignPath.node.right;
|
|
4061
|
+
if (t.isIdentifier(left) && left.name === currentName) {
|
|
4062
|
+
if (t.isNewExpression(right) && t.isIdentifier(right.callee)) {
|
|
4063
|
+
tracePath.push(`new ${right.callee.name}()`);
|
|
4064
|
+
foundNewExpression = right.callee.name;
|
|
4065
|
+
found = true;
|
|
4066
|
+
return;
|
|
4067
|
+
}
|
|
4068
|
+
if (t.isIdentifier(right)) {
|
|
4069
|
+
tracePath.push(right.name);
|
|
4070
|
+
nextName = right.name;
|
|
4071
|
+
found = true;
|
|
4072
|
+
return;
|
|
4073
|
+
}
|
|
4074
|
+
}
|
|
4075
|
+
}
|
|
4076
|
+
});
|
|
4077
|
+
// If we found a NewExpression, return the class name
|
|
4078
|
+
if (foundNewExpression) {
|
|
4079
|
+
return { errorClassName: foundNewExpression, tracePath };
|
|
3748
4080
|
}
|
|
4081
|
+
// If we found another variable to follow, continue
|
|
4082
|
+
if (nextName) {
|
|
4083
|
+
currentName = nextName;
|
|
4084
|
+
continue;
|
|
4085
|
+
}
|
|
4086
|
+
// Couldn't trace further
|
|
4087
|
+
break;
|
|
3749
4088
|
}
|
|
4089
|
+
return { errorClassName: null, tracePath };
|
|
4090
|
+
}
|
|
4091
|
+
/**
|
|
4092
|
+
* REG-311: Collect CATCHES_FROM info linking catch blocks to exception sources in try blocks.
|
|
4093
|
+
*
|
|
4094
|
+
* Sources include:
|
|
4095
|
+
* - Awaited calls: await foo() in try block
|
|
4096
|
+
* - Sync calls: foo() in try block (any call can throw)
|
|
4097
|
+
* - Throw statements: throw new Error() in try block
|
|
4098
|
+
* - Constructor calls: new SomeClass() in try block
|
|
4099
|
+
*
|
|
4100
|
+
* @param funcPath - Function path to traverse
|
|
4101
|
+
* @param catchBlocks - Collection of CATCH_BLOCK nodes
|
|
4102
|
+
* @param callSites - Collection of CALL nodes (direct function calls)
|
|
4103
|
+
* @param methodCalls - Collection of CALL nodes (method calls)
|
|
4104
|
+
* @param constructorCalls - Collection of CONSTRUCTOR_CALL nodes
|
|
4105
|
+
* @param catchesFromInfos - Collection to push CatchesFromInfo to
|
|
4106
|
+
* @param module - Module context
|
|
4107
|
+
*/
|
|
4108
|
+
collectCatchesFromInfo(funcPath, catchBlocks, callSites, methodCalls, constructorCalls, catchesFromInfos, module) {
|
|
4109
|
+
// Traverse to find TryStatements and collect sources
|
|
4110
|
+
funcPath.traverse({
|
|
4111
|
+
TryStatement: (tryPath) => {
|
|
4112
|
+
const tryNode = tryPath.node;
|
|
4113
|
+
const handler = tryNode.handler;
|
|
4114
|
+
// Skip if no catch clause
|
|
4115
|
+
if (!handler)
|
|
4116
|
+
return;
|
|
4117
|
+
// Find the catch block for this try
|
|
4118
|
+
// Match by line number since we don't have the tryBlockId here
|
|
4119
|
+
const catchLine = getLine(handler);
|
|
4120
|
+
const catchBlock = catchBlocks.find(cb => cb.file === module.file && cb.line === catchLine);
|
|
4121
|
+
if (!catchBlock || !catchBlock.parameterName)
|
|
4122
|
+
return;
|
|
4123
|
+
// Traverse only the try block body (not catch or finally)
|
|
4124
|
+
const _tryBody = tryNode.block;
|
|
4125
|
+
const sources = [];
|
|
4126
|
+
// Collect sources from try block
|
|
4127
|
+
tryPath.get('block').traverse({
|
|
4128
|
+
// Stop at nested TryStatement - don't collect from inner try blocks
|
|
4129
|
+
TryStatement: (innerPath) => {
|
|
4130
|
+
innerPath.skip(); // Don't traverse into nested try blocks
|
|
4131
|
+
},
|
|
4132
|
+
// Stop at function boundaries - don't collect from nested functions
|
|
4133
|
+
Function: (innerFuncPath) => {
|
|
4134
|
+
innerFuncPath.skip();
|
|
4135
|
+
},
|
|
4136
|
+
CallExpression: (callPath) => {
|
|
4137
|
+
const callNode = callPath.node;
|
|
4138
|
+
const callLine = getLine(callNode);
|
|
4139
|
+
const callColumn = getColumn(callNode);
|
|
4140
|
+
// Check if this is an awaited call
|
|
4141
|
+
const parent = callPath.parentPath;
|
|
4142
|
+
const isAwaited = parent?.isAwaitExpression() ?? false;
|
|
4143
|
+
// Find the CALL node that matches this CallExpression
|
|
4144
|
+
let sourceId = null;
|
|
4145
|
+
let sourceType = 'sync_call';
|
|
4146
|
+
// Check method calls first (includes Promise.reject which is a method call)
|
|
4147
|
+
const matchingMethodCall = methodCalls.find(mc => mc.file === module.file &&
|
|
4148
|
+
mc.line === callLine &&
|
|
4149
|
+
mc.column === callColumn);
|
|
4150
|
+
if (matchingMethodCall) {
|
|
4151
|
+
sourceId = matchingMethodCall.id;
|
|
4152
|
+
sourceType = isAwaited ? 'awaited_call' : 'sync_call';
|
|
4153
|
+
}
|
|
4154
|
+
else {
|
|
4155
|
+
// Check direct function calls
|
|
4156
|
+
const matchingCallSite = callSites.find(cs => cs.file === module.file &&
|
|
4157
|
+
cs.line === callLine &&
|
|
4158
|
+
cs.column === callColumn);
|
|
4159
|
+
if (matchingCallSite) {
|
|
4160
|
+
sourceId = matchingCallSite.id;
|
|
4161
|
+
sourceType = isAwaited ? 'awaited_call' : 'sync_call';
|
|
4162
|
+
}
|
|
4163
|
+
}
|
|
4164
|
+
if (sourceId) {
|
|
4165
|
+
sources.push({ id: sourceId, type: sourceType, line: callLine });
|
|
4166
|
+
}
|
|
4167
|
+
},
|
|
4168
|
+
ThrowStatement: (throwPath) => {
|
|
4169
|
+
const throwNode = throwPath.node;
|
|
4170
|
+
const throwLine = getLine(throwNode);
|
|
4171
|
+
const throwColumn = getColumn(throwNode);
|
|
4172
|
+
// Create a synthetic ID for the throw statement
|
|
4173
|
+
// We don't have THROW_STATEMENT nodes, so we use line/column as identifier
|
|
4174
|
+
const sourceId = `THROW#${module.file}#${throwLine}:${throwColumn}`;
|
|
4175
|
+
sources.push({ id: sourceId, type: 'throw_statement', line: throwLine });
|
|
4176
|
+
},
|
|
4177
|
+
NewExpression: (newPath) => {
|
|
4178
|
+
// Skip NewExpression that is direct argument of ThrowStatement
|
|
4179
|
+
// In `throw new Error()`, the throw statement is the primary source
|
|
4180
|
+
if (newPath.parentPath?.isThrowStatement()) {
|
|
4181
|
+
return;
|
|
4182
|
+
}
|
|
4183
|
+
const newNode = newPath.node;
|
|
4184
|
+
const newLine = getLine(newNode);
|
|
4185
|
+
const newColumn = getColumn(newNode);
|
|
4186
|
+
// Find matching constructor call
|
|
4187
|
+
const matchingConstructor = constructorCalls.find(cc => cc.file === module.file &&
|
|
4188
|
+
cc.line === newLine &&
|
|
4189
|
+
cc.column === newColumn);
|
|
4190
|
+
if (matchingConstructor) {
|
|
4191
|
+
sources.push({ id: matchingConstructor.id, type: 'constructor_call', line: newLine });
|
|
4192
|
+
}
|
|
4193
|
+
}
|
|
4194
|
+
});
|
|
4195
|
+
// Create CatchesFromInfo for each source
|
|
4196
|
+
for (const source of sources) {
|
|
4197
|
+
catchesFromInfos.push({
|
|
4198
|
+
catchBlockId: catchBlock.id,
|
|
4199
|
+
parameterName: catchBlock.parameterName,
|
|
4200
|
+
sourceId: source.id,
|
|
4201
|
+
sourceType: source.type,
|
|
4202
|
+
file: module.file,
|
|
4203
|
+
sourceLine: source.line
|
|
4204
|
+
});
|
|
4205
|
+
}
|
|
4206
|
+
}
|
|
4207
|
+
});
|
|
3750
4208
|
}
|
|
3751
4209
|
/**
|
|
3752
4210
|
* Detect array mutation calls (push, unshift, splice) inside functions
|
|
@@ -3831,20 +4289,24 @@ export class JSASTAnalyzer extends Plugin {
|
|
|
3831
4289
|
}
|
|
3832
4290
|
/**
|
|
3833
4291
|
* Detect indexed array assignment: arr[i] = value
|
|
3834
|
-
* Creates ArrayMutationInfo for FLOWS_INTO edge generation in GraphBuilder
|
|
4292
|
+
* Creates ArrayMutationInfo for FLOWS_INTO edge generation in GraphBuilder.
|
|
4293
|
+
* For non-variable values (LITERAL, OBJECT_LITERAL, ARRAY_LITERAL), creates
|
|
4294
|
+
* value nodes and sets valueNodeId so GraphBuilder can create FLOWS_INTO edges.
|
|
3835
4295
|
*
|
|
3836
4296
|
* @param assignNode - The assignment expression node
|
|
3837
4297
|
* @param module - Current module being analyzed
|
|
3838
4298
|
* @param arrayMutations - Collection to push mutation info into
|
|
4299
|
+
* @param scopeTracker - Scope tracker for semantic ID generation
|
|
4300
|
+
* @param collections - Collections for creating value nodes (literals, objectLiterals, etc.)
|
|
3839
4301
|
*/
|
|
3840
|
-
detectIndexedArrayAssignment(assignNode, module, arrayMutations, scopeTracker) {
|
|
4302
|
+
detectIndexedArrayAssignment(assignNode, module, arrayMutations, scopeTracker, collections) {
|
|
3841
4303
|
// Check for indexed array assignment: arr[i] = value
|
|
3842
4304
|
if (assignNode.left.type === 'MemberExpression' && assignNode.left.computed) {
|
|
3843
4305
|
const memberExpr = assignNode.left;
|
|
3844
4306
|
// Only process NumericLiteral keys - those are clearly array indexed assignments
|
|
3845
4307
|
// e.g., arr[0] = value, arr[1] = value
|
|
3846
|
-
//
|
|
3847
|
-
//
|
|
4308
|
+
// Other computed keys (Identifier, expressions) are ambiguous (could be array or object)
|
|
4309
|
+
// and are handled by detectObjectPropertyAssignment as computed mutations
|
|
3848
4310
|
if (memberExpr.property.type !== 'NumericLiteral') {
|
|
3849
4311
|
return;
|
|
3850
4312
|
}
|
|
@@ -3852,16 +4314,38 @@ export class JSASTAnalyzer extends Plugin {
|
|
|
3852
4314
|
if (memberExpr.object.type === 'Identifier') {
|
|
3853
4315
|
const arrayName = memberExpr.object.name;
|
|
3854
4316
|
const value = assignNode.right;
|
|
4317
|
+
// Use defensive loc checks instead of ! assertions
|
|
4318
|
+
const line = assignNode.loc?.start.line ?? 0;
|
|
4319
|
+
const column = assignNode.loc?.start.column ?? 0;
|
|
3855
4320
|
const argInfo = {
|
|
3856
4321
|
argIndex: 0,
|
|
3857
4322
|
isSpread: false,
|
|
3858
4323
|
valueType: 'EXPRESSION'
|
|
3859
4324
|
};
|
|
3860
|
-
// Determine value type
|
|
4325
|
+
// Determine value type and create value nodes for non-variable types (REG-392)
|
|
3861
4326
|
const literalValue = ExpressionEvaluator.extractLiteralValue(value);
|
|
3862
4327
|
if (literalValue !== null) {
|
|
3863
4328
|
argInfo.valueType = 'LITERAL';
|
|
3864
4329
|
argInfo.literalValue = literalValue;
|
|
4330
|
+
const valueLine = value.loc?.start.line ?? line;
|
|
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
|
+
}
|
|
3865
4349
|
}
|
|
3866
4350
|
else if (value.type === 'Identifier') {
|
|
3867
4351
|
argInfo.valueType = 'VARIABLE';
|
|
@@ -3869,18 +4353,37 @@ export class JSASTAnalyzer extends Plugin {
|
|
|
3869
4353
|
}
|
|
3870
4354
|
else if (value.type === 'ObjectExpression') {
|
|
3871
4355
|
argInfo.valueType = 'OBJECT_LITERAL';
|
|
4356
|
+
const valueLine = value.loc?.start.line ?? line;
|
|
4357
|
+
const valueColumn = value.loc?.start.column ?? column;
|
|
4358
|
+
// Create OBJECT_LITERAL node if collections available
|
|
4359
|
+
if (collections?.objectLiteralCounterRef) {
|
|
4360
|
+
if (!collections.objectLiterals)
|
|
4361
|
+
collections.objectLiterals = [];
|
|
4362
|
+
const objectLiteralCounterRef = collections.objectLiteralCounterRef;
|
|
4363
|
+
const objectNode = ObjectLiteralNode.create(module.file, valueLine, valueColumn, { counter: objectLiteralCounterRef.value++ });
|
|
4364
|
+
collections.objectLiterals.push(objectNode);
|
|
4365
|
+
argInfo.valueNodeId = objectNode.id;
|
|
4366
|
+
}
|
|
3872
4367
|
}
|
|
3873
4368
|
else if (value.type === 'ArrayExpression') {
|
|
3874
4369
|
argInfo.valueType = 'ARRAY_LITERAL';
|
|
4370
|
+
const valueLine = value.loc?.start.line ?? line;
|
|
4371
|
+
const valueColumn = value.loc?.start.column ?? column;
|
|
4372
|
+
// Create ARRAY_LITERAL node if collections available
|
|
4373
|
+
if (collections?.arrayLiteralCounterRef) {
|
|
4374
|
+
if (!collections.arrayLiterals)
|
|
4375
|
+
collections.arrayLiterals = [];
|
|
4376
|
+
const arrayLiteralCounterRef = collections.arrayLiteralCounterRef;
|
|
4377
|
+
const arrayNode = ArrayLiteralNode.create(module.file, valueLine, valueColumn, { counter: arrayLiteralCounterRef.value++ });
|
|
4378
|
+
collections.arrayLiterals.push(arrayNode);
|
|
4379
|
+
argInfo.valueNodeId = arrayNode.id;
|
|
4380
|
+
}
|
|
3875
4381
|
}
|
|
3876
4382
|
else if (value.type === 'CallExpression') {
|
|
3877
4383
|
argInfo.valueType = 'CALL';
|
|
3878
4384
|
argInfo.callLine = value.loc?.start.line;
|
|
3879
4385
|
argInfo.callColumn = value.loc?.start.column;
|
|
3880
4386
|
}
|
|
3881
|
-
// Use defensive loc checks instead of ! assertions
|
|
3882
|
-
const line = assignNode.loc?.start.line ?? 0;
|
|
3883
|
-
const column = assignNode.loc?.start.column ?? 0;
|
|
3884
4387
|
// Capture scope path for scope-aware lookup (REG-309)
|
|
3885
4388
|
const scopePath = scopeTracker?.getContext().scopePath ?? [];
|
|
3886
4389
|
arrayMutations.push({
|
|
@@ -4303,3 +4806,4 @@ export class JSASTAnalyzer extends Plugin {
|
|
|
4303
4806
|
}
|
|
4304
4807
|
}
|
|
4305
4808
|
}
|
|
4809
|
+
//# sourceMappingURL=JSASTAnalyzer.js.map
|