codegraph-java 0.1.0
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 +131 -0
- package/dist/base-graph/index.d.ts +57 -0
- package/dist/base-graph/index.d.ts.map +1 -0
- package/dist/base-graph/index.js +175 -0
- package/dist/base-graph/index.js.map +1 -0
- package/dist/bin/codegraph.d.ts +28 -0
- package/dist/bin/codegraph.d.ts.map +1 -0
- package/dist/bin/codegraph.js +2615 -0
- package/dist/bin/codegraph.js.map +1 -0
- package/dist/bin/command-supervision.d.ts +49 -0
- package/dist/bin/command-supervision.d.ts.map +1 -0
- package/dist/bin/command-supervision.js +95 -0
- package/dist/bin/command-supervision.js.map +1 -0
- package/dist/bin/fatal-handler.d.ts +20 -0
- package/dist/bin/fatal-handler.d.ts.map +1 -0
- package/dist/bin/fatal-handler.js +118 -0
- package/dist/bin/fatal-handler.js.map +1 -0
- package/dist/bin/node-version-check.d.ts +45 -0
- package/dist/bin/node-version-check.d.ts.map +1 -0
- package/dist/bin/node-version-check.js +98 -0
- package/dist/bin/node-version-check.js.map +1 -0
- package/dist/bin/uninstall.d.ts +14 -0
- package/dist/bin/uninstall.d.ts.map +1 -0
- package/dist/bin/uninstall.js +36 -0
- package/dist/bin/uninstall.js.map +1 -0
- package/dist/context/formatter.d.ts +36 -0
- package/dist/context/formatter.d.ts.map +1 -0
- package/dist/context/formatter.js +269 -0
- package/dist/context/formatter.js.map +1 -0
- package/dist/context/index.d.ts +131 -0
- package/dist/context/index.d.ts.map +1 -0
- package/dist/context/index.js +1378 -0
- package/dist/context/index.js.map +1 -0
- package/dist/context/markers.d.ts +19 -0
- package/dist/context/markers.d.ts.map +1 -0
- package/dist/context/markers.js +22 -0
- package/dist/context/markers.js.map +1 -0
- package/dist/db/index.d.ts +380 -0
- package/dist/db/index.d.ts.map +1 -0
- package/dist/db/index.js +854 -0
- package/dist/db/index.js.map +1 -0
- package/dist/db/migrations.d.ts +44 -0
- package/dist/db/migrations.d.ts.map +1 -0
- package/dist/db/migrations.js +255 -0
- package/dist/db/migrations.js.map +1 -0
- package/dist/db/queries.d.ts +1060 -0
- package/dist/db/queries.d.ts.map +1 -0
- package/dist/db/queries.js +3191 -0
- package/dist/db/queries.js.map +1 -0
- package/dist/db/schema.sql +209 -0
- package/dist/db/sqlite-adapter.d.ts +55 -0
- package/dist/db/sqlite-adapter.d.ts.map +1 -0
- package/dist/db/sqlite-adapter.js +135 -0
- package/dist/db/sqlite-adapter.js.map +1 -0
- package/dist/db/wal-valve.d.ts +119 -0
- package/dist/db/wal-valve.d.ts.map +1 -0
- package/dist/db/wal-valve.js +290 -0
- package/dist/db/wal-valve.js.map +1 -0
- package/dist/directory.d.ts +213 -0
- package/dist/directory.d.ts.map +1 -0
- package/dist/directory.js +781 -0
- package/dist/directory.js.map +1 -0
- package/dist/errors.d.ts +150 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/errors.js +234 -0
- package/dist/errors.js.map +1 -0
- package/dist/extraction/extraction-version.d.ts +25 -0
- package/dist/extraction/extraction-version.d.ts.map +1 -0
- package/dist/extraction/extraction-version.js +28 -0
- package/dist/extraction/extraction-version.js.map +1 -0
- package/dist/extraction/function-ref.d.ts +118 -0
- package/dist/extraction/function-ref.d.ts.map +1 -0
- package/dist/extraction/function-ref.js +746 -0
- package/dist/extraction/function-ref.js.map +1 -0
- package/dist/extraction/generated-detection.d.ts +66 -0
- package/dist/extraction/generated-detection.d.ts.map +1 -0
- package/dist/extraction/generated-detection.js +248 -0
- package/dist/extraction/generated-detection.js.map +1 -0
- package/dist/extraction/grammars.d.ts +148 -0
- package/dist/extraction/grammars.d.ts.map +1 -0
- package/dist/extraction/grammars.js +558 -0
- package/dist/extraction/grammars.js.map +1 -0
- package/dist/extraction/index.d.ts +367 -0
- package/dist/extraction/index.d.ts.map +1 -0
- package/dist/extraction/index.js +2833 -0
- package/dist/extraction/index.js.map +1 -0
- package/dist/extraction/kernel/decode.d.ts +9 -0
- package/dist/extraction/kernel/decode.d.ts.map +1 -0
- package/dist/extraction/kernel/decode.js +162 -0
- package/dist/extraction/kernel/decode.js.map +1 -0
- package/dist/extraction/kernel/index.d.ts +64 -0
- package/dist/extraction/kernel/index.d.ts.map +1 -0
- package/dist/extraction/kernel/index.js +204 -0
- package/dist/extraction/kernel/index.js.map +1 -0
- package/dist/extraction/kernel/layout.d.ts +101 -0
- package/dist/extraction/kernel/layout.d.ts.map +1 -0
- package/dist/extraction/kernel/layout.js +104 -0
- package/dist/extraction/kernel/layout.js.map +1 -0
- package/dist/extraction/kernel/loader.d.ts +103 -0
- package/dist/extraction/kernel/loader.d.ts.map +1 -0
- package/dist/extraction/kernel/loader.js +146 -0
- package/dist/extraction/kernel/loader.js.map +1 -0
- package/dist/extraction/languages/c-cpp.d.ts +124 -0
- package/dist/extraction/languages/c-cpp.d.ts.map +1 -0
- package/dist/extraction/languages/c-cpp.js +1689 -0
- package/dist/extraction/languages/c-cpp.js.map +1 -0
- package/dist/extraction/languages/index.d.ts +11 -0
- package/dist/extraction/languages/index.d.ts.map +1 -0
- package/dist/extraction/languages/index.js +15 -0
- package/dist/extraction/languages/index.js.map +1 -0
- package/dist/extraction/languages/java.d.ts +3 -0
- package/dist/extraction/languages/java.d.ts.map +1 -0
- package/dist/extraction/languages/java.js +315 -0
- package/dist/extraction/languages/java.js.map +1 -0
- package/dist/extraction/languages/rust.d.ts +23 -0
- package/dist/extraction/languages/rust.d.ts.map +1 -0
- package/dist/extraction/languages/rust.js +169 -0
- package/dist/extraction/languages/rust.js.map +1 -0
- package/dist/extraction/parse-pool.d.ts +167 -0
- package/dist/extraction/parse-pool.d.ts.map +1 -0
- package/dist/extraction/parse-pool.js +421 -0
- package/dist/extraction/parse-pool.js.map +1 -0
- package/dist/extraction/parse-worker.d.ts +8 -0
- package/dist/extraction/parse-worker.d.ts.map +1 -0
- package/dist/extraction/parse-worker.js +138 -0
- package/dist/extraction/parse-worker.js.map +1 -0
- package/dist/extraction/store-worker.d.ts +24 -0
- package/dist/extraction/store-worker.d.ts.map +1 -0
- package/dist/extraction/store-worker.js +139 -0
- package/dist/extraction/store-worker.js.map +1 -0
- package/dist/extraction/store-writer.d.ts +63 -0
- package/dist/extraction/store-writer.d.ts.map +1 -0
- package/dist/extraction/store-writer.js +174 -0
- package/dist/extraction/store-writer.js.map +1 -0
- package/dist/extraction/syntax-tokens.d.ts +103 -0
- package/dist/extraction/syntax-tokens.d.ts.map +1 -0
- package/dist/extraction/syntax-tokens.js +381 -0
- package/dist/extraction/syntax-tokens.js.map +1 -0
- package/dist/extraction/tree-sitter-helpers.d.ts +28 -0
- package/dist/extraction/tree-sitter-helpers.d.ts.map +1 -0
- package/dist/extraction/tree-sitter-helpers.js +153 -0
- package/dist/extraction/tree-sitter-helpers.js.map +1 -0
- package/dist/extraction/tree-sitter-types.d.ts +260 -0
- package/dist/extraction/tree-sitter-types.d.ts.map +1 -0
- package/dist/extraction/tree-sitter-types.js +10 -0
- package/dist/extraction/tree-sitter-types.js.map +1 -0
- package/dist/extraction/tree-sitter.d.ts +745 -0
- package/dist/extraction/tree-sitter.d.ts.map +1 -0
- package/dist/extraction/tree-sitter.js +6725 -0
- package/dist/extraction/tree-sitter.js.map +1 -0
- package/dist/extraction/wasm/tree-sitter-java.wasm +0 -0
- package/dist/extraction/wasm-runtime-flags.d.ts +53 -0
- package/dist/extraction/wasm-runtime-flags.d.ts.map +1 -0
- package/dist/extraction/wasm-runtime-flags.js +130 -0
- package/dist/extraction/wasm-runtime-flags.js.map +1 -0
- package/dist/graph/branch-guards.d.ts +248 -0
- package/dist/graph/branch-guards.d.ts.map +1 -0
- package/dist/graph/branch-guards.js +2231 -0
- package/dist/graph/branch-guards.js.map +1 -0
- package/dist/graph/dead-code.d.ts +251 -0
- package/dist/graph/dead-code.d.ts.map +1 -0
- package/dist/graph/dead-code.js +756 -0
- package/dist/graph/dead-code.js.map +1 -0
- package/dist/graph/dynamic-boundary-report.d.ts +121 -0
- package/dist/graph/dynamic-boundary-report.d.ts.map +1 -0
- package/dist/graph/dynamic-boundary-report.js +283 -0
- package/dist/graph/dynamic-boundary-report.js.map +1 -0
- package/dist/graph/index.d.ts +12 -0
- package/dist/graph/index.d.ts.map +1 -0
- package/dist/graph/index.js +28 -0
- package/dist/graph/index.js.map +1 -0
- package/dist/graph/named-symbol-flow.d.ts +153 -0
- package/dist/graph/named-symbol-flow.d.ts.map +1 -0
- package/dist/graph/named-symbol-flow.js +609 -0
- package/dist/graph/named-symbol-flow.js.map +1 -0
- package/dist/graph/queries.d.ts +106 -0
- package/dist/graph/queries.d.ts.map +1 -0
- package/dist/graph/queries.js +341 -0
- package/dist/graph/queries.js.map +1 -0
- package/dist/graph/traversal.d.ts +127 -0
- package/dist/graph/traversal.d.ts.map +1 -0
- package/dist/graph/traversal.js +599 -0
- package/dist/graph/traversal.js.map +1 -0
- package/dist/graph/type-hierarchy.d.ts +155 -0
- package/dist/graph/type-hierarchy.d.ts.map +1 -0
- package/dist/graph/type-hierarchy.js +382 -0
- package/dist/graph/type-hierarchy.js.map +1 -0
- package/dist/index.d.ts +886 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +2019 -0
- package/dist/index.js.map +1 -0
- package/dist/installer/beta-signup.d.ts +54 -0
- package/dist/installer/beta-signup.d.ts.map +1 -0
- package/dist/installer/beta-signup.js +178 -0
- package/dist/installer/beta-signup.js.map +1 -0
- package/dist/installer/config-writer.d.ts +28 -0
- package/dist/installer/config-writer.d.ts.map +1 -0
- package/dist/installer/config-writer.js +91 -0
- package/dist/installer/config-writer.js.map +1 -0
- package/dist/installer/index.d.ts +143 -0
- package/dist/installer/index.d.ts.map +1 -0
- package/dist/installer/index.js +632 -0
- package/dist/installer/index.js.map +1 -0
- package/dist/installer/instructions-template.d.ts +41 -0
- package/dist/installer/instructions-template.d.ts.map +1 -0
- package/dist/installer/instructions-template.js +53 -0
- package/dist/installer/instructions-template.js.map +1 -0
- package/dist/installer/targets/antigravity.d.ts +57 -0
- package/dist/installer/targets/antigravity.d.ts.map +1 -0
- package/dist/installer/targets/antigravity.js +308 -0
- package/dist/installer/targets/antigravity.js.map +1 -0
- package/dist/installer/targets/claude.d.ts +62 -0
- package/dist/installer/targets/claude.d.ts.map +1 -0
- package/dist/installer/targets/claude.js +486 -0
- package/dist/installer/targets/claude.js.map +1 -0
- package/dist/installer/targets/codex.d.ts +32 -0
- package/dist/installer/targets/codex.d.ts.map +1 -0
- package/dist/installer/targets/codex.js +202 -0
- package/dist/installer/targets/codex.js.map +1 -0
- package/dist/installer/targets/copilot-cli.d.ts +32 -0
- package/dist/installer/targets/copilot-cli.d.ts.map +1 -0
- package/dist/installer/targets/copilot-cli.js +209 -0
- package/dist/installer/targets/copilot-cli.js.map +1 -0
- package/dist/installer/targets/copilot-jetbrains.d.ts +42 -0
- package/dist/installer/targets/copilot-jetbrains.d.ts.map +1 -0
- package/dist/installer/targets/copilot-jetbrains.js +238 -0
- package/dist/installer/targets/copilot-jetbrains.js.map +1 -0
- package/dist/installer/targets/copilot-vscode.d.ts +47 -0
- package/dist/installer/targets/copilot-vscode.d.ts.map +1 -0
- package/dist/installer/targets/copilot-vscode.js +219 -0
- package/dist/installer/targets/copilot-vscode.js.map +1 -0
- package/dist/installer/targets/cursor.d.ts +35 -0
- package/dist/installer/targets/cursor.d.ts.map +1 -0
- package/dist/installer/targets/cursor.js +254 -0
- package/dist/installer/targets/cursor.js.map +1 -0
- package/dist/installer/targets/gemini.d.ts +26 -0
- package/dist/installer/targets/gemini.d.ts.map +1 -0
- package/dist/installer/targets/gemini.js +165 -0
- package/dist/installer/targets/gemini.js.map +1 -0
- package/dist/installer/targets/hermes.d.ts +18 -0
- package/dist/installer/targets/hermes.d.ts.map +1 -0
- package/dist/installer/targets/hermes.js +359 -0
- package/dist/installer/targets/hermes.js.map +1 -0
- package/dist/installer/targets/kiro.d.ts +27 -0
- package/dist/installer/targets/kiro.d.ts.map +1 -0
- package/dist/installer/targets/kiro.js +178 -0
- package/dist/installer/targets/kiro.js.map +1 -0
- package/dist/installer/targets/opencode.d.ts +38 -0
- package/dist/installer/targets/opencode.d.ts.map +1 -0
- package/dist/installer/targets/opencode.js +288 -0
- package/dist/installer/targets/opencode.js.map +1 -0
- package/dist/installer/targets/registry.d.ts +35 -0
- package/dist/installer/targets/registry.d.ts.map +1 -0
- package/dist/installer/targets/registry.js +101 -0
- package/dist/installer/targets/registry.js.map +1 -0
- package/dist/installer/targets/shared.d.ts +101 -0
- package/dist/installer/targets/shared.d.ts.map +1 -0
- package/dist/installer/targets/shared.js +264 -0
- package/dist/installer/targets/shared.js.map +1 -0
- package/dist/installer/targets/toml.d.ts +53 -0
- package/dist/installer/targets/toml.d.ts.map +1 -0
- package/dist/installer/targets/toml.js +226 -0
- package/dist/installer/targets/toml.js.map +1 -0
- package/dist/installer/targets/trae.d.ts +30 -0
- package/dist/installer/targets/trae.d.ts.map +1 -0
- package/dist/installer/targets/trae.js +164 -0
- package/dist/installer/targets/trae.js.map +1 -0
- package/dist/installer/targets/types.d.ts +108 -0
- package/dist/installer/targets/types.d.ts.map +1 -0
- package/dist/installer/targets/types.js +16 -0
- package/dist/installer/targets/types.js.map +1 -0
- package/dist/installer/targets/workbuddy.d.ts +29 -0
- package/dist/installer/targets/workbuddy.d.ts.map +1 -0
- package/dist/installer/targets/workbuddy.js +163 -0
- package/dist/installer/targets/workbuddy.js.map +1 -0
- package/dist/mcp/daemon-manager.d.ts +42 -0
- package/dist/mcp/daemon-manager.d.ts.map +1 -0
- package/dist/mcp/daemon-manager.js +129 -0
- package/dist/mcp/daemon-manager.js.map +1 -0
- package/dist/mcp/daemon-paths.d.ts +79 -0
- package/dist/mcp/daemon-paths.d.ts.map +1 -0
- package/dist/mcp/daemon-paths.js +219 -0
- package/dist/mcp/daemon-paths.js.map +1 -0
- package/dist/mcp/daemon-registry.d.ts +57 -0
- package/dist/mcp/daemon-registry.d.ts.map +1 -0
- package/dist/mcp/daemon-registry.js +281 -0
- package/dist/mcp/daemon-registry.js.map +1 -0
- package/dist/mcp/daemon.d.ts +293 -0
- package/dist/mcp/daemon.d.ts.map +1 -0
- package/dist/mcp/daemon.js +864 -0
- package/dist/mcp/daemon.js.map +1 -0
- package/dist/mcp/dynamic-boundaries.d.ts +41 -0
- package/dist/mcp/dynamic-boundaries.d.ts.map +1 -0
- package/dist/mcp/dynamic-boundaries.js +360 -0
- package/dist/mcp/dynamic-boundaries.js.map +1 -0
- package/dist/mcp/early-ppid.d.ts +26 -0
- package/dist/mcp/early-ppid.d.ts.map +1 -0
- package/dist/mcp/early-ppid.js +29 -0
- package/dist/mcp/early-ppid.js.map +1 -0
- package/dist/mcp/engine.d.ts +125 -0
- package/dist/mcp/engine.d.ts.map +1 -0
- package/dist/mcp/engine.js +392 -0
- package/dist/mcp/engine.js.map +1 -0
- package/dist/mcp/explore-dedup.d.ts +137 -0
- package/dist/mcp/explore-dedup.d.ts.map +1 -0
- package/dist/mcp/explore-dedup.js +236 -0
- package/dist/mcp/explore-dedup.js.map +1 -0
- package/dist/mcp/explore-diagnostics.d.ts +289 -0
- package/dist/mcp/explore-diagnostics.d.ts.map +1 -0
- package/dist/mcp/explore-diagnostics.js +558 -0
- package/dist/mcp/explore-diagnostics.js.map +1 -0
- package/dist/mcp/explore-session-state.d.ts +217 -0
- package/dist/mcp/explore-session-state.d.ts.map +1 -0
- package/dist/mcp/explore-session-state.js +322 -0
- package/dist/mcp/explore-session-state.js.map +1 -0
- package/dist/mcp/index.d.ts +137 -0
- package/dist/mcp/index.d.ts.map +1 -0
- package/dist/mcp/index.js +567 -0
- package/dist/mcp/index.js.map +1 -0
- package/dist/mcp/liveness-watchdog.d.ts +35 -0
- package/dist/mcp/liveness-watchdog.d.ts.map +1 -0
- package/dist/mcp/liveness-watchdog.js +269 -0
- package/dist/mcp/liveness-watchdog.js.map +1 -0
- package/dist/mcp/ppid-watchdog.d.ts +62 -0
- package/dist/mcp/ppid-watchdog.d.ts.map +1 -0
- package/dist/mcp/ppid-watchdog.js +64 -0
- package/dist/mcp/ppid-watchdog.js.map +1 -0
- package/dist/mcp/proxy.d.ts +87 -0
- package/dist/mcp/proxy.d.ts.map +1 -0
- package/dist/mcp/proxy.js +672 -0
- package/dist/mcp/proxy.js.map +1 -0
- package/dist/mcp/query-pool.d.ts +108 -0
- package/dist/mcp/query-pool.d.ts.map +1 -0
- package/dist/mcp/query-pool.js +315 -0
- package/dist/mcp/query-pool.js.map +1 -0
- package/dist/mcp/query-worker.d.ts +24 -0
- package/dist/mcp/query-worker.d.ts.map +1 -0
- package/dist/mcp/query-worker.js +87 -0
- package/dist/mcp/query-worker.js.map +1 -0
- package/dist/mcp/server-instructions.d.ts +34 -0
- package/dist/mcp/server-instructions.d.ts.map +1 -0
- package/dist/mcp/server-instructions.js +111 -0
- package/dist/mcp/server-instructions.js.map +1 -0
- package/dist/mcp/session.d.ts +109 -0
- package/dist/mcp/session.d.ts.map +1 -0
- package/dist/mcp/session.js +380 -0
- package/dist/mcp/session.js.map +1 -0
- package/dist/mcp/startup-handshake.d.ts +44 -0
- package/dist/mcp/startup-handshake.d.ts.map +1 -0
- package/dist/mcp/startup-handshake.js +73 -0
- package/dist/mcp/startup-handshake.js.map +1 -0
- package/dist/mcp/stdin-teardown.d.ts +27 -0
- package/dist/mcp/stdin-teardown.d.ts.map +1 -0
- package/dist/mcp/stdin-teardown.js +49 -0
- package/dist/mcp/stdin-teardown.js.map +1 -0
- package/dist/mcp/tools.d.ts +877 -0
- package/dist/mcp/tools.d.ts.map +1 -0
- package/dist/mcp/tools.js +6371 -0
- package/dist/mcp/tools.js.map +1 -0
- package/dist/mcp/transport.d.ts +188 -0
- package/dist/mcp/transport.d.ts.map +1 -0
- package/dist/mcp/transport.js +377 -0
- package/dist/mcp/transport.js.map +1 -0
- package/dist/mcp/version.d.ts +19 -0
- package/dist/mcp/version.d.ts.map +1 -0
- package/dist/mcp/version.js +71 -0
- package/dist/mcp/version.js.map +1 -0
- package/dist/project-config.d.ts +158 -0
- package/dist/project-config.d.ts.map +1 -0
- package/dist/project-config.js +489 -0
- package/dist/project-config.js.map +1 -0
- package/dist/resolution/c-fnptr-synthesizer.d.ts +6 -0
- package/dist/resolution/c-fnptr-synthesizer.d.ts.map +1 -0
- package/dist/resolution/c-fnptr-synthesizer.js +1506 -0
- package/dist/resolution/c-fnptr-synthesizer.js.map +1 -0
- package/dist/resolution/callback-synthesizer.d.ts +59 -0
- package/dist/resolution/callback-synthesizer.d.ts.map +1 -0
- package/dist/resolution/callback-synthesizer.js +4066 -0
- package/dist/resolution/callback-synthesizer.js.map +1 -0
- package/dist/resolution/cooperative-yield.d.ts +40 -0
- package/dist/resolution/cooperative-yield.d.ts.map +1 -0
- package/dist/resolution/cooperative-yield.js +44 -0
- package/dist/resolution/cooperative-yield.js.map +1 -0
- package/dist/resolution/expo-router-synthesizer.d.ts +32 -0
- package/dist/resolution/expo-router-synthesizer.d.ts.map +1 -0
- package/dist/resolution/expo-router-synthesizer.js +189 -0
- package/dist/resolution/expo-router-synthesizer.js.map +1 -0
- package/dist/resolution/frameworks/astro.d.ts +9 -0
- package/dist/resolution/frameworks/astro.d.ts.map +1 -0
- package/dist/resolution/frameworks/astro.js +169 -0
- package/dist/resolution/frameworks/astro.js.map +1 -0
- package/dist/resolution/frameworks/cargo-workspace.d.ts +18 -0
- package/dist/resolution/frameworks/cargo-workspace.d.ts.map +1 -0
- package/dist/resolution/frameworks/cargo-workspace.js +225 -0
- package/dist/resolution/frameworks/cargo-workspace.js.map +1 -0
- package/dist/resolution/frameworks/cics.d.ts +20 -0
- package/dist/resolution/frameworks/cics.d.ts.map +1 -0
- package/dist/resolution/frameworks/cics.js +90 -0
- package/dist/resolution/frameworks/cics.js.map +1 -0
- package/dist/resolution/frameworks/csharp.d.ts +8 -0
- package/dist/resolution/frameworks/csharp.d.ts.map +1 -0
- package/dist/resolution/frameworks/csharp.js +335 -0
- package/dist/resolution/frameworks/csharp.js.map +1 -0
- package/dist/resolution/frameworks/drupal.d.ts +51 -0
- package/dist/resolution/frameworks/drupal.d.ts.map +1 -0
- package/dist/resolution/frameworks/drupal.js +367 -0
- package/dist/resolution/frameworks/drupal.js.map +1 -0
- package/dist/resolution/frameworks/expo-modules.d.ts +3 -0
- package/dist/resolution/frameworks/expo-modules.d.ts.map +1 -0
- package/dist/resolution/frameworks/expo-modules.js +148 -0
- package/dist/resolution/frameworks/expo-modules.js.map +1 -0
- package/dist/resolution/frameworks/expo-router.d.ts +189 -0
- package/dist/resolution/frameworks/expo-router.d.ts.map +1 -0
- package/dist/resolution/frameworks/expo-router.js +785 -0
- package/dist/resolution/frameworks/expo-router.js.map +1 -0
- package/dist/resolution/frameworks/express.d.ts +8 -0
- package/dist/resolution/frameworks/express.d.ts.map +1 -0
- package/dist/resolution/frameworks/express.js +540 -0
- package/dist/resolution/frameworks/express.js.map +1 -0
- package/dist/resolution/frameworks/fabric.d.ts +3 -0
- package/dist/resolution/frameworks/fabric.d.ts.map +1 -0
- package/dist/resolution/frameworks/fabric.js +354 -0
- package/dist/resolution/frameworks/fabric.js.map +1 -0
- package/dist/resolution/frameworks/go.d.ts +8 -0
- package/dist/resolution/frameworks/go.d.ts.map +1 -0
- package/dist/resolution/frameworks/go.js +184 -0
- package/dist/resolution/frameworks/go.js.map +1 -0
- package/dist/resolution/frameworks/goframe.d.ts +41 -0
- package/dist/resolution/frameworks/goframe.d.ts.map +1 -0
- package/dist/resolution/frameworks/goframe.js +112 -0
- package/dist/resolution/frameworks/goframe.js.map +1 -0
- package/dist/resolution/frameworks/index.d.ts +30 -0
- package/dist/resolution/frameworks/index.d.ts.map +1 -0
- package/dist/resolution/frameworks/index.js +72 -0
- package/dist/resolution/frameworks/index.js.map +1 -0
- package/dist/resolution/frameworks/java.d.ts +8 -0
- package/dist/resolution/frameworks/java.d.ts.map +1 -0
- package/dist/resolution/frameworks/java.js +517 -0
- package/dist/resolution/frameworks/java.js.map +1 -0
- package/dist/resolution/frameworks/laravel.d.ts +13 -0
- package/dist/resolution/frameworks/laravel.d.ts.map +1 -0
- package/dist/resolution/frameworks/laravel.js +257 -0
- package/dist/resolution/frameworks/laravel.js.map +1 -0
- package/dist/resolution/frameworks/nestjs.d.ts +26 -0
- package/dist/resolution/frameworks/nestjs.d.ts.map +1 -0
- package/dist/resolution/frameworks/nestjs.js +690 -0
- package/dist/resolution/frameworks/nestjs.js.map +1 -0
- package/dist/resolution/frameworks/nextjs.d.ts +76 -0
- package/dist/resolution/frameworks/nextjs.d.ts.map +1 -0
- package/dist/resolution/frameworks/nextjs.js +328 -0
- package/dist/resolution/frameworks/nextjs.js.map +1 -0
- package/dist/resolution/frameworks/object-literal.d.ts +32 -0
- package/dist/resolution/frameworks/object-literal.d.ts.map +1 -0
- package/dist/resolution/frameworks/object-literal.js +139 -0
- package/dist/resolution/frameworks/object-literal.js.map +1 -0
- package/dist/resolution/frameworks/package-deps.d.ts +14 -0
- package/dist/resolution/frameworks/package-deps.d.ts.map +1 -0
- package/dist/resolution/frameworks/package-deps.js +78 -0
- package/dist/resolution/frameworks/package-deps.js.map +1 -0
- package/dist/resolution/frameworks/play.d.ts +19 -0
- package/dist/resolution/frameworks/play.d.ts.map +1 -0
- package/dist/resolution/frameworks/play.js +111 -0
- package/dist/resolution/frameworks/play.js.map +1 -0
- package/dist/resolution/frameworks/python.d.ts +10 -0
- package/dist/resolution/frameworks/python.d.ts.map +1 -0
- package/dist/resolution/frameworks/python.js +566 -0
- package/dist/resolution/frameworks/python.js.map +1 -0
- package/dist/resolution/frameworks/react-native.d.ts +29 -0
- package/dist/resolution/frameworks/react-native.d.ts.map +1 -0
- package/dist/resolution/frameworks/react-native.js +549 -0
- package/dist/resolution/frameworks/react-native.js.map +1 -0
- package/dist/resolution/frameworks/react-router.d.ts +46 -0
- package/dist/resolution/frameworks/react-router.d.ts.map +1 -0
- package/dist/resolution/frameworks/react-router.js +187 -0
- package/dist/resolution/frameworks/react-router.js.map +1 -0
- package/dist/resolution/frameworks/react.d.ts +9 -0
- package/dist/resolution/frameworks/react.d.ts.map +1 -0
- package/dist/resolution/frameworks/react.js +257 -0
- package/dist/resolution/frameworks/react.js.map +1 -0
- package/dist/resolution/frameworks/ruby.d.ts +8 -0
- package/dist/resolution/frameworks/ruby.d.ts.map +1 -0
- package/dist/resolution/frameworks/ruby.js +302 -0
- package/dist/resolution/frameworks/ruby.js.map +1 -0
- package/dist/resolution/frameworks/rust.d.ts +8 -0
- package/dist/resolution/frameworks/rust.d.ts.map +1 -0
- package/dist/resolution/frameworks/rust.js +304 -0
- package/dist/resolution/frameworks/rust.js.map +1 -0
- package/dist/resolution/frameworks/svelte.d.ts +9 -0
- package/dist/resolution/frameworks/svelte.d.ts.map +1 -0
- package/dist/resolution/frameworks/svelte.js +258 -0
- package/dist/resolution/frameworks/svelte.js.map +1 -0
- package/dist/resolution/frameworks/sveltekit-router.d.ts +36 -0
- package/dist/resolution/frameworks/sveltekit-router.d.ts.map +1 -0
- package/dist/resolution/frameworks/sveltekit-router.js +156 -0
- package/dist/resolution/frameworks/sveltekit-router.js.map +1 -0
- package/dist/resolution/frameworks/swift-objc.d.ts +37 -0
- package/dist/resolution/frameworks/swift-objc.d.ts.map +1 -0
- package/dist/resolution/frameworks/swift-objc.js +252 -0
- package/dist/resolution/frameworks/swift-objc.js.map +1 -0
- package/dist/resolution/frameworks/swift.d.ts +10 -0
- package/dist/resolution/frameworks/swift.d.ts.map +1 -0
- package/dist/resolution/frameworks/swift.js +410 -0
- package/dist/resolution/frameworks/swift.js.map +1 -0
- package/dist/resolution/frameworks/tanstack-router.d.ts +84 -0
- package/dist/resolution/frameworks/tanstack-router.d.ts.map +1 -0
- package/dist/resolution/frameworks/tanstack-router.js +437 -0
- package/dist/resolution/frameworks/tanstack-router.js.map +1 -0
- package/dist/resolution/frameworks/terraform.d.ts +38 -0
- package/dist/resolution/frameworks/terraform.d.ts.map +1 -0
- package/dist/resolution/frameworks/terraform.js +277 -0
- package/dist/resolution/frameworks/terraform.js.map +1 -0
- package/dist/resolution/frameworks/vue-router.d.ts +82 -0
- package/dist/resolution/frameworks/vue-router.d.ts.map +1 -0
- package/dist/resolution/frameworks/vue-router.js +377 -0
- package/dist/resolution/frameworks/vue-router.js.map +1 -0
- package/dist/resolution/frameworks/vue.d.ts +9 -0
- package/dist/resolution/frameworks/vue.d.ts.map +1 -0
- package/dist/resolution/frameworks/vue.js +303 -0
- package/dist/resolution/frameworks/vue.js.map +1 -0
- package/dist/resolution/go-module.d.ts +26 -0
- package/dist/resolution/go-module.d.ts.map +1 -0
- package/dist/resolution/go-module.js +78 -0
- package/dist/resolution/go-module.js.map +1 -0
- package/dist/resolution/goframe-synthesizer.d.ts +29 -0
- package/dist/resolution/goframe-synthesizer.d.ts.map +1 -0
- package/dist/resolution/goframe-synthesizer.js +163 -0
- package/dist/resolution/goframe-synthesizer.js.map +1 -0
- package/dist/resolution/import-resolver.d.ts +84 -0
- package/dist/resolution/import-resolver.d.ts.map +1 -0
- package/dist/resolution/import-resolver.js +2240 -0
- package/dist/resolution/import-resolver.js.map +1 -0
- package/dist/resolution/index.d.ts +343 -0
- package/dist/resolution/index.d.ts.map +1 -0
- package/dist/resolution/index.js +2352 -0
- package/dist/resolution/index.js.map +1 -0
- package/dist/resolution/lru-cache.d.ts +24 -0
- package/dist/resolution/lru-cache.d.ts.map +1 -0
- package/dist/resolution/lru-cache.js +62 -0
- package/dist/resolution/lru-cache.js.map +1 -0
- package/dist/resolution/memory-budget.d.ts +48 -0
- package/dist/resolution/memory-budget.d.ts.map +1 -0
- package/dist/resolution/memory-budget.js +162 -0
- package/dist/resolution/memory-budget.js.map +1 -0
- package/dist/resolution/name-matcher.d.ts +153 -0
- package/dist/resolution/name-matcher.d.ts.map +1 -0
- package/dist/resolution/name-matcher.js +2412 -0
- package/dist/resolution/name-matcher.js.map +1 -0
- package/dist/resolution/next-router-synthesizer.d.ts +27 -0
- package/dist/resolution/next-router-synthesizer.d.ts.map +1 -0
- package/dist/resolution/next-router-synthesizer.js +113 -0
- package/dist/resolution/next-router-synthesizer.js.map +1 -0
- package/dist/resolution/path-aliases.d.ts +70 -0
- package/dist/resolution/path-aliases.d.ts.map +1 -0
- package/dist/resolution/path-aliases.js +346 -0
- package/dist/resolution/path-aliases.js.map +1 -0
- package/dist/resolution/react-router-synthesizer.d.ts +33 -0
- package/dist/resolution/react-router-synthesizer.d.ts.map +1 -0
- package/dist/resolution/react-router-synthesizer.js +126 -0
- package/dist/resolution/react-router-synthesizer.js.map +1 -0
- package/dist/resolution/resolver-pool.d.ts +106 -0
- package/dist/resolution/resolver-pool.d.ts.map +1 -0
- package/dist/resolution/resolver-pool.js +344 -0
- package/dist/resolution/resolver-pool.js.map +1 -0
- package/dist/resolution/resolver-worker.d.ts +17 -0
- package/dist/resolution/resolver-worker.d.ts.map +1 -0
- package/dist/resolution/resolver-worker.js +141 -0
- package/dist/resolution/resolver-worker.js.map +1 -0
- package/dist/resolution/strip-comments.d.ts +27 -0
- package/dist/resolution/strip-comments.d.ts.map +1 -0
- package/dist/resolution/strip-comments.js +492 -0
- package/dist/resolution/strip-comments.js.map +1 -0
- package/dist/resolution/sveltekit-synthesizer.d.ts +58 -0
- package/dist/resolution/sveltekit-synthesizer.d.ts.map +1 -0
- package/dist/resolution/sveltekit-synthesizer.js +173 -0
- package/dist/resolution/sveltekit-synthesizer.js.map +1 -0
- package/dist/resolution/swift-objc-bridge.d.ts +134 -0
- package/dist/resolution/swift-objc-bridge.d.ts.map +1 -0
- package/dist/resolution/swift-objc-bridge.js +256 -0
- package/dist/resolution/swift-objc-bridge.js.map +1 -0
- package/dist/resolution/synth-utils.d.ts +26 -0
- package/dist/resolution/synth-utils.d.ts.map +1 -0
- package/dist/resolution/synth-utils.js +75 -0
- package/dist/resolution/synth-utils.js.map +1 -0
- package/dist/resolution/tanstack-router-synthesizer.d.ts +31 -0
- package/dist/resolution/tanstack-router-synthesizer.d.ts.map +1 -0
- package/dist/resolution/tanstack-router-synthesizer.js +114 -0
- package/dist/resolution/tanstack-router-synthesizer.js.map +1 -0
- package/dist/resolution/tier-synthesizer.d.ts +48 -0
- package/dist/resolution/tier-synthesizer.d.ts.map +1 -0
- package/dist/resolution/tier-synthesizer.js +907 -0
- package/dist/resolution/tier-synthesizer.js.map +1 -0
- package/dist/resolution/types.d.ts +290 -0
- package/dist/resolution/types.d.ts.map +1 -0
- package/dist/resolution/types.js +8 -0
- package/dist/resolution/types.js.map +1 -0
- package/dist/resolution/vue-router-synthesizer.d.ts +30 -0
- package/dist/resolution/vue-router-synthesizer.d.ts.map +1 -0
- package/dist/resolution/vue-router-synthesizer.js +115 -0
- package/dist/resolution/vue-router-synthesizer.js.map +1 -0
- package/dist/resolution/workspace-packages.d.ts +58 -0
- package/dist/resolution/workspace-packages.d.ts.map +1 -0
- package/dist/resolution/workspace-packages.js +346 -0
- package/dist/resolution/workspace-packages.js.map +1 -0
- package/dist/search/identifier-segments.d.ts +70 -0
- package/dist/search/identifier-segments.d.ts.map +1 -0
- package/dist/search/identifier-segments.js +205 -0
- package/dist/search/identifier-segments.js.map +1 -0
- package/dist/search/query-parser.d.ts +57 -0
- package/dist/search/query-parser.d.ts.map +1 -0
- package/dist/search/query-parser.js +177 -0
- package/dist/search/query-parser.js.map +1 -0
- package/dist/search/query-paths.d.ts +57 -0
- package/dist/search/query-paths.d.ts.map +1 -0
- package/dist/search/query-paths.js +298 -0
- package/dist/search/query-paths.js.map +1 -0
- package/dist/search/query-utils.d.ts +102 -0
- package/dist/search/query-utils.d.ts.map +1 -0
- package/dist/search/query-utils.js +487 -0
- package/dist/search/query-utils.js.map +1 -0
- package/dist/sync/git-hooks.d.ts +45 -0
- package/dist/sync/git-hooks.d.ts.map +1 -0
- package/dist/sync/git-hooks.js +227 -0
- package/dist/sync/git-hooks.js.map +1 -0
- package/dist/sync/index.d.ts +19 -0
- package/dist/sync/index.d.ts.map +1 -0
- package/dist/sync/index.js +35 -0
- package/dist/sync/index.js.map +1 -0
- package/dist/sync/watch-policy.d.ts +48 -0
- package/dist/sync/watch-policy.d.ts.map +1 -0
- package/dist/sync/watch-policy.js +124 -0
- package/dist/sync/watch-policy.js.map +1 -0
- package/dist/sync/watcher.d.ts +401 -0
- package/dist/sync/watcher.d.ts.map +1 -0
- package/dist/sync/watcher.js +967 -0
- package/dist/sync/watcher.js.map +1 -0
- package/dist/sync/worktree.d.ts +63 -0
- package/dist/sync/worktree.d.ts.map +1 -0
- package/dist/sync/worktree.js +182 -0
- package/dist/sync/worktree.js.map +1 -0
- package/dist/telemetry/index.d.ts +143 -0
- package/dist/telemetry/index.d.ts.map +1 -0
- package/dist/telemetry/index.js +541 -0
- package/dist/telemetry/index.js.map +1 -0
- package/dist/types.d.ts +486 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +118 -0
- package/dist/types.js.map +1 -0
- package/dist/ui/color.d.ts +21 -0
- package/dist/ui/color.d.ts.map +1 -0
- package/dist/ui/color.js +42 -0
- package/dist/ui/color.js.map +1 -0
- package/dist/ui/glyphs.d.ts +67 -0
- package/dist/ui/glyphs.d.ts.map +1 -0
- package/dist/ui/glyphs.js +125 -0
- package/dist/ui/glyphs.js.map +1 -0
- package/dist/ui/shimmer-progress.d.ts +11 -0
- package/dist/ui/shimmer-progress.d.ts.map +1 -0
- package/dist/ui/shimmer-progress.js +164 -0
- package/dist/ui/shimmer-progress.js.map +1 -0
- package/dist/ui/shimmer-worker.d.ts +2 -0
- package/dist/ui/shimmer-worker.d.ts.map +1 -0
- package/dist/ui/shimmer-worker.js +126 -0
- package/dist/ui/shimmer-worker.js.map +1 -0
- package/dist/ui/types.d.ts +15 -0
- package/dist/ui/types.d.ts.map +1 -0
- package/dist/ui/types.js +3 -0
- package/dist/ui/types.js.map +1 -0
- package/dist/ui-server/api/deadcode.d.ts +84 -0
- package/dist/ui-server/api/deadcode.d.ts.map +1 -0
- package/dist/ui-server/api/deadcode.js +136 -0
- package/dist/ui-server/api/deadcode.js.map +1 -0
- package/dist/ui-server/api/effects.d.ts +87 -0
- package/dist/ui-server/api/effects.d.ts.map +1 -0
- package/dist/ui-server/api/effects.js +499 -0
- package/dist/ui-server/api/effects.js.map +1 -0
- package/dist/ui-server/api/entrypoints.d.ts +86 -0
- package/dist/ui-server/api/entrypoints.d.ts.map +1 -0
- package/dist/ui-server/api/entrypoints.js +279 -0
- package/dist/ui-server/api/entrypoints.js.map +1 -0
- package/dist/ui-server/api/events.d.ts +183 -0
- package/dist/ui-server/api/events.d.ts.map +1 -0
- package/dist/ui-server/api/events.js +430 -0
- package/dist/ui-server/api/events.js.map +1 -0
- package/dist/ui-server/api/file.d.ts +66 -0
- package/dist/ui-server/api/file.d.ts.map +1 -0
- package/dist/ui-server/api/file.js +222 -0
- package/dist/ui-server/api/file.js.map +1 -0
- package/dist/ui-server/api/filecode.d.ts +126 -0
- package/dist/ui-server/api/filecode.d.ts.map +1 -0
- package/dist/ui-server/api/filecode.js +205 -0
- package/dist/ui-server/api/filecode.js.map +1 -0
- package/dist/ui-server/api/flow.d.ts +226 -0
- package/dist/ui-server/api/flow.d.ts.map +1 -0
- package/dist/ui-server/api/flow.js +611 -0
- package/dist/ui-server/api/flow.js.map +1 -0
- package/dist/ui-server/api/hierarchy.d.ts +81 -0
- package/dist/ui-server/api/hierarchy.d.ts.map +1 -0
- package/dist/ui-server/api/hierarchy.js +92 -0
- package/dist/ui-server/api/hierarchy.js.map +1 -0
- package/dist/ui-server/api/index.d.ts +90 -0
- package/dist/ui-server/api/index.d.ts.map +1 -0
- package/dist/ui-server/api/index.js +332 -0
- package/dist/ui-server/api/index.js.map +1 -0
- package/dist/ui-server/api/map.d.ts +187 -0
- package/dist/ui-server/api/map.d.ts.map +1 -0
- package/dist/ui-server/api/map.js +498 -0
- package/dist/ui-server/api/map.js.map +1 -0
- package/dist/ui-server/api/node.d.ts +81 -0
- package/dist/ui-server/api/node.d.ts.map +1 -0
- package/dist/ui-server/api/node.js +388 -0
- package/dist/ui-server/api/node.js.map +1 -0
- package/dist/ui-server/api/nodes.d.ts +25 -0
- package/dist/ui-server/api/nodes.d.ts.map +1 -0
- package/dist/ui-server/api/nodes.js +45 -0
- package/dist/ui-server/api/nodes.js.map +1 -0
- package/dist/ui-server/api/program.d.ts +139 -0
- package/dist/ui-server/api/program.d.ts.map +1 -0
- package/dist/ui-server/api/program.js +315 -0
- package/dist/ui-server/api/program.js.map +1 -0
- package/dist/ui-server/api/respond.d.ts +76 -0
- package/dist/ui-server/api/respond.d.ts.map +1 -0
- package/dist/ui-server/api/respond.js +186 -0
- package/dist/ui-server/api/respond.js.map +1 -0
- package/dist/ui-server/api/route-roots.d.ts +39 -0
- package/dist/ui-server/api/route-roots.d.ts.map +1 -0
- package/dist/ui-server/api/route-roots.js +96 -0
- package/dist/ui-server/api/route-roots.js.map +1 -0
- package/dist/ui-server/api/routes.d.ts +59 -0
- package/dist/ui-server/api/routes.d.ts.map +1 -0
- package/dist/ui-server/api/routes.js +112 -0
- package/dist/ui-server/api/routes.js.map +1 -0
- package/dist/ui-server/api/screens.d.ts +117 -0
- package/dist/ui-server/api/screens.d.ts.map +1 -0
- package/dist/ui-server/api/screens.js +611 -0
- package/dist/ui-server/api/screens.js.map +1 -0
- package/dist/ui-server/api/search.d.ts +29 -0
- package/dist/ui-server/api/search.d.ts.map +1 -0
- package/dist/ui-server/api/search.js +232 -0
- package/dist/ui-server/api/search.js.map +1 -0
- package/dist/ui-server/api/session.d.ts +52 -0
- package/dist/ui-server/api/session.d.ts.map +1 -0
- package/dist/ui-server/api/session.js +196 -0
- package/dist/ui-server/api/session.js.map +1 -0
- package/dist/ui-server/api/source.d.ts +200 -0
- package/dist/ui-server/api/source.d.ts.map +1 -0
- package/dist/ui-server/api/source.js +419 -0
- package/dist/ui-server/api/source.js.map +1 -0
- package/dist/ui-server/api/stats.d.ts +27 -0
- package/dist/ui-server/api/stats.d.ts.map +1 -0
- package/dist/ui-server/api/stats.js +164 -0
- package/dist/ui-server/api/stats.js.map +1 -0
- package/dist/ui-server/api/steps.d.ts +274 -0
- package/dist/ui-server/api/steps.d.ts.map +1 -0
- package/dist/ui-server/api/steps.js +1446 -0
- package/dist/ui-server/api/steps.js.map +1 -0
- package/dist/ui-server/api/trail-store.d.ts +142 -0
- package/dist/ui-server/api/trail-store.d.ts.map +1 -0
- package/dist/ui-server/api/trail-store.js +323 -0
- package/dist/ui-server/api/trail-store.js.map +1 -0
- package/dist/ui-server/api/trails.d.ts +163 -0
- package/dist/ui-server/api/trails.d.ts.map +1 -0
- package/dist/ui-server/api/trails.js +369 -0
- package/dist/ui-server/api/trails.js.map +1 -0
- package/dist/ui-server/api/when.d.ts +125 -0
- package/dist/ui-server/api/when.d.ts.map +1 -0
- package/dist/ui-server/api/when.js +251 -0
- package/dist/ui-server/api/when.js.map +1 -0
- package/dist/ui-server/api/wire.d.ts +179 -0
- package/dist/ui-server/api/wire.d.ts.map +1 -0
- package/dist/ui-server/api/wire.js +231 -0
- package/dist/ui-server/api/wire.js.map +1 -0
- package/dist/ui-server/assets.d.ts +40 -0
- package/dist/ui-server/assets.d.ts.map +1 -0
- package/dist/ui-server/assets.js +110 -0
- package/dist/ui-server/assets.js.map +1 -0
- package/dist/ui-server/constants.d.ts +32 -0
- package/dist/ui-server/constants.d.ts.map +1 -0
- package/dist/ui-server/constants.js +35 -0
- package/dist/ui-server/constants.js.map +1 -0
- package/dist/ui-server/highlight/index.d.ts +113 -0
- package/dist/ui-server/highlight/index.d.ts.map +1 -0
- package/dist/ui-server/highlight/index.js +307 -0
- package/dist/ui-server/highlight/index.js.map +1 -0
- package/dist/ui-server/highlight/languages.d.ts +36 -0
- package/dist/ui-server/highlight/languages.d.ts.map +1 -0
- package/dist/ui-server/highlight/languages.js +50 -0
- package/dist/ui-server/highlight/languages.js.map +1 -0
- package/dist/ui-server/index.d.ts +92 -0
- package/dist/ui-server/index.d.ts.map +1 -0
- package/dist/ui-server/index.js +399 -0
- package/dist/ui-server/index.js.map +1 -0
- package/dist/ui-server/open-browser.d.ts +31 -0
- package/dist/ui-server/open-browser.d.ts.map +1 -0
- package/dist/ui-server/open-browser.js +79 -0
- package/dist/ui-server/open-browser.js.map +1 -0
- package/dist/ui-server/security.d.ts +127 -0
- package/dist/ui-server/security.d.ts.map +1 -0
- package/dist/ui-server/security.js +332 -0
- package/dist/ui-server/security.js.map +1 -0
- package/dist/ui-server/static.d.ts +45 -0
- package/dist/ui-server/static.d.ts.map +1 -0
- package/dist/ui-server/static.js +165 -0
- package/dist/ui-server/static.js.map +1 -0
- package/dist/upgrade/index.d.ts +177 -0
- package/dist/upgrade/index.d.ts.map +1 -0
- package/dist/upgrade/index.js +665 -0
- package/dist/upgrade/index.js.map +1 -0
- package/dist/upgrade/remove-binary.d.ts +87 -0
- package/dist/upgrade/remove-binary.d.ts.map +1 -0
- package/dist/upgrade/remove-binary.js +289 -0
- package/dist/upgrade/remove-binary.js.map +1 -0
- package/dist/upgrade/update-check.d.ts +92 -0
- package/dist/upgrade/update-check.d.ts.map +1 -0
- package/dist/upgrade/update-check.js +258 -0
- package/dist/upgrade/update-check.js.map +1 -0
- package/dist/utils.d.ts +224 -0
- package/dist/utils.d.ts.map +1 -0
- package/dist/utils.js +583 -0
- package/dist/utils.js.map +1 -0
- package/dist/viewer/assets/archivo-latin-ext-wght-normal-C4zznr8T.woff2 +0 -0
- package/dist/viewer/assets/archivo-latin-wght-normal-E0tuGl4L.woff2 +0 -0
- package/dist/viewer/assets/archivo-vietnamese-wght-normal-XAtsl5Q_.woff2 +0 -0
- package/dist/viewer/assets/ibm-plex-mono-cyrillic-400-italic-2syK4fUT.woff +0 -0
- package/dist/viewer/assets/ibm-plex-mono-cyrillic-400-italic-CBJ8pzag.woff2 +0 -0
- package/dist/viewer/assets/ibm-plex-mono-cyrillic-400-normal-BSMlKf0J.woff2 +0 -0
- package/dist/viewer/assets/ibm-plex-mono-cyrillic-400-normal-CEL4l2ZJ.woff +0 -0
- package/dist/viewer/assets/ibm-plex-mono-cyrillic-500-normal-Ael50iVv.woff +0 -0
- package/dist/viewer/assets/ibm-plex-mono-cyrillic-500-normal-Bq9vWWag.woff2 +0 -0
- package/dist/viewer/assets/ibm-plex-mono-cyrillic-600-normal-CTOM6hUh.woff2 +0 -0
- package/dist/viewer/assets/ibm-plex-mono-cyrillic-600-normal-fLZuRloM.woff +0 -0
- package/dist/viewer/assets/ibm-plex-mono-cyrillic-ext-400-italic-B4oTjJdl.woff +0 -0
- package/dist/viewer/assets/ibm-plex-mono-cyrillic-ext-400-italic-CBjNughH.woff2 +0 -0
- package/dist/viewer/assets/ibm-plex-mono-cyrillic-ext-400-normal-DMdlQ8Kv.woff +0 -0
- package/dist/viewer/assets/ibm-plex-mono-cyrillic-ext-400-normal-xuaO2J-f.woff2 +0 -0
- package/dist/viewer/assets/ibm-plex-mono-cyrillic-ext-500-normal-BIfNGwUT.woff +0 -0
- package/dist/viewer/assets/ibm-plex-mono-cyrillic-ext-500-normal-BqneJy0T.woff2 +0 -0
- package/dist/viewer/assets/ibm-plex-mono-cyrillic-ext-600-normal-9HEixskS.woff +0 -0
- package/dist/viewer/assets/ibm-plex-mono-cyrillic-ext-600-normal-V-xxqcpd.woff2 +0 -0
- package/dist/viewer/assets/ibm-plex-mono-latin-400-italic-66oory27.woff +0 -0
- package/dist/viewer/assets/ibm-plex-mono-latin-400-italic-BqAiT5Ww.woff2 +0 -0
- package/dist/viewer/assets/ibm-plex-mono-latin-400-normal-CvHOgSBP.woff +0 -0
- package/dist/viewer/assets/ibm-plex-mono-latin-400-normal-DMJ8VG8y.woff2 +0 -0
- package/dist/viewer/assets/ibm-plex-mono-latin-500-normal-CB9ihrfo.woff +0 -0
- package/dist/viewer/assets/ibm-plex-mono-latin-500-normal-DSY6xOcd.woff2 +0 -0
- package/dist/viewer/assets/ibm-plex-mono-latin-600-normal-BgSNZQsw.woff2 +0 -0
- package/dist/viewer/assets/ibm-plex-mono-latin-600-normal-DWFSQ4vo.woff +0 -0
- package/dist/viewer/assets/ibm-plex-mono-latin-ext-400-italic-B7_fu1kp.woff2 +0 -0
- package/dist/viewer/assets/ibm-plex-mono-latin-ext-400-italic-Bg0ZHwF4.woff +0 -0
- package/dist/viewer/assets/ibm-plex-mono-latin-ext-400-normal-BmRBH3aV.woff2 +0 -0
- package/dist/viewer/assets/ibm-plex-mono-latin-ext-400-normal-D3D2R8hC.woff +0 -0
- package/dist/viewer/assets/ibm-plex-mono-latin-ext-500-normal-CAhNIIs5.woff2 +0 -0
- package/dist/viewer/assets/ibm-plex-mono-latin-ext-500-normal-CZ70TYgx.woff +0 -0
- package/dist/viewer/assets/ibm-plex-mono-latin-ext-600-normal-D38SheWl.woff2 +0 -0
- package/dist/viewer/assets/ibm-plex-mono-latin-ext-600-normal-DmB0ttJJ.woff +0 -0
- package/dist/viewer/assets/ibm-plex-mono-vietnamese-400-italic-D6eaYXMU.woff +0 -0
- package/dist/viewer/assets/ibm-plex-mono-vietnamese-400-italic-DRuN92E5.woff2 +0 -0
- package/dist/viewer/assets/ibm-plex-mono-vietnamese-400-normal-BulugwFq.woff2 +0 -0
- package/dist/viewer/assets/ibm-plex-mono-vietnamese-400-normal-DDuiU_S-.woff +0 -0
- package/dist/viewer/assets/ibm-plex-mono-vietnamese-500-normal-C8zxqsMH.woff +0 -0
- package/dist/viewer/assets/ibm-plex-mono-vietnamese-500-normal-DZ4AoWbu.woff2 +0 -0
- package/dist/viewer/assets/ibm-plex-mono-vietnamese-600-normal-D2EvbN8M.woff2 +0 -0
- package/dist/viewer/assets/ibm-plex-mono-vietnamese-600-normal-iLQfcSjf.woff +0 -0
- package/dist/viewer/assets/index-2IPslW5t.js +13 -0
- package/dist/viewer/assets/index-DSwi2Seu.css +1 -0
- package/dist/viewer/index.html +16 -0
- package/package.json +67 -0
|
@@ -0,0 +1,2231 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Branch guards — the conditions under which a call site runs.
|
|
4
|
+
*
|
|
5
|
+
* An edge says `handlePress → openObjectDetail`. What a reader wants to know
|
|
6
|
+
* is that it happens **when `isCollected`** and **not while `isUploading`**:
|
|
7
|
+
*
|
|
8
|
+
* if (isUploading) return ← early-return guard: !isUploading
|
|
9
|
+
* if (isCollected) { ← if: isCollected
|
|
10
|
+
* openObjectDetail(item) ← the call site
|
|
11
|
+
*
|
|
12
|
+
* This module derives that from the AST at query time. Given a file, its
|
|
13
|
+
* language and a call site (line, column), it walks from the innermost node at
|
|
14
|
+
* that position up to the enclosing function boundary and records every
|
|
15
|
+
* branch it passes through: `if` / `else` / `else if`, the arms of a ternary,
|
|
16
|
+
* `switch` cases, the right side of `&&` / `||`, a `catch`, and — at each
|
|
17
|
+
* statement block on the way — the early exits that precede the site
|
|
18
|
+
* (`if (x) return`, Swift `guard x else { return }`).
|
|
19
|
+
*
|
|
20
|
+
* Nothing is stored in the index. The viewer and `codegraph_explore` already
|
|
21
|
+
* re-read source per request (drift checks, source windows, highlighting), the
|
|
22
|
+
* grammars are loaded in both processes, and a file parses in about a
|
|
23
|
+
* millisecond — so labels are computed where they are shown, from the source
|
|
24
|
+
* as it is now, and the index schema and the native kernel are untouched. A
|
|
25
|
+
* small LRU keeps the last few parsed trees so a Symbol view that asks about
|
|
26
|
+
* forty call sites in one file parses it once.
|
|
27
|
+
*
|
|
28
|
+
* Only what the AST states is reported. Loops are not conditions and are not
|
|
29
|
+
* listed; a condition that cannot be read (a language without rules here, a
|
|
30
|
+
* file that will not parse) yields no label rather than a wrong one.
|
|
31
|
+
*/
|
|
32
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
33
|
+
if (k2 === undefined) k2 = k;
|
|
34
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
35
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
36
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
37
|
+
}
|
|
38
|
+
Object.defineProperty(o, k2, desc);
|
|
39
|
+
}) : (function(o, m, k, k2) {
|
|
40
|
+
if (k2 === undefined) k2 = k;
|
|
41
|
+
o[k2] = m[k];
|
|
42
|
+
}));
|
|
43
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
44
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
45
|
+
}) : function(o, v) {
|
|
46
|
+
o["default"] = v;
|
|
47
|
+
});
|
|
48
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
49
|
+
var ownKeys = function(o) {
|
|
50
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
51
|
+
var ar = [];
|
|
52
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
53
|
+
return ar;
|
|
54
|
+
};
|
|
55
|
+
return ownKeys(o);
|
|
56
|
+
};
|
|
57
|
+
return function (mod) {
|
|
58
|
+
if (mod && mod.__esModule) return mod;
|
|
59
|
+
var result = {};
|
|
60
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
61
|
+
__setModuleDefault(result, mod);
|
|
62
|
+
return result;
|
|
63
|
+
};
|
|
64
|
+
})();
|
|
65
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
66
|
+
exports.BRANCH_GUARD_LANGUAGES = exports.MAX_PARSE_BYTES = void 0;
|
|
67
|
+
exports.supportsBranchGuards = supportsBranchGuards;
|
|
68
|
+
exports.guardLabel = guardLabel;
|
|
69
|
+
exports.siteKey = siteKey;
|
|
70
|
+
exports.guardsForFile = guardsForFile;
|
|
71
|
+
exports.guardsForFileSync = guardsForFileSync;
|
|
72
|
+
exports.callArgumentsForFile = callArgumentsForFile;
|
|
73
|
+
exports.callArgumentsInSource = callArgumentsInSource;
|
|
74
|
+
exports.callArgumentsInTree = callArgumentsInTree;
|
|
75
|
+
exports.callSiteInTree = callSiteInTree;
|
|
76
|
+
exports.callSitesForFile = callSitesForFile;
|
|
77
|
+
exports.callSiteInSource = callSiteInSource;
|
|
78
|
+
exports.triggersForFile = triggersForFile;
|
|
79
|
+
exports.triggerInSource = triggerInSource;
|
|
80
|
+
exports.triggerInTree = triggerInTree;
|
|
81
|
+
exports.warmBranchGuardGrammars = warmBranchGuardGrammars;
|
|
82
|
+
exports.guardsInSource = guardsInSource;
|
|
83
|
+
exports.loopsInSource = loopsInSource;
|
|
84
|
+
exports.loopsInTree = loopsInTree;
|
|
85
|
+
exports.loopsForFile = loopsForFile;
|
|
86
|
+
exports.guardsInTree = guardsInTree;
|
|
87
|
+
exports.decoratorsForFile = decoratorsForFile;
|
|
88
|
+
exports.decoratorsInSource = decoratorsInSource;
|
|
89
|
+
exports.decoratorsInTree = decoratorsInTree;
|
|
90
|
+
exports.memberTypesForFile = memberTypesForFile;
|
|
91
|
+
exports.memberTypesInSource = memberTypesInSource;
|
|
92
|
+
exports.memberTypesInTree = memberTypesInTree;
|
|
93
|
+
const fs = __importStar(require("fs"));
|
|
94
|
+
const grammars_1 = require("../extraction/grammars");
|
|
95
|
+
/** Longest condition text kept before it is cut with an ellipsis. */
|
|
96
|
+
const MAX_TEXT = 80;
|
|
97
|
+
const JS_FAMILY = new Set(['typescript', 'javascript', 'tsx', 'jsx']);
|
|
98
|
+
/** Languages with walk rules below. Others yield no guards (never a wrong one). */
|
|
99
|
+
function supportsBranchGuards(language) {
|
|
100
|
+
return !!language && RULES_BY_LANGUAGE.has(language);
|
|
101
|
+
}
|
|
102
|
+
/**
|
|
103
|
+
* The label a rail or a flow connector prints: the conditions in execution
|
|
104
|
+
* order, joined with `&&`, each negated one written as `!x`. Empty when the
|
|
105
|
+
* site is unconditional.
|
|
106
|
+
*/
|
|
107
|
+
function guardLabel(guards) {
|
|
108
|
+
return guards.map(renderGuard).join(' && ');
|
|
109
|
+
}
|
|
110
|
+
function renderGuard(g) {
|
|
111
|
+
if (g.form === 'catch')
|
|
112
|
+
return g.text;
|
|
113
|
+
// `if (!object?.id || !object?.name)` joined to the guard before it with
|
|
114
|
+
// `&&` would read as two conditions: it keeps its parentheses.
|
|
115
|
+
if (!g.negated)
|
|
116
|
+
return hasTopLevelOr(g.text) ? `(${g.text})` : g.text;
|
|
117
|
+
// `!x` negated reads back as `x`; a simple operand takes a bare `!`;
|
|
118
|
+
// anything with operators is parenthesised so the negation is unambiguous.
|
|
119
|
+
if (/^!(?![=])/.test(g.text) && isSimpleOperand(g.text.slice(1)))
|
|
120
|
+
return g.text.slice(1);
|
|
121
|
+
if (/^not\s+/.test(g.text) && isSimpleOperand(g.text.slice(4).trim()))
|
|
122
|
+
return g.text.slice(4).trim();
|
|
123
|
+
// One comparison flips instead of wrapping: the reader of a Go `if err !=
|
|
124
|
+
// nil { return }` wants `err == nil`, not `!(err != nil)`.
|
|
125
|
+
const flipped = flipComparison(g.text);
|
|
126
|
+
if (flipped !== null)
|
|
127
|
+
return flipped;
|
|
128
|
+
return isSimpleOperand(g.text) ? `!${g.text}` : `!(${g.text})`;
|
|
129
|
+
}
|
|
130
|
+
/** `a != b` → `a == b`, `x is None` → `x is not None`; null when the text is not one plain comparison. */
|
|
131
|
+
function flipComparison(text) {
|
|
132
|
+
if (/&&|\|\||\band\b|\bor\b|\?/.test(text))
|
|
133
|
+
return null;
|
|
134
|
+
if (hasTopLevelOr(text))
|
|
135
|
+
return null;
|
|
136
|
+
const m = /^([^=!<>]+?)\s*(===|!==|==|!=|\bis not\b|\bis\b)\s*([^=!<>]+)$/.exec(text);
|
|
137
|
+
if (!m)
|
|
138
|
+
return null;
|
|
139
|
+
const flip = { '===': '!==', '!==': '===', '==': '!=', '!=': '==', is: 'is not', 'is not': 'is' };
|
|
140
|
+
const op = flip[m[2]];
|
|
141
|
+
return op ? `${m[1].trim()} ${op} ${m[3].trim()}` : null;
|
|
142
|
+
}
|
|
143
|
+
/** A `||` outside every bracket and string — the condition is a disjunction as written. */
|
|
144
|
+
function hasTopLevelOr(text) {
|
|
145
|
+
let depth = 0;
|
|
146
|
+
let quote = null;
|
|
147
|
+
for (let i = 0; i < text.length; i++) {
|
|
148
|
+
const ch = text[i];
|
|
149
|
+
if (quote !== null) {
|
|
150
|
+
if (ch === '\\')
|
|
151
|
+
i++;
|
|
152
|
+
else if (ch === quote)
|
|
153
|
+
quote = null;
|
|
154
|
+
continue;
|
|
155
|
+
}
|
|
156
|
+
if (ch === "'" || ch === '"' || ch === '`')
|
|
157
|
+
quote = ch;
|
|
158
|
+
else if (ch === '(' || ch === '[' || ch === '{')
|
|
159
|
+
depth++;
|
|
160
|
+
else if (ch === ')' || ch === ']' || ch === '}')
|
|
161
|
+
depth = Math.max(0, depth - 1);
|
|
162
|
+
else if (depth === 0 && ch === '|' && text[i + 1] === '|')
|
|
163
|
+
return true;
|
|
164
|
+
}
|
|
165
|
+
return false;
|
|
166
|
+
}
|
|
167
|
+
function isSimpleOperand(text) {
|
|
168
|
+
// A name, a member chain, or one call on it — `Objects.equals(owner.getId(), id)`
|
|
169
|
+
// included: the parens must balance and nothing may sit outside them.
|
|
170
|
+
if (/[=<>]/.test(text) || /\s(?:&&|\|\||and|or)\s/.test(text))
|
|
171
|
+
return false;
|
|
172
|
+
const m = /^([\w$.?!]+)(\(.*\))?$/s.exec(text);
|
|
173
|
+
if (!m)
|
|
174
|
+
return false;
|
|
175
|
+
if (!m[2])
|
|
176
|
+
return true;
|
|
177
|
+
let depth = 0;
|
|
178
|
+
for (let i = 0; i < m[2].length; i++) {
|
|
179
|
+
const ch = m[2][i];
|
|
180
|
+
if (ch === '(')
|
|
181
|
+
depth++;
|
|
182
|
+
else if (ch === ')') {
|
|
183
|
+
depth--;
|
|
184
|
+
if (depth === 0 && i < m[2].length - 1)
|
|
185
|
+
return false;
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
return depth === 0;
|
|
189
|
+
}
|
|
190
|
+
const TREE_CACHE_SIZE = 8;
|
|
191
|
+
const treeCache = new Map();
|
|
192
|
+
/**
|
|
193
|
+
* Files above this size are not parsed for labels. A 300 KB source file costs
|
|
194
|
+
* tens of milliseconds to parse, and a Symbol view is budgeted at 100 ms end
|
|
195
|
+
* to end; a call site in such a file simply shows no `when`.
|
|
196
|
+
*/
|
|
197
|
+
exports.MAX_PARSE_BYTES = 256 * 1024;
|
|
198
|
+
/** The `web-tree-sitter` trees held above are native memory: evict explicitly. */
|
|
199
|
+
function remember(path, entry) {
|
|
200
|
+
const old = treeCache.get(path);
|
|
201
|
+
if (old)
|
|
202
|
+
old.tree.delete();
|
|
203
|
+
treeCache.delete(path);
|
|
204
|
+
treeCache.set(path, entry);
|
|
205
|
+
if (treeCache.size > TREE_CACHE_SIZE) {
|
|
206
|
+
const oldest = treeCache.keys().next().value;
|
|
207
|
+
treeCache.get(oldest)?.tree.delete();
|
|
208
|
+
treeCache.delete(oldest);
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
async function treeFor(absPath, language) {
|
|
212
|
+
let stat;
|
|
213
|
+
try {
|
|
214
|
+
stat = fs.statSync(absPath);
|
|
215
|
+
}
|
|
216
|
+
catch {
|
|
217
|
+
return null;
|
|
218
|
+
}
|
|
219
|
+
const key = `${language}:${stat.mtimeMs}:${stat.size}`;
|
|
220
|
+
const hit = treeCache.get(absPath);
|
|
221
|
+
if (hit && hit.key === key)
|
|
222
|
+
return hit;
|
|
223
|
+
if (stat.size > exports.MAX_PARSE_BYTES)
|
|
224
|
+
return null;
|
|
225
|
+
let source;
|
|
226
|
+
try {
|
|
227
|
+
source = fs.readFileSync(absPath, 'utf8');
|
|
228
|
+
}
|
|
229
|
+
catch {
|
|
230
|
+
return null;
|
|
231
|
+
}
|
|
232
|
+
const tree = await parse(source, language);
|
|
233
|
+
if (!tree)
|
|
234
|
+
return null;
|
|
235
|
+
const entry = { key, tree, source };
|
|
236
|
+
remember(absPath, entry);
|
|
237
|
+
return entry;
|
|
238
|
+
}
|
|
239
|
+
async function parse(source, language) {
|
|
240
|
+
try {
|
|
241
|
+
await (0, grammars_1.loadGrammarsForLanguages)([language]);
|
|
242
|
+
const parser = (0, grammars_1.getParser)(language);
|
|
243
|
+
if (!parser)
|
|
244
|
+
return null;
|
|
245
|
+
return parser.parse(source) ?? null;
|
|
246
|
+
}
|
|
247
|
+
catch {
|
|
248
|
+
return null;
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
function siteKey(site) {
|
|
252
|
+
return `${site.line}:${typeof site.column === 'number' ? site.column : ''}${site.callee ? `:${site.callee}` : ''}`;
|
|
253
|
+
}
|
|
254
|
+
/**
|
|
255
|
+
* Guards for many call sites in one file, keyed by {@link siteKey}. The file
|
|
256
|
+
* is parsed once (and cached across requests until it changes on disk). A
|
|
257
|
+
* language without rules, or a file that cannot be read or parsed, yields an
|
|
258
|
+
* empty map.
|
|
259
|
+
*/
|
|
260
|
+
async function guardsForFile(absPath, language, sites) {
|
|
261
|
+
const out = new Map();
|
|
262
|
+
if (!supportsBranchGuards(language) || sites.length === 0)
|
|
263
|
+
return out;
|
|
264
|
+
const cached = await treeFor(absPath, language);
|
|
265
|
+
if (!cached)
|
|
266
|
+
return out;
|
|
267
|
+
for (const site of sites) {
|
|
268
|
+
const key = siteKey(site);
|
|
269
|
+
if (out.has(key))
|
|
270
|
+
continue;
|
|
271
|
+
out.set(key, guardsInTree(cached.tree.rootNode, cached.source, language, site.line, site.column ?? null));
|
|
272
|
+
}
|
|
273
|
+
return out;
|
|
274
|
+
}
|
|
275
|
+
/**
|
|
276
|
+
* Synchronous twin of {@link guardsForFile} for callers that cannot await
|
|
277
|
+
* (the explore text builder). It only serves languages whose grammar is
|
|
278
|
+
* ALREADY loaded — see {@link warmBranchGuardGrammars} — and yields an empty
|
|
279
|
+
* map otherwise, never a wrong label.
|
|
280
|
+
*/
|
|
281
|
+
function guardsForFileSync(absPath, language, sites) {
|
|
282
|
+
const out = new Map();
|
|
283
|
+
if (!supportsBranchGuards(language) || sites.length === 0)
|
|
284
|
+
return out;
|
|
285
|
+
let stat;
|
|
286
|
+
try {
|
|
287
|
+
stat = fs.statSync(absPath);
|
|
288
|
+
}
|
|
289
|
+
catch {
|
|
290
|
+
return out;
|
|
291
|
+
}
|
|
292
|
+
const key = `${language}:${stat.mtimeMs}:${stat.size}`;
|
|
293
|
+
let cached = treeCache.get(absPath);
|
|
294
|
+
if (!cached || cached.key !== key) {
|
|
295
|
+
if (stat.size > exports.MAX_PARSE_BYTES)
|
|
296
|
+
return out;
|
|
297
|
+
const parser = (0, grammars_1.getParser)(language);
|
|
298
|
+
if (!parser)
|
|
299
|
+
return out;
|
|
300
|
+
let source;
|
|
301
|
+
try {
|
|
302
|
+
source = fs.readFileSync(absPath, 'utf8');
|
|
303
|
+
}
|
|
304
|
+
catch {
|
|
305
|
+
return out;
|
|
306
|
+
}
|
|
307
|
+
const tree = parser.parse(source);
|
|
308
|
+
if (!tree)
|
|
309
|
+
return out;
|
|
310
|
+
cached = { key, tree, source };
|
|
311
|
+
remember(absPath, cached);
|
|
312
|
+
}
|
|
313
|
+
for (const site of sites) {
|
|
314
|
+
const k = siteKey(site);
|
|
315
|
+
if (!out.has(k))
|
|
316
|
+
out.set(k, guardsInTree(cached.tree.rootNode, cached.source, language, site.line, site.column ?? null));
|
|
317
|
+
}
|
|
318
|
+
return out;
|
|
319
|
+
}
|
|
320
|
+
/** The languages with rules here — what {@link warmBranchGuardGrammars} loads. */
|
|
321
|
+
exports.BRANCH_GUARD_LANGUAGES = [
|
|
322
|
+
'typescript',
|
|
323
|
+
'tsx',
|
|
324
|
+
'javascript',
|
|
325
|
+
'jsx',
|
|
326
|
+
'swift',
|
|
327
|
+
'python',
|
|
328
|
+
'java',
|
|
329
|
+
'kotlin',
|
|
330
|
+
'csharp',
|
|
331
|
+
'go',
|
|
332
|
+
'c',
|
|
333
|
+
'cpp',
|
|
334
|
+
'objc',
|
|
335
|
+
];
|
|
336
|
+
// =============================================================================
|
|
337
|
+
// Call arguments — what a site passes
|
|
338
|
+
// =============================================================================
|
|
339
|
+
/** Longest argument list kept before it is cut with an ellipsis. */
|
|
340
|
+
const MAX_ARGS_TEXT = 96;
|
|
341
|
+
/** Longest single argument (a string literal, a name) kept whole. */
|
|
342
|
+
const MAX_ARG_TEXT = 40;
|
|
343
|
+
/** Object keys listed before `…` stands for the rest. */
|
|
344
|
+
const MAX_OBJECT_KEYS = 4;
|
|
345
|
+
/** Call nodes, across the grammars with rules here: JS, Swift, Python, Java, Kotlin, C#, Go, C. */
|
|
346
|
+
const CALL_TYPES = new Set([
|
|
347
|
+
'call_expression',
|
|
348
|
+
'new_expression',
|
|
349
|
+
'call',
|
|
350
|
+
'method_invocation',
|
|
351
|
+
'object_creation_expression',
|
|
352
|
+
'invocation_expression',
|
|
353
|
+
'constructor_invocation',
|
|
354
|
+
]);
|
|
355
|
+
const ARGUMENT_CONTAINERS = new Set(['arguments', 'value_arguments', 'argument_list']);
|
|
356
|
+
const STRING_TYPES = new Set([
|
|
357
|
+
'string',
|
|
358
|
+
'template_string',
|
|
359
|
+
'line_string_literal',
|
|
360
|
+
'multi_line_string_literal',
|
|
361
|
+
'raw_string_literal',
|
|
362
|
+
'string_literal',
|
|
363
|
+
'interpreted_string_literal',
|
|
364
|
+
'concatenated_string',
|
|
365
|
+
'verbatim_string_literal',
|
|
366
|
+
'interpolated_string_expression',
|
|
367
|
+
'char_literal',
|
|
368
|
+
]);
|
|
369
|
+
const OBJECT_TYPES = new Set(['object', 'object_expression', 'dictionary', 'anonymous_object_creation_expression']);
|
|
370
|
+
const ARRAY_TYPES = new Set([
|
|
371
|
+
'array',
|
|
372
|
+
'array_literal',
|
|
373
|
+
'dictionary_literal',
|
|
374
|
+
'list',
|
|
375
|
+
'tuple',
|
|
376
|
+
'set',
|
|
377
|
+
'list_comprehension',
|
|
378
|
+
'array_creation_expression',
|
|
379
|
+
'array_initializer',
|
|
380
|
+
'initializer_list',
|
|
381
|
+
'collection_expression',
|
|
382
|
+
'collection_literal',
|
|
383
|
+
]);
|
|
384
|
+
const FUNCTION_TYPES = new Set([
|
|
385
|
+
'arrow_function',
|
|
386
|
+
'function_expression',
|
|
387
|
+
'function',
|
|
388
|
+
'lambda',
|
|
389
|
+
'lambda_expression',
|
|
390
|
+
'func_literal',
|
|
391
|
+
'anonymous_function',
|
|
392
|
+
'anonymous_method_expression',
|
|
393
|
+
]);
|
|
394
|
+
/**
|
|
395
|
+
* The arguments a call site passes, as written, abbreviated to what a reader
|
|
396
|
+
* scans for: a string literal whole (a storage key, a URL, a message), a name
|
|
397
|
+
* whole, an object as its keys (`{ email, password }`), an array as `[…]`, a
|
|
398
|
+
* function as `() => …`, a nested call as `f(…)`. The conditions say WHEN a
|
|
399
|
+
* step runs; this says WITH WHAT — `SecureStore.setItemAsync('userEmail',
|
|
400
|
+
* values.email)` is a different fact from `SecureStore.setItemAsync`.
|
|
401
|
+
*
|
|
402
|
+
* Keyed by {@link siteKey} like the guards, read from the same cached tree.
|
|
403
|
+
* A site that is not inside a call, or a language without rules, is absent.
|
|
404
|
+
*/
|
|
405
|
+
async function callArgumentsForFile(absPath, language, sites) {
|
|
406
|
+
const out = new Map();
|
|
407
|
+
if (!supportsBranchGuards(language) || sites.length === 0)
|
|
408
|
+
return out;
|
|
409
|
+
const cached = await treeFor(absPath, language);
|
|
410
|
+
if (!cached)
|
|
411
|
+
return out;
|
|
412
|
+
for (const site of sites) {
|
|
413
|
+
const key = siteKey(site);
|
|
414
|
+
if (out.has(key))
|
|
415
|
+
continue;
|
|
416
|
+
const text = callArgumentsInTree(cached.tree.rootNode, cached.source, site.line, site.column ?? null);
|
|
417
|
+
if (text !== null)
|
|
418
|
+
out.set(key, text);
|
|
419
|
+
}
|
|
420
|
+
return out;
|
|
421
|
+
}
|
|
422
|
+
/** {@link callArgumentsForFile} over source text — the test surface. */
|
|
423
|
+
async function callArgumentsInSource(source, language, line, column) {
|
|
424
|
+
if (!supportsBranchGuards(language))
|
|
425
|
+
return null;
|
|
426
|
+
const tree = await parse(source, language);
|
|
427
|
+
if (!tree)
|
|
428
|
+
return null;
|
|
429
|
+
try {
|
|
430
|
+
return callArgumentsInTree(tree.rootNode, source, line, column);
|
|
431
|
+
}
|
|
432
|
+
finally {
|
|
433
|
+
tree.delete();
|
|
434
|
+
}
|
|
435
|
+
}
|
|
436
|
+
function callArgumentsInTree(root, source, line, column) {
|
|
437
|
+
return callSiteInTree(root, source, line, column)?.args ?? null;
|
|
438
|
+
}
|
|
439
|
+
/** A node the climb to an enclosing call must not cross: the call is then a statement of its own inside a callback. */
|
|
440
|
+
const CALL_BOUNDARY = /function|lambda|closure|block|statement|body|declaration/;
|
|
441
|
+
/** The nearest call whose ARGUMENTS contain `call`, as its callee chain; null when there is none this side of a function or block. */
|
|
442
|
+
function enclosingCallText(call) {
|
|
443
|
+
for (let node = call.parent, up = 0; node && up < 12; node = node.parent, up++) {
|
|
444
|
+
if (CALL_BOUNDARY.test(node.type))
|
|
445
|
+
return null;
|
|
446
|
+
if (!CALL_TYPES.has(node.type))
|
|
447
|
+
continue;
|
|
448
|
+
const container = argumentsOf(node);
|
|
449
|
+
if (container && container.startIndex <= call.startIndex && call.endIndex <= container.endIndex)
|
|
450
|
+
return calleeChainText(node, container);
|
|
451
|
+
return null;
|
|
452
|
+
}
|
|
453
|
+
return null;
|
|
454
|
+
}
|
|
455
|
+
const STATUS_KEY = /^(?:status|statusCode|status_code|code)$/;
|
|
456
|
+
/** `{ status: 201 }` inside an argument — the literal an abbreviated object hides. */
|
|
457
|
+
function statusPropertyIn(node, depth = 0) {
|
|
458
|
+
if (depth > 2)
|
|
459
|
+
return null;
|
|
460
|
+
for (let i = 0; i < node.namedChildCount; i++) {
|
|
461
|
+
const c = node.namedChild(i);
|
|
462
|
+
if (c.type === 'pair' || c.type === 'keyword_argument' || c.type === 'named_argument' || c.type === 'property_assignment' || c.type === 'object_property') {
|
|
463
|
+
const key = c.childForFieldName('key') ?? c.childForFieldName('name') ?? c.namedChild(0);
|
|
464
|
+
const value = c.childForFieldName('value') ?? c.namedChild(c.namedChildCount - 1);
|
|
465
|
+
if (key && value && STATUS_KEY.test(key.text.replace(/['"]/g, '')) && /^[1-5]\d{2}$/.test(value.text))
|
|
466
|
+
return Number(value.text);
|
|
467
|
+
}
|
|
468
|
+
const inner = statusPropertyIn(c, depth + 1);
|
|
469
|
+
if (inner !== null)
|
|
470
|
+
return inner;
|
|
471
|
+
}
|
|
472
|
+
return null;
|
|
473
|
+
}
|
|
474
|
+
/** Longest callee text kept before it is cut. */
|
|
475
|
+
const MAX_CALLEE_TEXT = 96;
|
|
476
|
+
/** The call node a site belongs to: climb from the callee to the call. A few levels cover a member chain. */
|
|
477
|
+
function callAt(root, source, line, column, callee) {
|
|
478
|
+
const row = line - 1;
|
|
479
|
+
// The recorded column may sit a character off the callee (a 1-based
|
|
480
|
+
// column, the space before `prisma`): a near miss is tried before giving up.
|
|
481
|
+
const columns = column === null ? [firstNonBlankColumn(source, row)] : [column, column + 1, Math.max(0, column - 1), firstNonBlankColumn(source, row)];
|
|
482
|
+
const want = callee ? callee.split(/[.:]/).pop() ?? callee : null;
|
|
483
|
+
for (const col of columns) {
|
|
484
|
+
const start = innermostAt(root, row, col);
|
|
485
|
+
if (!start)
|
|
486
|
+
continue;
|
|
487
|
+
let node = start;
|
|
488
|
+
let first = null;
|
|
489
|
+
for (let up = 0; node && up < 10; up++, node = node.parent) {
|
|
490
|
+
if (!CALL_TYPES.has(node.type))
|
|
491
|
+
continue;
|
|
492
|
+
if (!first)
|
|
493
|
+
first = node;
|
|
494
|
+
if (want === null)
|
|
495
|
+
return node;
|
|
496
|
+
// A chain's position is its start: `res.status(201).json(…)` at `res`
|
|
497
|
+
// meets `res.status(…)` first; the call that is THIS one names `json`.
|
|
498
|
+
const container = argumentsOf(node);
|
|
499
|
+
const text = container ? calleeChainText(node, container) : '';
|
|
500
|
+
if ((text.replace(/\([^()]*\)/g, '').split(/[.:]/).pop() ?? '') === want)
|
|
501
|
+
return node;
|
|
502
|
+
}
|
|
503
|
+
if (first)
|
|
504
|
+
return first;
|
|
505
|
+
}
|
|
506
|
+
return null;
|
|
507
|
+
}
|
|
508
|
+
function callSiteInTree(root, source, line, column, want) {
|
|
509
|
+
const call = callAt(root, source, line, column, want);
|
|
510
|
+
if (!call)
|
|
511
|
+
return null;
|
|
512
|
+
const container = argumentsOf(call);
|
|
513
|
+
if (!container)
|
|
514
|
+
return null;
|
|
515
|
+
const callee = calleeChainText(call, container);
|
|
516
|
+
if (container.type === 'lambda_literal')
|
|
517
|
+
return { callee, args: '{ … }', argList: ['{ … }'] };
|
|
518
|
+
const parts = [];
|
|
519
|
+
let status = null;
|
|
520
|
+
for (let i = 0; i < container.namedChildCount; i++) {
|
|
521
|
+
const c = container.namedChild(i);
|
|
522
|
+
if (!c || c.type === 'comment')
|
|
523
|
+
continue;
|
|
524
|
+
parts.push(abbreviateArgument(c, source));
|
|
525
|
+
if (status === null && c.type !== 'comment')
|
|
526
|
+
status = statusPropertyIn(c);
|
|
527
|
+
}
|
|
528
|
+
const text = parts.join(', ');
|
|
529
|
+
if (status === null)
|
|
530
|
+
status = statusSetBefore(call, callee);
|
|
531
|
+
const span = {
|
|
532
|
+
start: { line: call.startPosition.row + 1, column: call.startPosition.column },
|
|
533
|
+
end: { line: call.endPosition.row + 1, column: call.endPosition.column },
|
|
534
|
+
};
|
|
535
|
+
const within = enclosingCallText(call);
|
|
536
|
+
return {
|
|
537
|
+
callee,
|
|
538
|
+
args: text.length > MAX_ARGS_TEXT ? `${text.slice(0, MAX_ARGS_TEXT - 1)}…` : text,
|
|
539
|
+
argList: parts,
|
|
540
|
+
span,
|
|
541
|
+
...(within ? { within } : {}),
|
|
542
|
+
...(status !== null ? { status } : {}),
|
|
543
|
+
};
|
|
544
|
+
}
|
|
545
|
+
const BODY_REPLY = /^(?:res|response|reply|rep|ctx|c|context)\.(?:json|jsonp|send|render|sendFile|download|end|text|html|body)$/;
|
|
546
|
+
const STATEMENT_BLOCKS = new Set(['statement_block', 'program', 'block', 'class_body', 'module']);
|
|
547
|
+
/** Statements looked back through for a status the reply's own chain does not carry. */
|
|
548
|
+
const STATUS_LOOKBACK = 6;
|
|
549
|
+
/**
|
|
550
|
+
* `res.status(202); res.json(user)` — the status set by an earlier statement
|
|
551
|
+
* in the same block, when the reply's own chain sets none. Only a statement
|
|
552
|
+
* that IS the status call counts (`res.status(404)` inside an `if` before it
|
|
553
|
+
* is another path, not this reply's); the first one found walking back wins.
|
|
554
|
+
*/
|
|
555
|
+
function statusSetBefore(call, callee) {
|
|
556
|
+
const bare = callee.replace(/\([^()]*\)/g, '');
|
|
557
|
+
if (!BODY_REPLY.test(bare) || /\b(?:status|code|sendStatus|writeHead)\(/.test(callee))
|
|
558
|
+
return null;
|
|
559
|
+
const receiver = bare.split('.')[0];
|
|
560
|
+
let statement = call;
|
|
561
|
+
while (statement.parent && !STATEMENT_BLOCKS.has(statement.parent.type))
|
|
562
|
+
statement = statement.parent;
|
|
563
|
+
const re = new RegExp(`^\\s*(?:await\\s+)?${receiver}\\s*\\.\\s*(?:status|code)\\s*\\(\\s*([1-5]\\d{2})\\s*\\)\\s*;?\\s*$|^\\s*${receiver}\\s*\\.\\s*statusCode\\s*=\\s*([1-5]\\d{2})\\s*;?\\s*$`);
|
|
564
|
+
let prev = statement.previousNamedSibling;
|
|
565
|
+
for (let i = 0; prev && i < STATUS_LOOKBACK; i++, prev = prev.previousNamedSibling) {
|
|
566
|
+
if (prev.type === 'comment')
|
|
567
|
+
continue;
|
|
568
|
+
const m = re.exec(prev.text);
|
|
569
|
+
if (m)
|
|
570
|
+
return Number(m[1] ?? m[2]);
|
|
571
|
+
}
|
|
572
|
+
return null;
|
|
573
|
+
}
|
|
574
|
+
/** The text of a call before its arguments, normalised to a member chain. */
|
|
575
|
+
function calleeChainText(call, container) {
|
|
576
|
+
// Kotlin and Swift wrap the arguments in a `call_suffix`; the callee is
|
|
577
|
+
// everything before that suffix.
|
|
578
|
+
let end = container.startIndex;
|
|
579
|
+
const suffix = container.parent && container.parent.type === 'call_suffix' ? container.parent : null;
|
|
580
|
+
if (suffix)
|
|
581
|
+
end = suffix.startIndex;
|
|
582
|
+
let text = collapse(call.text.slice(0, Math.max(0, end - call.startIndex)));
|
|
583
|
+
text = text.replace(/^(?:await|new|yield|return)\s+/, '').replace(/^(?:await|new)\s+/, '');
|
|
584
|
+
// Empty every nested argument list, innermost first: `a(b(c)).d` → `a().d`
|
|
585
|
+
// — keeping one short literal or name (`res.status(404).json`,
|
|
586
|
+
// `ResponseEntity.status(HttpStatus.NOT_FOUND).body`), which is the fact a
|
|
587
|
+
// reader of the chain wants.
|
|
588
|
+
for (let i = 0; i < 6 && /\([^()]*\)/.test(text); i++) {
|
|
589
|
+
text = text.replace(/\(([^()]*)\)/g, (_m, inner) => (/^\s*[\w.]{1,28}\s*$/.test(inner) ? `(${inner.trim()})` : '()'));
|
|
590
|
+
}
|
|
591
|
+
text = text
|
|
592
|
+
.replace(/\?\./g, '.')
|
|
593
|
+
.replace(/!\./g, '.')
|
|
594
|
+
.replace(/\s+/g, '')
|
|
595
|
+
.replace(/<[^<>]*>/g, '')
|
|
596
|
+
.replace(/\([^()]*\)$/, '');
|
|
597
|
+
return text.length > MAX_CALLEE_TEXT ? `${text.slice(0, MAX_CALLEE_TEXT - 1)}…` : text;
|
|
598
|
+
}
|
|
599
|
+
/** Both halves of every site, keyed by {@link siteKey}, from one cached tree. */
|
|
600
|
+
async function callSitesForFile(absPath, language, sites) {
|
|
601
|
+
const out = new Map();
|
|
602
|
+
if (!supportsBranchGuards(language) || sites.length === 0)
|
|
603
|
+
return out;
|
|
604
|
+
const cached = await treeFor(absPath, language);
|
|
605
|
+
if (!cached)
|
|
606
|
+
return out;
|
|
607
|
+
for (const site of sites) {
|
|
608
|
+
const key = siteKey(site);
|
|
609
|
+
if (out.has(key))
|
|
610
|
+
continue;
|
|
611
|
+
const found = callSiteInTree(cached.tree.rootNode, cached.source, site.line, site.column ?? null, site.callee);
|
|
612
|
+
if (found !== null)
|
|
613
|
+
out.set(key, found);
|
|
614
|
+
}
|
|
615
|
+
return out;
|
|
616
|
+
}
|
|
617
|
+
/** {@link callSitesForFile} over source text — the test surface. */
|
|
618
|
+
async function callSiteInSource(source, language, line, column) {
|
|
619
|
+
if (!supportsBranchGuards(language))
|
|
620
|
+
return null;
|
|
621
|
+
const tree = await parse(source, language);
|
|
622
|
+
if (!tree)
|
|
623
|
+
return null;
|
|
624
|
+
try {
|
|
625
|
+
return callSiteInTree(tree.rootNode, source, line, column);
|
|
626
|
+
}
|
|
627
|
+
finally {
|
|
628
|
+
tree.delete();
|
|
629
|
+
}
|
|
630
|
+
}
|
|
631
|
+
/** The node holding a call's arguments: the `arguments` field, a container child, or Swift's `call_suffix` contents. */
|
|
632
|
+
function argumentsOf(call) {
|
|
633
|
+
const field = call.childForFieldName('arguments');
|
|
634
|
+
if (field)
|
|
635
|
+
return field;
|
|
636
|
+
for (let i = 0; i < call.namedChildCount; i++) {
|
|
637
|
+
const c = call.namedChild(i);
|
|
638
|
+
if (!c)
|
|
639
|
+
continue;
|
|
640
|
+
if (ARGUMENT_CONTAINERS.has(c.type))
|
|
641
|
+
return c;
|
|
642
|
+
if (c.type === 'call_suffix') {
|
|
643
|
+
for (let j = 0; j < c.namedChildCount; j++) {
|
|
644
|
+
const inner = c.namedChild(j);
|
|
645
|
+
if (inner && (ARGUMENT_CONTAINERS.has(inner.type) || inner.type === 'lambda_literal'))
|
|
646
|
+
return inner;
|
|
647
|
+
}
|
|
648
|
+
return c;
|
|
649
|
+
}
|
|
650
|
+
}
|
|
651
|
+
return null;
|
|
652
|
+
}
|
|
653
|
+
function abbreviateArgument(node, source) {
|
|
654
|
+
const type = node.type;
|
|
655
|
+
// Python `name=value`, C# `name: value` — the name is half the meaning.
|
|
656
|
+
if (type === 'keyword_argument') {
|
|
657
|
+
const name = node.childForFieldName('name');
|
|
658
|
+
const value = node.childForFieldName('value');
|
|
659
|
+
return `${name?.text ?? ''}=${value ? abbreviateArgument(value, source) : ''}`;
|
|
660
|
+
}
|
|
661
|
+
if (type === 'argument') {
|
|
662
|
+
// C#: an `argument` wraps the expression, optionally with a name.
|
|
663
|
+
const name = node.childForFieldName('name');
|
|
664
|
+
const inner = lastNamed(node);
|
|
665
|
+
const value = inner ? abbreviateArgument(inner, source) : cut(collapse(node.text), MAX_ARG_TEXT);
|
|
666
|
+
return name && inner && name.id !== inner.id ? `${name.text}: ${value}` : value;
|
|
667
|
+
}
|
|
668
|
+
// Go `gin.H{"error": err}` / `User{Name: n}`: the type, then the braces.
|
|
669
|
+
if (type === 'composite_literal') {
|
|
670
|
+
const t = node.childForFieldName('type');
|
|
671
|
+
return `${t ? cut(collapse(t.text), 24) : ''}{…}`;
|
|
672
|
+
}
|
|
673
|
+
if (STRING_TYPES.has(type))
|
|
674
|
+
return cut(collapse(node.text), MAX_ARG_TEXT);
|
|
675
|
+
if (OBJECT_TYPES.has(type))
|
|
676
|
+
return objectKeys(node, source);
|
|
677
|
+
if (ARRAY_TYPES.has(type))
|
|
678
|
+
return '[…]';
|
|
679
|
+
if (FUNCTION_TYPES.has(type))
|
|
680
|
+
return '() => …';
|
|
681
|
+
if (type === 'lambda_literal')
|
|
682
|
+
return '{ … }';
|
|
683
|
+
if (type === 'spread_element')
|
|
684
|
+
return cut(collapse(node.text), MAX_ARG_TEXT);
|
|
685
|
+
if (type === 'await_expression') {
|
|
686
|
+
const inner = node.namedChild(0);
|
|
687
|
+
return inner ? `await ${abbreviateArgument(inner, source)}` : 'await …';
|
|
688
|
+
}
|
|
689
|
+
if (CALL_TYPES.has(type)) {
|
|
690
|
+
const callee = node.childForFieldName('function') ?? node.childForFieldName('constructor') ?? node.namedChild(0);
|
|
691
|
+
const name = callee ? cut(collapse(callee.text), 28) : '';
|
|
692
|
+
return `${type === 'new_expression' ? 'new ' : ''}${name}(…)`;
|
|
693
|
+
}
|
|
694
|
+
// Swift `label: value` — the label is half the meaning (`withName:`).
|
|
695
|
+
if (type === 'value_argument') {
|
|
696
|
+
const named = [];
|
|
697
|
+
for (let i = 0; i < node.namedChildCount; i++) {
|
|
698
|
+
const c = node.namedChild(i);
|
|
699
|
+
if (c)
|
|
700
|
+
named.push(c);
|
|
701
|
+
}
|
|
702
|
+
if (named.length >= 2 && (named[0].type === 'simple_identifier' || named[0].type === 'value_argument_label')) {
|
|
703
|
+
return `${named[0].text}: ${abbreviateArgument(named[named.length - 1], source)}`;
|
|
704
|
+
}
|
|
705
|
+
return named.length > 0 ? abbreviateArgument(named[named.length - 1], source) : cut(collapse(node.text), MAX_ARG_TEXT);
|
|
706
|
+
}
|
|
707
|
+
if (type === 'lambda_argument' || type === 'trailing_closure' || type === 'annotated_lambda')
|
|
708
|
+
return '{ … }';
|
|
709
|
+
return cut(collapse(node.text), MAX_ARG_TEXT);
|
|
710
|
+
}
|
|
711
|
+
/** `{ email, password, …}` — the keys an object literal passes, not its bulk. */
|
|
712
|
+
function objectKeys(node, source) {
|
|
713
|
+
const keys = [];
|
|
714
|
+
let more = 0;
|
|
715
|
+
for (let i = 0; i < node.namedChildCount; i++) {
|
|
716
|
+
const c = node.namedChild(i);
|
|
717
|
+
if (!c || c.type === 'comment')
|
|
718
|
+
continue;
|
|
719
|
+
let key = null;
|
|
720
|
+
if (c.type === 'pair')
|
|
721
|
+
key = c.childForFieldName('key')?.text ?? null;
|
|
722
|
+
else if (c.type === 'shorthand_property_identifier' || c.type === 'shorthand_property_identifier_pattern')
|
|
723
|
+
key = c.text;
|
|
724
|
+
else if (c.type === 'spread_element')
|
|
725
|
+
key = collapse(c.text);
|
|
726
|
+
else if (c.type === 'method_definition')
|
|
727
|
+
key = c.childForFieldName('name')?.text ?? null;
|
|
728
|
+
if (key === null)
|
|
729
|
+
continue;
|
|
730
|
+
if (keys.length >= MAX_OBJECT_KEYS) {
|
|
731
|
+
more++;
|
|
732
|
+
continue;
|
|
733
|
+
}
|
|
734
|
+
keys.push(cut(key, 24));
|
|
735
|
+
}
|
|
736
|
+
void source;
|
|
737
|
+
if (keys.length === 0)
|
|
738
|
+
return '{…}';
|
|
739
|
+
return `{ ${keys.join(', ')}${more > 0 ? ', …' : ''} }`;
|
|
740
|
+
}
|
|
741
|
+
/** Callees whose function argument runs LATER — a callback, not a call. Matched on the last segment. */
|
|
742
|
+
const LATER_CALLEES = new Set([
|
|
743
|
+
'useEffect',
|
|
744
|
+
'useLayoutEffect',
|
|
745
|
+
'useFocusEffect',
|
|
746
|
+
'useImperativeHandle',
|
|
747
|
+
'setTimeout',
|
|
748
|
+
'setInterval',
|
|
749
|
+
'requestAnimationFrame',
|
|
750
|
+
'requestIdleCallback',
|
|
751
|
+
'runAfterInteractions',
|
|
752
|
+
'addListener',
|
|
753
|
+
'addEventListener',
|
|
754
|
+
'on',
|
|
755
|
+
'once',
|
|
756
|
+
'subscribe',
|
|
757
|
+
'then',
|
|
758
|
+
'catch',
|
|
759
|
+
'finally',
|
|
760
|
+
'runOnJS',
|
|
761
|
+
'runOnUI',
|
|
762
|
+
'scheduleOnRN',
|
|
763
|
+
]);
|
|
764
|
+
/** The walk up never leaves the function the site belongs to — unless that function is inline. */
|
|
765
|
+
const TRIGGER_BOUNDARIES = new Set(['function_declaration', 'method_definition', 'class_declaration', 'class_body', 'program']);
|
|
766
|
+
const MAX_TRIGGER_CLIMB = 24;
|
|
767
|
+
async function triggersForFile(absPath, language, sites) {
|
|
768
|
+
const out = new Map();
|
|
769
|
+
if (!JS_FAMILY.has(language) || sites.length === 0)
|
|
770
|
+
return out;
|
|
771
|
+
const cached = await treeFor(absPath, language);
|
|
772
|
+
if (!cached)
|
|
773
|
+
return out;
|
|
774
|
+
for (const site of sites) {
|
|
775
|
+
const key = siteKey(site);
|
|
776
|
+
if (out.has(key))
|
|
777
|
+
continue;
|
|
778
|
+
const t = triggerInTree(cached.tree.rootNode, cached.source, site.line, site.column ?? null);
|
|
779
|
+
if (t !== null)
|
|
780
|
+
out.set(key, t);
|
|
781
|
+
}
|
|
782
|
+
return out;
|
|
783
|
+
}
|
|
784
|
+
/** {@link triggersForFile} over source text — the test surface. */
|
|
785
|
+
async function triggerInSource(source, language, line, column) {
|
|
786
|
+
if (!JS_FAMILY.has(language))
|
|
787
|
+
return null;
|
|
788
|
+
const tree = await parse(source, language);
|
|
789
|
+
if (!tree)
|
|
790
|
+
return null;
|
|
791
|
+
try {
|
|
792
|
+
return triggerInTree(tree.rootNode, source, line, column);
|
|
793
|
+
}
|
|
794
|
+
finally {
|
|
795
|
+
tree.delete();
|
|
796
|
+
}
|
|
797
|
+
}
|
|
798
|
+
function triggerInTree(root, source, line, column) {
|
|
799
|
+
const row = line - 1;
|
|
800
|
+
const col = column ?? firstNonBlankColumn(source, row);
|
|
801
|
+
let node = innermostAt(root, row, col);
|
|
802
|
+
let prev = null;
|
|
803
|
+
// Whether the climb crossed an inline function: `onPress={() => go()}`
|
|
804
|
+
// fires later, `behavior={isAndroid() ? 'a' : 'b'}` runs at render.
|
|
805
|
+
let deferred = false;
|
|
806
|
+
for (let up = 0; node && up < MAX_TRIGGER_CLIMB; up++, prev = node, node = node.parent) {
|
|
807
|
+
const type = node.type;
|
|
808
|
+
if (TRIGGER_BOUNDARIES.has(type))
|
|
809
|
+
return null;
|
|
810
|
+
// A named handler is its own story: `const handleX = useCallback(() => …)`
|
|
811
|
+
// binds a name, and whoever uses the name is the trigger of what is inside.
|
|
812
|
+
if (type === 'arrow_function' || type === 'function_expression') {
|
|
813
|
+
const p = node.parent;
|
|
814
|
+
if (p?.type === 'variable_declarator')
|
|
815
|
+
return null;
|
|
816
|
+
if (p?.type === 'arguments' && p.parent) {
|
|
817
|
+
const callee = lastSegment(calleeText(p.parent));
|
|
818
|
+
if (callee === 'useCallback' || callee === 'useMemo' || callee === 'useEffectEvent' || callee === 'useEvent')
|
|
819
|
+
return null;
|
|
820
|
+
}
|
|
821
|
+
deferred = true;
|
|
822
|
+
}
|
|
823
|
+
if (type === 'jsx_attribute') {
|
|
824
|
+
const name = node.namedChild(0);
|
|
825
|
+
const propName = name ? name.text : 'prop';
|
|
826
|
+
// An event prop, or any prop given a function: fired later. A value
|
|
827
|
+
// computed in the attribute (`behavior={isAndroid() ? …}`) is not.
|
|
828
|
+
if (!deferred && !/^on[A-Z]/.test(propName))
|
|
829
|
+
return null;
|
|
830
|
+
const element = node.parent;
|
|
831
|
+
const tag = element ? element.childForFieldName('name') : null;
|
|
832
|
+
return { kind: 'prop', name: propName, of: tag ? collapseText(tag.text) : null };
|
|
833
|
+
}
|
|
834
|
+
if (type === 'pair') {
|
|
835
|
+
const key = node.childForFieldName('key');
|
|
836
|
+
const keyText = key ? key.text.replace(/^['"`]|['"`]$/g, '') : '';
|
|
837
|
+
if (/^on[A-Z]\w*$/.test(keyText)) {
|
|
838
|
+
// `useFormik({ onSubmit: … })`, `Alert.alert(t, m, [{ onPress: … }])`:
|
|
839
|
+
// the object — possibly inside an array — is an argument of a call.
|
|
840
|
+
let holder = node.parent;
|
|
841
|
+
for (let hop = 0; holder && hop < 4 && (holder.type === 'object' || holder.type === 'array' || holder.type === 'pair'); hop++) {
|
|
842
|
+
holder = holder.parent;
|
|
843
|
+
}
|
|
844
|
+
const call = holder?.type === 'arguments' ? holder.parent : null;
|
|
845
|
+
return { kind: 'option', name: keyText, of: call && CALL_TYPES.has(call.type) ? calleeText(call) : null };
|
|
846
|
+
}
|
|
847
|
+
}
|
|
848
|
+
if (type === 'arguments' && node.parent && CALL_TYPES.has(node.parent.type) && prev !== null) {
|
|
849
|
+
const callee = lastSegment(calleeText(node.parent));
|
|
850
|
+
if (callee !== null && LATER_CALLEES.has(callee)) {
|
|
851
|
+
const first = node.namedChild(0);
|
|
852
|
+
const of = first && STRING_TYPES.has(first.type) ? cut(collapseText(first.text), MAX_ARG_TEXT) : null;
|
|
853
|
+
return { kind: 'callback', name: callee, of };
|
|
854
|
+
}
|
|
855
|
+
}
|
|
856
|
+
}
|
|
857
|
+
return null;
|
|
858
|
+
}
|
|
859
|
+
/** A call's callee as written: `nativeEmitter.addListener`, `Alert.alert`, `useFormik`. */
|
|
860
|
+
function calleeText(call) {
|
|
861
|
+
const callee = call.childForFieldName('function') ?? call.childForFieldName('constructor');
|
|
862
|
+
return callee ? cut(collapseText(callee.text), 40) : null;
|
|
863
|
+
}
|
|
864
|
+
/** The last segment of a callee: `nativeEmitter.addListener` → `addListener`. */
|
|
865
|
+
function lastSegment(text) {
|
|
866
|
+
if (text === null)
|
|
867
|
+
return null;
|
|
868
|
+
const m = text.match(/([A-Za-z_$][\w$]*)\s*$/);
|
|
869
|
+
return m ? m[1] : text;
|
|
870
|
+
}
|
|
871
|
+
function collapseText(text) {
|
|
872
|
+
return text.replace(/\s+/g, ' ').trim();
|
|
873
|
+
}
|
|
874
|
+
function collapse(text) {
|
|
875
|
+
return collapseText(text);
|
|
876
|
+
}
|
|
877
|
+
function cut(text, max) {
|
|
878
|
+
return text.length > max ? `${text.slice(0, max - 1)}…` : text;
|
|
879
|
+
}
|
|
880
|
+
function firstNonBlankColumn(source, row) {
|
|
881
|
+
const line = source.split('\n')[row] ?? '';
|
|
882
|
+
const m = line.match(/\S/);
|
|
883
|
+
return m ? (m.index ?? 0) : 0;
|
|
884
|
+
}
|
|
885
|
+
/** Load the grammars {@link guardsForFileSync} needs; a no-op once loaded, never throws. */
|
|
886
|
+
async function warmBranchGuardGrammars(only) {
|
|
887
|
+
const wanted = exports.BRANCH_GUARD_LANGUAGES.filter((l) => !only || only.includes(l));
|
|
888
|
+
if (wanted.length === 0)
|
|
889
|
+
return;
|
|
890
|
+
try {
|
|
891
|
+
await (0, grammars_1.loadGrammarsForLanguages)(wanted);
|
|
892
|
+
}
|
|
893
|
+
catch {
|
|
894
|
+
// Explore prints no `when` for that language; nothing else changes.
|
|
895
|
+
}
|
|
896
|
+
}
|
|
897
|
+
/** Guards for one site in source text — the test seam; production reads files. */
|
|
898
|
+
async function guardsInSource(source, language, line, column = null) {
|
|
899
|
+
if (!supportsBranchGuards(language))
|
|
900
|
+
return [];
|
|
901
|
+
const tree = await parse(source, language);
|
|
902
|
+
if (!tree)
|
|
903
|
+
return [];
|
|
904
|
+
try {
|
|
905
|
+
return guardsInTree(tree.rootNode, source, language, line, column);
|
|
906
|
+
}
|
|
907
|
+
finally {
|
|
908
|
+
tree.delete();
|
|
909
|
+
}
|
|
910
|
+
}
|
|
911
|
+
/** Loops for one site in source text — the test seam; production reads files. */
|
|
912
|
+
async function loopsInSource(source, language, line, column = null) {
|
|
913
|
+
if (!supportsBranchGuards(language))
|
|
914
|
+
return [];
|
|
915
|
+
const tree = await parse(source, language);
|
|
916
|
+
if (!tree)
|
|
917
|
+
return [];
|
|
918
|
+
try {
|
|
919
|
+
return loopsInTree(tree.rootNode, source, language, line, column);
|
|
920
|
+
}
|
|
921
|
+
finally {
|
|
922
|
+
tree.delete();
|
|
923
|
+
}
|
|
924
|
+
}
|
|
925
|
+
/** Loop node types across the grammars with rules here. A type absent yields nothing, never a wrong label. */
|
|
926
|
+
const LOOP_TYPES = new Map([
|
|
927
|
+
['for_statement', 'each'],
|
|
928
|
+
['for_in_statement', 'each'],
|
|
929
|
+
['for_of_statement', 'each'],
|
|
930
|
+
['for_each_statement', 'each'],
|
|
931
|
+
['enhanced_for_statement', 'each'],
|
|
932
|
+
['foreach_statement', 'each'],
|
|
933
|
+
['for_range_loop', 'each'],
|
|
934
|
+
['for_expression', 'each'],
|
|
935
|
+
['while_statement', 'while'],
|
|
936
|
+
['while_expression', 'while'],
|
|
937
|
+
['do_statement', 'while'],
|
|
938
|
+
['do_while_statement', 'while'],
|
|
939
|
+
['repeat_while_statement', 'while'],
|
|
940
|
+
]);
|
|
941
|
+
/**
|
|
942
|
+
* The loops a site is written inside, outermost first — what tells a reading in
|
|
943
|
+
* the code's order that a run of calls happens once PER ITEM rather than once.
|
|
944
|
+
* The climb is the guards' climb (the same boundaries, the same transparent
|
|
945
|
+
* inline functions), so a callback's body is read in its own function and a
|
|
946
|
+
* `.forEach` body under the loop it is written in.
|
|
947
|
+
*/
|
|
948
|
+
function loopsInTree(root, source, language, line, column) {
|
|
949
|
+
const rules = RULES_BY_LANGUAGE.get(language);
|
|
950
|
+
if (!rules)
|
|
951
|
+
return [];
|
|
952
|
+
const row = line - 1;
|
|
953
|
+
if (row < 0)
|
|
954
|
+
return [];
|
|
955
|
+
let col = column ?? 0;
|
|
956
|
+
if (column === null) {
|
|
957
|
+
const text = source.split('\n')[row] ?? '';
|
|
958
|
+
const first = text.search(/\S/);
|
|
959
|
+
col = first < 0 ? 0 : first;
|
|
960
|
+
}
|
|
961
|
+
let node = innermostAt(root, row, col);
|
|
962
|
+
if (!node)
|
|
963
|
+
return [];
|
|
964
|
+
const found = [];
|
|
965
|
+
while (node) {
|
|
966
|
+
const parent = node.parent;
|
|
967
|
+
if (!parent || rules.boundaries.has(parent.type))
|
|
968
|
+
break;
|
|
969
|
+
if (rules.inlineFunctions.has(parent.type)) {
|
|
970
|
+
const holder = parent.parent?.type ?? '';
|
|
971
|
+
if (rules.bindingParents.has(holder))
|
|
972
|
+
break;
|
|
973
|
+
node = parent;
|
|
974
|
+
continue;
|
|
975
|
+
}
|
|
976
|
+
const kind = LOOP_TYPES.get(parent.type);
|
|
977
|
+
// The header, not the body: a site is in the loop only when it is under it.
|
|
978
|
+
if (kind && !isField(parent, 'condition', node) && !isField(parent, 'value', node)) {
|
|
979
|
+
const text = loopHeader(parent);
|
|
980
|
+
if (text)
|
|
981
|
+
found.push({ text, kind, branch: branchKey(parent) });
|
|
982
|
+
}
|
|
983
|
+
node = parent;
|
|
984
|
+
}
|
|
985
|
+
found.reverse();
|
|
986
|
+
return found;
|
|
987
|
+
}
|
|
988
|
+
/** Loops for many sites in one file, keyed by {@link siteKey}. */
|
|
989
|
+
async function loopsForFile(absPath, language, sites) {
|
|
990
|
+
const out = new Map();
|
|
991
|
+
if (!supportsBranchGuards(language) || sites.length === 0)
|
|
992
|
+
return out;
|
|
993
|
+
const cached = await treeFor(absPath, language);
|
|
994
|
+
if (!cached)
|
|
995
|
+
return out;
|
|
996
|
+
for (const site of sites) {
|
|
997
|
+
const key = siteKey(site);
|
|
998
|
+
if (!out.has(key))
|
|
999
|
+
out.set(key, loopsInTree(cached.tree.rootNode, cached.source, language, site.line, site.column ?? null));
|
|
1000
|
+
}
|
|
1001
|
+
return out;
|
|
1002
|
+
}
|
|
1003
|
+
/** A loop's header as written, keyword and braces dropped: `item of items`, `queue.length > 0`. */
|
|
1004
|
+
function loopHeader(loop) {
|
|
1005
|
+
const body = loop.childForFieldName('body') ?? namedChildren(loop).find((c) => BLOCKISH.has(c.type)) ?? null;
|
|
1006
|
+
const raw = body && body.startIndex > loop.startIndex ? loop.text.slice(0, body.startIndex - loop.startIndex) : loop.text;
|
|
1007
|
+
let text = raw.replace(/\s+/g, ' ').trim();
|
|
1008
|
+
text = text.replace(/^(?:for|foreach|while|do|repeat)\b\s*/i, '');
|
|
1009
|
+
text = text.replace(/[{:]\s*$/, '').trim();
|
|
1010
|
+
const inner = /^\((.*)\)$/s.exec(text);
|
|
1011
|
+
if (inner)
|
|
1012
|
+
text = inner[1].trim();
|
|
1013
|
+
// `const item of items` reads as `item of items`; the binding word is noise here.
|
|
1014
|
+
text = text.replace(/^(?:const|let|var|val|final)\s+/, '');
|
|
1015
|
+
return cut(text, 60);
|
|
1016
|
+
}
|
|
1017
|
+
/**
|
|
1018
|
+
* The walk. `line` is 1-based, `column` 0-based (null → first non-blank).
|
|
1019
|
+
* Returns the guards outermost first — execution order, the way a reader
|
|
1020
|
+
* would list them.
|
|
1021
|
+
*/
|
|
1022
|
+
function guardsInTree(root, source, language, line, column) {
|
|
1023
|
+
const row = line - 1;
|
|
1024
|
+
if (row < 0)
|
|
1025
|
+
return [];
|
|
1026
|
+
let col = column ?? 0;
|
|
1027
|
+
if (column === null) {
|
|
1028
|
+
const text = source.split('\n')[row] ?? '';
|
|
1029
|
+
const first = text.search(/\S/);
|
|
1030
|
+
col = first < 0 ? 0 : first;
|
|
1031
|
+
}
|
|
1032
|
+
let node = innermostAt(root, row, col);
|
|
1033
|
+
if (!node)
|
|
1034
|
+
return [];
|
|
1035
|
+
const rules = RULES_BY_LANGUAGE.get(language);
|
|
1036
|
+
if (!rules)
|
|
1037
|
+
return [];
|
|
1038
|
+
const found = [];
|
|
1039
|
+
// Innermost → outermost. `found` is reversed at the end, so within one level
|
|
1040
|
+
// anything meant to read as OUTER must be pushed LATER.
|
|
1041
|
+
while (node) {
|
|
1042
|
+
const parent = node.parent;
|
|
1043
|
+
if (!parent || rules.boundaries.has(parent.type))
|
|
1044
|
+
break;
|
|
1045
|
+
if (rules.inlineFunctions.has(parent.type)) {
|
|
1046
|
+
const holder = parent.parent?.type ?? '';
|
|
1047
|
+
if (rules.bindingParents.has(holder))
|
|
1048
|
+
break;
|
|
1049
|
+
node = parent;
|
|
1050
|
+
continue;
|
|
1051
|
+
}
|
|
1052
|
+
// Anything `enclosing` pushed came from THIS construct, and the arm the
|
|
1053
|
+
// site is in is the child the walk came up through: stamp both, unless the
|
|
1054
|
+
// rule already named a different branch (a `switch` case's is the switch).
|
|
1055
|
+
const before = found.length;
|
|
1056
|
+
rules.enclosing(parent, node, found);
|
|
1057
|
+
for (let i = before; i < found.length; i++) {
|
|
1058
|
+
const g = found[i];
|
|
1059
|
+
if (!g.branch)
|
|
1060
|
+
g.branch = branchKey(parent);
|
|
1061
|
+
const arm = exitKind(node);
|
|
1062
|
+
if (arm && !g.armExit)
|
|
1063
|
+
g.armExit = arm;
|
|
1064
|
+
}
|
|
1065
|
+
if (rules.blocks.has(parent.type))
|
|
1066
|
+
rules.earlyExits(parent, node, found);
|
|
1067
|
+
node = parent;
|
|
1068
|
+
}
|
|
1069
|
+
found.reverse();
|
|
1070
|
+
return found;
|
|
1071
|
+
}
|
|
1072
|
+
/**
|
|
1073
|
+
* The innermost named node containing (row, col). `descendantForPosition` is
|
|
1074
|
+
* the fast path, but some grammars (Swift's `statements`) answer with the
|
|
1075
|
+
* container, so the result is refined by descending while a named child still
|
|
1076
|
+
* contains the point.
|
|
1077
|
+
*/
|
|
1078
|
+
function innermostAt(root, row, col) {
|
|
1079
|
+
let node = root.descendantForPosition({ row, column: col });
|
|
1080
|
+
if (!node)
|
|
1081
|
+
return null;
|
|
1082
|
+
for (;;) {
|
|
1083
|
+
let next = null;
|
|
1084
|
+
const here = node;
|
|
1085
|
+
for (let i = 0; i < here.namedChildCount; i++) {
|
|
1086
|
+
const c = here.namedChild(i);
|
|
1087
|
+
const s = c.startPosition;
|
|
1088
|
+
const e = c.endPosition;
|
|
1089
|
+
const afterStart = s.row < row || (s.row === row && s.column <= col);
|
|
1090
|
+
const beforeEnd = e.row > row || (e.row === row && e.column > col);
|
|
1091
|
+
if (afterStart && beforeEnd) {
|
|
1092
|
+
next = c;
|
|
1093
|
+
break;
|
|
1094
|
+
}
|
|
1095
|
+
}
|
|
1096
|
+
if (!next)
|
|
1097
|
+
return node;
|
|
1098
|
+
node = next;
|
|
1099
|
+
}
|
|
1100
|
+
}
|
|
1101
|
+
function condText(node) {
|
|
1102
|
+
if (!node)
|
|
1103
|
+
return '';
|
|
1104
|
+
let n = node;
|
|
1105
|
+
// `(x)` — the parens are the statement's, not the condition's.
|
|
1106
|
+
while (n.type === 'parenthesized_expression' && n.namedChildCount === 1)
|
|
1107
|
+
n = n.namedChild(0);
|
|
1108
|
+
const text = n.text.replace(/\s+/g, ' ').trim();
|
|
1109
|
+
// A bare keyword is a grammar's error recovery (`let` from an `if let` it
|
|
1110
|
+
// could not parse), never a condition a reader can use.
|
|
1111
|
+
if (/^(?:let|var|case|try|await|guard|if|else|some|any)$/.test(text))
|
|
1112
|
+
return '';
|
|
1113
|
+
return text.length > MAX_TEXT ? text.slice(0, MAX_TEXT - 1) + '…' : text;
|
|
1114
|
+
}
|
|
1115
|
+
/**
|
|
1116
|
+
* One guard. `branch` names the construct the arm belongs to: by default the
|
|
1117
|
+
* node the walk is climbing THROUGH (an `if`, a ternary, a `catch`), which is
|
|
1118
|
+
* right whenever the construct is the site's parent — and overridden where it
|
|
1119
|
+
* is not, by a `switch` case (whose parent is the case) and by an early exit
|
|
1120
|
+
* (whose branch is the `if` that returned, several statements back).
|
|
1121
|
+
*/
|
|
1122
|
+
function guard(form, cond, negated, text, branch, exit) {
|
|
1123
|
+
const t = text ?? condText(cond);
|
|
1124
|
+
if (!t)
|
|
1125
|
+
return null;
|
|
1126
|
+
return {
|
|
1127
|
+
text: t,
|
|
1128
|
+
negated,
|
|
1129
|
+
form,
|
|
1130
|
+
line: (cond ?? null) ? cond.startPosition.row + 1 : 0,
|
|
1131
|
+
branch: branch ? branchKey(branch) : '',
|
|
1132
|
+
...(exit ? { exit } : {}),
|
|
1133
|
+
};
|
|
1134
|
+
}
|
|
1135
|
+
/** A branching construct's identity: where it starts, `line:column`. */
|
|
1136
|
+
function branchKey(node) {
|
|
1137
|
+
return `${node.startPosition.row + 1}:${node.startPosition.column}`;
|
|
1138
|
+
}
|
|
1139
|
+
/**
|
|
1140
|
+
* Containers whose LAST statement decides how the whole thing leaves: a block,
|
|
1141
|
+
* and the clause wrappers a grammar puts an arm's block inside (`else_clause`,
|
|
1142
|
+
* `except_clause`) — the walk climbs through those, so the node it hands back
|
|
1143
|
+
* is the wrapper, not the block. A CONDITIONAL wrapper is not one of them: an
|
|
1144
|
+
* `elif` whose body raises does not mean the arm it is in always raises.
|
|
1145
|
+
*/
|
|
1146
|
+
const BLOCKISH = new Set([
|
|
1147
|
+
'statement_block',
|
|
1148
|
+
'block',
|
|
1149
|
+
'statements',
|
|
1150
|
+
'function_body',
|
|
1151
|
+
'compound_statement',
|
|
1152
|
+
'control_structure_body',
|
|
1153
|
+
'else_clause',
|
|
1154
|
+
'else_statement',
|
|
1155
|
+
'catch_clause',
|
|
1156
|
+
'catch_block',
|
|
1157
|
+
'except_clause',
|
|
1158
|
+
'finally_clause',
|
|
1159
|
+
]);
|
|
1160
|
+
/**
|
|
1161
|
+
* How a statement — or a block, by its last statement — leaves: `throw` for a
|
|
1162
|
+
* raised error, `return` for a return / break / continue, `exit` for a
|
|
1163
|
+
* language's other way out (Go's `panic`, C's `exit`) that the language rules
|
|
1164
|
+
* count as an exit but no keyword names. Null when it does not always leave.
|
|
1165
|
+
*/
|
|
1166
|
+
function exitKind(node) {
|
|
1167
|
+
if (!node)
|
|
1168
|
+
return null;
|
|
1169
|
+
const t = node.type;
|
|
1170
|
+
if (t === 'throw_statement' || t === 'raise_statement' || t === 'throw_expression')
|
|
1171
|
+
return 'throw';
|
|
1172
|
+
if (/^(?:return|break|continue|goto|yield)_statement$/.test(t))
|
|
1173
|
+
return 'return';
|
|
1174
|
+
// Swift's `control_transfer_statement` and Kotlin's `jump_expression` say
|
|
1175
|
+
// which in their first word.
|
|
1176
|
+
if (t === 'control_transfer_statement' || t === 'jump_expression')
|
|
1177
|
+
return /^\s*throw\b/.test(node.text) ? 'throw' : 'return';
|
|
1178
|
+
if (BLOCKISH.has(t))
|
|
1179
|
+
return exitKind(lastNamed(node));
|
|
1180
|
+
return null;
|
|
1181
|
+
}
|
|
1182
|
+
/** {@link exitKind}, or `exit` when the language's rules call it an exit and no keyword names it. */
|
|
1183
|
+
function exitKindOr(node, exits) {
|
|
1184
|
+
if (!exits)
|
|
1185
|
+
return null;
|
|
1186
|
+
return exitKind(node) ?? 'exit';
|
|
1187
|
+
}
|
|
1188
|
+
function push(out, g) {
|
|
1189
|
+
if (g)
|
|
1190
|
+
out.push(g);
|
|
1191
|
+
}
|
|
1192
|
+
function isField(parent, field, child) {
|
|
1193
|
+
const f = parent.childForFieldName(field);
|
|
1194
|
+
return !!f && f.id === child.id;
|
|
1195
|
+
}
|
|
1196
|
+
function lastNamed(node) {
|
|
1197
|
+
return node.namedChildCount > 0 ? node.namedChild(node.namedChildCount - 1) : null;
|
|
1198
|
+
}
|
|
1199
|
+
/** The named children of `parent` that come before `child`, in source order. */
|
|
1200
|
+
function precedingSiblings(parent, child) {
|
|
1201
|
+
const out = [];
|
|
1202
|
+
for (let i = 0; i < parent.namedChildCount; i++) {
|
|
1203
|
+
const s = parent.namedChild(i);
|
|
1204
|
+
if (s.id === child.id)
|
|
1205
|
+
break;
|
|
1206
|
+
out.push(s);
|
|
1207
|
+
}
|
|
1208
|
+
return out;
|
|
1209
|
+
}
|
|
1210
|
+
// ----------------------------------------------------------------------- JS --
|
|
1211
|
+
const JS_EXITS = new Set(['return_statement', 'throw_statement', 'break_statement', 'continue_statement']);
|
|
1212
|
+
/** A statement that always leaves the block: an exit, or a block ending in one. */
|
|
1213
|
+
function jsAlwaysExits(node) {
|
|
1214
|
+
if (!node)
|
|
1215
|
+
return false;
|
|
1216
|
+
if (JS_EXITS.has(node.type))
|
|
1217
|
+
return true;
|
|
1218
|
+
if (node.type === 'statement_block')
|
|
1219
|
+
return jsAlwaysExits(lastNamed(node));
|
|
1220
|
+
return false;
|
|
1221
|
+
}
|
|
1222
|
+
const JS = {
|
|
1223
|
+
boundaries: new Set([
|
|
1224
|
+
'function_declaration',
|
|
1225
|
+
'method_definition',
|
|
1226
|
+
'generator_function_declaration',
|
|
1227
|
+
'class_declaration',
|
|
1228
|
+
'class_body',
|
|
1229
|
+
'class',
|
|
1230
|
+
'program',
|
|
1231
|
+
]),
|
|
1232
|
+
inlineFunctions: new Set(['arrow_function', 'function_expression', 'function', 'generator_function']),
|
|
1233
|
+
bindingParents: new Set([
|
|
1234
|
+
'variable_declarator',
|
|
1235
|
+
'assignment_expression',
|
|
1236
|
+
'export_statement',
|
|
1237
|
+
'public_field_definition',
|
|
1238
|
+
'field_definition',
|
|
1239
|
+
'lexical_declaration',
|
|
1240
|
+
]),
|
|
1241
|
+
blocks: new Set(['statement_block', 'program', 'switch_case', 'switch_default']),
|
|
1242
|
+
enclosing(parent, child, out) {
|
|
1243
|
+
switch (parent.type) {
|
|
1244
|
+
case 'if_statement': {
|
|
1245
|
+
const cond = parent.childForFieldName('condition');
|
|
1246
|
+
if (isField(parent, 'consequence', child))
|
|
1247
|
+
push(out, guard('if', cond, false));
|
|
1248
|
+
else if (isField(parent, 'alternative', child))
|
|
1249
|
+
push(out, guard('else', cond, true));
|
|
1250
|
+
return;
|
|
1251
|
+
}
|
|
1252
|
+
case 'ternary_expression': {
|
|
1253
|
+
const cond = parent.childForFieldName('condition');
|
|
1254
|
+
if (isField(parent, 'consequence', child))
|
|
1255
|
+
push(out, guard('ternary', cond, false));
|
|
1256
|
+
else if (isField(parent, 'alternative', child))
|
|
1257
|
+
push(out, guard('ternary', cond, true));
|
|
1258
|
+
return;
|
|
1259
|
+
}
|
|
1260
|
+
case 'switch_case':
|
|
1261
|
+
case 'switch_default': {
|
|
1262
|
+
// `child` is one of the case's body statements (not its value).
|
|
1263
|
+
if (parent.type === 'switch_case' && isField(parent, 'value', child))
|
|
1264
|
+
return;
|
|
1265
|
+
const body = parent.parent; // switch_body
|
|
1266
|
+
const stmt = body?.parent; // switch_statement
|
|
1267
|
+
const subject = condText(stmt?.childForFieldName('value'));
|
|
1268
|
+
if (parent.type === 'switch_default')
|
|
1269
|
+
push(out, guard('case', stmt?.childForFieldName('value'), false, subject ? `${subject}: default` : 'default', stmt));
|
|
1270
|
+
else {
|
|
1271
|
+
const value = condText(parent.childForFieldName('value'));
|
|
1272
|
+
push(out, guard('case', parent.childForFieldName('value'), false, subject ? `${subject} === ${value}` : value, stmt));
|
|
1273
|
+
}
|
|
1274
|
+
return;
|
|
1275
|
+
}
|
|
1276
|
+
case 'binary_expression': {
|
|
1277
|
+
if (!isField(parent, 'right', child))
|
|
1278
|
+
return;
|
|
1279
|
+
const op = parent.childForFieldName('operator')?.text;
|
|
1280
|
+
const left = parent.childForFieldName('left');
|
|
1281
|
+
if (op === '&&')
|
|
1282
|
+
push(out, guard('and', left, false));
|
|
1283
|
+
else if (op === '||')
|
|
1284
|
+
push(out, guard('or', left, true));
|
|
1285
|
+
return;
|
|
1286
|
+
}
|
|
1287
|
+
case 'catch_clause':
|
|
1288
|
+
if (!isField(parent, 'parameter', child))
|
|
1289
|
+
push(out, guard('catch', null, false, 'on error'));
|
|
1290
|
+
return;
|
|
1291
|
+
default:
|
|
1292
|
+
return;
|
|
1293
|
+
}
|
|
1294
|
+
},
|
|
1295
|
+
earlyExits(parent, child, out) {
|
|
1296
|
+
// Outer-most last (the list is reversed once at the end): walk the
|
|
1297
|
+
// preceding statements backwards so the FIRST guard in the source ends up
|
|
1298
|
+
// first in the final order.
|
|
1299
|
+
const before = precedingSiblings(parent, child);
|
|
1300
|
+
for (let i = before.length - 1; i >= 0; i--) {
|
|
1301
|
+
const s = before[i];
|
|
1302
|
+
if (s.type !== 'if_statement' || s.childForFieldName('alternative'))
|
|
1303
|
+
continue;
|
|
1304
|
+
const body = s.childForFieldName('consequence');
|
|
1305
|
+
if (!jsAlwaysExits(body))
|
|
1306
|
+
continue;
|
|
1307
|
+
push(out, guard('guard', s.childForFieldName('condition'), true, undefined, s, exitKindOr(body, true)));
|
|
1308
|
+
}
|
|
1309
|
+
},
|
|
1310
|
+
};
|
|
1311
|
+
// -------------------------------------------------------------------- Swift --
|
|
1312
|
+
function swiftAlwaysExits(node) {
|
|
1313
|
+
if (!node)
|
|
1314
|
+
return false;
|
|
1315
|
+
if (node.type === 'control_transfer_statement')
|
|
1316
|
+
return true;
|
|
1317
|
+
if (node.type === 'statements')
|
|
1318
|
+
return swiftAlwaysExits(lastNamed(node));
|
|
1319
|
+
return false;
|
|
1320
|
+
}
|
|
1321
|
+
/** For a Swift `if`: is `child` after the `else` keyword? */
|
|
1322
|
+
function afterElse(parent, child) {
|
|
1323
|
+
let seenElse = false;
|
|
1324
|
+
for (let i = 0; i < parent.childCount; i++) {
|
|
1325
|
+
const c = parent.child(i);
|
|
1326
|
+
if (c.id === child.id)
|
|
1327
|
+
return seenElse;
|
|
1328
|
+
if (c.type === 'else')
|
|
1329
|
+
seenElse = true;
|
|
1330
|
+
}
|
|
1331
|
+
return false;
|
|
1332
|
+
}
|
|
1333
|
+
/** All `condition` fields of a Swift `if`/`guard`, joined — `if let x, y > 0`. */
|
|
1334
|
+
function swiftConditions(node) {
|
|
1335
|
+
// The grammar labels several tokens of `if let x = y, z > 0` as `condition`
|
|
1336
|
+
// (the binding's own pieces included), so the readable text is the SPAN from
|
|
1337
|
+
// the first to the last of them, not the pieces joined.
|
|
1338
|
+
const parts = [];
|
|
1339
|
+
for (let i = 0; i < node.childCount; i++) {
|
|
1340
|
+
if (node.fieldNameForChild(i) === 'condition')
|
|
1341
|
+
parts.push(node.child(i));
|
|
1342
|
+
}
|
|
1343
|
+
if (parts.length === 0)
|
|
1344
|
+
return { node: null, text: '' };
|
|
1345
|
+
const first = parts[0];
|
|
1346
|
+
const last = parts[parts.length - 1];
|
|
1347
|
+
const raw = node.text.slice(first.startIndex - node.startIndex, last.endIndex - node.startIndex);
|
|
1348
|
+
const text = raw.replace(/\s+/g, ' ').trim();
|
|
1349
|
+
if (/^(?:let|var|case|try|await)$/.test(text))
|
|
1350
|
+
return { node: null, text: '' };
|
|
1351
|
+
return { node: first, text: text.length > MAX_TEXT ? text.slice(0, MAX_TEXT - 1) + '…' : text };
|
|
1352
|
+
}
|
|
1353
|
+
const SWIFT = {
|
|
1354
|
+
boundaries: new Set([
|
|
1355
|
+
'function_declaration',
|
|
1356
|
+
'init_declaration',
|
|
1357
|
+
'deinit_declaration',
|
|
1358
|
+
'class_declaration',
|
|
1359
|
+
'protocol_declaration',
|
|
1360
|
+
'computed_property',
|
|
1361
|
+
'source_file',
|
|
1362
|
+
]),
|
|
1363
|
+
inlineFunctions: new Set(['lambda_literal']),
|
|
1364
|
+
bindingParents: new Set(['property_declaration', 'assignment']),
|
|
1365
|
+
blocks: new Set(['statements', 'function_body']),
|
|
1366
|
+
enclosing(parent, child, out) {
|
|
1367
|
+
switch (parent.type) {
|
|
1368
|
+
case 'if_statement': {
|
|
1369
|
+
const c = swiftConditions(parent);
|
|
1370
|
+
if (parent.fieldNameForChild(indexOf(parent, child)) === 'condition')
|
|
1371
|
+
return;
|
|
1372
|
+
push(out, guard(afterElse(parent, child) ? 'else' : 'if', c.node, afterElse(parent, child), c.text));
|
|
1373
|
+
return;
|
|
1374
|
+
}
|
|
1375
|
+
case 'guard_statement': {
|
|
1376
|
+
// Inside the guard's body the condition FAILED.
|
|
1377
|
+
if (parent.fieldNameForChild(indexOf(parent, child)) === 'condition')
|
|
1378
|
+
return;
|
|
1379
|
+
const c = swiftConditions(parent);
|
|
1380
|
+
push(out, guard('else', c.node, true, c.text));
|
|
1381
|
+
return;
|
|
1382
|
+
}
|
|
1383
|
+
case 'ternary_expression': {
|
|
1384
|
+
const cond = parent.childForFieldName('condition');
|
|
1385
|
+
if (isField(parent, 'if_true', child))
|
|
1386
|
+
push(out, guard('ternary', cond, false));
|
|
1387
|
+
else if (isField(parent, 'if_false', child))
|
|
1388
|
+
push(out, guard('ternary', cond, true));
|
|
1389
|
+
return;
|
|
1390
|
+
}
|
|
1391
|
+
case 'switch_entry': {
|
|
1392
|
+
const stmt = parent.parent;
|
|
1393
|
+
const subject = condText(stmt?.childForFieldName('expr'));
|
|
1394
|
+
const pattern = parent.namedChildren.find((n) => n.type === 'switch_pattern');
|
|
1395
|
+
if (pattern && pattern.id === child.id)
|
|
1396
|
+
return;
|
|
1397
|
+
const isDefault = parent.children.some((n) => n.type === 'default_keyword');
|
|
1398
|
+
const value = pattern ? condText(pattern) : '';
|
|
1399
|
+
const text = isDefault ? (subject ? `${subject}: default` : 'default') : subject ? `${subject} == ${value}` : value;
|
|
1400
|
+
push(out, guard('case', pattern ?? stmt?.childForFieldName('expr'), false, text, stmt));
|
|
1401
|
+
return;
|
|
1402
|
+
}
|
|
1403
|
+
case 'catch_block':
|
|
1404
|
+
push(out, guard('catch', null, false, 'on error'));
|
|
1405
|
+
return;
|
|
1406
|
+
default:
|
|
1407
|
+
return;
|
|
1408
|
+
}
|
|
1409
|
+
},
|
|
1410
|
+
earlyExits(parent, child, out) {
|
|
1411
|
+
const before = precedingSiblings(parent, child);
|
|
1412
|
+
for (let i = before.length - 1; i >= 0; i--) {
|
|
1413
|
+
const s = before[i];
|
|
1414
|
+
if (s.type === 'guard_statement') {
|
|
1415
|
+
const c = swiftConditions(s);
|
|
1416
|
+
const body = s.namedChildren.find((n) => n.type === 'statements') ?? null;
|
|
1417
|
+
push(out, guard('guard', c.node, false, c.text, s, exitKindOr(body, true)));
|
|
1418
|
+
}
|
|
1419
|
+
else if (s.type === 'if_statement' && !s.children.some((n) => n.type === 'else')) {
|
|
1420
|
+
const body = s.namedChildren.find((n) => n.type === 'statements') ?? null;
|
|
1421
|
+
if (!swiftAlwaysExits(body))
|
|
1422
|
+
continue;
|
|
1423
|
+
const c = swiftConditions(s);
|
|
1424
|
+
push(out, guard('guard', c.node, true, c.text, s, exitKindOr(body, true)));
|
|
1425
|
+
}
|
|
1426
|
+
}
|
|
1427
|
+
},
|
|
1428
|
+
};
|
|
1429
|
+
function indexOf(parent, child) {
|
|
1430
|
+
for (let i = 0; i < parent.childCount; i++)
|
|
1431
|
+
if (parent.child(i).id === child.id)
|
|
1432
|
+
return i;
|
|
1433
|
+
return -1;
|
|
1434
|
+
}
|
|
1435
|
+
/** The named children of a node, in order. */
|
|
1436
|
+
function namedChildren(node) {
|
|
1437
|
+
const out = [];
|
|
1438
|
+
for (let i = 0; i < node.namedChildCount; i++)
|
|
1439
|
+
out.push(node.namedChild(i));
|
|
1440
|
+
return out;
|
|
1441
|
+
}
|
|
1442
|
+
/**
|
|
1443
|
+
* Shared shape of "an early exit before the site": the preceding statements
|
|
1444
|
+
* of the block that are an `if` with no else whose body always leaves.
|
|
1445
|
+
*/
|
|
1446
|
+
function guardsBefore(parent, child, out, isIf, hasElse, body, alwaysExits, condition) {
|
|
1447
|
+
const before = precedingSiblings(parent, child);
|
|
1448
|
+
for (let i = before.length - 1; i >= 0; i--) {
|
|
1449
|
+
const s = before[i];
|
|
1450
|
+
if (!isIf(s) || hasElse(s))
|
|
1451
|
+
continue;
|
|
1452
|
+
const arm = body(s);
|
|
1453
|
+
if (!alwaysExits(arm))
|
|
1454
|
+
continue;
|
|
1455
|
+
const c = condition(s);
|
|
1456
|
+
push(out, guard('guard', c.node, true, c.text, s, exitKindOr(arm, true)));
|
|
1457
|
+
}
|
|
1458
|
+
}
|
|
1459
|
+
// ------------------------------------------------------------------- Python --
|
|
1460
|
+
const PY_EXITS = new Set(['return_statement', 'raise_statement', 'break_statement', 'continue_statement']);
|
|
1461
|
+
function pyAlwaysExits(node) {
|
|
1462
|
+
if (!node)
|
|
1463
|
+
return false;
|
|
1464
|
+
if (PY_EXITS.has(node.type))
|
|
1465
|
+
return true;
|
|
1466
|
+
if (node.type === 'block')
|
|
1467
|
+
return pyAlwaysExits(lastNamed(node));
|
|
1468
|
+
return false;
|
|
1469
|
+
}
|
|
1470
|
+
function pyOperator(node) {
|
|
1471
|
+
const field = node.childForFieldName('operator');
|
|
1472
|
+
if (field)
|
|
1473
|
+
return field.text;
|
|
1474
|
+
for (let i = 0; i < node.childCount; i++) {
|
|
1475
|
+
const c = node.child(i);
|
|
1476
|
+
if (!c.isNamed && (c.text === 'and' || c.text === 'or'))
|
|
1477
|
+
return c.text;
|
|
1478
|
+
}
|
|
1479
|
+
return '';
|
|
1480
|
+
}
|
|
1481
|
+
const PYTHON = {
|
|
1482
|
+
boundaries: new Set(['function_definition', 'class_definition', 'module']),
|
|
1483
|
+
inlineFunctions: new Set(['lambda']),
|
|
1484
|
+
bindingParents: new Set(['assignment', 'augmented_assignment']),
|
|
1485
|
+
blocks: new Set(['block', 'module']),
|
|
1486
|
+
enclosing(parent, child, out) {
|
|
1487
|
+
switch (parent.type) {
|
|
1488
|
+
case 'if_statement': {
|
|
1489
|
+
const cond = parent.childForFieldName('condition');
|
|
1490
|
+
if (isField(parent, 'consequence', child))
|
|
1491
|
+
push(out, guard('if', cond, false));
|
|
1492
|
+
else if (child.type === 'elif_clause' || child.type === 'else_clause') {
|
|
1493
|
+
// An elif/else arm runs when the `if` and every earlier elif failed.
|
|
1494
|
+
push(out, guard('else', cond, true));
|
|
1495
|
+
for (const s of precedingSiblings(parent, child)) {
|
|
1496
|
+
if (s.type === 'elif_clause')
|
|
1497
|
+
push(out, guard('else', s.childForFieldName('condition'), true));
|
|
1498
|
+
}
|
|
1499
|
+
}
|
|
1500
|
+
return;
|
|
1501
|
+
}
|
|
1502
|
+
case 'elif_clause': {
|
|
1503
|
+
if (isField(parent, 'consequence', child))
|
|
1504
|
+
push(out, guard('if', parent.childForFieldName('condition'), false));
|
|
1505
|
+
return;
|
|
1506
|
+
}
|
|
1507
|
+
case 'conditional_expression': {
|
|
1508
|
+
// `a if cond else b`: children are [a, cond, b].
|
|
1509
|
+
const kids = namedChildren(parent);
|
|
1510
|
+
if (kids.length < 3)
|
|
1511
|
+
return;
|
|
1512
|
+
if (child.id === kids[0].id)
|
|
1513
|
+
push(out, guard('ternary', kids[1], false));
|
|
1514
|
+
else if (child.id === kids[2].id)
|
|
1515
|
+
push(out, guard('ternary', kids[1], true));
|
|
1516
|
+
return;
|
|
1517
|
+
}
|
|
1518
|
+
case 'case_clause': {
|
|
1519
|
+
if (isField(parent, 'consequence', child)) {
|
|
1520
|
+
const stmt = parent.parent?.parent;
|
|
1521
|
+
const subject = condText(stmt?.childForFieldName('subject'));
|
|
1522
|
+
const pattern = namedChildren(parent).find((n) => n.type === 'case_pattern');
|
|
1523
|
+
const value = pattern ? condText(pattern) : '';
|
|
1524
|
+
const isDefault = value === '_' || value === '';
|
|
1525
|
+
const text = isDefault ? (subject ? `${subject}: default` : 'default') : subject ? `${subject} == ${value}` : value;
|
|
1526
|
+
push(out, guard('case', pattern ?? null, false, text, stmt));
|
|
1527
|
+
}
|
|
1528
|
+
return;
|
|
1529
|
+
}
|
|
1530
|
+
case 'boolean_operator': {
|
|
1531
|
+
if (!isField(parent, 'right', child))
|
|
1532
|
+
return;
|
|
1533
|
+
const op = pyOperator(parent);
|
|
1534
|
+
const left = parent.childForFieldName('left');
|
|
1535
|
+
if (op === 'and')
|
|
1536
|
+
push(out, guard('and', left, false));
|
|
1537
|
+
else if (op === 'or')
|
|
1538
|
+
push(out, guard('or', left, true));
|
|
1539
|
+
return;
|
|
1540
|
+
}
|
|
1541
|
+
case 'except_clause':
|
|
1542
|
+
case 'except_group_clause':
|
|
1543
|
+
if (child.type === 'block')
|
|
1544
|
+
push(out, guard('catch', null, false, 'on error'));
|
|
1545
|
+
return;
|
|
1546
|
+
default:
|
|
1547
|
+
return;
|
|
1548
|
+
}
|
|
1549
|
+
},
|
|
1550
|
+
earlyExits(parent, child, out) {
|
|
1551
|
+
guardsBefore(parent, child, out, (s) => s.type === 'if_statement', (s) => namedChildren(s).some((n) => n.type === 'elif_clause' || n.type === 'else_clause'), (s) => s.childForFieldName('consequence'), pyAlwaysExits, (s) => ({ node: s.childForFieldName('condition'), text: condText(s.childForFieldName('condition')) }));
|
|
1552
|
+
},
|
|
1553
|
+
};
|
|
1554
|
+
// --------------------------------------------------------------------- Java --
|
|
1555
|
+
const JAVA_EXITS = new Set(['return_statement', 'throw_statement', 'break_statement', 'continue_statement', 'yield_statement']);
|
|
1556
|
+
function javaAlwaysExits(node) {
|
|
1557
|
+
if (!node)
|
|
1558
|
+
return false;
|
|
1559
|
+
if (JAVA_EXITS.has(node.type))
|
|
1560
|
+
return true;
|
|
1561
|
+
if (node.type === 'block')
|
|
1562
|
+
return javaAlwaysExits(lastNamed(node));
|
|
1563
|
+
return false;
|
|
1564
|
+
}
|
|
1565
|
+
/** `case A:` / `case A ->` / `default` labels of a Java switch group or rule, as one condition text. */
|
|
1566
|
+
function javaCaseText(labels, subject) {
|
|
1567
|
+
const values = labels.map((l) => namedChildren(l).map((n) => condText(n)).filter(Boolean).join(', ')).filter(Boolean);
|
|
1568
|
+
if (values.length === 0)
|
|
1569
|
+
return subject ? `${subject}: default` : 'default';
|
|
1570
|
+
const value = values.join(', ');
|
|
1571
|
+
return subject ? `${subject} == ${value}` : value;
|
|
1572
|
+
}
|
|
1573
|
+
const JAVA = {
|
|
1574
|
+
boundaries: new Set([
|
|
1575
|
+
'method_declaration',
|
|
1576
|
+
'constructor_declaration',
|
|
1577
|
+
'class_declaration',
|
|
1578
|
+
'class_body',
|
|
1579
|
+
'interface_declaration',
|
|
1580
|
+
'enum_declaration',
|
|
1581
|
+
'record_declaration',
|
|
1582
|
+
'program',
|
|
1583
|
+
]),
|
|
1584
|
+
inlineFunctions: new Set(['lambda_expression']),
|
|
1585
|
+
bindingParents: new Set(['variable_declarator', 'assignment_expression', 'field_declaration']),
|
|
1586
|
+
blocks: new Set(['block', 'switch_block_statement_group', 'program', 'constructor_body']),
|
|
1587
|
+
enclosing(parent, child, out) {
|
|
1588
|
+
switch (parent.type) {
|
|
1589
|
+
case 'if_statement': {
|
|
1590
|
+
const cond = parent.childForFieldName('condition');
|
|
1591
|
+
if (isField(parent, 'consequence', child))
|
|
1592
|
+
push(out, guard('if', cond, false));
|
|
1593
|
+
else if (isField(parent, 'alternative', child))
|
|
1594
|
+
push(out, guard('else', cond, true));
|
|
1595
|
+
return;
|
|
1596
|
+
}
|
|
1597
|
+
case 'ternary_expression': {
|
|
1598
|
+
const cond = parent.childForFieldName('condition');
|
|
1599
|
+
if (isField(parent, 'consequence', child))
|
|
1600
|
+
push(out, guard('ternary', cond, false));
|
|
1601
|
+
else if (isField(parent, 'alternative', child))
|
|
1602
|
+
push(out, guard('ternary', cond, true));
|
|
1603
|
+
return;
|
|
1604
|
+
}
|
|
1605
|
+
case 'switch_block_statement_group':
|
|
1606
|
+
case 'switch_rule': {
|
|
1607
|
+
if (child.type === 'switch_label')
|
|
1608
|
+
return;
|
|
1609
|
+
const stmt = parent.parent?.parent;
|
|
1610
|
+
const subject = condText(stmt?.childForFieldName('condition'));
|
|
1611
|
+
const labels = namedChildren(parent).filter((n) => n.type === 'switch_label');
|
|
1612
|
+
push(out, guard('case', labels[0] ?? null, false, javaCaseText(labels, subject), stmt));
|
|
1613
|
+
return;
|
|
1614
|
+
}
|
|
1615
|
+
case 'binary_expression': {
|
|
1616
|
+
if (!isField(parent, 'right', child))
|
|
1617
|
+
return;
|
|
1618
|
+
const op = parent.childForFieldName('operator')?.text;
|
|
1619
|
+
const left = parent.childForFieldName('left');
|
|
1620
|
+
if (op === '&&')
|
|
1621
|
+
push(out, guard('and', left, false));
|
|
1622
|
+
else if (op === '||')
|
|
1623
|
+
push(out, guard('or', left, true));
|
|
1624
|
+
return;
|
|
1625
|
+
}
|
|
1626
|
+
case 'catch_clause':
|
|
1627
|
+
if (isField(parent, 'body', child))
|
|
1628
|
+
push(out, guard('catch', null, false, 'on error'));
|
|
1629
|
+
return;
|
|
1630
|
+
default:
|
|
1631
|
+
return;
|
|
1632
|
+
}
|
|
1633
|
+
},
|
|
1634
|
+
earlyExits(parent, child, out) {
|
|
1635
|
+
guardsBefore(parent, child, out, (s) => s.type === 'if_statement', (s) => !!s.childForFieldName('alternative'), (s) => s.childForFieldName('consequence'), javaAlwaysExits, (s) => ({ node: s.childForFieldName('condition'), text: condText(s.childForFieldName('condition')) }));
|
|
1636
|
+
},
|
|
1637
|
+
};
|
|
1638
|
+
// ------------------------------------------------------------------- Kotlin --
|
|
1639
|
+
function ktAlwaysExits(node) {
|
|
1640
|
+
if (!node)
|
|
1641
|
+
return false;
|
|
1642
|
+
if (node.type === 'jump_expression')
|
|
1643
|
+
return true;
|
|
1644
|
+
if (node.type === 'control_structure_body' || node.type === 'statements')
|
|
1645
|
+
return ktAlwaysExits(lastNamed(node));
|
|
1646
|
+
return false;
|
|
1647
|
+
}
|
|
1648
|
+
/** The two arms of a Kotlin `if`: the bodies, in order (then, else). */
|
|
1649
|
+
function ktArms(ifExpr) {
|
|
1650
|
+
return namedChildren(ifExpr).filter((n) => n.type === 'control_structure_body');
|
|
1651
|
+
}
|
|
1652
|
+
const KOTLIN = {
|
|
1653
|
+
boundaries: new Set([
|
|
1654
|
+
'function_declaration',
|
|
1655
|
+
'secondary_constructor',
|
|
1656
|
+
'class_declaration',
|
|
1657
|
+
'class_body',
|
|
1658
|
+
'object_declaration',
|
|
1659
|
+
'getter',
|
|
1660
|
+
'setter',
|
|
1661
|
+
'source_file',
|
|
1662
|
+
]),
|
|
1663
|
+
inlineFunctions: new Set(['lambda_literal', 'anonymous_function']),
|
|
1664
|
+
bindingParents: new Set(['property_declaration', 'assignment']),
|
|
1665
|
+
blocks: new Set(['statements', 'function_body', 'source_file']),
|
|
1666
|
+
enclosing(parent, child, out) {
|
|
1667
|
+
switch (parent.type) {
|
|
1668
|
+
case 'if_expression': {
|
|
1669
|
+
const kids = namedChildren(parent);
|
|
1670
|
+
const cond = kids[0] ?? null;
|
|
1671
|
+
if (cond && child.id === cond.id)
|
|
1672
|
+
return;
|
|
1673
|
+
const arms = ktArms(parent);
|
|
1674
|
+
if (arms[0] && child.id === arms[0].id)
|
|
1675
|
+
push(out, guard('if', cond, false));
|
|
1676
|
+
else if (arms[1] && child.id === arms[1].id)
|
|
1677
|
+
push(out, guard('else', cond, true));
|
|
1678
|
+
return;
|
|
1679
|
+
}
|
|
1680
|
+
case 'when_entry': {
|
|
1681
|
+
if (child.type !== 'control_structure_body')
|
|
1682
|
+
return;
|
|
1683
|
+
const when = parent.parent;
|
|
1684
|
+
const subject = condText(namedChildren(when).find((n) => n.type === 'when_subject')).replace(/^\((.*)\)$/, '$1');
|
|
1685
|
+
const conds = namedChildren(parent).filter((n) => n.type === 'when_condition');
|
|
1686
|
+
if (conds.length === 0)
|
|
1687
|
+
push(out, guard('case', null, false, subject ? `${subject}: else` : 'else', when));
|
|
1688
|
+
else {
|
|
1689
|
+
const value = conds.map((c) => condText(c)).join(', ');
|
|
1690
|
+
push(out, guard('case', conds[0], false, subject ? `${subject} == ${value}` : value, when));
|
|
1691
|
+
}
|
|
1692
|
+
return;
|
|
1693
|
+
}
|
|
1694
|
+
case 'conjunction_expression':
|
|
1695
|
+
case 'disjunction_expression': {
|
|
1696
|
+
const kids = namedChildren(parent);
|
|
1697
|
+
if (kids.length < 2 || child.id !== kids[kids.length - 1].id)
|
|
1698
|
+
return;
|
|
1699
|
+
const left = kids[0];
|
|
1700
|
+
if (parent.type === 'conjunction_expression')
|
|
1701
|
+
push(out, guard('and', left, false));
|
|
1702
|
+
else
|
|
1703
|
+
push(out, guard('or', left, true));
|
|
1704
|
+
return;
|
|
1705
|
+
}
|
|
1706
|
+
case 'catch_block':
|
|
1707
|
+
if (child.type === 'statements')
|
|
1708
|
+
push(out, guard('catch', null, false, 'on error'));
|
|
1709
|
+
return;
|
|
1710
|
+
default:
|
|
1711
|
+
return;
|
|
1712
|
+
}
|
|
1713
|
+
},
|
|
1714
|
+
earlyExits(parent, child, out) {
|
|
1715
|
+
guardsBefore(parent, child, out, (s) => s.type === 'if_expression', (s) => ktArms(s).length > 1, (s) => ktArms(s)[0] ?? null, ktAlwaysExits, (s) => {
|
|
1716
|
+
const cond = namedChildren(s)[0] ?? null;
|
|
1717
|
+
return { node: cond, text: condText(cond) };
|
|
1718
|
+
});
|
|
1719
|
+
},
|
|
1720
|
+
};
|
|
1721
|
+
// ----------------------------------------------------------------------- C# --
|
|
1722
|
+
const CS_EXITS = new Set(['return_statement', 'throw_statement', 'break_statement', 'continue_statement']);
|
|
1723
|
+
function csAlwaysExits(node) {
|
|
1724
|
+
if (!node)
|
|
1725
|
+
return false;
|
|
1726
|
+
if (CS_EXITS.has(node.type))
|
|
1727
|
+
return true;
|
|
1728
|
+
if (node.type === 'block')
|
|
1729
|
+
return csAlwaysExits(lastNamed(node));
|
|
1730
|
+
return false;
|
|
1731
|
+
}
|
|
1732
|
+
const CSHARP = {
|
|
1733
|
+
boundaries: new Set([
|
|
1734
|
+
'method_declaration',
|
|
1735
|
+
'constructor_declaration',
|
|
1736
|
+
'local_function_statement',
|
|
1737
|
+
'class_declaration',
|
|
1738
|
+
'struct_declaration',
|
|
1739
|
+
'record_declaration',
|
|
1740
|
+
'interface_declaration',
|
|
1741
|
+
'declaration_list',
|
|
1742
|
+
'property_declaration',
|
|
1743
|
+
'accessor_declaration',
|
|
1744
|
+
'compilation_unit',
|
|
1745
|
+
]),
|
|
1746
|
+
inlineFunctions: new Set(['lambda_expression', 'anonymous_method_expression']),
|
|
1747
|
+
bindingParents: new Set(['variable_declarator', 'assignment_expression', 'equals_value_clause']),
|
|
1748
|
+
blocks: new Set(['block', 'switch_section', 'compilation_unit']),
|
|
1749
|
+
enclosing(parent, child, out) {
|
|
1750
|
+
switch (parent.type) {
|
|
1751
|
+
case 'if_statement': {
|
|
1752
|
+
const cond = parent.childForFieldName('condition');
|
|
1753
|
+
if (isField(parent, 'consequence', child))
|
|
1754
|
+
push(out, guard('if', cond, false));
|
|
1755
|
+
else if (isField(parent, 'alternative', child))
|
|
1756
|
+
push(out, guard('else', cond, true));
|
|
1757
|
+
return;
|
|
1758
|
+
}
|
|
1759
|
+
case 'conditional_expression': {
|
|
1760
|
+
const cond = parent.childForFieldName('condition');
|
|
1761
|
+
if (isField(parent, 'consequence', child))
|
|
1762
|
+
push(out, guard('ternary', cond, false));
|
|
1763
|
+
else if (isField(parent, 'alternative', child))
|
|
1764
|
+
push(out, guard('ternary', cond, true));
|
|
1765
|
+
return;
|
|
1766
|
+
}
|
|
1767
|
+
case 'switch_section': {
|
|
1768
|
+
const isLabel = (n) => /pattern$|switch_label$/.test(n.type);
|
|
1769
|
+
if (isLabel(child))
|
|
1770
|
+
return;
|
|
1771
|
+
const stmt = parent.parent?.parent;
|
|
1772
|
+
const subject = condText(stmt?.childForFieldName('value'));
|
|
1773
|
+
const labels = namedChildren(parent).filter(isLabel);
|
|
1774
|
+
const value = labels.map((l) => condText(l)).filter(Boolean).join(', ');
|
|
1775
|
+
const text = value === '' ? (subject ? `${subject}: default` : 'default') : subject ? `${subject} == ${value}` : value;
|
|
1776
|
+
push(out, guard('case', labels[0] ?? null, false, text, stmt));
|
|
1777
|
+
return;
|
|
1778
|
+
}
|
|
1779
|
+
case 'switch_expression_arm': {
|
|
1780
|
+
if (!isField(parent, 'expression', child))
|
|
1781
|
+
return;
|
|
1782
|
+
const stmt = parent.parent;
|
|
1783
|
+
const subject = condText(stmt?.childForFieldName('value'));
|
|
1784
|
+
const pattern = parent.childForFieldName('pattern');
|
|
1785
|
+
const value = condText(pattern);
|
|
1786
|
+
const text = value === '_' || value === '' ? (subject ? `${subject}: default` : 'default') : subject ? `${subject} == ${value}` : value;
|
|
1787
|
+
push(out, guard('case', pattern, false, text, stmt));
|
|
1788
|
+
return;
|
|
1789
|
+
}
|
|
1790
|
+
case 'binary_expression': {
|
|
1791
|
+
if (!isField(parent, 'right', child))
|
|
1792
|
+
return;
|
|
1793
|
+
const op = parent.childForFieldName('operator')?.text;
|
|
1794
|
+
const left = parent.childForFieldName('left');
|
|
1795
|
+
if (op === '&&')
|
|
1796
|
+
push(out, guard('and', left, false));
|
|
1797
|
+
else if (op === '||')
|
|
1798
|
+
push(out, guard('or', left, true));
|
|
1799
|
+
return;
|
|
1800
|
+
}
|
|
1801
|
+
case 'catch_clause':
|
|
1802
|
+
if (isField(parent, 'body', child))
|
|
1803
|
+
push(out, guard('catch', null, false, 'on error'));
|
|
1804
|
+
return;
|
|
1805
|
+
default:
|
|
1806
|
+
return;
|
|
1807
|
+
}
|
|
1808
|
+
},
|
|
1809
|
+
earlyExits(parent, child, out) {
|
|
1810
|
+
guardsBefore(parent, child, out, (s) => s.type === 'if_statement', (s) => !!s.childForFieldName('alternative'), (s) => s.childForFieldName('consequence'), csAlwaysExits, (s) => ({ node: s.childForFieldName('condition'), text: condText(s.childForFieldName('condition')) }));
|
|
1811
|
+
},
|
|
1812
|
+
};
|
|
1813
|
+
// ----------------------------------------------------------------------- Go --
|
|
1814
|
+
const GO_EXITS = new Set(['return_statement', 'break_statement', 'continue_statement', 'goto_statement']);
|
|
1815
|
+
function goAlwaysExits(node) {
|
|
1816
|
+
if (!node)
|
|
1817
|
+
return false;
|
|
1818
|
+
if (GO_EXITS.has(node.type))
|
|
1819
|
+
return true;
|
|
1820
|
+
if (node.type === 'block')
|
|
1821
|
+
return goAlwaysExits(lastNamed(node));
|
|
1822
|
+
if (node.type === 'expression_statement') {
|
|
1823
|
+
const call = node.namedChild(0);
|
|
1824
|
+
const fn = call?.type === 'call_expression' ? call.childForFieldName('function')?.text : '';
|
|
1825
|
+
return fn === 'panic' || fn === 'os.Exit' || fn === 'log.Fatal' || fn === 'log.Fatalf' || fn === 'log.Fatalln';
|
|
1826
|
+
}
|
|
1827
|
+
return false;
|
|
1828
|
+
}
|
|
1829
|
+
const GO = {
|
|
1830
|
+
boundaries: new Set(['function_declaration', 'method_declaration', 'source_file']),
|
|
1831
|
+
inlineFunctions: new Set(['func_literal']),
|
|
1832
|
+
bindingParents: new Set(['short_var_declaration', 'var_spec', 'assignment_statement', 'const_spec']),
|
|
1833
|
+
blocks: new Set(['block', 'expression_case', 'default_case', 'type_case', 'communication_case', 'source_file']),
|
|
1834
|
+
enclosing(parent, child, out) {
|
|
1835
|
+
switch (parent.type) {
|
|
1836
|
+
case 'if_statement': {
|
|
1837
|
+
const cond = parent.childForFieldName('condition');
|
|
1838
|
+
if (isField(parent, 'consequence', child))
|
|
1839
|
+
push(out, guard('if', cond, false));
|
|
1840
|
+
else if (isField(parent, 'alternative', child))
|
|
1841
|
+
push(out, guard('else', cond, true));
|
|
1842
|
+
return;
|
|
1843
|
+
}
|
|
1844
|
+
case 'expression_case':
|
|
1845
|
+
case 'type_case':
|
|
1846
|
+
case 'communication_case': {
|
|
1847
|
+
const value = parent.childForFieldName('value') ?? parent.childForFieldName('type') ?? parent.childForFieldName('communication');
|
|
1848
|
+
if (value && child.id === value.id)
|
|
1849
|
+
return;
|
|
1850
|
+
const stmt = parent.parent;
|
|
1851
|
+
const subject = condText(stmt?.childForFieldName('value'));
|
|
1852
|
+
const v = condText(value);
|
|
1853
|
+
if (parent.type === 'communication_case')
|
|
1854
|
+
push(out, guard('case', value, false, v, stmt));
|
|
1855
|
+
else
|
|
1856
|
+
push(out, guard('case', value, false, subject ? `${subject} == ${v}` : v, stmt));
|
|
1857
|
+
return;
|
|
1858
|
+
}
|
|
1859
|
+
case 'default_case': {
|
|
1860
|
+
const stmt = parent.parent;
|
|
1861
|
+
const subject = condText(stmt?.childForFieldName('value'));
|
|
1862
|
+
push(out, guard('case', stmt?.childForFieldName('value'), false, subject ? `${subject}: default` : 'default', stmt));
|
|
1863
|
+
return;
|
|
1864
|
+
}
|
|
1865
|
+
case 'binary_expression': {
|
|
1866
|
+
if (!isField(parent, 'right', child))
|
|
1867
|
+
return;
|
|
1868
|
+
const op = parent.childForFieldName('operator')?.text;
|
|
1869
|
+
const left = parent.childForFieldName('left');
|
|
1870
|
+
if (op === '&&')
|
|
1871
|
+
push(out, guard('and', left, false));
|
|
1872
|
+
else if (op === '||')
|
|
1873
|
+
push(out, guard('or', left, true));
|
|
1874
|
+
return;
|
|
1875
|
+
}
|
|
1876
|
+
default:
|
|
1877
|
+
return;
|
|
1878
|
+
}
|
|
1879
|
+
},
|
|
1880
|
+
earlyExits(parent, child, out) {
|
|
1881
|
+
guardsBefore(parent, child, out, (s) => s.type === 'if_statement', (s) => !!s.childForFieldName('alternative'), (s) => s.childForFieldName('consequence'), goAlwaysExits, (s) => ({ node: s.childForFieldName('condition'), text: condText(s.childForFieldName('condition')) }));
|
|
1882
|
+
},
|
|
1883
|
+
};
|
|
1884
|
+
// -------------------------------------------------------------------- C/C++ --
|
|
1885
|
+
const C_EXITS = new Set(['return_statement', 'break_statement', 'continue_statement', 'goto_statement', 'throw_statement']);
|
|
1886
|
+
function cAlwaysExits(node) {
|
|
1887
|
+
if (!node)
|
|
1888
|
+
return false;
|
|
1889
|
+
if (C_EXITS.has(node.type))
|
|
1890
|
+
return true;
|
|
1891
|
+
if (node.type === 'compound_statement')
|
|
1892
|
+
return cAlwaysExits(lastNamed(node));
|
|
1893
|
+
if (node.type === 'expression_statement') {
|
|
1894
|
+
const call = node.namedChild(0);
|
|
1895
|
+
const fn = call?.type === 'call_expression' ? call.childForFieldName('function')?.text : '';
|
|
1896
|
+
return fn === 'exit' || fn === '_exit' || fn === 'abort' || fn === 'longjmp';
|
|
1897
|
+
}
|
|
1898
|
+
return false;
|
|
1899
|
+
}
|
|
1900
|
+
const C = {
|
|
1901
|
+
boundaries: new Set([
|
|
1902
|
+
'function_definition',
|
|
1903
|
+
'class_specifier',
|
|
1904
|
+
'struct_specifier',
|
|
1905
|
+
'namespace_definition',
|
|
1906
|
+
'translation_unit',
|
|
1907
|
+
'field_declaration_list',
|
|
1908
|
+
]),
|
|
1909
|
+
inlineFunctions: new Set(['lambda_expression']),
|
|
1910
|
+
bindingParents: new Set(['init_declarator', 'assignment_expression']),
|
|
1911
|
+
blocks: new Set(['compound_statement', 'case_statement', 'translation_unit']),
|
|
1912
|
+
enclosing(parent, child, out) {
|
|
1913
|
+
switch (parent.type) {
|
|
1914
|
+
case 'if_statement': {
|
|
1915
|
+
const cond = parent.childForFieldName('condition');
|
|
1916
|
+
if (isField(parent, 'consequence', child))
|
|
1917
|
+
push(out, guard('if', cond, false));
|
|
1918
|
+
else if (isField(parent, 'alternative', child) || child.type === 'else_clause')
|
|
1919
|
+
push(out, guard('else', cond, true));
|
|
1920
|
+
return;
|
|
1921
|
+
}
|
|
1922
|
+
case 'conditional_expression': {
|
|
1923
|
+
const cond = parent.childForFieldName('condition');
|
|
1924
|
+
if (isField(parent, 'consequence', child))
|
|
1925
|
+
push(out, guard('ternary', cond, false));
|
|
1926
|
+
else if (isField(parent, 'alternative', child))
|
|
1927
|
+
push(out, guard('ternary', cond, true));
|
|
1928
|
+
return;
|
|
1929
|
+
}
|
|
1930
|
+
case 'case_statement': {
|
|
1931
|
+
const value = parent.childForFieldName('value');
|
|
1932
|
+
if (value && child.id === value.id)
|
|
1933
|
+
return;
|
|
1934
|
+
const stmt = parent.parent?.parent;
|
|
1935
|
+
const subject = condText(stmt?.childForFieldName('condition'));
|
|
1936
|
+
if (!value)
|
|
1937
|
+
push(out, guard('case', stmt?.childForFieldName('condition'), false, subject ? `${subject}: default` : 'default', stmt));
|
|
1938
|
+
else {
|
|
1939
|
+
const v = condText(value);
|
|
1940
|
+
push(out, guard('case', value, false, subject ? `${subject} == ${v}` : v, stmt));
|
|
1941
|
+
}
|
|
1942
|
+
return;
|
|
1943
|
+
}
|
|
1944
|
+
case 'binary_expression': {
|
|
1945
|
+
if (!isField(parent, 'right', child))
|
|
1946
|
+
return;
|
|
1947
|
+
const op = parent.childForFieldName('operator')?.text;
|
|
1948
|
+
const left = parent.childForFieldName('left');
|
|
1949
|
+
if (op === '&&')
|
|
1950
|
+
push(out, guard('and', left, false));
|
|
1951
|
+
else if (op === '||')
|
|
1952
|
+
push(out, guard('or', left, true));
|
|
1953
|
+
return;
|
|
1954
|
+
}
|
|
1955
|
+
case 'catch_clause':
|
|
1956
|
+
if (isField(parent, 'body', child))
|
|
1957
|
+
push(out, guard('catch', null, false, 'on error'));
|
|
1958
|
+
return;
|
|
1959
|
+
default:
|
|
1960
|
+
return;
|
|
1961
|
+
}
|
|
1962
|
+
},
|
|
1963
|
+
earlyExits(parent, child, out) {
|
|
1964
|
+
guardsBefore(parent, child, out, (s) => s.type === 'if_statement', (s) => !!s.childForFieldName('alternative') || namedChildren(s).some((n) => n.type === 'else_clause'), (s) => s.childForFieldName('consequence'), cAlwaysExits, (s) => ({ node: s.childForFieldName('condition'), text: condText(s.childForFieldName('condition')) }));
|
|
1965
|
+
},
|
|
1966
|
+
};
|
|
1967
|
+
/** The rules per language. A language absent here yields no guards — never a wrong one. */
|
|
1968
|
+
const RULES_BY_LANGUAGE = new Map([
|
|
1969
|
+
['typescript', JS],
|
|
1970
|
+
['tsx', JS],
|
|
1971
|
+
['javascript', JS],
|
|
1972
|
+
['jsx', JS],
|
|
1973
|
+
['swift', SWIFT],
|
|
1974
|
+
['python', PYTHON],
|
|
1975
|
+
['java', JAVA],
|
|
1976
|
+
['kotlin', KOTLIN],
|
|
1977
|
+
['csharp', CSHARP],
|
|
1978
|
+
['go', GO],
|
|
1979
|
+
['c', C],
|
|
1980
|
+
['cpp', C],
|
|
1981
|
+
['objc', C],
|
|
1982
|
+
]);
|
|
1983
|
+
const DEFINITION_TYPES = new Set([
|
|
1984
|
+
'function_declaration',
|
|
1985
|
+
'function_definition',
|
|
1986
|
+
'method_definition',
|
|
1987
|
+
'method_declaration',
|
|
1988
|
+
'constructor_declaration',
|
|
1989
|
+
'class_declaration',
|
|
1990
|
+
'class_definition',
|
|
1991
|
+
'decorated_definition',
|
|
1992
|
+
'local_function_statement',
|
|
1993
|
+
'lexical_declaration',
|
|
1994
|
+
'variable_declaration',
|
|
1995
|
+
'public_field_definition',
|
|
1996
|
+
]);
|
|
1997
|
+
const CLASS_TYPES = new Set(['class_declaration', 'class_definition', 'class', 'object_declaration', 'struct_declaration', 'record_declaration']);
|
|
1998
|
+
const DECORATOR_TYPES = new Set(['decorator', 'annotation', 'marker_annotation', 'attribute']);
|
|
1999
|
+
const MAX_DECORATOR_TEXT = 80;
|
|
2000
|
+
async function decoratorsForFile(absPath, language, lines) {
|
|
2001
|
+
const out = new Map();
|
|
2002
|
+
if (!supportsBranchGuards(language) || lines.length === 0)
|
|
2003
|
+
return out;
|
|
2004
|
+
const cached = await treeFor(absPath, language);
|
|
2005
|
+
if (!cached)
|
|
2006
|
+
return out;
|
|
2007
|
+
for (const line of lines) {
|
|
2008
|
+
if (out.has(line))
|
|
2009
|
+
continue;
|
|
2010
|
+
const found = decoratorsInTree(cached.tree.rootNode, cached.source, line);
|
|
2011
|
+
if (found !== null)
|
|
2012
|
+
out.set(line, found);
|
|
2013
|
+
}
|
|
2014
|
+
return out;
|
|
2015
|
+
}
|
|
2016
|
+
/** {@link decoratorsForFile} over source text — the test surface. */
|
|
2017
|
+
async function decoratorsInSource(source, language, line) {
|
|
2018
|
+
if (!supportsBranchGuards(language))
|
|
2019
|
+
return null;
|
|
2020
|
+
const tree = await parse(source, language);
|
|
2021
|
+
if (!tree)
|
|
2022
|
+
return null;
|
|
2023
|
+
try {
|
|
2024
|
+
return decoratorsInTree(tree.rootNode, source, line);
|
|
2025
|
+
}
|
|
2026
|
+
finally {
|
|
2027
|
+
tree.delete();
|
|
2028
|
+
}
|
|
2029
|
+
}
|
|
2030
|
+
function decoratorsInTree(root, source, line) {
|
|
2031
|
+
const row = line - 1;
|
|
2032
|
+
const col = firstNonBlankColumn(source, row);
|
|
2033
|
+
let node = innermostAt(root, row, col);
|
|
2034
|
+
if (!node)
|
|
2035
|
+
return null;
|
|
2036
|
+
// Up to the definition the line belongs to.
|
|
2037
|
+
let definition = null;
|
|
2038
|
+
for (let up = 0; node && up < 12; up++, node = node.parent) {
|
|
2039
|
+
if (DEFINITION_TYPES.has(node.type)) {
|
|
2040
|
+
definition = node;
|
|
2041
|
+
break;
|
|
2042
|
+
}
|
|
2043
|
+
}
|
|
2044
|
+
if (!definition)
|
|
2045
|
+
return null;
|
|
2046
|
+
// A Python decorated function is the child of the node that holds the decorators.
|
|
2047
|
+
const holder = definition.parent && definition.parent.type === 'decorated_definition' ? definition.parent : definition;
|
|
2048
|
+
const own = decoratorsOn(holder);
|
|
2049
|
+
let cls = holder.parent;
|
|
2050
|
+
for (let up = 0; cls && up < 6 && !CLASS_TYPES.has(cls.type); up++)
|
|
2051
|
+
cls = cls.parent;
|
|
2052
|
+
const clsHolder = cls && cls.parent && cls.parent.type === 'decorated_definition' ? cls.parent : cls;
|
|
2053
|
+
return { own, class: clsHolder ? decoratorsOn(clsHolder) : [] };
|
|
2054
|
+
}
|
|
2055
|
+
/** Decorator texts on one definition node: its own leading decorator children, its modifiers/attribute lists, or the siblings before it. */
|
|
2056
|
+
function decoratorsOn(definition) {
|
|
2057
|
+
const out = [];
|
|
2058
|
+
const add = (n) => {
|
|
2059
|
+
if (n.type === 'attribute_list') {
|
|
2060
|
+
for (const a of namedChildren(n))
|
|
2061
|
+
if (a.type === 'attribute')
|
|
2062
|
+
out.push(decoratorText(a));
|
|
2063
|
+
return;
|
|
2064
|
+
}
|
|
2065
|
+
if (DECORATOR_TYPES.has(n.type))
|
|
2066
|
+
out.push(decoratorText(n));
|
|
2067
|
+
};
|
|
2068
|
+
for (const c of namedChildren(definition)) {
|
|
2069
|
+
if (c.type === 'modifiers')
|
|
2070
|
+
for (const m of namedChildren(c))
|
|
2071
|
+
add(m);
|
|
2072
|
+
else
|
|
2073
|
+
add(c);
|
|
2074
|
+
}
|
|
2075
|
+
// JS: decorators are siblings that precede the member in the class body.
|
|
2076
|
+
if (out.length === 0 && definition.parent) {
|
|
2077
|
+
const before = precedingSiblings(definition.parent, definition);
|
|
2078
|
+
for (let i = before.length - 1; i >= 0; i--) {
|
|
2079
|
+
const s = before[i];
|
|
2080
|
+
if (s.type !== 'decorator')
|
|
2081
|
+
break;
|
|
2082
|
+
out.unshift(decoratorText(s));
|
|
2083
|
+
}
|
|
2084
|
+
}
|
|
2085
|
+
return out;
|
|
2086
|
+
}
|
|
2087
|
+
function decoratorText(node) {
|
|
2088
|
+
let text = collapse(node.text).replace(/^@\s*/, '');
|
|
2089
|
+
if (node.type === 'attribute_list')
|
|
2090
|
+
text = text.replace(/^\[|\]$/g, '');
|
|
2091
|
+
return cut(text, MAX_DECORATOR_TEXT);
|
|
2092
|
+
}
|
|
2093
|
+
// =============================================================================
|
|
2094
|
+
// Member types — what a class declares its members to be
|
|
2095
|
+
// =============================================================================
|
|
2096
|
+
/**
|
|
2097
|
+
* The declared types of a class's members, read from the tree: the
|
|
2098
|
+
* constructor's parameter properties (`private readonly usersService:
|
|
2099
|
+
* UsersService`), its fields (`private final OwnerRepository owners`,
|
|
2100
|
+
* `val owners: OwnerRepository`, `private readonly IRepo _repo`), its typed
|
|
2101
|
+
* properties. The index keeps no type for these, and a member call the
|
|
2102
|
+
* extractor kept only the last segment of (`this.usersService.findByEmail`
|
|
2103
|
+
* → `findByEmail`) resolves by name alone; the declared type is what says
|
|
2104
|
+
* where it really goes, and whether it leaves the index.
|
|
2105
|
+
*
|
|
2106
|
+
* Keyed by member name, the type as written without generics'
|
|
2107
|
+
* arguments (`Repository<Cat>` → `Repository<Cat>` is kept whole; callers
|
|
2108
|
+
* strip what they need).
|
|
2109
|
+
*/
|
|
2110
|
+
async function memberTypesForFile(absPath, language, line) {
|
|
2111
|
+
const out = new Map();
|
|
2112
|
+
if (!supportsBranchGuards(language))
|
|
2113
|
+
return out;
|
|
2114
|
+
const cached = await treeFor(absPath, language);
|
|
2115
|
+
if (!cached)
|
|
2116
|
+
return out;
|
|
2117
|
+
return memberTypesInTree(cached.tree.rootNode, cached.source, line);
|
|
2118
|
+
}
|
|
2119
|
+
/** {@link memberTypesForFile} over source text — the test surface. */
|
|
2120
|
+
async function memberTypesInSource(source, language, line) {
|
|
2121
|
+
if (!supportsBranchGuards(language))
|
|
2122
|
+
return new Map();
|
|
2123
|
+
const tree = await parse(source, language);
|
|
2124
|
+
if (!tree)
|
|
2125
|
+
return new Map();
|
|
2126
|
+
try {
|
|
2127
|
+
return memberTypesInTree(tree.rootNode, source, line);
|
|
2128
|
+
}
|
|
2129
|
+
finally {
|
|
2130
|
+
tree.delete();
|
|
2131
|
+
}
|
|
2132
|
+
}
|
|
2133
|
+
const CLASS_BODY_TYPES = new Set(['class_body', 'declaration_list', 'field_declaration_list']);
|
|
2134
|
+
function memberTypesInTree(root, source, line) {
|
|
2135
|
+
const out = new Map();
|
|
2136
|
+
const row = line - 1;
|
|
2137
|
+
let node = innermostAt(root, row, firstNonBlankColumn(source, row));
|
|
2138
|
+
let cls = null;
|
|
2139
|
+
for (let up = 0; node && up < 16; up++, node = node.parent) {
|
|
2140
|
+
if (CLASS_TYPES.has(node.type)) {
|
|
2141
|
+
cls = node;
|
|
2142
|
+
break;
|
|
2143
|
+
}
|
|
2144
|
+
}
|
|
2145
|
+
if (!cls)
|
|
2146
|
+
return out;
|
|
2147
|
+
const typeText = (n) => (n ? collapse(n.text).replace(/^:\s*/, '').trim() : '');
|
|
2148
|
+
const put = (name, type) => {
|
|
2149
|
+
if (name && type && !out.has(name))
|
|
2150
|
+
out.set(name, type);
|
|
2151
|
+
};
|
|
2152
|
+
const visitParams = (params) => {
|
|
2153
|
+
if (!params)
|
|
2154
|
+
return;
|
|
2155
|
+
for (const p of namedChildren(params)) {
|
|
2156
|
+
// TS: `private readonly x: T` (a parameter property); Kotlin: `val x: T`; C#/Java: `T x` — a field of the same name may follow.
|
|
2157
|
+
if (p.type === 'required_parameter' || p.type === 'optional_parameter') {
|
|
2158
|
+
if (!namedChildren(p).some((c) => c.type === 'accessibility_modifier' || c.type === 'override_modifier') && !/^\s*(?:public|private|protected|readonly)\b/.test(p.text))
|
|
2159
|
+
continue;
|
|
2160
|
+
put(p.childForFieldName('pattern')?.text, typeText(p.childForFieldName('type')));
|
|
2161
|
+
}
|
|
2162
|
+
else if (p.type === 'class_parameter') {
|
|
2163
|
+
const kids = namedChildren(p);
|
|
2164
|
+
const name = kids.find((c) => c.type === 'simple_identifier');
|
|
2165
|
+
const type = kids.find((c) => c.type === 'user_type' || c.type === 'nullable_type');
|
|
2166
|
+
if (kids.some((c) => c.type === 'binding_pattern_kind'))
|
|
2167
|
+
put(name?.text, typeText(type));
|
|
2168
|
+
}
|
|
2169
|
+
else if (p.type === 'parameter' || p.type === 'formal_parameter') {
|
|
2170
|
+
put(p.childForFieldName('name')?.text, typeText(p.childForFieldName('type')));
|
|
2171
|
+
}
|
|
2172
|
+
}
|
|
2173
|
+
};
|
|
2174
|
+
// Kotlin's primary constructor sits on the class node itself.
|
|
2175
|
+
for (const c of namedChildren(cls))
|
|
2176
|
+
if (c.type === 'primary_constructor')
|
|
2177
|
+
visitParams(namedChildren(c).find((n) => n.type === 'class_parameters') ?? c);
|
|
2178
|
+
const body = namedChildren(cls).find((c) => CLASS_BODY_TYPES.has(c.type)) ?? cls.childForFieldName('body');
|
|
2179
|
+
if (!body)
|
|
2180
|
+
return out;
|
|
2181
|
+
for (const m of namedChildren(body)) {
|
|
2182
|
+
switch (m.type) {
|
|
2183
|
+
case 'public_field_definition':
|
|
2184
|
+
case 'field_definition':
|
|
2185
|
+
put(m.childForFieldName('name')?.text, typeText(m.childForFieldName('type')));
|
|
2186
|
+
break;
|
|
2187
|
+
case 'method_definition':
|
|
2188
|
+
if (m.childForFieldName('name')?.text === 'constructor')
|
|
2189
|
+
visitParams(m.childForFieldName('parameters'));
|
|
2190
|
+
break;
|
|
2191
|
+
case 'field_declaration': {
|
|
2192
|
+
// Java: `type` + `declarator`; C#: a `variable_declaration` inside.
|
|
2193
|
+
const type = m.childForFieldName('type');
|
|
2194
|
+
if (type) {
|
|
2195
|
+
for (const d of namedChildren(m))
|
|
2196
|
+
if (d.type === 'variable_declarator')
|
|
2197
|
+
put(d.childForFieldName('name')?.text, typeText(type));
|
|
2198
|
+
}
|
|
2199
|
+
else {
|
|
2200
|
+
const decl = namedChildren(m).find((c) => c.type === 'variable_declaration');
|
|
2201
|
+
const t = decl?.childForFieldName('type');
|
|
2202
|
+
for (const d of decl ? namedChildren(decl) : [])
|
|
2203
|
+
if (d.type === 'variable_declarator')
|
|
2204
|
+
put(d.childForFieldName('name')?.text, typeText(t));
|
|
2205
|
+
}
|
|
2206
|
+
break;
|
|
2207
|
+
}
|
|
2208
|
+
case 'property_declaration': {
|
|
2209
|
+
// C#: `type` + `name`; Kotlin: `variable_declaration (name) (type)`.
|
|
2210
|
+
const csType = m.childForFieldName('type');
|
|
2211
|
+
if (csType)
|
|
2212
|
+
put(m.childForFieldName('name')?.text, typeText(csType));
|
|
2213
|
+
else {
|
|
2214
|
+
const decl = namedChildren(m).find((c) => c.type === 'variable_declaration');
|
|
2215
|
+
const kids = decl ? namedChildren(decl) : [];
|
|
2216
|
+
const name = kids.find((c) => c.type === 'simple_identifier');
|
|
2217
|
+
const type = kids.find((c) => c.type === 'user_type' || c.type === 'nullable_type');
|
|
2218
|
+
put(name?.text, typeText(type));
|
|
2219
|
+
}
|
|
2220
|
+
break;
|
|
2221
|
+
}
|
|
2222
|
+
case 'constructor_declaration':
|
|
2223
|
+
visitParams(m.childForFieldName('parameters'));
|
|
2224
|
+
break;
|
|
2225
|
+
default:
|
|
2226
|
+
break;
|
|
2227
|
+
}
|
|
2228
|
+
}
|
|
2229
|
+
return out;
|
|
2230
|
+
}
|
|
2231
|
+
//# sourceMappingURL=branch-guards.js.map
|