@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
|
@@ -40,6 +40,7 @@ import {
|
|
|
40
40
|
ClassVisitor,
|
|
41
41
|
CallExpressionVisitor,
|
|
42
42
|
TypeScriptVisitor,
|
|
43
|
+
PropertyAccessVisitor,
|
|
43
44
|
type VisitorModule,
|
|
44
45
|
type VisitorCollections,
|
|
45
46
|
type TrackVariableAssignmentCallback
|
|
@@ -56,6 +57,7 @@ import { computeSemanticId } from '../../core/SemanticId.js';
|
|
|
56
57
|
import { ExpressionNode } from '../../core/nodes/ExpressionNode.js';
|
|
57
58
|
import { ConstructorCallNode } from '../../core/nodes/ConstructorCallNode.js';
|
|
58
59
|
import { ObjectLiteralNode } from '../../core/nodes/ObjectLiteralNode.js';
|
|
60
|
+
import { ArrayLiteralNode } from '../../core/nodes/ArrayLiteralNode.js';
|
|
59
61
|
import { NodeFactory } from '../../core/NodeFactory.js';
|
|
60
62
|
import type { PluginContext, PluginResult, PluginMetadata, GraphBackend } from '@grafema/types';
|
|
61
63
|
import type {
|
|
@@ -97,9 +99,13 @@ import type {
|
|
|
97
99
|
ObjectMutationValue,
|
|
98
100
|
VariableReassignmentInfo,
|
|
99
101
|
ReturnStatementInfo,
|
|
102
|
+
YieldExpressionInfo,
|
|
100
103
|
UpdateExpressionInfo,
|
|
101
104
|
PromiseResolutionInfo,
|
|
102
105
|
PromiseExecutorContext,
|
|
106
|
+
RejectionPatternInfo,
|
|
107
|
+
CatchesFromInfo,
|
|
108
|
+
PropertyAccessInfo,
|
|
103
109
|
CounterRef,
|
|
104
110
|
ProcessedNodes,
|
|
105
111
|
ASTCollections,
|
|
@@ -165,6 +171,11 @@ interface Collections {
|
|
|
165
171
|
promiseResolutions: PromiseResolutionInfo[];
|
|
166
172
|
// Promise executor contexts (REG-334) - keyed by executor function's start:end position
|
|
167
173
|
promiseExecutorContexts: Map<string, PromiseExecutorContext>;
|
|
174
|
+
// Yield expression tracking for YIELDS/DELEGATES_TO edges (REG-270)
|
|
175
|
+
yieldExpressions: YieldExpressionInfo[];
|
|
176
|
+
// Property access tracking for PROPERTY_ACCESS nodes (REG-395)
|
|
177
|
+
propertyAccesses: PropertyAccessInfo[];
|
|
178
|
+
propertyAccessCounterRef: CounterRef;
|
|
168
179
|
objectLiteralCounterRef: CounterRef;
|
|
169
180
|
arrayLiteralCounterRef: CounterRef;
|
|
170
181
|
ifScopeCounterRef: CounterRef;
|
|
@@ -250,7 +261,6 @@ export class JSASTAnalyzer extends Plugin {
|
|
|
250
261
|
return {
|
|
251
262
|
name: 'JSASTAnalyzer',
|
|
252
263
|
phase: 'ANALYSIS',
|
|
253
|
-
priority: 80,
|
|
254
264
|
creates: {
|
|
255
265
|
nodes: [
|
|
256
266
|
'FUNCTION', 'CLASS', 'METHOD', 'VARIABLE', 'CONSTANT', 'SCOPE',
|
|
@@ -269,7 +279,16 @@ export class JSASTAnalyzer extends Plugin {
|
|
|
269
279
|
'RESOLVES_TO'
|
|
270
280
|
]
|
|
271
281
|
},
|
|
272
|
-
dependencies: ['JSModuleIndexer']
|
|
282
|
+
dependencies: ['JSModuleIndexer'],
|
|
283
|
+
fields: [
|
|
284
|
+
{ name: 'object', fieldType: 'string', nodeTypes: ['CALL'] },
|
|
285
|
+
{ name: 'method', fieldType: 'string', nodeTypes: ['CALL'] },
|
|
286
|
+
{ name: 'async', fieldType: 'bool', nodeTypes: ['FUNCTION', 'METHOD'] },
|
|
287
|
+
{ name: 'scopeType', fieldType: 'string', nodeTypes: ['SCOPE'] },
|
|
288
|
+
{ name: 'importType', fieldType: 'string', nodeTypes: ['IMPORT'] },
|
|
289
|
+
{ name: 'exportType', fieldType: 'string', nodeTypes: ['EXPORT'] },
|
|
290
|
+
{ name: 'parentScopeId', fieldType: 'id', nodeTypes: ['FUNCTION', 'METHOD', 'SCOPE', 'VARIABLE'] },
|
|
291
|
+
]
|
|
273
292
|
};
|
|
274
293
|
}
|
|
275
294
|
|
|
@@ -1458,6 +1477,13 @@ export class JSASTAnalyzer extends Plugin {
|
|
|
1458
1477
|
const promiseResolutions: PromiseResolutionInfo[] = [];
|
|
1459
1478
|
// Promise executor contexts (REG-334) - keyed by executor function's start:end position
|
|
1460
1479
|
const promiseExecutorContexts = new Map<string, PromiseExecutorContext>();
|
|
1480
|
+
// Yield expression tracking for YIELDS/DELEGATES_TO edges (REG-270)
|
|
1481
|
+
const yieldExpressions: YieldExpressionInfo[] = [];
|
|
1482
|
+
// REG-311: Async error tracking
|
|
1483
|
+
const rejectionPatterns: RejectionPatternInfo[] = [];
|
|
1484
|
+
const catchesFromInfos: CatchesFromInfo[] = [];
|
|
1485
|
+
// Property access tracking for PROPERTY_ACCESS nodes (REG-395)
|
|
1486
|
+
const propertyAccesses: PropertyAccessInfo[] = [];
|
|
1461
1487
|
|
|
1462
1488
|
const ifScopeCounterRef: CounterRef = { value: 0 };
|
|
1463
1489
|
const scopeCounterRef: CounterRef = { value: 0 };
|
|
@@ -1471,6 +1497,7 @@ export class JSASTAnalyzer extends Plugin {
|
|
|
1471
1497
|
const arrayLiteralCounterRef: CounterRef = { value: 0 };
|
|
1472
1498
|
const branchCounterRef: CounterRef = { value: 0 };
|
|
1473
1499
|
const caseCounterRef: CounterRef = { value: 0 };
|
|
1500
|
+
const propertyAccessCounterRef: CounterRef = { value: 0 };
|
|
1474
1501
|
|
|
1475
1502
|
const processedNodes: ProcessedNodes = {
|
|
1476
1503
|
functions: new Set(),
|
|
@@ -1533,6 +1560,14 @@ export class JSASTAnalyzer extends Plugin {
|
|
|
1533
1560
|
// Promise resolution tracking (REG-334)
|
|
1534
1561
|
promiseResolutions,
|
|
1535
1562
|
promiseExecutorContexts,
|
|
1563
|
+
// Yield expression tracking (REG-270)
|
|
1564
|
+
yieldExpressions,
|
|
1565
|
+
// REG-311: Async error tracking
|
|
1566
|
+
rejectionPatterns,
|
|
1567
|
+
catchesFromInfos,
|
|
1568
|
+
// Property access tracking (REG-395)
|
|
1569
|
+
propertyAccesses,
|
|
1570
|
+
propertyAccessCounterRef,
|
|
1536
1571
|
objectLiteralCounterRef, arrayLiteralCounterRef,
|
|
1537
1572
|
ifScopeCounterRef, scopeCounterRef, varDeclCounterRef,
|
|
1538
1573
|
callSiteCounterRef, functionCounterRef, httpRequestCounterRef,
|
|
@@ -1634,7 +1669,7 @@ export class JSASTAnalyzer extends Plugin {
|
|
|
1634
1669
|
// === END VARIABLE REASSIGNMENT ===
|
|
1635
1670
|
|
|
1636
1671
|
// Check for indexed array assignment at module level: arr[i] = value
|
|
1637
|
-
this.detectIndexedArrayAssignment(assignNode, module, arrayMutations, scopeTracker);
|
|
1672
|
+
this.detectIndexedArrayAssignment(assignNode, module, arrayMutations, scopeTracker, allCollections);
|
|
1638
1673
|
|
|
1639
1674
|
// Check for object property assignment at module level: obj.prop = value
|
|
1640
1675
|
this.detectObjectPropertyAssignment(assignNode, module, objectMutations, scopeTracker);
|
|
@@ -1728,6 +1763,12 @@ export class JSASTAnalyzer extends Plugin {
|
|
|
1728
1763
|
traverse(ast, callExpressionVisitor.getHandlers());
|
|
1729
1764
|
this.profiler.end('traverse_calls');
|
|
1730
1765
|
|
|
1766
|
+
// Property access expressions (REG-395)
|
|
1767
|
+
this.profiler.start('traverse_property_access');
|
|
1768
|
+
const propertyAccessVisitor = new PropertyAccessVisitor(module, allCollections, scopeTracker);
|
|
1769
|
+
traverse(ast, propertyAccessVisitor.getHandlers());
|
|
1770
|
+
this.profiler.end('traverse_property_access');
|
|
1771
|
+
|
|
1731
1772
|
// Module-level NewExpression (constructor calls)
|
|
1732
1773
|
// This handles top-level code like `const x = new Date()` that's not inside a function
|
|
1733
1774
|
this.profiler.start('traverse_new');
|
|
@@ -1791,7 +1832,9 @@ export class JSASTAnalyzer extends Plugin {
|
|
|
1791
1832
|
resolveName,
|
|
1792
1833
|
rejectName,
|
|
1793
1834
|
file: module.file,
|
|
1794
|
-
line
|
|
1835
|
+
line,
|
|
1836
|
+
// REG-311: Module-level Promise has no creator function
|
|
1837
|
+
creatorFunctionId: undefined
|
|
1795
1838
|
});
|
|
1796
1839
|
}
|
|
1797
1840
|
}
|
|
@@ -1897,6 +1940,8 @@ export class JSASTAnalyzer extends Plugin {
|
|
|
1897
1940
|
variableReassignments,
|
|
1898
1941
|
// Return statement tracking
|
|
1899
1942
|
returnStatements,
|
|
1943
|
+
// Yield expression tracking (REG-270)
|
|
1944
|
+
yieldExpressions,
|
|
1900
1945
|
// Update expression tracking (REG-288, REG-312)
|
|
1901
1946
|
updateExpressions,
|
|
1902
1947
|
// Promise resolution tracking (REG-334)
|
|
@@ -1904,7 +1949,16 @@ export class JSASTAnalyzer extends Plugin {
|
|
|
1904
1949
|
// Object/Array literal tracking - use allCollections refs as visitors may have created new arrays
|
|
1905
1950
|
objectLiterals: allCollections.objectLiterals || objectLiterals,
|
|
1906
1951
|
objectProperties: allCollections.objectProperties || objectProperties,
|
|
1907
|
-
arrayLiterals: allCollections.arrayLiterals || arrayLiterals
|
|
1952
|
+
arrayLiterals: allCollections.arrayLiterals || arrayLiterals,
|
|
1953
|
+
// REG-311: Async error tracking
|
|
1954
|
+
rejectionPatterns: Array.isArray(allCollections.rejectionPatterns)
|
|
1955
|
+
? allCollections.rejectionPatterns as RejectionPatternInfo[]
|
|
1956
|
+
: rejectionPatterns,
|
|
1957
|
+
catchesFromInfos: Array.isArray(allCollections.catchesFromInfos)
|
|
1958
|
+
? allCollections.catchesFromInfos as CatchesFromInfo[]
|
|
1959
|
+
: catchesFromInfos,
|
|
1960
|
+
// Property access tracking (REG-395)
|
|
1961
|
+
propertyAccesses: allCollections.propertyAccesses || propertyAccesses
|
|
1908
1962
|
});
|
|
1909
1963
|
this.profiler.end('graph_build');
|
|
1910
1964
|
|
|
@@ -2401,7 +2455,7 @@ export class JSASTAnalyzer extends Plugin {
|
|
|
2401
2455
|
scopeTracker: ScopeTracker | undefined,
|
|
2402
2456
|
tryScopeMap: Map<t.TryStatement, TryScopeInfo>,
|
|
2403
2457
|
scopeIdStack?: string[],
|
|
2404
|
-
controlFlowState?: { hasTryCatch: boolean }
|
|
2458
|
+
controlFlowState?: { hasTryCatch: boolean; tryBlockDepth: number }
|
|
2405
2459
|
): { enter: (tryPath: NodePath<t.TryStatement>) => void; exit: (tryPath: NodePath<t.TryStatement>) => void } {
|
|
2406
2460
|
return {
|
|
2407
2461
|
enter: (tryPath: NodePath<t.TryStatement>) => {
|
|
@@ -2410,6 +2464,8 @@ export class JSASTAnalyzer extends Plugin {
|
|
|
2410
2464
|
// Phase 6 (REG-267): Mark that this function has try/catch
|
|
2411
2465
|
if (controlFlowState) {
|
|
2412
2466
|
controlFlowState.hasTryCatch = true;
|
|
2467
|
+
// REG-311: Increment try block depth for O(1) isInsideTry detection
|
|
2468
|
+
controlFlowState.tryBlockDepth++;
|
|
2413
2469
|
}
|
|
2414
2470
|
|
|
2415
2471
|
// Determine actual parent - use stack for nested structures, otherwise original parentScopeId
|
|
@@ -2549,7 +2605,13 @@ export class JSASTAnalyzer extends Plugin {
|
|
|
2549
2605
|
},
|
|
2550
2606
|
exit: (tryPath: NodePath<t.TryStatement>) => {
|
|
2551
2607
|
const tryNode = tryPath.node;
|
|
2552
|
-
const
|
|
2608
|
+
const _scopeInfo = tryScopeMap.get(tryNode);
|
|
2609
|
+
|
|
2610
|
+
// REG-311: Only decrement try block depth if we're still in 'try' block
|
|
2611
|
+
// (not transitioned to catch/finally, where we already decremented)
|
|
2612
|
+
if (controlFlowState && _scopeInfo?.currentBlock === 'try') {
|
|
2613
|
+
controlFlowState.tryBlockDepth--;
|
|
2614
|
+
}
|
|
2553
2615
|
|
|
2554
2616
|
// Pop the current scope from stack (could be try, catch, or finally)
|
|
2555
2617
|
if (scopeIdStack) {
|
|
@@ -2584,7 +2646,8 @@ export class JSASTAnalyzer extends Plugin {
|
|
|
2584
2646
|
varDeclCounterRef: CounterRef,
|
|
2585
2647
|
scopeTracker: ScopeTracker | undefined,
|
|
2586
2648
|
tryScopeMap: Map<t.TryStatement, TryScopeInfo>,
|
|
2587
|
-
scopeIdStack?: string[]
|
|
2649
|
+
scopeIdStack?: string[],
|
|
2650
|
+
controlFlowState?: { hasTryCatch: boolean; tryBlockDepth: number }
|
|
2588
2651
|
): { enter: (catchPath: NodePath<t.CatchClause>) => void } {
|
|
2589
2652
|
return {
|
|
2590
2653
|
enter: (catchPath: NodePath<t.CatchClause>) => {
|
|
@@ -2610,6 +2673,12 @@ export class JSASTAnalyzer extends Plugin {
|
|
|
2610
2673
|
scopeTracker.enterCountedScope('catch');
|
|
2611
2674
|
}
|
|
2612
2675
|
|
|
2676
|
+
// REG-311: Decrement tryBlockDepth when leaving try block for catch
|
|
2677
|
+
// Calls in catch block should NOT have isInsideTry=true
|
|
2678
|
+
if (controlFlowState) {
|
|
2679
|
+
controlFlowState.tryBlockDepth--;
|
|
2680
|
+
}
|
|
2681
|
+
|
|
2613
2682
|
scopeInfo.currentBlock = 'catch';
|
|
2614
2683
|
}
|
|
2615
2684
|
|
|
@@ -2947,6 +3016,209 @@ export class JSASTAnalyzer extends Plugin {
|
|
|
2947
3016
|
return parts.join('.');
|
|
2948
3017
|
}
|
|
2949
3018
|
|
|
3019
|
+
/**
|
|
3020
|
+
* Extract return expression info from an expression node.
|
|
3021
|
+
* Used for both explicit return statements and implicit arrow returns.
|
|
3022
|
+
*
|
|
3023
|
+
* This method consolidates ~450 lines of duplicated expression handling code
|
|
3024
|
+
* from three locations:
|
|
3025
|
+
* 1. Top-level implicit arrow returns (arrow function expression body)
|
|
3026
|
+
* 2. ReturnStatement handler (explicit returns)
|
|
3027
|
+
* 3. Nested arrow function implicit returns
|
|
3028
|
+
*
|
|
3029
|
+
* @param expr - The expression being returned
|
|
3030
|
+
* @param module - Module info for file context
|
|
3031
|
+
* @param literals - Collection to add literal nodes to
|
|
3032
|
+
* @param literalCounterRef - Counter for generating unique literal IDs
|
|
3033
|
+
* @param baseLine - Line number for literal ID generation
|
|
3034
|
+
* @param baseColumn - Column number for literal ID generation
|
|
3035
|
+
* @param literalIdSuffix - 'return' or 'implicit_return'
|
|
3036
|
+
* @returns Partial ReturnStatementInfo with expression-specific fields
|
|
3037
|
+
*/
|
|
3038
|
+
private extractReturnExpressionInfo(
|
|
3039
|
+
expr: t.Expression,
|
|
3040
|
+
module: { file: string },
|
|
3041
|
+
literals: LiteralInfo[],
|
|
3042
|
+
literalCounterRef: CounterRef,
|
|
3043
|
+
baseLine: number,
|
|
3044
|
+
baseColumn: number,
|
|
3045
|
+
literalIdSuffix: 'return' | 'implicit_return' | 'yield' = 'return'
|
|
3046
|
+
): Partial<ReturnStatementInfo> {
|
|
3047
|
+
const exprLine = getLine(expr);
|
|
3048
|
+
const exprColumn = getColumn(expr);
|
|
3049
|
+
|
|
3050
|
+
// Identifier (variable reference)
|
|
3051
|
+
if (t.isIdentifier(expr)) {
|
|
3052
|
+
return {
|
|
3053
|
+
returnValueType: 'VARIABLE',
|
|
3054
|
+
returnValueName: expr.name,
|
|
3055
|
+
};
|
|
3056
|
+
}
|
|
3057
|
+
|
|
3058
|
+
// TemplateLiteral must come BEFORE isLiteral (TemplateLiteral extends Literal)
|
|
3059
|
+
if (t.isTemplateLiteral(expr)) {
|
|
3060
|
+
const sourceNames: string[] = [];
|
|
3061
|
+
for (const embedded of expr.expressions) {
|
|
3062
|
+
if (t.isIdentifier(embedded)) {
|
|
3063
|
+
sourceNames.push(embedded.name);
|
|
3064
|
+
}
|
|
3065
|
+
}
|
|
3066
|
+
return {
|
|
3067
|
+
returnValueType: 'EXPRESSION',
|
|
3068
|
+
expressionType: 'TemplateLiteral',
|
|
3069
|
+
returnValueLine: exprLine,
|
|
3070
|
+
returnValueColumn: exprColumn,
|
|
3071
|
+
returnValueId: NodeFactory.generateExpressionId(
|
|
3072
|
+
'TemplateLiteral', module.file, exprLine, exprColumn
|
|
3073
|
+
),
|
|
3074
|
+
...(sourceNames.length > 0 ? { expressionSourceNames: sourceNames } : {}),
|
|
3075
|
+
};
|
|
3076
|
+
}
|
|
3077
|
+
|
|
3078
|
+
// Literal values (after TemplateLiteral check)
|
|
3079
|
+
if (t.isLiteral(expr)) {
|
|
3080
|
+
const literalId = `LITERAL#${literalIdSuffix}#${module.file}#${baseLine}:${baseColumn}:${literalCounterRef.value++}`;
|
|
3081
|
+
literals.push({
|
|
3082
|
+
id: literalId,
|
|
3083
|
+
type: 'LITERAL',
|
|
3084
|
+
value: ExpressionEvaluator.extractLiteralValue(expr),
|
|
3085
|
+
valueType: typeof ExpressionEvaluator.extractLiteralValue(expr),
|
|
3086
|
+
file: module.file,
|
|
3087
|
+
line: exprLine,
|
|
3088
|
+
column: exprColumn,
|
|
3089
|
+
});
|
|
3090
|
+
return {
|
|
3091
|
+
returnValueType: 'LITERAL',
|
|
3092
|
+
returnValueId: literalId,
|
|
3093
|
+
};
|
|
3094
|
+
}
|
|
3095
|
+
|
|
3096
|
+
// Direct function call: return foo()
|
|
3097
|
+
if (t.isCallExpression(expr) && t.isIdentifier(expr.callee)) {
|
|
3098
|
+
return {
|
|
3099
|
+
returnValueType: 'CALL_SITE',
|
|
3100
|
+
returnValueLine: exprLine,
|
|
3101
|
+
returnValueColumn: exprColumn,
|
|
3102
|
+
returnValueCallName: expr.callee.name,
|
|
3103
|
+
};
|
|
3104
|
+
}
|
|
3105
|
+
|
|
3106
|
+
// Method call: return obj.method()
|
|
3107
|
+
if (t.isCallExpression(expr) && t.isMemberExpression(expr.callee)) {
|
|
3108
|
+
return {
|
|
3109
|
+
returnValueType: 'METHOD_CALL',
|
|
3110
|
+
returnValueLine: exprLine,
|
|
3111
|
+
returnValueColumn: exprColumn,
|
|
3112
|
+
returnValueCallName: t.isIdentifier(expr.callee.property)
|
|
3113
|
+
? expr.callee.property.name
|
|
3114
|
+
: undefined,
|
|
3115
|
+
};
|
|
3116
|
+
}
|
|
3117
|
+
|
|
3118
|
+
// BinaryExpression: return a + b
|
|
3119
|
+
if (t.isBinaryExpression(expr)) {
|
|
3120
|
+
return {
|
|
3121
|
+
returnValueType: 'EXPRESSION',
|
|
3122
|
+
expressionType: 'BinaryExpression',
|
|
3123
|
+
returnValueLine: exprLine,
|
|
3124
|
+
returnValueColumn: exprColumn,
|
|
3125
|
+
operator: expr.operator,
|
|
3126
|
+
returnValueId: NodeFactory.generateExpressionId(
|
|
3127
|
+
'BinaryExpression', module.file, exprLine, exprColumn
|
|
3128
|
+
),
|
|
3129
|
+
leftSourceName: t.isIdentifier(expr.left) ? expr.left.name : undefined,
|
|
3130
|
+
rightSourceName: t.isIdentifier(expr.right) ? expr.right.name : undefined,
|
|
3131
|
+
};
|
|
3132
|
+
}
|
|
3133
|
+
|
|
3134
|
+
// LogicalExpression: return a && b, return a || b
|
|
3135
|
+
if (t.isLogicalExpression(expr)) {
|
|
3136
|
+
return {
|
|
3137
|
+
returnValueType: 'EXPRESSION',
|
|
3138
|
+
expressionType: 'LogicalExpression',
|
|
3139
|
+
returnValueLine: exprLine,
|
|
3140
|
+
returnValueColumn: exprColumn,
|
|
3141
|
+
operator: expr.operator,
|
|
3142
|
+
returnValueId: NodeFactory.generateExpressionId(
|
|
3143
|
+
'LogicalExpression', module.file, exprLine, exprColumn
|
|
3144
|
+
),
|
|
3145
|
+
leftSourceName: t.isIdentifier(expr.left) ? expr.left.name : undefined,
|
|
3146
|
+
rightSourceName: t.isIdentifier(expr.right) ? expr.right.name : undefined,
|
|
3147
|
+
};
|
|
3148
|
+
}
|
|
3149
|
+
|
|
3150
|
+
// ConditionalExpression: return condition ? a : b
|
|
3151
|
+
if (t.isConditionalExpression(expr)) {
|
|
3152
|
+
return {
|
|
3153
|
+
returnValueType: 'EXPRESSION',
|
|
3154
|
+
expressionType: 'ConditionalExpression',
|
|
3155
|
+
returnValueLine: exprLine,
|
|
3156
|
+
returnValueColumn: exprColumn,
|
|
3157
|
+
returnValueId: NodeFactory.generateExpressionId(
|
|
3158
|
+
'ConditionalExpression', module.file, exprLine, exprColumn
|
|
3159
|
+
),
|
|
3160
|
+
consequentSourceName: t.isIdentifier(expr.consequent) ? expr.consequent.name : undefined,
|
|
3161
|
+
alternateSourceName: t.isIdentifier(expr.alternate) ? expr.alternate.name : undefined,
|
|
3162
|
+
};
|
|
3163
|
+
}
|
|
3164
|
+
|
|
3165
|
+
// UnaryExpression: return !x, return -x
|
|
3166
|
+
if (t.isUnaryExpression(expr)) {
|
|
3167
|
+
return {
|
|
3168
|
+
returnValueType: 'EXPRESSION',
|
|
3169
|
+
expressionType: 'UnaryExpression',
|
|
3170
|
+
returnValueLine: exprLine,
|
|
3171
|
+
returnValueColumn: exprColumn,
|
|
3172
|
+
operator: expr.operator,
|
|
3173
|
+
returnValueId: NodeFactory.generateExpressionId(
|
|
3174
|
+
'UnaryExpression', module.file, exprLine, exprColumn
|
|
3175
|
+
),
|
|
3176
|
+
unaryArgSourceName: t.isIdentifier(expr.argument) ? expr.argument.name : undefined,
|
|
3177
|
+
};
|
|
3178
|
+
}
|
|
3179
|
+
|
|
3180
|
+
// MemberExpression (property access): return obj.prop
|
|
3181
|
+
if (t.isMemberExpression(expr)) {
|
|
3182
|
+
return {
|
|
3183
|
+
returnValueType: 'EXPRESSION',
|
|
3184
|
+
expressionType: 'MemberExpression',
|
|
3185
|
+
returnValueLine: exprLine,
|
|
3186
|
+
returnValueColumn: exprColumn,
|
|
3187
|
+
returnValueId: NodeFactory.generateExpressionId(
|
|
3188
|
+
'MemberExpression', module.file, exprLine, exprColumn
|
|
3189
|
+
),
|
|
3190
|
+
object: t.isIdentifier(expr.object) ? expr.object.name : undefined,
|
|
3191
|
+
objectSourceName: t.isIdentifier(expr.object) ? expr.object.name : undefined,
|
|
3192
|
+
property: t.isIdentifier(expr.property) ? expr.property.name : undefined,
|
|
3193
|
+
computed: expr.computed,
|
|
3194
|
+
};
|
|
3195
|
+
}
|
|
3196
|
+
|
|
3197
|
+
// NewExpression: return new Foo()
|
|
3198
|
+
if (t.isNewExpression(expr)) {
|
|
3199
|
+
return {
|
|
3200
|
+
returnValueType: 'EXPRESSION',
|
|
3201
|
+
expressionType: 'NewExpression',
|
|
3202
|
+
returnValueLine: exprLine,
|
|
3203
|
+
returnValueColumn: exprColumn,
|
|
3204
|
+
returnValueId: NodeFactory.generateExpressionId(
|
|
3205
|
+
'NewExpression', module.file, exprLine, exprColumn
|
|
3206
|
+
),
|
|
3207
|
+
};
|
|
3208
|
+
}
|
|
3209
|
+
|
|
3210
|
+
// Fallback for other expression types
|
|
3211
|
+
return {
|
|
3212
|
+
returnValueType: 'EXPRESSION',
|
|
3213
|
+
expressionType: expr.type,
|
|
3214
|
+
returnValueLine: exprLine,
|
|
3215
|
+
returnValueColumn: exprColumn,
|
|
3216
|
+
returnValueId: NodeFactory.generateExpressionId(
|
|
3217
|
+
expr.type, module.file, exprLine, exprColumn
|
|
3218
|
+
),
|
|
3219
|
+
};
|
|
3220
|
+
}
|
|
3221
|
+
|
|
2950
3222
|
/**
|
|
2951
3223
|
* Factory method to create IfStatement handler.
|
|
2952
3224
|
* Creates BRANCH node for if statement and SCOPE nodes for if/else bodies.
|
|
@@ -3284,7 +3556,7 @@ export class JSASTAnalyzer extends Plugin {
|
|
|
3284
3556
|
* Uses ScopeTracker from collections for semantic ID generation.
|
|
3285
3557
|
*/
|
|
3286
3558
|
analyzeFunctionBody(
|
|
3287
|
-
funcPath: NodePath<t.Function>,
|
|
3559
|
+
funcPath: NodePath<t.Function | t.StaticBlock>,
|
|
3288
3560
|
parentScopeId: string,
|
|
3289
3561
|
module: VisitorModule,
|
|
3290
3562
|
collections: VisitorCollections
|
|
@@ -3295,11 +3567,11 @@ export class JSASTAnalyzer extends Plugin {
|
|
|
3295
3567
|
const variableDeclarations = (collections.variableDeclarations ?? []) as VariableDeclarationInfo[];
|
|
3296
3568
|
const callSites = (collections.callSites ?? []) as CallSiteInfo[];
|
|
3297
3569
|
const methodCalls = (collections.methodCalls ?? []) as MethodCallInfo[];
|
|
3298
|
-
const
|
|
3299
|
-
const
|
|
3570
|
+
const _eventListeners = (collections.eventListeners ?? []) as EventListenerInfo[];
|
|
3571
|
+
const _methodCallbacks = (collections.methodCallbacks ?? []) as MethodCallbackInfo[];
|
|
3300
3572
|
const classInstantiations = (collections.classInstantiations ?? []) as ClassInstantiationInfo[];
|
|
3301
3573
|
const constructorCalls = (collections.constructorCalls ?? []) as ConstructorCallInfo[];
|
|
3302
|
-
const
|
|
3574
|
+
const _httpRequests = (collections.httpRequests ?? []) as HttpRequestInfo[];
|
|
3303
3575
|
const literals = (collections.literals ?? []) as LiteralInfo[];
|
|
3304
3576
|
const variableAssignments = (collections.variableAssignments ?? []) as VariableAssignmentInfo[];
|
|
3305
3577
|
const ifScopeCounterRef = (collections.ifScopeCounterRef ?? { value: 0 }) as CounterRef;
|
|
@@ -3307,9 +3579,9 @@ export class JSASTAnalyzer extends Plugin {
|
|
|
3307
3579
|
const varDeclCounterRef = (collections.varDeclCounterRef ?? { value: 0 }) as CounterRef;
|
|
3308
3580
|
const callSiteCounterRef = (collections.callSiteCounterRef ?? { value: 0 }) as CounterRef;
|
|
3309
3581
|
const functionCounterRef = (collections.functionCounterRef ?? { value: 0 }) as CounterRef;
|
|
3310
|
-
const
|
|
3582
|
+
const _httpRequestCounterRef = (collections.httpRequestCounterRef ?? { value: 0 }) as CounterRef;
|
|
3311
3583
|
const literalCounterRef = (collections.literalCounterRef ?? { value: 0 }) as CounterRef;
|
|
3312
|
-
const
|
|
3584
|
+
const _anonymousFunctionCounterRef = (collections.anonymousFunctionCounterRef ?? { value: 0 }) as CounterRef;
|
|
3313
3585
|
const scopeTracker = collections.scopeTracker as ScopeTracker | undefined;
|
|
3314
3586
|
// Object literal tracking (REG-328)
|
|
3315
3587
|
if (!collections.objectLiterals) {
|
|
@@ -3325,7 +3597,12 @@ export class JSASTAnalyzer extends Plugin {
|
|
|
3325
3597
|
const objectProperties = collections.objectProperties as ObjectPropertyInfo[];
|
|
3326
3598
|
const objectLiteralCounterRef = collections.objectLiteralCounterRef as CounterRef;
|
|
3327
3599
|
const returnStatements = (collections.returnStatements ?? []) as ReturnStatementInfo[];
|
|
3328
|
-
|
|
3600
|
+
// Initialize yieldExpressions if not exist to ensure nested function calls share same array
|
|
3601
|
+
if (!collections.yieldExpressions) {
|
|
3602
|
+
collections.yieldExpressions = [];
|
|
3603
|
+
}
|
|
3604
|
+
const yieldExpressions = collections.yieldExpressions as YieldExpressionInfo[];
|
|
3605
|
+
const _parameters = (collections.parameters ?? []) as ParameterInfo[];
|
|
3329
3606
|
// Control flow collections (Phase 2: LOOP nodes)
|
|
3330
3607
|
// Initialize if not exist to ensure nested function calls share same arrays
|
|
3331
3608
|
if (!collections.loops) {
|
|
@@ -3352,9 +3629,9 @@ export class JSASTAnalyzer extends Plugin {
|
|
|
3352
3629
|
const parentScopeVariables = new Set<{ name: string; id: string; scopeId: string }>();
|
|
3353
3630
|
|
|
3354
3631
|
const processedCallSites = processedNodes.callSites;
|
|
3355
|
-
const
|
|
3632
|
+
const _processedVarDecls = processedNodes.varDecls;
|
|
3356
3633
|
const processedMethodCalls = processedNodes.methodCalls;
|
|
3357
|
-
const
|
|
3634
|
+
const _processedEventListeners = processedNodes.eventListeners;
|
|
3358
3635
|
|
|
3359
3636
|
// Track if/else scope transitions (Phase 3: extended with branchId)
|
|
3360
3637
|
const ifElseScopeMap = new Map<t.IfStatement, IfElseScopeInfo>();
|
|
@@ -3411,6 +3688,16 @@ export class JSASTAnalyzer extends Plugin {
|
|
|
3411
3688
|
}
|
|
3412
3689
|
const promiseResolutions = collections.promiseResolutions as PromiseResolutionInfo[];
|
|
3413
3690
|
|
|
3691
|
+
// REG-311: Initialize rejectionPatterns and catchesFromInfos collections
|
|
3692
|
+
if (!collections.rejectionPatterns) {
|
|
3693
|
+
collections.rejectionPatterns = [];
|
|
3694
|
+
}
|
|
3695
|
+
if (!collections.catchesFromInfos) {
|
|
3696
|
+
collections.catchesFromInfos = [];
|
|
3697
|
+
}
|
|
3698
|
+
const rejectionPatterns = collections.rejectionPatterns as RejectionPatternInfo[];
|
|
3699
|
+
const catchesFromInfos = collections.catchesFromInfos as CatchesFromInfo[];
|
|
3700
|
+
|
|
3414
3701
|
// Dynamic scope ID stack for CONTAINS edges
|
|
3415
3702
|
// Starts with the function body scope, gets updated as we enter/exit conditional scopes
|
|
3416
3703
|
const scopeIdStack: string[] = [parentScopeId];
|
|
@@ -3418,16 +3705,24 @@ export class JSASTAnalyzer extends Plugin {
|
|
|
3418
3705
|
|
|
3419
3706
|
// Determine the ID of the function we're analyzing for RETURNS edges
|
|
3420
3707
|
// Find by matching file/line/column in functions collection (it was just added by the visitor)
|
|
3708
|
+
// REG-271: Skip for StaticBlock (static blocks don't have RETURNS edges or control flow metadata)
|
|
3421
3709
|
const funcNode = funcPath.node;
|
|
3710
|
+
const functionNode = t.isFunction(funcNode) ? funcNode : null;
|
|
3711
|
+
const functionPath = functionNode ? (funcPath as NodePath<t.Function>) : null;
|
|
3422
3712
|
const funcLine = getLine(funcNode);
|
|
3423
3713
|
const funcColumn = getColumn(funcNode);
|
|
3424
3714
|
let currentFunctionId: string | null = null;
|
|
3425
3715
|
|
|
3426
|
-
|
|
3427
|
-
|
|
3428
|
-
|
|
3429
|
-
(f
|
|
3430
|
-
|
|
3716
|
+
// StaticBlock is not a function - skip function matching for RETURNS edges
|
|
3717
|
+
// For StaticBlock, matchingFunction will be undefined
|
|
3718
|
+
const matchingFunction = funcNode.type !== 'StaticBlock'
|
|
3719
|
+
? functions.find(f =>
|
|
3720
|
+
f.file === module.file &&
|
|
3721
|
+
f.line === funcLine &&
|
|
3722
|
+
(f.column === undefined || f.column === funcColumn)
|
|
3723
|
+
)
|
|
3724
|
+
: undefined;
|
|
3725
|
+
|
|
3431
3726
|
if (matchingFunction) {
|
|
3432
3727
|
currentFunctionId = matchingFunction.id;
|
|
3433
3728
|
}
|
|
@@ -3442,7 +3737,9 @@ export class JSASTAnalyzer extends Plugin {
|
|
|
3442
3737
|
hasEarlyReturn: false,
|
|
3443
3738
|
hasThrow: false,
|
|
3444
3739
|
returnCount: 0, // Track total return count for early return detection
|
|
3445
|
-
totalStatements: 0
|
|
3740
|
+
totalStatements: 0, // Track if there are statements after returns
|
|
3741
|
+
// REG-311: Try block depth counter for O(1) isInsideTry detection
|
|
3742
|
+
tryBlockDepth: 0
|
|
3446
3743
|
};
|
|
3447
3744
|
|
|
3448
3745
|
// Handle implicit return for THIS arrow function if it has an expression body
|
|
@@ -3452,136 +3749,21 @@ export class JSASTAnalyzer extends Plugin {
|
|
|
3452
3749
|
const bodyLine = getLine(bodyExpr);
|
|
3453
3750
|
const bodyColumn = getColumn(bodyExpr);
|
|
3454
3751
|
|
|
3752
|
+
// Extract expression-specific info using shared method
|
|
3753
|
+
const exprInfo = this.extractReturnExpressionInfo(
|
|
3754
|
+
bodyExpr, module, literals, literalCounterRef, funcLine, funcColumn, 'implicit_return'
|
|
3755
|
+
);
|
|
3756
|
+
|
|
3455
3757
|
const returnInfo: ReturnStatementInfo = {
|
|
3456
3758
|
parentFunctionId: currentFunctionId,
|
|
3457
3759
|
file: module.file,
|
|
3458
3760
|
line: bodyLine,
|
|
3459
3761
|
column: bodyColumn,
|
|
3460
3762
|
returnValueType: 'NONE',
|
|
3461
|
-
isImplicitReturn: true
|
|
3763
|
+
isImplicitReturn: true,
|
|
3764
|
+
...exprInfo,
|
|
3462
3765
|
};
|
|
3463
3766
|
|
|
3464
|
-
// Apply type detection logic for the implicit return
|
|
3465
|
-
if (t.isIdentifier(bodyExpr)) {
|
|
3466
|
-
returnInfo.returnValueType = 'VARIABLE';
|
|
3467
|
-
returnInfo.returnValueName = bodyExpr.name;
|
|
3468
|
-
}
|
|
3469
|
-
// TemplateLiteral must come BEFORE isLiteral (TemplateLiteral extends Literal)
|
|
3470
|
-
else if (t.isTemplateLiteral(bodyExpr)) {
|
|
3471
|
-
returnInfo.returnValueType = 'EXPRESSION';
|
|
3472
|
-
returnInfo.expressionType = 'TemplateLiteral';
|
|
3473
|
-
returnInfo.returnValueLine = getLine(bodyExpr);
|
|
3474
|
-
returnInfo.returnValueColumn = getColumn(bodyExpr);
|
|
3475
|
-
returnInfo.returnValueId = NodeFactory.generateExpressionId(
|
|
3476
|
-
'TemplateLiteral', module.file, getLine(bodyExpr), getColumn(bodyExpr)
|
|
3477
|
-
);
|
|
3478
|
-
const sourceNames: string[] = [];
|
|
3479
|
-
for (const expr of bodyExpr.expressions) {
|
|
3480
|
-
if (t.isIdentifier(expr)) sourceNames.push(expr.name);
|
|
3481
|
-
}
|
|
3482
|
-
if (sourceNames.length > 0) returnInfo.expressionSourceNames = sourceNames;
|
|
3483
|
-
}
|
|
3484
|
-
else if (t.isLiteral(bodyExpr)) {
|
|
3485
|
-
returnInfo.returnValueType = 'LITERAL';
|
|
3486
|
-
const literalId = `LITERAL#implicit_return#${module.file}#${funcLine}:${funcColumn}:${literalCounterRef.value++}`;
|
|
3487
|
-
returnInfo.returnValueId = literalId;
|
|
3488
|
-
literals.push({
|
|
3489
|
-
id: literalId,
|
|
3490
|
-
type: 'LITERAL',
|
|
3491
|
-
value: ExpressionEvaluator.extractLiteralValue(bodyExpr),
|
|
3492
|
-
valueType: typeof ExpressionEvaluator.extractLiteralValue(bodyExpr),
|
|
3493
|
-
file: module.file,
|
|
3494
|
-
line: bodyLine,
|
|
3495
|
-
column: bodyColumn
|
|
3496
|
-
});
|
|
3497
|
-
}
|
|
3498
|
-
else if (t.isCallExpression(bodyExpr) && t.isIdentifier(bodyExpr.callee)) {
|
|
3499
|
-
returnInfo.returnValueType = 'CALL_SITE';
|
|
3500
|
-
returnInfo.returnValueLine = getLine(bodyExpr);
|
|
3501
|
-
returnInfo.returnValueColumn = getColumn(bodyExpr);
|
|
3502
|
-
returnInfo.returnValueCallName = bodyExpr.callee.name;
|
|
3503
|
-
}
|
|
3504
|
-
else if (t.isCallExpression(bodyExpr) && t.isMemberExpression(bodyExpr.callee)) {
|
|
3505
|
-
returnInfo.returnValueType = 'METHOD_CALL';
|
|
3506
|
-
returnInfo.returnValueLine = getLine(bodyExpr);
|
|
3507
|
-
returnInfo.returnValueColumn = getColumn(bodyExpr);
|
|
3508
|
-
if (t.isIdentifier(bodyExpr.callee.property)) {
|
|
3509
|
-
returnInfo.returnValueCallName = bodyExpr.callee.property.name;
|
|
3510
|
-
}
|
|
3511
|
-
}
|
|
3512
|
-
// REG-276: Detailed EXPRESSION handling for implicit arrow returns
|
|
3513
|
-
else if (t.isBinaryExpression(bodyExpr)) {
|
|
3514
|
-
returnInfo.returnValueType = 'EXPRESSION';
|
|
3515
|
-
returnInfo.expressionType = 'BinaryExpression';
|
|
3516
|
-
returnInfo.returnValueLine = getLine(bodyExpr);
|
|
3517
|
-
returnInfo.returnValueColumn = getColumn(bodyExpr);
|
|
3518
|
-
returnInfo.operator = bodyExpr.operator;
|
|
3519
|
-
returnInfo.returnValueId = NodeFactory.generateExpressionId(
|
|
3520
|
-
'BinaryExpression', module.file, getLine(bodyExpr), getColumn(bodyExpr)
|
|
3521
|
-
);
|
|
3522
|
-
if (t.isIdentifier(bodyExpr.left)) returnInfo.leftSourceName = bodyExpr.left.name;
|
|
3523
|
-
if (t.isIdentifier(bodyExpr.right)) returnInfo.rightSourceName = bodyExpr.right.name;
|
|
3524
|
-
}
|
|
3525
|
-
else if (t.isLogicalExpression(bodyExpr)) {
|
|
3526
|
-
returnInfo.returnValueType = 'EXPRESSION';
|
|
3527
|
-
returnInfo.expressionType = 'LogicalExpression';
|
|
3528
|
-
returnInfo.returnValueLine = getLine(bodyExpr);
|
|
3529
|
-
returnInfo.returnValueColumn = getColumn(bodyExpr);
|
|
3530
|
-
returnInfo.operator = bodyExpr.operator;
|
|
3531
|
-
returnInfo.returnValueId = NodeFactory.generateExpressionId(
|
|
3532
|
-
'LogicalExpression', module.file, getLine(bodyExpr), getColumn(bodyExpr)
|
|
3533
|
-
);
|
|
3534
|
-
if (t.isIdentifier(bodyExpr.left)) returnInfo.leftSourceName = bodyExpr.left.name;
|
|
3535
|
-
if (t.isIdentifier(bodyExpr.right)) returnInfo.rightSourceName = bodyExpr.right.name;
|
|
3536
|
-
}
|
|
3537
|
-
else if (t.isConditionalExpression(bodyExpr)) {
|
|
3538
|
-
returnInfo.returnValueType = 'EXPRESSION';
|
|
3539
|
-
returnInfo.expressionType = 'ConditionalExpression';
|
|
3540
|
-
returnInfo.returnValueLine = getLine(bodyExpr);
|
|
3541
|
-
returnInfo.returnValueColumn = getColumn(bodyExpr);
|
|
3542
|
-
returnInfo.returnValueId = NodeFactory.generateExpressionId(
|
|
3543
|
-
'ConditionalExpression', module.file, getLine(bodyExpr), getColumn(bodyExpr)
|
|
3544
|
-
);
|
|
3545
|
-
if (t.isIdentifier(bodyExpr.consequent)) returnInfo.consequentSourceName = bodyExpr.consequent.name;
|
|
3546
|
-
if (t.isIdentifier(bodyExpr.alternate)) returnInfo.alternateSourceName = bodyExpr.alternate.name;
|
|
3547
|
-
}
|
|
3548
|
-
else if (t.isUnaryExpression(bodyExpr)) {
|
|
3549
|
-
returnInfo.returnValueType = 'EXPRESSION';
|
|
3550
|
-
returnInfo.expressionType = 'UnaryExpression';
|
|
3551
|
-
returnInfo.returnValueLine = getLine(bodyExpr);
|
|
3552
|
-
returnInfo.returnValueColumn = getColumn(bodyExpr);
|
|
3553
|
-
returnInfo.operator = bodyExpr.operator;
|
|
3554
|
-
returnInfo.returnValueId = NodeFactory.generateExpressionId(
|
|
3555
|
-
'UnaryExpression', module.file, getLine(bodyExpr), getColumn(bodyExpr)
|
|
3556
|
-
);
|
|
3557
|
-
if (t.isIdentifier(bodyExpr.argument)) returnInfo.unaryArgSourceName = bodyExpr.argument.name;
|
|
3558
|
-
}
|
|
3559
|
-
else if (t.isMemberExpression(bodyExpr)) {
|
|
3560
|
-
returnInfo.returnValueType = 'EXPRESSION';
|
|
3561
|
-
returnInfo.expressionType = 'MemberExpression';
|
|
3562
|
-
returnInfo.returnValueLine = getLine(bodyExpr);
|
|
3563
|
-
returnInfo.returnValueColumn = getColumn(bodyExpr);
|
|
3564
|
-
returnInfo.returnValueId = NodeFactory.generateExpressionId(
|
|
3565
|
-
'MemberExpression', module.file, getLine(bodyExpr), getColumn(bodyExpr)
|
|
3566
|
-
);
|
|
3567
|
-
if (t.isIdentifier(bodyExpr.object)) {
|
|
3568
|
-
returnInfo.object = bodyExpr.object.name;
|
|
3569
|
-
returnInfo.objectSourceName = bodyExpr.object.name;
|
|
3570
|
-
}
|
|
3571
|
-
if (t.isIdentifier(bodyExpr.property)) returnInfo.property = bodyExpr.property.name;
|
|
3572
|
-
returnInfo.computed = bodyExpr.computed;
|
|
3573
|
-
}
|
|
3574
|
-
else {
|
|
3575
|
-
// Fallback: any other expression type
|
|
3576
|
-
returnInfo.returnValueType = 'EXPRESSION';
|
|
3577
|
-
returnInfo.expressionType = bodyExpr.type;
|
|
3578
|
-
returnInfo.returnValueLine = getLine(bodyExpr);
|
|
3579
|
-
returnInfo.returnValueColumn = getColumn(bodyExpr);
|
|
3580
|
-
returnInfo.returnValueId = NodeFactory.generateExpressionId(
|
|
3581
|
-
bodyExpr.type, module.file, getLine(bodyExpr), getColumn(bodyExpr)
|
|
3582
|
-
);
|
|
3583
|
-
}
|
|
3584
|
-
|
|
3585
3767
|
returnStatements.push(returnInfo);
|
|
3586
3768
|
}
|
|
3587
3769
|
|
|
@@ -3630,7 +3812,7 @@ export class JSASTAnalyzer extends Plugin {
|
|
|
3630
3812
|
const arrayMutations = collections.arrayMutations as ArrayMutationInfo[];
|
|
3631
3813
|
|
|
3632
3814
|
// Check for indexed array assignment: arr[i] = value
|
|
3633
|
-
this.detectIndexedArrayAssignment(assignNode, module, arrayMutations, scopeTracker);
|
|
3815
|
+
this.detectIndexedArrayAssignment(assignNode, module, arrayMutations, scopeTracker, collections);
|
|
3634
3816
|
|
|
3635
3817
|
// Initialize object mutations collection if not exists
|
|
3636
3818
|
if (!collections.objectMutations) {
|
|
@@ -3695,238 +3877,174 @@ export class JSASTAnalyzer extends Plugin {
|
|
|
3695
3877
|
|
|
3696
3878
|
const arg = returnNode.argument;
|
|
3697
3879
|
|
|
3698
|
-
//
|
|
3880
|
+
// Extract expression-specific info using shared method
|
|
3881
|
+
const exprInfo = this.extractReturnExpressionInfo(
|
|
3882
|
+
arg, module, literals, literalCounterRef, returnLine, returnColumn, 'return'
|
|
3883
|
+
);
|
|
3884
|
+
|
|
3699
3885
|
const returnInfo: ReturnStatementInfo = {
|
|
3700
3886
|
parentFunctionId: currentFunctionId,
|
|
3701
3887
|
file: module.file,
|
|
3702
3888
|
line: returnLine,
|
|
3703
3889
|
column: returnColumn,
|
|
3704
|
-
returnValueType: 'NONE'
|
|
3890
|
+
returnValueType: 'NONE',
|
|
3891
|
+
...exprInfo,
|
|
3705
3892
|
};
|
|
3706
3893
|
|
|
3707
|
-
|
|
3708
|
-
|
|
3709
|
-
returnInfo.returnValueType = 'VARIABLE';
|
|
3710
|
-
returnInfo.returnValueName = arg.name;
|
|
3711
|
-
}
|
|
3712
|
-
// TemplateLiteral must come BEFORE isLiteral (TemplateLiteral extends Literal)
|
|
3713
|
-
else if (t.isTemplateLiteral(arg)) {
|
|
3714
|
-
returnInfo.returnValueType = 'EXPRESSION';
|
|
3715
|
-
returnInfo.expressionType = 'TemplateLiteral';
|
|
3716
|
-
returnInfo.returnValueLine = getLine(arg);
|
|
3717
|
-
returnInfo.returnValueColumn = getColumn(arg);
|
|
3718
|
-
|
|
3719
|
-
returnInfo.returnValueId = NodeFactory.generateExpressionId(
|
|
3720
|
-
'TemplateLiteral',
|
|
3721
|
-
module.file,
|
|
3722
|
-
getLine(arg),
|
|
3723
|
-
getColumn(arg)
|
|
3724
|
-
);
|
|
3894
|
+
returnStatements.push(returnInfo);
|
|
3895
|
+
},
|
|
3725
3896
|
|
|
3726
|
-
|
|
3727
|
-
|
|
3728
|
-
|
|
3729
|
-
|
|
3730
|
-
|
|
3731
|
-
|
|
3732
|
-
|
|
3733
|
-
|
|
3734
|
-
|
|
3735
|
-
}
|
|
3736
|
-
}
|
|
3737
|
-
// Literal values (after TemplateLiteral check)
|
|
3738
|
-
else if (t.isLiteral(arg)) {
|
|
3739
|
-
returnInfo.returnValueType = 'LITERAL';
|
|
3740
|
-
// Create a LITERAL node ID for this return value
|
|
3741
|
-
const literalId = `LITERAL#return#${module.file}#${returnLine}:${returnColumn}:${literalCounterRef.value++}`;
|
|
3742
|
-
returnInfo.returnValueId = literalId;
|
|
3743
|
-
|
|
3744
|
-
// Also add to literals collection for node creation
|
|
3745
|
-
literals.push({
|
|
3746
|
-
id: literalId,
|
|
3747
|
-
type: 'LITERAL',
|
|
3748
|
-
value: ExpressionEvaluator.extractLiteralValue(arg),
|
|
3749
|
-
valueType: typeof ExpressionEvaluator.extractLiteralValue(arg),
|
|
3750
|
-
file: module.file,
|
|
3751
|
-
line: returnLine,
|
|
3752
|
-
column: returnColumn
|
|
3753
|
-
});
|
|
3754
|
-
}
|
|
3755
|
-
// Direct function call: return foo()
|
|
3756
|
-
else if (t.isCallExpression(arg) && t.isIdentifier(arg.callee)) {
|
|
3757
|
-
returnInfo.returnValueType = 'CALL_SITE';
|
|
3758
|
-
returnInfo.returnValueLine = getLine(arg);
|
|
3759
|
-
returnInfo.returnValueColumn = getColumn(arg);
|
|
3760
|
-
returnInfo.returnValueCallName = arg.callee.name;
|
|
3761
|
-
}
|
|
3762
|
-
// Method call: return obj.method()
|
|
3763
|
-
else if (t.isCallExpression(arg) && t.isMemberExpression(arg.callee)) {
|
|
3764
|
-
returnInfo.returnValueType = 'METHOD_CALL';
|
|
3765
|
-
returnInfo.returnValueLine = getLine(arg);
|
|
3766
|
-
returnInfo.returnValueColumn = getColumn(arg);
|
|
3767
|
-
// Extract method name for lookup
|
|
3768
|
-
if (t.isIdentifier(arg.callee.property)) {
|
|
3769
|
-
returnInfo.returnValueCallName = arg.callee.property.name;
|
|
3897
|
+
// Phase 6 (REG-267): Track throw statements for control flow metadata
|
|
3898
|
+
// REG-311: Also detect async_throw rejection patterns
|
|
3899
|
+
ThrowStatement: (throwPath: NodePath<t.ThrowStatement>) => {
|
|
3900
|
+
// Skip if this throw is inside a nested function (not the function we're analyzing)
|
|
3901
|
+
let parent: NodePath | null = throwPath.parentPath;
|
|
3902
|
+
while (parent) {
|
|
3903
|
+
if (t.isFunction(parent.node) && parent.node !== funcNode) {
|
|
3904
|
+
// This throw is inside a nested function - skip it
|
|
3905
|
+
return;
|
|
3770
3906
|
}
|
|
3907
|
+
parent = parent.parentPath;
|
|
3771
3908
|
}
|
|
3772
|
-
// BinaryExpression: return a + b
|
|
3773
|
-
else if (t.isBinaryExpression(arg)) {
|
|
3774
|
-
returnInfo.returnValueType = 'EXPRESSION';
|
|
3775
|
-
returnInfo.expressionType = 'BinaryExpression';
|
|
3776
|
-
returnInfo.returnValueLine = getLine(arg);
|
|
3777
|
-
returnInfo.returnValueColumn = getColumn(arg);
|
|
3778
|
-
returnInfo.operator = arg.operator;
|
|
3779
|
-
|
|
3780
|
-
// Generate stable ID for the EXPRESSION node
|
|
3781
|
-
returnInfo.returnValueId = NodeFactory.generateExpressionId(
|
|
3782
|
-
'BinaryExpression',
|
|
3783
|
-
module.file,
|
|
3784
|
-
getLine(arg),
|
|
3785
|
-
getColumn(arg)
|
|
3786
|
-
);
|
|
3787
3909
|
|
|
3788
|
-
|
|
3789
|
-
if (t.isIdentifier(arg.left)) {
|
|
3790
|
-
returnInfo.leftSourceName = arg.left.name;
|
|
3791
|
-
}
|
|
3792
|
-
// Extract right operand source
|
|
3793
|
-
if (t.isIdentifier(arg.right)) {
|
|
3794
|
-
returnInfo.rightSourceName = arg.right.name;
|
|
3795
|
-
}
|
|
3796
|
-
}
|
|
3797
|
-
// LogicalExpression: return a && b, return a || b
|
|
3798
|
-
else if (t.isLogicalExpression(arg)) {
|
|
3799
|
-
returnInfo.returnValueType = 'EXPRESSION';
|
|
3800
|
-
returnInfo.expressionType = 'LogicalExpression';
|
|
3801
|
-
returnInfo.returnValueLine = getLine(arg);
|
|
3802
|
-
returnInfo.returnValueColumn = getColumn(arg);
|
|
3803
|
-
returnInfo.operator = arg.operator;
|
|
3804
|
-
|
|
3805
|
-
returnInfo.returnValueId = NodeFactory.generateExpressionId(
|
|
3806
|
-
'LogicalExpression',
|
|
3807
|
-
module.file,
|
|
3808
|
-
getLine(arg),
|
|
3809
|
-
getColumn(arg)
|
|
3810
|
-
);
|
|
3910
|
+
controlFlowState.hasThrow = true;
|
|
3811
3911
|
|
|
3812
|
-
|
|
3813
|
-
|
|
3814
|
-
|
|
3815
|
-
|
|
3816
|
-
|
|
3912
|
+
// REG-311: Track rejection patterns for async functions
|
|
3913
|
+
const isAsyncFunction = functionNode?.async === true;
|
|
3914
|
+
if (isAsyncFunction && currentFunctionId && functionNode && functionPath) {
|
|
3915
|
+
const throwNode = throwPath.node;
|
|
3916
|
+
const arg = throwNode.argument;
|
|
3917
|
+
const throwLine = getLine(throwNode);
|
|
3918
|
+
const throwColumn = getColumn(throwNode);
|
|
3919
|
+
|
|
3920
|
+
// Case 1: throw new Error() or throw new CustomError()
|
|
3921
|
+
if (arg && t.isNewExpression(arg) && t.isIdentifier(arg.callee)) {
|
|
3922
|
+
rejectionPatterns.push({
|
|
3923
|
+
functionId: currentFunctionId,
|
|
3924
|
+
errorClassName: arg.callee.name,
|
|
3925
|
+
rejectionType: 'async_throw',
|
|
3926
|
+
file: module.file,
|
|
3927
|
+
line: throwLine,
|
|
3928
|
+
column: throwColumn
|
|
3929
|
+
});
|
|
3817
3930
|
}
|
|
3818
|
-
|
|
3819
|
-
|
|
3820
|
-
|
|
3821
|
-
returnInfo.returnValueType = 'EXPRESSION';
|
|
3822
|
-
returnInfo.expressionType = 'ConditionalExpression';
|
|
3823
|
-
returnInfo.returnValueLine = getLine(arg);
|
|
3824
|
-
returnInfo.returnValueColumn = getColumn(arg);
|
|
3825
|
-
|
|
3826
|
-
returnInfo.returnValueId = NodeFactory.generateExpressionId(
|
|
3827
|
-
'ConditionalExpression',
|
|
3828
|
-
module.file,
|
|
3829
|
-
getLine(arg),
|
|
3830
|
-
getColumn(arg)
|
|
3831
|
-
);
|
|
3931
|
+
// Case 2: throw identifier - needs micro-trace
|
|
3932
|
+
else if (arg && t.isIdentifier(arg)) {
|
|
3933
|
+
const varName = arg.name;
|
|
3832
3934
|
|
|
3833
|
-
|
|
3834
|
-
|
|
3835
|
-
|
|
3836
|
-
|
|
3837
|
-
|
|
3838
|
-
|
|
3839
|
-
|
|
3935
|
+
// Check if it's a parameter
|
|
3936
|
+
const isParameter = functionNode.params.some(param =>
|
|
3937
|
+
t.isIdentifier(param) && param.name === varName
|
|
3938
|
+
);
|
|
3939
|
+
|
|
3940
|
+
if (isParameter) {
|
|
3941
|
+
// Parameter forwarding - can't resolve statically
|
|
3942
|
+
rejectionPatterns.push({
|
|
3943
|
+
functionId: currentFunctionId,
|
|
3944
|
+
errorClassName: null,
|
|
3945
|
+
rejectionType: 'variable_parameter',
|
|
3946
|
+
file: module.file,
|
|
3947
|
+
line: throwLine,
|
|
3948
|
+
column: throwColumn,
|
|
3949
|
+
sourceVariableName: varName
|
|
3950
|
+
});
|
|
3951
|
+
} else {
|
|
3952
|
+
// Try micro-trace
|
|
3953
|
+
const { errorClassName, tracePath } = this.microTraceToErrorClass(
|
|
3954
|
+
varName,
|
|
3955
|
+
functionPath,
|
|
3956
|
+
variableDeclarations
|
|
3957
|
+
);
|
|
3958
|
+
|
|
3959
|
+
rejectionPatterns.push({
|
|
3960
|
+
functionId: currentFunctionId,
|
|
3961
|
+
errorClassName,
|
|
3962
|
+
rejectionType: errorClassName ? 'variable_traced' : 'variable_unknown',
|
|
3963
|
+
file: module.file,
|
|
3964
|
+
line: throwLine,
|
|
3965
|
+
column: throwColumn,
|
|
3966
|
+
sourceVariableName: varName,
|
|
3967
|
+
tracePath
|
|
3968
|
+
});
|
|
3969
|
+
}
|
|
3840
3970
|
}
|
|
3841
3971
|
}
|
|
3842
|
-
|
|
3843
|
-
else if (t.isUnaryExpression(arg)) {
|
|
3844
|
-
returnInfo.returnValueType = 'EXPRESSION';
|
|
3845
|
-
returnInfo.expressionType = 'UnaryExpression';
|
|
3846
|
-
returnInfo.returnValueLine = getLine(arg);
|
|
3847
|
-
returnInfo.returnValueColumn = getColumn(arg);
|
|
3848
|
-
returnInfo.operator = arg.operator;
|
|
3849
|
-
|
|
3850
|
-
returnInfo.returnValueId = NodeFactory.generateExpressionId(
|
|
3851
|
-
'UnaryExpression',
|
|
3852
|
-
module.file,
|
|
3853
|
-
getLine(arg),
|
|
3854
|
-
getColumn(arg)
|
|
3855
|
-
);
|
|
3972
|
+
},
|
|
3856
3973
|
|
|
3857
|
-
|
|
3858
|
-
|
|
3859
|
-
|
|
3974
|
+
// Handle yield expressions for YIELDS/DELEGATES_TO edges (REG-270)
|
|
3975
|
+
YieldExpression: (yieldPath: NodePath<t.YieldExpression>) => {
|
|
3976
|
+
// Skip if we couldn't determine the function ID
|
|
3977
|
+
if (!currentFunctionId) {
|
|
3978
|
+
return;
|
|
3860
3979
|
}
|
|
3861
|
-
// MemberExpression (property access): return obj.prop
|
|
3862
|
-
else if (t.isMemberExpression(arg)) {
|
|
3863
|
-
returnInfo.returnValueType = 'EXPRESSION';
|
|
3864
|
-
returnInfo.expressionType = 'MemberExpression';
|
|
3865
|
-
returnInfo.returnValueLine = getLine(arg);
|
|
3866
|
-
returnInfo.returnValueColumn = getColumn(arg);
|
|
3867
|
-
|
|
3868
|
-
returnInfo.returnValueId = NodeFactory.generateExpressionId(
|
|
3869
|
-
'MemberExpression',
|
|
3870
|
-
module.file,
|
|
3871
|
-
getLine(arg),
|
|
3872
|
-
getColumn(arg)
|
|
3873
|
-
);
|
|
3874
3980
|
|
|
3875
|
-
|
|
3876
|
-
|
|
3877
|
-
|
|
3878
|
-
|
|
3981
|
+
// Skip if this yield is inside a nested function (not the function we're analyzing)
|
|
3982
|
+
// Check if there's a function ancestor BETWEEN us and funcNode
|
|
3983
|
+
let parent: NodePath | null = yieldPath.parentPath;
|
|
3984
|
+
while (parent) {
|
|
3985
|
+
// If we've reached funcNode, we're done checking - this yield belongs to funcNode
|
|
3986
|
+
if (parent.node === funcNode) {
|
|
3987
|
+
break;
|
|
3879
3988
|
}
|
|
3880
|
-
if (t.
|
|
3881
|
-
|
|
3989
|
+
if (t.isFunction(parent.node)) {
|
|
3990
|
+
// Found a function between yieldPath and funcNode - this yield is inside a nested function
|
|
3991
|
+
return;
|
|
3882
3992
|
}
|
|
3883
|
-
|
|
3993
|
+
parent = parent.parentPath;
|
|
3884
3994
|
}
|
|
3885
|
-
|
|
3886
|
-
|
|
3887
|
-
|
|
3888
|
-
|
|
3889
|
-
|
|
3890
|
-
|
|
3891
|
-
|
|
3892
|
-
|
|
3893
|
-
|
|
3894
|
-
|
|
3895
|
-
getLine(arg),
|
|
3896
|
-
getColumn(arg)
|
|
3897
|
-
);
|
|
3995
|
+
|
|
3996
|
+
const yieldNode = yieldPath.node;
|
|
3997
|
+
const yieldLine = getLine(yieldNode);
|
|
3998
|
+
const yieldColumn = getColumn(yieldNode);
|
|
3999
|
+
const isDelegate = yieldNode.delegate ?? false;
|
|
4000
|
+
|
|
4001
|
+
// Handle bare yield; (no value) - only valid for non-delegate yield
|
|
4002
|
+
if (!yieldNode.argument && !isDelegate) {
|
|
4003
|
+
// Skip - no data flow value
|
|
4004
|
+
return;
|
|
3898
4005
|
}
|
|
3899
|
-
// Fallback for other expression types
|
|
3900
|
-
else {
|
|
3901
|
-
returnInfo.returnValueType = 'EXPRESSION';
|
|
3902
|
-
returnInfo.expressionType = arg.type;
|
|
3903
|
-
returnInfo.returnValueLine = getLine(arg);
|
|
3904
|
-
returnInfo.returnValueColumn = getColumn(arg);
|
|
3905
4006
|
|
|
3906
|
-
|
|
3907
|
-
|
|
3908
|
-
|
|
3909
|
-
getLine(arg),
|
|
3910
|
-
getColumn(arg)
|
|
3911
|
-
);
|
|
4007
|
+
// For yield* without argument (syntax error in practice, but handle gracefully)
|
|
4008
|
+
if (!yieldNode.argument) {
|
|
4009
|
+
return;
|
|
3912
4010
|
}
|
|
3913
4011
|
|
|
3914
|
-
|
|
3915
|
-
},
|
|
4012
|
+
const arg = yieldNode.argument;
|
|
3916
4013
|
|
|
3917
|
-
|
|
3918
|
-
|
|
3919
|
-
|
|
3920
|
-
|
|
3921
|
-
|
|
3922
|
-
if (t.isFunction(parent.node) && parent.node !== funcNode) {
|
|
3923
|
-
// This throw is inside a nested function - skip it
|
|
3924
|
-
return;
|
|
3925
|
-
}
|
|
3926
|
-
parent = parent.parentPath;
|
|
3927
|
-
}
|
|
4014
|
+
// Extract expression-specific info using shared method
|
|
4015
|
+
// Note: We reuse extractReturnExpressionInfo since yield values have identical semantics
|
|
4016
|
+
const exprInfo = this.extractReturnExpressionInfo(
|
|
4017
|
+
arg, module, literals, literalCounterRef, yieldLine, yieldColumn, 'yield'
|
|
4018
|
+
);
|
|
3928
4019
|
|
|
3929
|
-
|
|
4020
|
+
// Map ReturnStatementInfo fields to YieldExpressionInfo fields
|
|
4021
|
+
const yieldInfo: YieldExpressionInfo = {
|
|
4022
|
+
parentFunctionId: currentFunctionId,
|
|
4023
|
+
file: module.file,
|
|
4024
|
+
line: yieldLine,
|
|
4025
|
+
column: yieldColumn,
|
|
4026
|
+
isDelegate,
|
|
4027
|
+
yieldValueType: exprInfo.returnValueType ?? 'NONE',
|
|
4028
|
+
yieldValueName: exprInfo.returnValueName,
|
|
4029
|
+
yieldValueId: exprInfo.returnValueId,
|
|
4030
|
+
yieldValueLine: exprInfo.returnValueLine,
|
|
4031
|
+
yieldValueColumn: exprInfo.returnValueColumn,
|
|
4032
|
+
yieldValueCallName: exprInfo.returnValueCallName,
|
|
4033
|
+
expressionType: exprInfo.expressionType,
|
|
4034
|
+
operator: exprInfo.operator,
|
|
4035
|
+
leftSourceName: exprInfo.leftSourceName,
|
|
4036
|
+
rightSourceName: exprInfo.rightSourceName,
|
|
4037
|
+
consequentSourceName: exprInfo.consequentSourceName,
|
|
4038
|
+
alternateSourceName: exprInfo.alternateSourceName,
|
|
4039
|
+
object: exprInfo.object,
|
|
4040
|
+
property: exprInfo.property,
|
|
4041
|
+
computed: exprInfo.computed,
|
|
4042
|
+
objectSourceName: exprInfo.objectSourceName,
|
|
4043
|
+
expressionSourceNames: exprInfo.expressionSourceNames,
|
|
4044
|
+
unaryArgSourceName: exprInfo.unaryArgSourceName,
|
|
4045
|
+
};
|
|
4046
|
+
|
|
4047
|
+
yieldExpressions.push(yieldInfo);
|
|
3930
4048
|
},
|
|
3931
4049
|
|
|
3932
4050
|
ForStatement: this.createLoopScopeHandler('for', 'for-loop', 'for', parentScopeId, module, scopes, loops, scopeCounterRef, loopCounterRef, scopeTracker, scopeIdStack, controlFlowState),
|
|
@@ -3959,7 +4077,8 @@ export class JSASTAnalyzer extends Plugin {
|
|
|
3959
4077
|
varDeclCounterRef,
|
|
3960
4078
|
scopeTracker,
|
|
3961
4079
|
tryScopeMap,
|
|
3962
|
-
scopeIdStack
|
|
4080
|
+
scopeIdStack,
|
|
4081
|
+
controlFlowState
|
|
3963
4082
|
),
|
|
3964
4083
|
|
|
3965
4084
|
SwitchStatement: (switchPath: NodePath<t.SwitchStatement>) => {
|
|
@@ -3973,6 +4092,53 @@ export class JSASTAnalyzer extends Plugin {
|
|
|
3973
4092
|
);
|
|
3974
4093
|
},
|
|
3975
4094
|
|
|
4095
|
+
FunctionDeclaration: (funcDeclPath: NodePath<t.FunctionDeclaration>) => {
|
|
4096
|
+
const node = funcDeclPath.node;
|
|
4097
|
+
const funcName = node.id ? node.id.name : this.generateAnonymousName(scopeTracker);
|
|
4098
|
+
// Use semantic ID as primary ID when scopeTracker available
|
|
4099
|
+
const legacyId = `FUNCTION#${funcName}#${module.file}#${getLine(node)}:${getColumn(node)}:${functionCounterRef.value++}`;
|
|
4100
|
+
const functionId = scopeTracker
|
|
4101
|
+
? computeSemanticId('FUNCTION', funcName, scopeTracker.getContext())
|
|
4102
|
+
: legacyId;
|
|
4103
|
+
|
|
4104
|
+
functions.push({
|
|
4105
|
+
id: functionId,
|
|
4106
|
+
type: 'FUNCTION',
|
|
4107
|
+
name: funcName,
|
|
4108
|
+
file: module.file,
|
|
4109
|
+
line: getLine(node),
|
|
4110
|
+
column: getColumn(node),
|
|
4111
|
+
async: node.async || false,
|
|
4112
|
+
generator: node.generator || false,
|
|
4113
|
+
parentScopeId
|
|
4114
|
+
});
|
|
4115
|
+
|
|
4116
|
+
const nestedScopeId = `SCOPE#${funcName}:body#${module.file}#${getLine(node)}`;
|
|
4117
|
+
const closureSemanticId = this.generateSemanticId('closure', scopeTracker);
|
|
4118
|
+
scopes.push({
|
|
4119
|
+
id: nestedScopeId,
|
|
4120
|
+
type: 'SCOPE',
|
|
4121
|
+
scopeType: 'closure',
|
|
4122
|
+
name: `${funcName}:body`,
|
|
4123
|
+
semanticId: closureSemanticId,
|
|
4124
|
+
conditional: false,
|
|
4125
|
+
file: module.file,
|
|
4126
|
+
line: getLine(node),
|
|
4127
|
+
parentFunctionId: functionId,
|
|
4128
|
+
capturesFrom: parentScopeId
|
|
4129
|
+
});
|
|
4130
|
+
|
|
4131
|
+
// Enter nested function scope for semantic ID generation
|
|
4132
|
+
if (scopeTracker) {
|
|
4133
|
+
scopeTracker.enterScope(funcName, 'function');
|
|
4134
|
+
}
|
|
4135
|
+
this.analyzeFunctionBody(funcDeclPath, nestedScopeId, module, collections);
|
|
4136
|
+
if (scopeTracker) {
|
|
4137
|
+
scopeTracker.exitScope();
|
|
4138
|
+
}
|
|
4139
|
+
funcDeclPath.skip();
|
|
4140
|
+
},
|
|
4141
|
+
|
|
3976
4142
|
FunctionExpression: (funcPath: NodePath<t.FunctionExpression>) => {
|
|
3977
4143
|
const node = funcPath.node;
|
|
3978
4144
|
const funcName = node.id ? node.id.name : this.generateAnonymousName(scopeTracker);
|
|
@@ -4084,135 +4250,21 @@ export class JSASTAnalyzer extends Plugin {
|
|
|
4084
4250
|
const bodyLine = getLine(bodyExpr);
|
|
4085
4251
|
const bodyColumn = getColumn(bodyExpr);
|
|
4086
4252
|
|
|
4253
|
+
// Extract expression-specific info using shared method
|
|
4254
|
+
const exprInfo = this.extractReturnExpressionInfo(
|
|
4255
|
+
bodyExpr, module, literals, literalCounterRef, line, column, 'implicit_return'
|
|
4256
|
+
);
|
|
4257
|
+
|
|
4087
4258
|
const returnInfo: ReturnStatementInfo = {
|
|
4088
4259
|
parentFunctionId: functionId,
|
|
4089
4260
|
file: module.file,
|
|
4090
4261
|
line: bodyLine,
|
|
4091
4262
|
column: bodyColumn,
|
|
4092
4263
|
returnValueType: 'NONE',
|
|
4093
|
-
isImplicitReturn: true
|
|
4264
|
+
isImplicitReturn: true,
|
|
4265
|
+
...exprInfo,
|
|
4094
4266
|
};
|
|
4095
4267
|
|
|
4096
|
-
// Apply same type detection logic as ReturnStatement handler
|
|
4097
|
-
if (t.isIdentifier(bodyExpr)) {
|
|
4098
|
-
returnInfo.returnValueType = 'VARIABLE';
|
|
4099
|
-
returnInfo.returnValueName = bodyExpr.name;
|
|
4100
|
-
}
|
|
4101
|
-
// TemplateLiteral must come BEFORE isLiteral (TemplateLiteral extends Literal)
|
|
4102
|
-
else if (t.isTemplateLiteral(bodyExpr)) {
|
|
4103
|
-
returnInfo.returnValueType = 'EXPRESSION';
|
|
4104
|
-
returnInfo.expressionType = 'TemplateLiteral';
|
|
4105
|
-
returnInfo.returnValueLine = getLine(bodyExpr);
|
|
4106
|
-
returnInfo.returnValueColumn = getColumn(bodyExpr);
|
|
4107
|
-
returnInfo.returnValueId = NodeFactory.generateExpressionId(
|
|
4108
|
-
'TemplateLiteral', module.file, getLine(bodyExpr), getColumn(bodyExpr)
|
|
4109
|
-
);
|
|
4110
|
-
const sourceNames: string[] = [];
|
|
4111
|
-
for (const expr of bodyExpr.expressions) {
|
|
4112
|
-
if (t.isIdentifier(expr)) sourceNames.push(expr.name);
|
|
4113
|
-
}
|
|
4114
|
-
if (sourceNames.length > 0) returnInfo.expressionSourceNames = sourceNames;
|
|
4115
|
-
}
|
|
4116
|
-
else if (t.isLiteral(bodyExpr)) {
|
|
4117
|
-
returnInfo.returnValueType = 'LITERAL';
|
|
4118
|
-
const literalId = `LITERAL#implicit_return#${module.file}#${line}:${column}:${literalCounterRef.value++}`;
|
|
4119
|
-
returnInfo.returnValueId = literalId;
|
|
4120
|
-
literals.push({
|
|
4121
|
-
id: literalId,
|
|
4122
|
-
type: 'LITERAL',
|
|
4123
|
-
value: ExpressionEvaluator.extractLiteralValue(bodyExpr),
|
|
4124
|
-
valueType: typeof ExpressionEvaluator.extractLiteralValue(bodyExpr),
|
|
4125
|
-
file: module.file,
|
|
4126
|
-
line: bodyLine,
|
|
4127
|
-
column: bodyColumn
|
|
4128
|
-
});
|
|
4129
|
-
}
|
|
4130
|
-
else if (t.isCallExpression(bodyExpr) && t.isIdentifier(bodyExpr.callee)) {
|
|
4131
|
-
returnInfo.returnValueType = 'CALL_SITE';
|
|
4132
|
-
returnInfo.returnValueLine = getLine(bodyExpr);
|
|
4133
|
-
returnInfo.returnValueColumn = getColumn(bodyExpr);
|
|
4134
|
-
returnInfo.returnValueCallName = bodyExpr.callee.name;
|
|
4135
|
-
}
|
|
4136
|
-
else if (t.isCallExpression(bodyExpr) && t.isMemberExpression(bodyExpr.callee)) {
|
|
4137
|
-
returnInfo.returnValueType = 'METHOD_CALL';
|
|
4138
|
-
returnInfo.returnValueLine = getLine(bodyExpr);
|
|
4139
|
-
returnInfo.returnValueColumn = getColumn(bodyExpr);
|
|
4140
|
-
if (t.isIdentifier(bodyExpr.callee.property)) {
|
|
4141
|
-
returnInfo.returnValueCallName = bodyExpr.callee.property.name;
|
|
4142
|
-
}
|
|
4143
|
-
}
|
|
4144
|
-
// REG-276: Detailed EXPRESSION handling for nested implicit arrow returns
|
|
4145
|
-
else if (t.isBinaryExpression(bodyExpr)) {
|
|
4146
|
-
returnInfo.returnValueType = 'EXPRESSION';
|
|
4147
|
-
returnInfo.expressionType = 'BinaryExpression';
|
|
4148
|
-
returnInfo.returnValueLine = getLine(bodyExpr);
|
|
4149
|
-
returnInfo.returnValueColumn = getColumn(bodyExpr);
|
|
4150
|
-
returnInfo.operator = bodyExpr.operator;
|
|
4151
|
-
returnInfo.returnValueId = NodeFactory.generateExpressionId(
|
|
4152
|
-
'BinaryExpression', module.file, getLine(bodyExpr), getColumn(bodyExpr)
|
|
4153
|
-
);
|
|
4154
|
-
if (t.isIdentifier(bodyExpr.left)) returnInfo.leftSourceName = bodyExpr.left.name;
|
|
4155
|
-
if (t.isIdentifier(bodyExpr.right)) returnInfo.rightSourceName = bodyExpr.right.name;
|
|
4156
|
-
}
|
|
4157
|
-
else if (t.isLogicalExpression(bodyExpr)) {
|
|
4158
|
-
returnInfo.returnValueType = 'EXPRESSION';
|
|
4159
|
-
returnInfo.expressionType = 'LogicalExpression';
|
|
4160
|
-
returnInfo.returnValueLine = getLine(bodyExpr);
|
|
4161
|
-
returnInfo.returnValueColumn = getColumn(bodyExpr);
|
|
4162
|
-
returnInfo.operator = bodyExpr.operator;
|
|
4163
|
-
returnInfo.returnValueId = NodeFactory.generateExpressionId(
|
|
4164
|
-
'LogicalExpression', module.file, getLine(bodyExpr), getColumn(bodyExpr)
|
|
4165
|
-
);
|
|
4166
|
-
if (t.isIdentifier(bodyExpr.left)) returnInfo.leftSourceName = bodyExpr.left.name;
|
|
4167
|
-
if (t.isIdentifier(bodyExpr.right)) returnInfo.rightSourceName = bodyExpr.right.name;
|
|
4168
|
-
}
|
|
4169
|
-
else if (t.isConditionalExpression(bodyExpr)) {
|
|
4170
|
-
returnInfo.returnValueType = 'EXPRESSION';
|
|
4171
|
-
returnInfo.expressionType = 'ConditionalExpression';
|
|
4172
|
-
returnInfo.returnValueLine = getLine(bodyExpr);
|
|
4173
|
-
returnInfo.returnValueColumn = getColumn(bodyExpr);
|
|
4174
|
-
returnInfo.returnValueId = NodeFactory.generateExpressionId(
|
|
4175
|
-
'ConditionalExpression', module.file, getLine(bodyExpr), getColumn(bodyExpr)
|
|
4176
|
-
);
|
|
4177
|
-
if (t.isIdentifier(bodyExpr.consequent)) returnInfo.consequentSourceName = bodyExpr.consequent.name;
|
|
4178
|
-
if (t.isIdentifier(bodyExpr.alternate)) returnInfo.alternateSourceName = bodyExpr.alternate.name;
|
|
4179
|
-
}
|
|
4180
|
-
else if (t.isUnaryExpression(bodyExpr)) {
|
|
4181
|
-
returnInfo.returnValueType = 'EXPRESSION';
|
|
4182
|
-
returnInfo.expressionType = 'UnaryExpression';
|
|
4183
|
-
returnInfo.returnValueLine = getLine(bodyExpr);
|
|
4184
|
-
returnInfo.returnValueColumn = getColumn(bodyExpr);
|
|
4185
|
-
returnInfo.operator = bodyExpr.operator;
|
|
4186
|
-
returnInfo.returnValueId = NodeFactory.generateExpressionId(
|
|
4187
|
-
'UnaryExpression', module.file, getLine(bodyExpr), getColumn(bodyExpr)
|
|
4188
|
-
);
|
|
4189
|
-
if (t.isIdentifier(bodyExpr.argument)) returnInfo.unaryArgSourceName = bodyExpr.argument.name;
|
|
4190
|
-
}
|
|
4191
|
-
else if (t.isMemberExpression(bodyExpr)) {
|
|
4192
|
-
returnInfo.returnValueType = 'EXPRESSION';
|
|
4193
|
-
returnInfo.expressionType = 'MemberExpression';
|
|
4194
|
-
returnInfo.returnValueLine = getLine(bodyExpr);
|
|
4195
|
-
returnInfo.returnValueColumn = getColumn(bodyExpr);
|
|
4196
|
-
returnInfo.returnValueId = NodeFactory.generateExpressionId(
|
|
4197
|
-
'MemberExpression', module.file, getLine(bodyExpr), getColumn(bodyExpr)
|
|
4198
|
-
);
|
|
4199
|
-
if (t.isIdentifier(bodyExpr.object)) {
|
|
4200
|
-
returnInfo.object = bodyExpr.object.name;
|
|
4201
|
-
returnInfo.objectSourceName = bodyExpr.object.name;
|
|
4202
|
-
}
|
|
4203
|
-
if (t.isIdentifier(bodyExpr.property)) returnInfo.property = bodyExpr.property.name;
|
|
4204
|
-
returnInfo.computed = bodyExpr.computed;
|
|
4205
|
-
}
|
|
4206
|
-
else {
|
|
4207
|
-
returnInfo.returnValueType = 'EXPRESSION';
|
|
4208
|
-
returnInfo.expressionType = bodyExpr.type;
|
|
4209
|
-
returnInfo.returnValueLine = getLine(bodyExpr);
|
|
4210
|
-
returnInfo.returnValueColumn = getColumn(bodyExpr);
|
|
4211
|
-
returnInfo.returnValueId = NodeFactory.generateExpressionId(
|
|
4212
|
-
bodyExpr.type, module.file, getLine(bodyExpr), getColumn(bodyExpr)
|
|
4213
|
-
);
|
|
4214
|
-
}
|
|
4215
|
-
|
|
4216
4268
|
returnStatements.push(returnInfo);
|
|
4217
4269
|
}
|
|
4218
4270
|
|
|
@@ -4282,6 +4334,13 @@ export class JSASTAnalyzer extends Plugin {
|
|
|
4282
4334
|
|
|
4283
4335
|
// Function call expressions
|
|
4284
4336
|
CallExpression: (callPath: NodePath<t.CallExpression>) => {
|
|
4337
|
+
// REG-311: Detect isAwaited (parent is AwaitExpression)
|
|
4338
|
+
const parent = callPath.parentPath;
|
|
4339
|
+
const isAwaited = parent?.isAwaitExpression() ?? false;
|
|
4340
|
+
|
|
4341
|
+
// REG-311: Detect isInsideTry (O(1) via depth counter)
|
|
4342
|
+
const isInsideTry = controlFlowState.tryBlockDepth > 0;
|
|
4343
|
+
|
|
4285
4344
|
this.handleCallExpression(
|
|
4286
4345
|
callPath.node,
|
|
4287
4346
|
processedCallSites,
|
|
@@ -4292,7 +4351,9 @@ export class JSASTAnalyzer extends Plugin {
|
|
|
4292
4351
|
callSiteCounterRef,
|
|
4293
4352
|
scopeTracker,
|
|
4294
4353
|
getCurrentScopeId(),
|
|
4295
|
-
collections
|
|
4354
|
+
collections,
|
|
4355
|
+
isAwaited,
|
|
4356
|
+
isInsideTry
|
|
4296
4357
|
);
|
|
4297
4358
|
|
|
4298
4359
|
// REG-334: Check for resolve/reject calls inside Promise executors
|
|
@@ -4397,6 +4458,165 @@ export class JSASTAnalyzer extends Plugin {
|
|
|
4397
4458
|
|
|
4398
4459
|
funcParent = funcParent.getFunctionParent();
|
|
4399
4460
|
}
|
|
4461
|
+
|
|
4462
|
+
// REG-311: Detect executor_reject pattern - reject(new Error()) inside Promise executor
|
|
4463
|
+
// Walk up to find Promise executor context and check if this is reject call with NewExpression arg
|
|
4464
|
+
funcParent = callPath.getFunctionParent();
|
|
4465
|
+
while (funcParent && currentFunctionId) {
|
|
4466
|
+
const funcNode = funcParent.node;
|
|
4467
|
+
const funcKey = `${funcNode.start}:${funcNode.end}`;
|
|
4468
|
+
const context = promiseExecutorContexts.get(funcKey);
|
|
4469
|
+
|
|
4470
|
+
if (context && calleeName === context.rejectName && callNode.arguments.length > 0) {
|
|
4471
|
+
// REG-311: Use the creator function's ID (the function that created the Promise),
|
|
4472
|
+
// not the executor's ID
|
|
4473
|
+
const targetFunctionId = context.creatorFunctionId || currentFunctionId;
|
|
4474
|
+
const arg = callNode.arguments[0];
|
|
4475
|
+
const callLine = getLine(callNode);
|
|
4476
|
+
const callColumn = getColumn(callNode);
|
|
4477
|
+
|
|
4478
|
+
// Case 1: reject(new Error())
|
|
4479
|
+
if (t.isNewExpression(arg) && t.isIdentifier(arg.callee)) {
|
|
4480
|
+
rejectionPatterns.push({
|
|
4481
|
+
functionId: targetFunctionId,
|
|
4482
|
+
errorClassName: arg.callee.name,
|
|
4483
|
+
rejectionType: 'executor_reject',
|
|
4484
|
+
file: module.file,
|
|
4485
|
+
line: callLine,
|
|
4486
|
+
column: callColumn
|
|
4487
|
+
});
|
|
4488
|
+
}
|
|
4489
|
+
// Case 2: reject(err) where err is variable
|
|
4490
|
+
else if (t.isIdentifier(arg)) {
|
|
4491
|
+
const varName = arg.name;
|
|
4492
|
+
// Check if it's a parameter of ANY containing function (executor, outer, etc.)
|
|
4493
|
+
// Walk up the function chain to find if varName is a parameter
|
|
4494
|
+
let isParameter = false;
|
|
4495
|
+
let checkParent: NodePath<t.Node> | null = funcParent;
|
|
4496
|
+
while (checkParent) {
|
|
4497
|
+
if (t.isFunction(checkParent.node)) {
|
|
4498
|
+
if (checkParent.node.params.some(p =>
|
|
4499
|
+
t.isIdentifier(p) && p.name === varName
|
|
4500
|
+
)) {
|
|
4501
|
+
isParameter = true;
|
|
4502
|
+
break;
|
|
4503
|
+
}
|
|
4504
|
+
}
|
|
4505
|
+
checkParent = checkParent.getFunctionParent();
|
|
4506
|
+
}
|
|
4507
|
+
|
|
4508
|
+
if (isParameter) {
|
|
4509
|
+
rejectionPatterns.push({
|
|
4510
|
+
functionId: targetFunctionId,
|
|
4511
|
+
errorClassName: null,
|
|
4512
|
+
rejectionType: 'variable_parameter',
|
|
4513
|
+
file: module.file,
|
|
4514
|
+
line: callLine,
|
|
4515
|
+
column: callColumn,
|
|
4516
|
+
sourceVariableName: varName
|
|
4517
|
+
});
|
|
4518
|
+
} else {
|
|
4519
|
+
// Try micro-trace
|
|
4520
|
+
const { errorClassName, tracePath } = this.microTraceToErrorClass(
|
|
4521
|
+
varName,
|
|
4522
|
+
funcParent as NodePath<t.Function>,
|
|
4523
|
+
variableDeclarations
|
|
4524
|
+
);
|
|
4525
|
+
|
|
4526
|
+
rejectionPatterns.push({
|
|
4527
|
+
functionId: targetFunctionId,
|
|
4528
|
+
errorClassName,
|
|
4529
|
+
rejectionType: errorClassName ? 'variable_traced' : 'variable_unknown',
|
|
4530
|
+
file: module.file,
|
|
4531
|
+
line: callLine,
|
|
4532
|
+
column: callColumn,
|
|
4533
|
+
sourceVariableName: varName,
|
|
4534
|
+
tracePath
|
|
4535
|
+
});
|
|
4536
|
+
}
|
|
4537
|
+
}
|
|
4538
|
+
break;
|
|
4539
|
+
}
|
|
4540
|
+
funcParent = funcParent.getFunctionParent();
|
|
4541
|
+
}
|
|
4542
|
+
}
|
|
4543
|
+
|
|
4544
|
+
// REG-311: Detect Promise.reject(new Error()) pattern
|
|
4545
|
+
if (t.isMemberExpression(callNode.callee) && currentFunctionId) {
|
|
4546
|
+
const memberCallee = callNode.callee;
|
|
4547
|
+
if (t.isIdentifier(memberCallee.object) &&
|
|
4548
|
+
memberCallee.object.name === 'Promise' &&
|
|
4549
|
+
t.isIdentifier(memberCallee.property) &&
|
|
4550
|
+
memberCallee.property.name === 'reject' &&
|
|
4551
|
+
callNode.arguments.length > 0) {
|
|
4552
|
+
const arg = callNode.arguments[0];
|
|
4553
|
+
const callLine = getLine(callNode);
|
|
4554
|
+
const callColumn = getColumn(callNode);
|
|
4555
|
+
|
|
4556
|
+
// Case 1: Promise.reject(new Error())
|
|
4557
|
+
if (t.isNewExpression(arg) && t.isIdentifier(arg.callee)) {
|
|
4558
|
+
rejectionPatterns.push({
|
|
4559
|
+
functionId: currentFunctionId,
|
|
4560
|
+
errorClassName: arg.callee.name,
|
|
4561
|
+
rejectionType: 'promise_reject',
|
|
4562
|
+
file: module.file,
|
|
4563
|
+
line: callLine,
|
|
4564
|
+
column: callColumn
|
|
4565
|
+
});
|
|
4566
|
+
}
|
|
4567
|
+
// Case 2: Promise.reject(err) where err is variable
|
|
4568
|
+
else if (t.isIdentifier(arg)) {
|
|
4569
|
+
const varName = arg.name;
|
|
4570
|
+
// Check if it's a parameter of containing function
|
|
4571
|
+
const isParameter = functionNode
|
|
4572
|
+
? functionNode.params.some(param => t.isIdentifier(param) && param.name === varName)
|
|
4573
|
+
: false;
|
|
4574
|
+
|
|
4575
|
+
if (isParameter) {
|
|
4576
|
+
rejectionPatterns.push({
|
|
4577
|
+
functionId: currentFunctionId,
|
|
4578
|
+
errorClassName: null,
|
|
4579
|
+
rejectionType: 'variable_parameter',
|
|
4580
|
+
file: module.file,
|
|
4581
|
+
line: callLine,
|
|
4582
|
+
column: callColumn,
|
|
4583
|
+
sourceVariableName: varName
|
|
4584
|
+
});
|
|
4585
|
+
} else {
|
|
4586
|
+
// Try micro-trace
|
|
4587
|
+
if (!functionPath) {
|
|
4588
|
+
rejectionPatterns.push({
|
|
4589
|
+
functionId: currentFunctionId,
|
|
4590
|
+
errorClassName: null,
|
|
4591
|
+
rejectionType: 'variable_unknown',
|
|
4592
|
+
file: module.file,
|
|
4593
|
+
line: callLine,
|
|
4594
|
+
column: callColumn,
|
|
4595
|
+
sourceVariableName: varName,
|
|
4596
|
+
tracePath: [varName]
|
|
4597
|
+
});
|
|
4598
|
+
return;
|
|
4599
|
+
}
|
|
4600
|
+
|
|
4601
|
+
const { errorClassName, tracePath } = this.microTraceToErrorClass(
|
|
4602
|
+
varName,
|
|
4603
|
+
functionPath,
|
|
4604
|
+
variableDeclarations
|
|
4605
|
+
);
|
|
4606
|
+
|
|
4607
|
+
rejectionPatterns.push({
|
|
4608
|
+
functionId: currentFunctionId,
|
|
4609
|
+
errorClassName,
|
|
4610
|
+
rejectionType: errorClassName ? 'variable_traced' : 'variable_unknown',
|
|
4611
|
+
file: module.file,
|
|
4612
|
+
line: callLine,
|
|
4613
|
+
column: callColumn,
|
|
4614
|
+
sourceVariableName: varName,
|
|
4615
|
+
tracePath
|
|
4616
|
+
});
|
|
4617
|
+
}
|
|
4618
|
+
}
|
|
4619
|
+
}
|
|
4400
4620
|
}
|
|
4401
4621
|
},
|
|
4402
4622
|
|
|
@@ -4460,7 +4680,9 @@ export class JSASTAnalyzer extends Plugin {
|
|
|
4460
4680
|
resolveName,
|
|
4461
4681
|
rejectName,
|
|
4462
4682
|
file: module.file,
|
|
4463
|
-
line
|
|
4683
|
+
line,
|
|
4684
|
+
// REG-311: Store the ID of the function that creates the Promise
|
|
4685
|
+
creatorFunctionId: currentFunctionId || undefined
|
|
4464
4686
|
});
|
|
4465
4687
|
}
|
|
4466
4688
|
}
|
|
@@ -4535,9 +4757,65 @@ export class JSASTAnalyzer extends Plugin {
|
|
|
4535
4757
|
});
|
|
4536
4758
|
}
|
|
4537
4759
|
}
|
|
4760
|
+
},
|
|
4761
|
+
|
|
4762
|
+
// Property access expressions (REG-395)
|
|
4763
|
+
// Shared handler for both MemberExpression and OptionalMemberExpression
|
|
4764
|
+
MemberExpression: (memberPath: NodePath<t.MemberExpression>) => {
|
|
4765
|
+
// Initialize collections if needed
|
|
4766
|
+
if (!collections.propertyAccesses) {
|
|
4767
|
+
collections.propertyAccesses = [];
|
|
4768
|
+
}
|
|
4769
|
+
if (!collections.propertyAccessCounterRef) {
|
|
4770
|
+
collections.propertyAccessCounterRef = { value: 0 };
|
|
4771
|
+
}
|
|
4772
|
+
|
|
4773
|
+
PropertyAccessVisitor.extractPropertyAccesses(
|
|
4774
|
+
memberPath,
|
|
4775
|
+
memberPath.node,
|
|
4776
|
+
module,
|
|
4777
|
+
collections.propertyAccesses as PropertyAccessInfo[],
|
|
4778
|
+
collections.propertyAccessCounterRef as CounterRef,
|
|
4779
|
+
scopeTracker,
|
|
4780
|
+
currentFunctionId || getCurrentScopeId()
|
|
4781
|
+
);
|
|
4782
|
+
},
|
|
4783
|
+
// OptionalMemberExpression: obj?.prop (same logic as MemberExpression)
|
|
4784
|
+
OptionalMemberExpression: (memberPath: NodePath) => {
|
|
4785
|
+
// Initialize collections if needed
|
|
4786
|
+
if (!collections.propertyAccesses) {
|
|
4787
|
+
collections.propertyAccesses = [];
|
|
4788
|
+
}
|
|
4789
|
+
if (!collections.propertyAccessCounterRef) {
|
|
4790
|
+
collections.propertyAccessCounterRef = { value: 0 };
|
|
4791
|
+
}
|
|
4792
|
+
|
|
4793
|
+
PropertyAccessVisitor.extractPropertyAccesses(
|
|
4794
|
+
memberPath,
|
|
4795
|
+
memberPath.node as t.MemberExpression,
|
|
4796
|
+
module,
|
|
4797
|
+
collections.propertyAccesses as PropertyAccessInfo[],
|
|
4798
|
+
collections.propertyAccessCounterRef as CounterRef,
|
|
4799
|
+
scopeTracker,
|
|
4800
|
+
currentFunctionId || getCurrentScopeId()
|
|
4801
|
+
);
|
|
4538
4802
|
}
|
|
4539
4803
|
});
|
|
4540
4804
|
|
|
4805
|
+
// REG-311: Second pass - collect CATCHES_FROM info for try/catch blocks
|
|
4806
|
+
// This links catch blocks to exception sources in their corresponding try blocks
|
|
4807
|
+
if (functionPath) {
|
|
4808
|
+
this.collectCatchesFromInfo(
|
|
4809
|
+
functionPath,
|
|
4810
|
+
catchBlocks,
|
|
4811
|
+
callSites,
|
|
4812
|
+
methodCalls,
|
|
4813
|
+
constructorCalls,
|
|
4814
|
+
catchesFromInfos,
|
|
4815
|
+
module
|
|
4816
|
+
);
|
|
4817
|
+
}
|
|
4818
|
+
|
|
4541
4819
|
// Phase 6 (REG-267): Attach control flow metadata to the function node
|
|
4542
4820
|
if (matchingFunction) {
|
|
4543
4821
|
const cyclomaticComplexity = 1 +
|
|
@@ -4546,13 +4824,27 @@ export class JSASTAnalyzer extends Plugin {
|
|
|
4546
4824
|
controlFlowState.caseCount +
|
|
4547
4825
|
controlFlowState.logicalOpCount;
|
|
4548
4826
|
|
|
4827
|
+
// REG-311: Collect rejection info for this function
|
|
4828
|
+
const functionRejectionPatterns = rejectionPatterns.filter(p => p.functionId === matchingFunction.id);
|
|
4829
|
+
const canReject = functionRejectionPatterns.length > 0;
|
|
4830
|
+
const hasAsyncThrow = functionRejectionPatterns.some(p => p.rejectionType === 'async_throw');
|
|
4831
|
+
const rejectedBuiltinErrors = [...new Set(
|
|
4832
|
+
functionRejectionPatterns
|
|
4833
|
+
.filter(p => p.errorClassName !== null)
|
|
4834
|
+
.map(p => p.errorClassName!)
|
|
4835
|
+
)];
|
|
4836
|
+
|
|
4549
4837
|
matchingFunction.controlFlow = {
|
|
4550
4838
|
hasBranches: controlFlowState.branchCount > 0,
|
|
4551
4839
|
hasLoops: controlFlowState.loopCount > 0,
|
|
4552
4840
|
hasTryCatch: controlFlowState.hasTryCatch,
|
|
4553
4841
|
hasEarlyReturn: controlFlowState.hasEarlyReturn,
|
|
4554
4842
|
hasThrow: controlFlowState.hasThrow,
|
|
4555
|
-
cyclomaticComplexity
|
|
4843
|
+
cyclomaticComplexity,
|
|
4844
|
+
// REG-311: Async error tracking
|
|
4845
|
+
canReject,
|
|
4846
|
+
hasAsyncThrow,
|
|
4847
|
+
rejectedBuiltinErrors: rejectedBuiltinErrors.length > 0 ? rejectedBuiltinErrors : undefined
|
|
4556
4848
|
};
|
|
4557
4849
|
}
|
|
4558
4850
|
}
|
|
@@ -4566,6 +4858,7 @@ export class JSASTAnalyzer extends Plugin {
|
|
|
4566
4858
|
* - Method calls (MemberExpression callee) → methodCalls collection
|
|
4567
4859
|
* - Array mutation detection (push, unshift, splice)
|
|
4568
4860
|
* - Object.assign() detection
|
|
4861
|
+
* - REG-311: isAwaited and isInsideTry metadata on CALL nodes
|
|
4569
4862
|
*
|
|
4570
4863
|
* @param callNode - The call expression AST node
|
|
4571
4864
|
* @param processedCallSites - Set of already processed call site keys to avoid duplicates
|
|
@@ -4577,6 +4870,8 @@ export class JSASTAnalyzer extends Plugin {
|
|
|
4577
4870
|
* @param scopeTracker - Optional scope tracker for semantic ID generation
|
|
4578
4871
|
* @param parentScopeId - ID of the parent scope containing this call
|
|
4579
4872
|
* @param collections - Full collections object for array/object mutations
|
|
4873
|
+
* @param isAwaited - REG-311: true if wrapped in await expression
|
|
4874
|
+
* @param isInsideTry - REG-311: true if inside try block
|
|
4580
4875
|
*/
|
|
4581
4876
|
private handleCallExpression(
|
|
4582
4877
|
callNode: t.CallExpression,
|
|
@@ -4588,7 +4883,9 @@ export class JSASTAnalyzer extends Plugin {
|
|
|
4588
4883
|
callSiteCounterRef: CounterRef,
|
|
4589
4884
|
scopeTracker: ScopeTracker | undefined,
|
|
4590
4885
|
parentScopeId: string,
|
|
4591
|
-
collections: VisitorCollections
|
|
4886
|
+
collections: VisitorCollections,
|
|
4887
|
+
isAwaited: boolean = false,
|
|
4888
|
+
isInsideTry: boolean = false
|
|
4592
4889
|
): void {
|
|
4593
4890
|
// Handle direct function calls (greet(), main())
|
|
4594
4891
|
if (callNode.callee.type === 'Identifier') {
|
|
@@ -4616,7 +4913,10 @@ export class JSASTAnalyzer extends Plugin {
|
|
|
4616
4913
|
line: getLine(callNode),
|
|
4617
4914
|
column: getColumn(callNode), // REG-223: Add column for coordinate-based lookup
|
|
4618
4915
|
parentScopeId,
|
|
4619
|
-
targetFunctionName: calleeName
|
|
4916
|
+
targetFunctionName: calleeName,
|
|
4917
|
+
// REG-311: Async error tracking metadata
|
|
4918
|
+
isAwaited,
|
|
4919
|
+
isInsideTry
|
|
4620
4920
|
});
|
|
4621
4921
|
}
|
|
4622
4922
|
// Handle method calls (obj.method(), data.process())
|
|
@@ -4657,7 +4957,11 @@ export class JSASTAnalyzer extends Plugin {
|
|
|
4657
4957
|
file: module.file,
|
|
4658
4958
|
line: getLine(callNode),
|
|
4659
4959
|
column: getColumn(callNode),
|
|
4660
|
-
parentScopeId
|
|
4960
|
+
parentScopeId,
|
|
4961
|
+
// REG-311: Async error tracking metadata
|
|
4962
|
+
isAwaited,
|
|
4963
|
+
isInsideTry,
|
|
4964
|
+
isMethodCall: true
|
|
4661
4965
|
});
|
|
4662
4966
|
|
|
4663
4967
|
// Check for array mutation methods (push, unshift, splice)
|
|
@@ -4694,6 +4998,7 @@ export class JSASTAnalyzer extends Plugin {
|
|
|
4694
4998
|
}
|
|
4695
4999
|
}
|
|
4696
5000
|
// REG-117: Nested array mutations like obj.arr.push(item)
|
|
5001
|
+
// REG-395: General nested method calls like a.b.c() or obj.nested.method()
|
|
4697
5002
|
// object is MemberExpression, property is the method name
|
|
4698
5003
|
else if (object.type === 'MemberExpression' && property.type === 'Identifier') {
|
|
4699
5004
|
const nestedMember = object;
|
|
@@ -4731,10 +5036,290 @@ export class JSASTAnalyzer extends Plugin {
|
|
|
4731
5036
|
);
|
|
4732
5037
|
}
|
|
4733
5038
|
}
|
|
5039
|
+
|
|
5040
|
+
// REG-395: Create CALL node for nested method calls like a.b.c()
|
|
5041
|
+
const objectName = CallExpressionVisitor.extractMemberExpressionName(nestedMember as t.MemberExpression);
|
|
5042
|
+
if (objectName) {
|
|
5043
|
+
const nodeKey = `${callNode.start}:${callNode.end}`;
|
|
5044
|
+
if (!processedMethodCalls.has(nodeKey)) {
|
|
5045
|
+
processedMethodCalls.add(nodeKey);
|
|
5046
|
+
|
|
5047
|
+
const fullName = `${objectName}.${methodName}`;
|
|
5048
|
+
const legacyId = `CALL#${fullName}#${module.file}#${getLine(callNode)}:${getColumn(callNode)}:${callSiteCounterRef.value++}`;
|
|
5049
|
+
|
|
5050
|
+
let methodCallId = legacyId;
|
|
5051
|
+
if (scopeTracker) {
|
|
5052
|
+
const discriminator = scopeTracker.getItemCounter(`CALL:${fullName}`);
|
|
5053
|
+
methodCallId = computeSemanticId('CALL', fullName, scopeTracker.getContext(), { discriminator });
|
|
5054
|
+
}
|
|
5055
|
+
|
|
5056
|
+
methodCalls.push({
|
|
5057
|
+
id: methodCallId,
|
|
5058
|
+
type: 'CALL',
|
|
5059
|
+
name: fullName,
|
|
5060
|
+
object: objectName,
|
|
5061
|
+
method: methodName,
|
|
5062
|
+
file: module.file,
|
|
5063
|
+
line: getLine(callNode),
|
|
5064
|
+
column: getColumn(callNode),
|
|
5065
|
+
parentScopeId,
|
|
5066
|
+
isMethodCall: true
|
|
5067
|
+
});
|
|
5068
|
+
}
|
|
5069
|
+
}
|
|
4734
5070
|
}
|
|
4735
5071
|
}
|
|
4736
5072
|
}
|
|
4737
5073
|
|
|
5074
|
+
/**
|
|
5075
|
+
* REG-311: Micro-trace - follow variable assignments within function to find error source.
|
|
5076
|
+
* Used to resolve reject(err) or throw err where err is a variable.
|
|
5077
|
+
*
|
|
5078
|
+
* Uses cycle detection via Set<variableName> to avoid infinite loops on circular assignments.
|
|
5079
|
+
*
|
|
5080
|
+
* @param variableName - Name of variable to trace
|
|
5081
|
+
* @param funcPath - NodePath of containing function for AST traversal
|
|
5082
|
+
* @param variableDeclarations - Variable declarations in current scope
|
|
5083
|
+
* @returns Error class name if traced to NewExpression, null otherwise, plus trace path
|
|
5084
|
+
*/
|
|
5085
|
+
private microTraceToErrorClass(
|
|
5086
|
+
variableName: string,
|
|
5087
|
+
funcPath: NodePath<t.Function>,
|
|
5088
|
+
_variableDeclarations: VariableDeclarationInfo[]
|
|
5089
|
+
): { errorClassName: string | null; tracePath: string[] } {
|
|
5090
|
+
const tracePath: string[] = [variableName];
|
|
5091
|
+
const visited = new Set<string>(); // Cycle detection
|
|
5092
|
+
let currentName = variableName;
|
|
5093
|
+
|
|
5094
|
+
const funcBody = funcPath.node.body;
|
|
5095
|
+
if (!t.isBlockStatement(funcBody)) {
|
|
5096
|
+
return { errorClassName: null, tracePath };
|
|
5097
|
+
}
|
|
5098
|
+
|
|
5099
|
+
// Iterate until we find a NewExpression or can't trace further
|
|
5100
|
+
while (!visited.has(currentName)) {
|
|
5101
|
+
visited.add(currentName);
|
|
5102
|
+
let found = false;
|
|
5103
|
+
let foundNewExpression: string | null = null;
|
|
5104
|
+
let nextName: string | null = null;
|
|
5105
|
+
|
|
5106
|
+
// Walk AST to find assignments: currentName = newValue
|
|
5107
|
+
funcPath.traverse({
|
|
5108
|
+
VariableDeclarator: (declPath: NodePath<t.VariableDeclarator>) => {
|
|
5109
|
+
if (found || foundNewExpression) return;
|
|
5110
|
+
if (t.isIdentifier(declPath.node.id) && declPath.node.id.name === currentName) {
|
|
5111
|
+
const init = declPath.node.init;
|
|
5112
|
+
if (init) {
|
|
5113
|
+
// Case 1: const err = new Error()
|
|
5114
|
+
if (t.isNewExpression(init) && t.isIdentifier(init.callee)) {
|
|
5115
|
+
tracePath.push(`new ${init.callee.name}()`);
|
|
5116
|
+
foundNewExpression = init.callee.name;
|
|
5117
|
+
found = true;
|
|
5118
|
+
return;
|
|
5119
|
+
}
|
|
5120
|
+
// Case 2: const err = otherVar (chain)
|
|
5121
|
+
if (t.isIdentifier(init)) {
|
|
5122
|
+
tracePath.push(init.name);
|
|
5123
|
+
nextName = init.name;
|
|
5124
|
+
found = true;
|
|
5125
|
+
return;
|
|
5126
|
+
}
|
|
5127
|
+
}
|
|
5128
|
+
}
|
|
5129
|
+
},
|
|
5130
|
+
AssignmentExpression: (assignPath: NodePath<t.AssignmentExpression>) => {
|
|
5131
|
+
if (found || foundNewExpression) return;
|
|
5132
|
+
const left = assignPath.node.left;
|
|
5133
|
+
const right = assignPath.node.right;
|
|
5134
|
+
|
|
5135
|
+
if (t.isIdentifier(left) && left.name === currentName) {
|
|
5136
|
+
if (t.isNewExpression(right) && t.isIdentifier(right.callee)) {
|
|
5137
|
+
tracePath.push(`new ${right.callee.name}()`);
|
|
5138
|
+
foundNewExpression = right.callee.name;
|
|
5139
|
+
found = true;
|
|
5140
|
+
return;
|
|
5141
|
+
}
|
|
5142
|
+
if (t.isIdentifier(right)) {
|
|
5143
|
+
tracePath.push(right.name);
|
|
5144
|
+
nextName = right.name;
|
|
5145
|
+
found = true;
|
|
5146
|
+
return;
|
|
5147
|
+
}
|
|
5148
|
+
}
|
|
5149
|
+
}
|
|
5150
|
+
});
|
|
5151
|
+
|
|
5152
|
+
// If we found a NewExpression, return the class name
|
|
5153
|
+
if (foundNewExpression) {
|
|
5154
|
+
return { errorClassName: foundNewExpression, tracePath };
|
|
5155
|
+
}
|
|
5156
|
+
|
|
5157
|
+
// If we found another variable to follow, continue
|
|
5158
|
+
if (nextName) {
|
|
5159
|
+
currentName = nextName;
|
|
5160
|
+
continue;
|
|
5161
|
+
}
|
|
5162
|
+
|
|
5163
|
+
// Couldn't trace further
|
|
5164
|
+
break;
|
|
5165
|
+
}
|
|
5166
|
+
|
|
5167
|
+
return { errorClassName: null, tracePath };
|
|
5168
|
+
}
|
|
5169
|
+
|
|
5170
|
+
/**
|
|
5171
|
+
* REG-311: Collect CATCHES_FROM info linking catch blocks to exception sources in try blocks.
|
|
5172
|
+
*
|
|
5173
|
+
* Sources include:
|
|
5174
|
+
* - Awaited calls: await foo() in try block
|
|
5175
|
+
* - Sync calls: foo() in try block (any call can throw)
|
|
5176
|
+
* - Throw statements: throw new Error() in try block
|
|
5177
|
+
* - Constructor calls: new SomeClass() in try block
|
|
5178
|
+
*
|
|
5179
|
+
* @param funcPath - Function path to traverse
|
|
5180
|
+
* @param catchBlocks - Collection of CATCH_BLOCK nodes
|
|
5181
|
+
* @param callSites - Collection of CALL nodes (direct function calls)
|
|
5182
|
+
* @param methodCalls - Collection of CALL nodes (method calls)
|
|
5183
|
+
* @param constructorCalls - Collection of CONSTRUCTOR_CALL nodes
|
|
5184
|
+
* @param catchesFromInfos - Collection to push CatchesFromInfo to
|
|
5185
|
+
* @param module - Module context
|
|
5186
|
+
*/
|
|
5187
|
+
private collectCatchesFromInfo(
|
|
5188
|
+
funcPath: NodePath<t.Function>,
|
|
5189
|
+
catchBlocks: CatchBlockInfo[],
|
|
5190
|
+
callSites: CallSiteInfo[],
|
|
5191
|
+
methodCalls: MethodCallInfo[],
|
|
5192
|
+
constructorCalls: ConstructorCallInfo[],
|
|
5193
|
+
catchesFromInfos: CatchesFromInfo[],
|
|
5194
|
+
module: VisitorModule
|
|
5195
|
+
): void {
|
|
5196
|
+
// Traverse to find TryStatements and collect sources
|
|
5197
|
+
funcPath.traverse({
|
|
5198
|
+
TryStatement: (tryPath: NodePath<t.TryStatement>) => {
|
|
5199
|
+
const tryNode = tryPath.node;
|
|
5200
|
+
const handler = tryNode.handler;
|
|
5201
|
+
|
|
5202
|
+
// Skip if no catch clause
|
|
5203
|
+
if (!handler) return;
|
|
5204
|
+
|
|
5205
|
+
// Find the catch block for this try
|
|
5206
|
+
// Match by line number since we don't have the tryBlockId here
|
|
5207
|
+
const catchLine = getLine(handler);
|
|
5208
|
+
const catchBlock = catchBlocks.find(cb =>
|
|
5209
|
+
cb.file === module.file && cb.line === catchLine
|
|
5210
|
+
);
|
|
5211
|
+
|
|
5212
|
+
if (!catchBlock || !catchBlock.parameterName) return;
|
|
5213
|
+
|
|
5214
|
+
// Traverse only the try block body (not catch or finally)
|
|
5215
|
+
const _tryBody = tryNode.block;
|
|
5216
|
+
const sources: Array<{ id: string; type: CatchesFromInfo['sourceType']; line: number }> = [];
|
|
5217
|
+
|
|
5218
|
+
// Collect sources from try block
|
|
5219
|
+
tryPath.get('block').traverse({
|
|
5220
|
+
// Stop at nested TryStatement - don't collect from inner try blocks
|
|
5221
|
+
TryStatement: (innerPath) => {
|
|
5222
|
+
innerPath.skip(); // Don't traverse into nested try blocks
|
|
5223
|
+
},
|
|
5224
|
+
|
|
5225
|
+
// Stop at function boundaries - don't collect from nested functions
|
|
5226
|
+
Function: (innerFuncPath) => {
|
|
5227
|
+
innerFuncPath.skip();
|
|
5228
|
+
},
|
|
5229
|
+
|
|
5230
|
+
CallExpression: (callPath: NodePath<t.CallExpression>) => {
|
|
5231
|
+
const callNode = callPath.node;
|
|
5232
|
+
const callLine = getLine(callNode);
|
|
5233
|
+
const callColumn = getColumn(callNode);
|
|
5234
|
+
|
|
5235
|
+
// Check if this is an awaited call
|
|
5236
|
+
const parent = callPath.parentPath;
|
|
5237
|
+
const isAwaited = parent?.isAwaitExpression() ?? false;
|
|
5238
|
+
|
|
5239
|
+
// Find the CALL node that matches this CallExpression
|
|
5240
|
+
let sourceId: string | null = null;
|
|
5241
|
+
let sourceType: CatchesFromInfo['sourceType'] = 'sync_call';
|
|
5242
|
+
|
|
5243
|
+
// Check method calls first (includes Promise.reject which is a method call)
|
|
5244
|
+
const matchingMethodCall = methodCalls.find(mc =>
|
|
5245
|
+
mc.file === module.file &&
|
|
5246
|
+
mc.line === callLine &&
|
|
5247
|
+
mc.column === callColumn
|
|
5248
|
+
);
|
|
5249
|
+
|
|
5250
|
+
if (matchingMethodCall) {
|
|
5251
|
+
sourceId = matchingMethodCall.id;
|
|
5252
|
+
sourceType = isAwaited ? 'awaited_call' : 'sync_call';
|
|
5253
|
+
} else {
|
|
5254
|
+
// Check direct function calls
|
|
5255
|
+
const matchingCallSite = callSites.find(cs =>
|
|
5256
|
+
cs.file === module.file &&
|
|
5257
|
+
cs.line === callLine &&
|
|
5258
|
+
cs.column === callColumn
|
|
5259
|
+
);
|
|
5260
|
+
|
|
5261
|
+
if (matchingCallSite) {
|
|
5262
|
+
sourceId = matchingCallSite.id;
|
|
5263
|
+
sourceType = isAwaited ? 'awaited_call' : 'sync_call';
|
|
5264
|
+
}
|
|
5265
|
+
}
|
|
5266
|
+
|
|
5267
|
+
if (sourceId) {
|
|
5268
|
+
sources.push({ id: sourceId, type: sourceType, line: callLine });
|
|
5269
|
+
}
|
|
5270
|
+
},
|
|
5271
|
+
|
|
5272
|
+
ThrowStatement: (throwPath: NodePath<t.ThrowStatement>) => {
|
|
5273
|
+
const throwNode = throwPath.node;
|
|
5274
|
+
const throwLine = getLine(throwNode);
|
|
5275
|
+
const throwColumn = getColumn(throwNode);
|
|
5276
|
+
|
|
5277
|
+
// Create a synthetic ID for the throw statement
|
|
5278
|
+
// We don't have THROW_STATEMENT nodes, so we use line/column as identifier
|
|
5279
|
+
const sourceId = `THROW#${module.file}#${throwLine}:${throwColumn}`;
|
|
5280
|
+
|
|
5281
|
+
sources.push({ id: sourceId, type: 'throw_statement', line: throwLine });
|
|
5282
|
+
},
|
|
5283
|
+
|
|
5284
|
+
NewExpression: (newPath: NodePath<t.NewExpression>) => {
|
|
5285
|
+
// Skip NewExpression that is direct argument of ThrowStatement
|
|
5286
|
+
// In `throw new Error()`, the throw statement is the primary source
|
|
5287
|
+
if (newPath.parentPath?.isThrowStatement()) {
|
|
5288
|
+
return;
|
|
5289
|
+
}
|
|
5290
|
+
|
|
5291
|
+
const newNode = newPath.node;
|
|
5292
|
+
const newLine = getLine(newNode);
|
|
5293
|
+
const newColumn = getColumn(newNode);
|
|
5294
|
+
|
|
5295
|
+
// Find matching constructor call
|
|
5296
|
+
const matchingConstructor = constructorCalls.find(cc =>
|
|
5297
|
+
cc.file === module.file &&
|
|
5298
|
+
cc.line === newLine &&
|
|
5299
|
+
cc.column === newColumn
|
|
5300
|
+
);
|
|
5301
|
+
|
|
5302
|
+
if (matchingConstructor) {
|
|
5303
|
+
sources.push({ id: matchingConstructor.id, type: 'constructor_call', line: newLine });
|
|
5304
|
+
}
|
|
5305
|
+
}
|
|
5306
|
+
});
|
|
5307
|
+
|
|
5308
|
+
// Create CatchesFromInfo for each source
|
|
5309
|
+
for (const source of sources) {
|
|
5310
|
+
catchesFromInfos.push({
|
|
5311
|
+
catchBlockId: catchBlock.id,
|
|
5312
|
+
parameterName: catchBlock.parameterName,
|
|
5313
|
+
sourceId: source.id,
|
|
5314
|
+
sourceType: source.type,
|
|
5315
|
+
file: module.file,
|
|
5316
|
+
sourceLine: source.line
|
|
5317
|
+
});
|
|
5318
|
+
}
|
|
5319
|
+
}
|
|
5320
|
+
});
|
|
5321
|
+
}
|
|
5322
|
+
|
|
4738
5323
|
/**
|
|
4739
5324
|
* Detect array mutation calls (push, unshift, splice) inside functions
|
|
4740
5325
|
* and collect mutation info for FLOWS_INTO edge creation in GraphBuilder
|
|
@@ -4832,17 +5417,22 @@ export class JSASTAnalyzer extends Plugin {
|
|
|
4832
5417
|
|
|
4833
5418
|
/**
|
|
4834
5419
|
* Detect indexed array assignment: arr[i] = value
|
|
4835
|
-
* Creates ArrayMutationInfo for FLOWS_INTO edge generation in GraphBuilder
|
|
5420
|
+
* Creates ArrayMutationInfo for FLOWS_INTO edge generation in GraphBuilder.
|
|
5421
|
+
* For non-variable values (LITERAL, OBJECT_LITERAL, ARRAY_LITERAL), creates
|
|
5422
|
+
* value nodes and sets valueNodeId so GraphBuilder can create FLOWS_INTO edges.
|
|
4836
5423
|
*
|
|
4837
5424
|
* @param assignNode - The assignment expression node
|
|
4838
5425
|
* @param module - Current module being analyzed
|
|
4839
5426
|
* @param arrayMutations - Collection to push mutation info into
|
|
5427
|
+
* @param scopeTracker - Scope tracker for semantic ID generation
|
|
5428
|
+
* @param collections - Collections for creating value nodes (literals, objectLiterals, etc.)
|
|
4840
5429
|
*/
|
|
4841
5430
|
private detectIndexedArrayAssignment(
|
|
4842
5431
|
assignNode: t.AssignmentExpression,
|
|
4843
5432
|
module: VisitorModule,
|
|
4844
5433
|
arrayMutations: ArrayMutationInfo[],
|
|
4845
|
-
scopeTracker?: ScopeTracker
|
|
5434
|
+
scopeTracker?: ScopeTracker,
|
|
5435
|
+
collections?: VisitorCollections
|
|
4846
5436
|
): void {
|
|
4847
5437
|
// Check for indexed array assignment: arr[i] = value
|
|
4848
5438
|
if (assignNode.left.type === 'MemberExpression' && assignNode.left.computed) {
|
|
@@ -4850,8 +5440,8 @@ export class JSASTAnalyzer extends Plugin {
|
|
|
4850
5440
|
|
|
4851
5441
|
// Only process NumericLiteral keys - those are clearly array indexed assignments
|
|
4852
5442
|
// e.g., arr[0] = value, arr[1] = value
|
|
4853
|
-
//
|
|
4854
|
-
//
|
|
5443
|
+
// Other computed keys (Identifier, expressions) are ambiguous (could be array or object)
|
|
5444
|
+
// and are handled by detectObjectPropertyAssignment as computed mutations
|
|
4855
5445
|
if (memberExpr.property.type !== 'NumericLiteral') {
|
|
4856
5446
|
return;
|
|
4857
5447
|
}
|
|
@@ -4861,34 +5451,79 @@ export class JSASTAnalyzer extends Plugin {
|
|
|
4861
5451
|
const arrayName = memberExpr.object.name;
|
|
4862
5452
|
const value = assignNode.right;
|
|
4863
5453
|
|
|
5454
|
+
// Use defensive loc checks instead of ! assertions
|
|
5455
|
+
const line = assignNode.loc?.start.line ?? 0;
|
|
5456
|
+
const column = assignNode.loc?.start.column ?? 0;
|
|
5457
|
+
|
|
4864
5458
|
const argInfo: ArrayMutationArgument = {
|
|
4865
5459
|
argIndex: 0,
|
|
4866
5460
|
isSpread: false,
|
|
4867
5461
|
valueType: 'EXPRESSION'
|
|
4868
5462
|
};
|
|
4869
5463
|
|
|
4870
|
-
// Determine value type
|
|
5464
|
+
// Determine value type and create value nodes for non-variable types (REG-392)
|
|
4871
5465
|
const literalValue = ExpressionEvaluator.extractLiteralValue(value);
|
|
4872
5466
|
if (literalValue !== null) {
|
|
4873
5467
|
argInfo.valueType = 'LITERAL';
|
|
4874
5468
|
argInfo.literalValue = literalValue;
|
|
5469
|
+
const valueLine = value.loc?.start.line ?? line;
|
|
5470
|
+
const valueColumn = value.loc?.start.column ?? column;
|
|
5471
|
+
// Create LITERAL node if collections available
|
|
5472
|
+
if (collections?.literals && collections.literalCounterRef) {
|
|
5473
|
+
const literalCounterRef = collections.literalCounterRef as CounterRef;
|
|
5474
|
+
const literalId = `LITERAL#indexed#${module.file}#${valueLine}:${valueColumn}:${literalCounterRef.value++}`;
|
|
5475
|
+
(collections.literals as LiteralInfo[]).push({
|
|
5476
|
+
id: literalId,
|
|
5477
|
+
type: 'LITERAL',
|
|
5478
|
+
value: literalValue,
|
|
5479
|
+
valueType: typeof literalValue,
|
|
5480
|
+
file: module.file,
|
|
5481
|
+
line: valueLine,
|
|
5482
|
+
column: valueColumn,
|
|
5483
|
+
parentCallId: undefined,
|
|
5484
|
+
argIndex: 0
|
|
5485
|
+
} as LiteralInfo);
|
|
5486
|
+
argInfo.valueNodeId = literalId;
|
|
5487
|
+
}
|
|
4875
5488
|
} else if (value.type === 'Identifier') {
|
|
4876
5489
|
argInfo.valueType = 'VARIABLE';
|
|
4877
5490
|
argInfo.valueName = value.name;
|
|
4878
5491
|
} else if (value.type === 'ObjectExpression') {
|
|
4879
5492
|
argInfo.valueType = 'OBJECT_LITERAL';
|
|
5493
|
+
const valueLine = value.loc?.start.line ?? line;
|
|
5494
|
+
const valueColumn = value.loc?.start.column ?? column;
|
|
5495
|
+
// Create OBJECT_LITERAL node if collections available
|
|
5496
|
+
if (collections?.objectLiteralCounterRef) {
|
|
5497
|
+
if (!collections.objectLiterals) collections.objectLiterals = [];
|
|
5498
|
+
const objectLiteralCounterRef = collections.objectLiteralCounterRef as CounterRef;
|
|
5499
|
+
const objectNode = ObjectLiteralNode.create(
|
|
5500
|
+
module.file, valueLine, valueColumn,
|
|
5501
|
+
{ counter: objectLiteralCounterRef.value++ }
|
|
5502
|
+
);
|
|
5503
|
+
(collections.objectLiterals as ObjectLiteralInfo[]).push(objectNode as unknown as ObjectLiteralInfo);
|
|
5504
|
+
argInfo.valueNodeId = objectNode.id;
|
|
5505
|
+
}
|
|
4880
5506
|
} else if (value.type === 'ArrayExpression') {
|
|
4881
5507
|
argInfo.valueType = 'ARRAY_LITERAL';
|
|
5508
|
+
const valueLine = value.loc?.start.line ?? line;
|
|
5509
|
+
const valueColumn = value.loc?.start.column ?? column;
|
|
5510
|
+
// Create ARRAY_LITERAL node if collections available
|
|
5511
|
+
if (collections?.arrayLiteralCounterRef) {
|
|
5512
|
+
if (!collections.arrayLiterals) collections.arrayLiterals = [];
|
|
5513
|
+
const arrayLiteralCounterRef = collections.arrayLiteralCounterRef as CounterRef;
|
|
5514
|
+
const arrayNode = ArrayLiteralNode.create(
|
|
5515
|
+
module.file, valueLine, valueColumn,
|
|
5516
|
+
{ counter: arrayLiteralCounterRef.value++ }
|
|
5517
|
+
);
|
|
5518
|
+
(collections.arrayLiterals as ArrayLiteralInfo[]).push(arrayNode as unknown as ArrayLiteralInfo);
|
|
5519
|
+
argInfo.valueNodeId = arrayNode.id;
|
|
5520
|
+
}
|
|
4882
5521
|
} else if (value.type === 'CallExpression') {
|
|
4883
5522
|
argInfo.valueType = 'CALL';
|
|
4884
5523
|
argInfo.callLine = value.loc?.start.line;
|
|
4885
5524
|
argInfo.callColumn = value.loc?.start.column;
|
|
4886
5525
|
}
|
|
4887
5526
|
|
|
4888
|
-
// Use defensive loc checks instead of ! assertions
|
|
4889
|
-
const line = assignNode.loc?.start.line ?? 0;
|
|
4890
|
-
const column = assignNode.loc?.start.column ?? 0;
|
|
4891
|
-
|
|
4892
5527
|
// Capture scope path for scope-aware lookup (REG-309)
|
|
4893
5528
|
const scopePath = scopeTracker?.getContext().scopePath ?? [];
|
|
4894
5529
|
|