@optave/codegraph 3.12.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 +83 -46
- 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/audit.d.ts.map +1 -1
- package/dist/cli/commands/audit.js +2 -1
- package/dist/cli/commands/audit.js.map +1 -1
- package/dist/cli/commands/batch.d.ts.map +1 -1
- package/dist/cli/commands/batch.js +1 -0
- package/dist/cli/commands/batch.js.map +1 -1
- package/dist/cli/commands/build.d.ts.map +1 -1
- package/dist/cli/commands/build.js +6 -1
- package/dist/cli/commands/build.js.map +1 -1
- package/dist/cli/commands/config.d.ts +3 -0
- package/dist/cli/commands/config.d.ts.map +1 -0
- package/dist/cli/commands/config.js +275 -0
- package/dist/cli/commands/config.js.map +1 -0
- 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/cli/commands/triage.js +1 -1
- package/dist/cli/commands/triage.js.map +1 -1
- package/dist/cli/index.d.ts.map +1 -1
- package/dist/cli/index.js +10 -0
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/shared/options.d.ts +2 -1
- package/dist/cli/shared/options.d.ts.map +1 -1
- package/dist/cli/shared/options.js +11 -1
- package/dist/cli/shared/options.js.map +1 -1
- package/dist/cli/types.d.ts +2 -0
- package/dist/cli/types.d.ts.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 +69 -1
- 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 +29 -13
- package/dist/domain/graph/builder/call-resolver.d.ts.map +1 -1
- package/dist/domain/graph/builder/call-resolver.js +125 -205
- package/dist/domain/graph/builder/call-resolver.js.map +1 -1
- package/dist/domain/graph/builder/cha.d.ts +9 -1
- package/dist/domain/graph/builder/cha.d.ts.map +1 -1
- package/dist/domain/graph/builder/cha.js +17 -2
- package/dist/domain/graph/builder/cha.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 +24 -1
- package/dist/domain/graph/builder/helpers.d.ts.map +1 -1
- package/dist/domain/graph/builder/helpers.js +174 -65
- 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 +46 -5
- package/dist/domain/graph/builder/pipeline.js.map +1 -1
- package/dist/domain/graph/builder/stages/build-edges.d.ts +0 -2
- package/dist/domain/graph/builder/stages/build-edges.d.ts.map +1 -1
- package/dist/domain/graph/builder/stages/build-edges.js +554 -538
- 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 +3 -2
- package/dist/domain/graph/builder/stages/detect-changes.js.map +1 -1
- package/dist/domain/graph/builder/stages/finalize.d.ts.map +1 -1
- package/dist/domain/graph/builder/stages/finalize.js +4 -0
- package/dist/domain/graph/builder/stages/finalize.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 +952 -343
- package/dist/domain/graph/builder/stages/native-orchestrator.js.map +1 -1
- package/dist/domain/graph/builder/stages/resolve-imports.js +1 -1
- package/dist/domain/graph/builder/stages/resolve-imports.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 +16 -5
- package/dist/domain/parser.d.ts.map +1 -1
- package/dist/domain/parser.js +58 -17
- 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 +13 -2
- 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 +26 -5
- package/dist/domain/wasm-worker-pool.js.map +1 -1
- package/dist/domain/wasm-worker-protocol.d.ts +8 -0
- package/dist/domain/wasm-worker-protocol.d.ts.map +1 -1
- package/dist/extractors/cpp.d.ts.map +1 -1
- package/dist/extractors/cpp.js +42 -1
- package/dist/extractors/cpp.js.map +1 -1
- package/dist/extractors/cuda.d.ts.map +1 -1
- package/dist/extractors/cuda.js +42 -1
- package/dist/extractors/cuda.js.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 +15 -2
- package/dist/extractors/helpers.d.ts.map +1 -1
- package/dist/extractors/helpers.js +45 -1
- package/dist/extractors/helpers.js.map +1 -1
- package/dist/extractors/java.d.ts.map +1 -1
- package/dist/extractors/java.js +85 -8
- package/dist/extractors/java.js.map +1 -1
- package/dist/extractors/javascript.d.ts.map +1 -1
- package/dist/extractors/javascript.js +686 -169
- 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-query.d.ts +1 -1
- package/dist/features/structure-query.d.ts.map +1 -1
- package/dist/features/structure-query.js +6 -6
- package/dist/features/structure-query.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/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/infrastructure/config.d.ts +87 -4
- package/dist/infrastructure/config.d.ts.map +1 -1
- package/dist/infrastructure/config.js +424 -22
- package/dist/infrastructure/config.js.map +1 -1
- package/dist/infrastructure/registry.d.ts +27 -7
- package/dist/infrastructure/registry.d.ts.map +1 -1
- package/dist/infrastructure/registry.js +79 -5
- 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/structure.d.ts +1 -1
- package/dist/presentation/structure.d.ts.map +1 -1
- package/dist/presentation/structure.js +2 -2
- package/dist/presentation/structure.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 +113 -1
- package/dist/types.d.ts.map +1 -1
- package/grammars/tree-sitter-erlang.wasm +0 -0
- package/grammars/tree-sitter-gleam.wasm +0 -0
- package/package.json +7 -8
- 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/audit.ts +2 -1
- package/src/cli/commands/batch.ts +1 -0
- package/src/cli/commands/build.ts +6 -1
- package/src/cli/commands/config.ts +353 -0
- package/src/cli/commands/roles.ts +6 -1
- package/src/cli/commands/triage.ts +1 -1
- package/src/cli/index.ts +10 -0
- package/src/cli/shared/options.ts +11 -1
- package/src/cli/types.ts +2 -0
- 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 +69 -1
- 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 +156 -218
- package/src/domain/graph/builder/cha.ts +18 -1
- package/src/domain/graph/builder/context.ts +1 -0
- package/src/domain/graph/builder/helpers.ts +205 -67
- package/src/domain/graph/builder/incremental.ts +249 -119
- package/src/domain/graph/builder/pipeline.ts +59 -6
- package/src/domain/graph/builder/stages/build-edges.ts +783 -652
- package/src/domain/graph/builder/stages/collect-files.ts +12 -6
- package/src/domain/graph/builder/stages/detect-changes.ts +4 -2
- package/src/domain/graph/builder/stages/finalize.ts +4 -0
- package/src/domain/graph/builder/stages/native-orchestrator.ts +1214 -398
- package/src/domain/graph/builder/stages/resolve-imports.ts +1 -1
- 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 +57 -16
- package/src/domain/queries.ts +1 -1
- package/src/domain/wasm-worker-entry.ts +13 -2
- package/src/domain/wasm-worker-pool.ts +29 -4
- package/src/domain/wasm-worker-protocol.ts +5 -0
- package/src/extractors/cpp.ts +44 -1
- package/src/extractors/cuda.ts +44 -1
- package/src/extractors/dart.ts +48 -3
- package/src/extractors/groovy.ts +62 -2
- package/src/extractors/helpers.ts +48 -2
- package/src/extractors/java.ts +88 -8
- package/src/extractors/javascript.ts +693 -167
- 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-query.ts +7 -7
- 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/index.ts +5 -1
- package/src/infrastructure/config.ts +511 -23
- package/src/infrastructure/registry.ts +117 -12
- 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/structure.ts +3 -3
- 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 +127 -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.
|
|
@@ -169,7 +177,19 @@ function handleClassCapture(
|
|
|
169
177
|
|
|
170
178
|
/** Handle method_definition capture. */
|
|
171
179
|
function handleMethodCapture(c: Record<string, TreeSitterNode>, definitions: Definition[]): void {
|
|
172
|
-
const
|
|
180
|
+
const methNameNode = c.meth_name!;
|
|
181
|
+
let methName: string;
|
|
182
|
+
if (methNameNode.type === 'computed_property_name') {
|
|
183
|
+
// Extract the inner string literal from `['methodName']` or `["methodName"]`.
|
|
184
|
+
// Non-string computed keys (e.g. `[Symbol.iterator]`) cannot be resolved at
|
|
185
|
+
// dot-notation call sites, so skip them entirely.
|
|
186
|
+
const inner = methNameNode.child(1); // child(0)='[', child(1)=string, child(2)=']'
|
|
187
|
+
if (!inner || (inner.type !== 'string' && inner.type !== 'string_fragment')) return;
|
|
188
|
+
methName = inner.text.replace(/^['"]|['"]$/g, '');
|
|
189
|
+
if (!methName) return;
|
|
190
|
+
} else {
|
|
191
|
+
methName = methNameNode.text;
|
|
192
|
+
}
|
|
173
193
|
const parentClass = findParentClass(c.meth_node!);
|
|
174
194
|
const fullName = parentClass ? `${parentClass}.${methName}` : methName;
|
|
175
195
|
const methChildren = extractParameters(c.meth_node!);
|
|
@@ -292,12 +312,14 @@ function dispatchQueryMatch(
|
|
|
292
312
|
} else if (c.exp_node) {
|
|
293
313
|
handleExportCapture(c, exps, imports);
|
|
294
314
|
} else if (c.callfn_node) {
|
|
295
|
-
calls.
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
});
|
|
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);
|
|
299
318
|
calls.push(...extractCallbackReferenceCalls(c.callfn_node));
|
|
300
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.
|
|
301
323
|
const callInfo = extractCallInfo(c.callmem_fn!, c.callmem_node);
|
|
302
324
|
if (callInfo) calls.push(callInfo);
|
|
303
325
|
const cbDef = extractCallbackDefinition(c.callmem_node, c.callmem_fn);
|
|
@@ -308,10 +330,20 @@ function dispatchQueryMatch(
|
|
|
308
330
|
if (callInfo) calls.push(callInfo);
|
|
309
331
|
calls.push(...extractCallbackReferenceCalls(c.callsub_node));
|
|
310
332
|
} else if (c.newfn_node) {
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
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
|
+
}
|
|
315
347
|
} else if (c.newmem_node) {
|
|
316
348
|
const callInfo = extractCallInfo(c.newmem_fn!, c.newmem_node);
|
|
317
349
|
if (callInfo) calls.push(callInfo);
|
|
@@ -370,8 +402,11 @@ function extractSymbolsQuery(tree: TreeSitterTree, query: TreeSitterQuery): Extr
|
|
|
370
402
|
arrayCallbackBindings,
|
|
371
403
|
});
|
|
372
404
|
|
|
373
|
-
// Extract definitions from destructured bindings (query patterns don't match object_pattern)
|
|
374
|
-
|
|
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);
|
|
375
410
|
|
|
376
411
|
// Everything without bespoke traversal semantics is collected in ONE pass:
|
|
377
412
|
// dynamic import() calls, prototype-method definitions, param bindings,
|
|
@@ -414,6 +449,7 @@ function extractSymbolsQuery(tree: TreeSitterTree, query: TreeSitterQuery): Extr
|
|
|
414
449
|
thisCallBindings,
|
|
415
450
|
newExpressions,
|
|
416
451
|
...(definePropertyReceivers.size > 0 ? { definePropertyReceivers } : {}),
|
|
452
|
+
...(cjsRequireBindings.length > 0 ? { cjsRequireBindings } : {}),
|
|
417
453
|
};
|
|
418
454
|
}
|
|
419
455
|
|
|
@@ -462,6 +498,7 @@ function extractConstantsWalk(node: TreeSitterNode, definitions: Definition[]):
|
|
|
462
498
|
}
|
|
463
499
|
|
|
464
500
|
extractConstDeclarators(declNode, definitions);
|
|
501
|
+
extractLetVarObjLiteralDeclarators(declNode, definitions);
|
|
465
502
|
|
|
466
503
|
// Recurse into non-function, non-export-statement children (blocks, if-statements, etc.)
|
|
467
504
|
if (child.type !== 'export_statement') {
|
|
@@ -478,8 +515,15 @@ function extractConstantsWalk(node: TreeSitterNode, definitions: Definition[]):
|
|
|
478
515
|
/**
|
|
479
516
|
* Walk the AST to find destructured const bindings (query patterns don't match object_pattern).
|
|
480
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).
|
|
481
521
|
*/
|
|
482
|
-
function extractDestructuredBindingsWalk(
|
|
522
|
+
function extractDestructuredBindingsWalk(
|
|
523
|
+
node: TreeSitterNode,
|
|
524
|
+
definitions: Definition[],
|
|
525
|
+
cjsRequireBindings?: Array<{ names: string[]; source: string }>,
|
|
526
|
+
): void {
|
|
483
527
|
for (let i = 0; i < node.childCount; i++) {
|
|
484
528
|
const child = node.child(i);
|
|
485
529
|
if (!child) continue;
|
|
@@ -507,12 +551,58 @@ function extractDestructuredBindingsWalk(node: TreeSitterNode, definitions: Defi
|
|
|
507
551
|
nodeEndLine(declNode),
|
|
508
552
|
definitions,
|
|
509
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
|
+
}
|
|
591
|
+
} else if (nameN && nameN.type === 'array_pattern') {
|
|
592
|
+
// `const [x, y] = ...` — emit a single constant node whose name is the
|
|
593
|
+
// full array pattern text (e.g. `[x, y]`), matching native engine behaviour.
|
|
594
|
+
definitions.push({
|
|
595
|
+
name: nameN.text,
|
|
596
|
+
kind: 'constant',
|
|
597
|
+
line: nodeStartLine(declNode),
|
|
598
|
+
endLine: nodeEndLine(declNode),
|
|
599
|
+
});
|
|
510
600
|
}
|
|
511
601
|
}
|
|
512
602
|
}
|
|
513
603
|
|
|
514
604
|
if (child.type !== 'export_statement') {
|
|
515
|
-
extractDestructuredBindingsWalk(child, definitions);
|
|
605
|
+
extractDestructuredBindingsWalk(child, definitions, cjsRequireBindings);
|
|
516
606
|
}
|
|
517
607
|
}
|
|
518
608
|
}
|
|
@@ -557,6 +647,34 @@ function extractConstDeclarators(declNode: TreeSitterNode, definitions: Definiti
|
|
|
557
647
|
}
|
|
558
648
|
}
|
|
559
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
|
+
|
|
560
678
|
/**
|
|
561
679
|
* Recursive walk to find dynamic import() calls.
|
|
562
680
|
* Query patterns match call_expression with identifier/member_expression/subscript_expression
|
|
@@ -749,6 +867,9 @@ function walkJavaScriptNode(node: TreeSitterNode, ctx: ExtractorOutput): void {
|
|
|
749
867
|
case 'enum_declaration':
|
|
750
868
|
handleEnumDecl(node, ctx);
|
|
751
869
|
break;
|
|
870
|
+
case 'decorator':
|
|
871
|
+
handleDecorator(node, ctx.calls);
|
|
872
|
+
break;
|
|
752
873
|
case 'call_expression':
|
|
753
874
|
handleCallExpr(node, ctx);
|
|
754
875
|
break;
|
|
@@ -816,8 +937,20 @@ function handleClassDecl(node: TreeSitterNode, ctx: ExtractorOutput): void {
|
|
|
816
937
|
function handleMethodDef(node: TreeSitterNode, ctx: ExtractorOutput): void {
|
|
817
938
|
const nameNode = node.childForFieldName('name');
|
|
818
939
|
if (nameNode) {
|
|
940
|
+
let methName: string;
|
|
941
|
+
if (nameNode.type === 'computed_property_name') {
|
|
942
|
+
// Extract the inner string literal from `['methodName']` or `["methodName"]`.
|
|
943
|
+
// Non-string computed keys (e.g. `[Symbol.iterator]`) cannot be resolved at
|
|
944
|
+
// dot-notation call sites, so skip them entirely.
|
|
945
|
+
const inner = nameNode.child(1); // child(0)='[', child(1)=string, child(2)=']'
|
|
946
|
+
if (!inner || (inner.type !== 'string' && inner.type !== 'string_fragment')) return;
|
|
947
|
+
methName = inner.text.replace(/^['"]|['"]$/g, '');
|
|
948
|
+
if (!methName) return;
|
|
949
|
+
} else {
|
|
950
|
+
methName = nameNode.text;
|
|
951
|
+
}
|
|
819
952
|
const parentClass = findParentClass(node);
|
|
820
|
-
const fullName = parentClass ? `${parentClass}.${
|
|
953
|
+
const fullName = parentClass ? `${parentClass}.${methName}` : methName;
|
|
821
954
|
const methChildren = extractParameters(node);
|
|
822
955
|
const methVis = extractVisibility(node);
|
|
823
956
|
ctx.definitions.push({
|
|
@@ -1003,6 +1136,18 @@ function handleVariableDecl(node: TreeSitterNode, ctx: ExtractorOutput): void {
|
|
|
1003
1136
|
if (valueN.type === 'object') {
|
|
1004
1137
|
extractObjectLiteralFunctions(valueN, nameN.text, ctx.definitions);
|
|
1005
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);
|
|
1006
1151
|
} else if (isConst && nameN.type === 'object_pattern' && !hasFunctionScopeAncestor(node)) {
|
|
1007
1152
|
// Destructured bindings: const { handleToken, checkPermissions } = initAuth(...)
|
|
1008
1153
|
// Each destructured property becomes a function definition so it can be
|
|
@@ -1017,6 +1162,50 @@ function handleVariableDecl(node: TreeSitterNode, ctx: ExtractorOutput): void {
|
|
|
1017
1162
|
nodeEndLine(node),
|
|
1018
1163
|
ctx.definitions,
|
|
1019
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
|
+
}
|
|
1199
|
+
} else if (isConst && nameN.type === 'array_pattern' && !hasFunctionScopeAncestor(node)) {
|
|
1200
|
+
// Array destructuring: `const [x, y] = ...` — emit a single constant node
|
|
1201
|
+
// whose name is the full array pattern text (e.g. `[x, y]`), matching
|
|
1202
|
+
// native engine behaviour. Scope guard mirrors the object_pattern branch above.
|
|
1203
|
+
ctx.definitions.push({
|
|
1204
|
+
name: nameN.text,
|
|
1205
|
+
kind: 'constant',
|
|
1206
|
+
line: nodeStartLine(node),
|
|
1207
|
+
endLine: nodeEndLine(node),
|
|
1208
|
+
});
|
|
1020
1209
|
}
|
|
1021
1210
|
}
|
|
1022
1211
|
}
|
|
@@ -1065,8 +1254,19 @@ function extractObjectLiteralFunctions(
|
|
|
1065
1254
|
} else if (child.type === 'method_definition') {
|
|
1066
1255
|
const nameNode = child.childForFieldName('name');
|
|
1067
1256
|
if (nameNode) {
|
|
1257
|
+
let methodName: string;
|
|
1258
|
+
if (nameNode.type === 'computed_property_name') {
|
|
1259
|
+
// Strip brackets+quotes from `['methodName']` to get a resolvable name.
|
|
1260
|
+
// Skip non-string computed keys (e.g. [Symbol.iterator]).
|
|
1261
|
+
const inner = nameNode.child(1);
|
|
1262
|
+
if (!inner || (inner.type !== 'string' && inner.type !== 'string_fragment')) continue;
|
|
1263
|
+
methodName = inner.text.replace(/^['"]|['"]$/g, '');
|
|
1264
|
+
if (!methodName) continue;
|
|
1265
|
+
} else {
|
|
1266
|
+
methodName = nameNode.text;
|
|
1267
|
+
}
|
|
1068
1268
|
definitions.push({
|
|
1069
|
-
name: `${varName}.${
|
|
1269
|
+
name: `${varName}.${methodName}`,
|
|
1070
1270
|
kind: 'function',
|
|
1071
1271
|
line: nodeStartLine(child),
|
|
1072
1272
|
endLine: nodeEndLine(child),
|
|
@@ -1159,13 +1359,54 @@ function handleNewExpr(node: TreeSitterNode, ctx: ExtractorOutput): void {
|
|
|
1159
1359
|
const ctor = node.childForFieldName('constructor') || node.child(1);
|
|
1160
1360
|
if (!ctor) return;
|
|
1161
1361
|
if (ctor.type === 'identifier') {
|
|
1162
|
-
|
|
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
|
+
}
|
|
1163
1373
|
} else if (ctor.type === 'member_expression') {
|
|
1164
1374
|
const callInfo = extractCallInfo(ctor, node);
|
|
1165
1375
|
if (callInfo) ctx.calls.push(callInfo);
|
|
1166
1376
|
}
|
|
1167
1377
|
}
|
|
1168
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
|
+
|
|
1169
1410
|
/** Handle a dynamic import() call expression and add to imports if static. */
|
|
1170
1411
|
function handleDynamicImportCall(node: TreeSitterNode, imports: Import[]): void {
|
|
1171
1412
|
const args = node.childForFieldName('arguments') || findChild(node, 'arguments');
|
|
@@ -1538,8 +1779,8 @@ function storeReturnType(
|
|
|
1538
1779
|
const inferred = findReturnNewExprType(body);
|
|
1539
1780
|
if (inferred) {
|
|
1540
1781
|
const existing = returnTypeMap.get(fnName);
|
|
1541
|
-
if (!existing ||
|
|
1542
|
-
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 });
|
|
1543
1784
|
}
|
|
1544
1785
|
}
|
|
1545
1786
|
}
|
|
@@ -1813,9 +2054,23 @@ function runContextCollectorWalk(rootNode: TreeSitterNode, out: ContextCollector
|
|
|
1813
2054
|
// Qualify with the enclosing class name so the PTS key matches
|
|
1814
2055
|
// callerName from findCaller (which uses def.name = 'ClassName.method').
|
|
1815
2056
|
const enclosingClass = classStack.length > 0 ? classStack[classStack.length - 1] : null;
|
|
1816
|
-
|
|
1817
|
-
|
|
1818
|
-
|
|
2057
|
+
let rawName: string;
|
|
2058
|
+
if (nameNode.type === 'computed_property_name') {
|
|
2059
|
+
const inner = nameNode.child(1);
|
|
2060
|
+
if (!inner || (inner.type !== 'string' && inner.type !== 'string_fragment')) {
|
|
2061
|
+
// Non-string computed key — skip adding to funcStack (no resolvable name).
|
|
2062
|
+
rawName = '';
|
|
2063
|
+
} else {
|
|
2064
|
+
rawName = inner.text.replace(/^['"]|['"]$/g, '');
|
|
2065
|
+
}
|
|
2066
|
+
} else {
|
|
2067
|
+
rawName = nameNode.text;
|
|
2068
|
+
}
|
|
2069
|
+
if (rawName) {
|
|
2070
|
+
const qualifiedName = enclosingClass ? `${enclosingClass}.${rawName}` : rawName;
|
|
2071
|
+
funcStack.push(qualifiedName);
|
|
2072
|
+
pushedFunc = true;
|
|
2073
|
+
}
|
|
1819
2074
|
}
|
|
1820
2075
|
} else if (t === 'variable_declarator') {
|
|
1821
2076
|
// `const process = (arr) => { ... }` — arrow/expression functions assigned
|
|
@@ -1866,6 +2121,8 @@ function runContextCollectorWalk(rootNode: TreeSitterNode, out: ContextCollector
|
|
|
1866
2121
|
collectCollectionWrapBinding(node, out.fnRefBindings);
|
|
1867
2122
|
} else if (t === 'required_parameter' || t === 'optional_parameter') {
|
|
1868
2123
|
handleParamTypeMap(node, out.typeMap);
|
|
2124
|
+
} else if (t === 'public_field_definition' || t === 'field_definition') {
|
|
2125
|
+
handleFieldDefTypeMap(node, out.typeMap, typeMapClass);
|
|
1869
2126
|
} else if (t === 'assignment_expression') {
|
|
1870
2127
|
handlePropWriteTypeMap(node, out.typeMap, typeMapClass);
|
|
1871
2128
|
} else if (t === 'call_expression') {
|
|
@@ -1897,7 +2154,190 @@ function runContextCollectorWalk(rootNode: TreeSitterNode, out: ContextCollector
|
|
|
1897
2154
|
walk(rootNode, 0, null, null);
|
|
1898
2155
|
}
|
|
1899
2156
|
|
|
1900
|
-
/**
|
|
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
|
+
*/
|
|
1901
2341
|
function handleVarDeclaratorTypeMap(
|
|
1902
2342
|
node: TreeSitterNode,
|
|
1903
2343
|
typeMap: Map<string, TypeMapEntry>,
|
|
@@ -1911,48 +2351,12 @@ function handleVarDeclaratorTypeMap(
|
|
|
1911
2351
|
const typeAnno = findChild(node, 'type_annotation');
|
|
1912
2352
|
const valueN = node.childForFieldName('value');
|
|
1913
2353
|
|
|
1914
|
-
//
|
|
1915
|
-
// Captures `const fn = handler` (identifier) and `const fn = obj.method` (member_expression).
|
|
1916
|
-
// 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.
|
|
1917
2355
|
if (fnRefBindings && valueN) {
|
|
1918
|
-
|
|
1919
|
-
fnRefBindings.push({ lhs: nameN.text, rhs: valueN.text });
|
|
1920
|
-
} else if (valueN.type === 'member_expression') {
|
|
1921
|
-
const prop = valueN.childForFieldName('property');
|
|
1922
|
-
const obj = valueN.childForFieldName('object');
|
|
1923
|
-
// Guard: only static property access (property_identifier or identifier), not
|
|
1924
|
-
// computed subscript expressions like obj[expr] where prop.text would be the
|
|
1925
|
-
// full expression rather than a simple name — those can never match pts keys.
|
|
1926
|
-
if (
|
|
1927
|
-
prop &&
|
|
1928
|
-
(prop.type === 'property_identifier' || prop.type === 'identifier') &&
|
|
1929
|
-
obj?.type === 'identifier' &&
|
|
1930
|
-
!BUILTIN_GLOBALS.has(obj.text)
|
|
1931
|
-
) {
|
|
1932
|
-
fnRefBindings.push({ lhs: nameN.text, rhs: prop.text, rhsReceiver: obj.text });
|
|
1933
|
-
}
|
|
1934
|
-
} else if (valueN.type === 'call_expression') {
|
|
1935
|
-
// `const f = fn.bind(ctx)` — bind returns a bound copy of fn; track f → fn so
|
|
1936
|
-
// pts(f) ⊇ pts(fn) and subsequent `f(args)` calls resolve to fn.
|
|
1937
|
-
// Note: only flat-identifier binds (fn.bind) are tracked here; method-receiver
|
|
1938
|
-
// binds like `obj.method.bind(ctx)` are not captured (boundFn must be an identifier).
|
|
1939
|
-
const callFn = valueN.childForFieldName('function');
|
|
1940
|
-
if (callFn?.type === 'member_expression') {
|
|
1941
|
-
const bindProp = callFn.childForFieldName('property');
|
|
1942
|
-
if (bindProp?.text === 'bind') {
|
|
1943
|
-
const boundFn = callFn.childForFieldName('object');
|
|
1944
|
-
if (boundFn?.type === 'identifier' && !BUILTIN_GLOBALS.has(boundFn.text)) {
|
|
1945
|
-
fnRefBindings.push({ lhs: nameN.text, rhs: boundFn.text });
|
|
1946
|
-
}
|
|
1947
|
-
}
|
|
1948
|
-
}
|
|
1949
|
-
}
|
|
2356
|
+
collectFnRefBindings(nameN.text, valueN, fnRefBindings);
|
|
1950
2357
|
}
|
|
1951
2358
|
|
|
1952
|
-
// Constructor
|
|
1953
|
-
// what matters for call resolution (e.g. `const x: Base = new Derived()` should
|
|
1954
|
-
// resolve `x.render()` to `Derived.render`, not `Base.render`).
|
|
1955
|
-
// 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.
|
|
1956
2360
|
if (valueN?.type === 'new_expression') {
|
|
1957
2361
|
const ctorType = extractNewExprTypeName(valueN);
|
|
1958
2362
|
if (ctorType) {
|
|
@@ -1961,7 +2365,7 @@ function handleVarDeclaratorTypeMap(
|
|
|
1961
2365
|
}
|
|
1962
2366
|
}
|
|
1963
2367
|
|
|
1964
|
-
// Type annotation
|
|
2368
|
+
// 3. Type annotation — confidence 0.9.
|
|
1965
2369
|
if (typeAnno) {
|
|
1966
2370
|
const typeName = extractSimpleTypeName(typeAnno);
|
|
1967
2371
|
if (typeName) {
|
|
@@ -1973,108 +2377,15 @@ function handleVarDeclaratorTypeMap(
|
|
|
1973
2377
|
if (!valueN) return;
|
|
1974
2378
|
if (valueN.type === 'new_expression') return;
|
|
1975
2379
|
|
|
2380
|
+
// 4a. call_expression — Object.create / return-type propagation / factory heuristic.
|
|
1976
2381
|
if (valueN.type === 'call_expression') {
|
|
1977
|
-
|
|
1978
|
-
|
|
1979
|
-
if (createFn?.type === 'member_expression') {
|
|
1980
|
-
const createObj = createFn.childForFieldName('object');
|
|
1981
|
-
const createProp = createFn.childForFieldName('property');
|
|
1982
|
-
if (createObj?.text === 'Object' && createProp?.text === 'create') {
|
|
1983
|
-
const createArgs = valueN.childForFieldName('arguments') || findChild(valueN, 'arguments');
|
|
1984
|
-
if (createArgs) {
|
|
1985
|
-
let proto: TreeSitterNode | null = null;
|
|
1986
|
-
for (let i = 0; i < createArgs.childCount; i++) {
|
|
1987
|
-
const n = createArgs.child(i);
|
|
1988
|
-
if (n && n.type !== '(' && n.type !== ')' && n.type !== ',') {
|
|
1989
|
-
proto = n;
|
|
1990
|
-
break;
|
|
1991
|
-
}
|
|
1992
|
-
}
|
|
1993
|
-
if (proto?.type === 'object') {
|
|
1994
|
-
seedProtoProperties(nameN.text, proto, typeMap);
|
|
1995
|
-
}
|
|
1996
|
-
}
|
|
1997
|
-
return;
|
|
1998
|
-
}
|
|
1999
|
-
}
|
|
2000
|
-
// Phase 8.2: inter-procedural propagation — try to resolve return type from
|
|
2001
|
-
// the local returnTypeMap before falling back to factory heuristics.
|
|
2002
|
-
if (returnTypeMap) {
|
|
2003
|
-
const result = resolveCallExprReturnType(valueN, typeMap, returnTypeMap, 0);
|
|
2004
|
-
if (result) {
|
|
2005
|
-
setTypeMapEntry(typeMap, nameN.text, result.type, result.confidence);
|
|
2006
|
-
return;
|
|
2007
|
-
}
|
|
2008
|
-
}
|
|
2009
|
-
// Record for cross-file resolution in build-edges.ts (imported functions)
|
|
2010
|
-
if (callAssignments) {
|
|
2011
|
-
recordCallAssignment(valueN, nameN.text, typeMap, callAssignments);
|
|
2012
|
-
}
|
|
2013
|
-
// Factory method heuristic: const x = Foo.create() → type Foo, confidence 0.7
|
|
2014
|
-
const fn = valueN.childForFieldName('function');
|
|
2015
|
-
if (fn?.type === 'member_expression') {
|
|
2016
|
-
const obj = fn.childForFieldName('object');
|
|
2017
|
-
if (obj?.type === 'identifier') {
|
|
2018
|
-
const objName = obj.text;
|
|
2019
|
-
if (
|
|
2020
|
-
objName[0] &&
|
|
2021
|
-
objName[0] !== objName[0].toLowerCase() &&
|
|
2022
|
-
!BUILTIN_GLOBALS.has(objName)
|
|
2023
|
-
) {
|
|
2024
|
-
setTypeMapEntry(typeMap, nameN.text, objName, 0.7);
|
|
2025
|
-
}
|
|
2026
|
-
}
|
|
2027
|
-
}
|
|
2382
|
+
handleCallExprTypeMap(nameN.text, valueN, typeMap, returnTypeMap, callAssignments);
|
|
2383
|
+
return;
|
|
2028
2384
|
}
|
|
2029
2385
|
|
|
2030
|
-
//
|
|
2031
|
-
// `const obj = { baz: () => {} }` → typeMap['obj.baz'] = 'obj.baz'
|
|
2032
|
-
// `const obj = { baz }` (shorthand) → typeMap['obj.baz'] = 'baz' (bare identifier target)
|
|
2033
|
-
// `const obj = { baz: otherFn }` → typeMap['obj.baz'] = 'otherFn' (identifier alias)
|
|
2034
|
-
//
|
|
2035
|
-
// For function/arrow values, the value is the qualified name ('obj.baz') because
|
|
2036
|
-
// extractObjectLiteralFunctions now registers definitions under that qualified name to avoid
|
|
2037
|
-
// polluting the global index with bare property names like 'init', 'run', or 'render'.
|
|
2038
|
-
// Enables accessor this-dispatch: when typeMap['getter:this'] = 'obj',
|
|
2039
|
-
// resolving this.baz() inside getter → typeMap['obj.baz'] → 'obj.baz' → lookup.byName('obj.baz').
|
|
2040
|
-
//
|
|
2041
|
-
// Scope guard: mirrors Rust handle_var_decl's find_parent_of_types check — skip object literals
|
|
2042
|
-
// inside function bodies so function-scoped `const localObj = { fn: ... }` never seeds
|
|
2043
|
-
// 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.
|
|
2044
2387
|
if (valueN.type === 'object' && !hasFunctionScopeAncestor(node)) {
|
|
2045
|
-
|
|
2046
|
-
const child = valueN.child(i);
|
|
2047
|
-
if (!child) continue;
|
|
2048
|
-
if (child.type === 'shorthand_property_identifier') {
|
|
2049
|
-
setTypeMapEntry(typeMap, `${nameN.text}.${child.text}`, child.text, 0.85);
|
|
2050
|
-
} else if (child.type === 'pair') {
|
|
2051
|
-
const keyNode = child.childForFieldName('key');
|
|
2052
|
-
const valNode = child.childForFieldName('value');
|
|
2053
|
-
if (!keyNode || !valNode) continue;
|
|
2054
|
-
const keyName =
|
|
2055
|
-
keyNode.type === 'string' ? keyNode.text.replace(/^['"]|['"]$/g, '') : keyNode.text;
|
|
2056
|
-
if (!keyName) continue;
|
|
2057
|
-
const qualifiedKey = `${nameN.text}.${keyName}`;
|
|
2058
|
-
if (
|
|
2059
|
-
valNode.type === 'arrow_function' ||
|
|
2060
|
-
valNode.type === 'function_expression' ||
|
|
2061
|
-
valNode.type === 'function'
|
|
2062
|
-
) {
|
|
2063
|
-
// Store the qualified name so the resolver finds the qualified definition.
|
|
2064
|
-
setTypeMapEntry(typeMap, qualifiedKey, qualifiedKey, 0.85);
|
|
2065
|
-
} else if (valNode.type === 'identifier') {
|
|
2066
|
-
setTypeMapEntry(typeMap, qualifiedKey, valNode.text, 0.85);
|
|
2067
|
-
}
|
|
2068
|
-
} else if (child.type === 'method_definition') {
|
|
2069
|
-
// Method shorthand: `const obj = { baz() {} }` → typeMap['obj.baz'] = 'obj.baz'
|
|
2070
|
-
// extractObjectLiteralFunctions registers a definition under the qualified name;
|
|
2071
|
-
// seed the matching typeMap entry so the two-step accessor dispatch finds it.
|
|
2072
|
-
const nameNode = child.childForFieldName('name');
|
|
2073
|
-
if (!nameNode) continue;
|
|
2074
|
-
const qualifiedKey = `${nameN.text}.${nameNode.text}`;
|
|
2075
|
-
setTypeMapEntry(typeMap, qualifiedKey, qualifiedKey, 0.85);
|
|
2076
|
-
}
|
|
2077
|
-
}
|
|
2388
|
+
handleObjectLiteralTypeMap(nameN.text, valueN, typeMap);
|
|
2078
2389
|
}
|
|
2079
2390
|
}
|
|
2080
2391
|
|
|
@@ -2090,6 +2401,55 @@ function handleParamTypeMap(node: TreeSitterNode, typeMap: Map<string, TypeMapEn
|
|
|
2090
2401
|
}
|
|
2091
2402
|
}
|
|
2092
2403
|
|
|
2404
|
+
/**
|
|
2405
|
+
* Extract type info from a class field declaration: `private repo: Repository<User>`.
|
|
2406
|
+
*
|
|
2407
|
+
* Seeds a class-scoped key `ClassName.field` (confidence 0.9) as the primary entry
|
|
2408
|
+
* so that two classes with identically-named fields don't overwrite each other's
|
|
2409
|
+
* typeMap entry (issue #1458). The resolver's `CallerClass.X` fallback (call-resolver.ts
|
|
2410
|
+
* line 110) looks up exactly this key.
|
|
2411
|
+
*
|
|
2412
|
+
* Bare `field` and `this.field` keys are kept at lower confidence (0.6) as fallbacks
|
|
2413
|
+
* for single-class files where the resolver may not have a callerClass context.
|
|
2414
|
+
*
|
|
2415
|
+
* Mirrors the field_definition branch of match_js_type_map in
|
|
2416
|
+
* crates/codegraph-core/src/extractors/javascript.rs.
|
|
2417
|
+
*/
|
|
2418
|
+
function handleFieldDefTypeMap(
|
|
2419
|
+
node: TreeSitterNode,
|
|
2420
|
+
typeMap: Map<string, TypeMapEntry>,
|
|
2421
|
+
currentClass: string | null,
|
|
2422
|
+
): void {
|
|
2423
|
+
const nameNode =
|
|
2424
|
+
node.childForFieldName('name') ||
|
|
2425
|
+
node.childForFieldName('property') ||
|
|
2426
|
+
findChild(node, 'property_identifier');
|
|
2427
|
+
if (!nameNode) return;
|
|
2428
|
+
const kind = nameNode.type;
|
|
2429
|
+
if (
|
|
2430
|
+
kind !== 'property_identifier' &&
|
|
2431
|
+
kind !== 'identifier' &&
|
|
2432
|
+
kind !== 'private_property_identifier'
|
|
2433
|
+
)
|
|
2434
|
+
return;
|
|
2435
|
+
const typeAnno = findChild(node, 'type_annotation');
|
|
2436
|
+
if (!typeAnno) return;
|
|
2437
|
+
const typeName = extractSimpleTypeName(typeAnno);
|
|
2438
|
+
if (!typeName) return;
|
|
2439
|
+
if (currentClass) {
|
|
2440
|
+
// Primary: class-scoped key prevents cross-class collision (issue #1458).
|
|
2441
|
+
setTypeMapEntry(typeMap, `${currentClass}.${nameNode.text}`, typeName, 0.9);
|
|
2442
|
+
// Fallback: bare keys at lower confidence for single-class files or when
|
|
2443
|
+
// the resolver does not have a callerClass in scope.
|
|
2444
|
+
setTypeMapEntry(typeMap, nameNode.text, typeName, 0.6);
|
|
2445
|
+
setTypeMapEntry(typeMap, `this.${nameNode.text}`, typeName, 0.6);
|
|
2446
|
+
} else {
|
|
2447
|
+
// No enclosing class declaration (e.g. class expression) — use bare keys only.
|
|
2448
|
+
setTypeMapEntry(typeMap, nameNode.text, typeName, 0.9);
|
|
2449
|
+
setTypeMapEntry(typeMap, `this.${nameNode.text}`, typeName, 0.9);
|
|
2450
|
+
}
|
|
2451
|
+
}
|
|
2452
|
+
|
|
2093
2453
|
/**
|
|
2094
2454
|
* Phase 8.3d: seed the pts map from object property writes.
|
|
2095
2455
|
*
|
|
@@ -2618,6 +2978,28 @@ function extractReceiverName(objNode: TreeSitterNode | null): string | undefined
|
|
|
2618
2978
|
function extractCallInfo(fn: TreeSitterNode, callNode: TreeSitterNode): Call | null {
|
|
2619
2979
|
const fnType = fn.type;
|
|
2620
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
|
+
}
|
|
2621
3003
|
return { name: fn.text, line: nodeStartLine(callNode) };
|
|
2622
3004
|
}
|
|
2623
3005
|
if (fnType === 'member_expression') {
|
|
@@ -2629,6 +3011,45 @@ function extractCallInfo(fn: TreeSitterNode, callNode: TreeSitterNode): Call | n
|
|
|
2629
3011
|
return null;
|
|
2630
3012
|
}
|
|
2631
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
|
+
|
|
2632
3053
|
/** Extract call info from a member_expression function node (obj.method()). */
|
|
2633
3054
|
function extractMemberExprCallInfo(fn: TreeSitterNode, callNode: TreeSitterNode): Call | null {
|
|
2634
3055
|
const obj = fn.childForFieldName('object');
|
|
@@ -2637,23 +3058,101 @@ function extractMemberExprCallInfo(fn: TreeSitterNode, callNode: TreeSitterNode)
|
|
|
2637
3058
|
|
|
2638
3059
|
const callLine = nodeStartLine(callNode);
|
|
2639
3060
|
const propText = prop.text;
|
|
3061
|
+
const isReflect = obj?.type === 'identifier' && obj.text === 'Reflect';
|
|
2640
3062
|
|
|
2641
|
-
// .
|
|
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.
|
|
2642
3134
|
if (propText === 'call' || propText === 'apply' || propText === 'bind') {
|
|
2643
|
-
if (obj && obj.type === 'identifier') return { name: obj.text, line: callLine
|
|
3135
|
+
if (obj && obj.type === 'identifier') return { name: obj.text, line: callLine };
|
|
2644
3136
|
if (obj && obj.type === 'member_expression') {
|
|
2645
3137
|
const innerProp = obj.childForFieldName('property');
|
|
2646
|
-
if (innerProp)
|
|
3138
|
+
if (innerProp)
|
|
3139
|
+
return { name: innerProp.text, line: callLine, dynamic: true, dynamicKind: 'reflection' };
|
|
2647
3140
|
}
|
|
2648
3141
|
}
|
|
2649
3142
|
|
|
2650
|
-
// Computed property: obj["method"]()
|
|
3143
|
+
// Computed string property: obj["method"]() — target is a literal; resolvable
|
|
2651
3144
|
const propType = prop.type;
|
|
2652
3145
|
if (propType === 'string' || propType === 'string_fragment') {
|
|
2653
3146
|
const methodName = propText.replace(/['"]/g, '');
|
|
2654
3147
|
if (methodName) {
|
|
2655
3148
|
const receiver = extractReceiverName(obj);
|
|
2656
|
-
return {
|
|
3149
|
+
return {
|
|
3150
|
+
name: methodName,
|
|
3151
|
+
line: callLine,
|
|
3152
|
+
dynamic: true,
|
|
3153
|
+
dynamicKind: 'computed-literal',
|
|
3154
|
+
receiver,
|
|
3155
|
+
};
|
|
2657
3156
|
}
|
|
2658
3157
|
}
|
|
2659
3158
|
|
|
@@ -2661,7 +3160,7 @@ function extractMemberExprCallInfo(fn: TreeSitterNode, callNode: TreeSitterNode)
|
|
|
2661
3160
|
return { name: propText, line: callLine, receiver };
|
|
2662
3161
|
}
|
|
2663
3162
|
|
|
2664
|
-
/** Extract call info from a subscript_expression function node (obj[
|
|
3163
|
+
/** Extract call info from a subscript_expression function node (obj[key]()). */
|
|
2665
3164
|
function extractSubscriptCallInfo(fn: TreeSitterNode, callNode: TreeSitterNode): Call | null {
|
|
2666
3165
|
const obj = fn.childForFieldName('object');
|
|
2667
3166
|
const index = fn.childForFieldName('index');
|
|
@@ -2676,11 +3175,32 @@ function extractSubscriptCallInfo(fn: TreeSitterNode, callNode: TreeSitterNode):
|
|
|
2676
3175
|
name: methodName,
|
|
2677
3176
|
line: nodeStartLine(callNode),
|
|
2678
3177
|
dynamic: true,
|
|
3178
|
+
dynamicKind: 'computed-literal',
|
|
2679
3179
|
receiver,
|
|
2680
3180
|
};
|
|
2681
3181
|
}
|
|
2682
3182
|
}
|
|
2683
|
-
|
|
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
|
+
};
|
|
2684
3204
|
}
|
|
2685
3205
|
|
|
2686
3206
|
/**
|
|
@@ -2999,6 +3519,10 @@ function runCollectorWalk(rootNode: TreeSitterNode, targets: CollectorWalkTarget
|
|
|
2999
3519
|
if (name) targets.newExpressions.push(name);
|
|
3000
3520
|
break;
|
|
3001
3521
|
}
|
|
3522
|
+
case 'decorator': {
|
|
3523
|
+
if (targets.calls) handleDecorator(node, targets.calls);
|
|
3524
|
+
break;
|
|
3525
|
+
}
|
|
3002
3526
|
case 'field_definition':
|
|
3003
3527
|
case 'public_field_definition':
|
|
3004
3528
|
if (targets.classMemberDefs) handleFieldDef(node, targets.classMemberDefs);
|
|
@@ -3308,11 +3832,13 @@ function emitPrototypeMethod(
|
|
|
3308
3832
|
): void {
|
|
3309
3833
|
const fullName = `${className}.${methodName}`;
|
|
3310
3834
|
if (rhs.type === 'function_expression' || rhs.type === 'arrow_function') {
|
|
3835
|
+
const params = extractParameters(rhs);
|
|
3311
3836
|
definitions.push({
|
|
3312
3837
|
name: fullName,
|
|
3313
3838
|
kind: 'method',
|
|
3314
3839
|
line: nodeStartLine(rhs),
|
|
3315
3840
|
endLine: nodeEndLine(rhs),
|
|
3841
|
+
children: params.length > 0 ? params : undefined,
|
|
3316
3842
|
});
|
|
3317
3843
|
} else if (rhs.type === 'identifier' && !BUILTIN_GLOBALS.has(rhs.text)) {
|
|
3318
3844
|
// Prototype alias: `A.prototype.t = f` → typeMap['A.t'] = { type: 'f' }
|