@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
|
@@ -6,6 +6,7 @@ import type {
|
|
|
6
6
|
CallAssignment,
|
|
7
7
|
ClassRelation,
|
|
8
8
|
Definition,
|
|
9
|
+
DynamicKind,
|
|
9
10
|
Export,
|
|
10
11
|
ExtractorOutput,
|
|
11
12
|
FnRefBinding,
|
|
@@ -96,6 +97,13 @@ const BUILTIN_GLOBALS: Set<string> = new Set([
|
|
|
96
97
|
const MAX_PROPAGATION_DEPTH = 3;
|
|
97
98
|
/** Confidence penalty applied per propagation hop (1.0 → 0.9 → 0.8 → 0.7). */
|
|
98
99
|
export const PROPAGATION_HOP_PENALTY = 0.1;
|
|
100
|
+
/**
|
|
101
|
+
* Confidence score for a return type inferred from `return new Constructor()` with no
|
|
102
|
+
* explicit TypeScript annotation. Registered as `analysis.typeInferenceConfidence` in
|
|
103
|
+
* `src/infrastructure/config.ts` DEFAULTS — kept in sync manually until config is
|
|
104
|
+
* threaded through to `extractSymbols`.
|
|
105
|
+
*/
|
|
106
|
+
const INFERRED_RETURN_TYPE_CONFIDENCE = 0.85;
|
|
99
107
|
|
|
100
108
|
/**
|
|
101
109
|
* Extract symbols from a JS/TS parsed AST.
|
|
@@ -304,12 +312,14 @@ function dispatchQueryMatch(
|
|
|
304
312
|
} else if (c.exp_node) {
|
|
305
313
|
handleExportCapture(c, exps, imports);
|
|
306
314
|
} else if (c.callfn_node) {
|
|
307
|
-
calls.
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
});
|
|
315
|
+
// Route through extractCallInfo so special identifier calls (eval) get classified.
|
|
316
|
+
const callfnInfo = extractCallInfo(c.callfn_name!, c.callfn_node);
|
|
317
|
+
if (callfnInfo) calls.push(callfnInfo);
|
|
311
318
|
calls.push(...extractCallbackReferenceCalls(c.callfn_node));
|
|
312
319
|
} else if (c.callmem_node) {
|
|
320
|
+
// extractCallInfo → extractMemberExprCallInfo applies the plain-identifier guard for
|
|
321
|
+
// .call/.apply/.bind: when the object is a bare identifier (e.g. `fn.call(ctx)`),
|
|
322
|
+
// the call is emitted as static (no dynamic flag), matching the walk path and native engine.
|
|
313
323
|
const callInfo = extractCallInfo(c.callmem_fn!, c.callmem_node);
|
|
314
324
|
if (callInfo) calls.push(callInfo);
|
|
315
325
|
const cbDef = extractCallbackDefinition(c.callmem_node, c.callmem_fn);
|
|
@@ -320,10 +330,20 @@ function dispatchQueryMatch(
|
|
|
320
330
|
if (callInfo) calls.push(callInfo);
|
|
321
331
|
calls.push(...extractCallbackReferenceCalls(c.callsub_node));
|
|
322
332
|
} else if (c.newfn_node) {
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
333
|
+
if (c.newfn_name!.text === 'Function') {
|
|
334
|
+
// new Function(body) — dynamic code execution; classify as eval kind
|
|
335
|
+
calls.push({
|
|
336
|
+
name: '<dynamic:eval>',
|
|
337
|
+
line: nodeStartLine(c.newfn_node),
|
|
338
|
+
dynamic: true,
|
|
339
|
+
dynamicKind: 'eval',
|
|
340
|
+
});
|
|
341
|
+
} else {
|
|
342
|
+
calls.push({
|
|
343
|
+
name: c.newfn_name!.text,
|
|
344
|
+
line: nodeStartLine(c.newfn_node),
|
|
345
|
+
});
|
|
346
|
+
}
|
|
327
347
|
} else if (c.newmem_node) {
|
|
328
348
|
const callInfo = extractCallInfo(c.newmem_fn!, c.newmem_node);
|
|
329
349
|
if (callInfo) calls.push(callInfo);
|
|
@@ -382,8 +402,11 @@ function extractSymbolsQuery(tree: TreeSitterTree, query: TreeSitterQuery): Extr
|
|
|
382
402
|
arrayCallbackBindings,
|
|
383
403
|
});
|
|
384
404
|
|
|
385
|
-
// Extract definitions from destructured bindings (query patterns don't match object_pattern)
|
|
386
|
-
|
|
405
|
+
// Extract definitions from destructured bindings (query patterns don't match object_pattern).
|
|
406
|
+
// Also collects CJS require bindings (const { X } = require('…')) into a separate list so
|
|
407
|
+
// importedNames can classify them as import artifacts without creating DB edges (#1661).
|
|
408
|
+
const cjsRequireBindings: Array<{ names: string[]; source: string }> = [];
|
|
409
|
+
extractDestructuredBindingsWalk(tree.rootNode, definitions, cjsRequireBindings);
|
|
387
410
|
|
|
388
411
|
// Everything without bespoke traversal semantics is collected in ONE pass:
|
|
389
412
|
// dynamic import() calls, prototype-method definitions, param bindings,
|
|
@@ -426,6 +449,7 @@ function extractSymbolsQuery(tree: TreeSitterTree, query: TreeSitterQuery): Extr
|
|
|
426
449
|
thisCallBindings,
|
|
427
450
|
newExpressions,
|
|
428
451
|
...(definePropertyReceivers.size > 0 ? { definePropertyReceivers } : {}),
|
|
452
|
+
...(cjsRequireBindings.length > 0 ? { cjsRequireBindings } : {}),
|
|
429
453
|
};
|
|
430
454
|
}
|
|
431
455
|
|
|
@@ -474,6 +498,7 @@ function extractConstantsWalk(node: TreeSitterNode, definitions: Definition[]):
|
|
|
474
498
|
}
|
|
475
499
|
|
|
476
500
|
extractConstDeclarators(declNode, definitions);
|
|
501
|
+
extractLetVarObjLiteralDeclarators(declNode, definitions);
|
|
477
502
|
|
|
478
503
|
// Recurse into non-function, non-export-statement children (blocks, if-statements, etc.)
|
|
479
504
|
if (child.type !== 'export_statement') {
|
|
@@ -490,8 +515,15 @@ function extractConstantsWalk(node: TreeSitterNode, definitions: Definition[]):
|
|
|
490
515
|
/**
|
|
491
516
|
* Walk the AST to find destructured const bindings (query patterns don't match object_pattern).
|
|
492
517
|
* e.g. `const { handleToken, checkPermissions } = initAuth(config)`
|
|
518
|
+
*
|
|
519
|
+
* When `cjsRequireBindings` is provided, also records `const { X } = require('./path')` patterns
|
|
520
|
+
* so the edge builder can classify X as an import artifact rather than a local definition (#1661).
|
|
493
521
|
*/
|
|
494
|
-
function extractDestructuredBindingsWalk(
|
|
522
|
+
function extractDestructuredBindingsWalk(
|
|
523
|
+
node: TreeSitterNode,
|
|
524
|
+
definitions: Definition[],
|
|
525
|
+
cjsRequireBindings?: Array<{ names: string[]; source: string }>,
|
|
526
|
+
): void {
|
|
495
527
|
for (let i = 0; i < node.childCount; i++) {
|
|
496
528
|
const child = node.child(i);
|
|
497
529
|
if (!child) continue;
|
|
@@ -519,6 +551,43 @@ function extractDestructuredBindingsWalk(node: TreeSitterNode, definitions: Defi
|
|
|
519
551
|
nodeEndLine(declNode),
|
|
520
552
|
definitions,
|
|
521
553
|
);
|
|
554
|
+
// Record CJS require bindings so importedNames can classify these names
|
|
555
|
+
// as import artifacts, preventing false local-definition blocking (#1661).
|
|
556
|
+
if (cjsRequireBindings) {
|
|
557
|
+
const valueN = declarator.childForFieldName('value');
|
|
558
|
+
if (valueN?.type === 'call_expression') {
|
|
559
|
+
const fn = valueN.childForFieldName('function');
|
|
560
|
+
if (fn?.text === 'require') {
|
|
561
|
+
const args = valueN.childForFieldName('arguments');
|
|
562
|
+
const strArg = args && findChild(args, 'string');
|
|
563
|
+
if (strArg) {
|
|
564
|
+
const modPath = strArg.text.replace(/['"]/g, '');
|
|
565
|
+
const names: string[] = [];
|
|
566
|
+
for (let k = 0; k < nameN.childCount; k++) {
|
|
567
|
+
const prop = nameN.child(k);
|
|
568
|
+
if (!prop) continue;
|
|
569
|
+
if (
|
|
570
|
+
prop.type === 'shorthand_property_identifier_pattern' ||
|
|
571
|
+
prop.type === 'shorthand_property_identifier'
|
|
572
|
+
) {
|
|
573
|
+
names.push(prop.text);
|
|
574
|
+
} else if (prop.type === 'pair_pattern' || prop.type === 'pair') {
|
|
575
|
+
const val = prop.childForFieldName('value');
|
|
576
|
+
if (
|
|
577
|
+
val?.type === 'identifier' ||
|
|
578
|
+
val?.type === 'shorthand_property_identifier_pattern'
|
|
579
|
+
) {
|
|
580
|
+
names.push(val.text);
|
|
581
|
+
}
|
|
582
|
+
}
|
|
583
|
+
}
|
|
584
|
+
if (names.length > 0) {
|
|
585
|
+
cjsRequireBindings.push({ names, source: modPath });
|
|
586
|
+
}
|
|
587
|
+
}
|
|
588
|
+
}
|
|
589
|
+
}
|
|
590
|
+
}
|
|
522
591
|
} else if (nameN && nameN.type === 'array_pattern') {
|
|
523
592
|
// `const [x, y] = ...` — emit a single constant node whose name is the
|
|
524
593
|
// full array pattern text (e.g. `[x, y]`), matching native engine behaviour.
|
|
@@ -533,7 +602,7 @@ function extractDestructuredBindingsWalk(node: TreeSitterNode, definitions: Defi
|
|
|
533
602
|
}
|
|
534
603
|
|
|
535
604
|
if (child.type !== 'export_statement') {
|
|
536
|
-
extractDestructuredBindingsWalk(child, definitions);
|
|
605
|
+
extractDestructuredBindingsWalk(child, definitions, cjsRequireBindings);
|
|
537
606
|
}
|
|
538
607
|
}
|
|
539
608
|
}
|
|
@@ -578,6 +647,34 @@ function extractConstDeclarators(declNode: TreeSitterNode, definitions: Definiti
|
|
|
578
647
|
}
|
|
579
648
|
}
|
|
580
649
|
|
|
650
|
+
/**
|
|
651
|
+
* Extract qualified method definitions from `let`/`var` object-literal declarations.
|
|
652
|
+
* Mirrors `match_js_objlit_qualified_method_defs` in `javascript.rs`, which emits
|
|
653
|
+
* qualified definitions for `method_definition` (all declaration kinds) and
|
|
654
|
+
* `pair+arrow/function` (`let`/`var` only, since `const` is already handled by
|
|
655
|
+
* `extractConstDeclarators` → `extractObjectLiteralFunctions`).
|
|
656
|
+
*
|
|
657
|
+
* Called from extractConstantsWalk which already provides the function-scope guard.
|
|
658
|
+
* `var q1 = { m1() {} }` → emits Definition { name: 'q1.m1', kind: 'function' }
|
|
659
|
+
*/
|
|
660
|
+
function extractLetVarObjLiteralDeclarators(
|
|
661
|
+
declNode: TreeSitterNode,
|
|
662
|
+
definitions: Definition[],
|
|
663
|
+
): void {
|
|
664
|
+
const t = declNode.type;
|
|
665
|
+
if (t !== 'lexical_declaration' && t !== 'variable_declaration') return;
|
|
666
|
+
if (declNode.text.startsWith('const ')) return; // handled by extractConstDeclarators
|
|
667
|
+
|
|
668
|
+
for (let j = 0; j < declNode.childCount; j++) {
|
|
669
|
+
const declarator = declNode.child(j);
|
|
670
|
+
if (declarator?.type !== 'variable_declarator') continue;
|
|
671
|
+
const nameN = declarator.childForFieldName('name');
|
|
672
|
+
const valueN = declarator.childForFieldName('value');
|
|
673
|
+
if (nameN?.type !== 'identifier' || !valueN || valueN.type !== 'object') continue;
|
|
674
|
+
extractObjectLiteralFunctions(valueN, nameN.text, definitions);
|
|
675
|
+
}
|
|
676
|
+
}
|
|
677
|
+
|
|
581
678
|
/**
|
|
582
679
|
* Recursive walk to find dynamic import() calls.
|
|
583
680
|
* Query patterns match call_expression with identifier/member_expression/subscript_expression
|
|
@@ -770,6 +867,9 @@ function walkJavaScriptNode(node: TreeSitterNode, ctx: ExtractorOutput): void {
|
|
|
770
867
|
case 'enum_declaration':
|
|
771
868
|
handleEnumDecl(node, ctx);
|
|
772
869
|
break;
|
|
870
|
+
case 'decorator':
|
|
871
|
+
handleDecorator(node, ctx.calls);
|
|
872
|
+
break;
|
|
773
873
|
case 'call_expression':
|
|
774
874
|
handleCallExpr(node, ctx);
|
|
775
875
|
break;
|
|
@@ -1036,6 +1136,18 @@ function handleVariableDecl(node: TreeSitterNode, ctx: ExtractorOutput): void {
|
|
|
1036
1136
|
if (valueN.type === 'object') {
|
|
1037
1137
|
extractObjectLiteralFunctions(valueN, nameN.text, ctx.definitions);
|
|
1038
1138
|
}
|
|
1139
|
+
} else if (
|
|
1140
|
+
!isConst &&
|
|
1141
|
+
nameN.type === 'identifier' &&
|
|
1142
|
+
valueN.type === 'object' &&
|
|
1143
|
+
!hasFunctionScopeAncestor(node)
|
|
1144
|
+
) {
|
|
1145
|
+
// `let`/`var` object literals: extract qualified method definitions so that
|
|
1146
|
+
// `obj.method()` calls resolve correctly. Mirrors Rust match_js_objlit_qualified_method_defs
|
|
1147
|
+
// which emits method_definition qualified names for ALL declaration kinds and
|
|
1148
|
+
// pair+arrow/function for let/var only (const is already handled above).
|
|
1149
|
+
// Scope guard prevents local object properties from polluting the global index.
|
|
1150
|
+
extractObjectLiteralFunctions(valueN, nameN.text, ctx.definitions);
|
|
1039
1151
|
} else if (isConst && nameN.type === 'object_pattern' && !hasFunctionScopeAncestor(node)) {
|
|
1040
1152
|
// Destructured bindings: const { handleToken, checkPermissions } = initAuth(...)
|
|
1041
1153
|
// Each destructured property becomes a function definition so it can be
|
|
@@ -1050,6 +1162,40 @@ function handleVariableDecl(node: TreeSitterNode, ctx: ExtractorOutput): void {
|
|
|
1050
1162
|
nodeEndLine(node),
|
|
1051
1163
|
ctx.definitions,
|
|
1052
1164
|
);
|
|
1165
|
+
// Record CJS require bindings for import-artifact classification (#1661).
|
|
1166
|
+
if (valueN?.type === 'call_expression') {
|
|
1167
|
+
const fn = valueN.childForFieldName('function');
|
|
1168
|
+
if (fn?.text === 'require') {
|
|
1169
|
+
const args = valueN.childForFieldName('arguments');
|
|
1170
|
+
const strArg = args && findChild(args, 'string');
|
|
1171
|
+
if (strArg) {
|
|
1172
|
+
const modPath = strArg.text.replace(/['"]/g, '');
|
|
1173
|
+
const names: string[] = [];
|
|
1174
|
+
for (let k = 0; k < nameN.childCount; k++) {
|
|
1175
|
+
const prop = nameN.child(k);
|
|
1176
|
+
if (!prop) continue;
|
|
1177
|
+
if (
|
|
1178
|
+
prop.type === 'shorthand_property_identifier_pattern' ||
|
|
1179
|
+
prop.type === 'shorthand_property_identifier'
|
|
1180
|
+
) {
|
|
1181
|
+
names.push(prop.text);
|
|
1182
|
+
} else if (prop.type === 'pair_pattern' || prop.type === 'pair') {
|
|
1183
|
+
const val = prop.childForFieldName('value');
|
|
1184
|
+
if (
|
|
1185
|
+
val?.type === 'identifier' ||
|
|
1186
|
+
val?.type === 'shorthand_property_identifier_pattern'
|
|
1187
|
+
) {
|
|
1188
|
+
names.push(val.text);
|
|
1189
|
+
}
|
|
1190
|
+
}
|
|
1191
|
+
}
|
|
1192
|
+
if (names.length > 0) {
|
|
1193
|
+
if (!ctx.cjsRequireBindings) ctx.cjsRequireBindings = [];
|
|
1194
|
+
ctx.cjsRequireBindings.push({ names, source: modPath });
|
|
1195
|
+
}
|
|
1196
|
+
}
|
|
1197
|
+
}
|
|
1198
|
+
}
|
|
1053
1199
|
} else if (isConst && nameN.type === 'array_pattern' && !hasFunctionScopeAncestor(node)) {
|
|
1054
1200
|
// Array destructuring: `const [x, y] = ...` — emit a single constant node
|
|
1055
1201
|
// whose name is the full array pattern text (e.g. `[x, y]`), matching
|
|
@@ -1213,13 +1359,54 @@ function handleNewExpr(node: TreeSitterNode, ctx: ExtractorOutput): void {
|
|
|
1213
1359
|
const ctor = node.childForFieldName('constructor') || node.child(1);
|
|
1214
1360
|
if (!ctor) return;
|
|
1215
1361
|
if (ctor.type === 'identifier') {
|
|
1216
|
-
|
|
1362
|
+
if (ctor.text === 'Function') {
|
|
1363
|
+
// new Function(body) — dynamic code execution; undecidable static target
|
|
1364
|
+
ctx.calls.push({
|
|
1365
|
+
name: '<dynamic:eval>',
|
|
1366
|
+
line: nodeStartLine(node),
|
|
1367
|
+
dynamic: true,
|
|
1368
|
+
dynamicKind: 'eval' as DynamicKind,
|
|
1369
|
+
});
|
|
1370
|
+
} else {
|
|
1371
|
+
ctx.calls.push({ name: ctor.text, line: nodeStartLine(node) });
|
|
1372
|
+
}
|
|
1217
1373
|
} else if (ctor.type === 'member_expression') {
|
|
1218
1374
|
const callInfo = extractCallInfo(ctor, node);
|
|
1219
1375
|
if (callInfo) ctx.calls.push(callInfo);
|
|
1220
1376
|
}
|
|
1221
1377
|
}
|
|
1222
1378
|
|
|
1379
|
+
/**
|
|
1380
|
+
* Handle a TypeScript/JS decorator node.
|
|
1381
|
+
*
|
|
1382
|
+
* Only handles bare-identifier and bare-member-expression decorators
|
|
1383
|
+
* (`@Foo`, `@Foo.bar`) since decorated call expressions (`@Foo()`, `@Foo.bar()`)
|
|
1384
|
+
* are already visited as `call_expression` children by the recursive walker.
|
|
1385
|
+
*/
|
|
1386
|
+
function handleDecorator(node: TreeSitterNode, calls: Call[]): void {
|
|
1387
|
+
// Decorators wrap their expression; find the first non-@ child
|
|
1388
|
+
for (let i = 0; i < node.childCount; i++) {
|
|
1389
|
+
const child = node.child(i);
|
|
1390
|
+
if (!child || child.type === '@') continue;
|
|
1391
|
+
const t = child.type;
|
|
1392
|
+
if (t === 'identifier') {
|
|
1393
|
+
// @Foo — the identifier is the decorator factory; emit as reflection call
|
|
1394
|
+
calls.push({
|
|
1395
|
+
name: child.text,
|
|
1396
|
+
line: nodeStartLine(node),
|
|
1397
|
+
dynamic: true,
|
|
1398
|
+
dynamicKind: 'reflection',
|
|
1399
|
+
});
|
|
1400
|
+
} else if (t === 'member_expression') {
|
|
1401
|
+
// @Foo.bar — emit as reflection; always mark dynamic since it's decorator dispatch
|
|
1402
|
+
const callInfo = extractCallInfo(child, node);
|
|
1403
|
+
if (callInfo) calls.push({ ...callInfo, dynamic: true, dynamicKind: 'reflection' });
|
|
1404
|
+
}
|
|
1405
|
+
// call_expression / other — handled by the recursive walker automatically
|
|
1406
|
+
break;
|
|
1407
|
+
}
|
|
1408
|
+
}
|
|
1409
|
+
|
|
1223
1410
|
/** Handle a dynamic import() call expression and add to imports if static. */
|
|
1224
1411
|
function handleDynamicImportCall(node: TreeSitterNode, imports: Import[]): void {
|
|
1225
1412
|
const args = node.childForFieldName('arguments') || findChild(node, 'arguments');
|
|
@@ -1592,8 +1779,8 @@ function storeReturnType(
|
|
|
1592
1779
|
const inferred = findReturnNewExprType(body);
|
|
1593
1780
|
if (inferred) {
|
|
1594
1781
|
const existing = returnTypeMap.get(fnName);
|
|
1595
|
-
if (!existing ||
|
|
1596
|
-
returnTypeMap.set(fnName, { type: inferred, confidence:
|
|
1782
|
+
if (!existing || INFERRED_RETURN_TYPE_CONFIDENCE > existing.confidence)
|
|
1783
|
+
returnTypeMap.set(fnName, { type: inferred, confidence: INFERRED_RETURN_TYPE_CONFIDENCE });
|
|
1597
1784
|
}
|
|
1598
1785
|
}
|
|
1599
1786
|
}
|
|
@@ -1967,7 +2154,190 @@ function runContextCollectorWalk(rootNode: TreeSitterNode, out: ContextCollector
|
|
|
1967
2154
|
walk(rootNode, 0, null, null);
|
|
1968
2155
|
}
|
|
1969
2156
|
|
|
1970
|
-
/**
|
|
2157
|
+
/**
|
|
2158
|
+
* Record function-reference bindings from a variable_declarator's value node.
|
|
2159
|
+
*
|
|
2160
|
+
* Captures three patterns (Phase 8.3):
|
|
2161
|
+
* - `const fn = handler` (identifier alias)
|
|
2162
|
+
* - `const fn = obj.method` (member_expression alias)
|
|
2163
|
+
* - `const f = fn.bind(ctx)` (bind creates a bound alias)
|
|
2164
|
+
*
|
|
2165
|
+
* Must be called before any type-analysis early returns so every declarator
|
|
2166
|
+
* contributes to fnRefBindings regardless of whether it has a type annotation.
|
|
2167
|
+
*/
|
|
2168
|
+
function collectFnRefBindings(
|
|
2169
|
+
lhsName: string,
|
|
2170
|
+
valueN: TreeSitterNode,
|
|
2171
|
+
fnRefBindings: FnRefBinding[],
|
|
2172
|
+
): void {
|
|
2173
|
+
if (valueN.type === 'identifier' && !BUILTIN_GLOBALS.has(valueN.text)) {
|
|
2174
|
+
fnRefBindings.push({ lhs: lhsName, rhs: valueN.text });
|
|
2175
|
+
return;
|
|
2176
|
+
}
|
|
2177
|
+
if (valueN.type === 'member_expression') {
|
|
2178
|
+
const prop = valueN.childForFieldName('property');
|
|
2179
|
+
const obj = valueN.childForFieldName('object');
|
|
2180
|
+
// Guard: only static property access (property_identifier or identifier), not
|
|
2181
|
+
// computed subscript expressions like obj[expr] where prop.text would be the
|
|
2182
|
+
// full expression rather than a simple name — those can never match pts keys.
|
|
2183
|
+
if (
|
|
2184
|
+
prop &&
|
|
2185
|
+
(prop.type === 'property_identifier' || prop.type === 'identifier') &&
|
|
2186
|
+
obj?.type === 'identifier' &&
|
|
2187
|
+
!BUILTIN_GLOBALS.has(obj.text)
|
|
2188
|
+
) {
|
|
2189
|
+
fnRefBindings.push({ lhs: lhsName, rhs: prop.text, rhsReceiver: obj.text });
|
|
2190
|
+
}
|
|
2191
|
+
return;
|
|
2192
|
+
}
|
|
2193
|
+
if (valueN.type === 'call_expression') {
|
|
2194
|
+
// `const f = fn.bind(ctx)` — bind returns a bound copy of fn; track f → fn so
|
|
2195
|
+
// pts(f) ⊇ pts(fn) and subsequent `f(args)` calls resolve to fn.
|
|
2196
|
+
// Note: only flat-identifier binds (fn.bind) are tracked here; method-receiver
|
|
2197
|
+
// binds like `obj.method.bind(ctx)` are not captured (boundFn must be an identifier).
|
|
2198
|
+
const callFn = valueN.childForFieldName('function');
|
|
2199
|
+
if (callFn?.type === 'member_expression') {
|
|
2200
|
+
const bindProp = callFn.childForFieldName('property');
|
|
2201
|
+
if (bindProp?.text === 'bind') {
|
|
2202
|
+
const boundFn = callFn.childForFieldName('object');
|
|
2203
|
+
if (boundFn?.type === 'identifier' && !BUILTIN_GLOBALS.has(boundFn.text)) {
|
|
2204
|
+
fnRefBindings.push({ lhs: lhsName, rhs: boundFn.text });
|
|
2205
|
+
}
|
|
2206
|
+
}
|
|
2207
|
+
}
|
|
2208
|
+
}
|
|
2209
|
+
}
|
|
2210
|
+
|
|
2211
|
+
/**
|
|
2212
|
+
* Handle the `call_expression` branch of variable_declarator type-map seeding.
|
|
2213
|
+
*
|
|
2214
|
+
* Processes three sub-cases in priority order:
|
|
2215
|
+
* 1. Object.create({ ... }) — seeds composite pts keys from the prototype object (Phase 8.3e)
|
|
2216
|
+
* 2. Inter-procedural return-type propagation via returnTypeMap (Phase 8.2)
|
|
2217
|
+
* 3. Factory method heuristic: `const x = Foo.create()` → type Foo at confidence 0.7
|
|
2218
|
+
*/
|
|
2219
|
+
function handleCallExprTypeMap(
|
|
2220
|
+
lhsName: string,
|
|
2221
|
+
valueN: TreeSitterNode,
|
|
2222
|
+
typeMap: Map<string, TypeMapEntry>,
|
|
2223
|
+
returnTypeMap: Map<string, TypeMapEntry> | undefined,
|
|
2224
|
+
callAssignments: CallAssignment[] | undefined,
|
|
2225
|
+
): void {
|
|
2226
|
+
const createFn = valueN.childForFieldName('function');
|
|
2227
|
+
// Phase 8.3e: Object.create({ f1, f2 }) — seed composite pts keys obj.f1 → f1, etc.
|
|
2228
|
+
if (createFn?.type === 'member_expression') {
|
|
2229
|
+
const createObj = createFn.childForFieldName('object');
|
|
2230
|
+
const createProp = createFn.childForFieldName('property');
|
|
2231
|
+
if (createObj?.text === 'Object' && createProp?.text === 'create') {
|
|
2232
|
+
const createArgs = valueN.childForFieldName('arguments') || findChild(valueN, 'arguments');
|
|
2233
|
+
if (createArgs) {
|
|
2234
|
+
let proto: TreeSitterNode | null = null;
|
|
2235
|
+
for (let i = 0; i < createArgs.childCount; i++) {
|
|
2236
|
+
const n = createArgs.child(i);
|
|
2237
|
+
if (n && n.type !== '(' && n.type !== ')' && n.type !== ',') {
|
|
2238
|
+
proto = n;
|
|
2239
|
+
break;
|
|
2240
|
+
}
|
|
2241
|
+
}
|
|
2242
|
+
if (proto?.type === 'object') {
|
|
2243
|
+
seedProtoProperties(lhsName, proto, typeMap);
|
|
2244
|
+
}
|
|
2245
|
+
}
|
|
2246
|
+
return;
|
|
2247
|
+
}
|
|
2248
|
+
}
|
|
2249
|
+
// Phase 8.2: inter-procedural propagation — try to resolve return type from
|
|
2250
|
+
// the local returnTypeMap before falling back to factory heuristics.
|
|
2251
|
+
if (returnTypeMap) {
|
|
2252
|
+
const result = resolveCallExprReturnType(valueN, typeMap, returnTypeMap, 0);
|
|
2253
|
+
if (result) {
|
|
2254
|
+
setTypeMapEntry(typeMap, lhsName, result.type, result.confidence);
|
|
2255
|
+
return;
|
|
2256
|
+
}
|
|
2257
|
+
}
|
|
2258
|
+
// Record for cross-file resolution in build-edges.ts (imported functions)
|
|
2259
|
+
if (callAssignments) {
|
|
2260
|
+
recordCallAssignment(valueN, lhsName, typeMap, callAssignments);
|
|
2261
|
+
}
|
|
2262
|
+
// Factory method heuristic: const x = Foo.create() → type Foo, confidence 0.7
|
|
2263
|
+
if (createFn?.type === 'member_expression') {
|
|
2264
|
+
const obj = createFn.childForFieldName('object');
|
|
2265
|
+
if (obj?.type === 'identifier') {
|
|
2266
|
+
const objName = obj.text;
|
|
2267
|
+
if (objName[0] && objName[0] !== objName[0].toLowerCase() && !BUILTIN_GLOBALS.has(objName)) {
|
|
2268
|
+
setTypeMapEntry(typeMap, lhsName, objName, 0.7);
|
|
2269
|
+
}
|
|
2270
|
+
}
|
|
2271
|
+
}
|
|
2272
|
+
}
|
|
2273
|
+
|
|
2274
|
+
/**
|
|
2275
|
+
* Seed composite pts keys from a module-level object literal assignment (Phase 8.3f).
|
|
2276
|
+
*
|
|
2277
|
+
* `const obj = { baz: () => {} }` → typeMap['obj.baz'] = 'obj.baz'
|
|
2278
|
+
* `const obj = { baz }` (shorthand) → typeMap['obj.baz'] = 'baz' (bare identifier target)
|
|
2279
|
+
* `const obj = { baz: otherFn }` → typeMap['obj.baz'] = 'otherFn' (identifier alias)
|
|
2280
|
+
* `const obj = { baz() {} }` (method shorthand) → typeMap['obj.baz'] = 'obj.baz'
|
|
2281
|
+
*
|
|
2282
|
+
* For function/arrow values, the value is the qualified name ('obj.baz') because
|
|
2283
|
+
* extractObjectLiteralFunctions registers definitions under that qualified name to avoid
|
|
2284
|
+
* polluting the global index with bare property names like 'init', 'run', or 'render'.
|
|
2285
|
+
* Enables accessor this-dispatch: when typeMap['getter:this'] = 'obj',
|
|
2286
|
+
* resolving this.baz() inside getter → typeMap['obj.baz'] → 'obj.baz' → lookup.byName('obj.baz').
|
|
2287
|
+
*
|
|
2288
|
+
* Scope guard: caller must ensure `node` is not inside a function body
|
|
2289
|
+
* (mirrors Rust handle_var_decl's find_parent_of_types check — function-scoped
|
|
2290
|
+
* `const localObj = { fn: ... }` must not shadow a module-level `const obj`).
|
|
2291
|
+
*/
|
|
2292
|
+
function handleObjectLiteralTypeMap(
|
|
2293
|
+
lhsName: string,
|
|
2294
|
+
valueN: TreeSitterNode,
|
|
2295
|
+
typeMap: Map<string, TypeMapEntry>,
|
|
2296
|
+
): void {
|
|
2297
|
+
for (let i = 0; i < valueN.childCount; i++) {
|
|
2298
|
+
const child = valueN.child(i);
|
|
2299
|
+
if (!child) continue;
|
|
2300
|
+
if (child.type === 'shorthand_property_identifier') {
|
|
2301
|
+
setTypeMapEntry(typeMap, `${lhsName}.${child.text}`, child.text, 0.85);
|
|
2302
|
+
} else if (child.type === 'pair') {
|
|
2303
|
+
const keyNode = child.childForFieldName('key');
|
|
2304
|
+
const valNode = child.childForFieldName('value');
|
|
2305
|
+
if (!keyNode || !valNode) continue;
|
|
2306
|
+
const keyName =
|
|
2307
|
+
keyNode.type === 'string' ? keyNode.text.replace(/^['"]|['"]$/g, '') : keyNode.text;
|
|
2308
|
+
if (!keyName) continue;
|
|
2309
|
+
const qualifiedKey = `${lhsName}.${keyName}`;
|
|
2310
|
+
if (
|
|
2311
|
+
valNode.type === 'arrow_function' ||
|
|
2312
|
+
valNode.type === 'function_expression' ||
|
|
2313
|
+
valNode.type === 'function'
|
|
2314
|
+
) {
|
|
2315
|
+
// Store the qualified name so the resolver finds the qualified definition.
|
|
2316
|
+
setTypeMapEntry(typeMap, qualifiedKey, qualifiedKey, 0.85);
|
|
2317
|
+
} else if (valNode.type === 'identifier') {
|
|
2318
|
+
setTypeMapEntry(typeMap, qualifiedKey, valNode.text, 0.85);
|
|
2319
|
+
}
|
|
2320
|
+
} else if (child.type === 'method_definition') {
|
|
2321
|
+
// Method shorthand: `const obj = { baz() {} }` → typeMap['obj.baz'] = 'obj.baz'
|
|
2322
|
+
// extractObjectLiteralFunctions registers a definition under the qualified name;
|
|
2323
|
+
// seed the matching typeMap entry so the two-step accessor dispatch finds it.
|
|
2324
|
+
const nameNode = child.childForFieldName('name');
|
|
2325
|
+
if (!nameNode) continue;
|
|
2326
|
+
setTypeMapEntry(typeMap, `${lhsName}.${nameNode.text}`, `${lhsName}.${nameNode.text}`, 0.85);
|
|
2327
|
+
}
|
|
2328
|
+
}
|
|
2329
|
+
}
|
|
2330
|
+
|
|
2331
|
+
/**
|
|
2332
|
+
* Extract type info from a variable_declarator: type annotation, constructor, or factory.
|
|
2333
|
+
*
|
|
2334
|
+
* Orchestrates four concerns in priority order:
|
|
2335
|
+
* 1. fnRefBindings — always collected first (before any early return)
|
|
2336
|
+
* 2. new_expression — constructor wins over annotation (runtime type is authoritative)
|
|
2337
|
+
* 3. type_annotation — confidence 0.9 for static analysis
|
|
2338
|
+
* 4. call_expression / object literal — delegated to handleCallExprTypeMap /
|
|
2339
|
+
* handleObjectLiteralTypeMap
|
|
2340
|
+
*/
|
|
1971
2341
|
function handleVarDeclaratorTypeMap(
|
|
1972
2342
|
node: TreeSitterNode,
|
|
1973
2343
|
typeMap: Map<string, TypeMapEntry>,
|
|
@@ -1981,48 +2351,12 @@ function handleVarDeclaratorTypeMap(
|
|
|
1981
2351
|
const typeAnno = findChild(node, 'type_annotation');
|
|
1982
2352
|
const valueN = node.childForFieldName('value');
|
|
1983
2353
|
|
|
1984
|
-
//
|
|
1985
|
-
// Captures `const fn = handler` (identifier) and `const fn = obj.method` (member_expression).
|
|
1986
|
-
// Also handles `const f = fn.bind(ctx)` — bind returns a new function aliasing fn.
|
|
2354
|
+
// 1. fnRefBindings — must run before any early return so every declarator contributes.
|
|
1987
2355
|
if (fnRefBindings && valueN) {
|
|
1988
|
-
|
|
1989
|
-
fnRefBindings.push({ lhs: nameN.text, rhs: valueN.text });
|
|
1990
|
-
} else if (valueN.type === 'member_expression') {
|
|
1991
|
-
const prop = valueN.childForFieldName('property');
|
|
1992
|
-
const obj = valueN.childForFieldName('object');
|
|
1993
|
-
// Guard: only static property access (property_identifier or identifier), not
|
|
1994
|
-
// computed subscript expressions like obj[expr] where prop.text would be the
|
|
1995
|
-
// full expression rather than a simple name — those can never match pts keys.
|
|
1996
|
-
if (
|
|
1997
|
-
prop &&
|
|
1998
|
-
(prop.type === 'property_identifier' || prop.type === 'identifier') &&
|
|
1999
|
-
obj?.type === 'identifier' &&
|
|
2000
|
-
!BUILTIN_GLOBALS.has(obj.text)
|
|
2001
|
-
) {
|
|
2002
|
-
fnRefBindings.push({ lhs: nameN.text, rhs: prop.text, rhsReceiver: obj.text });
|
|
2003
|
-
}
|
|
2004
|
-
} else if (valueN.type === 'call_expression') {
|
|
2005
|
-
// `const f = fn.bind(ctx)` — bind returns a bound copy of fn; track f → fn so
|
|
2006
|
-
// pts(f) ⊇ pts(fn) and subsequent `f(args)` calls resolve to fn.
|
|
2007
|
-
// Note: only flat-identifier binds (fn.bind) are tracked here; method-receiver
|
|
2008
|
-
// binds like `obj.method.bind(ctx)` are not captured (boundFn must be an identifier).
|
|
2009
|
-
const callFn = valueN.childForFieldName('function');
|
|
2010
|
-
if (callFn?.type === 'member_expression') {
|
|
2011
|
-
const bindProp = callFn.childForFieldName('property');
|
|
2012
|
-
if (bindProp?.text === 'bind') {
|
|
2013
|
-
const boundFn = callFn.childForFieldName('object');
|
|
2014
|
-
if (boundFn?.type === 'identifier' && !BUILTIN_GLOBALS.has(boundFn.text)) {
|
|
2015
|
-
fnRefBindings.push({ lhs: nameN.text, rhs: boundFn.text });
|
|
2016
|
-
}
|
|
2017
|
-
}
|
|
2018
|
-
}
|
|
2019
|
-
}
|
|
2356
|
+
collectFnRefBindings(nameN.text, valueN, fnRefBindings);
|
|
2020
2357
|
}
|
|
2021
2358
|
|
|
2022
|
-
// Constructor
|
|
2023
|
-
// what matters for call resolution (e.g. `const x: Base = new Derived()` should
|
|
2024
|
-
// resolve `x.render()` to `Derived.render`, not `Base.render`).
|
|
2025
|
-
// When no constructor is present, annotation still takes precedence over factory.
|
|
2359
|
+
// 2. Constructor wins over annotation: `const x: Base = new Derived()` resolves to Derived.
|
|
2026
2360
|
if (valueN?.type === 'new_expression') {
|
|
2027
2361
|
const ctorType = extractNewExprTypeName(valueN);
|
|
2028
2362
|
if (ctorType) {
|
|
@@ -2031,7 +2365,7 @@ function handleVarDeclaratorTypeMap(
|
|
|
2031
2365
|
}
|
|
2032
2366
|
}
|
|
2033
2367
|
|
|
2034
|
-
// Type annotation
|
|
2368
|
+
// 3. Type annotation — confidence 0.9.
|
|
2035
2369
|
if (typeAnno) {
|
|
2036
2370
|
const typeName = extractSimpleTypeName(typeAnno);
|
|
2037
2371
|
if (typeName) {
|
|
@@ -2043,108 +2377,15 @@ function handleVarDeclaratorTypeMap(
|
|
|
2043
2377
|
if (!valueN) return;
|
|
2044
2378
|
if (valueN.type === 'new_expression') return;
|
|
2045
2379
|
|
|
2380
|
+
// 4a. call_expression — Object.create / return-type propagation / factory heuristic.
|
|
2046
2381
|
if (valueN.type === 'call_expression') {
|
|
2047
|
-
|
|
2048
|
-
|
|
2049
|
-
if (createFn?.type === 'member_expression') {
|
|
2050
|
-
const createObj = createFn.childForFieldName('object');
|
|
2051
|
-
const createProp = createFn.childForFieldName('property');
|
|
2052
|
-
if (createObj?.text === 'Object' && createProp?.text === 'create') {
|
|
2053
|
-
const createArgs = valueN.childForFieldName('arguments') || findChild(valueN, 'arguments');
|
|
2054
|
-
if (createArgs) {
|
|
2055
|
-
let proto: TreeSitterNode | null = null;
|
|
2056
|
-
for (let i = 0; i < createArgs.childCount; i++) {
|
|
2057
|
-
const n = createArgs.child(i);
|
|
2058
|
-
if (n && n.type !== '(' && n.type !== ')' && n.type !== ',') {
|
|
2059
|
-
proto = n;
|
|
2060
|
-
break;
|
|
2061
|
-
}
|
|
2062
|
-
}
|
|
2063
|
-
if (proto?.type === 'object') {
|
|
2064
|
-
seedProtoProperties(nameN.text, proto, typeMap);
|
|
2065
|
-
}
|
|
2066
|
-
}
|
|
2067
|
-
return;
|
|
2068
|
-
}
|
|
2069
|
-
}
|
|
2070
|
-
// Phase 8.2: inter-procedural propagation — try to resolve return type from
|
|
2071
|
-
// the local returnTypeMap before falling back to factory heuristics.
|
|
2072
|
-
if (returnTypeMap) {
|
|
2073
|
-
const result = resolveCallExprReturnType(valueN, typeMap, returnTypeMap, 0);
|
|
2074
|
-
if (result) {
|
|
2075
|
-
setTypeMapEntry(typeMap, nameN.text, result.type, result.confidence);
|
|
2076
|
-
return;
|
|
2077
|
-
}
|
|
2078
|
-
}
|
|
2079
|
-
// Record for cross-file resolution in build-edges.ts (imported functions)
|
|
2080
|
-
if (callAssignments) {
|
|
2081
|
-
recordCallAssignment(valueN, nameN.text, typeMap, callAssignments);
|
|
2082
|
-
}
|
|
2083
|
-
// Factory method heuristic: const x = Foo.create() → type Foo, confidence 0.7
|
|
2084
|
-
const fn = valueN.childForFieldName('function');
|
|
2085
|
-
if (fn?.type === 'member_expression') {
|
|
2086
|
-
const obj = fn.childForFieldName('object');
|
|
2087
|
-
if (obj?.type === 'identifier') {
|
|
2088
|
-
const objName = obj.text;
|
|
2089
|
-
if (
|
|
2090
|
-
objName[0] &&
|
|
2091
|
-
objName[0] !== objName[0].toLowerCase() &&
|
|
2092
|
-
!BUILTIN_GLOBALS.has(objName)
|
|
2093
|
-
) {
|
|
2094
|
-
setTypeMapEntry(typeMap, nameN.text, objName, 0.7);
|
|
2095
|
-
}
|
|
2096
|
-
}
|
|
2097
|
-
}
|
|
2382
|
+
handleCallExprTypeMap(nameN.text, valueN, typeMap, returnTypeMap, callAssignments);
|
|
2383
|
+
return;
|
|
2098
2384
|
}
|
|
2099
2385
|
|
|
2100
|
-
//
|
|
2101
|
-
// `const obj = { baz: () => {} }` → typeMap['obj.baz'] = 'obj.baz'
|
|
2102
|
-
// `const obj = { baz }` (shorthand) → typeMap['obj.baz'] = 'baz' (bare identifier target)
|
|
2103
|
-
// `const obj = { baz: otherFn }` → typeMap['obj.baz'] = 'otherFn' (identifier alias)
|
|
2104
|
-
//
|
|
2105
|
-
// For function/arrow values, the value is the qualified name ('obj.baz') because
|
|
2106
|
-
// extractObjectLiteralFunctions now registers definitions under that qualified name to avoid
|
|
2107
|
-
// polluting the global index with bare property names like 'init', 'run', or 'render'.
|
|
2108
|
-
// Enables accessor this-dispatch: when typeMap['getter:this'] = 'obj',
|
|
2109
|
-
// resolving this.baz() inside getter → typeMap['obj.baz'] → 'obj.baz' → lookup.byName('obj.baz').
|
|
2110
|
-
//
|
|
2111
|
-
// Scope guard: mirrors Rust handle_var_decl's find_parent_of_types check — skip object literals
|
|
2112
|
-
// inside function bodies so function-scoped `const localObj = { fn: ... }` never seeds
|
|
2113
|
-
// the typeMap (which would shadow a module-level `const obj` with the same property names).
|
|
2386
|
+
// 4b. Object literal — seed composite pts keys for module-level const objects.
|
|
2114
2387
|
if (valueN.type === 'object' && !hasFunctionScopeAncestor(node)) {
|
|
2115
|
-
|
|
2116
|
-
const child = valueN.child(i);
|
|
2117
|
-
if (!child) continue;
|
|
2118
|
-
if (child.type === 'shorthand_property_identifier') {
|
|
2119
|
-
setTypeMapEntry(typeMap, `${nameN.text}.${child.text}`, child.text, 0.85);
|
|
2120
|
-
} else if (child.type === 'pair') {
|
|
2121
|
-
const keyNode = child.childForFieldName('key');
|
|
2122
|
-
const valNode = child.childForFieldName('value');
|
|
2123
|
-
if (!keyNode || !valNode) continue;
|
|
2124
|
-
const keyName =
|
|
2125
|
-
keyNode.type === 'string' ? keyNode.text.replace(/^['"]|['"]$/g, '') : keyNode.text;
|
|
2126
|
-
if (!keyName) continue;
|
|
2127
|
-
const qualifiedKey = `${nameN.text}.${keyName}`;
|
|
2128
|
-
if (
|
|
2129
|
-
valNode.type === 'arrow_function' ||
|
|
2130
|
-
valNode.type === 'function_expression' ||
|
|
2131
|
-
valNode.type === 'function'
|
|
2132
|
-
) {
|
|
2133
|
-
// Store the qualified name so the resolver finds the qualified definition.
|
|
2134
|
-
setTypeMapEntry(typeMap, qualifiedKey, qualifiedKey, 0.85);
|
|
2135
|
-
} else if (valNode.type === 'identifier') {
|
|
2136
|
-
setTypeMapEntry(typeMap, qualifiedKey, valNode.text, 0.85);
|
|
2137
|
-
}
|
|
2138
|
-
} else if (child.type === 'method_definition') {
|
|
2139
|
-
// Method shorthand: `const obj = { baz() {} }` → typeMap['obj.baz'] = 'obj.baz'
|
|
2140
|
-
// extractObjectLiteralFunctions registers a definition under the qualified name;
|
|
2141
|
-
// seed the matching typeMap entry so the two-step accessor dispatch finds it.
|
|
2142
|
-
const nameNode = child.childForFieldName('name');
|
|
2143
|
-
if (!nameNode) continue;
|
|
2144
|
-
const qualifiedKey = `${nameN.text}.${nameNode.text}`;
|
|
2145
|
-
setTypeMapEntry(typeMap, qualifiedKey, qualifiedKey, 0.85);
|
|
2146
|
-
}
|
|
2147
|
-
}
|
|
2388
|
+
handleObjectLiteralTypeMap(nameN.text, valueN, typeMap);
|
|
2148
2389
|
}
|
|
2149
2390
|
}
|
|
2150
2391
|
|
|
@@ -2737,6 +2978,28 @@ function extractReceiverName(objNode: TreeSitterNode | null): string | undefined
|
|
|
2737
2978
|
function extractCallInfo(fn: TreeSitterNode, callNode: TreeSitterNode): Call | null {
|
|
2738
2979
|
const fnType = fn.type;
|
|
2739
2980
|
if (fnType === 'identifier') {
|
|
2981
|
+
if (fn.text === 'eval') {
|
|
2982
|
+
// eval(code) — dynamic code execution; capture first arg if it's a string literal
|
|
2983
|
+
const args = callNode.childForFieldName('arguments') || findChild(callNode, 'arguments');
|
|
2984
|
+
let keyExpr: string | undefined;
|
|
2985
|
+
if (args) {
|
|
2986
|
+
for (let i = 0; i < args.childCount; i++) {
|
|
2987
|
+
const child = args.child(i);
|
|
2988
|
+
if (!child) continue;
|
|
2989
|
+
const t = child.type;
|
|
2990
|
+
if (t === '(' || t === ')' || t === ',') continue;
|
|
2991
|
+
if (t === 'string' || t === 'template_string') keyExpr = child.text;
|
|
2992
|
+
break;
|
|
2993
|
+
}
|
|
2994
|
+
}
|
|
2995
|
+
return {
|
|
2996
|
+
name: '<dynamic:eval>',
|
|
2997
|
+
line: nodeStartLine(callNode),
|
|
2998
|
+
dynamic: true,
|
|
2999
|
+
dynamicKind: 'eval',
|
|
3000
|
+
keyExpr,
|
|
3001
|
+
};
|
|
3002
|
+
}
|
|
2740
3003
|
return { name: fn.text, line: nodeStartLine(callNode) };
|
|
2741
3004
|
}
|
|
2742
3005
|
if (fnType === 'member_expression') {
|
|
@@ -2748,6 +3011,45 @@ function extractCallInfo(fn: TreeSitterNode, callNode: TreeSitterNode): Call | n
|
|
|
2748
3011
|
return null;
|
|
2749
3012
|
}
|
|
2750
3013
|
|
|
3014
|
+
/** Return the first non-punctuation argument node from a call_expression. */
|
|
3015
|
+
function getFirstCallArg(callNode: TreeSitterNode): TreeSitterNode | null {
|
|
3016
|
+
const args = callNode.childForFieldName('arguments') || findChild(callNode, 'arguments');
|
|
3017
|
+
if (!args) return null;
|
|
3018
|
+
for (let i = 0; i < args.childCount; i++) {
|
|
3019
|
+
const child = args.child(i);
|
|
3020
|
+
if (!child) continue;
|
|
3021
|
+
const t = child.type;
|
|
3022
|
+
if (t === '(' || t === ')' || t === ',') continue;
|
|
3023
|
+
return child;
|
|
3024
|
+
}
|
|
3025
|
+
return null;
|
|
3026
|
+
}
|
|
3027
|
+
|
|
3028
|
+
/** Extract the logical callee from a Reflect.apply/call/construct first-arg. */
|
|
3029
|
+
function extractReflectCalleeFromArg(firstArg: TreeSitterNode | null, callLine: number): Call {
|
|
3030
|
+
if (firstArg?.type === 'identifier') {
|
|
3031
|
+
return { name: firstArg.text, line: callLine, dynamic: true, dynamicKind: 'reflection' };
|
|
3032
|
+
}
|
|
3033
|
+
if (firstArg?.type === 'member_expression') {
|
|
3034
|
+
const innerProp = firstArg.childForFieldName('property');
|
|
3035
|
+
if (innerProp?.type === 'identifier') {
|
|
3036
|
+
return {
|
|
3037
|
+
name: innerProp.text,
|
|
3038
|
+
line: callLine,
|
|
3039
|
+
dynamic: true,
|
|
3040
|
+
dynamicKind: 'reflection',
|
|
3041
|
+
receiver: extractReceiverName(firstArg.childForFieldName('object')),
|
|
3042
|
+
};
|
|
3043
|
+
}
|
|
3044
|
+
}
|
|
3045
|
+
return {
|
|
3046
|
+
name: '<dynamic:unresolved>',
|
|
3047
|
+
line: callLine,
|
|
3048
|
+
dynamic: true,
|
|
3049
|
+
dynamicKind: 'unresolved-dynamic',
|
|
3050
|
+
};
|
|
3051
|
+
}
|
|
3052
|
+
|
|
2751
3053
|
/** Extract call info from a member_expression function node (obj.method()). */
|
|
2752
3054
|
function extractMemberExprCallInfo(fn: TreeSitterNode, callNode: TreeSitterNode): Call | null {
|
|
2753
3055
|
const obj = fn.childForFieldName('object');
|
|
@@ -2756,23 +3058,101 @@ function extractMemberExprCallInfo(fn: TreeSitterNode, callNode: TreeSitterNode)
|
|
|
2756
3058
|
|
|
2757
3059
|
const callLine = nodeStartLine(callNode);
|
|
2758
3060
|
const propText = prop.text;
|
|
3061
|
+
const isReflect = obj?.type === 'identifier' && obj.text === 'Reflect';
|
|
2759
3062
|
|
|
2760
|
-
// .
|
|
3063
|
+
// Reflect.apply(fn, thisArg, args) — extract the first arg as callee
|
|
3064
|
+
// Note: Reflect.call does not exist in the ECMAScript spec (only Reflect.apply, construct, get, etc.)
|
|
3065
|
+
if (isReflect && propText === 'apply') {
|
|
3066
|
+
return extractReflectCalleeFromArg(getFirstCallArg(callNode), callLine);
|
|
3067
|
+
}
|
|
3068
|
+
|
|
3069
|
+
// Reflect.construct(Target, args) — extract the constructor as the callee
|
|
3070
|
+
if (isReflect && propText === 'construct') {
|
|
3071
|
+
return extractReflectCalleeFromArg(getFirstCallArg(callNode), callLine);
|
|
3072
|
+
}
|
|
3073
|
+
|
|
3074
|
+
// Reflect.get(target, prop) — property access via reflection
|
|
3075
|
+
if (isReflect && propText === 'get') {
|
|
3076
|
+
const args = callNode.childForFieldName('arguments') || findChild(callNode, 'arguments');
|
|
3077
|
+
if (args) {
|
|
3078
|
+
let argIdx = 0;
|
|
3079
|
+
let firstArg: TreeSitterNode | null = null;
|
|
3080
|
+
let secondArg: TreeSitterNode | null = null;
|
|
3081
|
+
for (let i = 0; i < args.childCount; i++) {
|
|
3082
|
+
const child = args.child(i);
|
|
3083
|
+
if (!child) continue;
|
|
3084
|
+
const t = child.type;
|
|
3085
|
+
if (t === '(' || t === ')' || t === ',') continue;
|
|
3086
|
+
if (argIdx === 0) firstArg = child;
|
|
3087
|
+
else if (argIdx === 1) secondArg = child;
|
|
3088
|
+
argIdx++;
|
|
3089
|
+
}
|
|
3090
|
+
if (secondArg) {
|
|
3091
|
+
const receiver = firstArg ? extractReceiverName(firstArg) : undefined;
|
|
3092
|
+
const st = secondArg.type;
|
|
3093
|
+
if (st === 'string' || st === 'string_fragment') {
|
|
3094
|
+
const propName = secondArg.text.replace(/['"]/g, '');
|
|
3095
|
+
if (propName) {
|
|
3096
|
+
return {
|
|
3097
|
+
name: propName,
|
|
3098
|
+
line: callLine,
|
|
3099
|
+
dynamic: true,
|
|
3100
|
+
dynamicKind: 'computed-literal',
|
|
3101
|
+
keyExpr: secondArg.text,
|
|
3102
|
+
receiver,
|
|
3103
|
+
};
|
|
3104
|
+
}
|
|
3105
|
+
}
|
|
3106
|
+
if (st === 'identifier') {
|
|
3107
|
+
return {
|
|
3108
|
+
name: '<dynamic:computed-key>',
|
|
3109
|
+
line: callLine,
|
|
3110
|
+
dynamic: true,
|
|
3111
|
+
dynamicKind: 'computed-key',
|
|
3112
|
+
keyExpr: secondArg.text,
|
|
3113
|
+
receiver,
|
|
3114
|
+
};
|
|
3115
|
+
}
|
|
3116
|
+
}
|
|
3117
|
+
}
|
|
3118
|
+
return {
|
|
3119
|
+
name: '<dynamic:unresolved>',
|
|
3120
|
+
line: callLine,
|
|
3121
|
+
dynamic: true,
|
|
3122
|
+
dynamicKind: 'unresolved-dynamic',
|
|
3123
|
+
};
|
|
3124
|
+
}
|
|
3125
|
+
|
|
3126
|
+
// .call()/.apply()/.bind() — this-rebinding; the wrapped function is the real callee.
|
|
3127
|
+
// When the object is a plain identifier (e.g. `f.call({})`), the target is statically
|
|
3128
|
+
// known so we emit a static call (no dynamic flag). This keeps parity with the native
|
|
3129
|
+
// Rust engine, which also resolves these as dyn=0, and prevents the dynZeroEdgeRows
|
|
3130
|
+
// upgrade path in emitDirectCallEdgesForCall from wrongly converting a dyn=0 edge
|
|
3131
|
+
// (emitted by a prior direct `f()` call) to dyn=1.
|
|
3132
|
+
// When the object is a member_expression (e.g. `obj.method.call({})`), we still mark
|
|
3133
|
+
// it dynamic/reflection because the inner callee requires a second resolution hop.
|
|
2761
3134
|
if (propText === 'call' || propText === 'apply' || propText === 'bind') {
|
|
2762
|
-
if (obj && obj.type === 'identifier') return { name: obj.text, line: callLine
|
|
3135
|
+
if (obj && obj.type === 'identifier') return { name: obj.text, line: callLine };
|
|
2763
3136
|
if (obj && obj.type === 'member_expression') {
|
|
2764
3137
|
const innerProp = obj.childForFieldName('property');
|
|
2765
|
-
if (innerProp)
|
|
3138
|
+
if (innerProp)
|
|
3139
|
+
return { name: innerProp.text, line: callLine, dynamic: true, dynamicKind: 'reflection' };
|
|
2766
3140
|
}
|
|
2767
3141
|
}
|
|
2768
3142
|
|
|
2769
|
-
// Computed property: obj["method"]()
|
|
3143
|
+
// Computed string property: obj["method"]() — target is a literal; resolvable
|
|
2770
3144
|
const propType = prop.type;
|
|
2771
3145
|
if (propType === 'string' || propType === 'string_fragment') {
|
|
2772
3146
|
const methodName = propText.replace(/['"]/g, '');
|
|
2773
3147
|
if (methodName) {
|
|
2774
3148
|
const receiver = extractReceiverName(obj);
|
|
2775
|
-
return {
|
|
3149
|
+
return {
|
|
3150
|
+
name: methodName,
|
|
3151
|
+
line: callLine,
|
|
3152
|
+
dynamic: true,
|
|
3153
|
+
dynamicKind: 'computed-literal',
|
|
3154
|
+
receiver,
|
|
3155
|
+
};
|
|
2776
3156
|
}
|
|
2777
3157
|
}
|
|
2778
3158
|
|
|
@@ -2780,7 +3160,7 @@ function extractMemberExprCallInfo(fn: TreeSitterNode, callNode: TreeSitterNode)
|
|
|
2780
3160
|
return { name: propText, line: callLine, receiver };
|
|
2781
3161
|
}
|
|
2782
3162
|
|
|
2783
|
-
/** Extract call info from a subscript_expression function node (obj[
|
|
3163
|
+
/** Extract call info from a subscript_expression function node (obj[key]()). */
|
|
2784
3164
|
function extractSubscriptCallInfo(fn: TreeSitterNode, callNode: TreeSitterNode): Call | null {
|
|
2785
3165
|
const obj = fn.childForFieldName('object');
|
|
2786
3166
|
const index = fn.childForFieldName('index');
|
|
@@ -2795,11 +3175,32 @@ function extractSubscriptCallInfo(fn: TreeSitterNode, callNode: TreeSitterNode):
|
|
|
2795
3175
|
name: methodName,
|
|
2796
3176
|
line: nodeStartLine(callNode),
|
|
2797
3177
|
dynamic: true,
|
|
3178
|
+
dynamicKind: 'computed-literal',
|
|
2798
3179
|
receiver,
|
|
2799
3180
|
};
|
|
2800
3181
|
}
|
|
2801
3182
|
}
|
|
2802
|
-
|
|
3183
|
+
|
|
3184
|
+
// obj[variable]() — key is a variable; may be resolvable via pts (RES-1), else flagged
|
|
3185
|
+
if (indexType === 'identifier') {
|
|
3186
|
+
const receiver = extractReceiverName(obj);
|
|
3187
|
+
return {
|
|
3188
|
+
name: '<dynamic:computed-key>',
|
|
3189
|
+
line: nodeStartLine(callNode),
|
|
3190
|
+
dynamic: true,
|
|
3191
|
+
dynamicKind: 'computed-key',
|
|
3192
|
+
keyExpr: index.text,
|
|
3193
|
+
receiver,
|
|
3194
|
+
};
|
|
3195
|
+
}
|
|
3196
|
+
|
|
3197
|
+
// Any other index expression (binary, call, template with ${}…) — not statically resolvable
|
|
3198
|
+
return {
|
|
3199
|
+
name: '<dynamic:unresolved>',
|
|
3200
|
+
line: nodeStartLine(callNode),
|
|
3201
|
+
dynamic: true,
|
|
3202
|
+
dynamicKind: 'unresolved-dynamic',
|
|
3203
|
+
};
|
|
2803
3204
|
}
|
|
2804
3205
|
|
|
2805
3206
|
/**
|
|
@@ -3118,6 +3519,10 @@ function runCollectorWalk(rootNode: TreeSitterNode, targets: CollectorWalkTarget
|
|
|
3118
3519
|
if (name) targets.newExpressions.push(name);
|
|
3119
3520
|
break;
|
|
3120
3521
|
}
|
|
3522
|
+
case 'decorator': {
|
|
3523
|
+
if (targets.calls) handleDecorator(node, targets.calls);
|
|
3524
|
+
break;
|
|
3525
|
+
}
|
|
3121
3526
|
case 'field_definition':
|
|
3122
3527
|
case 'public_field_definition':
|
|
3123
3528
|
if (targets.classMemberDefs) handleFieldDef(node, targets.classMemberDefs);
|