@duckmind/dm-linux-x64 0.66.0 → 0.66.2
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/dm +2 -2
- package/extensions/.dm-extensions.json +12 -546
- package/extensions/dm-lens/clients/dispatch/runners/utils/runner-helpers.js +1 -1
- package/extensions/dm-mcp-adapter/mcp-keyring-helper.cjs +1 -1
- package/extensions/dm-ponytail/hooks/ponytail-activate.js +1 -1
- package/extensions/dm-ponytail/hooks/ponytail-instructions.js +1 -1
- package/extensions/dm-subagents/src/runs/background/runner-aliases.js +2 -2
- package/extensions/dm-subagents/src/runs/foreground/prompt-audit.js +4 -1
- package/extensions/dm-subagents/src/runs/shared/child-session.js +8 -6
- package/extensions/dm-subagents/src/watchdog/permission-arbiter.js +9 -1
- package/extensions/dm-subagents/src/watchdog/review.js +4 -1
- package/package.json +1 -1
- package/extensions/dm-lens/dist/clients/actionable-warnings-logger.js +0 -24
- package/extensions/dm-lens/dist/clients/actionable-warnings.js +0 -1014
- package/extensions/dm-lens/dist/clients/advisory-provenance.js +0 -385
- package/extensions/dm-lens/dist/clients/agent-behavior-client.js +0 -94
- package/extensions/dm-lens/dist/clients/agent-nudge.js +0 -151
- package/extensions/dm-lens/dist/clients/analysed-root.js +0 -1
- package/extensions/dm-lens/dist/clients/ast-grep-client.js +0 -576
- package/extensions/dm-lens/dist/clients/ast-grep-rule-manager.js +0 -114
- package/extensions/dm-lens/dist/clients/ast-grep-tool-logger.js +0 -85
- package/extensions/dm-lens/dist/clients/ast-grep-types.js +0 -1
- package/extensions/dm-lens/dist/clients/ast-grep-yaml-synth.js +0 -109
- package/extensions/dm-lens/dist/clients/atomic-write-staging.js +0 -66
- package/extensions/dm-lens/dist/clients/atomic-write.js +0 -37
- package/extensions/dm-lens/dist/clients/availability-probe-flight.js +0 -11
- package/extensions/dm-lens/dist/clients/bash-file-access.js +0 -830
- package/extensions/dm-lens/dist/clients/biome-client.js +0 -221
- package/extensions/dm-lens/dist/clients/blocker-freshness.js +0 -385
- package/extensions/dm-lens/dist/clients/blocker-past-eof.js +0 -49
- package/extensions/dm-lens/dist/clients/bootstrap.js +0 -246
- package/extensions/dm-lens/dist/clients/bounded-cache.js +0 -105
- package/extensions/dm-lens/dist/clients/bounded-pid-file-lock.js +0 -173
- package/extensions/dm-lens/dist/clients/bounded-telemetry.js +0 -104
- package/extensions/dm-lens/dist/clients/build-identity.js +0 -45
- package/extensions/dm-lens/dist/clients/bundled-resource-health.js +0 -41
- package/extensions/dm-lens/dist/clients/bus-events-logger.js +0 -48
- package/extensions/dm-lens/dist/clients/bus-publish.js +0 -117
- package/extensions/dm-lens/dist/clients/cache/rule-cache.js +0 -103
- package/extensions/dm-lens/dist/clients/cache-manager.js +0 -340
- package/extensions/dm-lens/dist/clients/cache-observability.js +0 -847
- package/extensions/dm-lens/dist/clients/call-graph.js +0 -628
- package/extensions/dm-lens/dist/clients/cargo-manifest.js +0 -151
- package/extensions/dm-lens/dist/clients/cascade-budget.js +0 -52
- package/extensions/dm-lens/dist/clients/cascade-format.js +0 -160
- package/extensions/dm-lens/dist/clients/cascade-logger.js +0 -22
- package/extensions/dm-lens/dist/clients/cascade-types.js +0 -1
- package/extensions/dm-lens/dist/clients/child-unref.js +0 -59
- package/extensions/dm-lens/dist/clients/code-quality-warnings.js +0 -192
- package/extensions/dm-lens/dist/clients/codebase-model.js +0 -110
- package/extensions/dm-lens/dist/clients/collateral-test-role.js +0 -8
- package/extensions/dm-lens/dist/clients/complexity-client.js +0 -398
- package/extensions/dm-lens/dist/clients/config-core/deny.js +0 -117
- package/extensions/dm-lens/dist/clients/config-core/index.js +0 -2
- package/extensions/dm-lens/dist/clients/config-core/merge.js +0 -228
- package/extensions/dm-lens/dist/clients/config-core/normalize.js +0 -217
- package/extensions/dm-lens/dist/clients/config-core/process-spec.js +0 -147
- package/extensions/dm-lens/dist/clients/config-core/provenance.js +0 -54
- package/extensions/dm-lens/dist/clients/config-core/records.js +0 -79
- package/extensions/dm-lens/dist/clients/config-core/resolve.js +0 -48
- package/extensions/dm-lens/dist/clients/config-core/safe-object.js +0 -22
- package/extensions/dm-lens/dist/clients/config-core/schema.js +0 -90
- package/extensions/dm-lens/dist/clients/config-diagnostic-codes.js +0 -90
- package/extensions/dm-lens/dist/clients/config-locations.js +0 -175
- package/extensions/dm-lens/dist/clients/config-resolve.js +0 -359
- package/extensions/dm-lens/dist/clients/config-schema.js +0 -128
- package/extensions/dm-lens/dist/clients/config-warn.js +0 -146
- package/extensions/dm-lens/dist/clients/console-guard-install.js +0 -4
- package/extensions/dm-lens/dist/clients/cooperative-budget.js +0 -36
- package/extensions/dm-lens/dist/clients/dead-code-client.js +0 -342
- package/extensions/dm-lens/dist/clients/dead-code-logger.js +0 -18
- package/extensions/dm-lens/dist/clients/deadline-utils.js +0 -117
- package/extensions/dm-lens/dist/clients/debug-handles.js +0 -121
- package/extensions/dm-lens/dist/clients/debug-heap.js +0 -80
- package/extensions/dm-lens/dist/clients/deferred-lsp-work.js +0 -33
- package/extensions/dm-lens/dist/clients/degradation-ledger.js +0 -272
- package/extensions/dm-lens/dist/clients/demoted-finding-render.js +0 -61
- package/extensions/dm-lens/dist/clients/dependency-checker.js +0 -525
- package/extensions/dm-lens/dist/clients/deps/ast-grep-napi.js +0 -17
- package/extensions/dm-lens/dist/clients/deps/dm-host-deps.js +0 -10
- package/extensions/dm-lens/dist/clients/deps/dm-tui-compat.js +0 -58
- package/extensions/dm-lens/dist/clients/deps/js-yaml.js +0 -7
- package/extensions/dm-lens/dist/clients/deps/minimatch.js +0 -6
- package/extensions/dm-lens/dist/clients/deps/pidusage.js +0 -5
- package/extensions/dm-lens/dist/clients/deps/typebox.js +0 -5
- package/extensions/dm-lens/dist/clients/deps/vscode-jsonrpc.js +0 -8
- package/extensions/dm-lens/dist/clients/deps/web-tree-sitter.js +0 -11
- package/extensions/dm-lens/dist/clients/diagnostic-dispositions.js +0 -318
- package/extensions/dm-lens/dist/clients/diagnostic-line-freshness.js +0 -144
- package/extensions/dm-lens/dist/clients/diagnostic-logger.js +0 -60
- package/extensions/dm-lens/dist/clients/diagnostic-tracker.js +0 -89
- package/extensions/dm-lens/dist/clients/diagnostics-publish.js +0 -134
- package/extensions/dm-lens/dist/clients/dispatch/ast-grep-catalog.js +0 -16
- package/extensions/dm-lens/dist/clients/dispatch/auxiliary-lsp.js +0 -158
- package/extensions/dm-lens/dist/clients/dispatch/collect-later-tier.js +0 -25
- package/extensions/dm-lens/dist/clients/dispatch/diagnostic-taxonomy.js +0 -58
- package/extensions/dm-lens/dist/clients/dispatch/dispatcher.js +0 -953
- package/extensions/dm-lens/dist/clients/dispatch/fact-provider-types.js +0 -1
- package/extensions/dm-lens/dist/clients/dispatch/fact-rule-runner.js +0 -17
- package/extensions/dm-lens/dist/clients/dispatch/fact-runner.js +0 -18
- package/extensions/dm-lens/dist/clients/dispatch/fact-scheduler.js +0 -52
- package/extensions/dm-lens/dist/clients/dispatch/fact-store.js +0 -199
- package/extensions/dm-lens/dist/clients/dispatch/facts/comment-facts.js +0 -20
- package/extensions/dm-lens/dist/clients/dispatch/facts/file-content.js +0 -18
- package/extensions/dm-lens/dist/clients/dispatch/facts/function-facts.js +0 -281
- package/extensions/dm-lens/dist/clients/dispatch/facts/import-facts.js +0 -169
- package/extensions/dm-lens/dist/clients/dispatch/facts/tree-sitter-facts.js +0 -19
- package/extensions/dm-lens/dist/clients/dispatch/facts/try-catch-facts.js +0 -95
- package/extensions/dm-lens/dist/clients/dispatch/finding-policy.js +0 -159
- package/extensions/dm-lens/dist/clients/dispatch/indent-detect.js +0 -227
- package/extensions/dm-lens/dist/clients/dispatch/inline-suppressions.js +0 -52
- package/extensions/dm-lens/dist/clients/dispatch/integration.js +0 -1463
- package/extensions/dm-lens/dist/clients/dispatch/lazy.js +0 -8
- package/extensions/dm-lens/dist/clients/dispatch/pending-runner-findings.js +0 -75
- package/extensions/dm-lens/dist/clients/dispatch/plan.js +0 -278
- package/extensions/dm-lens/dist/clients/dispatch/priorities.js +0 -14
- package/extensions/dm-lens/dist/clients/dispatch/rule-id-normalize.js +0 -42
- package/extensions/dm-lens/dist/clients/dispatch/rule-ignores.js +0 -21
- package/extensions/dm-lens/dist/clients/dispatch/rule-policy.js +0 -73
- package/extensions/dm-lens/dist/clients/dispatch/rules/async-noise.js +0 -31
- package/extensions/dm-lens/dist/clients/dispatch/rules/async-unnecessary-wrapper.js +0 -27
- package/extensions/dm-lens/dist/clients/dispatch/rules/cors-wildcard.js +0 -35
- package/extensions/dm-lens/dist/clients/dispatch/rules/error-obscuring.js +0 -29
- package/extensions/dm-lens/dist/clients/dispatch/rules/error-swallowing.js +0 -29
- package/extensions/dm-lens/dist/clients/dispatch/rules/framework-call-noise.js +0 -31
- package/extensions/dm-lens/dist/clients/dispatch/rules/high-complexity.js +0 -56
- package/extensions/dm-lens/dist/clients/dispatch/rules/high-fan-out.js +0 -45
- package/extensions/dm-lens/dist/clients/dispatch/rules/high-import-coupling.js +0 -31
- package/extensions/dm-lens/dist/clients/dispatch/rules/missing-error-propagation.js +0 -45
- package/extensions/dm-lens/dist/clients/dispatch/rules/no-commented-credentials.js +0 -46
- package/extensions/dm-lens/dist/clients/dispatch/rules/pass-through-wrappers.js +0 -38
- package/extensions/dm-lens/dist/clients/dispatch/rules/placeholder-comments.js +0 -40
- package/extensions/dm-lens/dist/clients/dispatch/rules/unsafe-boundary.js +0 -101
- package/extensions/dm-lens/dist/clients/dispatch/runner-context.js +0 -46
- package/extensions/dm-lens/dist/clients/dispatch/runners/actionlint.js +0 -98
- package/extensions/dm-lens/dist/clients/dispatch/runners/ast-grep-napi.js +0 -690
- package/extensions/dm-lens/dist/clients/dispatch/runners/biome-check.js +0 -202
- package/extensions/dm-lens/dist/clients/dispatch/runners/cpp-check.js +0 -190
- package/extensions/dm-lens/dist/clients/dispatch/runners/credo.js +0 -66
- package/extensions/dm-lens/dist/clients/dispatch/runners/cue-vet.js +0 -167
- package/extensions/dm-lens/dist/clients/dispatch/runners/dart-analyze.js +0 -151
- package/extensions/dm-lens/dist/clients/dispatch/runners/detekt.js +0 -157
- package/extensions/dm-lens/dist/clients/dispatch/runners/dotnet-build.js +0 -157
- package/extensions/dm-lens/dist/clients/dispatch/runners/elixir-check.js +0 -153
- package/extensions/dm-lens/dist/clients/dispatch/runners/eslint.js +0 -99
- package/extensions/dm-lens/dist/clients/dispatch/runners/fact-rules.js +0 -20
- package/extensions/dm-lens/dist/clients/dispatch/runners/fish-indent.js +0 -65
- package/extensions/dm-lens/dist/clients/dispatch/runners/gleam-check.js +0 -107
- package/extensions/dm-lens/dist/clients/dispatch/runners/go-vet.js +0 -51
- package/extensions/dm-lens/dist/clients/dispatch/runners/golangci-lint.js +0 -89
- package/extensions/dm-lens/dist/clients/dispatch/runners/hadolint.js +0 -83
- package/extensions/dm-lens/dist/clients/dispatch/runners/helm-lint.js +0 -144
- package/extensions/dm-lens/dist/clients/dispatch/runners/helm-render.js +0 -593
- package/extensions/dm-lens/dist/clients/dispatch/runners/htmlhint.js +0 -103
- package/extensions/dm-lens/dist/clients/dispatch/runners/index.js +0 -100
- package/extensions/dm-lens/dist/clients/dispatch/runners/javac.js +0 -82
- package/extensions/dm-lens/dist/clients/dispatch/runners/ktlint.js +0 -93
- package/extensions/dm-lens/dist/clients/dispatch/runners/lsp.js +0 -206
- package/extensions/dm-lens/dist/clients/dispatch/runners/markdownlint.js +0 -129
- package/extensions/dm-lens/dist/clients/dispatch/runners/mypy.js +0 -70
- package/extensions/dm-lens/dist/clients/dispatch/runners/oxlint.js +0 -305
- package/extensions/dm-lens/dist/clients/dispatch/runners/php-lint.js +0 -70
- package/extensions/dm-lens/dist/clients/dispatch/runners/phpstan.js +0 -90
- package/extensions/dm-lens/dist/clients/dispatch/runners/prisma-validate.js +0 -56
- package/extensions/dm-lens/dist/clients/dispatch/runners/psscriptanalyzer.js +0 -307
- package/extensions/dm-lens/dist/clients/dispatch/runners/pyright.js +0 -118
- package/extensions/dm-lens/dist/clients/dispatch/runners/rubocop.js +0 -79
- package/extensions/dm-lens/dist/clients/dispatch/runners/ruff.js +0 -69
- package/extensions/dm-lens/dist/clients/dispatch/runners/rust-clippy.js +0 -147
- package/extensions/dm-lens/dist/clients/dispatch/runners/shellcheck.js +0 -111
- package/extensions/dm-lens/dist/clients/dispatch/runners/shfmt.js +0 -81
- package/extensions/dm-lens/dist/clients/dispatch/runners/spellcheck.js +0 -74
- package/extensions/dm-lens/dist/clients/dispatch/runners/spotbugs.js +0 -208
- package/extensions/dm-lens/dist/clients/dispatch/runners/sqlfluff.js +0 -136
- package/extensions/dm-lens/dist/clients/dispatch/runners/stylelint.js +0 -118
- package/extensions/dm-lens/dist/clients/dispatch/runners/swiftlint.js +0 -143
- package/extensions/dm-lens/dist/clients/dispatch/runners/taplo.js +0 -97
- package/extensions/dm-lens/dist/clients/dispatch/runners/terragrunt.js +0 -110
- package/extensions/dm-lens/dist/clients/dispatch/runners/tflint.js +0 -104
- package/extensions/dm-lens/dist/clients/dispatch/runners/tree-sitter.js +0 -516
- package/extensions/dm-lens/dist/clients/dispatch/runners/trivy-config.js +0 -155
- package/extensions/dm-lens/dist/clients/dispatch/runners/utils/availability-policy.js +0 -285
- package/extensions/dm-lens/dist/clients/dispatch/runners/utils/candidate-probe.js +0 -61
- package/extensions/dm-lens/dist/clients/dispatch/runners/utils/diagnostic-parsers.js +0 -61
- package/extensions/dm-lens/dist/clients/dispatch/runners/utils/lazy-installer.js +0 -130
- package/extensions/dm-lens/dist/clients/dispatch/runners/utils/runner-helpers.js +0 -1067
- package/extensions/dm-lens/dist/clients/dispatch/runners/utils/spawn-outcome.js +0 -40
- package/extensions/dm-lens/dist/clients/dispatch/runners/utils/tool-failure.js +0 -117
- package/extensions/dm-lens/dist/clients/dispatch/runners/utils/toolchain-availability.js +0 -88
- package/extensions/dm-lens/dist/clients/dispatch/runners/utils.js +0 -8
- package/extensions/dm-lens/dist/clients/dispatch/runners/vale.js +0 -106
- package/extensions/dm-lens/dist/clients/dispatch/runners/yaml-rule-parser.js +0 -163
- package/extensions/dm-lens/dist/clients/dispatch/runners/yamllint.js +0 -83
- package/extensions/dm-lens/dist/clients/dispatch/runners/zig-check.js +0 -72
- package/extensions/dm-lens/dist/clients/dispatch/suppress-writer.js +0 -57
- package/extensions/dm-lens/dist/clients/dispatch/tool-profile.js +0 -30
- package/extensions/dm-lens/dist/clients/dispatch/types.js +0 -7
- package/extensions/dm-lens/dist/clients/dispatch/utils/format-utils.js +0 -45
- package/extensions/dm-lens/dist/clients/dispatch/utils/lsp-diagnostics.js +0 -31
- package/extensions/dm-lens/dist/clients/disposition-logger.js +0 -19
- package/extensions/dm-lens/dist/clients/disposition-publish.js +0 -88
- package/extensions/dm-lens/dist/clients/durable-store.js +0 -77
- package/extensions/dm-lens/dist/clients/effective-config.js +0 -160
- package/extensions/dm-lens/dist/clients/env-utils.js +0 -26
- package/extensions/dm-lens/dist/clients/error-class.js +0 -3
- package/extensions/dm-lens/dist/clients/eval-timestamp.js +0 -2
- package/extensions/dm-lens/dist/clients/event-loop-hold.js +0 -119
- package/extensions/dm-lens/dist/clients/event-loop-monitor.js +0 -84
- package/extensions/dm-lens/dist/clients/extension-log.js +0 -230
- package/extensions/dm-lens/dist/clients/extension-mode.js +0 -20
- package/extensions/dm-lens/dist/clients/feature-hints.js +0 -88
- package/extensions/dm-lens/dist/clients/file-kinds.js +0 -304
- package/extensions/dm-lens/dist/clients/file-role.js +0 -50
- package/extensions/dm-lens/dist/clients/file-time.js +0 -112
- package/extensions/dm-lens/dist/clients/file-utils.js +0 -630
- package/extensions/dm-lens/dist/clients/finding-delivery-gate.js +0 -129
- package/extensions/dm-lens/dist/clients/finding-identity.js +0 -23
- package/extensions/dm-lens/dist/clients/fix-worklog.js +0 -75
- package/extensions/dm-lens/dist/clients/format-events-publish.js +0 -230
- package/extensions/dm-lens/dist/clients/format-service.js +0 -154
- package/extensions/dm-lens/dist/clients/formatters-lazy.js +0 -8
- package/extensions/dm-lens/dist/clients/formatters.js +0 -1351
- package/extensions/dm-lens/dist/clients/freshness-cadence.js +0 -1
- package/extensions/dm-lens/dist/clients/freshness.js +0 -8
- package/extensions/dm-lens/dist/clients/generated-artifacts.js +0 -209
- package/extensions/dm-lens/dist/clients/generation-guard.js +0 -105
- package/extensions/dm-lens/dist/clients/git-guard.js +0 -770
- package/extensions/dm-lens/dist/clients/git-tracked-ignore.js +0 -120
- package/extensions/dm-lens/dist/clients/gitleaks-client.js +0 -282
- package/extensions/dm-lens/dist/clients/go-client.js +0 -47
- package/extensions/dm-lens/dist/clients/govulncheck-client.js +0 -411
- package/extensions/dm-lens/dist/clients/gradle-ktfmt-style.js +0 -177
- package/extensions/dm-lens/dist/clients/grammar-source.js +0 -224
- package/extensions/dm-lens/dist/clients/gzip-stage-write.js +0 -85
- package/extensions/dm-lens/dist/clients/hashline-anchor.js +0 -222
- package/extensions/dm-lens/dist/clients/hook-budgets.js +0 -16
- package/extensions/dm-lens/dist/clients/host-edit-normalize.js +0 -87
- package/extensions/dm-lens/dist/clients/host-ports.js +0 -35
- package/extensions/dm-lens/dist/clients/indent-retarget.js +0 -102
- package/extensions/dm-lens/dist/clients/inline-blocker-dispositions.js +0 -103
- package/extensions/dm-lens/dist/clients/install-diagnostics.js +0 -123
- package/extensions/dm-lens/dist/clients/installer/index.js +0 -3770
- package/extensions/dm-lens/dist/clients/installer/managed-tool-refresh-session.js +0 -17
- package/extensions/dm-lens/dist/clients/installer/managed-tool-refresh.js +0 -522
- package/extensions/dm-lens/dist/clients/instance-reaper-state.js +0 -3
- package/extensions/dm-lens/dist/clients/instance-reaper.js +0 -577
- package/extensions/dm-lens/dist/clients/instance-registry-lock.js +0 -131
- package/extensions/dm-lens/dist/clients/instance-registry.js +0 -452
- package/extensions/dm-lens/dist/clients/jscpd-client.js +0 -252
- package/extensions/dm-lens/dist/clients/json-cache-read.js +0 -25
- package/extensions/dm-lens/dist/clients/knip-client.js +0 -472
- package/extensions/dm-lens/dist/clients/language-policy.js +0 -242
- package/extensions/dm-lens/dist/clients/language-profile.js +0 -261
- package/extensions/dm-lens/dist/clients/language-registry.js +0 -424
- package/extensions/dm-lens/dist/clients/latency-logger.js +0 -192
- package/extensions/dm-lens/dist/clients/lazy-import.js +0 -14
- package/extensions/dm-lens/dist/clients/ledger-bounds.js +0 -21
- package/extensions/dm-lens/dist/clients/lens-config.js +0 -237
- package/extensions/dm-lens/dist/clients/lens-engine.js +0 -181
- package/extensions/dm-lens/dist/clients/lens-events.js +0 -105
- package/extensions/dm-lens/dist/clients/lens-flag-registry.js +0 -302
- package/extensions/dm-lens/dist/clients/lens-map.js +0 -980
- package/extensions/dm-lens/dist/clients/live-bus-emitter.js +0 -53
- package/extensions/dm-lens/dist/clients/log-cleanup.js +0 -138
- package/extensions/dm-lens/dist/clients/lsp/aggregation.js +0 -88
- package/extensions/dm-lens/dist/clients/lsp/cascade-tier.js +0 -203
- package/extensions/dm-lens/dist/clients/lsp/client.js +0 -2709
- package/extensions/dm-lens/dist/clients/lsp/config.js +0 -252
- package/extensions/dm-lens/dist/clients/lsp/content-limits.js +0 -21
- package/extensions/dm-lens/dist/clients/lsp/diagnostic-binding.js +0 -79
- package/extensions/dm-lens/dist/clients/lsp/document-drift.js +0 -312
- package/extensions/dm-lens/dist/clients/lsp/edits.js +0 -930
- package/extensions/dm-lens/dist/clients/lsp/index.js +0 -4448
- package/extensions/dm-lens/dist/clients/lsp/inferred-project.js +0 -121
- package/extensions/dm-lens/dist/clients/lsp/jvm-runtime.js +0 -194
- package/extensions/dm-lens/dist/clients/lsp/language.js +0 -23
- package/extensions/dm-lens/dist/clients/lsp/launch.js +0 -468
- package/extensions/dm-lens/dist/clients/lsp/lombok.js +0 -146
- package/extensions/dm-lens/dist/clients/lsp/path-utils.js +0 -1
- package/extensions/dm-lens/dist/clients/lsp/pending-aux-coverage.js +0 -90
- package/extensions/dm-lens/dist/clients/lsp/position-encoding.js +0 -28
- package/extensions/dm-lens/dist/clients/lsp/ruby-drive-dirs.js +0 -42
- package/extensions/dm-lens/dist/clients/lsp/server.js +0 -2422
- package/extensions/dm-lens/dist/clients/lsp/session-roots.js +0 -52
- package/extensions/dm-lens/dist/clients/lsp/spawn-history.js +0 -15
- package/extensions/dm-lens/dist/clients/lsp/sync-kind.js +0 -30
- package/extensions/dm-lens/dist/clients/lsp/tsserver-sync.js +0 -156
- package/extensions/dm-lens/dist/clients/lsp/wait-policy/capability-snapshot.js +0 -1
- package/extensions/dm-lens/dist/clients/lsp/wait-policy/classification.js +0 -36
- package/extensions/dm-lens/dist/clients/lsp/wait-policy/index.js +0 -3
- package/extensions/dm-lens/dist/clients/lsp/wait-policy/strategies.js +0 -175
- package/extensions/dm-lens/dist/clients/lsp/watch-queue.js +0 -40
- package/extensions/dm-lens/dist/clients/lsp/workspace-diagnostics-cache.js +0 -267
- package/extensions/dm-lens/dist/clients/lsp/workspace-diagnostics-session.js +0 -7
- package/extensions/dm-lens/dist/clients/lsp/workspace-sweep-hold.js +0 -93
- package/extensions/dm-lens/dist/clients/lsp-budget.js +0 -83
- package/extensions/dm-lens/dist/clients/lsp-document-symbols.js +0 -93
- package/extensions/dm-lens/dist/clients/lsp-lazy.js +0 -8
- package/extensions/dm-lens/dist/clients/lsp-mutation.js +0 -273
- package/extensions/dm-lens/dist/clients/lsp-status.js +0 -37
- package/extensions/dm-lens/dist/clients/map-with-concurrency.js +0 -20
- package/extensions/dm-lens/dist/clients/mcp/analyze.js +0 -251
- package/extensions/dm-lens/dist/clients/mcp/host-shim.js +0 -14
- package/extensions/dm-lens/dist/clients/mcp/ipc.js +0 -302
- package/extensions/dm-lens/dist/clients/mcp/review.js +0 -137
- package/extensions/dm-lens/dist/clients/mcp/session.js +0 -305
- package/extensions/dm-lens/dist/clients/memory-sampler.js +0 -107
- package/extensions/dm-lens/dist/clients/message-end-attribution.js +0 -18
- package/extensions/dm-lens/dist/clients/metrics-client.js +0 -80
- package/extensions/dm-lens/dist/clients/metrics-history.js +0 -255
- package/extensions/dm-lens/dist/clients/middle-man-analysis.js +0 -161
- package/extensions/dm-lens/dist/clients/model-provider.js +0 -31
- package/extensions/dm-lens/dist/clients/module-report-lsp.js +0 -140
- package/extensions/dm-lens/dist/clients/module-report.js +0 -1642
- package/extensions/dm-lens/dist/clients/mutating-tool.js +0 -377
- package/extensions/dm-lens/dist/clients/mutation-attribution.js +0 -168
- package/extensions/dm-lens/dist/clients/mutation-bridge.js +0 -102
- package/extensions/dm-lens/dist/clients/ndjson-logger.js +0 -471
- package/extensions/dm-lens/dist/clients/observed-mutation-sources.js +0 -43
- package/extensions/dm-lens/dist/clients/observed-mutation.js +0 -709
- package/extensions/dm-lens/dist/clients/oldtext-autopatch.js +0 -100
- package/extensions/dm-lens/dist/clients/opaque-mutation-scan.js +0 -313
- package/extensions/dm-lens/dist/clients/opengrep-client.js +0 -216
- package/extensions/dm-lens/dist/clients/opengrep-config.js +0 -52
- package/extensions/dm-lens/dist/clients/package-manager.js +0 -303
- package/extensions/dm-lens/dist/clients/package-root.js +0 -25
- package/extensions/dm-lens/dist/clients/partial-edit-apply.js +0 -337
- package/extensions/dm-lens/dist/clients/path-attribution-telemetry.js +0 -31
- package/extensions/dm-lens/dist/clients/path-keyed-map.js +0 -57
- package/extensions/dm-lens/dist/clients/path-utils.js +0 -476
- package/extensions/dm-lens/dist/clients/performance-report.js +0 -291
- package/extensions/dm-lens/dist/clients/persist-debounce.js +0 -37
- package/extensions/dm-lens/dist/clients/persistent-reverify.js +0 -165
- package/extensions/dm-lens/dist/clients/php-cs-fixer-config.js +0 -15
- package/extensions/dm-lens/dist/clients/pipeline.js +0 -1091
- package/extensions/dm-lens/dist/clients/post-autofix-notice.js +0 -19
- package/extensions/dm-lens/dist/clients/print-mode.js +0 -3
- package/extensions/dm-lens/dist/clients/probe-home-state.js +0 -62
- package/extensions/dm-lens/dist/clients/process-bridge.js +0 -20
- package/extensions/dm-lens/dist/clients/process-singletons.js +0 -69
- package/extensions/dm-lens/dist/clients/process-snapshot.js +0 -22
- package/extensions/dm-lens/dist/clients/project-changes.js +0 -112
- package/extensions/dm-lens/dist/clients/project-conventions.js +0 -152
- package/extensions/dm-lens/dist/clients/project-diagnostics/cache.js +0 -91
- package/extensions/dm-lens/dist/clients/project-diagnostics/extractors.js +0 -53
- package/extensions/dm-lens/dist/clients/project-diagnostics/fresh-fetch.js +0 -408
- package/extensions/dm-lens/dist/clients/project-diagnostics/runner-adapters/call-graph-impact.js +0 -64
- package/extensions/dm-lens/dist/clients/project-diagnostics/runner-adapters/dead-code.js +0 -38
- package/extensions/dm-lens/dist/clients/project-diagnostics/runner-adapters/gitleaks.js +0 -21
- package/extensions/dm-lens/dist/clients/project-diagnostics/runner-adapters/govulncheck.js +0 -23
- package/extensions/dm-lens/dist/clients/project-diagnostics/runner-adapters/jscpd.js +0 -28
- package/extensions/dm-lens/dist/clients/project-diagnostics/runner-adapters/knip.js +0 -45
- package/extensions/dm-lens/dist/clients/project-diagnostics/runner-adapters/madge.js +0 -34
- package/extensions/dm-lens/dist/clients/project-diagnostics/runner-adapters/opengrep.js +0 -32
- package/extensions/dm-lens/dist/clients/project-diagnostics/runner-adapters/runner-findings.js +0 -79
- package/extensions/dm-lens/dist/clients/project-diagnostics/runner-adapters/trivy.js +0 -41
- package/extensions/dm-lens/dist/clients/project-diagnostics/scanner.js +0 -349
- package/extensions/dm-lens/dist/clients/project-diagnostics/types.js +0 -1
- package/extensions/dm-lens/dist/clients/project-lens-config.js +0 -437
- package/extensions/dm-lens/dist/clients/project-report.js +0 -527
- package/extensions/dm-lens/dist/clients/project-scale.js +0 -60
- package/extensions/dm-lens/dist/clients/project-scan-policy.js +0 -4
- package/extensions/dm-lens/dist/clients/project-snapshot-fingerprint.js +0 -39
- package/extensions/dm-lens/dist/clients/project-snapshot-persist-worker.js +0 -14
- package/extensions/dm-lens/dist/clients/project-snapshot.js +0 -1178
- package/extensions/dm-lens/dist/clients/project-trust.js +0 -104
- package/extensions/dm-lens/dist/clients/python-environment.js +0 -120
- package/extensions/dm-lens/dist/clients/python-provenance.js +0 -551
- package/extensions/dm-lens/dist/clients/quiet-window-config.js +0 -16
- package/extensions/dm-lens/dist/clients/quiet-window.js +0 -126
- package/extensions/dm-lens/dist/clients/read-bridge.js +0 -46
- package/extensions/dm-lens/dist/clients/read-expansion.js +0 -218
- package/extensions/dm-lens/dist/clients/read-guard-logger.js +0 -178
- package/extensions/dm-lens/dist/clients/read-guard-tool-lines.js +0 -1042
- package/extensions/dm-lens/dist/clients/read-guard.js +0 -1096
- package/extensions/dm-lens/dist/clients/recent-touches.js +0 -126
- package/extensions/dm-lens/dist/clients/redact/secrets.js +0 -386
- package/extensions/dm-lens/dist/clients/resource-sampler.js +0 -344
- package/extensions/dm-lens/dist/clients/reverse-deps.js +0 -227
- package/extensions/dm-lens/dist/clients/review-graph/builder.js +0 -3635
- package/extensions/dm-lens/dist/clients/review-graph/format.js +0 -30
- package/extensions/dm-lens/dist/clients/review-graph/git-identity.js +0 -115
- package/extensions/dm-lens/dist/clients/review-graph/import-resolvers.js +0 -325
- package/extensions/dm-lens/dist/clients/review-graph/persist-worker.js +0 -8
- package/extensions/dm-lens/dist/clients/review-graph/query.js +0 -195
- package/extensions/dm-lens/dist/clients/review-graph/revision-drift.js +0 -3
- package/extensions/dm-lens/dist/clients/review-graph/service.js +0 -60
- package/extensions/dm-lens/dist/clients/review-graph/shared-extraction-ir.js +0 -54
- package/extensions/dm-lens/dist/clients/review-graph/symbol-id.js +0 -29
- package/extensions/dm-lens/dist/clients/review-graph/tsconfig-paths.js +0 -253
- package/extensions/dm-lens/dist/clients/review-graph/types.js +0 -1
- package/extensions/dm-lens/dist/clients/review-graph/workspace-modules.js +0 -374
- package/extensions/dm-lens/dist/clients/review-graph-logger.js +0 -51
- package/extensions/dm-lens/dist/clients/ruff-client.js +0 -109
- package/extensions/dm-lens/dist/clients/rules-scanner.js +0 -113
- package/extensions/dm-lens/dist/clients/run-duration.js +0 -9
- package/extensions/dm-lens/dist/clients/runtime-agent-end.js +0 -608
- package/extensions/dm-lens/dist/clients/runtime-config.js +0 -39
- package/extensions/dm-lens/dist/clients/runtime-context.js +0 -155
- package/extensions/dm-lens/dist/clients/runtime-coordinator.js +0 -817
- package/extensions/dm-lens/dist/clients/runtime-session.js +0 -1625
- package/extensions/dm-lens/dist/clients/runtime-tool-call.js +0 -868
- package/extensions/dm-lens/dist/clients/runtime-tool-result.js +0 -1317
- package/extensions/dm-lens/dist/clients/runtime-turn.js +0 -2333
- package/extensions/dm-lens/dist/clients/rust-client.js +0 -48
- package/extensions/dm-lens/dist/clients/safe-spawn.js +0 -1267
- package/extensions/dm-lens/dist/clients/sanitize.js +0 -174
- package/extensions/dm-lens/dist/clients/scan-utils.js +0 -7
- package/extensions/dm-lens/dist/clients/scratch-tree-policy.js +0 -57
- package/extensions/dm-lens/dist/clients/search-read-registration.js +0 -59
- package/extensions/dm-lens/dist/clients/secret-findings.js +0 -69
- package/extensions/dm-lens/dist/clients/security-scan-client.js +0 -208
- package/extensions/dm-lens/dist/clients/session-event-guard.js +0 -111
- package/extensions/dm-lens/dist/clients/session-lifecycle.js +0 -173
- package/extensions/dm-lens/dist/clients/session-start-observability.js +0 -47
- package/extensions/dm-lens/dist/clients/session-state-store.js +0 -63
- package/extensions/dm-lens/dist/clients/session-summary.js +0 -8
- package/extensions/dm-lens/dist/clients/sessionstart-logger.js +0 -22
- package/extensions/dm-lens/dist/clients/sg-runner.js +0 -627
- package/extensions/dm-lens/dist/clients/sgconfig.js +0 -280
- package/extensions/dm-lens/dist/clients/shared-checkout-guard.js +0 -177
- package/extensions/dm-lens/dist/clients/single-flight.js +0 -52
- package/extensions/dm-lens/dist/clients/situational-tool-telemetry.js +0 -85
- package/extensions/dm-lens/dist/clients/skills-resolver.js +0 -38
- package/extensions/dm-lens/dist/clients/slow-fs.js +0 -77
- package/extensions/dm-lens/dist/clients/smells-rollup.js +0 -118
- package/extensions/dm-lens/dist/clients/source-filter.js +0 -320
- package/extensions/dm-lens/dist/clients/source-walker.js +0 -96
- package/extensions/dm-lens/dist/clients/spawn-output-cap.js +0 -35
- package/extensions/dm-lens/dist/clients/spawn-timeout-cooldown.js +0 -51
- package/extensions/dm-lens/dist/clients/stale-marker.js +0 -1
- package/extensions/dm-lens/dist/clients/startup-scan.js +0 -222
- package/extensions/dm-lens/dist/clients/startup-timing.js +0 -67
- package/extensions/dm-lens/dist/clients/string-utils.js +0 -6
- package/extensions/dm-lens/dist/clients/subagent-mode.js +0 -40
- package/extensions/dm-lens/dist/clients/symbol-containment.js +0 -16
- package/extensions/dm-lens/dist/clients/symbol-types.js +0 -1
- package/extensions/dm-lens/dist/clients/test-runner-client.js +0 -1504
- package/extensions/dm-lens/dist/clients/test-runner-delivery.js +0 -260
- package/extensions/dm-lens/dist/clients/todo-scanner.js +0 -164
- package/extensions/dm-lens/dist/clients/tool-agreement.js +0 -377
- package/extensions/dm-lens/dist/clients/tool-config.js +0 -218
- package/extensions/dm-lens/dist/clients/tool-cwd.js +0 -235
- package/extensions/dm-lens/dist/clients/tool-definition.js +0 -22
- package/extensions/dm-lens/dist/clients/tool-event.js +0 -18
- package/extensions/dm-lens/dist/clients/tool-policy.js +0 -2393
- package/extensions/dm-lens/dist/clients/tool-probe.js +0 -5
- package/extensions/dm-lens/dist/clients/tool-render.js +0 -79
- package/extensions/dm-lens/dist/clients/tool-set-policy.js +0 -70
- package/extensions/dm-lens/dist/clients/tree-sitter-cache.js +0 -206
- package/extensions/dm-lens/dist/clients/tree-sitter-client.js +0 -2787
- package/extensions/dm-lens/dist/clients/tree-sitter-logger.js +0 -77
- package/extensions/dm-lens/dist/clients/tree-sitter-navigator.js +0 -198
- package/extensions/dm-lens/dist/clients/tree-sitter-query-loader.js +0 -318
- package/extensions/dm-lens/dist/clients/tree-sitter-shared.js +0 -87
- package/extensions/dm-lens/dist/clients/tree-sitter-symbol-extractor.js +0 -957
- package/extensions/dm-lens/dist/clients/trivy-client.js +0 -305
- package/extensions/dm-lens/dist/clients/tui-fit.js +0 -26
- package/extensions/dm-lens/dist/clients/turn-context.js +0 -48
- package/extensions/dm-lens/dist/clients/turn-end/lane.js +0 -1
- package/extensions/dm-lens/dist/clients/turn-end/lanes/govulncheck.js +0 -54
- package/extensions/dm-lens/dist/clients/turn-end/lanes/secrets.js +0 -131
- package/extensions/dm-lens/dist/clients/turn-summary-render.js +0 -56
- package/extensions/dm-lens/dist/clients/turn-summary.js +0 -84
- package/extensions/dm-lens/dist/clients/typos-config.js +0 -9
- package/extensions/dm-lens/dist/clients/user-notify.js +0 -15
- package/extensions/dm-lens/dist/clients/vanished-instance-marker.js +0 -18
- package/extensions/dm-lens/dist/clients/warm-attach.js +0 -210
- package/extensions/dm-lens/dist/clients/widget-state.js +0 -963
- package/extensions/dm-lens/dist/clients/word-index-logger.js +0 -21
- package/extensions/dm-lens/dist/clients/word-index-store.js +0 -222
- package/extensions/dm-lens/dist/clients/word-index.js +0 -1426
- package/extensions/dm-lens/dist/clients/workspace-topology.js +0 -261
- package/extensions/dm-lens/dist/clients/write-ordering-guard.js +0 -24
- package/extensions/dm-lens/dist/clients/zizmor-config.js +0 -160
- package/extensions/dm-lens/dist/i18n.js +0 -67
- package/extensions/dm-lens/dist/index.js +0 -1782
- package/extensions/dm-lens/dist/mcp/analyze-cli.js +0 -178
- package/extensions/dm-lens/dist/mcp/build-staleness.js +0 -55
- package/extensions/dm-lens/dist/mcp/cli.js +0 -78
- package/extensions/dm-lens/dist/mcp/server.js +0 -1208
- package/extensions/dm-lens/dist/mcp/worker.js +0 -33
- package/extensions/dm-lens/dist/scripts/lib/process-scan.mjs +0 -256
- package/extensions/dm-lens/dist/scripts/lib/skills-predicate.mjs +0 -53
- package/extensions/dm-lens/dist/scripts/lib/web-tree-sitter-dir.mjs +0 -45
- package/extensions/dm-lens/dist/tools/activate-tools.js +0 -61
- package/extensions/dm-lens/dist/tools/ast-grep-outline.js +0 -127
- package/extensions/dm-lens/dist/tools/ast-grep-replace.js +0 -226
- package/extensions/dm-lens/dist/tools/ast-grep-search.js +0 -687
- package/extensions/dm-lens/dist/tools/effective-config.js +0 -59
- package/extensions/dm-lens/dist/tools/lens-diagnostic-mark.js +0 -240
- package/extensions/dm-lens/dist/tools/lens-diagnostics.js +0 -1484
- package/extensions/dm-lens/dist/tools/lsp-diagnostics.js +0 -1088
- package/extensions/dm-lens/dist/tools/lsp-navigation.js +0 -1194
- package/extensions/dm-lens/dist/tools/lsp-structured-output.js +0 -277
- package/extensions/dm-lens/dist/tools/module-report.js +0 -332
- package/extensions/dm-lens/dist/tools/project-report.js +0 -72
- package/extensions/dm-lens/dist/tools/render-compact.js +0 -272
- package/extensions/dm-lens/dist/tools/scan-progress.js +0 -31
- package/extensions/dm-lens/dist/tools/shared.js +0 -26
- package/extensions/dm-lens/dist/tools/symbol-search.js +0 -133
- package/extensions/dm-mcp-adapter/dist/abort.js +0 -35
- package/extensions/dm-mcp-adapter/dist/agent-dir.js +0 -45
- package/extensions/dm-mcp-adapter/dist/agent-plugin-loader.js +0 -392
- package/extensions/dm-mcp-adapter/dist/agent-plugin-provenance.js +0 -26
- package/extensions/dm-mcp-adapter/dist/bearer-command-resolver.js +0 -182
- package/extensions/dm-mcp-adapter/dist/claude-plugin-loader.js +0 -350
- package/extensions/dm-mcp-adapter/dist/config.js +0 -1310
- package/extensions/dm-mcp-adapter/dist/consent-manager.js +0 -84
- package/extensions/dm-mcp-adapter/dist/elicitation-handler.js +0 -308
- package/extensions/dm-mcp-adapter/dist/errors.js +0 -225
- package/extensions/dm-mcp-adapter/dist/http-ca.js +0 -71
- package/extensions/dm-mcp-adapter/dist/jev-client.js +0 -359
- package/extensions/dm-mcp-adapter/dist/jev-contracts.js +0 -1
- package/extensions/dm-mcp-adapter/dist/jev-key-store.js +0 -176
- package/extensions/dm-mcp-adapter/dist/json-schema-validator.js +0 -70
- package/extensions/dm-mcp-adapter/dist/lifecycle.js +0 -387
- package/extensions/dm-mcp-adapter/dist/logger.js +0 -115
- package/extensions/dm-mcp-adapter/dist/mcp-auth-fetch.js +0 -86
- package/extensions/dm-mcp-adapter/dist/mcp-auth-flow.js +0 -949
- package/extensions/dm-mcp-adapter/dist/mcp-auth.js +0 -997
- package/extensions/dm-mcp-adapter/dist/mcp-bearer-store.js +0 -182
- package/extensions/dm-mcp-adapter/dist/mcp-callback-server.js +0 -381
- package/extensions/dm-mcp-adapter/dist/mcp-oauth-provider.js +0 -524
- package/extensions/dm-mcp-adapter/dist/mcp-probe.js +0 -151
- package/extensions/dm-mcp-adapter/dist/mcp-tasks.js +0 -300
- package/extensions/dm-mcp-adapter/dist/mcp-trace.js +0 -215
- package/extensions/dm-mcp-adapter/dist/metadata-cache.js +0 -267
- package/extensions/dm-mcp-adapter/dist/npx-resolver.js +0 -465
- package/extensions/dm-mcp-adapter/dist/package-mcp-loader.js +0 -135
- package/extensions/dm-mcp-adapter/dist/request-headers-command.js +0 -294
- package/extensions/dm-mcp-adapter/dist/resource-tools.js +0 -7
- package/extensions/dm-mcp-adapter/dist/runtime-owner.js +0 -94
- package/extensions/dm-mcp-adapter/dist/sampling-handler.js +0 -213
- package/extensions/dm-mcp-adapter/dist/secure-keyring.js +0 -191
- package/extensions/dm-mcp-adapter/dist/server-manager.js +0 -1514
- package/extensions/dm-mcp-adapter/dist/session-approvals.js +0 -127
- package/extensions/dm-mcp-adapter/dist/session-recovery.js +0 -82
- package/extensions/dm-mcp-adapter/dist/state.js +0 -1
- package/extensions/dm-mcp-adapter/dist/types.js +0 -286
- package/extensions/dm-mcp-adapter/dist/ui-app-bridge-helpers.js +0 -36
- package/extensions/dm-mcp-adapter/dist/ui-resource-handler.js +0 -204
- package/extensions/dm-mcp-adapter/dist/ui-stream-types.js +0 -58
- package/extensions/dm-mcp-adapter/dist/ui-tool-visibility.js +0 -26
- package/extensions/dm-mcp-adapter/dist/unix-socket-transport.js +0 -83
- package/extensions/dm-mcp-adapter/dist/utils.js +0 -382
|
@@ -1,2787 +0,0 @@
|
|
|
1
|
-
import { AsyncLocalStorage } from "node:async_hooks";
|
|
2
|
-
import * as crypto from "node:crypto";
|
|
3
|
-
import * as fs from "node:fs";
|
|
4
|
-
import { createRequire } from "node:module";
|
|
5
|
-
import * as path from "node:path";
|
|
6
|
-
import { getDegradationLedgerGeneration, incrementDegradationCount, recordDegradation, recordDegradationOnce } from "./degradation-ledger.js";
|
|
7
|
-
import { BoundedFifoMap } from "./bounded-cache.js";
|
|
8
|
-
import { loadWebTreeSitter } from "./deps/web-tree-sitter.js";
|
|
9
|
-
import { transientRetryDelayMs } from "./dispatch/runners/utils/availability-policy.js";
|
|
10
|
-
import { getProjectIgnoreMatcher, isExcludedDirName } from "./file-utils.js";
|
|
11
|
-
import { downloadGrammarDetailed, fileHasWasmMagic, grammarBlockReason, grammarFileSha256, isVendoredGrammar, LANGUAGE_TO_GRAMMAR, pinnedGrammarHash, vendoredGrammarRefusal, vendoredGrammarsDir } from "./grammar-source.js";
|
|
12
|
-
import { getPackageRoot, resolvePackagePath } from "./package-root.js";
|
|
13
|
-
import { resolveWebTreeSitterPackageDir } from "../scripts/lib/web-tree-sitter-dir.mjs";
|
|
14
|
-
import { assertInstallAllowed, getProjectTrustGeneration } from "./project-trust.js";
|
|
15
|
-
import { escapeRegExp } from "./string-utils.js";
|
|
16
|
-
import { logTreeSitterDiagnostic } from "./tree-sitter-logger.js";
|
|
17
|
-
import { notifyUserDegradation } from "./user-notify.js";
|
|
18
|
-
const _require = createRequire(import.meta.url);
|
|
19
|
-
import { createTreeCacheCounters, deriveScanTreeCacheCapacity, TreeCache } from "./tree-sitter-cache.js";
|
|
20
|
-
import { TreeSitterNavigator } from "./tree-sitter-navigator.js";
|
|
21
|
-
import { isProvenSqlAlchemySessionReceiver, isSafePsycopgIdentifierComposition, isSqlAlchemyEntityQueryArgument, isSqlAlchemyStatementArgument, PYTHON_SQLALCHEMY_RECEIVER_NAMES, PYTHON_SQLALCHEMY_STATEMENT_BUILDERS } from "./python-provenance.js";
|
|
22
|
-
import { TreeSitterQueryLoader } from "./tree-sitter-query-loader.js";
|
|
23
|
-
const TREE_SITTER_MAX_SCAN_FILES = 20000;
|
|
24
|
-
const QUERY_BATCH_MAX_LOAD_FAILURES = 3;
|
|
25
|
-
const NO_NESTED_ANCHOR_VISIT_CAP = 1e4;
|
|
26
|
-
const PYTHON_SQL_SINK_METHODS = new Set([
|
|
27
|
-
"execute",
|
|
28
|
-
"executemany",
|
|
29
|
-
"query",
|
|
30
|
-
"raw"
|
|
31
|
-
]);
|
|
32
|
-
const TYPESCRIPT_SQL_KNOWN_PACKAGES = new Set([
|
|
33
|
-
"pg",
|
|
34
|
-
"mysql2",
|
|
35
|
-
"better-sqlite3",
|
|
36
|
-
"knex",
|
|
37
|
-
"@prisma/client"
|
|
38
|
-
]);
|
|
39
|
-
const TYPESCRIPT_SQL_IMPORTS = new WeakMap;
|
|
40
|
-
const NOT_PARSED = { parsed: false };
|
|
41
|
-
function createParserCounters() {
|
|
42
|
-
return {
|
|
43
|
-
parserInvocations: 0,
|
|
44
|
-
parserDurationMs: 0,
|
|
45
|
-
parserFailures: 0
|
|
46
|
-
};
|
|
47
|
-
}
|
|
48
|
-
function grammarWriteBlockedReason(dir) {
|
|
49
|
-
let probe = dir;
|
|
50
|
-
while (!fs.existsSync(probe)) {
|
|
51
|
-
const parent = path.dirname(probe);
|
|
52
|
-
if (parent === probe)
|
|
53
|
-
return "ENOENT";
|
|
54
|
-
probe = parent;
|
|
55
|
-
}
|
|
56
|
-
try {
|
|
57
|
-
fs.accessSync(probe, fs.constants.W_OK);
|
|
58
|
-
return;
|
|
59
|
-
} catch (err) {
|
|
60
|
-
return err?.code ?? "EACCES";
|
|
61
|
-
}
|
|
62
|
-
}
|
|
63
|
-
function grammarFileStamp(filePath) {
|
|
64
|
-
try {
|
|
65
|
-
const stat = fs.statSync(filePath);
|
|
66
|
-
return stat.isFile() ? `${stat.size}:${stat.mtimeMs}` : undefined;
|
|
67
|
-
} catch {
|
|
68
|
-
return;
|
|
69
|
-
}
|
|
70
|
-
}
|
|
71
|
-
function isTreeSitterWasmAbortError(error) {
|
|
72
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
73
|
-
return message.includes("Aborted") || message.includes("abort()");
|
|
74
|
-
}
|
|
75
|
-
const RESOLUTION_ERROR_CODES = new Set([
|
|
76
|
-
"ERR_MODULE_NOT_FOUND",
|
|
77
|
-
"MODULE_NOT_FOUND",
|
|
78
|
-
"ERR_INVALID_MODULE_SPECIFIER",
|
|
79
|
-
"ERR_UNSUPPORTED_DIR_IMPORT",
|
|
80
|
-
"ENOENT",
|
|
81
|
-
"EMFILE",
|
|
82
|
-
"EBUSY",
|
|
83
|
-
"EAGAIN",
|
|
84
|
-
"EPERM",
|
|
85
|
-
"ETXTBSY"
|
|
86
|
-
]);
|
|
87
|
-
function collectErrorCodes(err) {
|
|
88
|
-
const codes = [];
|
|
89
|
-
let current = err;
|
|
90
|
-
const seen = new Set;
|
|
91
|
-
while (current instanceof Error && !seen.has(current)) {
|
|
92
|
-
seen.add(current);
|
|
93
|
-
const code = current.code;
|
|
94
|
-
if (typeof code === "string")
|
|
95
|
-
codes.push(code);
|
|
96
|
-
current = current.cause;
|
|
97
|
-
}
|
|
98
|
-
return codes;
|
|
99
|
-
}
|
|
100
|
-
function classifyWebTreeSitterLoadFailure(err) {
|
|
101
|
-
const codes = collectErrorCodes(err);
|
|
102
|
-
return codes.some((code) => RESOLUTION_ERROR_CODES.has(code)) ? "resolution" : "evaluation";
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
export class TreeSitterClient {
|
|
106
|
-
initialized = false;
|
|
107
|
-
initPromise = null;
|
|
108
|
-
webTreeSitterLoadFailed = false;
|
|
109
|
-
languages = new Map;
|
|
110
|
-
parsers = new Map;
|
|
111
|
-
treeCache;
|
|
112
|
-
navigator = new TreeSitterNavigator;
|
|
113
|
-
grammarsDir;
|
|
114
|
-
grammarEnsurePromises = new Map;
|
|
115
|
-
grammarRetryAtMs = new Map;
|
|
116
|
-
verifiedGrammarPaths = new Map;
|
|
117
|
-
decodeFailedGrammarPaths = new Map;
|
|
118
|
-
verifiedGrammarVersionAt = new Map;
|
|
119
|
-
staleGrammarVersionAt = new Map;
|
|
120
|
-
staleReportedGrammarPaths = new Set;
|
|
121
|
-
poisonedGrammarPaths = new Set;
|
|
122
|
-
grammarFailureAttempts = new Map;
|
|
123
|
-
grammarLastNotifiedDelayMs = new Map;
|
|
124
|
-
grammarNotificationsLedgerGen = getDegradationLedgerGeneration();
|
|
125
|
-
trustBlockedGrammarNotifications = new Set;
|
|
126
|
-
trustNotificationsGeneration = getProjectTrustGeneration();
|
|
127
|
-
ParserClass = null;
|
|
128
|
-
LanguageLoader = null;
|
|
129
|
-
static QUERY_CACHE_MAX_ENTRIES = 256;
|
|
130
|
-
static QUERY_BATCH_CACHE_MAX_ENTRIES = 256;
|
|
131
|
-
queryCache = new BoundedFifoMap(TreeSitterClient.QUERY_CACHE_MAX_ENTRIES);
|
|
132
|
-
queryBatchCache = new BoundedFifoMap(TreeSitterClient.QUERY_BATCH_CACHE_MAX_ENTRIES);
|
|
133
|
-
queryCacheCap() {
|
|
134
|
-
const value = Number.parseInt(process.env.DM_LENS_TREE_SITTER_QUERY_CACHE_CAP ?? "", 10);
|
|
135
|
-
return Number.isSafeInteger(value) && value > 0 ? value : TreeSitterClient.QUERY_CACHE_MAX_ENTRIES;
|
|
136
|
-
}
|
|
137
|
-
queryBatchCacheCap() {
|
|
138
|
-
const value = Number.parseInt(process.env.DM_LENS_TREE_SITTER_QUERY_BATCH_CACHE_CAP ?? "", 10);
|
|
139
|
-
return Number.isSafeInteger(value) && value > 0 ? value : TreeSitterClient.QUERY_BATCH_CACHE_MAX_ENTRIES;
|
|
140
|
-
}
|
|
141
|
-
cacheQuery(key, value) {
|
|
142
|
-
this.queryCache.delete(key);
|
|
143
|
-
const evicted = this.queryCache.setMaxEntries(this.queryCacheCap());
|
|
144
|
-
evicted.push(...this.queryCache.set(key, value));
|
|
145
|
-
for (const [, dropped] of evicted)
|
|
146
|
-
dropped?.query?.delete?.();
|
|
147
|
-
}
|
|
148
|
-
cacheQueryBatch(key, value) {
|
|
149
|
-
this.queryBatchCache.delete(key);
|
|
150
|
-
const evicted = this.queryBatchCache.setMaxEntries(this.queryBatchCacheCap());
|
|
151
|
-
evicted.push(...this.queryBatchCache.set(key, value));
|
|
152
|
-
for (const [, dropped] of evicted)
|
|
153
|
-
dropped?.query?.delete?.();
|
|
154
|
-
}
|
|
155
|
-
queryBatchLoadFailures = new Map;
|
|
156
|
-
queryLoader = new TreeSitterQueryLoader;
|
|
157
|
-
verbose;
|
|
158
|
-
parserCounters = createParserCounters();
|
|
159
|
-
parseCacheMeasurement = new AsyncLocalStorage;
|
|
160
|
-
activeMeasurements = 0;
|
|
161
|
-
onWasmAbort;
|
|
162
|
-
wasmAborted = false;
|
|
163
|
-
grammarDirResolutionDeps;
|
|
164
|
-
constructor(verbose = false, onWasmAbort, grammarDirResolutionDeps) {
|
|
165
|
-
this.grammarDirResolutionDeps = grammarDirResolutionDeps ?? {
|
|
166
|
-
resolveAsset: (asset) => this.resolveWebTreeSitterAsset(asset),
|
|
167
|
-
resolvePackage: (specifier) => _require.resolve(specifier),
|
|
168
|
-
packageRoot: () => getPackageRoot(import.meta.url),
|
|
169
|
-
cwd: () => process.cwd()
|
|
170
|
-
};
|
|
171
|
-
this.grammarsDir = this.findGrammarsDir();
|
|
172
|
-
this.verbose = verbose;
|
|
173
|
-
this.onWasmAbort = onWasmAbort;
|
|
174
|
-
this.treeCache = new TreeCache(50, verbose, 4096, (key, amount) => {
|
|
175
|
-
const measurement = this.parseCacheMeasurement.getStore();
|
|
176
|
-
if (measurement)
|
|
177
|
-
measurement[key] += amount;
|
|
178
|
-
}, (error) => this.reportWasmAbort(error));
|
|
179
|
-
}
|
|
180
|
-
recordParserCounter(key, amount = 1) {
|
|
181
|
-
this.parserCounters[key] += amount;
|
|
182
|
-
const measurement = this.parseCacheMeasurement.getStore();
|
|
183
|
-
if (measurement)
|
|
184
|
-
measurement[key] += amount;
|
|
185
|
-
}
|
|
186
|
-
reportWasmAbort(error) {
|
|
187
|
-
if (!isTreeSitterWasmAbortError(error))
|
|
188
|
-
return false;
|
|
189
|
-
if (!this.wasmAborted) {
|
|
190
|
-
this.wasmAborted = true;
|
|
191
|
-
recordDegradation({
|
|
192
|
-
kind: "wasm-abort",
|
|
193
|
-
subject: "web-tree-sitter",
|
|
194
|
-
reason: error instanceof Error ? error.message : String(error)
|
|
195
|
-
});
|
|
196
|
-
this.onWasmAbort?.();
|
|
197
|
-
}
|
|
198
|
-
return true;
|
|
199
|
-
}
|
|
200
|
-
getRuntimeStats() {
|
|
201
|
-
return {
|
|
202
|
-
languagesLoaded: this.languages.size,
|
|
203
|
-
parsersLoaded: this.parsers.size,
|
|
204
|
-
queryCacheSize: this.queryCache.size,
|
|
205
|
-
queryBatchCacheSize: this.queryBatchCache.size
|
|
206
|
-
};
|
|
207
|
-
}
|
|
208
|
-
getParseCacheStats() {
|
|
209
|
-
return {
|
|
210
|
-
...this.treeCache.getStats(),
|
|
211
|
-
...this.parserCounters
|
|
212
|
-
};
|
|
213
|
-
}
|
|
214
|
-
ensureTreeCacheCapacity(fileCount) {
|
|
215
|
-
this.treeCache.setMaxSize(deriveScanTreeCacheCapacity(fileCount, this.treeCache.getMaxSize()));
|
|
216
|
-
}
|
|
217
|
-
async withParseCacheMeasurement(work, onComplete) {
|
|
218
|
-
const measurement = {
|
|
219
|
-
...createTreeCacheCounters(),
|
|
220
|
-
...createParserCounters()
|
|
221
|
-
};
|
|
222
|
-
this.activeMeasurements++;
|
|
223
|
-
try {
|
|
224
|
-
return await this.parseCacheMeasurement.run(measurement, async () => {
|
|
225
|
-
try {
|
|
226
|
-
return await work();
|
|
227
|
-
} finally {
|
|
228
|
-
const cacheStats = this.treeCache.getStats();
|
|
229
|
-
try {
|
|
230
|
-
onComplete({
|
|
231
|
-
...cacheStats,
|
|
232
|
-
...measurement,
|
|
233
|
-
misses: measurement.lookups - measurement.hits
|
|
234
|
-
});
|
|
235
|
-
} catch {}
|
|
236
|
-
}
|
|
237
|
-
});
|
|
238
|
-
} finally {
|
|
239
|
-
if (--this.activeMeasurements === 0)
|
|
240
|
-
this.parseCacheMeasurement.disable();
|
|
241
|
-
}
|
|
242
|
-
}
|
|
243
|
-
dbg(msg) {
|
|
244
|
-
if (this.verbose) {
|
|
245
|
-
logTreeSitterDiagnostic({
|
|
246
|
-
subsystem: "tree-sitter-client",
|
|
247
|
-
level: "debug",
|
|
248
|
-
message: msg
|
|
249
|
-
});
|
|
250
|
-
}
|
|
251
|
-
}
|
|
252
|
-
webTreeSitterPackageDir() {
|
|
253
|
-
return resolveWebTreeSitterPackageDir({
|
|
254
|
-
resolve: (specifier) => this.grammarDirResolutionDeps.resolvePackage(specifier),
|
|
255
|
-
packageRoot: () => this.grammarDirResolutionDeps.packageRoot(),
|
|
256
|
-
cwd: () => this.grammarDirResolutionDeps.cwd()
|
|
257
|
-
});
|
|
258
|
-
}
|
|
259
|
-
resolveWebTreeSitterAsset(asset) {
|
|
260
|
-
try {
|
|
261
|
-
const resolved = this.grammarDirResolutionDeps.resolvePackage(`web-tree-sitter/${asset}`);
|
|
262
|
-
if (fs.existsSync(resolved))
|
|
263
|
-
return resolved;
|
|
264
|
-
} catch {}
|
|
265
|
-
const pkgDir = this.webTreeSitterPackageDir();
|
|
266
|
-
if (pkgDir) {
|
|
267
|
-
const candidate = path.join(pkgDir, asset);
|
|
268
|
-
if (fs.existsSync(candidate))
|
|
269
|
-
return candidate;
|
|
270
|
-
}
|
|
271
|
-
return;
|
|
272
|
-
}
|
|
273
|
-
_bundledGrammarsDir;
|
|
274
|
-
bundledGrammarsDir() {
|
|
275
|
-
if (this._bundledGrammarsDir)
|
|
276
|
-
return this._bundledGrammarsDir;
|
|
277
|
-
try {
|
|
278
|
-
const dir = resolvePackagePath(import.meta.url, "grammars");
|
|
279
|
-
if (fs.existsSync(dir))
|
|
280
|
-
this._bundledGrammarsDir = dir;
|
|
281
|
-
return this._bundledGrammarsDir;
|
|
282
|
-
} catch {
|
|
283
|
-
return;
|
|
284
|
-
}
|
|
285
|
-
}
|
|
286
|
-
_vendoredGrammarsDir;
|
|
287
|
-
vendoredGrammarsDir() {
|
|
288
|
-
if (this._vendoredGrammarsDir)
|
|
289
|
-
return this._vendoredGrammarsDir;
|
|
290
|
-
try {
|
|
291
|
-
const dir = vendoredGrammarsDir();
|
|
292
|
-
if (fs.existsSync(dir))
|
|
293
|
-
this._vendoredGrammarsDir = dir;
|
|
294
|
-
return this._vendoredGrammarsDir;
|
|
295
|
-
} catch {
|
|
296
|
-
return;
|
|
297
|
-
}
|
|
298
|
-
}
|
|
299
|
-
grammarSourceDirs() {
|
|
300
|
-
const dirs = [];
|
|
301
|
-
const push = (d) => {
|
|
302
|
-
if (d && !dirs.includes(d))
|
|
303
|
-
dirs.push(d);
|
|
304
|
-
};
|
|
305
|
-
push(this.vendoredGrammarsDir());
|
|
306
|
-
push(this.bundledGrammarsDir());
|
|
307
|
-
push(this.grammarsDir || undefined);
|
|
308
|
-
push(this.resolveWebTreeSitterAsset("grammars"));
|
|
309
|
-
return dirs;
|
|
310
|
-
}
|
|
311
|
-
resolveGrammarFile(grammarFile) {
|
|
312
|
-
for (const dir of this.grammarSourceDirs()) {
|
|
313
|
-
const candidate = path.join(dir, grammarFile);
|
|
314
|
-
const stamp = grammarFileStamp(candidate);
|
|
315
|
-
if (!stamp)
|
|
316
|
-
continue;
|
|
317
|
-
if (this.decodeFailedGrammarPaths.get(candidate) === stamp)
|
|
318
|
-
continue;
|
|
319
|
-
if (this.verifiedGrammarPaths.get(candidate) === stamp) {
|
|
320
|
-
if (this.isGrammarVersionCurrent(candidate, grammarFile, stamp)) {
|
|
321
|
-
return candidate;
|
|
322
|
-
}
|
|
323
|
-
continue;
|
|
324
|
-
}
|
|
325
|
-
if (fileHasWasmMagic(candidate)) {
|
|
326
|
-
this.verifiedGrammarPaths.set(candidate, stamp);
|
|
327
|
-
if (this.isGrammarVersionCurrent(candidate, grammarFile, stamp)) {
|
|
328
|
-
return candidate;
|
|
329
|
-
}
|
|
330
|
-
continue;
|
|
331
|
-
}
|
|
332
|
-
this.verifiedGrammarPaths.delete(candidate);
|
|
333
|
-
this.reportPoisonedGrammarFile(candidate, grammarFile);
|
|
334
|
-
}
|
|
335
|
-
return;
|
|
336
|
-
}
|
|
337
|
-
refreshGrammarSessionLatches() {
|
|
338
|
-
const ledgerGen = getDegradationLedgerGeneration();
|
|
339
|
-
if (ledgerGen === this.grammarNotificationsLedgerGen)
|
|
340
|
-
return;
|
|
341
|
-
this.grammarNotificationsLedgerGen = ledgerGen;
|
|
342
|
-
this.grammarLastNotifiedDelayMs.clear();
|
|
343
|
-
this.poisonedGrammarPaths.clear();
|
|
344
|
-
this.staleReportedGrammarPaths.clear();
|
|
345
|
-
}
|
|
346
|
-
reportPoisonedGrammarFile(candidate, grammarFile) {
|
|
347
|
-
this.refreshGrammarSessionLatches();
|
|
348
|
-
if (this.poisonedGrammarPaths.has(candidate))
|
|
349
|
-
return;
|
|
350
|
-
this.poisonedGrammarPaths.add(candidate);
|
|
351
|
-
logTreeSitterDiagnostic({
|
|
352
|
-
subsystem: "tree-sitter-client",
|
|
353
|
-
level: "warn",
|
|
354
|
-
message: `ignoring ${candidate}: the file is not a wasm module (missing the \\0asm preamble) — ` + `most likely a captive-portal or proxy page written by an earlier download (#1548). ` + `dm-lens will treat the grammar as missing and try to fetch it again.`,
|
|
355
|
-
metadata: { grammarFile, path: candidate, outcome: "not-wasm" }
|
|
356
|
-
});
|
|
357
|
-
incrementDegradationCount({
|
|
358
|
-
kind: "grammar-blocked",
|
|
359
|
-
subject: grammarFile,
|
|
360
|
-
reason: "on-disk grammar file is not a wasm module — ignored, re-fetching"
|
|
361
|
-
});
|
|
362
|
-
}
|
|
363
|
-
isGrammarVersionCurrent(candidate, grammarFile, stamp) {
|
|
364
|
-
if (isVendoredGrammar(grammarFile))
|
|
365
|
-
return true;
|
|
366
|
-
if (this.staleGrammarVersionAt.get(candidate) === stamp) {
|
|
367
|
-
this.reportStaleGrammarVersion(candidate, grammarFile);
|
|
368
|
-
return false;
|
|
369
|
-
}
|
|
370
|
-
if (this.verifiedGrammarVersionAt.get(candidate) === stamp)
|
|
371
|
-
return true;
|
|
372
|
-
const pinnedHash = pinnedGrammarHash(grammarFile);
|
|
373
|
-
if (!pinnedHash) {
|
|
374
|
-
this.verifiedGrammarVersionAt.set(candidate, stamp);
|
|
375
|
-
return true;
|
|
376
|
-
}
|
|
377
|
-
const actualHash = grammarFileSha256(candidate);
|
|
378
|
-
if (actualHash === pinnedHash) {
|
|
379
|
-
this.verifiedGrammarVersionAt.set(candidate, stamp);
|
|
380
|
-
return true;
|
|
381
|
-
}
|
|
382
|
-
this.verifiedGrammarVersionAt.delete(candidate);
|
|
383
|
-
this.staleGrammarVersionAt.set(candidate, stamp);
|
|
384
|
-
this.reportStaleGrammarVersion(candidate, grammarFile);
|
|
385
|
-
return false;
|
|
386
|
-
}
|
|
387
|
-
reportStaleGrammarVersion(candidate, grammarFile) {
|
|
388
|
-
this.refreshGrammarSessionLatches();
|
|
389
|
-
if (this.staleReportedGrammarPaths.has(candidate))
|
|
390
|
-
return;
|
|
391
|
-
this.staleReportedGrammarPaths.add(candidate);
|
|
392
|
-
logTreeSitterDiagnostic({
|
|
393
|
-
subsystem: "tree-sitter-client",
|
|
394
|
-
level: "warn",
|
|
395
|
-
message: `ignoring ${candidate}: its sha256 no longer matches the pinned grammar ` + `manifest — the pinned tree-sitter-wasms version (or a SOURCE_OVERRIDES entry) ` + `moved since this file was downloaded. dm-lens will treat the grammar as ` + `missing and re-fetch the current build (#1760).`,
|
|
396
|
-
metadata: { grammarFile, path: candidate, outcome: "stale-version" }
|
|
397
|
-
});
|
|
398
|
-
incrementDegradationCount({
|
|
399
|
-
kind: "grammar-blocked",
|
|
400
|
-
subject: grammarFile,
|
|
401
|
-
reason: "cached grammar no longer matches the pinned manifest hash — ignored, re-fetching"
|
|
402
|
-
});
|
|
403
|
-
}
|
|
404
|
-
findGrammarsDir(deps = this.grammarDirResolutionDeps) {
|
|
405
|
-
const grammarsDir = deps.resolveAsset("grammars");
|
|
406
|
-
if (grammarsDir && fs.existsSync(path.join(grammarsDir, "tree-sitter-typescript.wasm"))) {
|
|
407
|
-
return grammarsDir;
|
|
408
|
-
}
|
|
409
|
-
try {
|
|
410
|
-
const wasmsOut = path.join(path.dirname(deps.resolvePackage("tree-sitter-wasms/package.json")), "out");
|
|
411
|
-
if (fs.existsSync(wasmsOut))
|
|
412
|
-
return wasmsOut;
|
|
413
|
-
} catch {}
|
|
414
|
-
const cwdWasms = path.join(deps.cwd(), "node_modules", "tree-sitter-wasms", "out");
|
|
415
|
-
if (fs.existsSync(cwdWasms))
|
|
416
|
-
return cwdWasms;
|
|
417
|
-
return "";
|
|
418
|
-
}
|
|
419
|
-
grammarsWriteDir() {
|
|
420
|
-
const pkgDir = this.webTreeSitterPackageDir();
|
|
421
|
-
return pkgDir ? path.join(pkgDir, "grammars") : undefined;
|
|
422
|
-
}
|
|
423
|
-
async ensureGrammar(grammarFile) {
|
|
424
|
-
if (this.resolveGrammarFile(grammarFile)) {
|
|
425
|
-
return true;
|
|
426
|
-
}
|
|
427
|
-
if (!assertInstallAllowed(`tree-sitter grammar fetch: ${grammarFile}`)) {
|
|
428
|
-
const unavailable = `tree-sitter grammar '${grammarFile}' is unavailable because the project is not trusted; ` + `runtime grammar downloads are disabled until trust is granted.`;
|
|
429
|
-
logTreeSitterDiagnostic({
|
|
430
|
-
subsystem: "tree-sitter-client",
|
|
431
|
-
message: unavailable,
|
|
432
|
-
metadata: { grammarFile, outcome: "trust-gated" }
|
|
433
|
-
});
|
|
434
|
-
recordDegradation({
|
|
435
|
-
kind: "grammar-blocked",
|
|
436
|
-
subject: grammarFile,
|
|
437
|
-
reason: "runtime grammar download blocked because project is untrusted"
|
|
438
|
-
});
|
|
439
|
-
const generation = getProjectTrustGeneration();
|
|
440
|
-
if (generation !== this.trustNotificationsGeneration) {
|
|
441
|
-
this.trustNotificationsGeneration = generation;
|
|
442
|
-
this.trustBlockedGrammarNotifications.clear();
|
|
443
|
-
}
|
|
444
|
-
if (!this.trustBlockedGrammarNotifications.has(grammarFile)) {
|
|
445
|
-
this.trustBlockedGrammarNotifications.add(grammarFile);
|
|
446
|
-
notifyUserDegradation(`dm-lens: ${unavailable}`);
|
|
447
|
-
}
|
|
448
|
-
return false;
|
|
449
|
-
}
|
|
450
|
-
const inflight = this.grammarEnsurePromises.get(grammarFile);
|
|
451
|
-
if (inflight)
|
|
452
|
-
return inflight;
|
|
453
|
-
const retryAt = this.grammarRetryAtMs.get(grammarFile);
|
|
454
|
-
if (retryAt !== undefined && Date.now() < retryAt) {
|
|
455
|
-
return false;
|
|
456
|
-
}
|
|
457
|
-
const task = (async () => {
|
|
458
|
-
try {
|
|
459
|
-
return await this.fetchGrammar(grammarFile);
|
|
460
|
-
} catch (err) {
|
|
461
|
-
const message = err instanceof Error ? err.message : String(err);
|
|
462
|
-
this.recordGrammarFailure(grammarFile, `The runtime grammar fetch threw an unexpected error (${message}).`, true, "download-failed");
|
|
463
|
-
return false;
|
|
464
|
-
}
|
|
465
|
-
})();
|
|
466
|
-
this.grammarEnsurePromises.set(grammarFile, task);
|
|
467
|
-
task.finally(() => {
|
|
468
|
-
if (this.grammarEnsurePromises.get(grammarFile) === task) {
|
|
469
|
-
this.grammarEnsurePromises.delete(grammarFile);
|
|
470
|
-
}
|
|
471
|
-
}).catch(() => {});
|
|
472
|
-
return task;
|
|
473
|
-
}
|
|
474
|
-
async fetchGrammar(grammarFile) {
|
|
475
|
-
if (isVendoredGrammar(grammarFile)) {
|
|
476
|
-
const { reason } = vendoredGrammarRefusal(grammarFile);
|
|
477
|
-
this.recordGrammarFailure(grammarFile, reason ?? `${grammarFile} is missing from vendor/grammars/.`, false);
|
|
478
|
-
return false;
|
|
479
|
-
}
|
|
480
|
-
const dir = this.grammarsDir && fs.existsSync(this.grammarsDir) ? this.grammarsDir : this.grammarsWriteDir();
|
|
481
|
-
if (!dir) {
|
|
482
|
-
this.recordGrammarFailure(grammarFile, "No grammars directory could be resolved for the runtime fetch: the web-tree-sitter package is not locatable from this runtime, from dm-lens's own package root, or from the working directory.", true, "write-dir-unresolvable");
|
|
483
|
-
return false;
|
|
484
|
-
}
|
|
485
|
-
const writeBlock = grammarWriteBlockedReason(dir);
|
|
486
|
-
if (writeBlock) {
|
|
487
|
-
this.recordGrammarFailure(grammarFile, `The grammars directory ${dir} cannot be written to (${writeBlock}), so the runtime fetch has nowhere to land.`, true, "write-dir-unwritable");
|
|
488
|
-
return false;
|
|
489
|
-
}
|
|
490
|
-
const { ok, retryable, reason } = await downloadGrammarDetailed(dir, grammarFile);
|
|
491
|
-
if (ok) {
|
|
492
|
-
if (!this.grammarsDir)
|
|
493
|
-
this.grammarsDir = dir;
|
|
494
|
-
this.grammarRetryAtMs.delete(grammarFile);
|
|
495
|
-
this.grammarFailureAttempts.delete(grammarFile);
|
|
496
|
-
this.grammarLastNotifiedDelayMs.delete(grammarFile);
|
|
497
|
-
logTreeSitterDiagnostic({
|
|
498
|
-
subsystem: "tree-sitter-client",
|
|
499
|
-
level: "warn",
|
|
500
|
-
message: `fetched missing tree-sitter grammar ${grammarFile} at runtime (install scripts were skipped by the package manager)`,
|
|
501
|
-
metadata: { grammarFile, outcome: "fetched" }
|
|
502
|
-
});
|
|
503
|
-
} else {
|
|
504
|
-
this.recordGrammarFailure(grammarFile, reason ?? "The package manager skipped install scripts and the runtime download failed.", retryable, "download-failed");
|
|
505
|
-
}
|
|
506
|
-
return ok;
|
|
507
|
-
}
|
|
508
|
-
recordGrammarFailure(grammarFile, detail, retryable, cause) {
|
|
509
|
-
const attempts = (this.grammarFailureAttempts.get(grammarFile) ?? 0) + 1;
|
|
510
|
-
this.grammarFailureAttempts.set(grammarFile, attempts);
|
|
511
|
-
const retryDelayMs = retryable ? transientRetryDelayMs(attempts, "probe-timeout") : undefined;
|
|
512
|
-
this.grammarRetryAtMs.set(grammarFile, retryDelayMs === undefined ? Number.POSITIVE_INFINITY : Date.now() + retryDelayMs);
|
|
513
|
-
const remedy = cause === "write-dir-unresolvable" ? `This is a packaging/module-resolution fault, not a download problem: reinstall dm-lens so its ` + `web-tree-sitter dependency is present, or drop the wasm into dm-lens's own grammars/ directory.` : cause === "write-dir-unwritable" ? `This is a filesystem-permission fault, not a download problem: make that directory writable ` + `for the user dm-lens runs as, or reinstall dm-lens as that user.` : retryable ? `if the problem persists, allow the package manager's build scripts ` + `(pnpm approve-builds / bun trustedDependencies) or restore network access.` : `Fix: reinstall with a manager that runs postinstall, allow its build scripts ` + `(pnpm approve-builds / bun trustedDependencies), or restore network access.`;
|
|
514
|
-
const unavailable = `tree-sitter grammar '${grammarFile}' is unavailable — symbol search, ` + `module reports and structural rules for this language will be degraded. ` + `${detail} ` + (retryable ? `dm-lens will retry automatically in ${Math.round(retryDelayMs / 1000)}s; ` : `The grammar source reports it does not exist (not a network problem), so this will not ` + `resolve on retry. `) + remedy;
|
|
515
|
-
logTreeSitterDiagnostic({
|
|
516
|
-
subsystem: "tree-sitter-client",
|
|
517
|
-
message: unavailable,
|
|
518
|
-
metadata: {
|
|
519
|
-
grammarFile,
|
|
520
|
-
outcome: "unavailable",
|
|
521
|
-
retryable,
|
|
522
|
-
retryDelayMs,
|
|
523
|
-
attempts,
|
|
524
|
-
...cause ? { cause } : {}
|
|
525
|
-
}
|
|
526
|
-
});
|
|
527
|
-
incrementDegradationCount({
|
|
528
|
-
kind: "grammar-blocked",
|
|
529
|
-
subject: grammarFile,
|
|
530
|
-
reason: retryable ? `runtime grammar download failed — retryable (${detail.trim()})` : `runtime grammar download failed — durable (${detail.trim()})`
|
|
531
|
-
});
|
|
532
|
-
this.refreshGrammarSessionLatches();
|
|
533
|
-
const notifyKey = retryDelayMs ?? -1;
|
|
534
|
-
if (this.grammarLastNotifiedDelayMs.get(grammarFile) !== notifyKey) {
|
|
535
|
-
this.grammarLastNotifiedDelayMs.set(grammarFile, notifyKey);
|
|
536
|
-
notifyUserDegradation(`dm-lens: ${unavailable}`);
|
|
537
|
-
}
|
|
538
|
-
}
|
|
539
|
-
recordGrammarLoadFailure(grammarPath, grammarFile, err) {
|
|
540
|
-
const stamp = grammarFileStamp(grammarPath);
|
|
541
|
-
this.verifiedGrammarPaths.delete(grammarPath);
|
|
542
|
-
if (stamp)
|
|
543
|
-
this.decodeFailedGrammarPaths.set(grammarPath, stamp);
|
|
544
|
-
const message = err instanceof Error ? err.message : String(err);
|
|
545
|
-
logTreeSitterDiagnostic({
|
|
546
|
-
subsystem: "tree-sitter-client",
|
|
547
|
-
level: "warn",
|
|
548
|
-
message: `Language.load failed for ${grammarPath} even though the wasm preamble check ` + `passed (${message}) — most likely a truncated download (#1564). Treating the ` + `grammar as missing so the next demand re-fetches it.`,
|
|
549
|
-
metadata: { grammarFile, path: grammarPath, outcome: "load-failed" }
|
|
550
|
-
});
|
|
551
|
-
this.recordGrammarFailure(grammarFile, `Language.load failed on a resolved grammar file (${message}).`, true);
|
|
552
|
-
}
|
|
553
|
-
async init() {
|
|
554
|
-
if (this.wasmAborted)
|
|
555
|
-
return false;
|
|
556
|
-
if (this.initialized)
|
|
557
|
-
return true;
|
|
558
|
-
if (this.webTreeSitterLoadFailed)
|
|
559
|
-
return false;
|
|
560
|
-
if (this.initPromise)
|
|
561
|
-
return this.initPromise;
|
|
562
|
-
this.initPromise = (async () => {
|
|
563
|
-
try {
|
|
564
|
-
let mod;
|
|
565
|
-
try {
|
|
566
|
-
mod = await loadWebTreeSitter();
|
|
567
|
-
} catch (err) {
|
|
568
|
-
const classification = classifyWebTreeSitterLoadFailure(err);
|
|
569
|
-
if (classification === "evaluation") {
|
|
570
|
-
this.webTreeSitterLoadFailed = true;
|
|
571
|
-
}
|
|
572
|
-
recordDegradationOnce({
|
|
573
|
-
kind: "web-tree-sitter-load-failed",
|
|
574
|
-
subject: "web-tree-sitter",
|
|
575
|
-
reason: (err instanceof Error ? err.message : String(err)) + (classification === "resolution" ? " (resolution failure — retryable)" : " (evaluation failure — latched for the session)")
|
|
576
|
-
});
|
|
577
|
-
throw err;
|
|
578
|
-
}
|
|
579
|
-
const anyMod = mod;
|
|
580
|
-
const ParserClass = anyMod.Parser || anyMod.default || anyMod;
|
|
581
|
-
if (!ParserClass || typeof ParserClass.init !== "function") {
|
|
582
|
-
this.dbg("Parser class not found or missing init method");
|
|
583
|
-
return false;
|
|
584
|
-
}
|
|
585
|
-
this.ParserClass = ParserClass;
|
|
586
|
-
this.LanguageLoader = mod.Language;
|
|
587
|
-
const wasmPath = this.resolveWebTreeSitterAsset("tree-sitter.wasm");
|
|
588
|
-
if (!wasmPath) {
|
|
589
|
-
this.dbg("Could not resolve tree-sitter.wasm");
|
|
590
|
-
return false;
|
|
591
|
-
}
|
|
592
|
-
const wasmDir = path.dirname(wasmPath);
|
|
593
|
-
this.dbg(`Looking for WASM at: ${wasmPath}, exists: ${fs.existsSync(wasmPath)}`);
|
|
594
|
-
await ParserClass.init({
|
|
595
|
-
locateFile: (scriptName) => {
|
|
596
|
-
const fullPath = path.join(wasmDir, scriptName);
|
|
597
|
-
this.dbg(`locateFile: ${scriptName} -> ${fullPath}`);
|
|
598
|
-
return fullPath;
|
|
599
|
-
}
|
|
600
|
-
});
|
|
601
|
-
this.initialized = true;
|
|
602
|
-
return true;
|
|
603
|
-
} catch (err) {
|
|
604
|
-
this.reportWasmAbort(err);
|
|
605
|
-
this.dbg(`Init error: ${err}`);
|
|
606
|
-
return false;
|
|
607
|
-
} finally {
|
|
608
|
-
this.initPromise = null;
|
|
609
|
-
}
|
|
610
|
-
})();
|
|
611
|
-
return this.initPromise;
|
|
612
|
-
}
|
|
613
|
-
resetLoadStateForSession() {
|
|
614
|
-
this.webTreeSitterLoadFailed = false;
|
|
615
|
-
}
|
|
616
|
-
async loadLanguage(languageId) {
|
|
617
|
-
if (this.wasmAborted)
|
|
618
|
-
return null;
|
|
619
|
-
this.dbg(`Loading language: ${languageId}`);
|
|
620
|
-
if (this.languages.has(languageId)) {
|
|
621
|
-
this.dbg(`Language ${languageId} already loaded`);
|
|
622
|
-
return this.languages.get(languageId);
|
|
623
|
-
}
|
|
624
|
-
if (!this.ParserClass) {
|
|
625
|
-
this.dbg(`ParserClass not initialized`);
|
|
626
|
-
return null;
|
|
627
|
-
}
|
|
628
|
-
const grammarFile = LANGUAGE_TO_GRAMMAR[languageId];
|
|
629
|
-
if (!grammarFile) {
|
|
630
|
-
this.dbg(`No grammar file for ${languageId}`);
|
|
631
|
-
return null;
|
|
632
|
-
}
|
|
633
|
-
const blockReason = grammarBlockReason(grammarFile);
|
|
634
|
-
if (blockReason) {
|
|
635
|
-
this.dbg(`Grammar ${grammarFile} blocked on this runtime — ${blockReason}`);
|
|
636
|
-
recordDegradation({
|
|
637
|
-
kind: "grammar-blocked",
|
|
638
|
-
subject: grammarFile,
|
|
639
|
-
reason: blockReason
|
|
640
|
-
});
|
|
641
|
-
return null;
|
|
642
|
-
}
|
|
643
|
-
let grammarPath = this.resolveGrammarFile(grammarFile);
|
|
644
|
-
if (!grammarPath) {
|
|
645
|
-
if (await this.ensureGrammar(grammarFile)) {
|
|
646
|
-
grammarPath = this.resolveGrammarFile(grammarFile);
|
|
647
|
-
}
|
|
648
|
-
}
|
|
649
|
-
this.dbg(`Grammar path: ${grammarPath}, exists: ${grammarPath && fs.existsSync(grammarPath)}`);
|
|
650
|
-
if (!grammarPath || !fs.existsSync(grammarPath)) {
|
|
651
|
-
this.dbg(`Grammar file not found: ${grammarPath}`);
|
|
652
|
-
return null;
|
|
653
|
-
}
|
|
654
|
-
try {
|
|
655
|
-
if (!this.LanguageLoader?.load) {
|
|
656
|
-
this.dbg(`LanguageLoader.load not available`);
|
|
657
|
-
return null;
|
|
658
|
-
}
|
|
659
|
-
this.dbg(`Calling Language.load...`);
|
|
660
|
-
const language = await this.LanguageLoader.load(grammarPath);
|
|
661
|
-
this.dbg(`Language loaded: ${language?.name || "unknown"}`);
|
|
662
|
-
if (language) {
|
|
663
|
-
this.languages.set(languageId, language);
|
|
664
|
-
}
|
|
665
|
-
return language;
|
|
666
|
-
} catch (err) {
|
|
667
|
-
if (!this.reportWasmAbort(err)) {
|
|
668
|
-
this.recordGrammarLoadFailure(grammarPath, grammarFile, err);
|
|
669
|
-
}
|
|
670
|
-
this.dbg(`Language load error: ${err}`);
|
|
671
|
-
return null;
|
|
672
|
-
}
|
|
673
|
-
}
|
|
674
|
-
async getParser(languageId) {
|
|
675
|
-
if (this.wasmAborted)
|
|
676
|
-
return null;
|
|
677
|
-
if (this.parsers.has(languageId)) {
|
|
678
|
-
return this.parsers.get(languageId);
|
|
679
|
-
}
|
|
680
|
-
const language = await this.loadLanguage(languageId);
|
|
681
|
-
if (!language || !this.ParserClass)
|
|
682
|
-
return null;
|
|
683
|
-
const parser = new this.ParserClass;
|
|
684
|
-
parser.setLanguage(language);
|
|
685
|
-
this.parsers.set(languageId, parser);
|
|
686
|
-
return parser;
|
|
687
|
-
}
|
|
688
|
-
async parseFile(filePath, languageId, contentOverride) {
|
|
689
|
-
const outcome = await this.parseFileAndUse(filePath, languageId, contentOverride, (tree) => tree);
|
|
690
|
-
return outcome.parsed ? outcome.value : null;
|
|
691
|
-
}
|
|
692
|
-
async withParsedTree(filePath, languageId, contentOverride, consume) {
|
|
693
|
-
return this.parseFileAndUse(filePath, languageId, contentOverride, consume);
|
|
694
|
-
}
|
|
695
|
-
async parseFileAndUse(filePath, languageId, contentOverride, consume) {
|
|
696
|
-
this.dbg(`Parsing ${filePath} with language ${languageId}`);
|
|
697
|
-
const parser = await this.getParser(languageId);
|
|
698
|
-
if (!parser) {
|
|
699
|
-
this.dbg(`Failed to get parser for ${languageId}`);
|
|
700
|
-
return NOT_PARSED;
|
|
701
|
-
}
|
|
702
|
-
let tree;
|
|
703
|
-
try {
|
|
704
|
-
const content = contentOverride ?? fs.readFileSync(filePath, "utf-8");
|
|
705
|
-
this.dbg(`File content length: ${content.length}`);
|
|
706
|
-
const cachedTree = this.treeCache.get(filePath, content, languageId);
|
|
707
|
-
if (cachedTree) {
|
|
708
|
-
this.dbg(`Using cached tree for ${filePath}`);
|
|
709
|
-
tree = cachedTree;
|
|
710
|
-
} else {
|
|
711
|
-
const parseStartedAt = performance.now();
|
|
712
|
-
this.recordParserCounter("parserInvocations");
|
|
713
|
-
try {
|
|
714
|
-
tree = parser.parse(content);
|
|
715
|
-
} catch (err) {
|
|
716
|
-
this.recordParserCounter("parserFailures");
|
|
717
|
-
throw err;
|
|
718
|
-
} finally {
|
|
719
|
-
this.recordParserCounter("parserDurationMs", performance.now() - parseStartedAt);
|
|
720
|
-
}
|
|
721
|
-
this.dbg(`Parsed, root node type: ${tree.rootNode.type}`);
|
|
722
|
-
this.treeCache.set(filePath, content, languageId, tree);
|
|
723
|
-
}
|
|
724
|
-
} catch (err) {
|
|
725
|
-
this.reportWasmAbort(err);
|
|
726
|
-
this.dbg(`Parse error: ${err}`);
|
|
727
|
-
return NOT_PARSED;
|
|
728
|
-
}
|
|
729
|
-
try {
|
|
730
|
-
return { parsed: true, value: consume(tree) };
|
|
731
|
-
} catch (error) {
|
|
732
|
-
this.reportWasmAbort(error);
|
|
733
|
-
throw error;
|
|
734
|
-
}
|
|
735
|
-
}
|
|
736
|
-
extractInjections(filePath, content) {
|
|
737
|
-
const injections = [];
|
|
738
|
-
const sqlPattern = /\b(sql|query|execute)\s*`([^`]+)`/gi;
|
|
739
|
-
let match;
|
|
740
|
-
while ((match = sqlPattern.exec(content)) !== null) {
|
|
741
|
-
const lines = content.slice(0, match.index).split(`
|
|
742
|
-
`);
|
|
743
|
-
injections.push({
|
|
744
|
-
type: "sql",
|
|
745
|
-
content: match[2],
|
|
746
|
-
line: lines.length,
|
|
747
|
-
column: lines[lines.length - 1].length
|
|
748
|
-
});
|
|
749
|
-
}
|
|
750
|
-
const cssPattern = /\b(styled(?:\.\w+)?|css)\s*`([^`]+)`/gi;
|
|
751
|
-
while ((match = cssPattern.exec(content)) !== null) {
|
|
752
|
-
const lines = content.slice(0, match.index).split(`
|
|
753
|
-
`);
|
|
754
|
-
injections.push({
|
|
755
|
-
type: "css",
|
|
756
|
-
content: match[2],
|
|
757
|
-
line: lines.length,
|
|
758
|
-
column: lines[lines.length - 1].length
|
|
759
|
-
});
|
|
760
|
-
}
|
|
761
|
-
const regexPattern = /new\s+RegExp\s*\(\s*`([^`]+)`/gi;
|
|
762
|
-
while ((match = regexPattern.exec(content)) !== null) {
|
|
763
|
-
const lines = content.slice(0, match.index).split(`
|
|
764
|
-
`);
|
|
765
|
-
injections.push({
|
|
766
|
-
type: "regex",
|
|
767
|
-
content: match[1],
|
|
768
|
-
line: lines.length,
|
|
769
|
-
column: lines[lines.length - 1].length
|
|
770
|
-
});
|
|
771
|
-
}
|
|
772
|
-
this.dbg(`Found ${injections.length} injections in ${filePath}`);
|
|
773
|
-
return injections;
|
|
774
|
-
}
|
|
775
|
-
isAvailable() {
|
|
776
|
-
if (this.wasmAborted)
|
|
777
|
-
return false;
|
|
778
|
-
if (this.resolveGrammarFile("tree-sitter-typescript.wasm"))
|
|
779
|
-
return true;
|
|
780
|
-
const dir = this.findGrammarsDir();
|
|
781
|
-
this.grammarsDir = dir;
|
|
782
|
-
return !!dir && fs.existsSync(dir);
|
|
783
|
-
}
|
|
784
|
-
async isLanguageSupported(languageId) {
|
|
785
|
-
if (this.wasmAborted)
|
|
786
|
-
return false;
|
|
787
|
-
if (!this.initialized)
|
|
788
|
-
await this.init();
|
|
789
|
-
const language = await this.loadLanguage(languageId);
|
|
790
|
-
return language !== null;
|
|
791
|
-
}
|
|
792
|
-
getLanguage(languageId) {
|
|
793
|
-
if (this.wasmAborted)
|
|
794
|
-
return null;
|
|
795
|
-
return this.languages.get(languageId) || null;
|
|
796
|
-
}
|
|
797
|
-
async structuralSearch(pattern, languageId, rootDir, options = {}) {
|
|
798
|
-
if (!this.initialized) {
|
|
799
|
-
const ok = await this.init();
|
|
800
|
-
if (!ok)
|
|
801
|
-
return [];
|
|
802
|
-
}
|
|
803
|
-
try {
|
|
804
|
-
await this.queryLoader.loadQueries(rootDir);
|
|
805
|
-
} catch (err) {
|
|
806
|
-
this.dbg(`Failed to load queries for ${rootDir}: ${err}`);
|
|
807
|
-
}
|
|
808
|
-
this.dbg(`Compiling pattern: ${pattern.slice(0, 50)}...`);
|
|
809
|
-
const compiled = await this.compileQuery(pattern, languageId);
|
|
810
|
-
if (!compiled) {
|
|
811
|
-
this.dbg(`Pattern compilation failed`);
|
|
812
|
-
return [];
|
|
813
|
-
}
|
|
814
|
-
this.dbg(`Pattern compiled, metavars: ${compiled.metavars.join(", ")}`);
|
|
815
|
-
const files = this.collectFiles(rootDir, languageId, options.fileFilter);
|
|
816
|
-
this.dbg(`Scanning ${files.length} files...`);
|
|
817
|
-
const matches = [];
|
|
818
|
-
const maxResults = options.maxResults ?? 50;
|
|
819
|
-
for (const file of files) {
|
|
820
|
-
if (matches.length >= maxResults)
|
|
821
|
-
break;
|
|
822
|
-
const fileMatches = await this.searchFileWithQuery(file, compiled.query, compiled.metavars, languageId, pattern, compiled.postFilter, compiled.postFilterParams);
|
|
823
|
-
matches.push(...fileMatches);
|
|
824
|
-
}
|
|
825
|
-
return matches.slice(0, maxResults);
|
|
826
|
-
}
|
|
827
|
-
async runQueryOnFile(queryDef, filePath, languageId, options = {}, contentOverride) {
|
|
828
|
-
if (!this.initialized) {
|
|
829
|
-
const ok = await this.init();
|
|
830
|
-
if (!ok)
|
|
831
|
-
return [];
|
|
832
|
-
}
|
|
833
|
-
const compiled = await this.compileRawQuery(queryDef.id, queryDef.query, queryDef.metavars, languageId, queryDef.post_filter, queryDef.post_filter_params);
|
|
834
|
-
if (!compiled)
|
|
835
|
-
return [];
|
|
836
|
-
const matches = await this.searchFileWithQuery(filePath, compiled.query, compiled.metavars, languageId, queryDef.id, compiled.postFilter, compiled.postFilterParams, contentOverride);
|
|
837
|
-
const maxResults = options.maxResults ?? 50;
|
|
838
|
-
return matches.slice(0, maxResults);
|
|
839
|
-
}
|
|
840
|
-
async runQueriesOnFile(queryDefs, filePath, languageId, options = {}, contentOverride) {
|
|
841
|
-
if (queryDefs.length === 0)
|
|
842
|
-
return [];
|
|
843
|
-
if (!this.initialized) {
|
|
844
|
-
const ok = await this.init();
|
|
845
|
-
if (!ok)
|
|
846
|
-
return [];
|
|
847
|
-
}
|
|
848
|
-
const maxResults = options.maxResults ?? 50;
|
|
849
|
-
const batch = await this.compileQueryBatch(queryDefs, languageId);
|
|
850
|
-
if (!batch) {
|
|
851
|
-
const results = [];
|
|
852
|
-
for (const queryDef of queryDefs) {
|
|
853
|
-
const matches = await this.runQueryOnFile(queryDef, filePath, languageId, options, contentOverride);
|
|
854
|
-
for (const match of matches)
|
|
855
|
-
results.push({ queryDef, match });
|
|
856
|
-
}
|
|
857
|
-
return results;
|
|
858
|
-
}
|
|
859
|
-
const perQuery = new Map;
|
|
860
|
-
await this.parseFileAndUse(filePath, languageId, contentOverride, (tree) => {
|
|
861
|
-
try {
|
|
862
|
-
const rootNode = tree.rootNode;
|
|
863
|
-
for (const match of batch.query.matches(rootNode)) {
|
|
864
|
-
const owner = batch.ownerOfPattern[match.patternIndex];
|
|
865
|
-
if (owner === undefined)
|
|
866
|
-
continue;
|
|
867
|
-
const bucket = perQuery.get(owner) ?? [];
|
|
868
|
-
if (bucket.length >= maxResults)
|
|
869
|
-
continue;
|
|
870
|
-
const entry = batch.entries[owner];
|
|
871
|
-
const captures = {};
|
|
872
|
-
for (const capture of match.captures) {
|
|
873
|
-
if (entry.metavars.includes(capture.name)) {
|
|
874
|
-
captures[capture.name] = capture.node;
|
|
875
|
-
}
|
|
876
|
-
}
|
|
877
|
-
if (!this.evaluatePredicates(batch.query, match))
|
|
878
|
-
continue;
|
|
879
|
-
if (entry.postFilter && !this.applyPostFilter(entry.postFilter, entry.postFilterParams, captures, rootNode)) {
|
|
880
|
-
continue;
|
|
881
|
-
}
|
|
882
|
-
if (match.captures.length === 0)
|
|
883
|
-
continue;
|
|
884
|
-
const firstNode = match.captures[0].node;
|
|
885
|
-
const textCaptures = {};
|
|
886
|
-
for (const [name, node] of Object.entries(captures)) {
|
|
887
|
-
textCaptures[name] = node.text;
|
|
888
|
-
}
|
|
889
|
-
bucket.push({
|
|
890
|
-
file: filePath,
|
|
891
|
-
line: firstNode.startPosition.row + 1,
|
|
892
|
-
column: firstNode.startPosition.column + 1,
|
|
893
|
-
matchedText: firstNode.text,
|
|
894
|
-
nodeType: firstNode.type,
|
|
895
|
-
captures: textCaptures
|
|
896
|
-
});
|
|
897
|
-
perQuery.set(owner, bucket);
|
|
898
|
-
}
|
|
899
|
-
} catch (err) {
|
|
900
|
-
this.reportWasmAbort(err);
|
|
901
|
-
this.dbg(`Batched query matching error: ${err}`);
|
|
902
|
-
}
|
|
903
|
-
});
|
|
904
|
-
const results = [];
|
|
905
|
-
for (let i = 0;i < batch.entries.length; i++) {
|
|
906
|
-
for (const match of perQuery.get(i) ?? []) {
|
|
907
|
-
results.push({ queryDef: batch.entries[i].queryDef, match });
|
|
908
|
-
}
|
|
909
|
-
}
|
|
910
|
-
return results;
|
|
911
|
-
}
|
|
912
|
-
async compileQueryBatch(queryDefs, languageId) {
|
|
913
|
-
const identity = crypto.createHash("sha256").update(JSON.stringify(queryDefs)).digest("hex");
|
|
914
|
-
const cacheKey = this.getQueryCacheKey(`batch:${identity}`, languageId);
|
|
915
|
-
const cached = this.queryBatchCache.get(cacheKey);
|
|
916
|
-
if (cached !== undefined) {
|
|
917
|
-
this.queryBatchCache.delete(cacheKey);
|
|
918
|
-
this.queryBatchCache.set(cacheKey, cached);
|
|
919
|
-
return cached;
|
|
920
|
-
}
|
|
921
|
-
const language = await this.loadLanguage(languageId);
|
|
922
|
-
if (!language) {
|
|
923
|
-
const failures = (this.queryBatchLoadFailures.get(cacheKey) ?? 0) + 1;
|
|
924
|
-
if (failures >= QUERY_BATCH_MAX_LOAD_FAILURES) {
|
|
925
|
-
this.dbg(`Batch: grammar for ${languageId} failed to load ${failures} times — caching miss`);
|
|
926
|
-
this.queryBatchLoadFailures.delete(cacheKey);
|
|
927
|
-
this.cacheQueryBatch(cacheKey, null);
|
|
928
|
-
} else {
|
|
929
|
-
this.queryBatchLoadFailures.set(cacheKey, failures);
|
|
930
|
-
}
|
|
931
|
-
return null;
|
|
932
|
-
}
|
|
933
|
-
this.queryBatchLoadFailures.delete(cacheKey);
|
|
934
|
-
const build = async () => {
|
|
935
|
-
const Query = (await loadWebTreeSitter()).Query;
|
|
936
|
-
const entries = [];
|
|
937
|
-
const sources = [];
|
|
938
|
-
const ownerOfPattern = [];
|
|
939
|
-
for (const queryDef of queryDefs) {
|
|
940
|
-
let patternCount;
|
|
941
|
-
try {
|
|
942
|
-
const probe = new Query(language, queryDef.query);
|
|
943
|
-
patternCount = probe.patternCount();
|
|
944
|
-
probe.delete?.();
|
|
945
|
-
} catch (err) {
|
|
946
|
-
if (this.reportWasmAbort(err))
|
|
947
|
-
return null;
|
|
948
|
-
this.dbg(`Batch: skipping ${queryDef.id} for ${languageId}: ${err}`);
|
|
949
|
-
this.reportQueryCompileFailure(queryDef.id, languageId, err);
|
|
950
|
-
continue;
|
|
951
|
-
}
|
|
952
|
-
const owner = entries.length;
|
|
953
|
-
entries.push({
|
|
954
|
-
queryDef,
|
|
955
|
-
metavars: queryDef.metavars ?? [],
|
|
956
|
-
postFilter: queryDef.post_filter,
|
|
957
|
-
postFilterParams: queryDef.post_filter_params
|
|
958
|
-
});
|
|
959
|
-
sources.push(queryDef.query);
|
|
960
|
-
for (let i = 0;i < patternCount; i++)
|
|
961
|
-
ownerOfPattern.push(owner);
|
|
962
|
-
}
|
|
963
|
-
if (entries.length === 0)
|
|
964
|
-
return null;
|
|
965
|
-
try {
|
|
966
|
-
const query = new Query(language, sources.join(`
|
|
967
|
-
`));
|
|
968
|
-
if (query.patternCount() !== ownerOfPattern.length) {
|
|
969
|
-
this.dbg(`Batch pattern count mismatch for ${languageId} (${query.patternCount()} vs ${ownerOfPattern.length}) — falling back`);
|
|
970
|
-
return null;
|
|
971
|
-
}
|
|
972
|
-
return { query, entries, ownerOfPattern };
|
|
973
|
-
} catch (err) {
|
|
974
|
-
if (this.reportWasmAbort(err))
|
|
975
|
-
return null;
|
|
976
|
-
this.dbg(`Batch compile failed for ${languageId}: ${err}`);
|
|
977
|
-
return null;
|
|
978
|
-
}
|
|
979
|
-
};
|
|
980
|
-
const batch = await build();
|
|
981
|
-
this.cacheQueryBatch(cacheKey, batch);
|
|
982
|
-
return batch;
|
|
983
|
-
}
|
|
984
|
-
patternToQuery(pattern, languageId) {
|
|
985
|
-
const loadedQuery = this.queryLoader.findMatchingQuery(pattern, languageId);
|
|
986
|
-
if (loadedQuery) {
|
|
987
|
-
this.dbg(`Using loaded query: ${loadedQuery.id}`);
|
|
988
|
-
return {
|
|
989
|
-
query: loadedQuery.query,
|
|
990
|
-
metavars: loadedQuery.metavars,
|
|
991
|
-
postFilter: loadedQuery.post_filter,
|
|
992
|
-
postFilterParams: loadedQuery.post_filter_params,
|
|
993
|
-
queryDef: loadedQuery
|
|
994
|
-
};
|
|
995
|
-
}
|
|
996
|
-
return this.getInlinePattern(pattern);
|
|
997
|
-
}
|
|
998
|
-
getInlinePattern(pattern) {
|
|
999
|
-
if (pattern.includes("async function") && pattern.includes("$NAME")) {
|
|
1000
|
-
return {
|
|
1001
|
-
query: `(function_declaration
|
|
1002
|
-
"async"
|
|
1003
|
-
name: (identifier) @NAME
|
|
1004
|
-
parameters: (formal_parameters) @PARAMS
|
|
1005
|
-
body: (statement_block) @BODY)`,
|
|
1006
|
-
metavars: ["NAME", "PARAMS", "BODY"]
|
|
1007
|
-
};
|
|
1008
|
-
}
|
|
1009
|
-
if (pattern.includes("console")) {
|
|
1010
|
-
return {
|
|
1011
|
-
query: `(call_expression
|
|
1012
|
-
function: (member_expression
|
|
1013
|
-
object: (identifier) @OBJ (#eq? @OBJ "console")
|
|
1014
|
-
property: (property_identifier) @METHOD)
|
|
1015
|
-
arguments: (arguments) @ARGS)`,
|
|
1016
|
-
metavars: ["OBJ", "METHOD", "ARGS"]
|
|
1017
|
-
};
|
|
1018
|
-
}
|
|
1019
|
-
if (pattern.includes("function $NAME") && pattern.includes("PARAMS")) {
|
|
1020
|
-
return {
|
|
1021
|
-
query: `(function_declaration
|
|
1022
|
-
name: (identifier) @NAME
|
|
1023
|
-
parameters: (formal_parameters) @PARAMS
|
|
1024
|
-
body: (statement_block) @BODY)`,
|
|
1025
|
-
metavars: ["NAME", "PARAMS", "BODY"],
|
|
1026
|
-
postFilter: "count_params",
|
|
1027
|
-
postFilterParams: { min_params: 6 }
|
|
1028
|
-
};
|
|
1029
|
-
}
|
|
1030
|
-
if (pattern.includes(".then") && pattern.includes(".catch")) {
|
|
1031
|
-
return {
|
|
1032
|
-
query: `(call_expression
|
|
1033
|
-
function: (member_expression
|
|
1034
|
-
object: (call_expression
|
|
1035
|
-
function: (member_expression
|
|
1036
|
-
object: (call_expression
|
|
1037
|
-
function: (member_expression
|
|
1038
|
-
property: (property_identifier) @M1)
|
|
1039
|
-
arguments: (arguments))
|
|
1040
|
-
property: (property_identifier) @M2)
|
|
1041
|
-
arguments: (arguments))
|
|
1042
|
-
property: (property_identifier) @M3)
|
|
1043
|
-
arguments: (arguments))
|
|
1044
|
-
(#match? @M1 "^(then|catch)$")
|
|
1045
|
-
(#match? @M2 "^(then|catch)$")
|
|
1046
|
-
(#match? @M3 "^(then|catch)$")`,
|
|
1047
|
-
metavars: ["M1", "M2", "M3"]
|
|
1048
|
-
};
|
|
1049
|
-
}
|
|
1050
|
-
const simpleMatch = pattern.match(/\$([A-Z_][A-Z0-9_]*)/);
|
|
1051
|
-
if (simpleMatch) {
|
|
1052
|
-
const name = simpleMatch[1];
|
|
1053
|
-
return {
|
|
1054
|
-
query: `(identifier) @${name}`,
|
|
1055
|
-
metavars: [name]
|
|
1056
|
-
};
|
|
1057
|
-
}
|
|
1058
|
-
return { query: "", metavars: [] };
|
|
1059
|
-
}
|
|
1060
|
-
getQueryCacheKey(pattern, languageId) {
|
|
1061
|
-
return `${languageId}:${pattern}`;
|
|
1062
|
-
}
|
|
1063
|
-
async compileQuery(pattern, languageId) {
|
|
1064
|
-
const cacheKey = this.getQueryCacheKey(pattern, languageId);
|
|
1065
|
-
if (this.queryCache.has(cacheKey)) {
|
|
1066
|
-
this.dbg(`Query cache hit: ${cacheKey}`);
|
|
1067
|
-
const cached = this.queryCache.get(cacheKey);
|
|
1068
|
-
this.queryCache.delete(cacheKey);
|
|
1069
|
-
this.queryCache.set(cacheKey, cached);
|
|
1070
|
-
return cached;
|
|
1071
|
-
}
|
|
1072
|
-
const language = await this.loadLanguage(languageId);
|
|
1073
|
-
if (!language) {
|
|
1074
|
-
this.dbg(`Could not load language ${languageId}`);
|
|
1075
|
-
return null;
|
|
1076
|
-
}
|
|
1077
|
-
const { query: queryStr, metavars, postFilter, postFilterParams } = this.patternToQuery(pattern, languageId);
|
|
1078
|
-
this.dbg(`Query string: ${queryStr.slice(0, 100)}...`);
|
|
1079
|
-
try {
|
|
1080
|
-
const Query = (await loadWebTreeSitter()).Query;
|
|
1081
|
-
const query = new Query(language, queryStr);
|
|
1082
|
-
this.dbg(`Query compiled with ${query.patternCount()} patterns`);
|
|
1083
|
-
const result = { query, metavars, postFilter, postFilterParams };
|
|
1084
|
-
this.cacheQuery(cacheKey, result);
|
|
1085
|
-
return result;
|
|
1086
|
-
} catch (err) {
|
|
1087
|
-
this.reportWasmAbort(err);
|
|
1088
|
-
this.dbg(`Query compilation failed: ${err}`);
|
|
1089
|
-
return null;
|
|
1090
|
-
}
|
|
1091
|
-
}
|
|
1092
|
-
async compileRawQuery(queryId, queryStr, metavars, languageId, postFilter, postFilterParams) {
|
|
1093
|
-
const cacheKey = this.getQueryCacheKey(`raw:${queryId}:${queryStr}`, languageId);
|
|
1094
|
-
if (this.queryCache.has(cacheKey)) {
|
|
1095
|
-
const cached = this.queryCache.get(cacheKey);
|
|
1096
|
-
this.queryCache.delete(cacheKey);
|
|
1097
|
-
this.queryCache.set(cacheKey, cached);
|
|
1098
|
-
return cached;
|
|
1099
|
-
}
|
|
1100
|
-
const language = await this.loadLanguage(languageId);
|
|
1101
|
-
if (!language)
|
|
1102
|
-
return null;
|
|
1103
|
-
try {
|
|
1104
|
-
const Query = (await loadWebTreeSitter()).Query;
|
|
1105
|
-
const query = new Query(language, queryStr);
|
|
1106
|
-
const result = { query, metavars, postFilter, postFilterParams };
|
|
1107
|
-
this.cacheQuery(cacheKey, result);
|
|
1108
|
-
return result;
|
|
1109
|
-
} catch (err) {
|
|
1110
|
-
this.reportWasmAbort(err);
|
|
1111
|
-
this.dbg(`Raw query compilation failed (${queryId}): ${err}`);
|
|
1112
|
-
this.reportQueryCompileFailure(queryId, languageId, err);
|
|
1113
|
-
return null;
|
|
1114
|
-
}
|
|
1115
|
-
}
|
|
1116
|
-
reportedCompileFailures = new Set;
|
|
1117
|
-
reportQueryCompileFailure(ruleId, languageId, err) {
|
|
1118
|
-
const key = `${ruleId}:${languageId}`;
|
|
1119
|
-
if (this.reportedCompileFailures.has(key))
|
|
1120
|
-
return;
|
|
1121
|
-
this.reportedCompileFailures.add(key);
|
|
1122
|
-
logTreeSitterDiagnostic({
|
|
1123
|
-
subsystem: "tree-sitter-client",
|
|
1124
|
-
languageId,
|
|
1125
|
-
message: `tree-sitter rule '${ruleId}' failed to compile against '${languageId}' — ` + `matches for this rule are silently dropped rather than reported. ` + `Fix the query in the rule definition to re-enable it. (${err})`,
|
|
1126
|
-
metadata: { ruleId }
|
|
1127
|
-
});
|
|
1128
|
-
}
|
|
1129
|
-
hasChildToken(node, token) {
|
|
1130
|
-
return node.children?.some((child) => child.type === token || child.text === token);
|
|
1131
|
-
}
|
|
1132
|
-
bindingNames(node) {
|
|
1133
|
-
const names = new Set;
|
|
1134
|
-
if (!node)
|
|
1135
|
-
return names;
|
|
1136
|
-
const stack = [node];
|
|
1137
|
-
while (stack.length > 0) {
|
|
1138
|
-
const current = stack.pop();
|
|
1139
|
-
if (current.type === "identifier")
|
|
1140
|
-
names.add(current.text);
|
|
1141
|
-
else if (current.type === "shorthand_property_identifier_pattern") {
|
|
1142
|
-
names.add(current.text);
|
|
1143
|
-
}
|
|
1144
|
-
if (current.type === "property_identifier" || current.type === "type_identifier" || current.type === "computed_property_name")
|
|
1145
|
-
continue;
|
|
1146
|
-
if (current.type === "assignment_pattern" || current.type === "object_assignment_pattern") {
|
|
1147
|
-
const left = current.childForFieldName?.("left");
|
|
1148
|
-
if (left)
|
|
1149
|
-
stack.push(left);
|
|
1150
|
-
continue;
|
|
1151
|
-
}
|
|
1152
|
-
stack.push(...current.children ?? []);
|
|
1153
|
-
}
|
|
1154
|
-
return names;
|
|
1155
|
-
}
|
|
1156
|
-
containsYieldInFunctionBody(node, root = node) {
|
|
1157
|
-
for (const child of node.children ?? []) {
|
|
1158
|
-
if (child.type === "yield")
|
|
1159
|
-
return true;
|
|
1160
|
-
if (child !== root && ["function_definition", "class_definition", "lambda"].includes(child.type)) {
|
|
1161
|
-
continue;
|
|
1162
|
-
}
|
|
1163
|
-
if (this.containsYieldInFunctionBody(child, root))
|
|
1164
|
-
return true;
|
|
1165
|
-
}
|
|
1166
|
-
return false;
|
|
1167
|
-
}
|
|
1168
|
-
isLikelySqlAlchemyReceiver(text) {
|
|
1169
|
-
const tail = text.split(".").pop() ?? text;
|
|
1170
|
-
return PYTHON_SQLALCHEMY_RECEIVER_NAMES.has(tail.toLowerCase());
|
|
1171
|
-
}
|
|
1172
|
-
switchCaseBodyStatements(caseNode) {
|
|
1173
|
-
const named = (caseNode.children ?? []).filter((c) => c.isNamed && !c.type.includes("comment"));
|
|
1174
|
-
return named.slice(1);
|
|
1175
|
-
}
|
|
1176
|
-
hasFallthroughMarker(caseNode) {
|
|
1177
|
-
const comments = [];
|
|
1178
|
-
const body = this.switchCaseBodyStatements(caseNode);
|
|
1179
|
-
const last = body[body.length - 1];
|
|
1180
|
-
const lastEnd = last?.endIndex ?? caseNode.startIndex;
|
|
1181
|
-
for (const c of caseNode.children ?? []) {
|
|
1182
|
-
if (c.type.includes("comment") && c.startIndex >= lastEnd) {
|
|
1183
|
-
comments.push(c);
|
|
1184
|
-
}
|
|
1185
|
-
}
|
|
1186
|
-
const siblings = (caseNode.parent?.children ?? []).filter((c) => c.isNamed);
|
|
1187
|
-
const index = siblings.findIndex((c) => c.startIndex === caseNode.startIndex);
|
|
1188
|
-
const next = index >= 0 ? siblings[index + 1] : undefined;
|
|
1189
|
-
if (next?.type.includes("comment"))
|
|
1190
|
-
comments.push(next);
|
|
1191
|
-
if (last) {
|
|
1192
|
-
const NESTED_FN = new Set([
|
|
1193
|
-
"function_declaration",
|
|
1194
|
-
"function_expression",
|
|
1195
|
-
"arrow_function",
|
|
1196
|
-
"method_definition",
|
|
1197
|
-
"generator_function",
|
|
1198
|
-
"generator_function_declaration",
|
|
1199
|
-
"class_declaration"
|
|
1200
|
-
]);
|
|
1201
|
-
const stack = [last];
|
|
1202
|
-
for (let visited = 0;stack.length > 0 && visited < 500; visited++) {
|
|
1203
|
-
const node = stack.pop();
|
|
1204
|
-
if (!node)
|
|
1205
|
-
break;
|
|
1206
|
-
if (node.type.includes("comment"))
|
|
1207
|
-
comments.push(node);
|
|
1208
|
-
if (!NESTED_FN.has(node.type)) {
|
|
1209
|
-
stack.push(...node.children ?? []);
|
|
1210
|
-
}
|
|
1211
|
-
}
|
|
1212
|
-
}
|
|
1213
|
-
return comments.some((c) => /falls?\s?through/i.test(c.text));
|
|
1214
|
-
}
|
|
1215
|
-
statementTerminates(node, depth = 0) {
|
|
1216
|
-
if (depth > 20)
|
|
1217
|
-
return false;
|
|
1218
|
-
const t = node.type;
|
|
1219
|
-
const TERMINATORS = new Set([
|
|
1220
|
-
"break_statement",
|
|
1221
|
-
"return_statement",
|
|
1222
|
-
"throw_statement",
|
|
1223
|
-
"continue_statement"
|
|
1224
|
-
]);
|
|
1225
|
-
if (TERMINATORS.has(t))
|
|
1226
|
-
return true;
|
|
1227
|
-
if (t === "statement_block" || t === "else_clause" || t === "catch_clause" || t === "finally_clause") {
|
|
1228
|
-
const inner = (node.children ?? []).filter((c) => c.isNamed && !c.type.includes("comment"));
|
|
1229
|
-
const last = inner[inner.length - 1];
|
|
1230
|
-
if (!last)
|
|
1231
|
-
return false;
|
|
1232
|
-
return this.statementTerminates(last, depth + 1);
|
|
1233
|
-
}
|
|
1234
|
-
if (t === "try_statement") {
|
|
1235
|
-
const tryBody = (node.children ?? []).find((c) => c.type === "statement_block");
|
|
1236
|
-
const tryTerminates = tryBody ? this.statementTerminates(tryBody, depth + 1) : false;
|
|
1237
|
-
const catches = (node.children ?? []).filter((c) => c.type === "catch_clause");
|
|
1238
|
-
const fin = (node.children ?? []).find((c) => c.type === "finally_clause");
|
|
1239
|
-
if (tryTerminates) {
|
|
1240
|
-
return catches.every((c) => this.statementTerminates(c, depth + 1));
|
|
1241
|
-
}
|
|
1242
|
-
return !!fin && this.statementTerminates(fin, depth + 1);
|
|
1243
|
-
}
|
|
1244
|
-
if (t === "if_statement") {
|
|
1245
|
-
const named = (node.children ?? []).filter((c) => c.isNamed && !c.type.includes("comment"));
|
|
1246
|
-
const consequence = named[1];
|
|
1247
|
-
const alternative = named[2];
|
|
1248
|
-
if (!consequence || !alternative)
|
|
1249
|
-
return false;
|
|
1250
|
-
return this.statementTerminates(consequence, depth + 1) && this.statementTerminates(alternative, depth + 1);
|
|
1251
|
-
}
|
|
1252
|
-
return false;
|
|
1253
|
-
}
|
|
1254
|
-
nextSiblingIsSwitchLabel(caseNode) {
|
|
1255
|
-
const siblings = (caseNode.parent?.children ?? []).filter((c) => c.isNamed && !c.type.includes("comment"));
|
|
1256
|
-
const index = siblings.findIndex((c) => c.startIndex === caseNode.startIndex);
|
|
1257
|
-
const next = index >= 0 ? siblings[index + 1] : undefined;
|
|
1258
|
-
return next?.type === "switch_case" || next?.type === "switch_default";
|
|
1259
|
-
}
|
|
1260
|
-
bodyHasLoopExit(node, insideNestedLoop) {
|
|
1261
|
-
const NESTS_BREAK = new Set([
|
|
1262
|
-
"for_statement",
|
|
1263
|
-
"for_in_statement",
|
|
1264
|
-
"while_statement",
|
|
1265
|
-
"do_statement",
|
|
1266
|
-
"switch_statement"
|
|
1267
|
-
]);
|
|
1268
|
-
const NESTED_FN = new Set([
|
|
1269
|
-
"function_declaration",
|
|
1270
|
-
"function_expression",
|
|
1271
|
-
"arrow_function",
|
|
1272
|
-
"method_definition",
|
|
1273
|
-
"generator_function",
|
|
1274
|
-
"generator_function_declaration",
|
|
1275
|
-
"class_declaration"
|
|
1276
|
-
]);
|
|
1277
|
-
for (const child of node.children ?? []) {
|
|
1278
|
-
const t = child.type;
|
|
1279
|
-
if (NESTED_FN.has(t))
|
|
1280
|
-
continue;
|
|
1281
|
-
if (t === "return_statement" || t === "throw_statement")
|
|
1282
|
-
return true;
|
|
1283
|
-
if (t === "break_statement") {
|
|
1284
|
-
const labeled = (child.children ?? []).some((c) => c.isNamed);
|
|
1285
|
-
if (!insideNestedLoop || labeled)
|
|
1286
|
-
return true;
|
|
1287
|
-
}
|
|
1288
|
-
if (this.bodyHasLoopExit(child, insideNestedLoop || NESTS_BREAK.has(t))) {
|
|
1289
|
-
return true;
|
|
1290
|
-
}
|
|
1291
|
-
}
|
|
1292
|
-
return false;
|
|
1293
|
-
}
|
|
1294
|
-
enclosingBindingName(node) {
|
|
1295
|
-
let cur = node?.parent;
|
|
1296
|
-
for (let depth = 0;cur && depth < 12; depth++) {
|
|
1297
|
-
const t = cur.type;
|
|
1298
|
-
if (t === "variable_declarator") {
|
|
1299
|
-
const name = cur.children?.find((c) => c.isNamed);
|
|
1300
|
-
return name?.text ?? "";
|
|
1301
|
-
}
|
|
1302
|
-
if (t === "assignment_expression") {
|
|
1303
|
-
return cur.children?.[0]?.text ?? "";
|
|
1304
|
-
}
|
|
1305
|
-
if (t === "statement_block" || t === "program" || t === "function_declaration" || t === "function_expression" || t === "arrow_function" || t === "method_definition") {
|
|
1306
|
-
return "";
|
|
1307
|
-
}
|
|
1308
|
-
cur = cur.parent;
|
|
1309
|
-
}
|
|
1310
|
-
return "";
|
|
1311
|
-
}
|
|
1312
|
-
resolvesToFileLiteralConst(name, rootNode) {
|
|
1313
|
-
const valueNode = this.resolveFileConstValueNode(name, rootNode);
|
|
1314
|
-
if (!valueNode)
|
|
1315
|
-
return false;
|
|
1316
|
-
return this.isFixedUrlLiteralExpr(valueNode);
|
|
1317
|
-
}
|
|
1318
|
-
resolveFileConstValueNode(name, rootNode) {
|
|
1319
|
-
const constDeclarators = [];
|
|
1320
|
-
let hasNonConstBinding = false;
|
|
1321
|
-
let hasReassignment = false;
|
|
1322
|
-
const stack = [rootNode];
|
|
1323
|
-
while (stack.length > 0) {
|
|
1324
|
-
const node = stack.pop();
|
|
1325
|
-
if (!node)
|
|
1326
|
-
continue;
|
|
1327
|
-
if (node.type === "variable_declarator") {
|
|
1328
|
-
const nameNode = node.childForFieldName?.("name");
|
|
1329
|
-
if (nameNode?.type === "identifier" && nameNode.text === name) {
|
|
1330
|
-
const decl = node.parent;
|
|
1331
|
-
const isConst = decl?.type === "lexical_declaration" && (decl.children ?? []).some((c) => c.type === "const");
|
|
1332
|
-
if (isConst) {
|
|
1333
|
-
constDeclarators.push(node);
|
|
1334
|
-
} else {
|
|
1335
|
-
hasNonConstBinding = true;
|
|
1336
|
-
}
|
|
1337
|
-
}
|
|
1338
|
-
} else if (node.type === "assignment_expression" || node.type === "augmented_assignment_expression") {
|
|
1339
|
-
const left = node.childForFieldName?.("left");
|
|
1340
|
-
if (left?.type === "identifier" && left.text === name) {
|
|
1341
|
-
hasReassignment = true;
|
|
1342
|
-
} else if ((left?.type === "member_expression" || left?.type === "subscript_expression") && left.childForFieldName?.("object")?.type === "identifier" && left.childForFieldName?.("object")?.text === name) {
|
|
1343
|
-
const searchOnly = left.type === "member_expression" && left.childForFieldName?.("property")?.text === "search";
|
|
1344
|
-
if (!searchOnly) {
|
|
1345
|
-
hasReassignment = true;
|
|
1346
|
-
}
|
|
1347
|
-
}
|
|
1348
|
-
}
|
|
1349
|
-
for (const child of node.children ?? [])
|
|
1350
|
-
stack.push(child);
|
|
1351
|
-
}
|
|
1352
|
-
if (hasNonConstBinding || hasReassignment || constDeclarators.length !== 1) {
|
|
1353
|
-
return null;
|
|
1354
|
-
}
|
|
1355
|
-
return constDeclarators[0].childForFieldName?.("value") ?? null;
|
|
1356
|
-
}
|
|
1357
|
-
isFixedUrlLiteralExpr(node) {
|
|
1358
|
-
if (node.type === "string")
|
|
1359
|
-
return true;
|
|
1360
|
-
if (node.type === "template_string") {
|
|
1361
|
-
return !(node.children ?? []).some((c) => c.type === "template_substitution");
|
|
1362
|
-
}
|
|
1363
|
-
return false;
|
|
1364
|
-
}
|
|
1365
|
-
isImportedBinding(name, rootNode) {
|
|
1366
|
-
return this.importedAs(name, rootNode) !== null;
|
|
1367
|
-
}
|
|
1368
|
-
importedAs(name, rootNode) {
|
|
1369
|
-
const stack = [rootNode];
|
|
1370
|
-
while (stack.length > 0) {
|
|
1371
|
-
const node = stack.pop();
|
|
1372
|
-
if (!node)
|
|
1373
|
-
continue;
|
|
1374
|
-
if (node.type === "import_specifier") {
|
|
1375
|
-
const nameNode = node.childForFieldName?.("name");
|
|
1376
|
-
const aliasNode = node.childForFieldName?.("alias");
|
|
1377
|
-
const local = (aliasNode ?? nameNode)?.text;
|
|
1378
|
-
if (local === name)
|
|
1379
|
-
return nameNode?.text ?? null;
|
|
1380
|
-
} else if (node.type === "namespace_import" || node.type === "import_clause") {
|
|
1381
|
-
for (const c of node.children ?? []) {
|
|
1382
|
-
if (c.type === "identifier" && c.text === name)
|
|
1383
|
-
return name;
|
|
1384
|
-
}
|
|
1385
|
-
}
|
|
1386
|
-
for (const child of node.children ?? [])
|
|
1387
|
-
stack.push(child);
|
|
1388
|
-
}
|
|
1389
|
-
return null;
|
|
1390
|
-
}
|
|
1391
|
-
isFixedUrlBaseExpr(base, rootNode) {
|
|
1392
|
-
if (base.type === "string")
|
|
1393
|
-
return true;
|
|
1394
|
-
if (base.type === "identifier") {
|
|
1395
|
-
return this.isFixedBaseIdentifier(base, rootNode);
|
|
1396
|
-
}
|
|
1397
|
-
if (base.type === "template_string") {
|
|
1398
|
-
for (const child of base.children ?? []) {
|
|
1399
|
-
if (child.type !== "template_substitution")
|
|
1400
|
-
continue;
|
|
1401
|
-
const inner = (child.children ?? []).find((c) => c.isNamed);
|
|
1402
|
-
if (!inner || inner.type !== "identifier" || !this.isFixedBaseIdentifier(inner, rootNode)) {
|
|
1403
|
-
return false;
|
|
1404
|
-
}
|
|
1405
|
-
}
|
|
1406
|
-
return true;
|
|
1407
|
-
}
|
|
1408
|
-
return false;
|
|
1409
|
-
}
|
|
1410
|
-
isFixedBaseIdentifier(ident, rootNode) {
|
|
1411
|
-
const name = ident.text;
|
|
1412
|
-
if (!this.isShadowedByEnclosingParam(ident, name) && this.resolvesToFileLiteralConst(name, rootNode)) {
|
|
1413
|
-
return true;
|
|
1414
|
-
}
|
|
1415
|
-
return this.isImportedBinding(name, rootNode);
|
|
1416
|
-
}
|
|
1417
|
-
isShadowedByEnclosingParam(node, name) {
|
|
1418
|
-
const FUNCTION_TYPES = new Set([
|
|
1419
|
-
"function_declaration",
|
|
1420
|
-
"function_expression",
|
|
1421
|
-
"generator_function",
|
|
1422
|
-
"generator_function_declaration",
|
|
1423
|
-
"arrow_function",
|
|
1424
|
-
"method_definition"
|
|
1425
|
-
]);
|
|
1426
|
-
let current = node.parent;
|
|
1427
|
-
while (current) {
|
|
1428
|
-
if (FUNCTION_TYPES.has(current.type)) {
|
|
1429
|
-
const bare = current.childForFieldName?.("parameter");
|
|
1430
|
-
if (bare?.type === "identifier" && bare.text === name)
|
|
1431
|
-
return true;
|
|
1432
|
-
const params = current.childForFieldName?.("parameters");
|
|
1433
|
-
if (params && this.paramsBindName(params, name))
|
|
1434
|
-
return true;
|
|
1435
|
-
}
|
|
1436
|
-
current = current.parent;
|
|
1437
|
-
}
|
|
1438
|
-
return false;
|
|
1439
|
-
}
|
|
1440
|
-
paramsBindName(params, name) {
|
|
1441
|
-
for (const param of params.children ?? []) {
|
|
1442
|
-
if (!param.isNamed)
|
|
1443
|
-
continue;
|
|
1444
|
-
const pattern = param.type === "required_parameter" || param.type === "optional_parameter" ? param.childForFieldName?.("pattern") : param;
|
|
1445
|
-
if (pattern && this.patternBindsName(pattern, name))
|
|
1446
|
-
return true;
|
|
1447
|
-
}
|
|
1448
|
-
return false;
|
|
1449
|
-
}
|
|
1450
|
-
patternBindsName(pattern, name) {
|
|
1451
|
-
if (pattern.type === "identifier")
|
|
1452
|
-
return pattern.text === name;
|
|
1453
|
-
const stack = [pattern];
|
|
1454
|
-
while (stack.length > 0) {
|
|
1455
|
-
const n = stack.pop();
|
|
1456
|
-
if (!n)
|
|
1457
|
-
continue;
|
|
1458
|
-
if ((n.type === "identifier" || n.type === "shorthand_property_identifier_pattern") && n.text === name) {
|
|
1459
|
-
return true;
|
|
1460
|
-
}
|
|
1461
|
-
if (n.type === "assignment_pattern") {
|
|
1462
|
-
const left = n.childForFieldName?.("left") ?? n.children?.[0];
|
|
1463
|
-
if (left)
|
|
1464
|
-
stack.push(left);
|
|
1465
|
-
continue;
|
|
1466
|
-
}
|
|
1467
|
-
for (const c of n.children ?? [])
|
|
1468
|
-
stack.push(c);
|
|
1469
|
-
}
|
|
1470
|
-
return false;
|
|
1471
|
-
}
|
|
1472
|
-
resolvesToFixedNewUrlConst(name, rootNode) {
|
|
1473
|
-
const value = this.resolveFileConstValueNode(name, rootNode);
|
|
1474
|
-
if (!value || value.type !== "new_expression")
|
|
1475
|
-
return false;
|
|
1476
|
-
const ctor = value.childForFieldName?.("constructor");
|
|
1477
|
-
const ctorName = ctor?.text ?? "";
|
|
1478
|
-
const isUrlCtor = ctorName === "URL" || ctor?.type === "identifier" && this.importedAs(ctorName, rootNode) === "URL";
|
|
1479
|
-
if (!isUrlCtor)
|
|
1480
|
-
return false;
|
|
1481
|
-
const args = (value.childForFieldName?.("arguments")?.children ?? []).filter((c) => c.isNamed && c.type !== "comment");
|
|
1482
|
-
if (args.length === 0)
|
|
1483
|
-
return false;
|
|
1484
|
-
if (!this.isFixedUrlLiteralExpr(args[0]))
|
|
1485
|
-
return false;
|
|
1486
|
-
if (args.length === 1)
|
|
1487
|
-
return true;
|
|
1488
|
-
return this.isFixedUrlBaseExpr(args[1], rootNode);
|
|
1489
|
-
}
|
|
1490
|
-
newUrlBaseVarName(urlNode) {
|
|
1491
|
-
let member;
|
|
1492
|
-
if (urlNode.type === "call_expression") {
|
|
1493
|
-
member = urlNode.childForFieldName?.("function");
|
|
1494
|
-
} else if (urlNode.type === "member_expression") {
|
|
1495
|
-
member = urlNode;
|
|
1496
|
-
}
|
|
1497
|
-
if (!member || member.type !== "member_expression")
|
|
1498
|
-
return null;
|
|
1499
|
-
const prop = member.childForFieldName?.("property")?.text;
|
|
1500
|
-
if (prop !== "toString" && prop !== "href")
|
|
1501
|
-
return null;
|
|
1502
|
-
const object = member.childForFieldName?.("object");
|
|
1503
|
-
if (object?.type !== "identifier")
|
|
1504
|
-
return null;
|
|
1505
|
-
return object.text;
|
|
1506
|
-
}
|
|
1507
|
-
isSafeSqlAlchemyExpressionCall(node) {
|
|
1508
|
-
if (node.type !== "call")
|
|
1509
|
-
return false;
|
|
1510
|
-
const callee = node.children?.[0]?.text ?? "";
|
|
1511
|
-
const expression = node.text;
|
|
1512
|
-
for (const name of PYTHON_SQLALCHEMY_STATEMENT_BUILDERS) {
|
|
1513
|
-
if (callee === name || expression.startsWith(`${name}(`))
|
|
1514
|
-
return true;
|
|
1515
|
-
}
|
|
1516
|
-
return false;
|
|
1517
|
-
}
|
|
1518
|
-
applyPostFilter(postFilter, postFilterParams, captures, rootNode) {
|
|
1519
|
-
function extractSlots(classNode) {
|
|
1520
|
-
const classText = classNode.text ?? "";
|
|
1521
|
-
if (!classText.includes("__slots__"))
|
|
1522
|
-
return null;
|
|
1523
|
-
const body = classNode.children?.find((c) => c.type === "block");
|
|
1524
|
-
if (!body)
|
|
1525
|
-
return null;
|
|
1526
|
-
const slots = [];
|
|
1527
|
-
for (const stmt of body.children ?? []) {
|
|
1528
|
-
if (stmt.type !== "expression_statement")
|
|
1529
|
-
continue;
|
|
1530
|
-
const assignment = stmt.children?.find((c) => c.type === "assignment");
|
|
1531
|
-
if (!assignment)
|
|
1532
|
-
continue;
|
|
1533
|
-
const lhsText = (assignment.children?.[0]?.text ?? "").trim();
|
|
1534
|
-
if (lhsText !== "__slots__")
|
|
1535
|
-
continue;
|
|
1536
|
-
const rhs = assignment.children?.[2];
|
|
1537
|
-
if (!rhs)
|
|
1538
|
-
continue;
|
|
1539
|
-
if (rhs.type === "string") {
|
|
1540
|
-
const s = (rhs.text ?? "").replace(/^["']|["']$/g, "");
|
|
1541
|
-
if (s)
|
|
1542
|
-
slots.push(s);
|
|
1543
|
-
} else if (rhs.type === "tuple" || rhs.type === "list") {
|
|
1544
|
-
for (const el of rhs.children ?? []) {
|
|
1545
|
-
if (!el.isNamed)
|
|
1546
|
-
continue;
|
|
1547
|
-
if (el.type === "string") {
|
|
1548
|
-
slots.push((el.text ?? "").replace(/^["']|["']$/g, ""));
|
|
1549
|
-
}
|
|
1550
|
-
}
|
|
1551
|
-
}
|
|
1552
|
-
break;
|
|
1553
|
-
}
|
|
1554
|
-
return slots;
|
|
1555
|
-
}
|
|
1556
|
-
switch (postFilter) {
|
|
1557
|
-
case "no_nested_anchor_chain": {
|
|
1558
|
-
try {
|
|
1559
|
-
const outer = captures.OUTER_ELEMENT;
|
|
1560
|
-
if (!outer) {
|
|
1561
|
-
this.reportPostFilterFailure(postFilter, "missing OUTER_ELEMENT capture");
|
|
1562
|
-
return true;
|
|
1563
|
-
}
|
|
1564
|
-
const isAnchorElement = (node) => {
|
|
1565
|
-
if (node.type !== "jsx_element")
|
|
1566
|
-
return false;
|
|
1567
|
-
const opening = node.childForFieldName?.("open_tag") ?? node.children?.find((child) => child.type === "jsx_opening_element");
|
|
1568
|
-
const name = opening?.childForFieldName?.("name") ?? opening?.children?.find((child) => child.type === "identifier");
|
|
1569
|
-
return name?.text === "a";
|
|
1570
|
-
};
|
|
1571
|
-
let visited = 0;
|
|
1572
|
-
let ancestor = outer.parent;
|
|
1573
|
-
while (ancestor) {
|
|
1574
|
-
if (++visited > NO_NESTED_ANCHOR_VISIT_CAP) {
|
|
1575
|
-
this.reportPostFilterFailure(postFilter, `ancestor walk exceeded ${NO_NESTED_ANCHOR_VISIT_CAP} nodes`);
|
|
1576
|
-
return true;
|
|
1577
|
-
}
|
|
1578
|
-
if (isAnchorElement(ancestor))
|
|
1579
|
-
return false;
|
|
1580
|
-
ancestor = ancestor.parent;
|
|
1581
|
-
}
|
|
1582
|
-
const pending = [...outer.children ?? []];
|
|
1583
|
-
while (pending.length > 0) {
|
|
1584
|
-
const node = pending.pop();
|
|
1585
|
-
if (!node)
|
|
1586
|
-
continue;
|
|
1587
|
-
if (++visited > NO_NESTED_ANCHOR_VISIT_CAP) {
|
|
1588
|
-
this.reportPostFilterFailure(postFilter, `descendant walk exceeded ${NO_NESTED_ANCHOR_VISIT_CAP} nodes`);
|
|
1589
|
-
return true;
|
|
1590
|
-
}
|
|
1591
|
-
if (isAnchorElement(node))
|
|
1592
|
-
return true;
|
|
1593
|
-
pending.push(...node.children ?? []);
|
|
1594
|
-
}
|
|
1595
|
-
return false;
|
|
1596
|
-
} catch (error) {
|
|
1597
|
-
const reason = error instanceof Error ? error.message : String(error);
|
|
1598
|
-
this.reportPostFilterFailure(postFilter, `tree walk failed${reason ? `: ${reason}` : ""}`);
|
|
1599
|
-
return true;
|
|
1600
|
-
}
|
|
1601
|
-
}
|
|
1602
|
-
case "differs_only_by_case": {
|
|
1603
|
-
try {
|
|
1604
|
-
const field = captures.FIELD?.text ?? "";
|
|
1605
|
-
const method = captures.METHOD?.text ?? "";
|
|
1606
|
-
return field !== method && field.toLocaleLowerCase() === method.toLocaleLowerCase();
|
|
1607
|
-
} catch {
|
|
1608
|
-
return true;
|
|
1609
|
-
}
|
|
1610
|
-
}
|
|
1611
|
-
case "in_default_package": {
|
|
1612
|
-
try {
|
|
1613
|
-
if (!rootNode)
|
|
1614
|
-
return true;
|
|
1615
|
-
return !(rootNode.children ?? []).some((node) => node.type === "package_declaration");
|
|
1616
|
-
} catch {
|
|
1617
|
-
return true;
|
|
1618
|
-
}
|
|
1619
|
-
}
|
|
1620
|
-
case "missing_mimetype_and_download_name": {
|
|
1621
|
-
try {
|
|
1622
|
-
const firstArg = captures.FIRST_ARG;
|
|
1623
|
-
if (!firstArg)
|
|
1624
|
-
return true;
|
|
1625
|
-
if (firstArg.type === "string")
|
|
1626
|
-
return false;
|
|
1627
|
-
const args = firstArg.parent;
|
|
1628
|
-
if (!args)
|
|
1629
|
-
return true;
|
|
1630
|
-
return !(args.children ?? []).some((node) => {
|
|
1631
|
-
if (node.type !== "keyword_argument")
|
|
1632
|
-
return false;
|
|
1633
|
-
const name = node.childForFieldName?.("name")?.text;
|
|
1634
|
-
return name === "mimetype" || name === "download_name";
|
|
1635
|
-
});
|
|
1636
|
-
} catch {
|
|
1637
|
-
return true;
|
|
1638
|
-
}
|
|
1639
|
-
}
|
|
1640
|
-
case "missing_super_call": {
|
|
1641
|
-
try {
|
|
1642
|
-
const body = captures.BODY;
|
|
1643
|
-
const method = captures.METHOD?.text ?? "";
|
|
1644
|
-
if (!body || !method)
|
|
1645
|
-
return true;
|
|
1646
|
-
const stack = [body];
|
|
1647
|
-
for (let visited = 0;stack.length > 0 && visited < 1e4; visited++) {
|
|
1648
|
-
const node = stack.pop();
|
|
1649
|
-
if (!node)
|
|
1650
|
-
break;
|
|
1651
|
-
if (node.type === "method_invocation" && node.childForFieldName?.("object")?.text === "super" && node.childForFieldName?.("name")?.text === method) {
|
|
1652
|
-
return false;
|
|
1653
|
-
}
|
|
1654
|
-
stack.push(...node.children ?? []);
|
|
1655
|
-
}
|
|
1656
|
-
return true;
|
|
1657
|
-
} catch {
|
|
1658
|
-
return true;
|
|
1659
|
-
}
|
|
1660
|
-
}
|
|
1661
|
-
case "no_assertion_call": {
|
|
1662
|
-
try {
|
|
1663
|
-
const body = captures.BODY;
|
|
1664
|
-
if (!body)
|
|
1665
|
-
return true;
|
|
1666
|
-
const assertionNames = /^(?:assert\w*|fail|verify|expect|check|assume\w*)$/i;
|
|
1667
|
-
const stack = [body];
|
|
1668
|
-
for (let visited = 0;stack.length > 0 && visited < 1e4; visited++) {
|
|
1669
|
-
const node = stack.pop();
|
|
1670
|
-
if (!node)
|
|
1671
|
-
break;
|
|
1672
|
-
if (node.type === "method_invocation") {
|
|
1673
|
-
const name = node.childForFieldName?.("name")?.text ?? "";
|
|
1674
|
-
if (assertionNames.test(name))
|
|
1675
|
-
return false;
|
|
1676
|
-
}
|
|
1677
|
-
stack.push(...node.children ?? []);
|
|
1678
|
-
}
|
|
1679
|
-
return true;
|
|
1680
|
-
} catch {
|
|
1681
|
-
return true;
|
|
1682
|
-
}
|
|
1683
|
-
}
|
|
1684
|
-
case "not_closed_or_try_with_resources": {
|
|
1685
|
-
try {
|
|
1686
|
-
const declaration = captures.DECL;
|
|
1687
|
-
const resource = captures.RESOURCE?.text ?? "";
|
|
1688
|
-
if (!declaration || !resource)
|
|
1689
|
-
return true;
|
|
1690
|
-
const scope = this.navigator.findParent(declaration, [
|
|
1691
|
-
"method_declaration",
|
|
1692
|
-
"constructor_declaration",
|
|
1693
|
-
"block"
|
|
1694
|
-
]) ?? rootNode;
|
|
1695
|
-
if (!scope)
|
|
1696
|
-
return true;
|
|
1697
|
-
const resourceWord = new RegExp(`\\b${escapeRegExp(resource)}\\b`);
|
|
1698
|
-
const stack = [scope];
|
|
1699
|
-
for (let visited = 0;stack.length > 0 && visited < 1e4; visited++) {
|
|
1700
|
-
const node = stack.pop();
|
|
1701
|
-
if (!node)
|
|
1702
|
-
break;
|
|
1703
|
-
if (node.type === "method_invocation" && node.childForFieldName?.("object")?.text === resource && node.childForFieldName?.("name")?.text === "close") {
|
|
1704
|
-
return false;
|
|
1705
|
-
}
|
|
1706
|
-
if (node.type === "resource" && resourceWord.test(node.text ?? "")) {
|
|
1707
|
-
return false;
|
|
1708
|
-
}
|
|
1709
|
-
stack.push(...node.children ?? []);
|
|
1710
|
-
}
|
|
1711
|
-
return true;
|
|
1712
|
-
} catch {
|
|
1713
|
-
return true;
|
|
1714
|
-
}
|
|
1715
|
-
}
|
|
1716
|
-
case "same_method_no_base_case": {
|
|
1717
|
-
try {
|
|
1718
|
-
const method = captures.NAME?.text ?? "";
|
|
1719
|
-
if (!method || captures.RECURSE?.text !== method)
|
|
1720
|
-
return false;
|
|
1721
|
-
const call = captures.CALL;
|
|
1722
|
-
const declaration = call ? this.navigator.findParent(call, ["method_declaration"]) : undefined;
|
|
1723
|
-
if (!declaration)
|
|
1724
|
-
return true;
|
|
1725
|
-
const stack = [declaration];
|
|
1726
|
-
for (let visited = 0;stack.length > 0 && visited < 1e4; visited++) {
|
|
1727
|
-
const node = stack.pop();
|
|
1728
|
-
if (!node)
|
|
1729
|
-
break;
|
|
1730
|
-
if (node.type === "if_statement" || node.type === "switch_expression" || node.type === "switch_statement" || node.type === "while_statement" || node.type === "do_statement" || node.type === "for_statement" || node.type === "enhanced_for_statement" || node.type === "ternary_expression") {
|
|
1731
|
-
return false;
|
|
1732
|
-
}
|
|
1733
|
-
stack.push(...node.children ?? []);
|
|
1734
|
-
}
|
|
1735
|
-
return stack.length > 0 ? false : true;
|
|
1736
|
-
} catch {
|
|
1737
|
-
return true;
|
|
1738
|
-
}
|
|
1739
|
-
}
|
|
1740
|
-
case "memset_for_sensitive_data": {
|
|
1741
|
-
try {
|
|
1742
|
-
const destination = captures.DEST?.text ?? "";
|
|
1743
|
-
const value = captures.VALUE?.text?.trim() ?? "";
|
|
1744
|
-
if (!/^(?:0|0x0+|NULL|nullptr)$/.test(value))
|
|
1745
|
-
return false;
|
|
1746
|
-
return /(?:pass(?:word)?|passwd|secret|token|api_?key|private_?key|credential|auth|pin)/i.test(destination);
|
|
1747
|
-
} catch {
|
|
1748
|
-
return true;
|
|
1749
|
-
}
|
|
1750
|
-
}
|
|
1751
|
-
case "unsafe_regex_dynamic_identifier": {
|
|
1752
|
-
try {
|
|
1753
|
-
const interpolationNode = captures.INTERPOLATION;
|
|
1754
|
-
const interpolation = interpolationNode?.text ?? "";
|
|
1755
|
-
const identifier = interpolation.match(/^\$\{\s*([A-Za-z_$][\w$]*)\s*\}$/)?.[1];
|
|
1756
|
-
if (!identifier || !rootNode || !interpolationNode)
|
|
1757
|
-
return true;
|
|
1758
|
-
const useRow = interpolationNode.startPosition.row;
|
|
1759
|
-
const hasEscapeSignal = (text) => /\b(?:escape|Escape)\w*\s*\(/.test(text) || /\.\s*replace\s*\(\s*\//.test(text);
|
|
1760
|
-
let lastWriteRow = -1;
|
|
1761
|
-
let lastWriteSafe = false;
|
|
1762
|
-
const consider = (row, valueText) => {
|
|
1763
|
-
if (row >= useRow || row < lastWriteRow)
|
|
1764
|
-
return;
|
|
1765
|
-
lastWriteRow = row;
|
|
1766
|
-
lastWriteSafe = valueText !== undefined && hasEscapeSignal(valueText);
|
|
1767
|
-
};
|
|
1768
|
-
const stack = [rootNode];
|
|
1769
|
-
while (stack.length > 0) {
|
|
1770
|
-
const node = stack.pop();
|
|
1771
|
-
if (!node)
|
|
1772
|
-
continue;
|
|
1773
|
-
if (node.type === "variable_declarator") {
|
|
1774
|
-
const name = node.childForFieldName?.("name");
|
|
1775
|
-
if (name?.text === identifier) {
|
|
1776
|
-
const initializer = node.childForFieldName?.("value");
|
|
1777
|
-
consider(node.startPosition.row, initializer?.text);
|
|
1778
|
-
}
|
|
1779
|
-
} else if (node.type === "assignment_expression") {
|
|
1780
|
-
const left = node.childForFieldName?.("left");
|
|
1781
|
-
if (left?.type === "identifier" && left.text === identifier) {
|
|
1782
|
-
const right = node.childForFieldName?.("right");
|
|
1783
|
-
consider(node.startPosition.row, right?.text);
|
|
1784
|
-
}
|
|
1785
|
-
}
|
|
1786
|
-
stack.push(...node.children);
|
|
1787
|
-
}
|
|
1788
|
-
return lastWriteRow >= 0 && lastWriteSafe ? false : true;
|
|
1789
|
-
} catch {
|
|
1790
|
-
return true;
|
|
1791
|
-
}
|
|
1792
|
-
}
|
|
1793
|
-
case "is_generator_with_valued_return": {
|
|
1794
|
-
const returnNode = captures.RETURN;
|
|
1795
|
-
const functionNode = captures.FUNCTION ?? (returnNode ? this.navigator.findParent(returnNode, ["function_definition"]) : undefined);
|
|
1796
|
-
if (!functionNode)
|
|
1797
|
-
return false;
|
|
1798
|
-
if (this.hasChildToken(functionNode, "async"))
|
|
1799
|
-
return false;
|
|
1800
|
-
return this.containsYieldInFunctionBody(functionNode);
|
|
1801
|
-
}
|
|
1802
|
-
case "count_params": {
|
|
1803
|
-
const paramsNode = captures.PARAMS;
|
|
1804
|
-
if (!paramsNode)
|
|
1805
|
-
return true;
|
|
1806
|
-
const paramCount = paramsNode.children.filter((c) => {
|
|
1807
|
-
if (c.type !== "required_parameter")
|
|
1808
|
-
return false;
|
|
1809
|
-
if (c.text.includes("="))
|
|
1810
|
-
return false;
|
|
1811
|
-
if (c.children?.some((ch) => ch.text === "?"))
|
|
1812
|
-
return false;
|
|
1813
|
-
return true;
|
|
1814
|
-
}).length;
|
|
1815
|
-
return paramCount >= (postFilterParams?.min_params ?? 6);
|
|
1816
|
-
}
|
|
1817
|
-
case "empty_body": {
|
|
1818
|
-
const bodyNode = captures.BODY;
|
|
1819
|
-
if (!bodyNode)
|
|
1820
|
-
return true;
|
|
1821
|
-
const meaningful = bodyNode.children.filter((c) => c.isNamed && c.type !== "comment" && c.type !== "line_comment" && c.type !== "block_comment");
|
|
1822
|
-
return meaningful.length === 0;
|
|
1823
|
-
}
|
|
1824
|
-
case "is_empty_block": {
|
|
1825
|
-
const caseNode = captures.CASE;
|
|
1826
|
-
if (!caseNode)
|
|
1827
|
-
return false;
|
|
1828
|
-
if (this.switchCaseBodyStatements(caseNode).length > 0)
|
|
1829
|
-
return false;
|
|
1830
|
-
return !this.nextSiblingIsSwitchLabel(caseNode);
|
|
1831
|
-
}
|
|
1832
|
-
case "no_break_or_return_in_body": {
|
|
1833
|
-
const bodyNode = captures.BODY;
|
|
1834
|
-
if (!bodyNode)
|
|
1835
|
-
return false;
|
|
1836
|
-
return !this.bodyHasLoopExit(bodyNode, false);
|
|
1837
|
-
}
|
|
1838
|
-
case "same_param_name": {
|
|
1839
|
-
const first = this.bindingNames(captures.PARAM1);
|
|
1840
|
-
const second = this.bindingNames(captures.NAME);
|
|
1841
|
-
return [...first].some((name) => second.has(name));
|
|
1842
|
-
}
|
|
1843
|
-
case "no_terminating_statement": {
|
|
1844
|
-
const caseNode = captures.CASE;
|
|
1845
|
-
if (!caseNode)
|
|
1846
|
-
return false;
|
|
1847
|
-
if (this.hasFallthroughMarker(caseNode))
|
|
1848
|
-
return false;
|
|
1849
|
-
const body = this.switchCaseBodyStatements(caseNode);
|
|
1850
|
-
if (body.length === 0)
|
|
1851
|
-
return false;
|
|
1852
|
-
return !this.statementTerminates(body[body.length - 1]);
|
|
1853
|
-
}
|
|
1854
|
-
case "no_break_or_return": {
|
|
1855
|
-
const bodyNode = captures.BODY;
|
|
1856
|
-
if (!bodyNode)
|
|
1857
|
-
return false;
|
|
1858
|
-
return !this.bodyHasLoopExit(bodyNode, false);
|
|
1859
|
-
}
|
|
1860
|
-
case "is_double_checked_locking": {
|
|
1861
|
-
const outer = captures.FIELD?.text;
|
|
1862
|
-
const inner = captures.FIELD2?.text;
|
|
1863
|
-
return !!outer && outer === inner;
|
|
1864
|
-
}
|
|
1865
|
-
case "shadows_parent_field": {
|
|
1866
|
-
const parentName = captures.PARENT?.text;
|
|
1867
|
-
const fieldName = captures.NAME?.text;
|
|
1868
|
-
if (!parentName || !fieldName)
|
|
1869
|
-
return false;
|
|
1870
|
-
let root = captures.NAME;
|
|
1871
|
-
while (root.parent)
|
|
1872
|
-
root = root.parent;
|
|
1873
|
-
const stack = [root];
|
|
1874
|
-
while (stack.length) {
|
|
1875
|
-
const node = stack.pop();
|
|
1876
|
-
if (node.type === "class_declaration" && node.childForFieldName?.("name")?.text === parentName) {
|
|
1877
|
-
const body = node.childForFieldName?.("body");
|
|
1878
|
-
for (const member of body?.children ?? []) {
|
|
1879
|
-
if (member.type !== "field_declaration")
|
|
1880
|
-
continue;
|
|
1881
|
-
for (const declarator of member.children ?? []) {
|
|
1882
|
-
if (declarator.type === "variable_declarator" && declarator.childForFieldName?.("name")?.text === fieldName) {
|
|
1883
|
-
return true;
|
|
1884
|
-
}
|
|
1885
|
-
}
|
|
1886
|
-
}
|
|
1887
|
-
}
|
|
1888
|
-
for (const child of node.children ?? [])
|
|
1889
|
-
stack.push(child);
|
|
1890
|
-
}
|
|
1891
|
-
return false;
|
|
1892
|
-
}
|
|
1893
|
-
case "missing_break_between_cases": {
|
|
1894
|
-
const label = captures.LABEL;
|
|
1895
|
-
const group = label?.parent;
|
|
1896
|
-
if (!group)
|
|
1897
|
-
return false;
|
|
1898
|
-
const TERMINATORS = new Set([
|
|
1899
|
-
"break_statement",
|
|
1900
|
-
"return_statement",
|
|
1901
|
-
"throw_statement",
|
|
1902
|
-
"continue_statement",
|
|
1903
|
-
"yield_statement"
|
|
1904
|
-
]);
|
|
1905
|
-
for (const child of group.children ?? []) {
|
|
1906
|
-
if (TERMINATORS.has(child.type))
|
|
1907
|
-
return false;
|
|
1908
|
-
}
|
|
1909
|
-
return true;
|
|
1910
|
-
}
|
|
1911
|
-
case "scoped_lock_empty_args": {
|
|
1912
|
-
const decl = captures.DECL;
|
|
1913
|
-
if (!decl)
|
|
1914
|
-
return false;
|
|
1915
|
-
for (const sibling of decl.parent?.children ?? []) {
|
|
1916
|
-
if (sibling.type === "argument_list")
|
|
1917
|
-
return false;
|
|
1918
|
-
}
|
|
1919
|
-
return true;
|
|
1920
|
-
}
|
|
1921
|
-
case "calc_missing_spaces": {
|
|
1922
|
-
const text = captures.EXPR?.text ?? "";
|
|
1923
|
-
return /[\w%)][+-][\w.(]/.test(text);
|
|
1924
|
-
}
|
|
1925
|
-
case "bare_except_only": {
|
|
1926
|
-
const clauseNode = captures.CLAUSE;
|
|
1927
|
-
if (!clauseNode)
|
|
1928
|
-
return true;
|
|
1929
|
-
const hasExceptionSpec = clauseNode.children.some((c) => {
|
|
1930
|
-
if (!c.isNamed)
|
|
1931
|
-
return false;
|
|
1932
|
-
return c.type !== "block";
|
|
1933
|
-
});
|
|
1934
|
-
return !hasExceptionSpec;
|
|
1935
|
-
}
|
|
1936
|
-
case "eq_mod_fn": {
|
|
1937
|
-
const mod = captures.MOD?.text ?? "";
|
|
1938
|
-
const fn = captures.FN?.text ?? "";
|
|
1939
|
-
return mod === "threading" && fn === "Thread";
|
|
1940
|
-
}
|
|
1941
|
-
case "regex_first_arg_identifier": {
|
|
1942
|
-
const mod = captures.MOD?.text ?? "";
|
|
1943
|
-
if (mod !== "re")
|
|
1944
|
-
return false;
|
|
1945
|
-
const func = captures.FUNC?.text ?? "";
|
|
1946
|
-
if (!/^(compile|match|search|fullmatch|findall|finditer|sub|subn|split)$/.test(func)) {
|
|
1947
|
-
return false;
|
|
1948
|
-
}
|
|
1949
|
-
const argsNode = captures.ARGS;
|
|
1950
|
-
if (!argsNode)
|
|
1951
|
-
return false;
|
|
1952
|
-
const firstNamed = (argsNode.children ?? []).find((c) => c.isNamed);
|
|
1953
|
-
if (!firstNamed)
|
|
1954
|
-
return false;
|
|
1955
|
-
return firstNamed.type === "identifier";
|
|
1956
|
-
}
|
|
1957
|
-
case "open_mode_invalid": {
|
|
1958
|
-
const modeNode = captures.MODE;
|
|
1959
|
-
if (!modeNode)
|
|
1960
|
-
return false;
|
|
1961
|
-
const text = modeNode.text ?? "";
|
|
1962
|
-
const stripped = text.replace(/^["']|["']$/g, "");
|
|
1963
|
-
if (stripped.length === 0)
|
|
1964
|
-
return false;
|
|
1965
|
-
if (stripped.length === 1)
|
|
1966
|
-
return false;
|
|
1967
|
-
if (!/^[rwxabt+]+$/.test(stripped))
|
|
1968
|
-
return true;
|
|
1969
|
-
const validShape = /^[rwax][bt]?\+?$/;
|
|
1970
|
-
if (!validShape.test(stripped))
|
|
1971
|
-
return true;
|
|
1972
|
-
return false;
|
|
1973
|
-
}
|
|
1974
|
-
case "status_204_with_value_return": {
|
|
1975
|
-
const funcNode = captures.FUNC;
|
|
1976
|
-
const valNode = captures.VAL;
|
|
1977
|
-
if (!funcNode || !valNode)
|
|
1978
|
-
return false;
|
|
1979
|
-
if (Number(valNode.text ?? 0) !== 204)
|
|
1980
|
-
return false;
|
|
1981
|
-
const queue = [funcNode];
|
|
1982
|
-
while (queue.length > 0) {
|
|
1983
|
-
const node = queue.shift();
|
|
1984
|
-
if (node.type === "return_statement") {
|
|
1985
|
-
const hasValue = node.children.some((c) => c.isNamed && c.type !== "comment");
|
|
1986
|
-
if (hasValue)
|
|
1987
|
-
return true;
|
|
1988
|
-
}
|
|
1989
|
-
if (node.children)
|
|
1990
|
-
queue.push(...node.children);
|
|
1991
|
-
}
|
|
1992
|
-
return false;
|
|
1993
|
-
}
|
|
1994
|
-
case "has_mixed_async": {
|
|
1995
|
-
const bodyNode = captures.BODY;
|
|
1996
|
-
if (!bodyNode)
|
|
1997
|
-
return true;
|
|
1998
|
-
const bodyText = bodyNode.text;
|
|
1999
|
-
return bodyText.includes("await") && /\.\s*(then|catch)\s*\(/.test(bodyText);
|
|
2000
|
-
}
|
|
2001
|
-
case "format_arity_mismatch": {
|
|
2002
|
-
const formatNode = captures.FORMAT;
|
|
2003
|
-
const argsNode = captures.ARGS;
|
|
2004
|
-
if (!formatNode || !argsNode)
|
|
2005
|
-
return false;
|
|
2006
|
-
const fmtText = (formatNode.text ?? "").replace(/^["']|["']$/g, "");
|
|
2007
|
-
const fmt = fmtText;
|
|
2008
|
-
let placeholderCount = 0;
|
|
2009
|
-
const namedKeys = [];
|
|
2010
|
-
const positionalRegex = /%(?:\([^)]+\))?[#0\- +]*\d*(?:\.\d+)?[hlL]?[diouxXeEfFgGcrs%]/g;
|
|
2011
|
-
const positionalMatches = fmt.match(positionalRegex) ?? [];
|
|
2012
|
-
for (const m of positionalMatches) {
|
|
2013
|
-
if (m === "%%")
|
|
2014
|
-
continue;
|
|
2015
|
-
placeholderCount++;
|
|
2016
|
-
const namedMatch = m.match(/^%\(([^)]+)\)/);
|
|
2017
|
-
if (namedMatch)
|
|
2018
|
-
namedKeys.push(namedMatch[1]);
|
|
2019
|
-
}
|
|
2020
|
-
if (namedKeys.length > 0) {
|
|
2021
|
-
if (argsNode.type === "dictionary") {
|
|
2022
|
-
const dictKeys = [];
|
|
2023
|
-
for (const child of argsNode.children ?? []) {
|
|
2024
|
-
if (child.type === "pair" && child.children?.[0]) {
|
|
2025
|
-
dictKeys.push((child.children[0].text ?? "").replace(/^["']|["']$/g, ""));
|
|
2026
|
-
}
|
|
2027
|
-
}
|
|
2028
|
-
const missing = namedKeys.filter((k) => !dictKeys.includes(k));
|
|
2029
|
-
return missing.length > 0;
|
|
2030
|
-
}
|
|
2031
|
-
return true;
|
|
2032
|
-
}
|
|
2033
|
-
if (argsNode.type === "tuple") {
|
|
2034
|
-
const argCount = (argsNode.children ?? []).filter((c) => c.isNamed).length;
|
|
2035
|
-
if (argCount !== placeholderCount)
|
|
2036
|
-
return true;
|
|
2037
|
-
}
|
|
2038
|
-
return false;
|
|
2039
|
-
}
|
|
2040
|
-
case "aws_policy_public": {
|
|
2041
|
-
const policyNode = captures.POLICY;
|
|
2042
|
-
if (!policyNode)
|
|
2043
|
-
return false;
|
|
2044
|
-
const text = policyNode.text ?? "";
|
|
2045
|
-
const patterns = [
|
|
2046
|
-
/"Principal"\s*:\s*"\*"/,
|
|
2047
|
-
/"Principal"\s*:\s*\{\s*"AWS"\s*:\s*"\*"\s*\}/,
|
|
2048
|
-
/"Effect"\s*:\s*"Allow"[\s\S]*?"Action"\s*:\s*"\*"[\s\S]*?"Resource"\s*:\s*"\*"/,
|
|
2049
|
-
/"Principal"\s*:\s*"\*"/
|
|
2050
|
-
];
|
|
2051
|
-
return patterns.some((p) => p.test(text));
|
|
2052
|
-
}
|
|
2053
|
-
case "slots_attribute_mismatch": {
|
|
2054
|
-
const selfNode = captures.SELF;
|
|
2055
|
-
const attrNode = captures.ATTR;
|
|
2056
|
-
const methodNode = captures.METHOD;
|
|
2057
|
-
if (!selfNode || !attrNode || !methodNode)
|
|
2058
|
-
return false;
|
|
2059
|
-
if (selfNode.text !== "self")
|
|
2060
|
-
return false;
|
|
2061
|
-
const attrName = attrNode.text ?? "";
|
|
2062
|
-
let parent = methodNode.parent;
|
|
2063
|
-
while (parent && parent.type !== "class_definition") {
|
|
2064
|
-
parent = parent.parent;
|
|
2065
|
-
}
|
|
2066
|
-
if (!parent)
|
|
2067
|
-
return false;
|
|
2068
|
-
const slots = extractSlots(parent);
|
|
2069
|
-
if (slots === null || slots.length === 0)
|
|
2070
|
-
return false;
|
|
2071
|
-
return !slots.includes(attrName);
|
|
2072
|
-
}
|
|
2073
|
-
case "special_method_arity": {
|
|
2074
|
-
const nameNode = captures.NAME;
|
|
2075
|
-
const paramsNode = captures.PARAMS;
|
|
2076
|
-
if (!nameNode || !paramsNode)
|
|
2077
|
-
return false;
|
|
2078
|
-
const name = nameNode.text ?? "";
|
|
2079
|
-
const expected = {
|
|
2080
|
-
__del__: 0,
|
|
2081
|
-
__repr__: 0,
|
|
2082
|
-
__str__: 0,
|
|
2083
|
-
__hash__: 0,
|
|
2084
|
-
__bool__: 0,
|
|
2085
|
-
__len__: 0,
|
|
2086
|
-
__eq__: 1,
|
|
2087
|
-
__lt__: 1,
|
|
2088
|
-
__le__: 1,
|
|
2089
|
-
__gt__: 1,
|
|
2090
|
-
__ge__: 1,
|
|
2091
|
-
__ne__: 1
|
|
2092
|
-
};
|
|
2093
|
-
const expectedCount = expected[name];
|
|
2094
|
-
if (expectedCount === undefined)
|
|
2095
|
-
return false;
|
|
2096
|
-
const paramCount = (paramsNode.children ?? []).filter((c) => {
|
|
2097
|
-
if (c.type !== "identifier" && c.type !== "typed_parameter")
|
|
2098
|
-
return false;
|
|
2099
|
-
if (c.text.includes("="))
|
|
2100
|
-
return false;
|
|
2101
|
-
return true;
|
|
2102
|
-
}).length;
|
|
2103
|
-
return paramCount !== expectedCount + 1;
|
|
2104
|
-
}
|
|
2105
|
-
case "no_super_call": {
|
|
2106
|
-
const bodyNode = captures.BODY;
|
|
2107
|
-
if (!bodyNode)
|
|
2108
|
-
return true;
|
|
2109
|
-
return !/(?<!\/\/.*)super\s*\(/.test(bodyNode.text);
|
|
2110
|
-
}
|
|
2111
|
-
case "in_test_block": {
|
|
2112
|
-
const first = Object.values(captures)[0];
|
|
2113
|
-
return !!first && this.navigator.isInTestBlock(first);
|
|
2114
|
-
}
|
|
2115
|
-
case "not_in_test_block": {
|
|
2116
|
-
const first = Object.values(captures)[0];
|
|
2117
|
-
return !first || !this.navigator.isInTestBlock(first);
|
|
2118
|
-
}
|
|
2119
|
-
case "not_in_try_catch": {
|
|
2120
|
-
const first = Object.values(captures)[0];
|
|
2121
|
-
return !first || !this.navigator.isInTryCatch(first);
|
|
2122
|
-
}
|
|
2123
|
-
case "in_try_catch": {
|
|
2124
|
-
const first = Object.values(captures)[0];
|
|
2125
|
-
return !!first && this.navigator.isInTryCatch(first);
|
|
2126
|
-
}
|
|
2127
|
-
case "name_matches_param": {
|
|
2128
|
-
const nameNode = captures.NAME;
|
|
2129
|
-
const paramNode = captures.PARAM;
|
|
2130
|
-
return !!nameNode && !!paramNode && nameNode.text === paramNode.text;
|
|
2131
|
-
}
|
|
2132
|
-
case "not_in_function": {
|
|
2133
|
-
const first = Object.values(captures)[0];
|
|
2134
|
-
return !first || !this.navigator.isInside(first, [
|
|
2135
|
-
"function_definition",
|
|
2136
|
-
"function_declaration",
|
|
2137
|
-
"method_definition",
|
|
2138
|
-
"arrow_function"
|
|
2139
|
-
]);
|
|
2140
|
-
}
|
|
2141
|
-
case "check_secret_pattern": {
|
|
2142
|
-
const varName = captures.VARNAME?.text ?? "";
|
|
2143
|
-
const varNameLower = varName.toLowerCase();
|
|
2144
|
-
if (varName === varName.toUpperCase() && /[A-Z]/.test(varName)) {
|
|
2145
|
-
return false;
|
|
2146
|
-
}
|
|
2147
|
-
return [
|
|
2148
|
-
/api[_-]?key/,
|
|
2149
|
-
/api[_-]?secret/,
|
|
2150
|
-
/password/,
|
|
2151
|
-
/passwd/,
|
|
2152
|
-
/secret/,
|
|
2153
|
-
/token/,
|
|
2154
|
-
/auth/,
|
|
2155
|
-
/private[_-]?key/,
|
|
2156
|
-
/access[_-]?token/,
|
|
2157
|
-
/credentials/,
|
|
2158
|
-
/aws[_-]?secret/,
|
|
2159
|
-
/github[_-]?token/,
|
|
2160
|
-
/client[_-]?secret/
|
|
2161
|
-
].some((p) => p.test(varNameLower));
|
|
2162
|
-
}
|
|
2163
|
-
case "returns_error": {
|
|
2164
|
-
const first = Object.values(captures)[0];
|
|
2165
|
-
if (!first)
|
|
2166
|
-
return false;
|
|
2167
|
-
const funcNode = this.navigator.findParent(first, [
|
|
2168
|
-
"function_declaration",
|
|
2169
|
-
"method_declaration"
|
|
2170
|
-
]);
|
|
2171
|
-
if (!funcNode)
|
|
2172
|
-
return false;
|
|
2173
|
-
const signature = String(funcNode.text ?? "").split("{", 1)[0]?.trim() ?? "";
|
|
2174
|
-
const returnPart = signature.match(/func\s*(?:\([^)]*\)\s*)?[A-Za-z_]\w*\s*\([^)]*\)\s*(.*)$/s)?.[1]?.trim() ?? "";
|
|
2175
|
-
return returnPart.length > 0 && /\berror\b/.test(returnPart);
|
|
2176
|
-
}
|
|
2177
|
-
case "python_empty_except": {
|
|
2178
|
-
const bodyNode = captures.BODY;
|
|
2179
|
-
if (!bodyNode)
|
|
2180
|
-
return true;
|
|
2181
|
-
return !bodyNode.children.some((c) => c.isNamed && c.type !== "pass_statement" && c.type !== "comment");
|
|
2182
|
-
}
|
|
2183
|
-
case "check_in_operator_types": {
|
|
2184
|
-
const target = captures.TARGET;
|
|
2185
|
-
if (!target)
|
|
2186
|
-
return false;
|
|
2187
|
-
return ["none", "true", "false", "integer", "float"].includes(target.type);
|
|
2188
|
-
}
|
|
2189
|
-
case "torchscript_super_call": {
|
|
2190
|
-
const call = captures.CALL;
|
|
2191
|
-
if (!call)
|
|
2192
|
-
return false;
|
|
2193
|
-
const isTorchScriptDecorated = (node) => {
|
|
2194
|
-
const decorated = node?.parent;
|
|
2195
|
-
if (!decorated || decorated.type !== "decorated_definition")
|
|
2196
|
-
return false;
|
|
2197
|
-
return (decorated.children ?? []).some((c) => c.type === "decorator" && /^@(torch\.jit\.script|jit\.script)$/.test(c.text ?? ""));
|
|
2198
|
-
};
|
|
2199
|
-
const methodNode = this.navigator.findParent(call, [
|
|
2200
|
-
"function_definition"
|
|
2201
|
-
]);
|
|
2202
|
-
if (!methodNode)
|
|
2203
|
-
return false;
|
|
2204
|
-
const classNode = this.navigator.findParent(methodNode, [
|
|
2205
|
-
"class_definition"
|
|
2206
|
-
]);
|
|
2207
|
-
return isTorchScriptDecorated(methodNode) || isTorchScriptDecorated(classNode);
|
|
2208
|
-
}
|
|
2209
|
-
case "exit_params_insufficient": {
|
|
2210
|
-
const params = captures.PARAMS;
|
|
2211
|
-
if (!params)
|
|
2212
|
-
return true;
|
|
2213
|
-
const named = (params.children ?? []).filter((c) => c.isNamed);
|
|
2214
|
-
if (named.some((c) => c.type === "list_splat_pattern" || c.type === "dictionary_splat_pattern")) {
|
|
2215
|
-
return false;
|
|
2216
|
-
}
|
|
2217
|
-
return named.length < 4;
|
|
2218
|
-
}
|
|
2219
|
-
case "ruby_empty_rescue": {
|
|
2220
|
-
const bodyNode = captures.BODY;
|
|
2221
|
-
if (!bodyNode)
|
|
2222
|
-
return true;
|
|
2223
|
-
return !bodyNode.children.some((c) => c.isNamed && !["comment", "nil", "nil_literal"].includes(c.type));
|
|
2224
|
-
}
|
|
2225
|
-
case "ts_command_injection_sink":
|
|
2226
|
-
return captures.MOD?.text === "child_process" && /^(exec|execSync)$/.test(captures.FN?.text ?? "");
|
|
2227
|
-
case "ts_sql_injection_sink": {
|
|
2228
|
-
const template = captures.TEMPLATE?.text ?? "";
|
|
2229
|
-
const rawPrefix = template.replace(/^`/, "").replace(/`$/, "").replace(/^(?:\s|\/\*[\s\S]*?\*\/|--[^\n]*(?:\n|$))*/, "");
|
|
2230
|
-
if (/^(?:SELECT[\s\S]*(?:\bFROM\b|\$\{)|INSERT\s+INTO\b|UPDATE\s+[\s\S]*\bSET\b|DELETE\s+FROM\b|CREATE\s+(?:OR\s+REPLACE\s+|TEMP(?:ORARY)?\s+|UNIQUE\s+|MATERIALIZED\s+)*(?:TABLE|INDEX|VIEW|SCHEMA|DATABASE|TRIGGER|FUNCTION|PROCEDURE|SEQUENCE|TYPE|EXTENSION)\b|DROP\s+(?:OR\s+REPLACE\s+|TEMP(?:ORARY)?\s+|UNIQUE\s+|MATERIALIZED\s+)*(?:TABLE|INDEX|VIEW|SCHEMA|DATABASE|TRIGGER|FUNCTION|PROCEDURE|SEQUENCE|TYPE|EXTENSION)\b|ALTER\s+(?:OR\s+REPLACE\s+|TEMP(?:ORARY)?\s+|UNIQUE\s+|MATERIALIZED\s+)*(?:TABLE|INDEX|VIEW|SCHEMA|DATABASE|TRIGGER|FUNCTION|PROCEDURE|SEQUENCE|TYPE|EXTENSION)\b|GRANT\b|REVOKE\b|WITH[\s\S]*\bSELECT\b|MERGE\s+INTO\b|TRUNCATE\s+TABLE\b|REPLACE\s+INTO\b)/i.test(rawPrefix))
|
|
2231
|
-
return true;
|
|
2232
|
-
if (!rootNode || !captures.OBJ)
|
|
2233
|
-
return false;
|
|
2234
|
-
let imported = TYPESCRIPT_SQL_IMPORTS.get(rootNode);
|
|
2235
|
-
if (!imported) {
|
|
2236
|
-
const names = new Set;
|
|
2237
|
-
const stack = [rootNode];
|
|
2238
|
-
while (stack.length > 0) {
|
|
2239
|
-
const node = stack.pop();
|
|
2240
|
-
if (!node)
|
|
2241
|
-
continue;
|
|
2242
|
-
if (node.type === "import_statement") {
|
|
2243
|
-
const source = node.childForFieldName?.("source")?.text ?? "";
|
|
2244
|
-
const packageName = source.replace(/^['"]|['"]$/g, "");
|
|
2245
|
-
const isKnown = [...TYPESCRIPT_SQL_KNOWN_PACKAGES].some((pkg) => packageName === pkg || packageName.startsWith(`${pkg}/`));
|
|
2246
|
-
if (isKnown) {
|
|
2247
|
-
const importStack = [node];
|
|
2248
|
-
while (importStack.length > 0) {
|
|
2249
|
-
const importNode = importStack.pop();
|
|
2250
|
-
if (!importNode)
|
|
2251
|
-
continue;
|
|
2252
|
-
if (importNode.type === "identifier" || importNode.type === "import_specifier") {
|
|
2253
|
-
names.add(importNode.text);
|
|
2254
|
-
}
|
|
2255
|
-
importStack.push(...importNode.children);
|
|
2256
|
-
}
|
|
2257
|
-
}
|
|
2258
|
-
}
|
|
2259
|
-
stack.push(...node.children);
|
|
2260
|
-
}
|
|
2261
|
-
const declarations = new Map;
|
|
2262
|
-
const declarationStack = [rootNode];
|
|
2263
|
-
while (declarationStack.length > 0) {
|
|
2264
|
-
const node = declarationStack.pop();
|
|
2265
|
-
if (!node)
|
|
2266
|
-
continue;
|
|
2267
|
-
if (node.type === "variable_declarator") {
|
|
2268
|
-
const name = node.childForFieldName?.("name")?.text;
|
|
2269
|
-
const value = node.childForFieldName?.("value");
|
|
2270
|
-
if (name && value)
|
|
2271
|
-
declarations.set(name, value);
|
|
2272
|
-
}
|
|
2273
|
-
declarationStack.push(...node.children);
|
|
2274
|
-
}
|
|
2275
|
-
const resolvesKnownValue = (node, seen = new Set) => {
|
|
2276
|
-
if (node.type === "identifier") {
|
|
2277
|
-
if (names.has(node.text))
|
|
2278
|
-
return true;
|
|
2279
|
-
if (seen.has(node.text))
|
|
2280
|
-
return false;
|
|
2281
|
-
const value = declarations.get(node.text);
|
|
2282
|
-
if (!value)
|
|
2283
|
-
return false;
|
|
2284
|
-
seen.add(node.text);
|
|
2285
|
-
return resolvesKnownValue(value, seen);
|
|
2286
|
-
}
|
|
2287
|
-
if (node.type === "await_expression") {
|
|
2288
|
-
const value = node.children.find((child) => child.isNamed);
|
|
2289
|
-
return value ? resolvesKnownValue(value, seen) : false;
|
|
2290
|
-
}
|
|
2291
|
-
if (node.type === "new_expression") {
|
|
2292
|
-
const ctor = node.childForFieldName?.("constructor");
|
|
2293
|
-
return ctor ? resolvesKnownValue(ctor, seen) : false;
|
|
2294
|
-
}
|
|
2295
|
-
if (node.type === "call_expression") {
|
|
2296
|
-
const fn = node.childForFieldName?.("function");
|
|
2297
|
-
return fn ? resolvesKnownValue(fn, seen) : false;
|
|
2298
|
-
}
|
|
2299
|
-
return false;
|
|
2300
|
-
};
|
|
2301
|
-
let changed = true;
|
|
2302
|
-
while (changed) {
|
|
2303
|
-
changed = false;
|
|
2304
|
-
for (const [name, value] of declarations) {
|
|
2305
|
-
if (!names.has(name) && resolvesKnownValue(value)) {
|
|
2306
|
-
names.add(name);
|
|
2307
|
-
changed = true;
|
|
2308
|
-
}
|
|
2309
|
-
}
|
|
2310
|
-
}
|
|
2311
|
-
imported = names;
|
|
2312
|
-
TYPESCRIPT_SQL_IMPORTS.set(rootNode, imported);
|
|
2313
|
-
}
|
|
2314
|
-
const resolvesToKnownClient = (node) => {
|
|
2315
|
-
if (node.type === "identifier")
|
|
2316
|
-
return imported.has(node.text);
|
|
2317
|
-
if (node.type === "await_expression") {
|
|
2318
|
-
const value = node.children.find((child) => child.isNamed);
|
|
2319
|
-
return value ? resolvesToKnownClient(value) : false;
|
|
2320
|
-
}
|
|
2321
|
-
if (node.type === "new_expression") {
|
|
2322
|
-
const ctor = node.childForFieldName?.("constructor");
|
|
2323
|
-
return ctor ? resolvesToKnownClient(ctor) : false;
|
|
2324
|
-
}
|
|
2325
|
-
if (node.type === "call_expression") {
|
|
2326
|
-
const fn = node.childForFieldName?.("function");
|
|
2327
|
-
return fn ? resolvesToKnownClient(fn) : false;
|
|
2328
|
-
}
|
|
2329
|
-
return false;
|
|
2330
|
-
};
|
|
2331
|
-
return resolvesToKnownClient(captures.OBJ);
|
|
2332
|
-
}
|
|
2333
|
-
case "ts_ssrf_sink": {
|
|
2334
|
-
const fn = captures.FN?.text ?? "";
|
|
2335
|
-
const obj = captures.OBJ?.text ?? "";
|
|
2336
|
-
const urlText = captures.URL?.text ?? "";
|
|
2337
|
-
const allowedFns = new Set([
|
|
2338
|
-
"fetch",
|
|
2339
|
-
"request",
|
|
2340
|
-
"get",
|
|
2341
|
-
"post",
|
|
2342
|
-
"put",
|
|
2343
|
-
"patch",
|
|
2344
|
-
"delete"
|
|
2345
|
-
]);
|
|
2346
|
-
if (!allowedFns.has(fn))
|
|
2347
|
-
return false;
|
|
2348
|
-
if (rootNode && captures.URL?.type === "identifier" && this.resolvesToFileLiteralConst(urlText, rootNode)) {
|
|
2349
|
-
return false;
|
|
2350
|
-
}
|
|
2351
|
-
if (rootNode && captures.URL) {
|
|
2352
|
-
const urlVar = this.newUrlBaseVarName(captures.URL);
|
|
2353
|
-
if (urlVar && this.resolvesToFixedNewUrlConst(urlVar, rootNode)) {
|
|
2354
|
-
return false;
|
|
2355
|
-
}
|
|
2356
|
-
}
|
|
2357
|
-
const looksLikeExternalInput = urlText.includes(".") || /user|external|remote|input|target|webhook|callback|redirect|untrusted|arbitrary/i.test(urlText);
|
|
2358
|
-
if (!looksLikeExternalInput)
|
|
2359
|
-
return false;
|
|
2360
|
-
if (!obj)
|
|
2361
|
-
return fn === "fetch";
|
|
2362
|
-
return new Set([
|
|
2363
|
-
"axios",
|
|
2364
|
-
"http",
|
|
2365
|
-
"https",
|
|
2366
|
-
"got",
|
|
2367
|
-
"request",
|
|
2368
|
-
"superagent",
|
|
2369
|
-
"undici"
|
|
2370
|
-
]).has(obj);
|
|
2371
|
-
}
|
|
2372
|
-
case "ts_weak_hash_algorithm":
|
|
2373
|
-
return captures.FN?.text === "createHash" && /^(md5|sha1)$/i.test(captures.ALG?.text ?? "");
|
|
2374
|
-
case "ts_insecure_random_source": {
|
|
2375
|
-
if (captures.OBJ?.text !== "Math" || captures.FN?.text !== "random")
|
|
2376
|
-
return false;
|
|
2377
|
-
const varName = this.enclosingBindingName(captures.CALL ?? captures.VAR);
|
|
2378
|
-
return /token|secret|password|key|nonce|salt|csrf|auth|session|credential|hash|otp|pin/i.test(varName);
|
|
2379
|
-
}
|
|
2380
|
-
case "ts_detached_async_call":
|
|
2381
|
-
return /(Async$|fetch$|request$)/.test(captures.FN?.text ?? "");
|
|
2382
|
-
case "incomplete_assertion": {
|
|
2383
|
-
const expectNode = captures.EXPECT;
|
|
2384
|
-
if (!expectNode)
|
|
2385
|
-
return false;
|
|
2386
|
-
const CHAI_PROPERTY_ASSERTIONS = new Set([
|
|
2387
|
-
"true",
|
|
2388
|
-
"false",
|
|
2389
|
-
"null",
|
|
2390
|
-
"undefined",
|
|
2391
|
-
"empty",
|
|
2392
|
-
"NaN",
|
|
2393
|
-
"finite",
|
|
2394
|
-
"exist",
|
|
2395
|
-
"arguments",
|
|
2396
|
-
"extensible",
|
|
2397
|
-
"sealed",
|
|
2398
|
-
"frozen",
|
|
2399
|
-
"locked"
|
|
2400
|
-
]);
|
|
2401
|
-
let current = expectNode.parent;
|
|
2402
|
-
if (!current)
|
|
2403
|
-
return false;
|
|
2404
|
-
current = current.parent;
|
|
2405
|
-
if (!current)
|
|
2406
|
-
return false;
|
|
2407
|
-
if (current.type === "expression_statement" || current.type === "return_statement")
|
|
2408
|
-
return true;
|
|
2409
|
-
let lastPropertyName = null;
|
|
2410
|
-
while (current && current.type === "member_expression") {
|
|
2411
|
-
const propNode = current.children?.find((c) => c.type === "property_identifier");
|
|
2412
|
-
if (propNode)
|
|
2413
|
-
lastPropertyName = propNode.text;
|
|
2414
|
-
const parent = current.parent;
|
|
2415
|
-
if (!parent)
|
|
2416
|
-
return false;
|
|
2417
|
-
if (parent.type === "expression_statement" || parent.type === "return_statement") {
|
|
2418
|
-
if (lastPropertyName && CHAI_PROPERTY_ASSERTIONS.has(lastPropertyName))
|
|
2419
|
-
return false;
|
|
2420
|
-
return true;
|
|
2421
|
-
}
|
|
2422
|
-
if (parent.type === "call_expression")
|
|
2423
|
-
return false;
|
|
2424
|
-
current = parent;
|
|
2425
|
-
}
|
|
2426
|
-
return false;
|
|
2427
|
-
}
|
|
2428
|
-
case "py_command_injection_sink": {
|
|
2429
|
-
const mod = captures.MOD?.text ?? "";
|
|
2430
|
-
const fn = captures.FN?.text ?? "";
|
|
2431
|
-
const kw = captures.KW?.text ?? "";
|
|
2432
|
-
return mod === "os" && /^(system|popen)$/.test(fn) || mod === "subprocess" && /^(run|Popen|call|check_output|check_call)$/.test(fn) && kw === "shell";
|
|
2433
|
-
}
|
|
2434
|
-
case "go_command_injection_sink":
|
|
2435
|
-
return captures.PKG?.text === "exec" && /^(Command|CommandContext)$/.test(captures.FN?.text ?? "") && /^"(sh|bash|zsh|cmd|powershell|pwsh)"$/.test(captures.SHELL?.text ?? "") && /^"(-c|\/c)"$/.test(captures.FLAG?.text ?? "");
|
|
2436
|
-
case "ruby_command_injection_sink":
|
|
2437
|
-
return /^(system|exec|spawn|popen|capture3|capture2|capture2e)$/.test(captures.FN?.text ?? "");
|
|
2438
|
-
case "py_ssrf_sink":
|
|
2439
|
-
return captures.MOD?.text === "requests" && /^(get|post|put|patch|delete|request|head|options)$/.test(captures.FN?.text ?? "");
|
|
2440
|
-
case "py_path_traversal_sink":
|
|
2441
|
-
return /^(open|read_text|read_bytes|write_text|write_bytes|remove|unlink|rmdir)$/.test(captures.FN?.text ?? "");
|
|
2442
|
-
case "go_path_traversal_sink":
|
|
2443
|
-
return /^(os|ioutil)$/.test(captures.PKG?.text ?? "") && /^(Open|OpenFile|ReadFile|WriteFile|Create|Remove|RemoveAll)$/.test(captures.FN?.text ?? "");
|
|
2444
|
-
case "py_sql_injection_sink": {
|
|
2445
|
-
const fn = captures.FN?.text ?? "";
|
|
2446
|
-
if (!PYTHON_SQL_SINK_METHODS.has(fn))
|
|
2447
|
-
return false;
|
|
2448
|
-
const sqlNode = captures.SQL;
|
|
2449
|
-
const receiver = captures.OBJ;
|
|
2450
|
-
if (fn === "execute" && this.isLikelySqlAlchemyReceiver(receiver?.text ?? "")) {
|
|
2451
|
-
return false;
|
|
2452
|
-
}
|
|
2453
|
-
if (sqlNode && this.isSafeSqlAlchemyExpressionCall(sqlNode)) {
|
|
2454
|
-
return false;
|
|
2455
|
-
}
|
|
2456
|
-
if (isProvenSqlAlchemySessionReceiver(receiver, rootNode)) {
|
|
2457
|
-
if (fn === "query" && isSqlAlchemyEntityQueryArgument(sqlNode, rootNode)) {
|
|
2458
|
-
return false;
|
|
2459
|
-
}
|
|
2460
|
-
if (fn === "execute" && isSqlAlchemyStatementArgument(sqlNode, rootNode)) {
|
|
2461
|
-
return false;
|
|
2462
|
-
}
|
|
2463
|
-
}
|
|
2464
|
-
if (isSafePsycopgIdentifierComposition(sqlNode, rootNode)) {
|
|
2465
|
-
return false;
|
|
2466
|
-
}
|
|
2467
|
-
return true;
|
|
2468
|
-
}
|
|
2469
|
-
case "go_sql_injection_sink":
|
|
2470
|
-
return /^(Query|QueryContext|QueryRow|QueryRowContext|Exec|ExecContext)$/.test(captures.DBFN?.text ?? "") && captures.FMTPKG?.text === "fmt" && captures.FMTFN?.text === "Sprintf";
|
|
2471
|
-
case "py_insecure_deserialization_sink":
|
|
2472
|
-
return /^(pickle|yaml)$/.test(captures.MOD?.text ?? "") && /^(load|loads|unsafe_load)$/.test(captures.FN?.text ?? "");
|
|
2473
|
-
case "ruby_insecure_deserialization_sink":
|
|
2474
|
-
return /^(Marshal|YAML|Psych)$/.test(captures.MOD?.text ?? "") && /^(load|unsafe_load)$/.test(captures.FN?.text ?? "");
|
|
2475
|
-
case "match_captures": {
|
|
2476
|
-
for (const [captureName, pattern] of Object.entries(postFilterParams ?? {})) {
|
|
2477
|
-
const node = captures[captureName];
|
|
2478
|
-
if (!node)
|
|
2479
|
-
return false;
|
|
2480
|
-
if (!new RegExp(pattern).test(node.text))
|
|
2481
|
-
return false;
|
|
2482
|
-
}
|
|
2483
|
-
return true;
|
|
2484
|
-
}
|
|
2485
|
-
case "case_range_single_value": {
|
|
2486
|
-
const start = captures.START?.text ?? "";
|
|
2487
|
-
const end = captures.END?.text ?? "";
|
|
2488
|
-
return start === end;
|
|
2489
|
-
}
|
|
2490
|
-
case "goto_jumps_backward": {
|
|
2491
|
-
const label = captures.LABEL;
|
|
2492
|
-
const gotoNode = captures.GOTO;
|
|
2493
|
-
if (!label || !gotoNode)
|
|
2494
|
-
return false;
|
|
2495
|
-
return label.startIndex < gotoNode.startIndex;
|
|
2496
|
-
}
|
|
2497
|
-
case "goto_targets_inner_block": {
|
|
2498
|
-
const target = captures.TARGET;
|
|
2499
|
-
if (!target)
|
|
2500
|
-
return false;
|
|
2501
|
-
let depth = 0;
|
|
2502
|
-
let node = target.parent;
|
|
2503
|
-
while (node) {
|
|
2504
|
-
if (node.type === "compound_statement")
|
|
2505
|
-
depth++;
|
|
2506
|
-
node = node.parent;
|
|
2507
|
-
}
|
|
2508
|
-
return depth >= 2;
|
|
2509
|
-
}
|
|
2510
|
-
case "c_memset_sensitive_arg": {
|
|
2511
|
-
const callNode = captures.CALL;
|
|
2512
|
-
if (!callNode || callNode.type !== "call_expression")
|
|
2513
|
-
return false;
|
|
2514
|
-
const argList = callNode.children?.find((c) => c.type === "argument_list");
|
|
2515
|
-
if (!argList)
|
|
2516
|
-
return false;
|
|
2517
|
-
const firstArg = argList.children?.find((c) => c.isNamed);
|
|
2518
|
-
if (!firstArg)
|
|
2519
|
-
return false;
|
|
2520
|
-
const argName = firstArg.text ?? "";
|
|
2521
|
-
return /password|secret|key|token|credential|auth|private|passwd|pin|salt|nonce|iv|seed/i.test(argName);
|
|
2522
|
-
}
|
|
2523
|
-
case "c_stdlib_name": {
|
|
2524
|
-
const name = captures.NAME?.text ?? "";
|
|
2525
|
-
const STDLIB_NAMES = new Set([
|
|
2526
|
-
"malloc",
|
|
2527
|
-
"calloc",
|
|
2528
|
-
"realloc",
|
|
2529
|
-
"free",
|
|
2530
|
-
"alloca",
|
|
2531
|
-
"printf",
|
|
2532
|
-
"fprintf",
|
|
2533
|
-
"sprintf",
|
|
2534
|
-
"snprintf",
|
|
2535
|
-
"vprintf",
|
|
2536
|
-
"vfprintf",
|
|
2537
|
-
"vsprintf",
|
|
2538
|
-
"vsnprintf",
|
|
2539
|
-
"scanf",
|
|
2540
|
-
"fscanf",
|
|
2541
|
-
"sscanf",
|
|
2542
|
-
"vscanf",
|
|
2543
|
-
"vfscanf",
|
|
2544
|
-
"vsscanf",
|
|
2545
|
-
"strcpy",
|
|
2546
|
-
"strncpy",
|
|
2547
|
-
"strcat",
|
|
2548
|
-
"strncat",
|
|
2549
|
-
"strcmp",
|
|
2550
|
-
"strncmp",
|
|
2551
|
-
"strlen",
|
|
2552
|
-
"strchr",
|
|
2553
|
-
"strrchr",
|
|
2554
|
-
"strstr",
|
|
2555
|
-
"strerror",
|
|
2556
|
-
"memcpy",
|
|
2557
|
-
"memmove",
|
|
2558
|
-
"memset",
|
|
2559
|
-
"memcmp",
|
|
2560
|
-
"memchr",
|
|
2561
|
-
"fopen",
|
|
2562
|
-
"fclose",
|
|
2563
|
-
"fread",
|
|
2564
|
-
"fwrite",
|
|
2565
|
-
"fgets",
|
|
2566
|
-
"fputs",
|
|
2567
|
-
"getc",
|
|
2568
|
-
"putc",
|
|
2569
|
-
"getchar",
|
|
2570
|
-
"putchar",
|
|
2571
|
-
"exit",
|
|
2572
|
-
"abort",
|
|
2573
|
-
"assert",
|
|
2574
|
-
"errno",
|
|
2575
|
-
"abs",
|
|
2576
|
-
"labs",
|
|
2577
|
-
"llabs",
|
|
2578
|
-
"div",
|
|
2579
|
-
"ldiv",
|
|
2580
|
-
"lldiv",
|
|
2581
|
-
"atoi",
|
|
2582
|
-
"atol",
|
|
2583
|
-
"atoll",
|
|
2584
|
-
"strtol",
|
|
2585
|
-
"strtoll",
|
|
2586
|
-
"strtoul",
|
|
2587
|
-
"strtoull",
|
|
2588
|
-
"strtod",
|
|
2589
|
-
"strtof",
|
|
2590
|
-
"strtold",
|
|
2591
|
-
"qsort",
|
|
2592
|
-
"bsearch",
|
|
2593
|
-
"time",
|
|
2594
|
-
"clock",
|
|
2595
|
-
"difftime",
|
|
2596
|
-
"mktime",
|
|
2597
|
-
"strftime",
|
|
2598
|
-
"getenv",
|
|
2599
|
-
"setenv",
|
|
2600
|
-
"putenv",
|
|
2601
|
-
"system",
|
|
2602
|
-
"isalpha",
|
|
2603
|
-
"isdigit",
|
|
2604
|
-
"isalnum",
|
|
2605
|
-
"isspace",
|
|
2606
|
-
"isupper",
|
|
2607
|
-
"islower",
|
|
2608
|
-
"toupper",
|
|
2609
|
-
"tolower",
|
|
2610
|
-
"sizeof",
|
|
2611
|
-
"offsetof",
|
|
2612
|
-
"NULL",
|
|
2613
|
-
"EXIT_SUCCESS",
|
|
2614
|
-
"EXIT_FAILURE"
|
|
2615
|
-
]);
|
|
2616
|
-
return STDLIB_NAMES.has(name);
|
|
2617
|
-
}
|
|
2618
|
-
case "c_octal_literal": {
|
|
2619
|
-
const num = captures.NUM?.text ?? "";
|
|
2620
|
-
return /^0[0-7]+$/.test(num);
|
|
2621
|
-
}
|
|
2622
|
-
case "c_noreturn_attr": {
|
|
2623
|
-
const attr = captures.ATTR?.text ?? "";
|
|
2624
|
-
return attr === "noreturn";
|
|
2625
|
-
}
|
|
2626
|
-
case "c_label_in_switch": {
|
|
2627
|
-
const stmt = captures.STMT;
|
|
2628
|
-
if (!stmt)
|
|
2629
|
-
return false;
|
|
2630
|
-
let node = stmt.parent;
|
|
2631
|
-
while (node) {
|
|
2632
|
-
if (node.type === "switch_statement")
|
|
2633
|
-
return true;
|
|
2634
|
-
node = node.parent;
|
|
2635
|
-
}
|
|
2636
|
-
return false;
|
|
2637
|
-
}
|
|
2638
|
-
default:
|
|
2639
|
-
this.reportMissingPostFilter(postFilter);
|
|
2640
|
-
return false;
|
|
2641
|
-
}
|
|
2642
|
-
}
|
|
2643
|
-
reportedMissingPostFilters = new Set;
|
|
2644
|
-
reportedPostFilterFailures = new Set;
|
|
2645
|
-
reportPostFilterFailure(postFilter, reason) {
|
|
2646
|
-
if (this.reportedPostFilterFailures.has(postFilter))
|
|
2647
|
-
return;
|
|
2648
|
-
this.reportedPostFilterFailures.add(postFilter);
|
|
2649
|
-
logTreeSitterDiagnostic({
|
|
2650
|
-
subsystem: "tree-sitter-client",
|
|
2651
|
-
message: `tree-sitter rule post_filter '${postFilter}' failed — ` + `keeping the diagnostic (fail-open): ${reason}.`,
|
|
2652
|
-
metadata: { postFilter }
|
|
2653
|
-
});
|
|
2654
|
-
}
|
|
2655
|
-
reportMissingPostFilter(postFilter) {
|
|
2656
|
-
if (this.reportedMissingPostFilters.has(postFilter))
|
|
2657
|
-
return;
|
|
2658
|
-
this.reportedMissingPostFilters.add(postFilter);
|
|
2659
|
-
logTreeSitterDiagnostic({
|
|
2660
|
-
subsystem: "tree-sitter-client",
|
|
2661
|
-
message: `tree-sitter rule post_filter '${postFilter}' is not implemented — ` + `matches for the rules using it are suppressed rather than reported unfiltered. ` + `Implement it in applyPostFilter (clients/tree-sitter-client.ts) to re-enable them.`,
|
|
2662
|
-
metadata: { postFilter }
|
|
2663
|
-
});
|
|
2664
|
-
}
|
|
2665
|
-
textPredicatesInvalidLogged = false;
|
|
2666
|
-
hasValidTextPredicates(query) {
|
|
2667
|
-
const valid = Array.isArray(query?.textPredicates);
|
|
2668
|
-
if (!valid) {
|
|
2669
|
-
if (!this.textPredicatesInvalidLogged) {
|
|
2670
|
-
this.textPredicatesInvalidLogged = true;
|
|
2671
|
-
logTreeSitterDiagnostic({
|
|
2672
|
-
subsystem: "tree-sitter-client",
|
|
2673
|
-
message: "web-tree-sitter Query.textPredicates is missing or not an array — " + "#match?/#eq? predicates cannot be evaluated. Failing CLOSED: matches " + "for this query are dropped rather than reported unfiltered.",
|
|
2674
|
-
metadata: { textPredicatesType: typeof query?.textPredicates }
|
|
2675
|
-
});
|
|
2676
|
-
}
|
|
2677
|
-
recordDegradationOnce({
|
|
2678
|
-
kind: "query-predicates-invalid",
|
|
2679
|
-
subject: "web-tree-sitter",
|
|
2680
|
-
reason: "Query.textPredicates is missing or not an array — #match?/#eq? " + "predicates cannot be evaluated; structural matches relying on them " + "are dropped fail-closed"
|
|
2681
|
-
});
|
|
2682
|
-
}
|
|
2683
|
-
return valid;
|
|
2684
|
-
}
|
|
2685
|
-
evaluatePredicates(query, match) {
|
|
2686
|
-
if (!this.hasValidTextPredicates(query))
|
|
2687
|
-
return false;
|
|
2688
|
-
const predicates = query.textPredicates?.[match.patternIndex] ?? [];
|
|
2689
|
-
return predicates.every((fn) => fn(match.captures));
|
|
2690
|
-
}
|
|
2691
|
-
async searchFileWithQuery(filePath, query, metavars, languageId, _originalPattern, postFilter, postFilterParams, contentOverride) {
|
|
2692
|
-
const matches = [];
|
|
2693
|
-
await this.parseFileAndUse(filePath, languageId, contentOverride, (tree) => {
|
|
2694
|
-
try {
|
|
2695
|
-
const rootNode = tree.rootNode;
|
|
2696
|
-
const queryMatches = query.matches(rootNode);
|
|
2697
|
-
for (const match of queryMatches) {
|
|
2698
|
-
const captures = {};
|
|
2699
|
-
for (const capture of match.captures) {
|
|
2700
|
-
if (metavars.includes(capture.name)) {
|
|
2701
|
-
captures[capture.name] = capture.node;
|
|
2702
|
-
}
|
|
2703
|
-
}
|
|
2704
|
-
if (!this.evaluatePredicates(query, match)) {
|
|
2705
|
-
continue;
|
|
2706
|
-
}
|
|
2707
|
-
if (postFilter && !this.applyPostFilter(postFilter, postFilterParams, captures, rootNode)) {
|
|
2708
|
-
continue;
|
|
2709
|
-
}
|
|
2710
|
-
if (match.captures.length > 0) {
|
|
2711
|
-
const firstNode = match.captures[0].node;
|
|
2712
|
-
const textCaptures = {};
|
|
2713
|
-
for (const [name, node] of Object.entries(captures)) {
|
|
2714
|
-
textCaptures[name] = node.text;
|
|
2715
|
-
}
|
|
2716
|
-
matches.push({
|
|
2717
|
-
file: filePath,
|
|
2718
|
-
line: firstNode.startPosition.row + 1,
|
|
2719
|
-
column: firstNode.startPosition.column + 1,
|
|
2720
|
-
matchedText: firstNode.text,
|
|
2721
|
-
nodeType: firstNode.type,
|
|
2722
|
-
captures: textCaptures
|
|
2723
|
-
});
|
|
2724
|
-
}
|
|
2725
|
-
}
|
|
2726
|
-
if (matches.length > 0) {
|
|
2727
|
-
this.dbg(`Found ${matches.length} matches in ${path.basename(filePath)}`);
|
|
2728
|
-
}
|
|
2729
|
-
} catch (err) {
|
|
2730
|
-
this.reportWasmAbort(err);
|
|
2731
|
-
this.dbg(`Query matching error: ${err}`);
|
|
2732
|
-
}
|
|
2733
|
-
});
|
|
2734
|
-
return matches;
|
|
2735
|
-
}
|
|
2736
|
-
collectFiles(dir, languageId, fileFilter) {
|
|
2737
|
-
const files = [];
|
|
2738
|
-
const extensions = this.getExtensionsForLanguage(languageId);
|
|
2739
|
-
const rootDir = path.resolve(dir);
|
|
2740
|
-
const ignoreMatcher = getProjectIgnoreMatcher(rootDir);
|
|
2741
|
-
const scan = (d) => {
|
|
2742
|
-
if (files.length >= TREE_SITTER_MAX_SCAN_FILES)
|
|
2743
|
-
return;
|
|
2744
|
-
try {
|
|
2745
|
-
const entries = fs.readdirSync(d, { withFileTypes: true });
|
|
2746
|
-
for (const entry of entries) {
|
|
2747
|
-
if (files.length >= TREE_SITTER_MAX_SCAN_FILES)
|
|
2748
|
-
return;
|
|
2749
|
-
const full = path.join(d, entry.name);
|
|
2750
|
-
if (entry.isDirectory()) {
|
|
2751
|
-
if (isExcludedDirName(entry.name))
|
|
2752
|
-
continue;
|
|
2753
|
-
if (ignoreMatcher.isIgnored(full, true))
|
|
2754
|
-
continue;
|
|
2755
|
-
scan(full);
|
|
2756
|
-
} else if (extensions.some((ext) => entry.name.endsWith(ext))) {
|
|
2757
|
-
if (ignoreMatcher.isIgnored(full, false))
|
|
2758
|
-
continue;
|
|
2759
|
-
if (!fileFilter || fileFilter(full)) {
|
|
2760
|
-
files.push(full);
|
|
2761
|
-
}
|
|
2762
|
-
}
|
|
2763
|
-
}
|
|
2764
|
-
} catch {}
|
|
2765
|
-
};
|
|
2766
|
-
scan(rootDir);
|
|
2767
|
-
return files;
|
|
2768
|
-
}
|
|
2769
|
-
getExtensionsForLanguage(languageId) {
|
|
2770
|
-
const mapping = {
|
|
2771
|
-
typescript: [".ts", ".mts", ".cts"],
|
|
2772
|
-
tsx: [".tsx"],
|
|
2773
|
-
javascript: [".js", ".mjs", ".cjs"],
|
|
2774
|
-
python: [".py"],
|
|
2775
|
-
rust: [".rs"],
|
|
2776
|
-
go: [".go"],
|
|
2777
|
-
java: [".java"],
|
|
2778
|
-
kotlin: [".kt", ".kts"],
|
|
2779
|
-
dart: [".dart"],
|
|
2780
|
-
c: [".c", ".h"],
|
|
2781
|
-
cpp: [".cpp", ".hpp", ".cc", ".hh"],
|
|
2782
|
-
elixir: [".ex", ".exs"],
|
|
2783
|
-
ruby: [".rb"]
|
|
2784
|
-
};
|
|
2785
|
-
return mapping[languageId] || [];
|
|
2786
|
-
}
|
|
2787
|
-
}
|