gitnexus 1.6.8-rc.8 → 1.6.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +3 -3
- package/dist/_shared/graph/types.d.ts +31 -1
- package/dist/_shared/graph/types.d.ts.map +1 -1
- package/dist/_shared/language-detection.d.ts.map +1 -1
- package/dist/_shared/language-detection.js +11 -1
- package/dist/_shared/language-detection.js.map +1 -1
- package/dist/_shared/lbug/schema-constants.d.ts +1 -1
- package/dist/_shared/lbug/schema-constants.d.ts.map +1 -1
- package/dist/_shared/lbug/schema-constants.js +6 -0
- package/dist/_shared/lbug/schema-constants.js.map +1 -1
- package/dist/_shared/scope-resolution/parsed-file.d.ts +21 -0
- package/dist/_shared/scope-resolution/parsed-file.d.ts.map +1 -1
- package/dist/_shared/scope-resolution/symbol-definition.d.ts +6 -0
- package/dist/_shared/scope-resolution/symbol-definition.d.ts.map +1 -1
- package/dist/cli/ai-context.d.ts +31 -1
- package/dist/cli/ai-context.js +18 -16
- package/dist/cli/analyze-config.js +11 -0
- package/dist/cli/analyze.d.ts +14 -0
- package/dist/cli/analyze.js +98 -0
- package/dist/cli/embedding-dims.d.ts +33 -0
- package/dist/cli/embedding-dims.js +39 -0
- package/dist/cli/eval-server.d.ts +8 -0
- package/dist/cli/eval-server.js +242 -2
- package/dist/cli/help-i18n.js +19 -0
- package/dist/cli/i18n/en.d.ts +15 -1
- package/dist/cli/i18n/en.js +15 -1
- package/dist/cli/i18n/resources.d.ts +29 -1
- package/dist/cli/i18n/zh-CN.d.ts +14 -0
- package/dist/cli/i18n/zh-CN.js +15 -1
- package/dist/cli/index.js +89 -1
- package/dist/cli/mcp.d.ts +6 -1
- package/dist/cli/mcp.js +26 -1
- package/dist/cli/optional-grammars.d.ts +6 -8
- package/dist/cli/optional-grammars.js +8 -11
- package/dist/cli/setup.d.ts +3 -1
- package/dist/cli/setup.js +67 -17
- package/dist/cli/skill-gen.js +2 -1
- package/dist/cli/tool.d.ts +19 -1
- package/dist/cli/tool.js +96 -3
- package/dist/core/embeddings/hf-env.d.ts +0 -66
- package/dist/core/embeddings/http-client.d.ts +7 -0
- package/dist/core/embeddings/http-client.js +5 -3
- package/dist/core/embeddings/server-mapping.d.ts +1 -1
- package/dist/core/embeddings/server-mapping.js +7 -3
- package/dist/core/graph/import-cycles.d.ts +10 -0
- package/dist/core/graph/import-cycles.js +103 -0
- package/dist/core/group/cross-impact.d.ts +2 -1
- package/dist/core/group/cross-impact.js +5 -1
- package/dist/core/group/extractors/grpc-patterns/proto.js +10 -6
- package/dist/core/group/extractors/http-patterns/kotlin.js +7 -7
- package/dist/core/group/extractors/http-route-extractor.d.ts +24 -0
- package/dist/core/group/extractors/http-route-extractor.js +22 -4
- package/dist/core/group/extractors/include-extractor.js +22 -13
- package/dist/core/group/sync.d.ts +27 -1
- package/dist/core/group/sync.js +0 -0
- package/dist/core/incremental/subgraph-extract.js +18 -1
- package/dist/core/ingestion/cfg/cfg-builder.d.ts +114 -0
- package/dist/core/ingestion/cfg/cfg-builder.js +196 -0
- package/dist/core/ingestion/cfg/collect.d.ts +51 -0
- package/dist/core/ingestion/cfg/collect.js +77 -0
- package/dist/core/ingestion/cfg/control-dependence.d.ts +58 -0
- package/dist/core/ingestion/cfg/control-dependence.js +182 -0
- package/dist/core/ingestion/cfg/control-flow-context.d.ts +106 -0
- package/dist/core/ingestion/cfg/control-flow-context.js +124 -0
- package/dist/core/ingestion/cfg/emit.d.ts +293 -0
- package/dist/core/ingestion/cfg/emit.js +646 -0
- package/dist/core/ingestion/cfg/post-dominators.d.ts +79 -0
- package/dist/core/ingestion/cfg/post-dominators.js +176 -0
- package/dist/core/ingestion/cfg/reaching-def-reason-codec.d.ts +106 -0
- package/dist/core/ingestion/cfg/reaching-def-reason-codec.js +133 -0
- package/dist/core/ingestion/cfg/reaching-defs-graph.d.ts +18 -0
- package/dist/core/ingestion/cfg/reaching-defs-graph.js +312 -0
- package/dist/core/ingestion/cfg/reaching-defs.d.ts +162 -0
- package/dist/core/ingestion/cfg/reaching-defs.js +785 -0
- package/dist/core/ingestion/cfg/synthetic-escape.d.ts +124 -0
- package/dist/core/ingestion/cfg/synthetic-escape.js +300 -0
- package/dist/core/ingestion/cfg/traversal-result.d.ts +20 -0
- package/dist/core/ingestion/cfg/traversal-result.js +2 -0
- package/dist/core/ingestion/cfg/types.d.ts +274 -0
- package/dist/core/ingestion/cfg/types.js +13 -0
- package/dist/core/ingestion/cfg/visitors/c-cpp-harvest.d.ts +111 -0
- package/dist/core/ingestion/cfg/visitors/c-cpp-harvest.js +541 -0
- package/dist/core/ingestion/cfg/visitors/c-cpp.d.ts +74 -0
- package/dist/core/ingestion/cfg/visitors/c-cpp.js +584 -0
- package/dist/core/ingestion/cfg/visitors/call-site-harvest.d.ts +195 -0
- package/dist/core/ingestion/cfg/visitors/call-site-harvest.js +350 -0
- package/dist/core/ingestion/cfg/visitors/csharp-harvest.d.ts +124 -0
- package/dist/core/ingestion/cfg/visitors/csharp-harvest.js +589 -0
- package/dist/core/ingestion/cfg/visitors/csharp.d.ts +87 -0
- package/dist/core/ingestion/cfg/visitors/csharp.js +867 -0
- package/dist/core/ingestion/cfg/visitors/dart-harvest.d.ts +278 -0
- package/dist/core/ingestion/cfg/visitors/dart-harvest.js +870 -0
- package/dist/core/ingestion/cfg/visitors/dart.d.ts +119 -0
- package/dist/core/ingestion/cfg/visitors/dart.js +836 -0
- package/dist/core/ingestion/cfg/visitors/go-harvest.d.ts +160 -0
- package/dist/core/ingestion/cfg/visitors/go-harvest.js +621 -0
- package/dist/core/ingestion/cfg/visitors/go.d.ts +108 -0
- package/dist/core/ingestion/cfg/visitors/go.js +638 -0
- package/dist/core/ingestion/cfg/visitors/java-harvest.d.ts +108 -0
- package/dist/core/ingestion/cfg/visitors/java-harvest.js +513 -0
- package/dist/core/ingestion/cfg/visitors/java.d.ts +101 -0
- package/dist/core/ingestion/cfg/visitors/java.js +812 -0
- package/dist/core/ingestion/cfg/visitors/kotlin-harvest.d.ts +253 -0
- package/dist/core/ingestion/cfg/visitors/kotlin-harvest.js +719 -0
- package/dist/core/ingestion/cfg/visitors/kotlin.d.ts +121 -0
- package/dist/core/ingestion/cfg/visitors/kotlin.js +809 -0
- package/dist/core/ingestion/cfg/visitors/php-harvest.d.ts +172 -0
- package/dist/core/ingestion/cfg/visitors/php-harvest.js +626 -0
- package/dist/core/ingestion/cfg/visitors/php.d.ts +96 -0
- package/dist/core/ingestion/cfg/visitors/php.js +721 -0
- package/dist/core/ingestion/cfg/visitors/python-harvest.d.ts +212 -0
- package/dist/core/ingestion/cfg/visitors/python-harvest.js +739 -0
- package/dist/core/ingestion/cfg/visitors/python.d.ts +103 -0
- package/dist/core/ingestion/cfg/visitors/python.js +558 -0
- package/dist/core/ingestion/cfg/visitors/ruby-harvest.d.ts +223 -0
- package/dist/core/ingestion/cfg/visitors/ruby-harvest.js +587 -0
- package/dist/core/ingestion/cfg/visitors/ruby.d.ts +112 -0
- package/dist/core/ingestion/cfg/visitors/ruby.js +756 -0
- package/dist/core/ingestion/cfg/visitors/rust-harvest.d.ts +324 -0
- package/dist/core/ingestion/cfg/visitors/rust-harvest.js +873 -0
- package/dist/core/ingestion/cfg/visitors/rust.d.ts +95 -0
- package/dist/core/ingestion/cfg/visitors/rust.js +558 -0
- package/dist/core/ingestion/cfg/visitors/scope-tree-harvest.d.ts +87 -0
- package/dist/core/ingestion/cfg/visitors/scope-tree-harvest.js +116 -0
- package/dist/core/ingestion/cfg/visitors/swift-harvest.d.ts +255 -0
- package/dist/core/ingestion/cfg/visitors/swift-harvest.js +679 -0
- package/dist/core/ingestion/cfg/visitors/swift.d.ts +116 -0
- package/dist/core/ingestion/cfg/visitors/swift.js +787 -0
- package/dist/core/ingestion/cfg/visitors/typescript-harvest.d.ts +149 -0
- package/dist/core/ingestion/cfg/visitors/typescript-harvest.js +1056 -0
- package/dist/core/ingestion/cfg/visitors/typescript.d.ts +66 -0
- package/dist/core/ingestion/cfg/visitors/typescript.js +583 -0
- package/dist/core/ingestion/import-resolvers/utils.js +2 -0
- package/dist/core/ingestion/language-provider.d.ts +23 -2
- package/dist/core/ingestion/languages/c-cpp.js +17 -5
- package/dist/core/ingestion/languages/cobol.js +4 -0
- package/dist/core/ingestion/languages/cpp/arity-metadata.js +41 -2
- package/dist/core/ingestion/languages/cpp/captures.js +50 -1
- package/dist/core/ingestion/languages/cpp/conversion-rank.d.ts +2 -0
- package/dist/core/ingestion/languages/cpp/conversion-rank.js +89 -0
- package/dist/core/ingestion/languages/cpp/header-scan.d.ts +1 -1
- package/dist/core/ingestion/languages/cpp/header-scan.js +2 -2
- package/dist/core/ingestion/languages/cpp/inline-namespaces.js +7 -2
- package/dist/core/ingestion/languages/cpp/member-lookup.js +2 -1
- package/dist/core/ingestion/languages/cpp/query.js +23 -0
- package/dist/core/ingestion/languages/cpp/scope-resolver.js +2 -1
- package/dist/core/ingestion/languages/csharp.js +2 -0
- package/dist/core/ingestion/languages/dart.js +2 -0
- package/dist/core/ingestion/languages/go.js +2 -0
- package/dist/core/ingestion/languages/java.js +3 -0
- package/dist/core/ingestion/languages/kotlin/query.js +3 -2
- package/dist/core/ingestion/languages/kotlin.js +8 -1
- package/dist/core/ingestion/languages/php.js +2 -0
- package/dist/core/ingestion/languages/python.js +2 -0
- package/dist/core/ingestion/languages/ruby.js +2 -0
- package/dist/core/ingestion/languages/rust.js +2 -0
- package/dist/core/ingestion/languages/swift.js +2 -0
- package/dist/core/ingestion/languages/typescript.js +5 -0
- package/dist/core/ingestion/languages/vue.js +6 -0
- package/dist/core/ingestion/method-extractors/configs/c-cpp.js +6 -13
- package/dist/core/ingestion/method-extractors/generic.js +1 -0
- package/dist/core/ingestion/method-types.d.ts +2 -0
- package/dist/core/ingestion/model/symbol-table.d.ts +1 -0
- package/dist/core/ingestion/model/symbol-table.js +1 -0
- package/dist/core/ingestion/mro-processor.js +109 -0
- package/dist/core/ingestion/parsing-processor.js +44 -0
- package/dist/core/ingestion/pipeline-phases/call-summaries.d.ts +26 -0
- package/dist/core/ingestion/pipeline-phases/call-summaries.js +46 -0
- package/dist/core/ingestion/pipeline-phases/index.d.ts +2 -0
- package/dist/core/ingestion/pipeline-phases/index.js +2 -0
- package/dist/core/ingestion/pipeline-phases/parse-impl.d.ts +0 -21
- package/dist/core/ingestion/pipeline-phases/parse-impl.js +18 -1
- package/dist/core/ingestion/pipeline-phases/routes.d.ts +10 -0
- package/dist/core/ingestion/pipeline-phases/routes.js +30 -1
- package/dist/core/ingestion/pipeline-phases/taint-summaries.d.ts +30 -0
- package/dist/core/ingestion/pipeline-phases/taint-summaries.js +81 -0
- package/dist/core/ingestion/pipeline.d.ts +92 -10
- package/dist/core/ingestion/pipeline.js +11 -2
- package/dist/core/ingestion/scope-extractor.js +12 -2
- package/dist/core/ingestion/scope-resolution/contract/scope-resolver.d.ts +9 -0
- package/dist/core/ingestion/scope-resolution/graph-bridge/callee-id-sink.d.ts +75 -0
- package/dist/core/ingestion/scope-resolution/graph-bridge/callee-id-sink.js +67 -0
- package/dist/core/ingestion/scope-resolution/graph-bridge/edges.d.ts +12 -2
- package/dist/core/ingestion/scope-resolution/graph-bridge/edges.js +14 -2
- package/dist/core/ingestion/scope-resolution/graph-bridge/references-to-edges.d.ts +6 -1
- package/dist/core/ingestion/scope-resolution/graph-bridge/references-to-edges.js +13 -1
- package/dist/core/ingestion/scope-resolution/passes/free-call-fallback.d.ts +9 -1
- package/dist/core/ingestion/scope-resolution/passes/free-call-fallback.js +28 -2
- package/dist/core/ingestion/scope-resolution/passes/overload-narrowing.d.ts +4 -0
- package/dist/core/ingestion/scope-resolution/passes/overload-narrowing.js +9 -0
- package/dist/core/ingestion/scope-resolution/passes/receiver-bound-calls.d.ts +6 -1
- package/dist/core/ingestion/scope-resolution/passes/receiver-bound-calls.js +174 -40
- package/dist/core/ingestion/scope-resolution/pipeline/phase.d.ts +24 -0
- package/dist/core/ingestion/scope-resolution/pipeline/phase.js +276 -190
- package/dist/core/ingestion/scope-resolution/pipeline/reconcile-ownership.js +43 -3
- package/dist/core/ingestion/scope-resolution/pipeline/run.d.ts +75 -0
- package/dist/core/ingestion/scope-resolution/pipeline/run.js +349 -1
- package/dist/core/ingestion/scope-resolution/resolution-outcome.d.ts +1 -1
- package/dist/core/ingestion/taint/call-summary-codec.d.ts +75 -0
- package/dist/core/ingestion/taint/call-summary-codec.js +128 -0
- package/dist/core/ingestion/taint/call-summary-emit.d.ts +43 -0
- package/dist/core/ingestion/taint/call-summary-emit.js +70 -0
- package/dist/core/ingestion/taint/call-summary-harvest.d.ts +77 -0
- package/dist/core/ingestion/taint/call-summary-harvest.js +179 -0
- package/dist/core/ingestion/taint/call-summary-model.d.ts +47 -0
- package/dist/core/ingestion/taint/call-summary-model.js +25 -0
- package/dist/core/ingestion/taint/emit.d.ts +125 -0
- package/dist/core/ingestion/taint/emit.js +208 -0
- package/dist/core/ingestion/taint/interproc-emit.d.ts +50 -0
- package/dist/core/ingestion/taint/interproc-emit.js +88 -0
- package/dist/core/ingestion/taint/interproc-solver.d.ts +110 -0
- package/dist/core/ingestion/taint/interproc-solver.js +312 -0
- package/dist/core/ingestion/taint/match.d.ts +153 -0
- package/dist/core/ingestion/taint/match.js +278 -0
- package/dist/core/ingestion/taint/path-codec.d.ts +134 -0
- package/dist/core/ingestion/taint/path-codec.js +190 -0
- package/dist/core/ingestion/taint/propagate.d.ts +216 -0
- package/dist/core/ingestion/taint/propagate.js +659 -0
- package/dist/core/ingestion/taint/site-safety.d.ts +29 -0
- package/dist/core/ingestion/taint/site-safety.js +98 -0
- package/dist/core/ingestion/taint/source-sink-config.d.ts +107 -23
- package/dist/core/ingestion/taint/source-sink-config.js +35 -12
- package/dist/core/ingestion/taint/source-sink-registry.d.ts +6 -4
- package/dist/core/ingestion/taint/source-sink-registry.js +6 -4
- package/dist/core/ingestion/taint/summary-harvest-driver.d.ts +80 -0
- package/dist/core/ingestion/taint/summary-harvest-driver.js +179 -0
- package/dist/core/ingestion/taint/summary-harvest.d.ts +89 -0
- package/dist/core/ingestion/taint/summary-harvest.js +537 -0
- package/dist/core/ingestion/taint/summary-model.d.ts +200 -0
- package/dist/core/ingestion/taint/summary-model.js +86 -0
- package/dist/core/ingestion/taint/typescript-model.d.ts +38 -0
- package/dist/core/ingestion/taint/typescript-model.js +102 -0
- package/dist/core/ingestion/type-extractors/c-cpp.js +21 -0
- package/dist/core/ingestion/utils/env.d.ts +7 -0
- package/dist/core/ingestion/utils/env.js +12 -0
- package/dist/core/ingestion/utils/method-props.js +1 -0
- package/dist/core/ingestion/workers/clone-safety.d.ts +109 -0
- package/dist/core/ingestion/workers/clone-safety.js +465 -0
- package/dist/core/ingestion/workers/parse-worker.d.ts +23 -0
- package/dist/core/ingestion/workers/parse-worker.js +81 -53
- package/dist/core/ingestion/workers/post-result.d.ts +22 -0
- package/dist/core/ingestion/workers/post-result.js +87 -0
- package/dist/core/ingestion/workers/result-merge.d.ts +20 -0
- package/dist/core/ingestion/workers/result-merge.js +43 -0
- package/dist/core/ingestion/workers/worker-pool.d.ts +15 -40
- package/dist/core/ingestion/workers/worker-pool.js +43 -16
- package/dist/core/lbug/csv-generator.d.ts +51 -3
- package/dist/core/lbug/csv-generator.js +369 -255
- package/dist/core/lbug/lbug-adapter.d.ts +58 -18
- package/dist/core/lbug/lbug-adapter.js +346 -84
- package/dist/core/lbug/lbug-config.d.ts +19 -0
- package/dist/core/lbug/lbug-config.js +29 -0
- package/dist/core/lbug/pdg-emit-sink.d.ts +127 -0
- package/dist/core/lbug/pdg-emit-sink.js +358 -0
- package/dist/core/lbug/pool-adapter.d.ts +35 -0
- package/dist/core/lbug/pool-adapter.js +90 -1
- package/dist/core/lbug/rel-pair-routing.d.ts +54 -0
- package/dist/core/lbug/rel-pair-routing.js +140 -0
- package/dist/core/lbug/schema.d.ts +2 -2
- package/dist/core/lbug/schema.js +3 -0
- package/dist/core/logger.d.ts +0 -5
- package/dist/core/run-analyze.d.ts +96 -0
- package/dist/core/run-analyze.js +207 -3
- package/dist/core/tree-sitter/parser-loader.js +5 -4
- package/dist/core/tree-sitter/safe-parse.d.ts +0 -5
- package/dist/core/tree-sitter/vendored-grammars.d.ts +39 -0
- package/dist/core/tree-sitter/vendored-grammars.js +57 -0
- package/dist/core/wiki/generator.js +3 -1
- package/dist/core/wiki/graph-queries.d.ts +6 -0
- package/dist/core/wiki/graph-queries.js +9 -1
- package/dist/mcp/http-transport.d.ts +134 -0
- package/dist/mcp/http-transport.js +503 -0
- package/dist/mcp/local/local-backend.d.ts +110 -24
- package/dist/mcp/local/local-backend.js +1225 -19
- package/dist/mcp/local/pdg-impact.d.ts +384 -0
- package/dist/mcp/local/pdg-impact.js +1972 -0
- package/dist/mcp/resources.js +13 -1
- package/dist/mcp/tools.d.ts +12 -0
- package/dist/mcp/tools.js +214 -7
- package/dist/server/analyze-launch.d.ts +29 -0
- package/dist/server/analyze-launch.js +137 -0
- package/dist/server/analyze-upload.d.ts +33 -0
- package/dist/server/analyze-upload.js +123 -0
- package/dist/server/analyze-worker-ipc.d.ts +58 -0
- package/dist/server/analyze-worker-ipc.js +16 -0
- package/dist/server/analyze-worker.d.ts +17 -1
- package/dist/server/analyze-worker.js +7 -1
- package/dist/server/api.d.ts +17 -0
- package/dist/server/api.js +132 -168
- package/dist/server/git-clone.d.ts +43 -2
- package/dist/server/git-clone.js +211 -16
- package/dist/server/mcp-http.d.ts +5 -6
- package/dist/server/mcp-http.js +9 -82
- package/dist/server/middleware.d.ts +40 -0
- package/dist/server/middleware.js +92 -0
- package/dist/server/private-ip.d.ts +1 -0
- package/dist/server/private-ip.js +15 -0
- package/dist/server/upload-ingest.d.ts +56 -0
- package/dist/server/upload-ingest.js +276 -0
- package/dist/server/upload-paths.d.ts +37 -0
- package/dist/server/upload-paths.js +57 -0
- package/dist/server/upload-sweep.d.ts +20 -0
- package/dist/server/upload-sweep.js +57 -0
- package/dist/storage/git.js +5 -8
- package/dist/storage/parse-cache.d.ts +22 -1
- package/dist/storage/parse-cache.js +44 -10
- package/dist/storage/repo-manager.d.ts +121 -10
- package/dist/storage/repo-manager.js +43 -5
- package/dist/types/pipeline.d.ts +9 -0
- package/hooks/antigravity/gitnexus-antigravity-hook.cjs +110 -10
- package/hooks/claude/gitnexus-hook.cjs +110 -13
- package/hooks/claude/hook-db-lock-probe.cjs +513 -26
- package/package.json +8 -2
- package/scripts/assert-publish-grammar-coverage.cjs +31 -0
- package/scripts/build-tree-sitter-grammars.cjs +16 -10
- package/scripts/cross-platform-tests.ts +1 -0
- package/skills/gitnexus-debugging.md +16 -4
- package/skills/gitnexus-exploring.md +3 -3
- package/skills/gitnexus-guide.md +33 -0
- package/skills/gitnexus-pdg-query.md +89 -0
- package/skills/gitnexus-refactoring.md +1 -1
- package/skills/gitnexus-taint-analysis.md +178 -0
- package/vendor/tree-sitter-c/package.json +1 -1
- package/vendor/tree-sitter-dart/package.json +1 -1
- package/vendor/tree-sitter-kotlin/package.json +1 -1
- package/vendor/tree-sitter-proto/package.json +1 -1
- package/vendor/tree-sitter-swift/package.json +1 -1
- package/web/assets/{agent-CKPMqImC.js → agent-D3Xd7zZE.js} +123 -123
- package/web/assets/architecture-7EHR7CIX-6QZW5X65-DxJw65fT.js +1 -0
- package/web/assets/architectureDiagram-UL44E2DR-ChH9xW-N.js +36 -0
- package/web/assets/{blockDiagram-7IZFK4PR-B4niSLvR.js → blockDiagram-7IZFK4PR-BkWhpzWB.js} +1 -1
- package/web/assets/{c4Diagram-Y2BXMSZH-DIzEXalR.js → c4Diagram-Y2BXMSZH-DtBvpzWD.js} +1 -1
- package/web/assets/{chunk-3SSMPTDK-BzGeeheI.js → chunk-3SSMPTDK-B4_etUhr.js} +2 -2
- package/web/assets/{chunk-5IMINLNL-CnTLsXIV.js → chunk-5IMINLNL-DOdTeQri.js} +1 -1
- package/web/assets/{chunk-5VCL7Z4A-w1BCx98Z.js → chunk-5VCL7Z4A-CQ-2dYWj.js} +1 -1
- package/web/assets/chunk-6764PJDD-DpIS7hxD.js +1 -0
- package/web/assets/{chunk-67TQ5CYL-CChC2cAv.js → chunk-67TQ5CYL-4sNPJU1q.js} +3 -3
- package/web/assets/{chunk-7W6UQGC5-CgFyfZ6k.js → chunk-7W6UQGC5-KyEG0HQg.js} +1 -1
- package/web/assets/{chunk-AZZRMDJM-5E9ARdDQ.js → chunk-AZZRMDJM-y-xJBuMz.js} +1 -1
- package/web/assets/{chunk-INKRHTLW-ELpfuk9h.js → chunk-INKRHTLW-Nu4ri9P-.js} +1 -1
- package/web/assets/{chunk-JQRUD6KW-Dcq_Qnyy.js → chunk-JQRUD6KW-Dbva2Z17.js} +1 -1
- package/web/assets/{chunk-KGFNY3KK-W-VR57xb.js → chunk-KGFNY3KK-JGMJvKCK.js} +1 -1
- package/web/assets/chunk-KGYTTC2M-CWC_c3H9.js +161 -0
- package/web/assets/{chunk-KNLZD3CH-BuZBOcso.js → chunk-KNLZD3CH-BGlP6X9R.js} +1 -1
- package/web/assets/{chunk-KRXBNO2N-DH2NPM7I.js → chunk-KRXBNO2N-BW5o0KUN.js} +1 -1
- package/web/assets/{chunk-LCXTWHL2-Dfmux4m1.js → chunk-LCXTWHL2-zoomRtWX.js} +1 -1
- package/web/assets/{chunk-LII3EMHJ-3X33tCpU.js → chunk-LII3EMHJ-D-jm-dLa.js} +1 -1
- package/web/assets/{chunk-QA3QBVWF-D5Ha3GgB.js → chunk-QA3QBVWF-BgRW3SXd.js} +1 -1
- package/web/assets/{chunk-RG4AUYOV-CAkzcoRj.js → chunk-RG4AUYOV-DD0B2N2B.js} +1 -1
- package/web/assets/{chunk-T2UQINTJ-kGYG7ppP.js → chunk-T2UQINTJ-DSBqkzH7.js} +1 -1
- package/web/assets/chunk-T5OCTHI4-DdZvN-9i.js +1 -0
- package/web/assets/{chunk-UY5QBCOK-Du-c56gs.js → chunk-UY5QBCOK-DyF0vyGd.js} +1 -1
- package/web/assets/{chunk-W44A43WB-lrCpuupA.js → chunk-W44A43WB-JqMLIpR0.js} +1 -1
- package/web/assets/{chunk-ZXARS5L4-CSZRiyOf.js → chunk-ZXARS5L4-CxN8oiwI.js} +1 -1
- package/web/assets/classDiagram-KGZ6W3CR-DJm9BgF-.js +1 -0
- package/web/assets/classDiagram-v2-72OJOZXJ-DJm9BgF-.js +1 -0
- package/web/assets/{context-builder-_HS0v2ma.js → context-builder-BHiFUA8O.js} +10 -2
- package/web/assets/{cose-bilkent-UX7MHV2Q-BHv2bbtl.js → cose-bilkent-UX7MHV2Q-D71wNYRJ.js} +1 -1
- package/web/assets/dagre-ND4H6XIP-BIkxmhcu.js +4 -0
- package/web/assets/diagram-3NCE3AQN-BTa6iH0T.js +43 -0
- package/web/assets/diagram-GF46GFSD-DZDV0vBj.js +24 -0
- package/web/assets/{diagram-HNR7UZ2L-fUWreZzx.js → diagram-HNR7UZ2L-DFCfd5LI.js} +2 -2
- package/web/assets/diagram-QXG6HAR7-BzM--Jj0.js +24 -0
- package/web/assets/diagram-WEQXMOUZ-DFFwz0k4.js +10 -0
- package/web/assets/{erDiagram-L5TCEMPS-QN2eEP1e.js → erDiagram-L5TCEMPS-CfHMJJqo.js} +1 -1
- package/web/assets/eventmodeling-FCH6USID-MREXMVOE-BQm9QzEa.js +1 -0
- package/web/assets/{flowDiagram-H6V6AXG4-CXbXImlN.js → flowDiagram-H6V6AXG4-Be805QiJ.js} +3 -3
- package/web/assets/{ganttDiagram-JCBTUEKG-BHQssTz6.js → ganttDiagram-JCBTUEKG-D3iL2Aet.js} +1 -1
- package/web/assets/gitGraph-WXDBUCRP-R675I2BI-Cr_Bm2Nb.js +1 -0
- package/web/assets/gitGraphDiagram-S2ZK5IYY-CPIDgxGm.js +106 -0
- package/web/assets/index-BvYev1UJ.js +635 -0
- package/web/assets/index-_lgn7hs5.css +2 -0
- package/web/assets/info-J43DQDTF-KCYPFFUO-Cs4p2oyk.js +1 -0
- package/web/assets/infoDiagram-3YFTVSEB-Y7BKSoke.js +2 -0
- package/web/assets/{ishikawaDiagram-BNXS4ZKH-6LKOvBfp.js → ishikawaDiagram-BNXS4ZKH-CT4SHFuI.js} +1 -1
- package/web/assets/{journeyDiagram-M6C3CM3L-DNPYCpvq.js → journeyDiagram-M6C3CM3L-CvArszLo.js} +1 -1
- package/web/assets/{kanban-definition-75IXJCU3-DgDi9oJT.js → kanban-definition-75IXJCU3-C6yDxr7N.js} +3 -3
- package/web/assets/{katex-K3KEBU37-CrVLz8l7.js → katex-K3KEBU37-CTc5BslQ.js} +1 -1
- package/web/assets/{mindmap-definition-2TDM6QVE-CwR5sBB-.js → mindmap-definition-2TDM6QVE-BOc2WEne.js} +1 -1
- package/web/assets/packet-YPE3B663-LP52Z2RK-CKCrztD2.js +1 -0
- package/web/assets/pie-LRSECV5Y-TCRJHUBD-BB7pHqoD.js +1 -0
- package/web/assets/{pieDiagram-CU6KROY3-By8g6f6B.js → pieDiagram-CU6KROY3-Cvc0f_Pd.js} +1 -1
- package/web/assets/{quadrantDiagram-VICAPDV7-bAm6Hi_5.js → quadrantDiagram-VICAPDV7-yXSZ2lLz.js} +1 -1
- package/web/assets/radar-GUYGQ44K-RDLRG3WG-DhoTOcuK.js +1 -0
- package/web/assets/{requirementDiagram-JXO7QTGE-7oDcJ1_J.js → requirementDiagram-JXO7QTGE-BRcZvHs4.js} +1 -1
- package/web/assets/{sankeyDiagram-URQDO5SZ-LFJz53u5.js → sankeyDiagram-URQDO5SZ-1qEjlFIB.js} +1 -1
- package/web/assets/{sequenceDiagram-VS2MUI6T-FySeKCUy.js → sequenceDiagram-VS2MUI6T-PcSCuvrB.js} +3 -3
- package/web/assets/stateDiagram-7D4R322I-D6qYSV1E.js +1 -0
- package/web/assets/stateDiagram-v2-36443NZ5-Bg-ImOSX.js +1 -0
- package/web/assets/{timeline-definition-O6YCAMPW-Ds2CnVZK.js → timeline-definition-O6YCAMPW-QWkh01xw.js} +1 -1
- package/web/assets/treeView-BLDUP644-QA4HXRO3-ODehyGKL.js +1 -0
- package/web/assets/treemap-LRROVOQU-LLAWBHMP-BfpgC-7o.js +1 -0
- package/web/assets/{vennDiagram-MWXL3ELB-DUIEwXWp.js → vennDiagram-MWXL3ELB-ak4DfwuM.js} +3 -3
- package/web/assets/wardley-L42UT6IY-5TKZOOLJ-CNeL6VHE.js +1 -0
- package/web/assets/{wardleyDiagram-CUQ6CDDI-DEiFPQih.js → wardleyDiagram-CUQ6CDDI-CTzR4Loq.js} +1 -1
- package/web/assets/{xychartDiagram-N2JHSOCM-BUbayhST.js → xychartDiagram-N2JHSOCM-DA1BEaQz.js} +1 -1
- package/web/index.html +13 -15
- package/scripts/materialize-vendor-grammars.cjs +0 -97
- package/web/assets/architecture-7EHR7CIX-6QZW5X65-BETj2x4g.js +0 -1
- package/web/assets/architectureDiagram-UL44E2DR-D1EXI0zA.js +0 -36
- package/web/assets/chunk-2T2R6R2M-n6s9JZqv.js +0 -4
- package/web/assets/chunk-2UTLFMKG-CMBB1TMN.js +0 -1
- package/web/assets/chunk-4R4BOZG6-mKhL59ul.js +0 -159
- package/web/assets/chunk-6764PJDD-ChwMM2z4.js +0 -1
- package/web/assets/chunk-7J6CGLKN-DT-b53FT.js +0 -10
- package/web/assets/chunk-C62D2QBJ-CDAWj26E.js +0 -1
- package/web/assets/chunk-CEXFNPSA-D68Tk6ls.js +0 -1
- package/web/assets/chunk-CilyBKbf.js +0 -1
- package/web/assets/chunk-J5EP6P6S-DxWW0yvu.js +0 -1
- package/web/assets/chunk-KGYTTC2M-C6PHeuay.js +0 -1
- package/web/assets/chunk-RERM46MO-DQNbXtfw.js +0 -1
- package/web/assets/chunk-RKZBBQEN-BmTPLSyv.js +0 -1
- package/web/assets/chunk-RLI5ZMPA-DExu2DOK.js +0 -1
- package/web/assets/chunk-T5OCTHI4--9wWpVws.js +0 -1
- package/web/assets/chunk-UP6H54XL-DsKdC6jC.js +0 -1
- package/web/assets/chunk-UXSXWOXI-DR81EqLr.js +0 -1
- package/web/assets/classDiagram-KGZ6W3CR-Bv93af_b.js +0 -1
- package/web/assets/classDiagram-v2-72OJOZXJ-CLfEqHUa.js +0 -1
- package/web/assets/dagre-ND4H6XIP-DsIr9MOv.js +0 -4
- package/web/assets/diagram-3NCE3AQN-BqAtKUpW.js +0 -43
- package/web/assets/diagram-GF46GFSD-CaBG6n6o.js +0 -24
- package/web/assets/diagram-QXG6HAR7-CZ-O3rcV.js +0 -24
- package/web/assets/diagram-WEQXMOUZ-P4lSL4GH.js +0 -10
- package/web/assets/eventmodeling-FCH6USID-MREXMVOE-B3myHUst.js +0 -1
- package/web/assets/gitGraph-WXDBUCRP-R675I2BI-DF3rtf4A.js +0 -1
- package/web/assets/gitGraphDiagram-S2ZK5IYY-CCwVLT__.js +0 -106
- package/web/assets/index-BKWA-m7o.css +0 -2
- package/web/assets/index-CG6q8eTs.js +0 -626
- package/web/assets/info-J43DQDTF-KCYPFFUO-R4XV_akJ.js +0 -1
- package/web/assets/infoDiagram-3YFTVSEB-DmLICZx1.js +0 -2
- package/web/assets/packet-YPE3B663-LP52Z2RK-CgQPdNZa.js +0 -1
- package/web/assets/pie-LRSECV5Y-TCRJHUBD-BzWOwc_c.js +0 -1
- package/web/assets/radar-GUYGQ44K-RDLRG3WG-B7KG0FN7.js +0 -1
- package/web/assets/stateDiagram-7D4R322I-CB2nABwH.js +0 -1
- package/web/assets/stateDiagram-v2-36443NZ5-COBGd2RL.js +0 -1
- package/web/assets/treeView-BLDUP644-QA4HXRO3-CRSxEwLO.js +0 -1
- package/web/assets/treemap-LRROVOQU-LLAWBHMP-a0PfKoVn.js +0 -1
- package/web/assets/wardley-L42UT6IY-5TKZOOLJ-BKQ6e5mC.js +0 -1
- /package/web/assets/{chunk-AQ6EADP3-CCFje6lL.js → chunk-AQ6EADP3-CZhslHi-.js} +0 -0
|
@@ -0,0 +1,638 @@
|
|
|
1
|
+
import { CfgBuilder } from '../cfg-builder.js';
|
|
2
|
+
import { ControlFlowContext, drainFinalizerPending, wireJumpThroughFinalizers, } from '../control-flow-context.js';
|
|
3
|
+
import { GoHarvester } from './go-harvest.js';
|
|
4
|
+
/** Go node types that own a CFG-bearing function body. */
|
|
5
|
+
const GO_FUNCTION_TYPES = new Set(['function_declaration', 'method_declaration', 'func_literal']);
|
|
6
|
+
/** Statement node types that break a basic block (everything else coalesces). */
|
|
7
|
+
const CONTROL_FLOW_TYPES = new Set([
|
|
8
|
+
'if_statement',
|
|
9
|
+
'for_statement',
|
|
10
|
+
'expression_switch_statement',
|
|
11
|
+
'type_switch_statement',
|
|
12
|
+
'select_statement',
|
|
13
|
+
'return_statement',
|
|
14
|
+
'break_statement',
|
|
15
|
+
'continue_statement',
|
|
16
|
+
'goto_statement',
|
|
17
|
+
'labeled_statement',
|
|
18
|
+
'fallthrough_statement',
|
|
19
|
+
'defer_statement',
|
|
20
|
+
'block',
|
|
21
|
+
]);
|
|
22
|
+
const startLineOf = (n) => n.startPosition.row + 1;
|
|
23
|
+
const endLineOf = (n) => n.endPosition.row + 1;
|
|
24
|
+
/**
|
|
25
|
+
* Per-function Go walk state. One instance per function so the
|
|
26
|
+
* {@link ControlFlowContext}, the `defer` finalizer chain, and the label tables
|
|
27
|
+
* are scoped to that function and never leak across functions.
|
|
28
|
+
*/
|
|
29
|
+
class GoCfgWalk {
|
|
30
|
+
builder;
|
|
31
|
+
harvest;
|
|
32
|
+
cfc = new ControlFlowContext();
|
|
33
|
+
/** label name → its `labeled_statement` body's entry block (resolved on demand). */
|
|
34
|
+
labelBlocks = new Map();
|
|
35
|
+
/** Pending gotos to a label not yet seen: label → list of source blocks. */
|
|
36
|
+
pendingGotos = new Map();
|
|
37
|
+
/** Label(s) pending attachment to the NEXT pushed loop/switch/select frame. */
|
|
38
|
+
pendingLabels = [];
|
|
39
|
+
/**
|
|
40
|
+
* Active `defer` finalizer frames in source (push) order. Innermost-LIFO is the
|
|
41
|
+
* REVERSE of this list — `finalizersForReturn()` already yields innermost-first,
|
|
42
|
+
* matching Go's LIFO defer execution. Frames stay active for the whole function
|
|
43
|
+
* tail and are drained once at the top-level walk's end.
|
|
44
|
+
*/
|
|
45
|
+
deferFrames = [];
|
|
46
|
+
constructor(builder, harvest) {
|
|
47
|
+
this.builder = builder;
|
|
48
|
+
this.harvest = harvest;
|
|
49
|
+
}
|
|
50
|
+
/** Statements of a block node, ignoring comments. */
|
|
51
|
+
statementsOf(block) {
|
|
52
|
+
return block.namedChildren.filter((c) => c.type !== 'comment');
|
|
53
|
+
}
|
|
54
|
+
/** The `body` block of a node. */
|
|
55
|
+
bodyBlockOf(node) {
|
|
56
|
+
return node.childForFieldName('body') ?? node.namedChildren.find((c) => c.type === 'block');
|
|
57
|
+
}
|
|
58
|
+
/** Visit a body that may be a `block` or a single statement. */
|
|
59
|
+
visitBody(node) {
|
|
60
|
+
return this.builder.withNesting(() => {
|
|
61
|
+
if (!node)
|
|
62
|
+
return null;
|
|
63
|
+
if (node.type === 'block')
|
|
64
|
+
return this.visitSeq(this.statementsOf(node));
|
|
65
|
+
return this.visitStmt(node);
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
/** Wire a sequence of statements, coalescing straight-line runs into blocks. */
|
|
69
|
+
visitSeq(stmts) {
|
|
70
|
+
return this.builder.withNesting(() => {
|
|
71
|
+
let entry;
|
|
72
|
+
let dangling = [];
|
|
73
|
+
let openSimple;
|
|
74
|
+
for (const stmt of stmts) {
|
|
75
|
+
if (CONTROL_FLOW_TYPES.has(stmt.type)) {
|
|
76
|
+
openSimple = undefined; // close any open straight-line block
|
|
77
|
+
const res = this.visitStmt(stmt);
|
|
78
|
+
if (res === null)
|
|
79
|
+
continue; // transparent (empty nested block)
|
|
80
|
+
if (entry === undefined)
|
|
81
|
+
entry = res.entry;
|
|
82
|
+
else
|
|
83
|
+
this.builder.connect(dangling, res.entry, 'seq');
|
|
84
|
+
dangling = [...res.exits];
|
|
85
|
+
}
|
|
86
|
+
else {
|
|
87
|
+
if (openSimple === undefined) {
|
|
88
|
+
const idx = this.builder.newBlock(startLineOf(stmt), endLineOf(stmt), stmt.text, 'normal', this.harvest.facts(stmt));
|
|
89
|
+
if (entry === undefined)
|
|
90
|
+
entry = idx;
|
|
91
|
+
else
|
|
92
|
+
this.builder.connect(dangling, idx, 'seq');
|
|
93
|
+
openSimple = idx;
|
|
94
|
+
dangling = [idx];
|
|
95
|
+
}
|
|
96
|
+
else {
|
|
97
|
+
this.builder.extendBlock(openSimple, endLineOf(stmt), stmt.text, this.harvest.facts(stmt));
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
if (entry === undefined)
|
|
102
|
+
return null;
|
|
103
|
+
return { entry, exits: dangling };
|
|
104
|
+
});
|
|
105
|
+
}
|
|
106
|
+
/** Dispatch one statement to its handler. Non-null except for empty blocks. */
|
|
107
|
+
visitStmt(stmt) {
|
|
108
|
+
switch (stmt.type) {
|
|
109
|
+
case 'if_statement':
|
|
110
|
+
return this.visitIf(stmt);
|
|
111
|
+
case 'for_statement':
|
|
112
|
+
return this.visitFor(stmt);
|
|
113
|
+
case 'expression_switch_statement':
|
|
114
|
+
return this.visitExprSwitch(stmt);
|
|
115
|
+
case 'type_switch_statement':
|
|
116
|
+
return this.visitTypeSwitch(stmt);
|
|
117
|
+
case 'select_statement':
|
|
118
|
+
return this.visitSelect(stmt);
|
|
119
|
+
case 'return_statement':
|
|
120
|
+
return this.visitReturn(stmt);
|
|
121
|
+
case 'break_statement':
|
|
122
|
+
return this.visitBreak(stmt);
|
|
123
|
+
case 'continue_statement':
|
|
124
|
+
return this.visitContinue(stmt);
|
|
125
|
+
case 'goto_statement':
|
|
126
|
+
return this.visitGoto(stmt);
|
|
127
|
+
case 'labeled_statement':
|
|
128
|
+
return this.visitLabeled(stmt);
|
|
129
|
+
case 'fallthrough_statement':
|
|
130
|
+
return this.visitFallthrough(stmt);
|
|
131
|
+
case 'defer_statement':
|
|
132
|
+
return this.visitDefer(stmt);
|
|
133
|
+
case 'block':
|
|
134
|
+
return this.visitSeq(this.statementsOf(stmt));
|
|
135
|
+
default:
|
|
136
|
+
return this.visitSimple(stmt);
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
visitSimple(stmt) {
|
|
140
|
+
const idx = this.builder.newBlock(startLineOf(stmt), endLineOf(stmt), stmt.text, 'normal', this.harvest.facts(stmt));
|
|
141
|
+
return { entry: idx, exits: [idx] };
|
|
142
|
+
}
|
|
143
|
+
/**
|
|
144
|
+
* `return [expr…]` — threads through EVERY active `defer` (innermost-first =
|
|
145
|
+
* LIFO) before EXIT. `finalizersForReturn()` yields the active finalizer frames
|
|
146
|
+
* innermost-first, which is exactly Go's defer execution order.
|
|
147
|
+
*/
|
|
148
|
+
visitReturn(stmt) {
|
|
149
|
+
const idx = this.builder.newBlock(startLineOf(stmt), endLineOf(stmt), stmt.text, 'normal', this.harvest.facts(stmt));
|
|
150
|
+
wireJumpThroughFinalizers(this.builder, idx, this.cfc.finalizersForReturn(), this.builder.exitIndex, 'return');
|
|
151
|
+
return { entry: idx, exits: [] };
|
|
152
|
+
}
|
|
153
|
+
visitBreak(stmt) {
|
|
154
|
+
const idx = this.builder.newBlock(startLineOf(stmt), endLineOf(stmt), stmt.text);
|
|
155
|
+
const label = this.jumpLabel(stmt);
|
|
156
|
+
const res = this.cfc.resolveBreak(label);
|
|
157
|
+
const { target, finalizers } = res ?? {
|
|
158
|
+
target: this.builder.exitIndex,
|
|
159
|
+
finalizers: this.cfc.finalizersForReturn(),
|
|
160
|
+
};
|
|
161
|
+
wireJumpThroughFinalizers(this.builder, idx, finalizers, target, 'break');
|
|
162
|
+
return { entry: idx, exits: [] };
|
|
163
|
+
}
|
|
164
|
+
visitContinue(stmt) {
|
|
165
|
+
const idx = this.builder.newBlock(startLineOf(stmt), endLineOf(stmt), stmt.text);
|
|
166
|
+
const label = this.jumpLabel(stmt);
|
|
167
|
+
const res = this.cfc.resolveContinue(label);
|
|
168
|
+
const { target, finalizers } = res ?? {
|
|
169
|
+
target: this.builder.exitIndex,
|
|
170
|
+
finalizers: this.cfc.finalizersForReturn(),
|
|
171
|
+
};
|
|
172
|
+
wireJumpThroughFinalizers(this.builder, idx, finalizers, target, 'continue');
|
|
173
|
+
return { entry: idx, exits: [] };
|
|
174
|
+
}
|
|
175
|
+
/** The trailing `label_name` of a `break`/`continue`, if any. */
|
|
176
|
+
jumpLabel(stmt) {
|
|
177
|
+
const id = stmt.namedChildren.find((c) => c.type === 'label_name');
|
|
178
|
+
return id?.text;
|
|
179
|
+
}
|
|
180
|
+
/** `goto label;` — route to the label block if known, else defer / EXIT. */
|
|
181
|
+
visitGoto(stmt) {
|
|
182
|
+
const idx = this.builder.newBlock(startLineOf(stmt), endLineOf(stmt), stmt.text);
|
|
183
|
+
const label = stmt.namedChildren.find((c) => c.type === 'label_name')?.text;
|
|
184
|
+
if (label === undefined) {
|
|
185
|
+
this.builder.edge(idx, this.builder.exitIndex, 'seq'); // malformed — single-exit
|
|
186
|
+
return { entry: idx, exits: [] };
|
|
187
|
+
}
|
|
188
|
+
const target = this.labelBlocks.get(label);
|
|
189
|
+
if (target !== undefined) {
|
|
190
|
+
this.builder.edge(idx, target, 'seq'); // backward goto: label already built
|
|
191
|
+
}
|
|
192
|
+
else {
|
|
193
|
+
const list = this.pendingGotos.get(label);
|
|
194
|
+
if (list)
|
|
195
|
+
list.push(idx);
|
|
196
|
+
else
|
|
197
|
+
this.pendingGotos.set(label, [idx]);
|
|
198
|
+
}
|
|
199
|
+
return { entry: idx, exits: [] };
|
|
200
|
+
}
|
|
201
|
+
/**
|
|
202
|
+
* `label: <statement>` — the label names the construct it directly wraps. For a
|
|
203
|
+
* loop / switch / select we forward the label so its pushed frame carries it
|
|
204
|
+
* (`break outer;` then resolves to it); for any other labeled statement we
|
|
205
|
+
* register the label block so a `goto label` reaches it.
|
|
206
|
+
*/
|
|
207
|
+
visitLabeled(stmt) {
|
|
208
|
+
const labelNode = stmt.childForFieldName('label');
|
|
209
|
+
const label = labelNode?.text;
|
|
210
|
+
const body = stmt.namedChildren.find((c) => c.id !== labelNode?.id && c.type !== 'comment') ?? null;
|
|
211
|
+
if (label !== undefined && body && this.isBreakableStatement(body)) {
|
|
212
|
+
// Forward the label to the loop/switch/select frame this statement pushes.
|
|
213
|
+
this.pendingLabels = [...this.pendingLabels, label];
|
|
214
|
+
const res = this.visitStmt(body);
|
|
215
|
+
this.registerLabel(label, res?.entry ?? this.synthLabelBlock(stmt));
|
|
216
|
+
return res ?? { entry: this.labelBlocks.get(label), exits: [] };
|
|
217
|
+
}
|
|
218
|
+
const res = this.visitBody(body);
|
|
219
|
+
if (label !== undefined) {
|
|
220
|
+
const entry = res?.entry ?? this.synthLabelBlock(stmt);
|
|
221
|
+
this.registerLabel(label, entry);
|
|
222
|
+
if (!res)
|
|
223
|
+
return { entry, exits: [entry] };
|
|
224
|
+
}
|
|
225
|
+
return res;
|
|
226
|
+
}
|
|
227
|
+
synthLabelBlock(stmt) {
|
|
228
|
+
return this.builder.newBlock(endLineOf(stmt), endLineOf(stmt), '');
|
|
229
|
+
}
|
|
230
|
+
/** Register a resolved label block and wire any forward gotos that waited on it. */
|
|
231
|
+
registerLabel(label, entry) {
|
|
232
|
+
this.labelBlocks.set(label, entry);
|
|
233
|
+
const pending = this.pendingGotos.get(label);
|
|
234
|
+
if (pending) {
|
|
235
|
+
for (const from of pending)
|
|
236
|
+
this.builder.edge(from, entry, 'seq');
|
|
237
|
+
this.pendingGotos.delete(label);
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
isBreakableStatement(node) {
|
|
241
|
+
return (node.type === 'for_statement' ||
|
|
242
|
+
node.type === 'expression_switch_statement' ||
|
|
243
|
+
node.type === 'type_switch_statement' ||
|
|
244
|
+
node.type === 'select_statement');
|
|
245
|
+
}
|
|
246
|
+
/** Take and clear the labels queued by an enclosing `labeled_statement`. */
|
|
247
|
+
takeLabels() {
|
|
248
|
+
const labels = this.pendingLabels;
|
|
249
|
+
this.pendingLabels = [];
|
|
250
|
+
return labels;
|
|
251
|
+
}
|
|
252
|
+
/**
|
|
253
|
+
* `fallthrough` — EXPLICIT transfer to the next case body (Go cases do not fall
|
|
254
|
+
* through implicitly). Recorded as a marker block; the enclosing switch wires
|
|
255
|
+
* the `fallthrough` edge from a case body whose dangling exit is this block.
|
|
256
|
+
* It carries no normal exit (control leaves to the next case).
|
|
257
|
+
*/
|
|
258
|
+
visitFallthrough(stmt) {
|
|
259
|
+
const idx = this.builder.newBlock(startLineOf(stmt), endLineOf(stmt), stmt.text);
|
|
260
|
+
this.fallthroughBlocks.add(idx);
|
|
261
|
+
return { entry: idx, exits: [idx] };
|
|
262
|
+
}
|
|
263
|
+
/** Blocks that are an explicit `fallthrough` terminator (per-function). */
|
|
264
|
+
fallthroughBlocks = new Set();
|
|
265
|
+
/**
|
|
266
|
+
* `defer f()` — register the deferred call as a finalizer frame that stays
|
|
267
|
+
* active for the rest of the function tail. Every later `return` (and the
|
|
268
|
+
* normal fall-off) threads through it; LIFO across multiple defers falls out of
|
|
269
|
+
* `finalizersForReturn()` yielding innermost-first. The defer's call expression
|
|
270
|
+
* carries its def/use facts. The frame is NOT popped here — the top-level walk
|
|
271
|
+
* drains all defer frames once at function end.
|
|
272
|
+
*/
|
|
273
|
+
visitDefer(stmt) {
|
|
274
|
+
// A single facts-only block is created for the deferred call body; it is the
|
|
275
|
+
// finalizer entry that completion legs route through.
|
|
276
|
+
const call = stmt.namedChildren.find((c) => c.type !== 'comment') ?? stmt;
|
|
277
|
+
const deferBlock = this.builder.newBlock(startLineOf(stmt), endLineOf(stmt), stmt.text, 'normal', this.harvest.facts(call));
|
|
278
|
+
const frame = this.cfc.pushFinalizer(deferBlock);
|
|
279
|
+
this.deferFrames.push(frame);
|
|
280
|
+
// The `defer` statement itself is a no-op at its source position — control
|
|
281
|
+
// falls straight through to the next statement (the deferred body only runs
|
|
282
|
+
// at function exit, modeled by the completion-leg threading). We therefore
|
|
283
|
+
// return a SEPARATE marker block as the in-line position so the deferred
|
|
284
|
+
// block stays out of the straight-line flow.
|
|
285
|
+
const marker = this.builder.newBlock(startLineOf(stmt), startLineOf(stmt), '');
|
|
286
|
+
return { entry: marker, exits: [marker] };
|
|
287
|
+
}
|
|
288
|
+
/**
|
|
289
|
+
* Drain the active `defer` chain at function end. Each deferred block is a
|
|
290
|
+
* single block, so its "exit" is itself; the chain runs innermost-first (LIFO,
|
|
291
|
+
* matching Go). Returns the block set control reaches AFTER the whole defer
|
|
292
|
+
* chain runs (to be wired to EXIT by the caller), or `normalExits` unchanged
|
|
293
|
+
* when there are no defers.
|
|
294
|
+
*
|
|
295
|
+
* Two completion sources converge here:
|
|
296
|
+
* - `return` statements that crossed these frames registered pending legs via
|
|
297
|
+
* `wireJumpThroughFinalizers`; {@link drainFinalizerPending} wires them
|
|
298
|
+
* (return → defer[0]; defer[i] → defer[i+1]; defer[last] → EXIT).
|
|
299
|
+
* - the function's NORMAL fall-off is threaded explicitly here through the
|
|
300
|
+
* same chain (`return` first leg, `finally-return` inter-defer legs); the
|
|
301
|
+
* builder de-dups, so legs shared with the return paths collapse.
|
|
302
|
+
*/
|
|
303
|
+
finishDefers(normalExits) {
|
|
304
|
+
if (this.deferFrames.length === 0)
|
|
305
|
+
return normalExits;
|
|
306
|
+
// Innermost-first (LIFO): the LAST-registered defer runs first.
|
|
307
|
+
const lifo = [...this.deferFrames].reverse();
|
|
308
|
+
// Pop the frames off the active stack and drain any pending completion legs
|
|
309
|
+
// the return/break handlers registered while these frames were active.
|
|
310
|
+
for (let i = 0; i < lifo.length; i++)
|
|
311
|
+
this.cfc.pop();
|
|
312
|
+
for (const frame of lifo)
|
|
313
|
+
drainFinalizerPending(this.builder, frame, [frame.entry]);
|
|
314
|
+
// Thread the normal fall-off through the chain innermost-first: the first leg
|
|
315
|
+
// keeps the bare `return` kind (the "kind ⟹ source terminator" invariant), the
|
|
316
|
+
// inter-defer legs are `finally-return` completion edges.
|
|
317
|
+
if (normalExits.length > 0) {
|
|
318
|
+
this.builder.connect(normalExits, lifo[0].entry, 'return');
|
|
319
|
+
for (let i = 0; i + 1 < lifo.length; i++) {
|
|
320
|
+
this.builder.edge(lifo[i].entry, lifo[i + 1].entry, 'finally-return');
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
// After the outermost defer runs, control reaches EXIT.
|
|
324
|
+
return [lifo[lifo.length - 1].entry];
|
|
325
|
+
}
|
|
326
|
+
/**
|
|
327
|
+
* Route any forward `goto`s whose label never appeared in the function to EXIT
|
|
328
|
+
* (single-exit preserved) and log them so a dropped jump is never silent (R4).
|
|
329
|
+
* Called once after the body walk.
|
|
330
|
+
*/
|
|
331
|
+
flushGotos(builder) {
|
|
332
|
+
for (const [label, froms] of this.pendingGotos) {
|
|
333
|
+
// eslint-disable-next-line no-console
|
|
334
|
+
console.warn(`[cfg] Go: unresolved goto label "${label}" routed to EXIT (${froms.length} site(s))`);
|
|
335
|
+
for (const from of froms)
|
|
336
|
+
builder.edge(from, builder.exitIndex, 'seq');
|
|
337
|
+
}
|
|
338
|
+
this.pendingGotos.clear();
|
|
339
|
+
}
|
|
340
|
+
visitIf(stmt) {
|
|
341
|
+
const cond = stmt.childForFieldName('condition') ?? stmt;
|
|
342
|
+
const init = stmt.childForFieldName('initializer');
|
|
343
|
+
// The header block carries the (optional) initializer's facts AND the
|
|
344
|
+
// condition's facts — both evaluate before the branch.
|
|
345
|
+
const header = this.builder.newBlock(init ? startLineOf(init) : startLineOf(stmt), endLineOf(cond), init ? `${init.text}; ${cond.text}` : cond.text, 'normal', this.harvest.facts(cond));
|
|
346
|
+
if (init)
|
|
347
|
+
this.builder.attachFacts(header, this.harvest.facts(init));
|
|
348
|
+
const exits = [];
|
|
349
|
+
const thenRes = this.visitBody(stmt.childForFieldName('consequence'));
|
|
350
|
+
if (thenRes) {
|
|
351
|
+
this.builder.edge(header, thenRes.entry, 'cond-true');
|
|
352
|
+
exits.push(...thenRes.exits);
|
|
353
|
+
}
|
|
354
|
+
else {
|
|
355
|
+
exits.push(header); // empty then — true path falls through
|
|
356
|
+
}
|
|
357
|
+
// No `else_clause` wrapper in Go: `alternative` is the else `block` or the
|
|
358
|
+
// nested `if_statement` of an `else if` chain directly.
|
|
359
|
+
const elseNode = stmt.childForFieldName('alternative');
|
|
360
|
+
if (elseNode) {
|
|
361
|
+
const elseRes = this.visitBody(elseNode);
|
|
362
|
+
if (elseRes) {
|
|
363
|
+
this.builder.edge(header, elseRes.entry, 'cond-false');
|
|
364
|
+
exits.push(...elseRes.exits);
|
|
365
|
+
}
|
|
366
|
+
else {
|
|
367
|
+
exits.push(header);
|
|
368
|
+
}
|
|
369
|
+
}
|
|
370
|
+
else {
|
|
371
|
+
exits.push(header); // no else — false path falls through to the join
|
|
372
|
+
}
|
|
373
|
+
return { entry: header, exits: [...new Set(exits)] };
|
|
374
|
+
}
|
|
375
|
+
/**
|
|
376
|
+
* `for_statement` — Go's single loop keyword covers all four shapes:
|
|
377
|
+
* 1. `for clause { }` (`for_clause`: init?/cond?/update? — C-style)
|
|
378
|
+
* 2. `for range x { }` (`range_clause`)
|
|
379
|
+
* 3. `for cond { }` (a bare condition expression — while-style)
|
|
380
|
+
* 4. `for { }` (no header child — infinite)
|
|
381
|
+
*/
|
|
382
|
+
visitFor(stmt) {
|
|
383
|
+
const labels = this.takeLabels();
|
|
384
|
+
const head = this.forHeadChild(stmt);
|
|
385
|
+
if (head?.type === 'for_clause')
|
|
386
|
+
return this.visitForClause(stmt, head, labels);
|
|
387
|
+
if (head?.type === 'range_clause')
|
|
388
|
+
return this.visitForRange(stmt, head, labels);
|
|
389
|
+
// While-style (bare condition) or infinite (`for {}`): `head` is the
|
|
390
|
+
// condition expression (or undefined).
|
|
391
|
+
return this.visitForCond(stmt, head ?? undefined, labels);
|
|
392
|
+
}
|
|
393
|
+
/** The header child of a `for_statement` (for_clause / range_clause / cond), or undefined. */
|
|
394
|
+
forHeadChild(stmt) {
|
|
395
|
+
const body = stmt.childForFieldName('body');
|
|
396
|
+
return stmt.namedChildren.find((c) => c.id !== body?.id && c.type !== 'comment');
|
|
397
|
+
}
|
|
398
|
+
visitForClause(stmt, clause, labels) {
|
|
399
|
+
const init = clause.childForFieldName('initializer');
|
|
400
|
+
const cond = clause.childForFieldName('condition');
|
|
401
|
+
const incr = clause.childForFieldName('update');
|
|
402
|
+
const header = this.builder.newBlock(cond ? startLineOf(cond) : startLineOf(stmt), cond ? endLineOf(cond) : startLineOf(stmt), cond ? cond.text : 'for{}', 'normal', cond ? this.harvest.facts(cond) : undefined);
|
|
403
|
+
const loopExit = this.builder.newBlock(endLineOf(stmt), endLineOf(stmt), '');
|
|
404
|
+
let incrBlock = header;
|
|
405
|
+
if (incr) {
|
|
406
|
+
incrBlock = this.builder.newBlock(startLineOf(incr), endLineOf(incr), incr.text, 'normal', this.harvest.facts(incr));
|
|
407
|
+
this.builder.edge(incrBlock, header, 'loop-back');
|
|
408
|
+
}
|
|
409
|
+
this.cfc.pushLoop(incrBlock, loopExit, labels);
|
|
410
|
+
const body = this.visitBody(this.bodyBlockOf(stmt));
|
|
411
|
+
this.cfc.pop();
|
|
412
|
+
if (body) {
|
|
413
|
+
this.builder.edge(header, body.entry, 'cond-true');
|
|
414
|
+
this.builder.connect(body.exits, incrBlock, incr ? 'seq' : 'loop-back');
|
|
415
|
+
}
|
|
416
|
+
else {
|
|
417
|
+
this.builder.edge(header, incrBlock, 'cond-true');
|
|
418
|
+
if (!incr)
|
|
419
|
+
this.builder.edge(header, header, 'loop-back');
|
|
420
|
+
}
|
|
421
|
+
// Structural exit edge — `for ;; {}` (no condition) still keeps EXIT
|
|
422
|
+
// reverse-reachable so CDG is not silently skipped for the function.
|
|
423
|
+
this.builder.edge(header, loopExit, 'cond-false');
|
|
424
|
+
let entry = header;
|
|
425
|
+
if (init) {
|
|
426
|
+
const initBlock = this.builder.newBlock(startLineOf(init), endLineOf(init), init.text, 'normal', this.harvest.facts(init));
|
|
427
|
+
this.builder.edge(initBlock, header, 'seq');
|
|
428
|
+
entry = initBlock;
|
|
429
|
+
}
|
|
430
|
+
return { entry, exits: [loopExit] };
|
|
431
|
+
}
|
|
432
|
+
visitForRange(stmt, clause, labels) {
|
|
433
|
+
// Header carries the range head facts (loop vars are defs, the iterated
|
|
434
|
+
// expression is a use).
|
|
435
|
+
const header = this.builder.newBlock(startLineOf(stmt), endLineOf(clause), clause.text, 'normal', this.harvest.rangeHeadFacts(clause));
|
|
436
|
+
const loopExit = this.builder.newBlock(endLineOf(stmt), endLineOf(stmt), '');
|
|
437
|
+
this.cfc.pushLoop(header, loopExit, labels);
|
|
438
|
+
const body = this.visitBody(this.bodyBlockOf(stmt));
|
|
439
|
+
this.cfc.pop();
|
|
440
|
+
if (body) {
|
|
441
|
+
this.builder.edge(header, body.entry, 'cond-true');
|
|
442
|
+
this.builder.connect(body.exits, header, 'loop-back');
|
|
443
|
+
}
|
|
444
|
+
else {
|
|
445
|
+
this.builder.edge(header, header, 'loop-back');
|
|
446
|
+
}
|
|
447
|
+
this.builder.edge(header, loopExit, 'cond-false');
|
|
448
|
+
return { entry: header, exits: [loopExit] };
|
|
449
|
+
}
|
|
450
|
+
visitForCond(stmt, cond, labels) {
|
|
451
|
+
const header = this.builder.newBlock(cond ? startLineOf(cond) : startLineOf(stmt), cond ? endLineOf(cond) : startLineOf(stmt), cond ? cond.text : 'for{}', 'normal', cond ? this.harvest.facts(cond) : undefined);
|
|
452
|
+
const loopExit = this.builder.newBlock(endLineOf(stmt), endLineOf(stmt), '');
|
|
453
|
+
this.cfc.pushLoop(header, loopExit, labels);
|
|
454
|
+
const body = this.visitBody(this.bodyBlockOf(stmt));
|
|
455
|
+
this.cfc.pop();
|
|
456
|
+
if (body) {
|
|
457
|
+
this.builder.edge(header, body.entry, 'cond-true');
|
|
458
|
+
this.builder.connect(body.exits, header, 'loop-back');
|
|
459
|
+
}
|
|
460
|
+
else {
|
|
461
|
+
this.builder.edge(header, header, 'loop-back'); // empty `for {}` body re-tests
|
|
462
|
+
}
|
|
463
|
+
// Always emit the structural exit edge — even `for {}` (infinite, no
|
|
464
|
+
// condition) and `for cond {}` keep EXIT reverse-reachable for the CDG pass.
|
|
465
|
+
this.builder.edge(header, loopExit, 'cond-false');
|
|
466
|
+
return { entry: header, exits: [loopExit] };
|
|
467
|
+
}
|
|
468
|
+
visitExprSwitch(stmt) {
|
|
469
|
+
const labels = this.takeLabels();
|
|
470
|
+
const value = stmt.childForFieldName('value');
|
|
471
|
+
const init = stmt.childForFieldName('initializer');
|
|
472
|
+
const dispatch = this.builder.newBlock(startLineOf(stmt), value ? endLineOf(value) : startLineOf(stmt), value ? value.text : 'switch{}', 'normal', value ? this.harvest.facts(value) : undefined);
|
|
473
|
+
if (init)
|
|
474
|
+
this.builder.attachFacts(dispatch, this.harvest.facts(init));
|
|
475
|
+
const switchExit = this.builder.newBlock(endLineOf(stmt), endLineOf(stmt), '');
|
|
476
|
+
this.cfc.pushSwitch(switchExit, labels);
|
|
477
|
+
const cases = stmt.namedChildren.filter((c) => c.type === 'expression_case' || c.type === 'default_case');
|
|
478
|
+
// Each `expression_case`'s test value(s) evaluate before the body — harvest
|
|
479
|
+
// their uses CONDITIONALLY onto the dispatch block (a later case only tests
|
|
480
|
+
// when earlier cases didn't match).
|
|
481
|
+
for (const c of cases) {
|
|
482
|
+
if (c.type !== 'expression_case')
|
|
483
|
+
continue;
|
|
484
|
+
const test = c.childForFieldName('value');
|
|
485
|
+
if (test)
|
|
486
|
+
this.builder.attachFacts(dispatch, this.harvest.factsConditional(test));
|
|
487
|
+
}
|
|
488
|
+
const result = this.buildCases(dispatch, switchExit, cases, (c) => this.exprCaseBody(c));
|
|
489
|
+
this.cfc.pop();
|
|
490
|
+
return result;
|
|
491
|
+
}
|
|
492
|
+
visitTypeSwitch(stmt) {
|
|
493
|
+
const labels = this.takeLabels();
|
|
494
|
+
const dispatch = this.builder.newBlock(startLineOf(stmt), startLineOf(stmt), this.typeSwitchHeaderText(stmt), 'normal', this.harvest.typeSwitchHeadFacts(stmt));
|
|
495
|
+
const switchExit = this.builder.newBlock(endLineOf(stmt), endLineOf(stmt), '');
|
|
496
|
+
this.cfc.pushSwitch(switchExit, labels);
|
|
497
|
+
const cases = stmt.namedChildren.filter((c) => c.type === 'type_case' || c.type === 'default_case');
|
|
498
|
+
const result = this.buildCases(dispatch, switchExit, cases, (c) => this.typeCaseBody(c));
|
|
499
|
+
this.cfc.pop();
|
|
500
|
+
return result;
|
|
501
|
+
}
|
|
502
|
+
visitSelect(stmt) {
|
|
503
|
+
const labels = this.takeLabels();
|
|
504
|
+
const dispatch = this.builder.newBlock(startLineOf(stmt), startLineOf(stmt), 'select');
|
|
505
|
+
const selectExit = this.builder.newBlock(endLineOf(stmt), endLineOf(stmt), '');
|
|
506
|
+
this.cfc.pushSwitch(selectExit, labels);
|
|
507
|
+
const cases = stmt.namedChildren.filter((c) => c.type === 'communication_case' || c.type === 'default_case');
|
|
508
|
+
// A comm case's communication clause (`v := <-ch` / `ch <- x`) evaluates as
|
|
509
|
+
// part of dispatch — harvest its facts onto the dispatch block.
|
|
510
|
+
for (const c of cases) {
|
|
511
|
+
if (c.type !== 'communication_case')
|
|
512
|
+
continue;
|
|
513
|
+
const comm = c.childForFieldName('communication');
|
|
514
|
+
if (comm)
|
|
515
|
+
this.builder.attachFacts(dispatch, this.harvest.facts(comm));
|
|
516
|
+
}
|
|
517
|
+
const hasDefault = cases.some((c) => c.type === 'default_case');
|
|
518
|
+
const result = this.buildCases(dispatch, selectExit, cases, (c) => this.commCaseBody(c));
|
|
519
|
+
// A `select` with NO default BLOCKS until a case is ready — and a `select {}`
|
|
520
|
+
// with no cases at all blocks forever. Either way EXIT must stay
|
|
521
|
+
// reverse-reachable: emit a structural escape edge dispatch → selectExit so
|
|
522
|
+
// the CDG pass is not silently skipped for the function.
|
|
523
|
+
if (!hasDefault)
|
|
524
|
+
this.builder.edge(dispatch, selectExit, 'switch-case');
|
|
525
|
+
this.cfc.pop();
|
|
526
|
+
return result;
|
|
527
|
+
}
|
|
528
|
+
/**
|
|
529
|
+
* Shared dispatch builder for expression-switch / type-switch / select. Cases
|
|
530
|
+
* do NOT fall through by default (the opposite of C); an EXPLICIT
|
|
531
|
+
* `fallthrough` block in a case body spills into the NEXT case instead of the
|
|
532
|
+
* switch exit. `default_case` is the no-match target; without one, the dispatch
|
|
533
|
+
* also reaches the switch exit directly (no-match path).
|
|
534
|
+
*/
|
|
535
|
+
buildCases(dispatch, switchExit, cases, bodyOf) {
|
|
536
|
+
const caseResults = cases.map((c) => this.visitSeq(bodyOf(c)));
|
|
537
|
+
const hasDefault = cases.some((c) => c.type === 'default_case');
|
|
538
|
+
const entryOf = new Array(cases.length);
|
|
539
|
+
let after = switchExit;
|
|
540
|
+
for (let i = cases.length - 1; i >= 0; i--) {
|
|
541
|
+
entryOf[i] = caseResults[i]?.entry ?? after;
|
|
542
|
+
after = entryOf[i];
|
|
543
|
+
}
|
|
544
|
+
for (let i = 0; i < cases.length; i++) {
|
|
545
|
+
this.builder.edge(dispatch, entryOf[i], 'switch-case');
|
|
546
|
+
}
|
|
547
|
+
if (!hasDefault)
|
|
548
|
+
this.builder.edge(dispatch, switchExit, 'switch-case'); // no-match path
|
|
549
|
+
// Case bodies rejoin AFTER the switch (no implicit fallthrough), UNLESS the
|
|
550
|
+
// body ends in an explicit `fallthrough`, which spills into the next case.
|
|
551
|
+
for (let i = 0; i < cases.length; i++) {
|
|
552
|
+
const res = caseResults[i];
|
|
553
|
+
if (!res)
|
|
554
|
+
continue;
|
|
555
|
+
const fallTarget = i + 1 < cases.length ? entryOf[i + 1] : switchExit;
|
|
556
|
+
for (const ex of res.exits) {
|
|
557
|
+
if (this.fallthroughBlocks.has(ex)) {
|
|
558
|
+
this.builder.edge(ex, fallTarget, 'fallthrough');
|
|
559
|
+
}
|
|
560
|
+
else {
|
|
561
|
+
this.builder.edge(ex, switchExit, 'seq');
|
|
562
|
+
}
|
|
563
|
+
}
|
|
564
|
+
}
|
|
565
|
+
return { entry: dispatch, exits: [switchExit] };
|
|
566
|
+
}
|
|
567
|
+
/** `expression_case` body statements (everything but the `value` test). */
|
|
568
|
+
exprCaseBody(caseNode) {
|
|
569
|
+
const value = caseNode.childForFieldName('value');
|
|
570
|
+
return caseNode.namedChildren.filter((c) => c.id !== value?.id && c.type !== 'comment');
|
|
571
|
+
}
|
|
572
|
+
/** `type_case` body statements (everything but the `type` field children). */
|
|
573
|
+
typeCaseBody(caseNode) {
|
|
574
|
+
const typeIds = new Set();
|
|
575
|
+
// All `type` field children (a type_case may list several types).
|
|
576
|
+
for (let i = 0; i < caseNode.childCount; i++) {
|
|
577
|
+
if (caseNode.fieldNameForChild(i) === 'type') {
|
|
578
|
+
const c = caseNode.child(i);
|
|
579
|
+
if (c)
|
|
580
|
+
typeIds.add(c.id);
|
|
581
|
+
}
|
|
582
|
+
}
|
|
583
|
+
return caseNode.namedChildren.filter((c) => !typeIds.has(c.id) && c.type !== 'comment');
|
|
584
|
+
}
|
|
585
|
+
/** `communication_case` body statements (everything but the `communication` clause). */
|
|
586
|
+
commCaseBody(caseNode) {
|
|
587
|
+
const comm = caseNode.childForFieldName('communication');
|
|
588
|
+
return caseNode.namedChildren.filter((c) => c.id !== comm?.id && c.type !== 'comment');
|
|
589
|
+
}
|
|
590
|
+
typeSwitchHeaderText(stmt) {
|
|
591
|
+
const alias = stmt.childForFieldName('alias')?.text;
|
|
592
|
+
const value = stmt.childForFieldName('value')?.text ?? '';
|
|
593
|
+
return alias ? `switch ${alias} := ${value}.(type)` : `switch ${value}.(type)`;
|
|
594
|
+
}
|
|
595
|
+
}
|
|
596
|
+
/** Build the CFG for one Go function node, or `undefined` if not modelable. */
|
|
597
|
+
function buildFunctionCfg(fnNode, filePath) {
|
|
598
|
+
try {
|
|
599
|
+
if (!GO_FUNCTION_TYPES.has(fnNode.type))
|
|
600
|
+
return undefined;
|
|
601
|
+
const startLine = startLineOf(fnNode);
|
|
602
|
+
const endLine = endLineOf(fnNode);
|
|
603
|
+
const startColumn = fnNode.startPosition.column;
|
|
604
|
+
const body = fnNode.childForFieldName('body');
|
|
605
|
+
if (!body || body.type !== 'block')
|
|
606
|
+
return undefined; // forward decl / interface method
|
|
607
|
+
const builder = new CfgBuilder(filePath, startLine, endLine, startColumn);
|
|
608
|
+
const harvest = new GoHarvester(fnNode);
|
|
609
|
+
const paramFacts = harvest.paramFacts();
|
|
610
|
+
if (paramFacts)
|
|
611
|
+
builder.attachFacts(builder.entryIndex, paramFacts);
|
|
612
|
+
const walk = new GoCfgWalk(builder, harvest);
|
|
613
|
+
const res = walk.visitSeq(body.namedChildren.filter((c) => c.type !== 'comment'));
|
|
614
|
+
builder.edge(builder.entryIndex, res ? res.entry : builder.exitIndex, 'seq');
|
|
615
|
+
// Normal fall-off threads through the active `defer` chain (LIFO) → EXIT.
|
|
616
|
+
const normalExits = res ? res.exits : [builder.entryIndex];
|
|
617
|
+
const afterDefers = walk.finishDefers(normalExits);
|
|
618
|
+
builder.connect(afterDefers, builder.exitIndex, 'seq');
|
|
619
|
+
walk.flushGotos(builder);
|
|
620
|
+
return builder.finish(harvest.bindingTable());
|
|
621
|
+
}
|
|
622
|
+
catch (err) {
|
|
623
|
+
// Never throw out of buildFunctionCfg — a malformed AST shape must skip only
|
|
624
|
+
// this one function's CFG, never drop the whole file's language group (R4).
|
|
625
|
+
// eslint-disable-next-line no-console
|
|
626
|
+
console.warn(`[cfg] Go buildFunctionCfg skipped a function in ${filePath}: ${String(err)}`);
|
|
627
|
+
return undefined;
|
|
628
|
+
}
|
|
629
|
+
}
|
|
630
|
+
/** Whether a node is a Go function this visitor builds a CFG for. */
|
|
631
|
+
function isFunction(node) {
|
|
632
|
+
return GO_FUNCTION_TYPES.has(node.type);
|
|
633
|
+
}
|
|
634
|
+
/** The Go CFG visitor. */
|
|
635
|
+
export function createGoCfgVisitor() {
|
|
636
|
+
return { buildFunctionCfg, isFunction };
|
|
637
|
+
}
|
|
638
|
+
export { GO_FUNCTION_TYPES };
|