@optave/codegraph 3.13.0 → 3.15.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +35 -34
- package/dist/ast-analysis/engine.d.ts.map +1 -1
- package/dist/ast-analysis/engine.js +38 -40
- package/dist/ast-analysis/engine.js.map +1 -1
- package/dist/ast-analysis/rules/b2.d.ts +7 -0
- package/dist/ast-analysis/rules/b2.d.ts.map +1 -0
- package/dist/ast-analysis/rules/b2.js +240 -0
- package/dist/ast-analysis/rules/b2.js.map +1 -0
- package/dist/ast-analysis/rules/b3.d.ts +6 -0
- package/dist/ast-analysis/rules/b3.d.ts.map +1 -0
- package/dist/ast-analysis/rules/b3.js +105 -0
- package/dist/ast-analysis/rules/b3.js.map +1 -0
- package/dist/ast-analysis/rules/b4.d.ts +9 -0
- package/dist/ast-analysis/rules/b4.d.ts.map +1 -0
- package/dist/ast-analysis/rules/b4.js +361 -0
- package/dist/ast-analysis/rules/b4.js.map +1 -0
- package/dist/ast-analysis/rules/b5.d.ts +4 -0
- package/dist/ast-analysis/rules/b5.d.ts.map +1 -0
- package/dist/ast-analysis/rules/b5.js +52 -0
- package/dist/ast-analysis/rules/b5.js.map +1 -0
- package/dist/ast-analysis/rules/c.d.ts +4 -0
- package/dist/ast-analysis/rules/c.d.ts.map +1 -0
- package/dist/ast-analysis/rules/c.js +143 -0
- package/dist/ast-analysis/rules/c.js.map +1 -0
- package/dist/ast-analysis/rules/index.d.ts.map +1 -1
- package/dist/ast-analysis/rules/index.js +34 -0
- package/dist/ast-analysis/rules/index.js.map +1 -1
- package/dist/ast-analysis/rules/javascript.d.ts.map +1 -1
- package/dist/ast-analysis/rules/javascript.js +3 -0
- package/dist/ast-analysis/rules/javascript.js.map +1 -1
- package/dist/ast-analysis/shared.d.ts.map +1 -1
- package/dist/ast-analysis/shared.js +2 -0
- package/dist/ast-analysis/shared.js.map +1 -1
- package/dist/ast-analysis/visitor-utils.d.ts +1 -0
- package/dist/ast-analysis/visitor-utils.d.ts.map +1 -1
- package/dist/ast-analysis/visitor-utils.js +5 -0
- package/dist/ast-analysis/visitor-utils.js.map +1 -1
- package/dist/ast-analysis/visitor.d.ts.map +1 -1
- package/dist/ast-analysis/visitor.js +60 -47
- package/dist/ast-analysis/visitor.js.map +1 -1
- package/dist/ast-analysis/visitors/cfg-visitor.d.ts.map +1 -1
- package/dist/ast-analysis/visitors/cfg-visitor.js +126 -76
- package/dist/ast-analysis/visitors/cfg-visitor.js.map +1 -1
- package/dist/ast-analysis/visitors/complexity-visitor.d.ts.map +1 -1
- package/dist/ast-analysis/visitors/complexity-visitor.js +27 -15
- package/dist/ast-analysis/visitors/complexity-visitor.js.map +1 -1
- package/dist/ast-analysis/visitors/dataflow-visitor.d.ts.map +1 -1
- package/dist/ast-analysis/visitors/dataflow-visitor.js +54 -21
- package/dist/ast-analysis/visitors/dataflow-visitor.js.map +1 -1
- package/dist/cli/commands/config.d.ts.map +1 -1
- package/dist/cli/commands/config.js +137 -134
- package/dist/cli/commands/config.js.map +1 -1
- package/dist/cli/commands/roles.d.ts.map +1 -1
- package/dist/cli/commands/roles.js +6 -1
- package/dist/cli/commands/roles.js.map +1 -1
- package/dist/db/better-sqlite3.d.ts +2 -1
- package/dist/db/better-sqlite3.d.ts.map +1 -1
- package/dist/db/better-sqlite3.js.map +1 -1
- package/dist/db/connection.d.ts +7 -1
- package/dist/db/connection.d.ts.map +1 -1
- package/dist/db/connection.js +20 -5
- package/dist/db/connection.js.map +1 -1
- package/dist/db/index.d.ts +1 -1
- package/dist/db/index.d.ts.map +1 -1
- package/dist/db/index.js +1 -1
- package/dist/db/index.js.map +1 -1
- package/dist/db/migrations.d.ts.map +1 -1
- package/dist/db/migrations.js +68 -0
- package/dist/db/migrations.js.map +1 -1
- package/dist/db/repository/build-stmts.d.ts.map +1 -1
- package/dist/db/repository/build-stmts.js +18 -0
- package/dist/db/repository/build-stmts.js.map +1 -1
- package/dist/db/repository/dataflow.d.ts +5 -0
- package/dist/db/repository/dataflow.d.ts.map +1 -1
- package/dist/db/repository/dataflow.js +14 -0
- package/dist/db/repository/dataflow.js.map +1 -1
- package/dist/db/repository/index.d.ts +1 -1
- package/dist/db/repository/index.d.ts.map +1 -1
- package/dist/db/repository/index.js +1 -1
- package/dist/db/repository/index.js.map +1 -1
- package/dist/db/repository/native-repository.d.ts.map +1 -1
- package/dist/db/repository/native-repository.js +47 -34
- package/dist/db/repository/native-repository.js.map +1 -1
- package/dist/domain/analysis/context.d.ts +2 -2
- package/dist/domain/analysis/dependencies.d.ts +2 -2
- package/dist/domain/analysis/diff-impact.d.ts +2 -2
- package/dist/domain/analysis/fn-impact.d.ts +3 -1
- package/dist/domain/analysis/fn-impact.d.ts.map +1 -1
- package/dist/domain/analysis/fn-impact.js +4 -0
- package/dist/domain/analysis/fn-impact.js.map +1 -1
- package/dist/domain/analysis/implementations.d.ts +2 -2
- package/dist/domain/analysis/module-map.d.ts.map +1 -1
- package/dist/domain/analysis/module-map.js +32 -5
- package/dist/domain/analysis/module-map.js.map +1 -1
- package/dist/domain/analysis/roles.d.ts +7 -1
- package/dist/domain/analysis/roles.d.ts.map +1 -1
- package/dist/domain/analysis/roles.js +16 -0
- package/dist/domain/analysis/roles.js.map +1 -1
- package/dist/domain/analysis/symbol-lookup.d.ts +4 -4
- package/dist/domain/graph/builder/call-resolver.d.ts +17 -5
- package/dist/domain/graph/builder/call-resolver.d.ts.map +1 -1
- package/dist/domain/graph/builder/call-resolver.js +85 -220
- package/dist/domain/graph/builder/call-resolver.js.map +1 -1
- package/dist/domain/graph/builder/context.d.ts +1 -0
- package/dist/domain/graph/builder/context.d.ts.map +1 -1
- package/dist/domain/graph/builder/context.js.map +1 -1
- package/dist/domain/graph/builder/helpers.d.ts +16 -1
- package/dist/domain/graph/builder/helpers.d.ts.map +1 -1
- package/dist/domain/graph/builder/helpers.js +162 -72
- package/dist/domain/graph/builder/helpers.js.map +1 -1
- package/dist/domain/graph/builder/incremental.d.ts.map +1 -1
- package/dist/domain/graph/builder/incremental.js +166 -97
- package/dist/domain/graph/builder/incremental.js.map +1 -1
- package/dist/domain/graph/builder/pipeline.d.ts.map +1 -1
- package/dist/domain/graph/builder/pipeline.js +10 -4
- package/dist/domain/graph/builder/pipeline.js.map +1 -1
- package/dist/domain/graph/builder/stages/build-edges.d.ts.map +1 -1
- package/dist/domain/graph/builder/stages/build-edges.js +496 -250
- package/dist/domain/graph/builder/stages/build-edges.js.map +1 -1
- package/dist/domain/graph/builder/stages/collect-files.d.ts.map +1 -1
- package/dist/domain/graph/builder/stages/collect-files.js +10 -7
- package/dist/domain/graph/builder/stages/collect-files.js.map +1 -1
- package/dist/domain/graph/builder/stages/detect-changes.d.ts.map +1 -1
- package/dist/domain/graph/builder/stages/detect-changes.js +2 -1
- package/dist/domain/graph/builder/stages/detect-changes.js.map +1 -1
- package/dist/domain/graph/builder/stages/native-orchestrator.d.ts.map +1 -1
- package/dist/domain/graph/builder/stages/native-orchestrator.js +895 -545
- package/dist/domain/graph/builder/stages/native-orchestrator.js.map +1 -1
- package/dist/domain/graph/resolver/points-to.d.ts.map +1 -1
- package/dist/domain/graph/resolver/points-to.js +105 -57
- package/dist/domain/graph/resolver/points-to.js.map +1 -1
- package/dist/domain/graph/resolver/strategy.d.ts +61 -0
- package/dist/domain/graph/resolver/strategy.d.ts.map +1 -0
- package/dist/domain/graph/resolver/strategy.js +222 -0
- package/dist/domain/graph/resolver/strategy.js.map +1 -0
- package/dist/domain/graph/watcher.d.ts.map +1 -1
- package/dist/domain/graph/watcher.js +16 -9
- package/dist/domain/graph/watcher.js.map +1 -1
- package/dist/domain/parser.d.ts +12 -0
- package/dist/domain/parser.d.ts.map +1 -1
- package/dist/domain/parser.js +12 -2
- package/dist/domain/parser.js.map +1 -1
- package/dist/domain/queries.d.ts +1 -1
- package/dist/domain/queries.d.ts.map +1 -1
- package/dist/domain/queries.js +1 -1
- package/dist/domain/queries.js.map +1 -1
- package/dist/domain/wasm-worker-entry.js +3 -0
- package/dist/domain/wasm-worker-entry.js.map +1 -1
- package/dist/domain/wasm-worker-pool.d.ts.map +1 -1
- package/dist/domain/wasm-worker-pool.js +24 -5
- package/dist/domain/wasm-worker-pool.js.map +1 -1
- package/dist/domain/wasm-worker-protocol.d.ts +7 -0
- package/dist/domain/wasm-worker-protocol.d.ts.map +1 -1
- package/dist/extractors/dart.js +48 -3
- package/dist/extractors/dart.js.map +1 -1
- package/dist/extractors/groovy.js +62 -3
- package/dist/extractors/groovy.js.map +1 -1
- package/dist/extractors/helpers.d.ts +4 -2
- package/dist/extractors/helpers.d.ts.map +1 -1
- package/dist/extractors/helpers.js +5 -1
- package/dist/extractors/helpers.js.map +1 -1
- package/dist/extractors/java.js +77 -1
- package/dist/extractors/java.js.map +1 -1
- package/dist/extractors/javascript.d.ts.map +1 -1
- package/dist/extractors/javascript.js +549 -163
- package/dist/extractors/javascript.js.map +1 -1
- package/dist/extractors/kotlin.js +58 -3
- package/dist/extractors/kotlin.js.map +1 -1
- package/dist/extractors/objc.js +25 -2
- package/dist/extractors/objc.js.map +1 -1
- package/dist/extractors/scala.js +62 -2
- package/dist/extractors/scala.js.map +1 -1
- package/dist/extractors/swift.js +52 -3
- package/dist/extractors/swift.js.map +1 -1
- package/dist/features/audit.js +26 -23
- package/dist/features/audit.js.map +1 -1
- package/dist/features/boundaries.d.ts.map +1 -1
- package/dist/features/boundaries.js +12 -9
- package/dist/features/boundaries.js.map +1 -1
- package/dist/features/cfg.d.ts.map +1 -1
- package/dist/features/cfg.js +25 -18
- package/dist/features/cfg.js.map +1 -1
- package/dist/features/check.d.ts.map +1 -1
- package/dist/features/check.js +18 -5
- package/dist/features/check.js.map +1 -1
- package/dist/features/communities.d.ts +4 -2
- package/dist/features/communities.d.ts.map +1 -1
- package/dist/features/communities.js +6 -4
- package/dist/features/communities.js.map +1 -1
- package/dist/features/dataflow.d.ts +60 -0
- package/dist/features/dataflow.d.ts.map +1 -1
- package/dist/features/dataflow.js +530 -6
- package/dist/features/dataflow.js.map +1 -1
- package/dist/features/manifesto.d.ts.map +1 -1
- package/dist/features/manifesto.js +59 -72
- package/dist/features/manifesto.js.map +1 -1
- package/dist/features/sequence.d.ts.map +1 -1
- package/dist/features/sequence.js +27 -22
- package/dist/features/sequence.js.map +1 -1
- package/dist/features/snapshot.d.ts.map +1 -1
- package/dist/features/snapshot.js +36 -28
- package/dist/features/snapshot.js.map +1 -1
- package/dist/features/structure.d.ts.map +1 -1
- package/dist/features/structure.js +150 -62
- package/dist/features/structure.js.map +1 -1
- package/dist/features/triage.d.ts.map +1 -1
- package/dist/features/triage.js +18 -11
- package/dist/features/triage.js.map +1 -1
- package/dist/graph/algorithms/bfs.d.ts +1 -1
- package/dist/graph/algorithms/bfs.d.ts.map +1 -1
- package/dist/graph/algorithms/bfs.js +14 -13
- package/dist/graph/algorithms/bfs.js.map +1 -1
- package/dist/graph/algorithms/tarjan.d.ts.map +1 -1
- package/dist/graph/algorithms/tarjan.js +5 -0
- package/dist/graph/algorithms/tarjan.js.map +1 -1
- package/dist/graph/builders/dependency.js +28 -22
- package/dist/graph/builders/dependency.js.map +1 -1
- package/dist/graph/classifiers/roles.d.ts +10 -1
- package/dist/graph/classifiers/roles.d.ts.map +1 -1
- package/dist/graph/classifiers/roles.js +60 -6
- package/dist/graph/classifiers/roles.js.map +1 -1
- package/dist/infrastructure/config.d.ts +10 -0
- package/dist/infrastructure/config.d.ts.map +1 -1
- package/dist/infrastructure/config.js +31 -3
- package/dist/infrastructure/config.js.map +1 -1
- package/dist/infrastructure/registry.d.ts +0 -7
- package/dist/infrastructure/registry.d.ts.map +1 -1
- package/dist/infrastructure/registry.js +29 -13
- package/dist/infrastructure/registry.js.map +1 -1
- package/dist/infrastructure/update-check.d.ts.map +1 -1
- package/dist/infrastructure/update-check.js +49 -31
- package/dist/infrastructure/update-check.js.map +1 -1
- package/dist/mcp/server.d.ts +2 -10
- package/dist/mcp/server.d.ts.map +1 -1
- package/dist/mcp/server.js.map +1 -1
- package/dist/mcp/tools/ast-query.d.ts +1 -1
- package/dist/mcp/tools/ast-query.d.ts.map +1 -1
- package/dist/mcp/tools/audit.d.ts +1 -1
- package/dist/mcp/tools/audit.d.ts.map +1 -1
- package/dist/mcp/tools/batch-query.d.ts +1 -1
- package/dist/mcp/tools/batch-query.d.ts.map +1 -1
- package/dist/mcp/tools/branch-compare.d.ts +1 -1
- package/dist/mcp/tools/branch-compare.d.ts.map +1 -1
- package/dist/mcp/tools/brief.d.ts +1 -1
- package/dist/mcp/tools/brief.d.ts.map +1 -1
- package/dist/mcp/tools/cfg.d.ts +1 -1
- package/dist/mcp/tools/cfg.d.ts.map +1 -1
- package/dist/mcp/tools/check.d.ts +1 -1
- package/dist/mcp/tools/check.d.ts.map +1 -1
- package/dist/mcp/tools/co-changes.d.ts +1 -1
- package/dist/mcp/tools/co-changes.d.ts.map +1 -1
- package/dist/mcp/tools/code-owners.d.ts +1 -1
- package/dist/mcp/tools/code-owners.d.ts.map +1 -1
- package/dist/mcp/tools/communities.d.ts +1 -1
- package/dist/mcp/tools/communities.d.ts.map +1 -1
- package/dist/mcp/tools/complexity.d.ts +1 -1
- package/dist/mcp/tools/complexity.d.ts.map +1 -1
- package/dist/mcp/tools/context.d.ts +1 -1
- package/dist/mcp/tools/context.d.ts.map +1 -1
- package/dist/mcp/tools/dataflow.d.ts +1 -1
- package/dist/mcp/tools/dataflow.d.ts.map +1 -1
- package/dist/mcp/tools/diff-impact.d.ts +1 -1
- package/dist/mcp/tools/diff-impact.d.ts.map +1 -1
- package/dist/mcp/tools/execution-flow.d.ts +1 -1
- package/dist/mcp/tools/execution-flow.d.ts.map +1 -1
- package/dist/mcp/tools/export-graph.d.ts +1 -1
- package/dist/mcp/tools/export-graph.d.ts.map +1 -1
- package/dist/mcp/tools/file-deps.d.ts +1 -1
- package/dist/mcp/tools/file-deps.d.ts.map +1 -1
- package/dist/mcp/tools/file-exports.d.ts +1 -1
- package/dist/mcp/tools/file-exports.d.ts.map +1 -1
- package/dist/mcp/tools/find-cycles.d.ts +1 -1
- package/dist/mcp/tools/find-cycles.d.ts.map +1 -1
- package/dist/mcp/tools/fn-impact.d.ts +1 -1
- package/dist/mcp/tools/fn-impact.d.ts.map +1 -1
- package/dist/mcp/tools/impact-analysis.d.ts +1 -1
- package/dist/mcp/tools/impact-analysis.d.ts.map +1 -1
- package/dist/mcp/tools/implementations.d.ts +1 -1
- package/dist/mcp/tools/implementations.d.ts.map +1 -1
- package/dist/mcp/tools/index.d.ts +2 -5
- package/dist/mcp/tools/index.d.ts.map +1 -1
- package/dist/mcp/tools/index.js.map +1 -1
- package/dist/mcp/tools/interfaces.d.ts +1 -1
- package/dist/mcp/tools/interfaces.d.ts.map +1 -1
- package/dist/mcp/tools/list-functions.d.ts +1 -1
- package/dist/mcp/tools/list-functions.d.ts.map +1 -1
- package/dist/mcp/tools/list-repos.d.ts +1 -1
- package/dist/mcp/tools/list-repos.d.ts.map +1 -1
- package/dist/mcp/tools/module-map.d.ts +1 -1
- package/dist/mcp/tools/module-map.d.ts.map +1 -1
- package/dist/mcp/tools/node-roles.d.ts +1 -1
- package/dist/mcp/tools/node-roles.d.ts.map +1 -1
- package/dist/mcp/tools/path.d.ts +1 -1
- package/dist/mcp/tools/path.d.ts.map +1 -1
- package/dist/mcp/tools/query.d.ts +1 -1
- package/dist/mcp/tools/query.d.ts.map +1 -1
- package/dist/mcp/tools/semantic-search.d.ts +1 -1
- package/dist/mcp/tools/semantic-search.d.ts.map +1 -1
- package/dist/mcp/tools/sequence.d.ts +1 -1
- package/dist/mcp/tools/sequence.d.ts.map +1 -1
- package/dist/mcp/tools/structure.d.ts +1 -1
- package/dist/mcp/tools/structure.d.ts.map +1 -1
- package/dist/mcp/tools/symbol-children.d.ts +1 -1
- package/dist/mcp/tools/symbol-children.d.ts.map +1 -1
- package/dist/mcp/tools/triage.d.ts +1 -1
- package/dist/mcp/tools/triage.d.ts.map +1 -1
- package/dist/mcp/tools/where.d.ts +1 -1
- package/dist/mcp/tools/where.d.ts.map +1 -1
- package/dist/mcp/types.d.ts +19 -0
- package/dist/mcp/types.d.ts.map +1 -0
- package/dist/mcp/types.js +6 -0
- package/dist/mcp/types.js.map +1 -0
- package/dist/presentation/queries-cli/index.d.ts +1 -1
- package/dist/presentation/queries-cli/index.d.ts.map +1 -1
- package/dist/presentation/queries-cli/index.js +1 -1
- package/dist/presentation/queries-cli/index.js.map +1 -1
- package/dist/presentation/queries-cli/overview.d.ts +1 -0
- package/dist/presentation/queries-cli/overview.d.ts.map +1 -1
- package/dist/presentation/queries-cli/overview.js +20 -1
- package/dist/presentation/queries-cli/overview.js.map +1 -1
- package/dist/presentation/queries-cli.d.ts +1 -1
- package/dist/presentation/queries-cli.d.ts.map +1 -1
- package/dist/presentation/queries-cli.js +1 -1
- package/dist/presentation/queries-cli.js.map +1 -1
- package/dist/presentation/viewer.d.ts.map +1 -1
- package/dist/presentation/viewer.js +45 -32
- package/dist/presentation/viewer.js.map +1 -1
- package/dist/shared/constants.d.ts +21 -0
- package/dist/shared/constants.d.ts.map +1 -1
- package/dist/shared/constants.js +25 -0
- package/dist/shared/constants.js.map +1 -1
- package/dist/shared/normalize.d.ts.map +1 -1
- package/dist/shared/normalize.js +12 -22
- package/dist/shared/normalize.js.map +1 -1
- package/dist/shared/paginate.d.ts +4 -17
- package/dist/shared/paginate.d.ts.map +1 -1
- package/dist/shared/paginate.js.map +1 -1
- package/dist/types.d.ts +76 -1
- package/dist/types.d.ts.map +1 -1
- package/grammars/tree-sitter-erlang.wasm +0 -0
- package/package.json +7 -7
- package/src/ast-analysis/engine.ts +43 -63
- package/src/ast-analysis/rules/b2.ts +263 -0
- package/src/ast-analysis/rules/b3.ts +127 -0
- package/src/ast-analysis/rules/b4.ts +378 -0
- package/src/ast-analysis/rules/b5.ts +65 -0
- package/src/ast-analysis/rules/c.ts +157 -0
- package/src/ast-analysis/rules/index.ts +34 -0
- package/src/ast-analysis/rules/javascript.ts +3 -0
- package/src/ast-analysis/shared.ts +2 -0
- package/src/ast-analysis/visitor-utils.ts +5 -0
- package/src/ast-analysis/visitor.ts +82 -52
- package/src/ast-analysis/visitors/cfg-visitor.ts +198 -84
- package/src/ast-analysis/visitors/complexity-visitor.ts +44 -16
- package/src/ast-analysis/visitors/dataflow-visitor.ts +68 -29
- package/src/cli/commands/config.ts +184 -184
- package/src/cli/commands/roles.ts +6 -1
- package/src/db/better-sqlite3.ts +5 -4
- package/src/db/connection.ts +23 -5
- package/src/db/index.ts +1 -0
- package/src/db/migrations.ts +68 -0
- package/src/db/repository/build-stmts.ts +30 -0
- package/src/db/repository/dataflow.ts +16 -0
- package/src/db/repository/index.ts +1 -1
- package/src/db/repository/native-repository.ts +56 -40
- package/src/domain/analysis/fn-impact.ts +4 -0
- package/src/domain/analysis/module-map.ts +38 -6
- package/src/domain/analysis/roles.ts +23 -0
- package/src/domain/graph/builder/call-resolver.ts +112 -232
- package/src/domain/graph/builder/context.ts +1 -0
- package/src/domain/graph/builder/helpers.ts +190 -72
- package/src/domain/graph/builder/incremental.ts +249 -120
- package/src/domain/graph/builder/pipeline.ts +11 -5
- package/src/domain/graph/builder/stages/build-edges.ts +696 -296
- package/src/domain/graph/builder/stages/collect-files.ts +12 -6
- package/src/domain/graph/builder/stages/detect-changes.ts +3 -1
- package/src/domain/graph/builder/stages/native-orchestrator.ts +1102 -590
- package/src/domain/graph/resolver/points-to.ts +182 -59
- package/src/domain/graph/resolver/strategy.ts +265 -0
- package/src/domain/graph/watcher.ts +19 -9
- package/src/domain/parser.ts +12 -2
- package/src/domain/queries.ts +1 -1
- package/src/domain/wasm-worker-entry.ts +3 -0
- package/src/domain/wasm-worker-pool.ts +28 -4
- package/src/domain/wasm-worker-protocol.ts +4 -0
- package/src/extractors/dart.ts +48 -3
- package/src/extractors/groovy.ts +62 -2
- package/src/extractors/helpers.ts +5 -2
- package/src/extractors/java.ts +80 -1
- package/src/extractors/javascript.ts +566 -161
- package/src/extractors/kotlin.ts +57 -3
- package/src/extractors/objc.ts +25 -1
- package/src/extractors/scala.ts +63 -1
- package/src/extractors/swift.ts +46 -3
- package/src/features/audit.ts +43 -34
- package/src/features/boundaries.ts +17 -9
- package/src/features/cfg.ts +31 -22
- package/src/features/check.ts +21 -5
- package/src/features/communities.ts +28 -19
- package/src/features/dataflow.ts +755 -6
- package/src/features/manifesto.ts +76 -75
- package/src/features/sequence.ts +29 -23
- package/src/features/snapshot.ts +36 -25
- package/src/features/structure.ts +185 -55
- package/src/features/triage.ts +28 -15
- package/src/graph/algorithms/bfs.ts +13 -12
- package/src/graph/algorithms/tarjan.ts +5 -0
- package/src/graph/builders/dependency.ts +35 -23
- package/src/graph/classifiers/roles.ts +74 -7
- package/src/infrastructure/config.ts +32 -3
- package/src/infrastructure/registry.ts +44 -20
- package/src/infrastructure/update-check.ts +55 -33
- package/src/mcp/server.ts +2 -8
- package/src/mcp/tools/ast-query.ts +1 -1
- package/src/mcp/tools/audit.ts +1 -1
- package/src/mcp/tools/batch-query.ts +1 -1
- package/src/mcp/tools/branch-compare.ts +1 -1
- package/src/mcp/tools/brief.ts +1 -1
- package/src/mcp/tools/cfg.ts +1 -1
- package/src/mcp/tools/check.ts +1 -1
- package/src/mcp/tools/co-changes.ts +1 -1
- package/src/mcp/tools/code-owners.ts +1 -1
- package/src/mcp/tools/communities.ts +1 -1
- package/src/mcp/tools/complexity.ts +1 -1
- package/src/mcp/tools/context.ts +1 -1
- package/src/mcp/tools/dataflow.ts +1 -1
- package/src/mcp/tools/diff-impact.ts +1 -1
- package/src/mcp/tools/execution-flow.ts +1 -1
- package/src/mcp/tools/export-graph.ts +1 -1
- package/src/mcp/tools/file-deps.ts +1 -1
- package/src/mcp/tools/file-exports.ts +1 -1
- package/src/mcp/tools/find-cycles.ts +1 -1
- package/src/mcp/tools/fn-impact.ts +1 -1
- package/src/mcp/tools/impact-analysis.ts +1 -1
- package/src/mcp/tools/implementations.ts +1 -1
- package/src/mcp/tools/index.ts +2 -5
- package/src/mcp/tools/interfaces.ts +1 -1
- package/src/mcp/tools/list-functions.ts +1 -1
- package/src/mcp/tools/list-repos.ts +1 -1
- package/src/mcp/tools/module-map.ts +1 -1
- package/src/mcp/tools/node-roles.ts +1 -1
- package/src/mcp/tools/path.ts +1 -1
- package/src/mcp/tools/query.ts +1 -1
- package/src/mcp/tools/semantic-search.ts +1 -1
- package/src/mcp/tools/sequence.ts +1 -1
- package/src/mcp/tools/structure.ts +1 -1
- package/src/mcp/tools/symbol-children.ts +1 -1
- package/src/mcp/tools/triage.ts +1 -1
- package/src/mcp/tools/where.ts +1 -1
- package/src/mcp/types.ts +21 -0
- package/src/presentation/queries-cli/index.ts +1 -1
- package/src/presentation/queries-cli/overview.ts +35 -1
- package/src/presentation/queries-cli.ts +1 -0
- package/src/presentation/viewer.ts +98 -87
- package/src/shared/constants.ts +26 -0
- package/src/shared/normalize.ts +13 -22
- package/src/shared/paginate.ts +4 -18
- package/src/types.ts +86 -1
|
@@ -64,6 +64,13 @@ const BUILTIN_GLOBALS = new Set([
|
|
|
64
64
|
const MAX_PROPAGATION_DEPTH = 3;
|
|
65
65
|
/** Confidence penalty applied per propagation hop (1.0 → 0.9 → 0.8 → 0.7). */
|
|
66
66
|
export const PROPAGATION_HOP_PENALTY = 0.1;
|
|
67
|
+
/**
|
|
68
|
+
* Confidence score for a return type inferred from `return new Constructor()` with no
|
|
69
|
+
* explicit TypeScript annotation. Registered as `analysis.typeInferenceConfidence` in
|
|
70
|
+
* `src/infrastructure/config.ts` DEFAULTS — kept in sync manually until config is
|
|
71
|
+
* threaded through to `extractSymbols`.
|
|
72
|
+
*/
|
|
73
|
+
const INFERRED_RETURN_TYPE_CONFIDENCE = 0.85;
|
|
67
74
|
/**
|
|
68
75
|
* Extract symbols from a JS/TS parsed AST.
|
|
69
76
|
* When a compiled tree-sitter Query is provided (from parser.js),
|
|
@@ -252,13 +259,16 @@ function dispatchQueryMatch(c, definitions, calls, imports, classes, exps) {
|
|
|
252
259
|
handleExportCapture(c, exps, imports);
|
|
253
260
|
}
|
|
254
261
|
else if (c.callfn_node) {
|
|
255
|
-
calls.
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
262
|
+
// Route through extractCallInfo so special identifier calls (eval) get classified.
|
|
263
|
+
const callfnInfo = extractCallInfo(c.callfn_name, c.callfn_node);
|
|
264
|
+
if (callfnInfo)
|
|
265
|
+
calls.push(callfnInfo);
|
|
259
266
|
calls.push(...extractCallbackReferenceCalls(c.callfn_node));
|
|
260
267
|
}
|
|
261
268
|
else if (c.callmem_node) {
|
|
269
|
+
// extractCallInfo → extractMemberExprCallInfo applies the plain-identifier guard for
|
|
270
|
+
// .call/.apply/.bind: when the object is a bare identifier (e.g. `fn.call(ctx)`),
|
|
271
|
+
// the call is emitted as static (no dynamic flag), matching the walk path and native engine.
|
|
262
272
|
const callInfo = extractCallInfo(c.callmem_fn, c.callmem_node);
|
|
263
273
|
if (callInfo)
|
|
264
274
|
calls.push(callInfo);
|
|
@@ -274,10 +284,21 @@ function dispatchQueryMatch(c, definitions, calls, imports, classes, exps) {
|
|
|
274
284
|
calls.push(...extractCallbackReferenceCalls(c.callsub_node));
|
|
275
285
|
}
|
|
276
286
|
else if (c.newfn_node) {
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
287
|
+
if (c.newfn_name.text === 'Function') {
|
|
288
|
+
// new Function(body) — dynamic code execution; classify as eval kind
|
|
289
|
+
calls.push({
|
|
290
|
+
name: '<dynamic:eval>',
|
|
291
|
+
line: nodeStartLine(c.newfn_node),
|
|
292
|
+
dynamic: true,
|
|
293
|
+
dynamicKind: 'eval',
|
|
294
|
+
});
|
|
295
|
+
}
|
|
296
|
+
else {
|
|
297
|
+
calls.push({
|
|
298
|
+
name: c.newfn_name.text,
|
|
299
|
+
line: nodeStartLine(c.newfn_node),
|
|
300
|
+
});
|
|
301
|
+
}
|
|
281
302
|
}
|
|
282
303
|
else if (c.newmem_node) {
|
|
283
304
|
const callInfo = extractCallInfo(c.newmem_fn, c.newmem_node);
|
|
@@ -333,8 +354,11 @@ function extractSymbolsQuery(tree, query) {
|
|
|
333
354
|
forOfBindings,
|
|
334
355
|
arrayCallbackBindings,
|
|
335
356
|
});
|
|
336
|
-
// Extract definitions from destructured bindings (query patterns don't match object_pattern)
|
|
337
|
-
|
|
357
|
+
// Extract definitions from destructured bindings (query patterns don't match object_pattern).
|
|
358
|
+
// Also collects CJS require bindings (const { X } = require('…')) into a separate list so
|
|
359
|
+
// importedNames can classify them as import artifacts without creating DB edges (#1661).
|
|
360
|
+
const cjsRequireBindings = [];
|
|
361
|
+
extractDestructuredBindingsWalk(tree.rootNode, definitions, cjsRequireBindings);
|
|
338
362
|
// Everything without bespoke traversal semantics is collected in ONE pass:
|
|
339
363
|
// dynamic import() calls, prototype-method definitions, param bindings,
|
|
340
364
|
// array-element bindings, object-prop bindings, `new X()` names,
|
|
@@ -375,6 +399,7 @@ function extractSymbolsQuery(tree, query) {
|
|
|
375
399
|
thisCallBindings,
|
|
376
400
|
newExpressions,
|
|
377
401
|
...(definePropertyReceivers.size > 0 ? { definePropertyReceivers } : {}),
|
|
402
|
+
...(cjsRequireBindings.length > 0 ? { cjsRequireBindings } : {}),
|
|
378
403
|
};
|
|
379
404
|
}
|
|
380
405
|
/** Node types that define a function scope — constants inside these are skipped. */
|
|
@@ -421,6 +446,7 @@ function extractConstantsWalk(node, definitions) {
|
|
|
421
446
|
declNode = inner;
|
|
422
447
|
}
|
|
423
448
|
extractConstDeclarators(declNode, definitions);
|
|
449
|
+
extractLetVarObjLiteralDeclarators(declNode, definitions);
|
|
424
450
|
// Recurse into non-function, non-export-statement children (blocks, if-statements, etc.)
|
|
425
451
|
if (child.type !== 'export_statement') {
|
|
426
452
|
extractConstantsWalk(child, definitions);
|
|
@@ -434,8 +460,11 @@ function extractConstantsWalk(node, definitions) {
|
|
|
434
460
|
/**
|
|
435
461
|
* Walk the AST to find destructured const bindings (query patterns don't match object_pattern).
|
|
436
462
|
* e.g. `const { handleToken, checkPermissions } = initAuth(config)`
|
|
463
|
+
*
|
|
464
|
+
* When `cjsRequireBindings` is provided, also records `const { X } = require('./path')` patterns
|
|
465
|
+
* so the edge builder can classify X as an import artifact rather than a local definition (#1661).
|
|
437
466
|
*/
|
|
438
|
-
function extractDestructuredBindingsWalk(node, definitions) {
|
|
467
|
+
function extractDestructuredBindingsWalk(node, definitions, cjsRequireBindings) {
|
|
439
468
|
for (let i = 0; i < node.childCount; i++) {
|
|
440
469
|
const child = node.child(i);
|
|
441
470
|
if (!child)
|
|
@@ -458,6 +487,41 @@ function extractDestructuredBindingsWalk(node, definitions) {
|
|
|
458
487
|
const nameN = declarator.childForFieldName('name');
|
|
459
488
|
if (nameN && nameN.type === 'object_pattern') {
|
|
460
489
|
extractDestructuredBindings(nameN, nodeStartLine(declNode), nodeEndLine(declNode), definitions);
|
|
490
|
+
// Record CJS require bindings so importedNames can classify these names
|
|
491
|
+
// as import artifacts, preventing false local-definition blocking (#1661).
|
|
492
|
+
if (cjsRequireBindings) {
|
|
493
|
+
const valueN = declarator.childForFieldName('value');
|
|
494
|
+
if (valueN?.type === 'call_expression') {
|
|
495
|
+
const fn = valueN.childForFieldName('function');
|
|
496
|
+
if (fn?.text === 'require') {
|
|
497
|
+
const args = valueN.childForFieldName('arguments');
|
|
498
|
+
const strArg = args && findChild(args, 'string');
|
|
499
|
+
if (strArg) {
|
|
500
|
+
const modPath = strArg.text.replace(/['"]/g, '');
|
|
501
|
+
const names = [];
|
|
502
|
+
for (let k = 0; k < nameN.childCount; k++) {
|
|
503
|
+
const prop = nameN.child(k);
|
|
504
|
+
if (!prop)
|
|
505
|
+
continue;
|
|
506
|
+
if (prop.type === 'shorthand_property_identifier_pattern' ||
|
|
507
|
+
prop.type === 'shorthand_property_identifier') {
|
|
508
|
+
names.push(prop.text);
|
|
509
|
+
}
|
|
510
|
+
else if (prop.type === 'pair_pattern' || prop.type === 'pair') {
|
|
511
|
+
const val = prop.childForFieldName('value');
|
|
512
|
+
if (val?.type === 'identifier' ||
|
|
513
|
+
val?.type === 'shorthand_property_identifier_pattern') {
|
|
514
|
+
names.push(val.text);
|
|
515
|
+
}
|
|
516
|
+
}
|
|
517
|
+
}
|
|
518
|
+
if (names.length > 0) {
|
|
519
|
+
cjsRequireBindings.push({ names, source: modPath });
|
|
520
|
+
}
|
|
521
|
+
}
|
|
522
|
+
}
|
|
523
|
+
}
|
|
524
|
+
}
|
|
461
525
|
}
|
|
462
526
|
else if (nameN && nameN.type === 'array_pattern') {
|
|
463
527
|
// `const [x, y] = ...` — emit a single constant node whose name is the
|
|
@@ -472,7 +536,7 @@ function extractDestructuredBindingsWalk(node, definitions) {
|
|
|
472
536
|
}
|
|
473
537
|
}
|
|
474
538
|
if (child.type !== 'export_statement') {
|
|
475
|
-
extractDestructuredBindingsWalk(child, definitions);
|
|
539
|
+
extractDestructuredBindingsWalk(child, definitions, cjsRequireBindings);
|
|
476
540
|
}
|
|
477
541
|
}
|
|
478
542
|
}
|
|
@@ -516,6 +580,33 @@ function extractConstDeclarators(declNode, definitions) {
|
|
|
516
580
|
}
|
|
517
581
|
}
|
|
518
582
|
}
|
|
583
|
+
/**
|
|
584
|
+
* Extract qualified method definitions from `let`/`var` object-literal declarations.
|
|
585
|
+
* Mirrors `match_js_objlit_qualified_method_defs` in `javascript.rs`, which emits
|
|
586
|
+
* qualified definitions for `method_definition` (all declaration kinds) and
|
|
587
|
+
* `pair+arrow/function` (`let`/`var` only, since `const` is already handled by
|
|
588
|
+
* `extractConstDeclarators` → `extractObjectLiteralFunctions`).
|
|
589
|
+
*
|
|
590
|
+
* Called from extractConstantsWalk which already provides the function-scope guard.
|
|
591
|
+
* `var q1 = { m1() {} }` → emits Definition { name: 'q1.m1', kind: 'function' }
|
|
592
|
+
*/
|
|
593
|
+
function extractLetVarObjLiteralDeclarators(declNode, definitions) {
|
|
594
|
+
const t = declNode.type;
|
|
595
|
+
if (t !== 'lexical_declaration' && t !== 'variable_declaration')
|
|
596
|
+
return;
|
|
597
|
+
if (declNode.text.startsWith('const '))
|
|
598
|
+
return; // handled by extractConstDeclarators
|
|
599
|
+
for (let j = 0; j < declNode.childCount; j++) {
|
|
600
|
+
const declarator = declNode.child(j);
|
|
601
|
+
if (declarator?.type !== 'variable_declarator')
|
|
602
|
+
continue;
|
|
603
|
+
const nameN = declarator.childForFieldName('name');
|
|
604
|
+
const valueN = declarator.childForFieldName('value');
|
|
605
|
+
if (nameN?.type !== 'identifier' || !valueN || valueN.type !== 'object')
|
|
606
|
+
continue;
|
|
607
|
+
extractObjectLiteralFunctions(valueN, nameN.text, definitions);
|
|
608
|
+
}
|
|
609
|
+
}
|
|
519
610
|
/**
|
|
520
611
|
* Recursive walk to find dynamic import() calls.
|
|
521
612
|
* Query patterns match call_expression with identifier/member_expression/subscript_expression
|
|
@@ -692,6 +783,9 @@ function walkJavaScriptNode(node, ctx) {
|
|
|
692
783
|
case 'enum_declaration':
|
|
693
784
|
handleEnumDecl(node, ctx);
|
|
694
785
|
break;
|
|
786
|
+
case 'decorator':
|
|
787
|
+
handleDecorator(node, ctx.calls);
|
|
788
|
+
break;
|
|
695
789
|
case 'call_expression':
|
|
696
790
|
handleCallExpr(node, ctx);
|
|
697
791
|
break;
|
|
@@ -945,6 +1039,17 @@ function handleVariableDecl(node, ctx) {
|
|
|
945
1039
|
extractObjectLiteralFunctions(valueN, nameN.text, ctx.definitions);
|
|
946
1040
|
}
|
|
947
1041
|
}
|
|
1042
|
+
else if (!isConst &&
|
|
1043
|
+
nameN.type === 'identifier' &&
|
|
1044
|
+
valueN.type === 'object' &&
|
|
1045
|
+
!hasFunctionScopeAncestor(node)) {
|
|
1046
|
+
// `let`/`var` object literals: extract qualified method definitions so that
|
|
1047
|
+
// `obj.method()` calls resolve correctly. Mirrors Rust match_js_objlit_qualified_method_defs
|
|
1048
|
+
// which emits method_definition qualified names for ALL declaration kinds and
|
|
1049
|
+
// pair+arrow/function for let/var only (const is already handled above).
|
|
1050
|
+
// Scope guard prevents local object properties from polluting the global index.
|
|
1051
|
+
extractObjectLiteralFunctions(valueN, nameN.text, ctx.definitions);
|
|
1052
|
+
}
|
|
948
1053
|
else if (isConst && nameN.type === 'object_pattern' && !hasFunctionScopeAncestor(node)) {
|
|
949
1054
|
// Destructured bindings: const { handleToken, checkPermissions } = initAuth(...)
|
|
950
1055
|
// Each destructured property becomes a function definition so it can be
|
|
@@ -954,6 +1059,39 @@ function handleVariableDecl(node, ctx) {
|
|
|
954
1059
|
// Scope guard mirrors extractDestructuredBindingsWalk (query path) and
|
|
955
1060
|
// handle_var_decl (Rust path) — skips bindings inside function bodies.
|
|
956
1061
|
extractDestructuredBindings(nameN, nodeStartLine(node), nodeEndLine(node), ctx.definitions);
|
|
1062
|
+
// Record CJS require bindings for import-artifact classification (#1661).
|
|
1063
|
+
if (valueN?.type === 'call_expression') {
|
|
1064
|
+
const fn = valueN.childForFieldName('function');
|
|
1065
|
+
if (fn?.text === 'require') {
|
|
1066
|
+
const args = valueN.childForFieldName('arguments');
|
|
1067
|
+
const strArg = args && findChild(args, 'string');
|
|
1068
|
+
if (strArg) {
|
|
1069
|
+
const modPath = strArg.text.replace(/['"]/g, '');
|
|
1070
|
+
const names = [];
|
|
1071
|
+
for (let k = 0; k < nameN.childCount; k++) {
|
|
1072
|
+
const prop = nameN.child(k);
|
|
1073
|
+
if (!prop)
|
|
1074
|
+
continue;
|
|
1075
|
+
if (prop.type === 'shorthand_property_identifier_pattern' ||
|
|
1076
|
+
prop.type === 'shorthand_property_identifier') {
|
|
1077
|
+
names.push(prop.text);
|
|
1078
|
+
}
|
|
1079
|
+
else if (prop.type === 'pair_pattern' || prop.type === 'pair') {
|
|
1080
|
+
const val = prop.childForFieldName('value');
|
|
1081
|
+
if (val?.type === 'identifier' ||
|
|
1082
|
+
val?.type === 'shorthand_property_identifier_pattern') {
|
|
1083
|
+
names.push(val.text);
|
|
1084
|
+
}
|
|
1085
|
+
}
|
|
1086
|
+
}
|
|
1087
|
+
if (names.length > 0) {
|
|
1088
|
+
if (!ctx.cjsRequireBindings)
|
|
1089
|
+
ctx.cjsRequireBindings = [];
|
|
1090
|
+
ctx.cjsRequireBindings.push({ names, source: modPath });
|
|
1091
|
+
}
|
|
1092
|
+
}
|
|
1093
|
+
}
|
|
1094
|
+
}
|
|
957
1095
|
}
|
|
958
1096
|
else if (isConst && nameN.type === 'array_pattern' && !hasFunctionScopeAncestor(node)) {
|
|
959
1097
|
// Array destructuring: `const [x, y] = ...` — emit a single constant node
|
|
@@ -1119,7 +1257,18 @@ function handleNewExpr(node, ctx) {
|
|
|
1119
1257
|
if (!ctor)
|
|
1120
1258
|
return;
|
|
1121
1259
|
if (ctor.type === 'identifier') {
|
|
1122
|
-
|
|
1260
|
+
if (ctor.text === 'Function') {
|
|
1261
|
+
// new Function(body) — dynamic code execution; undecidable static target
|
|
1262
|
+
ctx.calls.push({
|
|
1263
|
+
name: '<dynamic:eval>',
|
|
1264
|
+
line: nodeStartLine(node),
|
|
1265
|
+
dynamic: true,
|
|
1266
|
+
dynamicKind: 'eval',
|
|
1267
|
+
});
|
|
1268
|
+
}
|
|
1269
|
+
else {
|
|
1270
|
+
ctx.calls.push({ name: ctor.text, line: nodeStartLine(node) });
|
|
1271
|
+
}
|
|
1123
1272
|
}
|
|
1124
1273
|
else if (ctor.type === 'member_expression') {
|
|
1125
1274
|
const callInfo = extractCallInfo(ctor, node);
|
|
@@ -1127,6 +1276,39 @@ function handleNewExpr(node, ctx) {
|
|
|
1127
1276
|
ctx.calls.push(callInfo);
|
|
1128
1277
|
}
|
|
1129
1278
|
}
|
|
1279
|
+
/**
|
|
1280
|
+
* Handle a TypeScript/JS decorator node.
|
|
1281
|
+
*
|
|
1282
|
+
* Only handles bare-identifier and bare-member-expression decorators
|
|
1283
|
+
* (`@Foo`, `@Foo.bar`) since decorated call expressions (`@Foo()`, `@Foo.bar()`)
|
|
1284
|
+
* are already visited as `call_expression` children by the recursive walker.
|
|
1285
|
+
*/
|
|
1286
|
+
function handleDecorator(node, calls) {
|
|
1287
|
+
// Decorators wrap their expression; find the first non-@ child
|
|
1288
|
+
for (let i = 0; i < node.childCount; i++) {
|
|
1289
|
+
const child = node.child(i);
|
|
1290
|
+
if (!child || child.type === '@')
|
|
1291
|
+
continue;
|
|
1292
|
+
const t = child.type;
|
|
1293
|
+
if (t === 'identifier') {
|
|
1294
|
+
// @Foo — the identifier is the decorator factory; emit as reflection call
|
|
1295
|
+
calls.push({
|
|
1296
|
+
name: child.text,
|
|
1297
|
+
line: nodeStartLine(node),
|
|
1298
|
+
dynamic: true,
|
|
1299
|
+
dynamicKind: 'reflection',
|
|
1300
|
+
});
|
|
1301
|
+
}
|
|
1302
|
+
else if (t === 'member_expression') {
|
|
1303
|
+
// @Foo.bar — emit as reflection; always mark dynamic since it's decorator dispatch
|
|
1304
|
+
const callInfo = extractCallInfo(child, node);
|
|
1305
|
+
if (callInfo)
|
|
1306
|
+
calls.push({ ...callInfo, dynamic: true, dynamicKind: 'reflection' });
|
|
1307
|
+
}
|
|
1308
|
+
// call_expression / other — handled by the recursive walker automatically
|
|
1309
|
+
break;
|
|
1310
|
+
}
|
|
1311
|
+
}
|
|
1130
1312
|
/** Handle a dynamic import() call expression and add to imports if static. */
|
|
1131
1313
|
function handleDynamicImportCall(node, imports) {
|
|
1132
1314
|
const args = node.childForFieldName('arguments') || findChild(node, 'arguments');
|
|
@@ -1482,8 +1664,8 @@ function storeReturnType(fnNode, fnName, returnTypeMap) {
|
|
|
1482
1664
|
const inferred = findReturnNewExprType(body);
|
|
1483
1665
|
if (inferred) {
|
|
1484
1666
|
const existing = returnTypeMap.get(fnName);
|
|
1485
|
-
if (!existing ||
|
|
1486
|
-
returnTypeMap.set(fnName, { type: inferred, confidence:
|
|
1667
|
+
if (!existing || INFERRED_RETURN_TYPE_CONFIDENCE > existing.confidence)
|
|
1668
|
+
returnTypeMap.set(fnName, { type: inferred, confidence: INFERRED_RETURN_TYPE_CONFIDENCE });
|
|
1487
1669
|
}
|
|
1488
1670
|
}
|
|
1489
1671
|
}
|
|
@@ -1824,54 +2006,186 @@ function runContextCollectorWalk(rootNode, out) {
|
|
|
1824
2006
|
};
|
|
1825
2007
|
walk(rootNode, 0, null, null);
|
|
1826
2008
|
}
|
|
1827
|
-
/**
|
|
1828
|
-
function
|
|
1829
|
-
|
|
1830
|
-
|
|
2009
|
+
/**
|
|
2010
|
+
* Record function-reference bindings from a variable_declarator's value node.
|
|
2011
|
+
*
|
|
2012
|
+
* Captures three patterns (Phase 8.3):
|
|
2013
|
+
* - `const fn = handler` (identifier alias)
|
|
2014
|
+
* - `const fn = obj.method` (member_expression alias)
|
|
2015
|
+
* - `const f = fn.bind(ctx)` (bind creates a bound alias)
|
|
2016
|
+
*
|
|
2017
|
+
* Must be called before any type-analysis early returns so every declarator
|
|
2018
|
+
* contributes to fnRefBindings regardless of whether it has a type annotation.
|
|
2019
|
+
*/
|
|
2020
|
+
function collectFnRefBindings(lhsName, valueN, fnRefBindings) {
|
|
2021
|
+
if (valueN.type === 'identifier' && !BUILTIN_GLOBALS.has(valueN.text)) {
|
|
2022
|
+
fnRefBindings.push({ lhs: lhsName, rhs: valueN.text });
|
|
1831
2023
|
return;
|
|
1832
|
-
|
|
1833
|
-
|
|
1834
|
-
|
|
1835
|
-
|
|
1836
|
-
|
|
1837
|
-
|
|
1838
|
-
|
|
1839
|
-
|
|
1840
|
-
|
|
1841
|
-
|
|
1842
|
-
|
|
1843
|
-
|
|
1844
|
-
|
|
1845
|
-
|
|
1846
|
-
|
|
1847
|
-
|
|
1848
|
-
|
|
1849
|
-
|
|
1850
|
-
|
|
1851
|
-
|
|
2024
|
+
}
|
|
2025
|
+
if (valueN.type === 'member_expression') {
|
|
2026
|
+
const prop = valueN.childForFieldName('property');
|
|
2027
|
+
const obj = valueN.childForFieldName('object');
|
|
2028
|
+
// Guard: only static property access (property_identifier or identifier), not
|
|
2029
|
+
// computed subscript expressions like obj[expr] where prop.text would be the
|
|
2030
|
+
// full expression rather than a simple name — those can never match pts keys.
|
|
2031
|
+
if (prop &&
|
|
2032
|
+
(prop.type === 'property_identifier' || prop.type === 'identifier') &&
|
|
2033
|
+
obj?.type === 'identifier' &&
|
|
2034
|
+
!BUILTIN_GLOBALS.has(obj.text)) {
|
|
2035
|
+
fnRefBindings.push({ lhs: lhsName, rhs: prop.text, rhsReceiver: obj.text });
|
|
2036
|
+
}
|
|
2037
|
+
return;
|
|
2038
|
+
}
|
|
2039
|
+
if (valueN.type === 'call_expression') {
|
|
2040
|
+
// `const f = fn.bind(ctx)` — bind returns a bound copy of fn; track f → fn so
|
|
2041
|
+
// pts(f) ⊇ pts(fn) and subsequent `f(args)` calls resolve to fn.
|
|
2042
|
+
// Note: only flat-identifier binds (fn.bind) are tracked here; method-receiver
|
|
2043
|
+
// binds like `obj.method.bind(ctx)` are not captured (boundFn must be an identifier).
|
|
2044
|
+
const callFn = valueN.childForFieldName('function');
|
|
2045
|
+
if (callFn?.type === 'member_expression') {
|
|
2046
|
+
const bindProp = callFn.childForFieldName('property');
|
|
2047
|
+
if (bindProp?.text === 'bind') {
|
|
2048
|
+
const boundFn = callFn.childForFieldName('object');
|
|
2049
|
+
if (boundFn?.type === 'identifier' && !BUILTIN_GLOBALS.has(boundFn.text)) {
|
|
2050
|
+
fnRefBindings.push({ lhs: lhsName, rhs: boundFn.text });
|
|
2051
|
+
}
|
|
1852
2052
|
}
|
|
1853
2053
|
}
|
|
1854
|
-
|
|
1855
|
-
|
|
1856
|
-
|
|
1857
|
-
|
|
1858
|
-
|
|
1859
|
-
|
|
1860
|
-
|
|
1861
|
-
|
|
1862
|
-
|
|
1863
|
-
|
|
1864
|
-
|
|
1865
|
-
|
|
2054
|
+
}
|
|
2055
|
+
}
|
|
2056
|
+
/**
|
|
2057
|
+
* Handle the `call_expression` branch of variable_declarator type-map seeding.
|
|
2058
|
+
*
|
|
2059
|
+
* Processes three sub-cases in priority order:
|
|
2060
|
+
* 1. Object.create({ ... }) — seeds composite pts keys from the prototype object (Phase 8.3e)
|
|
2061
|
+
* 2. Inter-procedural return-type propagation via returnTypeMap (Phase 8.2)
|
|
2062
|
+
* 3. Factory method heuristic: `const x = Foo.create()` → type Foo at confidence 0.7
|
|
2063
|
+
*/
|
|
2064
|
+
function handleCallExprTypeMap(lhsName, valueN, typeMap, returnTypeMap, callAssignments) {
|
|
2065
|
+
const createFn = valueN.childForFieldName('function');
|
|
2066
|
+
// Phase 8.3e: Object.create({ f1, f2 }) — seed composite pts keys obj.f1 → f1, etc.
|
|
2067
|
+
if (createFn?.type === 'member_expression') {
|
|
2068
|
+
const createObj = createFn.childForFieldName('object');
|
|
2069
|
+
const createProp = createFn.childForFieldName('property');
|
|
2070
|
+
if (createObj?.text === 'Object' && createProp?.text === 'create') {
|
|
2071
|
+
const createArgs = valueN.childForFieldName('arguments') || findChild(valueN, 'arguments');
|
|
2072
|
+
if (createArgs) {
|
|
2073
|
+
let proto = null;
|
|
2074
|
+
for (let i = 0; i < createArgs.childCount; i++) {
|
|
2075
|
+
const n = createArgs.child(i);
|
|
2076
|
+
if (n && n.type !== '(' && n.type !== ')' && n.type !== ',') {
|
|
2077
|
+
proto = n;
|
|
2078
|
+
break;
|
|
1866
2079
|
}
|
|
1867
2080
|
}
|
|
2081
|
+
if (proto?.type === 'object') {
|
|
2082
|
+
seedProtoProperties(lhsName, proto, typeMap);
|
|
2083
|
+
}
|
|
1868
2084
|
}
|
|
2085
|
+
return;
|
|
2086
|
+
}
|
|
2087
|
+
}
|
|
2088
|
+
// Phase 8.2: inter-procedural propagation — try to resolve return type from
|
|
2089
|
+
// the local returnTypeMap before falling back to factory heuristics.
|
|
2090
|
+
if (returnTypeMap) {
|
|
2091
|
+
const result = resolveCallExprReturnType(valueN, typeMap, returnTypeMap, 0);
|
|
2092
|
+
if (result) {
|
|
2093
|
+
setTypeMapEntry(typeMap, lhsName, result.type, result.confidence);
|
|
2094
|
+
return;
|
|
2095
|
+
}
|
|
2096
|
+
}
|
|
2097
|
+
// Record for cross-file resolution in build-edges.ts (imported functions)
|
|
2098
|
+
if (callAssignments) {
|
|
2099
|
+
recordCallAssignment(valueN, lhsName, typeMap, callAssignments);
|
|
2100
|
+
}
|
|
2101
|
+
// Factory method heuristic: const x = Foo.create() → type Foo, confidence 0.7
|
|
2102
|
+
if (createFn?.type === 'member_expression') {
|
|
2103
|
+
const obj = createFn.childForFieldName('object');
|
|
2104
|
+
if (obj?.type === 'identifier') {
|
|
2105
|
+
const objName = obj.text;
|
|
2106
|
+
if (objName[0] && objName[0] !== objName[0].toLowerCase() && !BUILTIN_GLOBALS.has(objName)) {
|
|
2107
|
+
setTypeMapEntry(typeMap, lhsName, objName, 0.7);
|
|
2108
|
+
}
|
|
2109
|
+
}
|
|
2110
|
+
}
|
|
2111
|
+
}
|
|
2112
|
+
/**
|
|
2113
|
+
* Seed composite pts keys from a module-level object literal assignment (Phase 8.3f).
|
|
2114
|
+
*
|
|
2115
|
+
* `const obj = { baz: () => {} }` → typeMap['obj.baz'] = 'obj.baz'
|
|
2116
|
+
* `const obj = { baz }` (shorthand) → typeMap['obj.baz'] = 'baz' (bare identifier target)
|
|
2117
|
+
* `const obj = { baz: otherFn }` → typeMap['obj.baz'] = 'otherFn' (identifier alias)
|
|
2118
|
+
* `const obj = { baz() {} }` (method shorthand) → typeMap['obj.baz'] = 'obj.baz'
|
|
2119
|
+
*
|
|
2120
|
+
* For function/arrow values, the value is the qualified name ('obj.baz') because
|
|
2121
|
+
* extractObjectLiteralFunctions registers definitions under that qualified name to avoid
|
|
2122
|
+
* polluting the global index with bare property names like 'init', 'run', or 'render'.
|
|
2123
|
+
* Enables accessor this-dispatch: when typeMap['getter:this'] = 'obj',
|
|
2124
|
+
* resolving this.baz() inside getter → typeMap['obj.baz'] → 'obj.baz' → lookup.byName('obj.baz').
|
|
2125
|
+
*
|
|
2126
|
+
* Scope guard: caller must ensure `node` is not inside a function body
|
|
2127
|
+
* (mirrors Rust handle_var_decl's find_parent_of_types check — function-scoped
|
|
2128
|
+
* `const localObj = { fn: ... }` must not shadow a module-level `const obj`).
|
|
2129
|
+
*/
|
|
2130
|
+
function handleObjectLiteralTypeMap(lhsName, valueN, typeMap) {
|
|
2131
|
+
for (let i = 0; i < valueN.childCount; i++) {
|
|
2132
|
+
const child = valueN.child(i);
|
|
2133
|
+
if (!child)
|
|
2134
|
+
continue;
|
|
2135
|
+
if (child.type === 'shorthand_property_identifier') {
|
|
2136
|
+
setTypeMapEntry(typeMap, `${lhsName}.${child.text}`, child.text, 0.85);
|
|
2137
|
+
}
|
|
2138
|
+
else if (child.type === 'pair') {
|
|
2139
|
+
const keyNode = child.childForFieldName('key');
|
|
2140
|
+
const valNode = child.childForFieldName('value');
|
|
2141
|
+
if (!keyNode || !valNode)
|
|
2142
|
+
continue;
|
|
2143
|
+
const keyName = keyNode.type === 'string' ? keyNode.text.replace(/^['"]|['"]$/g, '') : keyNode.text;
|
|
2144
|
+
if (!keyName)
|
|
2145
|
+
continue;
|
|
2146
|
+
const qualifiedKey = `${lhsName}.${keyName}`;
|
|
2147
|
+
if (valNode.type === 'arrow_function' ||
|
|
2148
|
+
valNode.type === 'function_expression' ||
|
|
2149
|
+
valNode.type === 'function') {
|
|
2150
|
+
// Store the qualified name so the resolver finds the qualified definition.
|
|
2151
|
+
setTypeMapEntry(typeMap, qualifiedKey, qualifiedKey, 0.85);
|
|
2152
|
+
}
|
|
2153
|
+
else if (valNode.type === 'identifier') {
|
|
2154
|
+
setTypeMapEntry(typeMap, qualifiedKey, valNode.text, 0.85);
|
|
2155
|
+
}
|
|
2156
|
+
}
|
|
2157
|
+
else if (child.type === 'method_definition') {
|
|
2158
|
+
// Method shorthand: `const obj = { baz() {} }` → typeMap['obj.baz'] = 'obj.baz'
|
|
2159
|
+
// extractObjectLiteralFunctions registers a definition under the qualified name;
|
|
2160
|
+
// seed the matching typeMap entry so the two-step accessor dispatch finds it.
|
|
2161
|
+
const nameNode = child.childForFieldName('name');
|
|
2162
|
+
if (!nameNode)
|
|
2163
|
+
continue;
|
|
2164
|
+
setTypeMapEntry(typeMap, `${lhsName}.${nameNode.text}`, `${lhsName}.${nameNode.text}`, 0.85);
|
|
1869
2165
|
}
|
|
1870
2166
|
}
|
|
1871
|
-
|
|
1872
|
-
|
|
1873
|
-
|
|
1874
|
-
|
|
2167
|
+
}
|
|
2168
|
+
/**
|
|
2169
|
+
* Extract type info from a variable_declarator: type annotation, constructor, or factory.
|
|
2170
|
+
*
|
|
2171
|
+
* Orchestrates four concerns in priority order:
|
|
2172
|
+
* 1. fnRefBindings — always collected first (before any early return)
|
|
2173
|
+
* 2. new_expression — constructor wins over annotation (runtime type is authoritative)
|
|
2174
|
+
* 3. type_annotation — confidence 0.9 for static analysis
|
|
2175
|
+
* 4. call_expression / object literal — delegated to handleCallExprTypeMap /
|
|
2176
|
+
* handleObjectLiteralTypeMap
|
|
2177
|
+
*/
|
|
2178
|
+
function handleVarDeclaratorTypeMap(node, typeMap, returnTypeMap, callAssignments, fnRefBindings) {
|
|
2179
|
+
const nameN = node.childForFieldName('name');
|
|
2180
|
+
if (nameN?.type !== 'identifier')
|
|
2181
|
+
return;
|
|
2182
|
+
const typeAnno = findChild(node, 'type_annotation');
|
|
2183
|
+
const valueN = node.childForFieldName('value');
|
|
2184
|
+
// 1. fnRefBindings — must run before any early return so every declarator contributes.
|
|
2185
|
+
if (fnRefBindings && valueN) {
|
|
2186
|
+
collectFnRefBindings(nameN.text, valueN, fnRefBindings);
|
|
2187
|
+
}
|
|
2188
|
+
// 2. Constructor wins over annotation: `const x: Base = new Derived()` resolves to Derived.
|
|
1875
2189
|
if (valueN?.type === 'new_expression') {
|
|
1876
2190
|
const ctorType = extractNewExprTypeName(valueN);
|
|
1877
2191
|
if (ctorType) {
|
|
@@ -1879,7 +2193,7 @@ function handleVarDeclaratorTypeMap(node, typeMap, returnTypeMap, callAssignment
|
|
|
1879
2193
|
return;
|
|
1880
2194
|
}
|
|
1881
2195
|
}
|
|
1882
|
-
// Type annotation
|
|
2196
|
+
// 3. Type annotation — confidence 0.9.
|
|
1883
2197
|
if (typeAnno) {
|
|
1884
2198
|
const typeName = extractSimpleTypeName(typeAnno);
|
|
1885
2199
|
if (typeName) {
|
|
@@ -1891,109 +2205,14 @@ function handleVarDeclaratorTypeMap(node, typeMap, returnTypeMap, callAssignment
|
|
|
1891
2205
|
return;
|
|
1892
2206
|
if (valueN.type === 'new_expression')
|
|
1893
2207
|
return;
|
|
2208
|
+
// 4a. call_expression — Object.create / return-type propagation / factory heuristic.
|
|
1894
2209
|
if (valueN.type === 'call_expression') {
|
|
1895
|
-
|
|
1896
|
-
|
|
1897
|
-
if (createFn?.type === 'member_expression') {
|
|
1898
|
-
const createObj = createFn.childForFieldName('object');
|
|
1899
|
-
const createProp = createFn.childForFieldName('property');
|
|
1900
|
-
if (createObj?.text === 'Object' && createProp?.text === 'create') {
|
|
1901
|
-
const createArgs = valueN.childForFieldName('arguments') || findChild(valueN, 'arguments');
|
|
1902
|
-
if (createArgs) {
|
|
1903
|
-
let proto = null;
|
|
1904
|
-
for (let i = 0; i < createArgs.childCount; i++) {
|
|
1905
|
-
const n = createArgs.child(i);
|
|
1906
|
-
if (n && n.type !== '(' && n.type !== ')' && n.type !== ',') {
|
|
1907
|
-
proto = n;
|
|
1908
|
-
break;
|
|
1909
|
-
}
|
|
1910
|
-
}
|
|
1911
|
-
if (proto?.type === 'object') {
|
|
1912
|
-
seedProtoProperties(nameN.text, proto, typeMap);
|
|
1913
|
-
}
|
|
1914
|
-
}
|
|
1915
|
-
return;
|
|
1916
|
-
}
|
|
1917
|
-
}
|
|
1918
|
-
// Phase 8.2: inter-procedural propagation — try to resolve return type from
|
|
1919
|
-
// the local returnTypeMap before falling back to factory heuristics.
|
|
1920
|
-
if (returnTypeMap) {
|
|
1921
|
-
const result = resolveCallExprReturnType(valueN, typeMap, returnTypeMap, 0);
|
|
1922
|
-
if (result) {
|
|
1923
|
-
setTypeMapEntry(typeMap, nameN.text, result.type, result.confidence);
|
|
1924
|
-
return;
|
|
1925
|
-
}
|
|
1926
|
-
}
|
|
1927
|
-
// Record for cross-file resolution in build-edges.ts (imported functions)
|
|
1928
|
-
if (callAssignments) {
|
|
1929
|
-
recordCallAssignment(valueN, nameN.text, typeMap, callAssignments);
|
|
1930
|
-
}
|
|
1931
|
-
// Factory method heuristic: const x = Foo.create() → type Foo, confidence 0.7
|
|
1932
|
-
const fn = valueN.childForFieldName('function');
|
|
1933
|
-
if (fn?.type === 'member_expression') {
|
|
1934
|
-
const obj = fn.childForFieldName('object');
|
|
1935
|
-
if (obj?.type === 'identifier') {
|
|
1936
|
-
const objName = obj.text;
|
|
1937
|
-
if (objName[0] &&
|
|
1938
|
-
objName[0] !== objName[0].toLowerCase() &&
|
|
1939
|
-
!BUILTIN_GLOBALS.has(objName)) {
|
|
1940
|
-
setTypeMapEntry(typeMap, nameN.text, objName, 0.7);
|
|
1941
|
-
}
|
|
1942
|
-
}
|
|
1943
|
-
}
|
|
2210
|
+
handleCallExprTypeMap(nameN.text, valueN, typeMap, returnTypeMap, callAssignments);
|
|
2211
|
+
return;
|
|
1944
2212
|
}
|
|
1945
|
-
//
|
|
1946
|
-
// `const obj = { baz: () => {} }` → typeMap['obj.baz'] = 'obj.baz'
|
|
1947
|
-
// `const obj = { baz }` (shorthand) → typeMap['obj.baz'] = 'baz' (bare identifier target)
|
|
1948
|
-
// `const obj = { baz: otherFn }` → typeMap['obj.baz'] = 'otherFn' (identifier alias)
|
|
1949
|
-
//
|
|
1950
|
-
// For function/arrow values, the value is the qualified name ('obj.baz') because
|
|
1951
|
-
// extractObjectLiteralFunctions now registers definitions under that qualified name to avoid
|
|
1952
|
-
// polluting the global index with bare property names like 'init', 'run', or 'render'.
|
|
1953
|
-
// Enables accessor this-dispatch: when typeMap['getter:this'] = 'obj',
|
|
1954
|
-
// resolving this.baz() inside getter → typeMap['obj.baz'] → 'obj.baz' → lookup.byName('obj.baz').
|
|
1955
|
-
//
|
|
1956
|
-
// Scope guard: mirrors Rust handle_var_decl's find_parent_of_types check — skip object literals
|
|
1957
|
-
// inside function bodies so function-scoped `const localObj = { fn: ... }` never seeds
|
|
1958
|
-
// the typeMap (which would shadow a module-level `const obj` with the same property names).
|
|
2213
|
+
// 4b. Object literal — seed composite pts keys for module-level const objects.
|
|
1959
2214
|
if (valueN.type === 'object' && !hasFunctionScopeAncestor(node)) {
|
|
1960
|
-
|
|
1961
|
-
const child = valueN.child(i);
|
|
1962
|
-
if (!child)
|
|
1963
|
-
continue;
|
|
1964
|
-
if (child.type === 'shorthand_property_identifier') {
|
|
1965
|
-
setTypeMapEntry(typeMap, `${nameN.text}.${child.text}`, child.text, 0.85);
|
|
1966
|
-
}
|
|
1967
|
-
else if (child.type === 'pair') {
|
|
1968
|
-
const keyNode = child.childForFieldName('key');
|
|
1969
|
-
const valNode = child.childForFieldName('value');
|
|
1970
|
-
if (!keyNode || !valNode)
|
|
1971
|
-
continue;
|
|
1972
|
-
const keyName = keyNode.type === 'string' ? keyNode.text.replace(/^['"]|['"]$/g, '') : keyNode.text;
|
|
1973
|
-
if (!keyName)
|
|
1974
|
-
continue;
|
|
1975
|
-
const qualifiedKey = `${nameN.text}.${keyName}`;
|
|
1976
|
-
if (valNode.type === 'arrow_function' ||
|
|
1977
|
-
valNode.type === 'function_expression' ||
|
|
1978
|
-
valNode.type === 'function') {
|
|
1979
|
-
// Store the qualified name so the resolver finds the qualified definition.
|
|
1980
|
-
setTypeMapEntry(typeMap, qualifiedKey, qualifiedKey, 0.85);
|
|
1981
|
-
}
|
|
1982
|
-
else if (valNode.type === 'identifier') {
|
|
1983
|
-
setTypeMapEntry(typeMap, qualifiedKey, valNode.text, 0.85);
|
|
1984
|
-
}
|
|
1985
|
-
}
|
|
1986
|
-
else if (child.type === 'method_definition') {
|
|
1987
|
-
// Method shorthand: `const obj = { baz() {} }` → typeMap['obj.baz'] = 'obj.baz'
|
|
1988
|
-
// extractObjectLiteralFunctions registers a definition under the qualified name;
|
|
1989
|
-
// seed the matching typeMap entry so the two-step accessor dispatch finds it.
|
|
1990
|
-
const nameNode = child.childForFieldName('name');
|
|
1991
|
-
if (!nameNode)
|
|
1992
|
-
continue;
|
|
1993
|
-
const qualifiedKey = `${nameN.text}.${nameNode.text}`;
|
|
1994
|
-
setTypeMapEntry(typeMap, qualifiedKey, qualifiedKey, 0.85);
|
|
1995
|
-
}
|
|
1996
|
-
}
|
|
2215
|
+
handleObjectLiteralTypeMap(nameN.text, valueN, typeMap);
|
|
1997
2216
|
}
|
|
1998
2217
|
}
|
|
1999
2218
|
/** Extract type info from a required_parameter or optional_parameter. */
|
|
@@ -2579,6 +2798,31 @@ function extractReceiverName(objNode) {
|
|
|
2579
2798
|
function extractCallInfo(fn, callNode) {
|
|
2580
2799
|
const fnType = fn.type;
|
|
2581
2800
|
if (fnType === 'identifier') {
|
|
2801
|
+
if (fn.text === 'eval') {
|
|
2802
|
+
// eval(code) — dynamic code execution; capture first arg if it's a string literal
|
|
2803
|
+
const args = callNode.childForFieldName('arguments') || findChild(callNode, 'arguments');
|
|
2804
|
+
let keyExpr;
|
|
2805
|
+
if (args) {
|
|
2806
|
+
for (let i = 0; i < args.childCount; i++) {
|
|
2807
|
+
const child = args.child(i);
|
|
2808
|
+
if (!child)
|
|
2809
|
+
continue;
|
|
2810
|
+
const t = child.type;
|
|
2811
|
+
if (t === '(' || t === ')' || t === ',')
|
|
2812
|
+
continue;
|
|
2813
|
+
if (t === 'string' || t === 'template_string')
|
|
2814
|
+
keyExpr = child.text;
|
|
2815
|
+
break;
|
|
2816
|
+
}
|
|
2817
|
+
}
|
|
2818
|
+
return {
|
|
2819
|
+
name: '<dynamic:eval>',
|
|
2820
|
+
line: nodeStartLine(callNode),
|
|
2821
|
+
dynamic: true,
|
|
2822
|
+
dynamicKind: 'eval',
|
|
2823
|
+
keyExpr,
|
|
2824
|
+
};
|
|
2825
|
+
}
|
|
2582
2826
|
return { name: fn.text, line: nodeStartLine(callNode) };
|
|
2583
2827
|
}
|
|
2584
2828
|
if (fnType === 'member_expression') {
|
|
@@ -2589,6 +2833,46 @@ function extractCallInfo(fn, callNode) {
|
|
|
2589
2833
|
}
|
|
2590
2834
|
return null;
|
|
2591
2835
|
}
|
|
2836
|
+
/** Return the first non-punctuation argument node from a call_expression. */
|
|
2837
|
+
function getFirstCallArg(callNode) {
|
|
2838
|
+
const args = callNode.childForFieldName('arguments') || findChild(callNode, 'arguments');
|
|
2839
|
+
if (!args)
|
|
2840
|
+
return null;
|
|
2841
|
+
for (let i = 0; i < args.childCount; i++) {
|
|
2842
|
+
const child = args.child(i);
|
|
2843
|
+
if (!child)
|
|
2844
|
+
continue;
|
|
2845
|
+
const t = child.type;
|
|
2846
|
+
if (t === '(' || t === ')' || t === ',')
|
|
2847
|
+
continue;
|
|
2848
|
+
return child;
|
|
2849
|
+
}
|
|
2850
|
+
return null;
|
|
2851
|
+
}
|
|
2852
|
+
/** Extract the logical callee from a Reflect.apply/call/construct first-arg. */
|
|
2853
|
+
function extractReflectCalleeFromArg(firstArg, callLine) {
|
|
2854
|
+
if (firstArg?.type === 'identifier') {
|
|
2855
|
+
return { name: firstArg.text, line: callLine, dynamic: true, dynamicKind: 'reflection' };
|
|
2856
|
+
}
|
|
2857
|
+
if (firstArg?.type === 'member_expression') {
|
|
2858
|
+
const innerProp = firstArg.childForFieldName('property');
|
|
2859
|
+
if (innerProp?.type === 'identifier') {
|
|
2860
|
+
return {
|
|
2861
|
+
name: innerProp.text,
|
|
2862
|
+
line: callLine,
|
|
2863
|
+
dynamic: true,
|
|
2864
|
+
dynamicKind: 'reflection',
|
|
2865
|
+
receiver: extractReceiverName(firstArg.childForFieldName('object')),
|
|
2866
|
+
};
|
|
2867
|
+
}
|
|
2868
|
+
}
|
|
2869
|
+
return {
|
|
2870
|
+
name: '<dynamic:unresolved>',
|
|
2871
|
+
line: callLine,
|
|
2872
|
+
dynamic: true,
|
|
2873
|
+
dynamicKind: 'unresolved-dynamic',
|
|
2874
|
+
};
|
|
2875
|
+
}
|
|
2592
2876
|
/** Extract call info from a member_expression function node (obj.method()). */
|
|
2593
2877
|
function extractMemberExprCallInfo(fn, callNode) {
|
|
2594
2878
|
const obj = fn.childForFieldName('object');
|
|
@@ -2597,29 +2881,107 @@ function extractMemberExprCallInfo(fn, callNode) {
|
|
|
2597
2881
|
return null;
|
|
2598
2882
|
const callLine = nodeStartLine(callNode);
|
|
2599
2883
|
const propText = prop.text;
|
|
2600
|
-
|
|
2884
|
+
const isReflect = obj?.type === 'identifier' && obj.text === 'Reflect';
|
|
2885
|
+
// Reflect.apply(fn, thisArg, args) — extract the first arg as callee
|
|
2886
|
+
// Note: Reflect.call does not exist in the ECMAScript spec (only Reflect.apply, construct, get, etc.)
|
|
2887
|
+
if (isReflect && propText === 'apply') {
|
|
2888
|
+
return extractReflectCalleeFromArg(getFirstCallArg(callNode), callLine);
|
|
2889
|
+
}
|
|
2890
|
+
// Reflect.construct(Target, args) — extract the constructor as the callee
|
|
2891
|
+
if (isReflect && propText === 'construct') {
|
|
2892
|
+
return extractReflectCalleeFromArg(getFirstCallArg(callNode), callLine);
|
|
2893
|
+
}
|
|
2894
|
+
// Reflect.get(target, prop) — property access via reflection
|
|
2895
|
+
if (isReflect && propText === 'get') {
|
|
2896
|
+
const args = callNode.childForFieldName('arguments') || findChild(callNode, 'arguments');
|
|
2897
|
+
if (args) {
|
|
2898
|
+
let argIdx = 0;
|
|
2899
|
+
let firstArg = null;
|
|
2900
|
+
let secondArg = null;
|
|
2901
|
+
for (let i = 0; i < args.childCount; i++) {
|
|
2902
|
+
const child = args.child(i);
|
|
2903
|
+
if (!child)
|
|
2904
|
+
continue;
|
|
2905
|
+
const t = child.type;
|
|
2906
|
+
if (t === '(' || t === ')' || t === ',')
|
|
2907
|
+
continue;
|
|
2908
|
+
if (argIdx === 0)
|
|
2909
|
+
firstArg = child;
|
|
2910
|
+
else if (argIdx === 1)
|
|
2911
|
+
secondArg = child;
|
|
2912
|
+
argIdx++;
|
|
2913
|
+
}
|
|
2914
|
+
if (secondArg) {
|
|
2915
|
+
const receiver = firstArg ? extractReceiverName(firstArg) : undefined;
|
|
2916
|
+
const st = secondArg.type;
|
|
2917
|
+
if (st === 'string' || st === 'string_fragment') {
|
|
2918
|
+
const propName = secondArg.text.replace(/['"]/g, '');
|
|
2919
|
+
if (propName) {
|
|
2920
|
+
return {
|
|
2921
|
+
name: propName,
|
|
2922
|
+
line: callLine,
|
|
2923
|
+
dynamic: true,
|
|
2924
|
+
dynamicKind: 'computed-literal',
|
|
2925
|
+
keyExpr: secondArg.text,
|
|
2926
|
+
receiver,
|
|
2927
|
+
};
|
|
2928
|
+
}
|
|
2929
|
+
}
|
|
2930
|
+
if (st === 'identifier') {
|
|
2931
|
+
return {
|
|
2932
|
+
name: '<dynamic:computed-key>',
|
|
2933
|
+
line: callLine,
|
|
2934
|
+
dynamic: true,
|
|
2935
|
+
dynamicKind: 'computed-key',
|
|
2936
|
+
keyExpr: secondArg.text,
|
|
2937
|
+
receiver,
|
|
2938
|
+
};
|
|
2939
|
+
}
|
|
2940
|
+
}
|
|
2941
|
+
}
|
|
2942
|
+
return {
|
|
2943
|
+
name: '<dynamic:unresolved>',
|
|
2944
|
+
line: callLine,
|
|
2945
|
+
dynamic: true,
|
|
2946
|
+
dynamicKind: 'unresolved-dynamic',
|
|
2947
|
+
};
|
|
2948
|
+
}
|
|
2949
|
+
// .call()/.apply()/.bind() — this-rebinding; the wrapped function is the real callee.
|
|
2950
|
+
// When the object is a plain identifier (e.g. `f.call({})`), the target is statically
|
|
2951
|
+
// known so we emit a static call (no dynamic flag). This keeps parity with the native
|
|
2952
|
+
// Rust engine, which also resolves these as dyn=0, and prevents the dynZeroEdgeRows
|
|
2953
|
+
// upgrade path in emitDirectCallEdgesForCall from wrongly converting a dyn=0 edge
|
|
2954
|
+
// (emitted by a prior direct `f()` call) to dyn=1.
|
|
2955
|
+
// When the object is a member_expression (e.g. `obj.method.call({})`), we still mark
|
|
2956
|
+
// it dynamic/reflection because the inner callee requires a second resolution hop.
|
|
2601
2957
|
if (propText === 'call' || propText === 'apply' || propText === 'bind') {
|
|
2602
2958
|
if (obj && obj.type === 'identifier')
|
|
2603
|
-
return { name: obj.text, line: callLine
|
|
2959
|
+
return { name: obj.text, line: callLine };
|
|
2604
2960
|
if (obj && obj.type === 'member_expression') {
|
|
2605
2961
|
const innerProp = obj.childForFieldName('property');
|
|
2606
2962
|
if (innerProp)
|
|
2607
|
-
return { name: innerProp.text, line: callLine, dynamic: true };
|
|
2963
|
+
return { name: innerProp.text, line: callLine, dynamic: true, dynamicKind: 'reflection' };
|
|
2608
2964
|
}
|
|
2609
2965
|
}
|
|
2610
|
-
// Computed property: obj["method"]()
|
|
2966
|
+
// Computed string property: obj["method"]() — target is a literal; resolvable
|
|
2611
2967
|
const propType = prop.type;
|
|
2612
2968
|
if (propType === 'string' || propType === 'string_fragment') {
|
|
2613
2969
|
const methodName = propText.replace(/['"]/g, '');
|
|
2614
2970
|
if (methodName) {
|
|
2615
2971
|
const receiver = extractReceiverName(obj);
|
|
2616
|
-
return {
|
|
2972
|
+
return {
|
|
2973
|
+
name: methodName,
|
|
2974
|
+
line: callLine,
|
|
2975
|
+
dynamic: true,
|
|
2976
|
+
dynamicKind: 'computed-literal',
|
|
2977
|
+
receiver,
|
|
2978
|
+
};
|
|
2617
2979
|
}
|
|
2618
2980
|
}
|
|
2619
2981
|
const receiver = extractReceiverName(obj);
|
|
2620
2982
|
return { name: propText, line: callLine, receiver };
|
|
2621
2983
|
}
|
|
2622
|
-
/** Extract call info from a subscript_expression function node (obj[
|
|
2984
|
+
/** Extract call info from a subscript_expression function node (obj[key]()). */
|
|
2623
2985
|
function extractSubscriptCallInfo(fn, callNode) {
|
|
2624
2986
|
const obj = fn.childForFieldName('object');
|
|
2625
2987
|
const index = fn.childForFieldName('index');
|
|
@@ -2634,11 +2996,30 @@ function extractSubscriptCallInfo(fn, callNode) {
|
|
|
2634
2996
|
name: methodName,
|
|
2635
2997
|
line: nodeStartLine(callNode),
|
|
2636
2998
|
dynamic: true,
|
|
2999
|
+
dynamicKind: 'computed-literal',
|
|
2637
3000
|
receiver,
|
|
2638
3001
|
};
|
|
2639
3002
|
}
|
|
2640
3003
|
}
|
|
2641
|
-
|
|
3004
|
+
// obj[variable]() — key is a variable; may be resolvable via pts (RES-1), else flagged
|
|
3005
|
+
if (indexType === 'identifier') {
|
|
3006
|
+
const receiver = extractReceiverName(obj);
|
|
3007
|
+
return {
|
|
3008
|
+
name: '<dynamic:computed-key>',
|
|
3009
|
+
line: nodeStartLine(callNode),
|
|
3010
|
+
dynamic: true,
|
|
3011
|
+
dynamicKind: 'computed-key',
|
|
3012
|
+
keyExpr: index.text,
|
|
3013
|
+
receiver,
|
|
3014
|
+
};
|
|
3015
|
+
}
|
|
3016
|
+
// Any other index expression (binary, call, template with ${}…) — not statically resolvable
|
|
3017
|
+
return {
|
|
3018
|
+
name: '<dynamic:unresolved>',
|
|
3019
|
+
line: nodeStartLine(callNode),
|
|
3020
|
+
dynamic: true,
|
|
3021
|
+
dynamicKind: 'unresolved-dynamic',
|
|
3022
|
+
};
|
|
2642
3023
|
}
|
|
2643
3024
|
/**
|
|
2644
3025
|
* Callee names that idiomatically accept callback references. Used to gate
|
|
@@ -2927,6 +3308,11 @@ function runCollectorWalk(rootNode, targets) {
|
|
|
2927
3308
|
targets.newExpressions.push(name);
|
|
2928
3309
|
break;
|
|
2929
3310
|
}
|
|
3311
|
+
case 'decorator': {
|
|
3312
|
+
if (targets.calls)
|
|
3313
|
+
handleDecorator(node, targets.calls);
|
|
3314
|
+
break;
|
|
3315
|
+
}
|
|
2930
3316
|
case 'field_definition':
|
|
2931
3317
|
case 'public_field_definition':
|
|
2932
3318
|
if (targets.classMemberDefs)
|