cgraphx 2.0.1 → 2.0.3
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/dist/.claude-template/FLOW-MAP.md +7 -5
- package/dist/.claude-template/commands/end.md +1 -1
- package/dist/.claude-template/skills/cgraphx-guide/SKILL.md +2 -1
- package/dist/.claude-template/skills/cgraphx-guide/how-to-use.html +4 -2
- package/dist/.claude-template/skills/cgraphx-guide/how-to-use.md +5 -2
- package/dist/.claude-template/skills/iboc-check/SKILL.md +116 -0
- package/dist/.claude-template/skills/iboc-check/filler-prompt.md +75 -0
- package/dist/.claude-template/skills/implementation/SKILL.md +7 -0
- package/dist/.claude-template/skills/init-project-guides/SKILL.md +1 -1
- package/dist/.claude-template/skills/run-api-test/references/db-verification.md +2 -2
- package/dist/.claude-template/skills/subagent-implement/SKILL.md +3 -2
- package/dist/.claude-template/skills/write-api/assets/template-request.bru +3 -3
- package/dist/.claude-template/skills/write-api/references/bru-format.md +6 -6
- package/dist/.claude-template/skills/write-delivery-guide/SKILL.md +97 -0
- package/dist/.claude-template/skills/write-delivery-guide/template-change-type.md +120 -0
- package/dist/.claude-template/skills/write-delivery-guide/template-multi-role-flow.md +148 -0
- package/dist/.claude-template/skills/write-unit-test-code/SKILL.md +23 -6
- package/dist/core/code/engine/cli/analyze-config.js +320 -0
- package/dist/core/code/engine/cli/analyze.js +1086 -0
- package/dist/core/code/engine/cli/cli-message.js +83 -0
- package/dist/core/code/engine/cli/detect-changes-format.js +58 -0
- package/dist/core/code/engine/cli/embedding-dims.js +43 -0
- package/dist/core/code/engine/cli/format-elapsed.js +12 -0
- package/dist/core/code/engine/cli/help-i18n.js +144 -0
- package/dist/core/code/engine/cli/i18n/en.js +110 -0
- package/dist/core/code/engine/cli/i18n/index.js +47 -0
- package/dist/core/code/engine/cli/i18n/resources.js +9 -0
- package/dist/core/code/engine/cli/i18n/zh-CN.js +110 -0
- package/dist/core/code/engine/cli/lazy-action.js +67 -0
- package/dist/core/code/engine/cli/optional-grammars.js +136 -0
- package/dist/core/code/engine/cli/resolve-invocation.js +76 -0
- package/dist/core/code/engine/cli/status.js +156 -0
- package/dist/core/code/engine/cli/tool.js +384 -0
- package/dist/core/code/engine/config/ignore-service.js +511 -0
- package/dist/core/code/engine/config/supported-languages.js +17 -0
- package/dist/core/code/engine/core/analysis-features.js +64 -0
- package/dist/core/code/engine/core/analyzer-identity.js +2171 -0
- package/dist/core/code/engine/core/git-staleness.js +180 -0
- package/dist/core/code/engine/core/graph/graph.js +180 -0
- package/dist/core/code/engine/core/graph/import-cycles.js +106 -0
- package/dist/core/code/engine/core/graph/types.js +2 -0
- package/dist/core/code/engine/core/index-freshness.js +12 -0
- package/dist/core/code/engine/core/ingestion/binding-accumulator.js +341 -0
- package/dist/core/code/engine/core/ingestion/call-extractors/configs/c-cpp.js +168 -0
- package/dist/core/code/engine/core/ingestion/call-extractors/configs/csharp.js +9 -0
- package/dist/core/code/engine/core/ingestion/call-extractors/configs/dart.js +8 -0
- package/dist/core/code/engine/core/ingestion/call-extractors/configs/go.js +8 -0
- package/dist/core/code/engine/core/ingestion/call-extractors/configs/jvm.js +54 -0
- package/dist/core/code/engine/core/ingestion/call-extractors/configs/php.js +8 -0
- package/dist/core/code/engine/core/ingestion/call-extractors/configs/python.js +8 -0
- package/dist/core/code/engine/core/ingestion/call-extractors/configs/ruby.js +8 -0
- package/dist/core/code/engine/core/ingestion/call-extractors/configs/rust.js +8 -0
- package/dist/core/code/engine/core/ingestion/call-extractors/configs/swift.js +8 -0
- package/dist/core/code/engine/core/ingestion/call-extractors/configs/typescript-javascript.js +11 -0
- package/dist/core/code/engine/core/ingestion/call-extractors/generic.js +62 -0
- package/dist/core/code/engine/core/ingestion/call-processor.js +503 -0
- package/dist/core/code/engine/core/ingestion/call-routing.js +98 -0
- package/dist/core/code/engine/core/ingestion/call-types.js +3 -0
- package/dist/core/code/engine/core/ingestion/cfg/callee-cell-format.js +45 -0
- package/dist/core/code/engine/core/ingestion/cfg/cfg-builder.js +202 -0
- package/dist/core/code/engine/core/ingestion/cfg/collect.js +81 -0
- package/dist/core/code/engine/core/ingestion/cfg/control-dependence.js +185 -0
- package/dist/core/code/engine/core/ingestion/cfg/control-flow-context.js +130 -0
- package/dist/core/code/engine/core/ingestion/cfg/emit.js +646 -0
- package/dist/core/code/engine/core/ingestion/cfg/post-dominators.js +182 -0
- package/dist/core/code/engine/core/ingestion/cfg/reaching-def-reason-codec.js +139 -0
- package/dist/core/code/engine/core/ingestion/cfg/reaching-defs-graph.js +322 -0
- package/dist/core/code/engine/core/ingestion/cfg/reaching-defs.js +792 -0
- package/dist/core/code/engine/core/ingestion/cfg/synthetic-escape.js +305 -0
- package/dist/core/code/engine/core/ingestion/cfg/traversal-result.js +6 -0
- package/dist/core/code/engine/core/ingestion/cfg/types.js +14 -0
- package/dist/core/code/engine/core/ingestion/cfg/visitors/c-cpp-harvest.js +545 -0
- package/dist/core/code/engine/core/ingestion/cfg/visitors/c-cpp.js +590 -0
- package/dist/core/code/engine/core/ingestion/cfg/visitors/call-site-harvest.js +356 -0
- package/dist/core/code/engine/core/ingestion/cfg/visitors/csharp-harvest.js +593 -0
- package/dist/core/code/engine/core/ingestion/cfg/visitors/csharp.js +871 -0
- package/dist/core/code/engine/core/ingestion/cfg/visitors/dart-harvest.js +874 -0
- package/dist/core/code/engine/core/ingestion/cfg/visitors/dart.js +840 -0
- package/dist/core/code/engine/core/ingestion/cfg/visitors/go-harvest.js +625 -0
- package/dist/core/code/engine/core/ingestion/cfg/visitors/go.js +642 -0
- package/dist/core/code/engine/core/ingestion/cfg/visitors/java-harvest.js +517 -0
- package/dist/core/code/engine/core/ingestion/cfg/visitors/java.js +816 -0
- package/dist/core/code/engine/core/ingestion/cfg/visitors/kotlin-harvest.js +723 -0
- package/dist/core/code/engine/core/ingestion/cfg/visitors/kotlin.js +813 -0
- package/dist/core/code/engine/core/ingestion/cfg/visitors/php-harvest.js +630 -0
- package/dist/core/code/engine/core/ingestion/cfg/visitors/php.js +725 -0
- package/dist/core/code/engine/core/ingestion/cfg/visitors/python-harvest.js +776 -0
- package/dist/core/code/engine/core/ingestion/cfg/visitors/python.js +562 -0
- package/dist/core/code/engine/core/ingestion/cfg/visitors/ruby-harvest.js +591 -0
- package/dist/core/code/engine/core/ingestion/cfg/visitors/ruby.js +760 -0
- package/dist/core/code/engine/core/ingestion/cfg/visitors/rust-harvest.js +877 -0
- package/dist/core/code/engine/core/ingestion/cfg/visitors/rust.js +562 -0
- package/dist/core/code/engine/core/ingestion/cfg/visitors/scope-tree-harvest.js +120 -0
- package/dist/core/code/engine/core/ingestion/cfg/visitors/swift-harvest.js +683 -0
- package/dist/core/code/engine/core/ingestion/cfg/visitors/swift.js +791 -0
- package/dist/core/code/engine/core/ingestion/cfg/visitors/typescript-harvest.js +1060 -0
- package/dist/core/code/engine/core/ingestion/cfg/visitors/typescript.js +587 -0
- package/dist/core/code/engine/core/ingestion/class-extractors/configs/c-cpp.js +77 -0
- package/dist/core/code/engine/core/ingestion/class-extractors/configs/csharp.js +24 -0
- package/dist/core/code/engine/core/ingestion/class-extractors/configs/dart.js +10 -0
- package/dist/core/code/engine/core/ingestion/class-extractors/configs/go.js +28 -0
- package/dist/core/code/engine/core/ingestion/class-extractors/configs/jvm.js +67 -0
- package/dist/core/code/engine/core/ingestion/class-extractors/configs/php.js +10 -0
- package/dist/core/code/engine/core/ingestion/class-extractors/configs/python.js +10 -0
- package/dist/core/code/engine/core/ingestion/class-extractors/configs/ruby.js +13 -0
- package/dist/core/code/engine/core/ingestion/class-extractors/configs/rust.js +10 -0
- package/dist/core/code/engine/core/ingestion/class-extractors/configs/swift.js +21 -0
- package/dist/core/code/engine/core/ingestion/class-extractors/configs/typescript-javascript.js +31 -0
- package/dist/core/code/engine/core/ingestion/class-extractors/generic.js +144 -0
- package/dist/core/code/engine/core/ingestion/class-types.js +2 -0
- package/dist/core/code/engine/core/ingestion/cluster-enricher.js +174 -0
- package/dist/core/code/engine/core/ingestion/community-processor.js +604 -0
- package/dist/core/code/engine/core/ingestion/constants.js +26 -0
- package/dist/core/code/engine/core/ingestion/cpp-ue-preprocessor.js +263 -0
- package/dist/core/code/engine/core/ingestion/csharp-namespace-gate.js +133 -0
- package/dist/core/code/engine/core/ingestion/di-extractors/index.js +38 -0
- package/dist/core/code/engine/core/ingestion/di-extractors/spring.js +310 -0
- package/dist/core/code/engine/core/ingestion/emit-references.js +244 -0
- package/dist/core/code/engine/core/ingestion/entry-point-scoring.js +201 -0
- package/dist/core/code/engine/core/ingestion/export-detection.js +244 -0
- package/dist/core/code/engine/core/ingestion/field-extractor.js +29 -0
- package/dist/core/code/engine/core/ingestion/field-extractors/configs/c-cpp.js +107 -0
- package/dist/core/code/engine/core/ingestion/field-extractors/configs/csharp.js +124 -0
- package/dist/core/code/engine/core/ingestion/field-extractors/configs/dart.js +99 -0
- package/dist/core/code/engine/core/ingestion/field-extractors/configs/go.js +102 -0
- package/dist/core/code/engine/core/ingestion/field-extractors/configs/helpers.js +198 -0
- package/dist/core/code/engine/core/ingestion/field-extractors/configs/jvm.js +172 -0
- package/dist/core/code/engine/core/ingestion/field-extractors/configs/php.js +67 -0
- package/dist/core/code/engine/core/ingestion/field-extractors/configs/python.js +94 -0
- package/dist/core/code/engine/core/ingestion/field-extractors/configs/ruby.js +79 -0
- package/dist/core/code/engine/core/ingestion/field-extractors/configs/rust.js +55 -0
- package/dist/core/code/engine/core/ingestion/field-extractors/configs/swift.js +93 -0
- package/dist/core/code/engine/core/ingestion/field-extractors/configs/typescript-javascript.js +59 -0
- package/dist/core/code/engine/core/ingestion/field-extractors/generic.js +147 -0
- package/dist/core/code/engine/core/ingestion/field-extractors/typescript.js +266 -0
- package/dist/core/code/engine/core/ingestion/field-types.js +3 -0
- package/dist/core/code/engine/core/ingestion/filesystem-walker.js +136 -0
- package/dist/core/code/engine/core/ingestion/finalize-orchestrator.js +159 -0
- package/dist/core/code/engine/core/ingestion/framework-detection.js +432 -0
- package/dist/core/code/engine/core/ingestion/frameworks/spring/analysis-features.js +38 -0
- package/dist/core/code/engine/core/ingestion/frameworks/spring/annotation-arguments.js +234 -0
- package/dist/core/code/engine/core/ingestion/frameworks/spring/aop-candidates.js +88 -0
- package/dist/core/code/engine/core/ingestion/frameworks/spring/aop.js +487 -0
- package/dist/core/code/engine/core/ingestion/frameworks/spring/auto-configuration.js +21 -0
- package/dist/core/code/engine/core/ingestion/frameworks/spring/bean-candidates.js +189 -0
- package/dist/core/code/engine/core/ingestion/frameworks/spring/bean-catalog.js +32 -0
- package/dist/core/code/engine/core/ingestion/frameworks/spring/bean-factories.js +73 -0
- package/dist/core/code/engine/core/ingestion/frameworks/spring/conditionals.js +323 -0
- package/dist/core/code/engine/core/ingestion/frameworks/spring/config-bindings.js +119 -0
- package/dist/core/code/engine/core/ingestion/frameworks/spring/di-metadata.js +385 -0
- package/dist/core/code/engine/core/ingestion/frameworks/spring/resource-injection.js +96 -0
- package/dist/core/code/engine/core/ingestion/import-resolvers/configs/c-cpp.js +17 -0
- package/dist/core/code/engine/core/ingestion/import-resolvers/configs/csharp.js +46 -0
- package/dist/core/code/engine/core/ingestion/import-resolvers/configs/dart.js +59 -0
- package/dist/core/code/engine/core/ingestion/import-resolvers/configs/go.js +30 -0
- package/dist/core/code/engine/core/ingestion/import-resolvers/configs/jvm.js +73 -0
- package/dist/core/code/engine/core/ingestion/import-resolvers/configs/php.js +19 -0
- package/dist/core/code/engine/core/ingestion/import-resolvers/configs/python.js +45 -0
- package/dist/core/code/engine/core/ingestion/import-resolvers/configs/ruby.js +20 -0
- package/dist/core/code/engine/core/ingestion/import-resolvers/configs/rust.js +58 -0
- package/dist/core/code/engine/core/ingestion/import-resolvers/configs/swift.js +94 -0
- package/dist/core/code/engine/core/ingestion/import-resolvers/configs/typescript-javascript.js +26 -0
- package/dist/core/code/engine/core/ingestion/import-resolvers/csharp.js +128 -0
- package/dist/core/code/engine/core/ingestion/import-resolvers/go.js +50 -0
- package/dist/core/code/engine/core/ingestion/import-resolvers/jvm.js +112 -0
- package/dist/core/code/engine/core/ingestion/import-resolvers/php.js +80 -0
- package/dist/core/code/engine/core/ingestion/import-resolvers/python.js +75 -0
- package/dist/core/code/engine/core/ingestion/import-resolvers/resolver-factory.js +36 -0
- package/dist/core/code/engine/core/ingestion/import-resolvers/ruby.js +20 -0
- package/dist/core/code/engine/core/ingestion/import-resolvers/rust.js +79 -0
- package/dist/core/code/engine/core/ingestion/import-resolvers/standard.js +180 -0
- package/dist/core/code/engine/core/ingestion/import-resolvers/types.js +7 -0
- package/dist/core/code/engine/core/ingestion/import-resolvers/utils.js +153 -0
- package/dist/core/code/engine/core/ingestion/import-target-adapter.js +99 -0
- package/dist/core/code/engine/core/ingestion/language-config.js +391 -0
- package/dist/core/code/engine/core/ingestion/language-provider.js +25 -0
- package/dist/core/code/engine/core/ingestion/languages/c/arity-metadata.js +98 -0
- package/dist/core/code/engine/core/ingestion/languages/c/arity.js +21 -0
- package/dist/core/code/engine/core/ingestion/languages/c/capture-side-channel.js +69 -0
- package/dist/core/code/engine/core/ingestion/languages/c/captures.js +189 -0
- package/dist/core/code/engine/core/ingestion/languages/c/header-scan.js +58 -0
- package/dist/core/code/engine/core/ingestion/languages/c/import-decomposer.js +68 -0
- package/dist/core/code/engine/core/ingestion/languages/c/import-target.js +103 -0
- package/dist/core/code/engine/core/ingestion/languages/c/index.js +33 -0
- package/dist/core/code/engine/core/ingestion/languages/c/interpret.js +53 -0
- package/dist/core/code/engine/core/ingestion/languages/c/merge-bindings.js +26 -0
- package/dist/core/code/engine/core/ingestion/languages/c/query.js +210 -0
- package/dist/core/code/engine/core/ingestion/languages/c/scope-resolver.js +112 -0
- package/dist/core/code/engine/core/ingestion/languages/c/simple-hooks.js +24 -0
- package/dist/core/code/engine/core/ingestion/languages/c/static-linkage.js +109 -0
- package/dist/core/code/engine/core/ingestion/languages/c-cpp.js +506 -0
- package/dist/core/code/engine/core/ingestion/languages/cpp/adl.js +804 -0
- package/dist/core/code/engine/core/ingestion/languages/cpp/arity-metadata.js +258 -0
- package/dist/core/code/engine/core/ingestion/languages/cpp/arity.js +37 -0
- package/dist/core/code/engine/core/ingestion/languages/cpp/capture-side-channel.js +89 -0
- package/dist/core/code/engine/core/ingestion/languages/cpp/captures.js +1975 -0
- package/dist/core/code/engine/core/ingestion/languages/cpp/constraint-extractor.js +311 -0
- package/dist/core/code/engine/core/ingestion/languages/cpp/constraint-filter.js +210 -0
- package/dist/core/code/engine/core/ingestion/languages/cpp/conversion-rank.js +163 -0
- package/dist/core/code/engine/core/ingestion/languages/cpp/file-local-linkage.js +327 -0
- package/dist/core/code/engine/core/ingestion/languages/cpp/header-scan.js +53 -0
- package/dist/core/code/engine/core/ingestion/languages/cpp/import-decomposer.js +134 -0
- package/dist/core/code/engine/core/ingestion/languages/cpp/import-target.js +16 -0
- package/dist/core/code/engine/core/ingestion/languages/cpp/index.js +33 -0
- package/dist/core/code/engine/core/ingestion/languages/cpp/inline-namespaces.js +379 -0
- package/dist/core/code/engine/core/ingestion/languages/cpp/interpret.js +239 -0
- package/dist/core/code/engine/core/ingestion/languages/cpp/member-lookup.js +470 -0
- package/dist/core/code/engine/core/ingestion/languages/cpp/merge-bindings.js +32 -0
- package/dist/core/code/engine/core/ingestion/languages/cpp/query.js +763 -0
- package/dist/core/code/engine/core/ingestion/languages/cpp/range-bindings.js +230 -0
- package/dist/core/code/engine/core/ingestion/languages/cpp/scope-resolver.js +354 -0
- package/dist/core/code/engine/core/ingestion/languages/cpp/simple-hooks.js +67 -0
- package/dist/core/code/engine/core/ingestion/languages/cpp/two-phase-lookup.js +348 -0
- package/dist/core/code/engine/core/ingestion/languages/cpp/type-classifier.js +56 -0
- package/dist/core/code/engine/core/ingestion/languages/cpp/user-defined-conversions.js +128 -0
- package/dist/core/code/engine/core/ingestion/languages/csharp/accessor-unwrap.js +67 -0
- package/dist/core/code/engine/core/ingestion/languages/csharp/arity-metadata.js +49 -0
- package/dist/core/code/engine/core/ingestion/languages/csharp/arity.js +40 -0
- package/dist/core/code/engine/core/ingestion/languages/csharp/cache-stats.js +32 -0
- package/dist/core/code/engine/core/ingestion/languages/csharp/captures.js +557 -0
- package/dist/core/code/engine/core/ingestion/languages/csharp/import-decomposer.js +96 -0
- package/dist/core/code/engine/core/ingestion/languages/csharp/import-target.js +176 -0
- package/dist/core/code/engine/core/ingestion/languages/csharp/index.js +95 -0
- package/dist/core/code/engine/core/ingestion/languages/csharp/interpret.js +150 -0
- package/dist/core/code/engine/core/ingestion/languages/csharp/merge-bindings.js +58 -0
- package/dist/core/code/engine/core/ingestion/languages/csharp/namespace-siblings.js +708 -0
- package/dist/core/code/engine/core/ingestion/languages/csharp/qualified-type-names.js +62 -0
- package/dist/core/code/engine/core/ingestion/languages/csharp/query.js +578 -0
- package/dist/core/code/engine/core/ingestion/languages/csharp/receiver-binding.js +142 -0
- package/dist/core/code/engine/core/ingestion/languages/csharp/resolution-config.js +18 -0
- package/dist/core/code/engine/core/ingestion/languages/csharp/scope-resolver.js +84 -0
- package/dist/core/code/engine/core/ingestion/languages/csharp/simple-hooks.js +81 -0
- package/dist/core/code/engine/core/ingestion/languages/csharp.js +204 -0
- package/dist/core/code/engine/core/ingestion/languages/dart/arity-metadata.js +38 -0
- package/dist/core/code/engine/core/ingestion/languages/dart/arity.js +34 -0
- package/dist/core/code/engine/core/ingestion/languages/dart/built-ins.js +37 -0
- package/dist/core/code/engine/core/ingestion/languages/dart/cache-stats.js +30 -0
- package/dist/core/code/engine/core/ingestion/languages/dart/captures.js +1096 -0
- package/dist/core/code/engine/core/ingestion/languages/dart/expand-wildcards.js +34 -0
- package/dist/core/code/engine/core/ingestion/languages/dart/extension-type-preprocess.js +33 -0
- package/dist/core/code/engine/core/ingestion/languages/dart/import-target.js +68 -0
- package/dist/core/code/engine/core/ingestion/languages/dart/index.js +45 -0
- package/dist/core/code/engine/core/ingestion/languages/dart/interpret.js +101 -0
- package/dist/core/code/engine/core/ingestion/languages/dart/merge-bindings.js +42 -0
- package/dist/core/code/engine/core/ingestion/languages/dart/query.js +246 -0
- package/dist/core/code/engine/core/ingestion/languages/dart/receiver-binding.js +90 -0
- package/dist/core/code/engine/core/ingestion/languages/dart/scope-resolver.js +197 -0
- package/dist/core/code/engine/core/ingestion/languages/dart/signature-bindings.js +54 -0
- package/dist/core/code/engine/core/ingestion/languages/dart/simple-hooks.js +61 -0
- package/dist/core/code/engine/core/ingestion/languages/dart.js +138 -0
- package/dist/core/code/engine/core/ingestion/languages/go/arity-metadata.js +71 -0
- package/dist/core/code/engine/core/ingestion/languages/go/arity.js +17 -0
- package/dist/core/code/engine/core/ingestion/languages/go/cache-stats.js +21 -0
- package/dist/core/code/engine/core/ingestion/languages/go/captures.js +492 -0
- package/dist/core/code/engine/core/ingestion/languages/go/expand-wildcards.js +97 -0
- package/dist/core/code/engine/core/ingestion/languages/go/generic-type-parameters.js +146 -0
- package/dist/core/code/engine/core/ingestion/languages/go/import-decomposer.js +47 -0
- package/dist/core/code/engine/core/ingestion/languages/go/import-target.js +70 -0
- package/dist/core/code/engine/core/ingestion/languages/go/index.js +39 -0
- package/dist/core/code/engine/core/ingestion/languages/go/interface-impls.js +955 -0
- package/dist/core/code/engine/core/ingestion/languages/go/interpret.js +177 -0
- package/dist/core/code/engine/core/ingestion/languages/go/merge-bindings.js +21 -0
- package/dist/core/code/engine/core/ingestion/languages/go/method-owners.js +131 -0
- package/dist/core/code/engine/core/ingestion/languages/go/namespace-mirror.js +56 -0
- package/dist/core/code/engine/core/ingestion/languages/go/package-clause.js +79 -0
- package/dist/core/code/engine/core/ingestion/languages/go/package-siblings.js +83 -0
- package/dist/core/code/engine/core/ingestion/languages/go/query.js +298 -0
- package/dist/core/code/engine/core/ingestion/languages/go/range-binding.js +127 -0
- package/dist/core/code/engine/core/ingestion/languages/go/receiver-binding.js +24 -0
- package/dist/core/code/engine/core/ingestion/languages/go/scope-resolver.js +75 -0
- package/dist/core/code/engine/core/ingestion/languages/go/simple-hooks.js +31 -0
- package/dist/core/code/engine/core/ingestion/languages/go/type-binding.js +279 -0
- package/dist/core/code/engine/core/ingestion/languages/go.js +160 -0
- package/dist/core/code/engine/core/ingestion/languages/index.js +66 -0
- package/dist/core/code/engine/core/ingestion/languages/java/analysis-features.js +16 -0
- package/dist/core/code/engine/core/ingestion/languages/java/arity-metadata.js +43 -0
- package/dist/core/code/engine/core/ingestion/languages/java/arity.js +27 -0
- package/dist/core/code/engine/core/ingestion/languages/java/cache-stats.js +32 -0
- package/dist/core/code/engine/core/ingestion/languages/java/capture-side-channel.js +123 -0
- package/dist/core/code/engine/core/ingestion/languages/java/captures.js +791 -0
- package/dist/core/code/engine/core/ingestion/languages/java/import-decomposer.js +88 -0
- package/dist/core/code/engine/core/ingestion/languages/java/import-target.js +103 -0
- package/dist/core/code/engine/core/ingestion/languages/java/index.js +43 -0
- package/dist/core/code/engine/core/ingestion/languages/java/interpret.js +146 -0
- package/dist/core/code/engine/core/ingestion/languages/java/merge-bindings.js +43 -0
- package/dist/core/code/engine/core/ingestion/languages/java/package-facts.js +16 -0
- package/dist/core/code/engine/core/ingestion/languages/java/package-siblings.js +11 -0
- package/dist/core/code/engine/core/ingestion/languages/java/query.js +319 -0
- package/dist/core/code/engine/core/ingestion/languages/java/receiver-binding.js +98 -0
- package/dist/core/code/engine/core/ingestion/languages/java/scope-resolver.js +213 -0
- package/dist/core/code/engine/core/ingestion/languages/java/simple-hooks.js +39 -0
- package/dist/core/code/engine/core/ingestion/languages/java/spring-aop.js +53 -0
- package/dist/core/code/engine/core/ingestion/languages/java/spring-bean-metadata.js +11 -0
- package/dist/core/code/engine/core/ingestion/languages/java/spring-conditionals.js +52 -0
- package/dist/core/code/engine/core/ingestion/languages/java/spring-config-bindings.js +222 -0
- package/dist/core/code/engine/core/ingestion/languages/java/spring-di.js +165 -0
- package/dist/core/code/engine/core/ingestion/languages/java.js +192 -0
- package/dist/core/code/engine/core/ingestion/languages/javascript/arity.js +15 -0
- package/dist/core/code/engine/core/ingestion/languages/javascript/captures.js +1122 -0
- package/dist/core/code/engine/core/ingestion/languages/javascript/import-target.js +56 -0
- package/dist/core/code/engine/core/ingestion/languages/javascript/index.js +109 -0
- package/dist/core/code/engine/core/ingestion/languages/javascript/interpret.js +45 -0
- package/dist/core/code/engine/core/ingestion/languages/javascript/merge-bindings.js +21 -0
- package/dist/core/code/engine/core/ingestion/languages/javascript/query.js +659 -0
- package/dist/core/code/engine/core/ingestion/languages/javascript/scope-resolver.js +78 -0
- package/dist/core/code/engine/core/ingestion/languages/javascript/simple-hooks.js +44 -0
- package/dist/core/code/engine/core/ingestion/languages/jvm/package-facts.js +46 -0
- package/dist/core/code/engine/core/ingestion/languages/jvm/package-siblings.js +200 -0
- package/dist/core/code/engine/core/ingestion/languages/kotlin/arity-metadata.js +23 -0
- package/dist/core/code/engine/core/ingestion/languages/kotlin/arity.js +18 -0
- package/dist/core/code/engine/core/ingestion/languages/kotlin/cache-stats.js +21 -0
- package/dist/core/code/engine/core/ingestion/languages/kotlin/capture-side-channel.js +160 -0
- package/dist/core/code/engine/core/ingestion/languages/kotlin/captures.js +1262 -0
- package/dist/core/code/engine/core/ingestion/languages/kotlin/companion-scopes.js +72 -0
- package/dist/core/code/engine/core/ingestion/languages/kotlin/import-decomposer.js +40 -0
- package/dist/core/code/engine/core/ingestion/languages/kotlin/import-target.js +135 -0
- package/dist/core/code/engine/core/ingestion/languages/kotlin/index.js +26 -0
- package/dist/core/code/engine/core/ingestion/languages/kotlin/interpret.js +75 -0
- package/dist/core/code/engine/core/ingestion/languages/kotlin/merge-bindings.js +28 -0
- package/dist/core/code/engine/core/ingestion/languages/kotlin/owners.js +134 -0
- package/dist/core/code/engine/core/ingestion/languages/kotlin/package-facts.js +16 -0
- package/dist/core/code/engine/core/ingestion/languages/kotlin/package-siblings.js +11 -0
- package/dist/core/code/engine/core/ingestion/languages/kotlin/query.js +243 -0
- package/dist/core/code/engine/core/ingestion/languages/kotlin/receiver-binding.js +103 -0
- package/dist/core/code/engine/core/ingestion/languages/kotlin/scope-resolver.js +207 -0
- package/dist/core/code/engine/core/ingestion/languages/kotlin/simple-hooks.js +42 -0
- package/dist/core/code/engine/core/ingestion/languages/kotlin/spring-aop.js +68 -0
- package/dist/core/code/engine/core/ingestion/languages/kotlin/spring-bean-metadata.js +11 -0
- package/dist/core/code/engine/core/ingestion/languages/kotlin/spring-conditionals.js +53 -0
- package/dist/core/code/engine/core/ingestion/languages/kotlin/spring-di.js +304 -0
- package/dist/core/code/engine/core/ingestion/languages/kotlin.js +189 -0
- package/dist/core/code/engine/core/ingestion/languages/php/arity-metadata.js +66 -0
- package/dist/core/code/engine/core/ingestion/languages/php/arity.js +43 -0
- package/dist/core/code/engine/core/ingestion/languages/php/cache-stats.js +32 -0
- package/dist/core/code/engine/core/ingestion/languages/php/captures.js +1166 -0
- package/dist/core/code/engine/core/ingestion/languages/php/import-decomposer.js +238 -0
- package/dist/core/code/engine/core/ingestion/languages/php/import-target.js +213 -0
- package/dist/core/code/engine/core/ingestion/languages/php/index.js +85 -0
- package/dist/core/code/engine/core/ingestion/languages/php/interpret.js +256 -0
- package/dist/core/code/engine/core/ingestion/languages/php/merge-bindings.js +50 -0
- package/dist/core/code/engine/core/ingestion/languages/php/namespace-siblings.js +353 -0
- package/dist/core/code/engine/core/ingestion/languages/php/query.js +391 -0
- package/dist/core/code/engine/core/ingestion/languages/php/receiver-binding.js +135 -0
- package/dist/core/code/engine/core/ingestion/languages/php/scope-resolver.js +361 -0
- package/dist/core/code/engine/core/ingestion/languages/php/simple-hooks.js +116 -0
- package/dist/core/code/engine/core/ingestion/languages/php.js +302 -0
- package/dist/core/code/engine/core/ingestion/languages/python/arity-metadata.js +49 -0
- package/dist/core/code/engine/core/ingestion/languages/python/arity.js +41 -0
- package/dist/core/code/engine/core/ingestion/languages/python/cache-stats.js +34 -0
- package/dist/core/code/engine/core/ingestion/languages/python/captures.js +299 -0
- package/dist/core/code/engine/core/ingestion/languages/python/depends-references.js +68 -0
- package/dist/core/code/engine/core/ingestion/languages/python/import-decomposer.js +115 -0
- package/dist/core/code/engine/core/ingestion/languages/python/import-target.js +440 -0
- package/dist/core/code/engine/core/ingestion/languages/python/index-stats.js +30 -0
- package/dist/core/code/engine/core/ingestion/languages/python/index.js +96 -0
- package/dist/core/code/engine/core/ingestion/languages/python/interpret.js +430 -0
- package/dist/core/code/engine/core/ingestion/languages/python/merge-bindings.js +47 -0
- package/dist/core/code/engine/core/ingestion/languages/python/query.js +323 -0
- package/dist/core/code/engine/core/ingestion/languages/python/receiver-binding.js +310 -0
- package/dist/core/code/engine/core/ingestion/languages/python/scope-resolver.js +80 -0
- package/dist/core/code/engine/core/ingestion/languages/python/simple-hooks.js +53 -0
- package/dist/core/code/engine/core/ingestion/languages/python.js +140 -0
- package/dist/core/code/engine/core/ingestion/languages/ruby/arity.js +41 -0
- package/dist/core/code/engine/core/ingestion/languages/ruby/cache-stats.js +21 -0
- package/dist/core/code/engine/core/ingestion/languages/ruby/captures.js +864 -0
- package/dist/core/code/engine/core/ingestion/languages/ruby/import-target.js +88 -0
- package/dist/core/code/engine/core/ingestion/languages/ruby/index.js +29 -0
- package/dist/core/code/engine/core/ingestion/languages/ruby/interpret.js +115 -0
- package/dist/core/code/engine/core/ingestion/languages/ruby/merge-bindings.js +21 -0
- package/dist/core/code/engine/core/ingestion/languages/ruby/query.js +349 -0
- package/dist/core/code/engine/core/ingestion/languages/ruby/receiver-binding.js +70 -0
- package/dist/core/code/engine/core/ingestion/languages/ruby/scope-resolver.js +263 -0
- package/dist/core/code/engine/core/ingestion/languages/ruby/simple-hooks.js +68 -0
- package/dist/core/code/engine/core/ingestion/languages/ruby.js +215 -0
- package/dist/core/code/engine/core/ingestion/languages/rust/arity.js +16 -0
- package/dist/core/code/engine/core/ingestion/languages/rust/cache-stats.js +21 -0
- package/dist/core/code/engine/core/ingestion/languages/rust/captures.js +304 -0
- package/dist/core/code/engine/core/ingestion/languages/rust/import-decomposer.js +167 -0
- package/dist/core/code/engine/core/ingestion/languages/rust/import-target.js +108 -0
- package/dist/core/code/engine/core/ingestion/languages/rust/index.js +29 -0
- package/dist/core/code/engine/core/ingestion/languages/rust/interpret.js +201 -0
- package/dist/core/code/engine/core/ingestion/languages/rust/merge-bindings.js +21 -0
- package/dist/core/code/engine/core/ingestion/languages/rust/method-owners.js +76 -0
- package/dist/core/code/engine/core/ingestion/languages/rust/module-path.js +222 -0
- package/dist/core/code/engine/core/ingestion/languages/rust/qualified-call.js +482 -0
- package/dist/core/code/engine/core/ingestion/languages/rust/query.js +280 -0
- package/dist/core/code/engine/core/ingestion/languages/rust/range-binding.js +687 -0
- package/dist/core/code/engine/core/ingestion/languages/rust/receiver-binding.js +148 -0
- package/dist/core/code/engine/core/ingestion/languages/rust/scope-resolver.js +151 -0
- package/dist/core/code/engine/core/ingestion/languages/rust/simple-hooks.js +32 -0
- package/dist/core/code/engine/core/ingestion/languages/rust.js +183 -0
- package/dist/core/code/engine/core/ingestion/languages/swift/arity-metadata.js +44 -0
- package/dist/core/code/engine/core/ingestion/languages/swift/arity.js +45 -0
- package/dist/core/code/engine/core/ingestion/languages/swift/base-type.js +30 -0
- package/dist/core/code/engine/core/ingestion/languages/swift/cache-stats.js +32 -0
- package/dist/core/code/engine/core/ingestion/languages/swift/captures.js +594 -0
- package/dist/core/code/engine/core/ingestion/languages/swift/conditional-directive-preprocess.js +256 -0
- package/dist/core/code/engine/core/ingestion/languages/swift/implicit-imports.js +60 -0
- package/dist/core/code/engine/core/ingestion/languages/swift/import-decomposer.js +87 -0
- package/dist/core/code/engine/core/ingestion/languages/swift/import-target.js +84 -0
- package/dist/core/code/engine/core/ingestion/languages/swift/index.js +56 -0
- package/dist/core/code/engine/core/ingestion/languages/swift/interpret.js +93 -0
- package/dist/core/code/engine/core/ingestion/languages/swift/merge-bindings.js +51 -0
- package/dist/core/code/engine/core/ingestion/languages/swift/query.js +226 -0
- package/dist/core/code/engine/core/ingestion/languages/swift/receiver-binding.js +169 -0
- package/dist/core/code/engine/core/ingestion/languages/swift/scope-resolver.js +192 -0
- package/dist/core/code/engine/core/ingestion/languages/swift/sibling-type-bindings.js +68 -0
- package/dist/core/code/engine/core/ingestion/languages/swift/signature-bindings.js +69 -0
- package/dist/core/code/engine/core/ingestion/languages/swift/simple-hooks.js +65 -0
- package/dist/core/code/engine/core/ingestion/languages/swift/target-grouping.js +97 -0
- package/dist/core/code/engine/core/ingestion/languages/swift/target-siblings.js +74 -0
- package/dist/core/code/engine/core/ingestion/languages/swift.js +246 -0
- package/dist/core/code/engine/core/ingestion/languages/typescript/arity-metadata.js +106 -0
- package/dist/core/code/engine/core/ingestion/languages/typescript/arity.js +57 -0
- package/dist/core/code/engine/core/ingestion/languages/typescript/array-callback.js +58 -0
- package/dist/core/code/engine/core/ingestion/languages/typescript/cache-stats.js +34 -0
- package/dist/core/code/engine/core/ingestion/languages/typescript/captures.js +956 -0
- package/dist/core/code/engine/core/ingestion/languages/typescript/cjs-export-assignment.js +535 -0
- package/dist/core/code/engine/core/ingestion/languages/typescript/cjs-module-exports.js +196 -0
- package/dist/core/code/engine/core/ingestion/languages/typescript/import-decomposer.js +374 -0
- package/dist/core/code/engine/core/ingestion/languages/typescript/import-target.js +65 -0
- package/dist/core/code/engine/core/ingestion/languages/typescript/index.js +108 -0
- package/dist/core/code/engine/core/ingestion/languages/typescript/interpret.js +344 -0
- package/dist/core/code/engine/core/ingestion/languages/typescript/merge-bindings.js +161 -0
- package/dist/core/code/engine/core/ingestion/languages/typescript/nuxt-auto-imports.js +325 -0
- package/dist/core/code/engine/core/ingestion/languages/typescript/query.js +1328 -0
- package/dist/core/code/engine/core/ingestion/languages/typescript/receiver-binding.js +201 -0
- package/dist/core/code/engine/core/ingestion/languages/typescript/scope-resolver.js +293 -0
- package/dist/core/code/engine/core/ingestion/languages/typescript/simple-hooks.js +139 -0
- package/dist/core/code/engine/core/ingestion/languages/typescript.js +455 -0
- package/dist/core/code/engine/core/ingestion/languages/vue/captures.js +70 -0
- package/dist/core/code/engine/core/ingestion/languages/vue/import-target.js +61 -0
- package/dist/core/code/engine/core/ingestion/languages/vue/index.js +55 -0
- package/dist/core/code/engine/core/ingestion/languages/vue/scope-resolver.js +295 -0
- package/dist/core/code/engine/core/ingestion/languages/vue.js +96 -0
- package/dist/core/code/engine/core/ingestion/local-symbol-pruner.js +68 -0
- package/dist/core/code/engine/core/ingestion/method-extractors/configs/c-cpp.js +387 -0
- package/dist/core/code/engine/core/ingestion/method-extractors/configs/csharp.js +290 -0
- package/dist/core/code/engine/core/ingestion/method-extractors/configs/dart.js +392 -0
- package/dist/core/code/engine/core/ingestion/method-extractors/configs/go.js +179 -0
- package/dist/core/code/engine/core/ingestion/method-extractors/configs/jvm.js +350 -0
- package/dist/core/code/engine/core/ingestion/method-extractors/configs/php.js +306 -0
- package/dist/core/code/engine/core/ingestion/method-extractors/configs/python.js +312 -0
- package/dist/core/code/engine/core/ingestion/method-extractors/configs/ruby.js +289 -0
- package/dist/core/code/engine/core/ingestion/method-extractors/configs/rust.js +198 -0
- package/dist/core/code/engine/core/ingestion/method-extractors/configs/swift.js +286 -0
- package/dist/core/code/engine/core/ingestion/method-extractors/configs/typescript-javascript.js +341 -0
- package/dist/core/code/engine/core/ingestion/method-extractors/generic.js +209 -0
- package/dist/core/code/engine/core/ingestion/method-types.js +3 -0
- package/dist/core/code/engine/core/ingestion/model/field-registry.js +41 -0
- package/dist/core/code/engine/core/ingestion/model/index.js +52 -0
- package/dist/core/code/engine/core/ingestion/model/method-registry.js +138 -0
- package/dist/core/code/engine/core/ingestion/model/owned-members-lookup.js +46 -0
- package/dist/core/code/engine/core/ingestion/model/registration-table.js +234 -0
- package/dist/core/code/engine/core/ingestion/model/resolve.js +183 -0
- package/dist/core/code/engine/core/ingestion/model/scope-resolution-indexes.js +43 -0
- package/dist/core/code/engine/core/ingestion/model/semantic-model.js +179 -0
- package/dist/core/code/engine/core/ingestion/model/symbol-table.js +216 -0
- package/dist/core/code/engine/core/ingestion/model/type-registry.js +84 -0
- package/dist/core/code/engine/core/ingestion/mro-processor.js +709 -0
- package/dist/core/code/engine/core/ingestion/parsing-processor.js +312 -0
- package/dist/core/code/engine/core/ingestion/pipeline-phases/communities.js +69 -0
- package/dist/core/code/engine/core/ingestion/pipeline-phases/cross-file.js +71 -0
- package/dist/core/code/engine/core/ingestion/pipeline-phases/di.js +338 -0
- package/dist/core/code/engine/core/ingestion/pipeline-phases/http-api-calls.js +312 -0
- package/dist/core/code/engine/core/ingestion/pipeline-phases/index.js +54 -0
- package/dist/core/code/engine/core/ingestion/pipeline-phases/mro.js +40 -0
- package/dist/core/code/engine/core/ingestion/pipeline-phases/orm.js +78 -0
- package/dist/core/code/engine/core/ingestion/pipeline-phases/parse-impl.js +1286 -0
- package/dist/core/code/engine/core/ingestion/pipeline-phases/parse.js +41 -0
- package/dist/core/code/engine/core/ingestion/pipeline-phases/processes.js +193 -0
- package/dist/core/code/engine/core/ingestion/pipeline-phases/prune-local-symbols.js +29 -0
- package/dist/core/code/engine/core/ingestion/pipeline-phases/registry.js +52 -0
- package/dist/core/code/engine/core/ingestion/pipeline-phases/routes.js +409 -0
- package/dist/core/code/engine/core/ingestion/pipeline-phases/rpc-edges.js +301 -0
- package/dist/core/code/engine/core/ingestion/pipeline-phases/runner.js +207 -0
- package/dist/core/code/engine/core/ingestion/pipeline-phases/scan.js +49 -0
- package/dist/core/code/engine/core/ingestion/pipeline-phases/spring-aop.js +442 -0
- package/dist/core/code/engine/core/ingestion/pipeline-phases/spring-auto-configuration.js +264 -0
- package/dist/core/code/engine/core/ingestion/pipeline-phases/spring-config.js +440 -0
- package/dist/core/code/engine/core/ingestion/pipeline-phases/structure.js +38 -0
- package/dist/core/code/engine/core/ingestion/pipeline-phases/tools.js +89 -0
- package/dist/core/code/engine/core/ingestion/pipeline-phases/types.js +40 -0
- package/dist/core/code/engine/core/ingestion/pipeline.js +152 -0
- package/dist/core/code/engine/core/ingestion/process-processor.js +325 -0
- package/dist/core/code/engine/core/ingestion/resolve-references.js +205 -0
- package/dist/core/code/engine/core/ingestion/route-extractors/constant-resolver.js +135 -0
- package/dist/core/code/engine/core/ingestion/route-extractors/django-root-discovery.js +221 -0
- package/dist/core/code/engine/core/ingestion/route-extractors/django.js +428 -0
- package/dist/core/code/engine/core/ingestion/route-extractors/expo.js +39 -0
- package/dist/core/code/engine/core/ingestion/route-extractors/fastapi-router-bindings.js +264 -0
- package/dist/core/code/engine/core/ingestion/route-extractors/laravel.js +501 -0
- package/dist/core/code/engine/core/ingestion/route-extractors/middleware.js +175 -0
- package/dist/core/code/engine/core/ingestion/route-extractors/nextjs.js +81 -0
- package/dist/core/code/engine/core/ingestion/route-extractors/php.js +25 -0
- package/dist/core/code/engine/core/ingestion/route-extractors/python-const-resolver.js +307 -0
- package/dist/core/code/engine/core/ingestion/route-extractors/response-shapes.js +299 -0
- package/dist/core/code/engine/core/ingestion/route-extractors/route-path.js +71 -0
- package/dist/core/code/engine/core/ingestion/route-extractors/spring-shared.js +310 -0
- package/dist/core/code/engine/core/ingestion/route-extractors/spring.js +441 -0
- package/dist/core/code/engine/core/ingestion/scope-extractor-bridge.js +60 -0
- package/dist/core/code/engine/core/ingestion/scope-extractor.js +1373 -0
- package/dist/core/code/engine/core/ingestion/scope-resolution/contract/scope-resolver.js +282 -0
- package/dist/core/code/engine/core/ingestion/scope-resolution/graph-bridge/callee-id-sink.js +72 -0
- package/dist/core/code/engine/core/ingestion/scope-resolution/graph-bridge/edges.js +194 -0
- package/dist/core/code/engine/core/ingestion/scope-resolution/graph-bridge/ids.js +472 -0
- package/dist/core/code/engine/core/ingestion/scope-resolution/graph-bridge/imports-to-edges.js +49 -0
- package/dist/core/code/engine/core/ingestion/scope-resolution/graph-bridge/method-dispatch.js +43 -0
- package/dist/core/code/engine/core/ingestion/scope-resolution/graph-bridge/node-lookup.js +274 -0
- package/dist/core/code/engine/core/ingestion/scope-resolution/graph-bridge/references-to-edges.js +93 -0
- package/dist/core/code/engine/core/ingestion/scope-resolution/passes/callable-value-flow.js +1240 -0
- package/dist/core/code/engine/core/ingestion/scope-resolution/passes/compound-receiver.js +1174 -0
- package/dist/core/code/engine/core/ingestion/scope-resolution/passes/free-call-fallback.js +873 -0
- package/dist/core/code/engine/core/ingestion/scope-resolution/passes/imported-return-types.js +226 -0
- package/dist/core/code/engine/core/ingestion/scope-resolution/passes/mro.js +107 -0
- package/dist/core/code/engine/core/ingestion/scope-resolution/passes/overload-narrowing.js +441 -0
- package/dist/core/code/engine/core/ingestion/scope-resolution/passes/property-dispatch.js +122 -0
- package/dist/core/code/engine/core/ingestion/scope-resolution/passes/receiver-bound-calls.js +1720 -0
- package/dist/core/code/engine/core/ingestion/scope-resolution/pipeline/phase.js +395 -0
- package/dist/core/code/engine/core/ingestion/scope-resolution/pipeline/reconcile-ownership.js +208 -0
- package/dist/core/code/engine/core/ingestion/scope-resolution/pipeline/registry.js +49 -0
- package/dist/core/code/engine/core/ingestion/scope-resolution/pipeline/run.js +632 -0
- package/dist/core/code/engine/core/ingestion/scope-resolution/pipeline/validate-bindings-immutability.js +112 -0
- package/dist/core/code/engine/core/ingestion/scope-resolution/resolution-outcome.js +41 -0
- package/dist/core/code/engine/core/ingestion/scope-resolution/scope/namespace-targets.js +81 -0
- package/dist/core/code/engine/core/ingestion/scope-resolution/scope/walkers.js +1835 -0
- package/dist/core/code/engine/core/ingestion/scope-resolution/unresolved-receivers.js +242 -0
- package/dist/core/code/engine/core/ingestion/scope-resolution/utils/definition-id.js +22 -0
- package/dist/core/code/engine/core/ingestion/scope-resolution/workspace-index.js +151 -0
- package/dist/core/code/engine/core/ingestion/structure-processor.js +40 -0
- package/dist/core/code/engine/core/ingestion/tree-sitter-queries.js +2244 -0
- package/dist/core/code/engine/core/ingestion/ts-js-hoc-utils.js +115 -0
- package/dist/core/code/engine/core/ingestion/type-env.js +1136 -0
- package/dist/core/code/engine/core/ingestion/type-extractors/c-cpp.js +555 -0
- package/dist/core/code/engine/core/ingestion/type-extractors/csharp.js +570 -0
- package/dist/core/code/engine/core/ingestion/type-extractors/dart.js +372 -0
- package/dist/core/code/engine/core/ingestion/type-extractors/go.js +508 -0
- package/dist/core/code/engine/core/ingestion/type-extractors/jvm.js +875 -0
- package/dist/core/code/engine/core/ingestion/type-extractors/php.js +537 -0
- package/dist/core/code/engine/core/ingestion/type-extractors/python.js +477 -0
- package/dist/core/code/engine/core/ingestion/type-extractors/ruby.js +380 -0
- package/dist/core/code/engine/core/ingestion/type-extractors/rust.js +502 -0
- package/dist/core/code/engine/core/ingestion/type-extractors/shared.js +843 -0
- package/dist/core/code/engine/core/ingestion/type-extractors/swift.js +490 -0
- package/dist/core/code/engine/core/ingestion/type-extractors/types.js +2 -0
- package/dist/core/code/engine/core/ingestion/type-extractors/typescript.js +690 -0
- package/dist/core/code/engine/core/ingestion/utils/ast-helpers.js +1693 -0
- package/dist/core/code/engine/core/ingestion/utils/call-analysis.js +779 -0
- package/dist/core/code/engine/core/ingestion/utils/callable-flow-captures.js +932 -0
- package/dist/core/code/engine/core/ingestion/utils/callable-labels.js +49 -0
- package/dist/core/code/engine/core/ingestion/utils/deferred-resolution-profile.js +151 -0
- package/dist/core/code/engine/core/ingestion/utils/effective-ram.js +67 -0
- package/dist/core/code/engine/core/ingestion/utils/env.js +60 -0
- package/dist/core/code/engine/core/ingestion/utils/event-loop.js +9 -0
- package/dist/core/code/engine/core/ingestion/utils/graph-sort.js +103 -0
- package/dist/core/code/engine/core/ingestion/utils/heap-probe.js +45 -0
- package/dist/core/code/engine/core/ingestion/utils/heritage-marker.js +47 -0
- package/dist/core/code/engine/core/ingestion/utils/line-base.js +24 -0
- package/dist/core/code/engine/core/ingestion/utils/max-file-size.js +59 -0
- package/dist/core/code/engine/core/ingestion/utils/method-props.js +198 -0
- package/dist/core/code/engine/core/ingestion/utils/qualified-name.js +73 -0
- package/dist/core/code/engine/core/ingestion/utils/receiver-chain-captures.js +60 -0
- package/dist/core/code/engine/core/ingestion/utils/receiver-chain-codec.js +188 -0
- package/dist/core/code/engine/core/ingestion/utils/scope-tree-walk.js +36 -0
- package/dist/core/code/engine/core/ingestion/utils/symbol-labels.js +48 -0
- package/dist/core/code/engine/core/ingestion/utils/template-arguments.js +187 -0
- package/dist/core/code/engine/core/ingestion/utils/type-parameters.js +209 -0
- package/dist/core/code/engine/core/ingestion/utils/verbose.js +6 -0
- package/dist/core/code/engine/core/ingestion/variable-extractors/configs/c-cpp.js +133 -0
- package/dist/core/code/engine/core/ingestion/variable-extractors/configs/csharp.js +66 -0
- package/dist/core/code/engine/core/ingestion/variable-extractors/configs/dart.js +111 -0
- package/dist/core/code/engine/core/ingestion/variable-extractors/configs/go.js +153 -0
- package/dist/core/code/engine/core/ingestion/variable-extractors/configs/jvm.js +145 -0
- package/dist/core/code/engine/core/ingestion/variable-extractors/configs/php.js +61 -0
- package/dist/core/code/engine/core/ingestion/variable-extractors/configs/python.js +104 -0
- package/dist/core/code/engine/core/ingestion/variable-extractors/configs/ruby.js +55 -0
- package/dist/core/code/engine/core/ingestion/variable-extractors/configs/rust.js +79 -0
- package/dist/core/code/engine/core/ingestion/variable-extractors/configs/swift.js +91 -0
- package/dist/core/code/engine/core/ingestion/variable-extractors/configs/typescript-javascript.js +86 -0
- package/dist/core/code/engine/core/ingestion/variable-extractors/generic.js +111 -0
- package/dist/core/code/engine/core/ingestion/variable-types.js +3 -0
- package/dist/core/code/engine/core/ingestion/vue-sfc-extractor.js +544 -0
- package/dist/core/code/engine/core/ingestion/workers/callable-id.js +122 -0
- package/dist/core/code/engine/core/ingestion/workers/clone-safety.js +470 -0
- package/dist/core/code/engine/core/ingestion/workers/parse-worker.js +2456 -0
- package/dist/core/code/engine/core/ingestion/workers/post-result.js +90 -0
- package/dist/core/code/engine/core/ingestion/workers/quarantine.js +41 -0
- package/dist/core/code/engine/core/ingestion/workers/result-merge.js +59 -0
- package/dist/core/code/engine/core/ingestion/workers/worker-pool.js +1725 -0
- package/dist/core/code/engine/core/ingestion/workspace-config.js +139 -0
- package/dist/core/code/engine/core/lbug/conn-lock.js +72 -0
- package/dist/core/code/engine/core/lbug/csv-generator.js +710 -0
- package/dist/core/code/engine/core/lbug/cypher-escape.js +24 -0
- package/dist/core/code/engine/core/lbug/extension-load-error.js +339 -0
- package/dist/core/code/engine/core/lbug/extension-loader.js +261 -0
- package/dist/core/code/engine/core/lbug/graph-emit-sink.js +584 -0
- package/dist/core/code/engine/core/lbug/lbug-adapter.js +2703 -0
- package/dist/core/code/engine/core/lbug/lbug-config.js +1029 -0
- package/dist/core/code/engine/core/lbug/native-check.js +435 -0
- package/dist/core/code/engine/core/lbug/pool-adapter.js +973 -0
- package/dist/core/code/engine/core/lbug/query-params.js +25 -0
- package/dist/core/code/engine/core/lbug/query-result-utils.js +31 -0
- package/dist/core/code/engine/core/lbug/rel-pair-routing.js +373 -0
- package/dist/core/code/engine/core/lbug/schema.js +771 -0
- package/dist/core/code/engine/core/lbug/shutdown-helpers.js +40 -0
- package/dist/core/code/engine/core/lbug/sidecar-recovery.js +716 -0
- package/dist/core/code/engine/core/lbug/stdio-capture.js +49 -0
- package/dist/core/code/engine/core/lbug/sync-csv-writer.js +115 -0
- package/dist/core/code/engine/core/lbug/wal-checkpoint-driver.js +215 -0
- package/dist/core/code/engine/core/lbug/wal-driver-state.js +28 -0
- package/dist/core/code/engine/core/logger.js +339 -0
- package/dist/core/code/engine/core/platform/capabilities.js +91 -0
- package/dist/core/code/engine/core/run-analyze.js +1098 -0
- package/dist/core/code/engine/core/tree-sitter/parser-loader.js +281 -0
- package/dist/core/code/engine/core/tree-sitter/safe-parse.js +258 -0
- package/dist/core/code/engine/core/tree-sitter/vendored-grammars.js +64 -0
- package/dist/core/code/engine/lib/utils.js +121 -0
- package/dist/core/code/engine/mcp/core/lbug-adapter.js +27 -0
- package/dist/core/code/engine/mcp/local/aop-metadata.js +230 -0
- package/dist/core/code/engine/mcp/local/bean-metadata.js +49 -0
- package/dist/core/code/engine/mcp/local/limits.js +15 -0
- package/dist/core/code/engine/mcp/local/line-display.js +6 -0
- package/dist/core/code/engine/mcp/local/local-backend.js +4142 -0
- package/dist/core/code/engine/storage/branch-index.js +72 -0
- package/dist/core/code/engine/storage/file-hash.js +95 -0
- package/dist/core/code/engine/storage/fs-atomic.js +34 -0
- package/dist/core/code/engine/storage/git.js +555 -0
- package/dist/core/code/engine/storage/index-lock.js +664 -0
- package/dist/core/code/engine/storage/parse-cache.js +650 -0
- package/dist/core/code/engine/storage/parsedfile-store.js +620 -0
- package/dist/core/code/engine/storage/repo-manager.js +1061 -0
- package/dist/core/code/engine/storage/scope-index-store.js +247 -0
- package/dist/core/code/engine/types/pipeline.js +2 -0
- package/dist/core/code/scripts/install-duckdb-extension.mjs +125 -0
- package/dist/core/code/scripts/resolve-analyze-cmd.cjs +346 -0
- package/dist/core/code/shared/graph/types.js +8 -0
- package/dist/core/code/shared/index.js +105 -0
- package/dist/core/code/shared/integrations/circuit-breaker.js +242 -0
- package/dist/core/code/shared/integrations/resilient-fetch.js +224 -0
- package/dist/core/code/shared/integrations/retry.js +70 -0
- package/dist/core/code/shared/integrations/understand-quickly.js +145 -0
- package/dist/core/code/shared/language-detection.js +162 -0
- package/dist/core/code/shared/languages.js +27 -0
- package/dist/core/code/shared/lbug/schema-constants.js +98 -0
- package/dist/core/code/shared/mro-strategy.js +2 -0
- package/dist/core/code/shared/pipeline.js +5 -0
- package/dist/core/code/shared/scope-resolution/callable-flow-site.js +11 -0
- package/dist/core/code/shared/scope-resolution/def-index.js +53 -0
- package/dist/core/code/shared/scope-resolution/evidence-weights.js +87 -0
- package/dist/core/code/shared/scope-resolution/finalize-algorithm.js +807 -0
- package/dist/core/code/shared/scope-resolution/language-classification.js +46 -0
- package/dist/core/code/shared/scope-resolution/method-dispatch-index.js +100 -0
- package/dist/core/code/shared/scope-resolution/module-scope-index.js +59 -0
- package/dist/core/code/shared/scope-resolution/origin-priority.js +23 -0
- package/dist/core/code/shared/scope-resolution/parsed-file.js +54 -0
- package/dist/core/code/shared/scope-resolution/position-index.js +136 -0
- package/dist/core/code/shared/scope-resolution/qualified-name-index.js +77 -0
- package/dist/core/code/shared/scope-resolution/reference-site.js +24 -0
- package/dist/core/code/shared/scope-resolution/registries/class-registry.js +32 -0
- package/dist/core/code/shared/scope-resolution/registries/context.js +52 -0
- package/dist/core/code/shared/scope-resolution/registries/evidence.js +152 -0
- package/dist/core/code/shared/scope-resolution/registries/field-registry.js +33 -0
- package/dist/core/code/shared/scope-resolution/registries/lookup-core.js +392 -0
- package/dist/core/code/shared/scope-resolution/registries/lookup-qualified.js +58 -0
- package/dist/core/code/shared/scope-resolution/registries/macro-registry.js +34 -0
- package/dist/core/code/shared/scope-resolution/registries/method-registry.js +34 -0
- package/dist/core/code/shared/scope-resolution/registries/tie-breaks.js +63 -0
- package/dist/core/code/shared/scope-resolution/resolve-type-ref.js +128 -0
- package/dist/core/code/shared/scope-resolution/scope-id.js +49 -0
- package/dist/core/code/shared/scope-resolution/scope-tree.js +225 -0
- package/dist/core/code/shared/scope-resolution/symbol-definition.js +12 -0
- package/dist/core/code/shared/scope-resolution/types.js +25 -0
- package/dist/core/code/shared/test-helpers.js +17 -0
- package/dist/core/code/vendor/leiden/index.cjs +355 -0
- package/dist/core/code/vendor/leiden/utils.cjs +419 -0
- package/dist/core/features/scanner.d.ts +2 -0
- package/dist/core/features/scanner.d.ts.map +1 -1
- package/dist/core/features/scanner.js +27 -6
- package/dist/core/features/scanner.js.map +1 -1
- package/dist/core/timeline/cli.d.ts.map +1 -1
- package/dist/core/timeline/cli.js +13 -6
- package/dist/core/timeline/cli.js.map +1 -1
- package/dist/core/timeline/debris.d.ts +20 -0
- package/dist/core/timeline/debris.d.ts.map +1 -0
- package/dist/core/timeline/debris.js +124 -0
- package/dist/core/timeline/debris.js.map +1 -0
- package/dist/core/timeline/hook-runner.d.ts.map +1 -1
- package/dist/core/timeline/hook-runner.js +3 -1
- package/dist/core/timeline/hook-runner.js.map +1 -1
- package/dist/core/timeline/hooks.d.ts.map +1 -1
- package/dist/core/timeline/hooks.js +13 -5
- package/dist/core/timeline/hooks.js.map +1 -1
- package/dist/core/timeline/installer.d.ts +2 -0
- package/dist/core/timeline/installer.d.ts.map +1 -1
- package/dist/core/timeline/installer.js +12 -0
- package/dist/core/timeline/installer.js.map +1 -1
- package/dist/core/timeline/project-root.d.ts +21 -0
- package/dist/core/timeline/project-root.d.ts.map +1 -0
- package/dist/core/timeline/project-root.js +83 -0
- package/dist/core/timeline/project-root.js.map +1 -0
- package/package.json +1 -1
|
@@ -0,0 +1,1166 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* `emitScopeCaptures` for PHP (RFC #909 Ring 3 LANG-php).
|
|
4
|
+
*
|
|
5
|
+
* Drives the PHP scope query against tree-sitter-php and groups raw
|
|
6
|
+
* matches into `CaptureMatch[]` for the central extractor. Layers two
|
|
7
|
+
* synthesized streams on top:
|
|
8
|
+
*
|
|
9
|
+
* 1. **Decomposed use declarations** — each `namespace_use_declaration`
|
|
10
|
+
* is re-emitted with `@import.kind/source/name/alias` markers so
|
|
11
|
+
* `interpretPhpImport` can recover the ParsedImport shape without
|
|
12
|
+
* re-parsing raw text. Grouped uses fan out to one match per clause.
|
|
13
|
+
*
|
|
14
|
+
* 2. **Receiver-binding synthesis** — `$this` and `parent` type-bindings
|
|
15
|
+
* are synthesized on every non-static method entry. PHP's grammar
|
|
16
|
+
* does not express "implicit receiver of a non-static class method"
|
|
17
|
+
* via a clean `.scm` pattern, so we walk up the AST in code.
|
|
18
|
+
*
|
|
19
|
+
* 3. **Arity metadata synthesis** — `@declaration.parameter-count` /
|
|
20
|
+
* `@declaration.required-parameter-count` / `@declaration.parameter-types`
|
|
21
|
+
* are synthesized on function-like declarations so the registry can
|
|
22
|
+
* narrow overloads.
|
|
23
|
+
*
|
|
24
|
+
* 4. **PHPDoc synthesis** — @param and @return annotations in comment
|
|
25
|
+
* nodes preceding method/function declarations are extracted and emitted
|
|
26
|
+
* as `@type-binding.parameter` and `@type-binding.return` matches.
|
|
27
|
+
*
|
|
28
|
+
* 5. **Foreach loop synthesis** — `foreach ($users as $user)` emits
|
|
29
|
+
* a `@type-binding.alias` match binding the loop variable to the
|
|
30
|
+
* element type of the iterable (resolved from PHPDoc or scopeEnv).
|
|
31
|
+
*
|
|
32
|
+
* 6. **PHPDoc `@var` property synthesis** — a docblock on an UNTYPED
|
|
33
|
+
* property emits the `@type-binding.annotation` + `@declaration.property`
|
|
34
|
+
* pair the native typed-property rules emit, which is the only way PHP
|
|
35
|
+
* can declare a generic field type (#2833).
|
|
36
|
+
*
|
|
37
|
+
* Pure given the input source text. No I/O, no globals consulted.
|
|
38
|
+
*/
|
|
39
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
40
|
+
exports.emitPhpScopeCaptures = emitPhpScopeCaptures;
|
|
41
|
+
const ast_helpers_js_1 = require("../../utils/ast-helpers.js");
|
|
42
|
+
const import_decomposer_js_1 = require("./import-decomposer.js");
|
|
43
|
+
const arity_metadata_js_1 = require("./arity-metadata.js");
|
|
44
|
+
const receiver_binding_js_1 = require("./receiver-binding.js");
|
|
45
|
+
const query_js_1 = require("./query.js");
|
|
46
|
+
const cache_stats_js_1 = require("./cache-stats.js");
|
|
47
|
+
const constants_js_1 = require("../../constants.js");
|
|
48
|
+
const safe_parse_js_1 = require("../../../tree-sitter/safe-parse.js");
|
|
49
|
+
const callable_flow_captures_js_1 = require("../../utils/callable-flow-captures.js");
|
|
50
|
+
const receiver_chain_captures_js_1 = require("../../utils/receiver-chain-captures.js");
|
|
51
|
+
const PHP_CALLABLE_CAPTURE_OPTIONS = {
|
|
52
|
+
functionNodeTypes: new Set([
|
|
53
|
+
'function_definition',
|
|
54
|
+
'method_declaration',
|
|
55
|
+
'anonymous_function',
|
|
56
|
+
'arrow_function',
|
|
57
|
+
]),
|
|
58
|
+
callNodeTypes: new Set(['function_call_expression']),
|
|
59
|
+
parameterListNodeTypes: new Set(['formal_parameters', 'arguments']),
|
|
60
|
+
// tree-sitter-php has no 'optional_parameter' node (defaults ride on
|
|
61
|
+
// simple_parameter); property promotion is constructor-only and carries no
|
|
62
|
+
// callable-flow value (#2522 review).
|
|
63
|
+
parameterNodeTypes: new Set(['simple_parameter', 'variadic_parameter']),
|
|
64
|
+
bindingNodeTypes: new Set(['assignment_expression']),
|
|
65
|
+
assignmentNodeTypes: new Set(['assignment_expression']),
|
|
66
|
+
identifierNodeTypes: new Set(['name', 'qualified_name', 'namespace_name']),
|
|
67
|
+
functionScopedValueBindings: true,
|
|
68
|
+
emitCanonicalInvokeReference: true,
|
|
69
|
+
extractCallableReference: (node) => {
|
|
70
|
+
if (node.type !== 'function_call_expression')
|
|
71
|
+
return undefined;
|
|
72
|
+
const args = node.childForFieldName('arguments');
|
|
73
|
+
const isFirstClass = args?.namedChildren.some((child) => child !== null && child.type === 'variadic_placeholder') === true;
|
|
74
|
+
const target = node.childForFieldName('function');
|
|
75
|
+
if (!isFirstClass || target === null || target.type === 'variable_name')
|
|
76
|
+
return undefined;
|
|
77
|
+
return { name: target.text.replace(/^\\+/, ''), anchor: target };
|
|
78
|
+
},
|
|
79
|
+
};
|
|
80
|
+
/** Declaration anchors that carry function-like arity metadata. */
|
|
81
|
+
const FUNCTION_DECL_TAGS = ['@declaration.method', '@declaration.function'];
|
|
82
|
+
/** tree-sitter-php node types that the method extractor accepts. */
|
|
83
|
+
const FUNCTION_NODE_TYPES = [
|
|
84
|
+
'method_declaration',
|
|
85
|
+
'function_definition',
|
|
86
|
+
'anonymous_function',
|
|
87
|
+
'arrow_function',
|
|
88
|
+
];
|
|
89
|
+
function emitPhpScopeCaptures(sourceText, _filePath, cachedTree) {
|
|
90
|
+
// Skip the parse when the caller already produced a Tree for this source.
|
|
91
|
+
// The cachedTree parameter is typed as `unknown` at the LanguageProvider
|
|
92
|
+
// contract layer; cast here at the use site.
|
|
93
|
+
let tree = cachedTree;
|
|
94
|
+
if (tree === undefined) {
|
|
95
|
+
tree = (0, safe_parse_js_1.parseSourceSafe)((0, query_js_1.getPhpParser)(), sourceText, undefined, {
|
|
96
|
+
bufferSize: (0, constants_js_1.getTreeSitterBufferSize)(sourceText),
|
|
97
|
+
});
|
|
98
|
+
(0, cache_stats_js_1.recordCacheMiss)();
|
|
99
|
+
}
|
|
100
|
+
else {
|
|
101
|
+
(0, cache_stats_js_1.recordCacheHit)();
|
|
102
|
+
}
|
|
103
|
+
const rawMatches = (0, query_js_1.getPhpScopeQuery)().matches(tree.rootNode);
|
|
104
|
+
const out = [];
|
|
105
|
+
// Pre-scan: collect anchor node IDs of property_declaration nodes already
|
|
106
|
+
// matched by the typed @declaration.property pattern (query.ts ~lines 95–98).
|
|
107
|
+
// The untyped @declaration.variable catch-all (query.ts ~lines 101–103) is
|
|
108
|
+
// intentionally loose — it has no `type:` constraint, so tree-sitter also
|
|
109
|
+
// matches it against typed property declarations and emits a second capture
|
|
110
|
+
// for the same property_declaration anchor. Graph-level def-id collision
|
|
111
|
+
// currently masks the duplicate at the node-emit layer, but the catch-all
|
|
112
|
+
// capture still flows through scope-binding / name-keyed registries with a
|
|
113
|
+
// `$`-prefixed name that the typed branch's `$`-strip never normalizes —
|
|
114
|
+
// a known vector for receiver-binding lookup pollution. The two patterns
|
|
115
|
+
// produce separate rawMatches entries with separate `grouped` maps, so the
|
|
116
|
+
// dedup has to be cross-match: build the set here, then skip
|
|
117
|
+
// @declaration.variable matches whose anchor is in it (loop below).
|
|
118
|
+
const typedPropertyAnchorIds = new Set();
|
|
119
|
+
for (const m of rawMatches) {
|
|
120
|
+
for (const c of m.captures) {
|
|
121
|
+
if (c.name === 'declaration.property') {
|
|
122
|
+
typedPropertyAnchorIds.add(c.node.id);
|
|
123
|
+
break;
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
// The one full-tree walk: class/trait heritage, and PHPDoc `@var` on an
|
|
128
|
+
// untyped property. Run BEFORE the match loop rather than appended after it,
|
|
129
|
+
// because the property declarations the `@var` half claims must join
|
|
130
|
+
// `typedPropertyAnchorIds`: it emits the same `@declaration.property` the
|
|
131
|
+
// typed rule does, so without this the loose `@declaration.variable`
|
|
132
|
+
// catch-all would declare the very same node a second time under its
|
|
133
|
+
// `$`-sigilled name — exactly the duplicate the set above exists to suppress.
|
|
134
|
+
// Its matches are still appended in the original order after the loop.
|
|
135
|
+
const walked = synthesizePhpTreeWalkCaptures(tree.rootNode);
|
|
136
|
+
for (const id of walked.docPropertyAnchorIds)
|
|
137
|
+
typedPropertyAnchorIds.add(id);
|
|
138
|
+
for (const m of rawMatches) {
|
|
139
|
+
// Group captures by their tag name. Tree-sitter strips the leading
|
|
140
|
+
// `@`; we put it back so the central extractor's prefix lookups work.
|
|
141
|
+
const grouped = {};
|
|
142
|
+
// Parallel tag -> captured SyntaxNode map: the query hands us each matched
|
|
143
|
+
// node as c.node, so anchors resolve via a type-guarded lookup (nodeIfType)
|
|
144
|
+
// instead of re-deriving them with findNodeAtRange(tree.rootNode, ...) per
|
|
145
|
+
// match — the O(matches x rootChildren) root-walk fixed for go #1915 /
|
|
146
|
+
// python #1918, mirrored here.
|
|
147
|
+
const nodeMap = {};
|
|
148
|
+
for (const c of m.captures) {
|
|
149
|
+
const tag = '@' + c.name;
|
|
150
|
+
grouped[tag] = (0, ast_helpers_js_1.nodeToCapture)(tag, c.node);
|
|
151
|
+
nodeMap[tag] = c.node;
|
|
152
|
+
}
|
|
153
|
+
if (Object.keys(grouped).length === 0)
|
|
154
|
+
continue;
|
|
155
|
+
// Cross-match dedup for the typed-property double-match described above:
|
|
156
|
+
// skip @declaration.variable matches whose anchor was already captured as
|
|
157
|
+
// @declaration.property in an earlier match.
|
|
158
|
+
if (grouped['@declaration.variable'] !== undefined) {
|
|
159
|
+
const varCap = m.captures.find((c) => c.name === 'declaration.variable');
|
|
160
|
+
if (varCap !== undefined && typedPropertyAnchorIds.has(varCap.node.id))
|
|
161
|
+
continue;
|
|
162
|
+
}
|
|
163
|
+
// Normalize PHP property declarations: strip leading `$` from
|
|
164
|
+
// `@declaration.name` for @declaration.property matches. PHP stores
|
|
165
|
+
// field names WITHOUT the `$` sigil in the graph so that member access
|
|
166
|
+
// lookups like `$user->address` can find the property named `address`
|
|
167
|
+
// (not `$address`). `@type-binding.annotation` already strips `$` in
|
|
168
|
+
// `interpretPhpTypeBinding`; this mirrors that for the declaration side.
|
|
169
|
+
//
|
|
170
|
+
// Only applies to `@declaration.property` — typed class properties and
|
|
171
|
+
// constructor-promoted parameters. Untyped `@declaration.variable` keeps
|
|
172
|
+
// its `$` prefix (those defs are Variable type and not in the field
|
|
173
|
+
// registry, so their name doesn't affect member lookup).
|
|
174
|
+
if (grouped['@declaration.property'] !== undefined &&
|
|
175
|
+
grouped['@declaration.name'] !== undefined) {
|
|
176
|
+
const nameCap = grouped['@declaration.name'];
|
|
177
|
+
if (nameCap.text.startsWith('$')) {
|
|
178
|
+
grouped['@declaration.name'] = { ...nameCap, text: nameCap.text.slice(1) };
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
// Normalize PHP receiver expressions so the compound-receiver resolver
|
|
182
|
+
// can walk chains expressed with `->` (PHP) as if they used `.` (the
|
|
183
|
+
// resolver's canonical separator). Without this, `$user->address->save()`
|
|
184
|
+
// has receiver text `$user->address` — the resolver sees no `.` separator,
|
|
185
|
+
// treats it as a bare identifier, and cannot walk field types.
|
|
186
|
+
//
|
|
187
|
+
// Transformation applied to `@reference.receiver` captures:
|
|
188
|
+
// 1. Replace `->` with `.` ($user->address → $user.address)
|
|
189
|
+
// 2. Strip leading `$` from each segment ($user.address → user.address)
|
|
190
|
+
// 3. Strip trailing `?` on null-safe receivers ($user? → user)
|
|
191
|
+
//
|
|
192
|
+
// This is a PHP-local normalization — no shared pipeline code is changed.
|
|
193
|
+
if (grouped['@reference.receiver'] !== undefined) {
|
|
194
|
+
const recvCap = grouped['@reference.receiver'];
|
|
195
|
+
const normalized = normalizePhpReceiver(recvCap.text);
|
|
196
|
+
if (normalized !== recvCap.text) {
|
|
197
|
+
grouped['@reference.receiver'] = { ...recvCap, text: normalized };
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
// Normalize static property write: strip leading `$` from `@reference.name`
|
|
201
|
+
// so `User::$count` resolves to property `count` (stored without `$` in graph).
|
|
202
|
+
if (grouped['@reference.write.static'] !== undefined) {
|
|
203
|
+
const nameCap = grouped['@reference.name'];
|
|
204
|
+
if (nameCap !== undefined && nameCap.text.startsWith('$')) {
|
|
205
|
+
grouped['@reference.name'] = {
|
|
206
|
+
...nameCap,
|
|
207
|
+
text: nameCap.text.slice(1),
|
|
208
|
+
};
|
|
209
|
+
}
|
|
210
|
+
// Re-tag as @reference.write.member so downstream passes see a uniform write kind.
|
|
211
|
+
grouped['@reference.write.member'] = grouped['@reference.write.static'];
|
|
212
|
+
delete grouped['@reference.write.static'];
|
|
213
|
+
}
|
|
214
|
+
// Decompose each `namespace_use_declaration` so `interpretPhpImport`
|
|
215
|
+
// sees the kind/source/name/alias markers it consumes.
|
|
216
|
+
if (grouped['@import.statement'] !== undefined) {
|
|
217
|
+
const stmtNode = (0, ast_helpers_js_1.nodeIfType)(nodeMap['@import.statement'], 'namespace_use_declaration');
|
|
218
|
+
if (stmtNode !== null) {
|
|
219
|
+
const decomposed = (0, import_decomposer_js_1.splitNamespaceUseDeclaration)(stmtNode);
|
|
220
|
+
if (decomposed.length > 0) {
|
|
221
|
+
for (const d of decomposed)
|
|
222
|
+
out.push(d);
|
|
223
|
+
continue;
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
// Defensive fallback: emit the raw match.
|
|
227
|
+
// Structural receiver chain for a call whose receiver is itself an
|
|
228
|
+
// expression, so resolution can type it by folding over structure
|
|
229
|
+
// instead of re-parsing the receiver's source text. Self-gating: a
|
|
230
|
+
// non-call match, an absent receiver, or a chain with no nameable base
|
|
231
|
+
// all leave `grouped` untouched.
|
|
232
|
+
(0, receiver_chain_captures_js_1.synthesizeReceiverChainCapture)(grouped, nodeMap['@reference.receiver']);
|
|
233
|
+
out.push(grouped);
|
|
234
|
+
continue;
|
|
235
|
+
}
|
|
236
|
+
// Synthesize `$this` / `parent` receiver type-bindings on every
|
|
237
|
+
// non-static method-like. Mirrors C#'s `this` / `base` synthesis.
|
|
238
|
+
if (grouped['@scope.function'] !== undefined) {
|
|
239
|
+
// Structural receiver chain for a call whose receiver is itself an
|
|
240
|
+
// expression, so resolution can type it by folding over structure
|
|
241
|
+
// instead of re-parsing the receiver's source text. Self-gating: a
|
|
242
|
+
// non-call match, an absent receiver, or a chain with no nameable base
|
|
243
|
+
// all leave `grouped` untouched.
|
|
244
|
+
(0, receiver_chain_captures_js_1.synthesizeReceiverChainCapture)(grouped, nodeMap['@reference.receiver']);
|
|
245
|
+
out.push(grouped);
|
|
246
|
+
const fnNode = (0, ast_helpers_js_1.nodeIfType)(nodeMap['@scope.function'], ...FUNCTION_NODE_TYPES);
|
|
247
|
+
if (fnNode !== null) {
|
|
248
|
+
for (const synth of (0, receiver_binding_js_1.synthesizePhpReceiverBinding)(fnNode)) {
|
|
249
|
+
out.push(synth);
|
|
250
|
+
}
|
|
251
|
+
// Synthesize PHPDoc @param and @return type bindings for this fn.
|
|
252
|
+
for (const synth of synthesizePhpDocBindings(fnNode)) {
|
|
253
|
+
out.push(synth);
|
|
254
|
+
}
|
|
255
|
+
// Synthesize foreach loop variable bindings inside this fn body.
|
|
256
|
+
for (const synth of synthesizeForeachBindings(fnNode)) {
|
|
257
|
+
out.push(synth);
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
continue;
|
|
261
|
+
}
|
|
262
|
+
// Synthesize arity metadata on function-like declarations so the
|
|
263
|
+
// registry can narrow overloads.
|
|
264
|
+
const declTag = FUNCTION_DECL_TAGS.find((t) => grouped[t] !== undefined);
|
|
265
|
+
if (declTag !== undefined) {
|
|
266
|
+
const fnNode = (0, ast_helpers_js_1.nodeIfType)(nodeMap[declTag], ...FUNCTION_NODE_TYPES);
|
|
267
|
+
if (fnNode !== null) {
|
|
268
|
+
const arity = (0, arity_metadata_js_1.computePhpArityMetadata)(fnNode);
|
|
269
|
+
if (arity.parameterCount !== undefined) {
|
|
270
|
+
grouped['@declaration.parameter-count'] = (0, ast_helpers_js_1.syntheticCapture)('@declaration.parameter-count', fnNode, String(arity.parameterCount));
|
|
271
|
+
}
|
|
272
|
+
if (arity.requiredParameterCount !== undefined) {
|
|
273
|
+
grouped['@declaration.required-parameter-count'] = (0, ast_helpers_js_1.syntheticCapture)('@declaration.required-parameter-count', fnNode, String(arity.requiredParameterCount));
|
|
274
|
+
}
|
|
275
|
+
if (arity.parameterTypes !== undefined) {
|
|
276
|
+
grouped['@declaration.parameter-types'] = (0, ast_helpers_js_1.syntheticCapture)('@declaration.parameter-types', fnNode, JSON.stringify(arity.parameterTypes));
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
// Synthesize `@reference.arity` on every call site so the registry's
|
|
281
|
+
// arity filter can narrow overloads. Count the `argument` children of
|
|
282
|
+
// the backing `arguments` node. Mirrors C#'s pattern (csharp/captures.ts
|
|
283
|
+
// lines 149-186). PHP needs this for arity-based dispatch (Cluster H).
|
|
284
|
+
const callTag = ['@reference.call.free', '@reference.call.member', '@reference.call.constructor'].find((t) => grouped[t] !== undefined);
|
|
285
|
+
if (callTag !== undefined) {
|
|
286
|
+
const possibleFirstClass = (0, ast_helpers_js_1.nodeIfType)(nodeMap[callTag], 'function_call_expression');
|
|
287
|
+
const argumentsNode = possibleFirstClass?.childForFieldName('arguments');
|
|
288
|
+
if (argumentsNode?.namedChildren.some((child) => child !== null && child.type === 'variadic_placeholder') === true) {
|
|
289
|
+
// PHP 8.1 `target(...)` creates a Closure; it does not invoke target.
|
|
290
|
+
// Callable-flow synthesis below owns this site as a seed.
|
|
291
|
+
continue;
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
if (callTag !== undefined && grouped['@reference.arity'] === undefined) {
|
|
295
|
+
const callNode = (0, ast_helpers_js_1.nodeIfType)(nodeMap[callTag], 'function_call_expression', 'member_call_expression', 'nullsafe_member_call_expression', 'scoped_call_expression', 'object_creation_expression');
|
|
296
|
+
if (callNode !== null) {
|
|
297
|
+
const argList = callNode.childForFieldName('arguments');
|
|
298
|
+
const args = [];
|
|
299
|
+
if (argList !== null) {
|
|
300
|
+
for (let i = 0; i < argList.namedChildCount; i++) {
|
|
301
|
+
const child = argList.namedChild(i);
|
|
302
|
+
if (child !== null && child.type === 'argument')
|
|
303
|
+
args.push(child);
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
grouped['@reference.arity'] = (0, ast_helpers_js_1.syntheticCapture)('@reference.arity', callNode, String(args.length));
|
|
307
|
+
// Infer argument types from literal nodes for type-based narrowing.
|
|
308
|
+
// Non-literal arguments emit empty string ("unknown" = any-match).
|
|
309
|
+
const argTypes = args.map((arg) => inferPhpArgType(arg));
|
|
310
|
+
grouped['@reference.parameter-types'] = (0, ast_helpers_js_1.syntheticCapture)('@reference.parameter-types', callNode, JSON.stringify(argTypes));
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
// Structural receiver chain for a call whose receiver is itself an
|
|
314
|
+
// expression, so resolution can type it by folding over structure
|
|
315
|
+
// instead of re-parsing the receiver's source text. Self-gating: a
|
|
316
|
+
// non-call match, an absent receiver, or a chain with no nameable base
|
|
317
|
+
// all leave `grouped` untouched.
|
|
318
|
+
(0, receiver_chain_captures_js_1.synthesizeReceiverChainCapture)(grouped, nodeMap['@reference.receiver']);
|
|
319
|
+
out.push(grouped);
|
|
320
|
+
}
|
|
321
|
+
out.push(...walked.inheritance);
|
|
322
|
+
out.push(...walked.docProperties);
|
|
323
|
+
out.push(...(0, callable_flow_captures_js_1.synthesizeCallableFlowCaptures)(tree.rootNode, PHP_CALLABLE_CAPTURE_OPTIONS));
|
|
324
|
+
return out;
|
|
325
|
+
}
|
|
326
|
+
// ─── PHP whole-tree synthesis ────────────────────────────────────────────────
|
|
327
|
+
/**
|
|
328
|
+
* The single `walkNamedTree` pass of `emitPhpScopeCaptures`, dispatching every
|
|
329
|
+
* synthesis that needs to see the whole tree.
|
|
330
|
+
*
|
|
331
|
+
* ONE walk, not one per synthesis. A tree-sitter node walk is not cheap next to
|
|
332
|
+
* the work it feeds: measured on a 1.2k-line PHP source (9.6k nodes), a single
|
|
333
|
+
* `walkNamedTree` pass costs 7.4 ms against 2.1 ms to PARSE the file, because
|
|
334
|
+
* every step materializes node wrappers across the N-API boundary. So a new
|
|
335
|
+
* node kind is a branch here rather than a pass of its own — the two below emit
|
|
336
|
+
* into separate arrays, and `emitPhpScopeCaptures` appends them in the order
|
|
337
|
+
* they were appended when they were two passes.
|
|
338
|
+
*
|
|
339
|
+
* The `@reference.inherits` half exists so the registry-primary
|
|
340
|
+
* scope-resolution path emits EXTENDS / IMPLEMENTS edges (mirrors C#
|
|
341
|
+
* `synthesizeCsharpInheritanceReferences` / C++ `emitCppInheritanceCaptures`).
|
|
342
|
+
* Without it, PHP inheritance edges came only from the legacy heritage-capture
|
|
343
|
+
* leg (removed in #942), which the worker pipeline drops for registry-primary
|
|
344
|
+
* languages (issue #1951). See {@link emitPhpDocPropertyBinding} for the other.
|
|
345
|
+
*/
|
|
346
|
+
function synthesizePhpTreeWalkCaptures(root) {
|
|
347
|
+
const inheritance = [];
|
|
348
|
+
const docProperties = [];
|
|
349
|
+
const docPropertyAnchorIds = new Set();
|
|
350
|
+
(0, ast_helpers_js_1.walkNamedTree)(root, (node) => {
|
|
351
|
+
if (node.type === 'class_declaration' || node.type === 'trait_declaration') {
|
|
352
|
+
emitPhpHeritageReferences(node, inheritance);
|
|
353
|
+
}
|
|
354
|
+
else if (node.type === 'property_declaration') {
|
|
355
|
+
emitPhpDocPropertyBinding(node, docProperties, docPropertyAnchorIds);
|
|
356
|
+
}
|
|
357
|
+
});
|
|
358
|
+
return { inheritance, docProperties, docPropertyAnchorIds };
|
|
359
|
+
}
|
|
360
|
+
/**
|
|
361
|
+
* Emit `@reference.inherits` for the heritage of one `class_declaration` or
|
|
362
|
+
* `trait_declaration`.
|
|
363
|
+
*
|
|
364
|
+
* Scope matches the legacy PHP heritage query (tree-sitter-queries.ts
|
|
365
|
+
* PHP_QUERIES extends / implements / trait-use captures):
|
|
366
|
+
*
|
|
367
|
+
* 1. `class_declaration` > `base_clause` > [(name) (qualified_name)] — extends
|
|
368
|
+
* 2. `class_declaration` > `class_interface_clause` > [(name) (qualified_name)] — implements
|
|
369
|
+
* 3. `class_declaration` body `use_declaration` > [(name) (qualified_name)] — trait use
|
|
370
|
+
* 4. `trait_declaration` body `use_declaration` > [(name) (qualified_name)] — trait use
|
|
371
|
+
*
|
|
372
|
+
* The EXTENDS-vs-IMPLEMENTS split is decided downstream from the resolved
|
|
373
|
+
* target's symbol kind (`preEmitInheritanceEdges`: `Interface` → IMPLEMENTS,
|
|
374
|
+
* else EXTENDS), so all bases emit the same `inherits` kind here. The base
|
|
375
|
+
* lookup name is normalized to its bare simple identifier (`Foo\Bar\Base` →
|
|
376
|
+
* `Base`) to match the V1 simple-name `findClassBindingInScope` contract.
|
|
377
|
+
*
|
|
378
|
+
* NOTE (#1951 trait-use parity): a PHP `use Trait;` is emitted as an IMPLEMENTS
|
|
379
|
+
* edge — `preEmitInheritanceEdges` (run.ts) maps a
|
|
380
|
+
* resolved `Interface` OR `Trait` target to IMPLEMENTS (`type === 'Interface'
|
|
381
|
+
* || type === 'Trait' ? 'IMPLEMENTS' : 'EXTENDS'`), so `use Trait` resolves to
|
|
382
|
+
* IMPLEMENTS on both the legacy and registry-primary paths.
|
|
383
|
+
*/
|
|
384
|
+
function emitPhpHeritageReferences(node, out) {
|
|
385
|
+
if (node.type === 'class_declaration') {
|
|
386
|
+
// extends: single base_clause child carrying one base name.
|
|
387
|
+
const baseClause = findNamedChild(node, 'base_clause');
|
|
388
|
+
if (baseClause !== null)
|
|
389
|
+
emitPhpBaseNames(baseClause, out);
|
|
390
|
+
// implements: class_interface_clause may list several interfaces.
|
|
391
|
+
const ifaceClause = findNamedChild(node, 'class_interface_clause');
|
|
392
|
+
if (ifaceClause !== null)
|
|
393
|
+
emitPhpBaseNames(ifaceClause, out);
|
|
394
|
+
}
|
|
395
|
+
// trait use: `use TraitName;` inside the class body, and trait-uses-trait:
|
|
396
|
+
// `use OtherTrait;` inside a trait body.
|
|
397
|
+
emitPhpTraitUses(node, out);
|
|
398
|
+
}
|
|
399
|
+
/**
|
|
400
|
+
* Emit `@reference.inherits` for every `use_declaration` (trait use) in the
|
|
401
|
+
* declaration body of `node` (a class_declaration or trait_declaration).
|
|
402
|
+
* Class-body `use_declaration` is the trait-use node (distinct from the
|
|
403
|
+
* top-level `namespace_use_declaration` import node).
|
|
404
|
+
*/
|
|
405
|
+
function emitPhpTraitUses(node, out) {
|
|
406
|
+
const body = node.childForFieldName('body');
|
|
407
|
+
if (body === null || body.type !== 'declaration_list')
|
|
408
|
+
return;
|
|
409
|
+
for (let i = 0; i < body.namedChildCount; i++) {
|
|
410
|
+
const child = body.namedChild(i);
|
|
411
|
+
if (child !== null && child.type === 'use_declaration') {
|
|
412
|
+
emitPhpBaseNames(child, out);
|
|
413
|
+
}
|
|
414
|
+
}
|
|
415
|
+
}
|
|
416
|
+
/**
|
|
417
|
+
* Walk the named children of a heritage clause (`base_clause`,
|
|
418
|
+
* `class_interface_clause`, or `use_declaration`) and emit one
|
|
419
|
+
* `@reference.inherits` match per `name` / `qualified_name` base. The lookup
|
|
420
|
+
* name is the bare tail identifier so `findClassBindingInScope` resolves it.
|
|
421
|
+
*/
|
|
422
|
+
function emitPhpBaseNames(clause, out) {
|
|
423
|
+
for (let i = 0; i < clause.namedChildCount; i++) {
|
|
424
|
+
const base = clause.namedChild(i);
|
|
425
|
+
if (base === null)
|
|
426
|
+
continue;
|
|
427
|
+
if (base.type !== 'name' && base.type !== 'qualified_name')
|
|
428
|
+
continue;
|
|
429
|
+
const bareName = phpBareBaseName(base);
|
|
430
|
+
if (bareName === '')
|
|
431
|
+
continue;
|
|
432
|
+
out.push({
|
|
433
|
+
'@reference.inherits': (0, ast_helpers_js_1.nodeToCapture)('@reference.inherits', base),
|
|
434
|
+
'@reference.name': (0, ast_helpers_js_1.syntheticCapture)('@reference.name', base, bareName),
|
|
435
|
+
});
|
|
436
|
+
}
|
|
437
|
+
}
|
|
438
|
+
/**
|
|
439
|
+
* Normalize a PHP base node to its bare simple identifier:
|
|
440
|
+
* `Base` (name) → `Base`
|
|
441
|
+
* `Foo\Bar\Base` (qualified_name)→ `Base` (last `name` child)
|
|
442
|
+
* `\Foo\Base` (qualified_name)→ `Base`
|
|
443
|
+
* Mirrors C#'s `terminalTypeNameNode`: strip the qualifier tail so the V1
|
|
444
|
+
* simple-name scope-chain lookup resolves the target def.
|
|
445
|
+
*/
|
|
446
|
+
function phpBareBaseName(base) {
|
|
447
|
+
if (base.type === 'name')
|
|
448
|
+
return base.text;
|
|
449
|
+
if (base.type === 'qualified_name') {
|
|
450
|
+
// qualified_name holds one or more `name` children (plus `\` separators);
|
|
451
|
+
// the bare class is the last `name` child.
|
|
452
|
+
for (let i = base.namedChildCount - 1; i >= 0; i--) {
|
|
453
|
+
const child = base.namedChild(i);
|
|
454
|
+
if (child !== null && child.type === 'name')
|
|
455
|
+
return child.text;
|
|
456
|
+
}
|
|
457
|
+
// Fallback: split the raw text on the namespace separator.
|
|
458
|
+
const segs = base.text.split('\\').filter((s) => s.length > 0);
|
|
459
|
+
return segs.length > 0 ? segs[segs.length - 1] : '';
|
|
460
|
+
}
|
|
461
|
+
return '';
|
|
462
|
+
}
|
|
463
|
+
/** Find the first named child of `node` with the given type. */
|
|
464
|
+
function findNamedChild(node, type) {
|
|
465
|
+
for (let i = 0; i < node.namedChildCount; i++) {
|
|
466
|
+
const child = node.namedChild(i);
|
|
467
|
+
if (child !== null && child.type === type)
|
|
468
|
+
return child;
|
|
469
|
+
}
|
|
470
|
+
return null;
|
|
471
|
+
}
|
|
472
|
+
/** Pre-order walk over named children, invoking `cb` on each node. */
|
|
473
|
+
// ─── PHP receiver normalization ──────────────────────────────────────────────
|
|
474
|
+
/**
|
|
475
|
+
* Normalize a PHP receiver expression so the language-agnostic
|
|
476
|
+
* compound-receiver resolver (which splits on `.`) can walk field-type chains.
|
|
477
|
+
*
|
|
478
|
+
* The compound-receiver resolver:
|
|
479
|
+
* - splits on `.` to get chain segments
|
|
480
|
+
* - looks up the first segment in `typeBindings` (keyed with `$` for variables)
|
|
481
|
+
* - walks subsequent segments as field names (stored without `$` in the graph)
|
|
482
|
+
*
|
|
483
|
+
* Transformation:
|
|
484
|
+
* 1. Replace `->` and `?->` with `.` so the resolver's splitter works
|
|
485
|
+
* 2. Strip any bare `?` fragment left by null-safe chain ends
|
|
486
|
+
* 3. Strip `$` from all segments EXCEPT the first (which is a variable
|
|
487
|
+
* and must keep `$` for typeBindings lookup — e.g. `$user → User`)
|
|
488
|
+
*
|
|
489
|
+
* Examples:
|
|
490
|
+
* `$user` → `$user` (bare variable — unchanged)
|
|
491
|
+
* `$user->address` → `$user.address`
|
|
492
|
+
* `$user->address->city` → `$user.address.city`
|
|
493
|
+
* `$user?` → `$user` (null-safe trailing `?` stripped)
|
|
494
|
+
* `$this` → `$this` (receiverBinding uses `$this`)
|
|
495
|
+
* `parent` → `parent` (super-receiver check)
|
|
496
|
+
*/
|
|
497
|
+
function normalizePhpReceiver(raw) {
|
|
498
|
+
// Keep `$this`, `parent`, and `self` as-is.
|
|
499
|
+
if (raw === '$this' || raw === 'parent' || raw === 'self')
|
|
500
|
+
return raw;
|
|
501
|
+
// Replace `?->` (null-safe) and plain `->` with `.`.
|
|
502
|
+
let text = raw.replace(/\?->/g, '.').replace(/->/g, '.');
|
|
503
|
+
// Strip a trailing `?` (null-safe fragment on the last object node).
|
|
504
|
+
text = text.replace(/\?$/, '');
|
|
505
|
+
// Collapse any doubled dots from `?->` where `?` was on its own.
|
|
506
|
+
text = text.replace(/\.{2,}/g, '.');
|
|
507
|
+
// Strip trailing dot.
|
|
508
|
+
text = text.replace(/\.$/, '');
|
|
509
|
+
// Split on `.` and strip `$` from all segments EXCEPT the first.
|
|
510
|
+
// The first segment is a PHP variable (typeBinding key includes `$`).
|
|
511
|
+
// Subsequent segments are property/method names (stored without `$`).
|
|
512
|
+
const segments = text.split('.');
|
|
513
|
+
for (let i = 1; i < segments.length; i++) {
|
|
514
|
+
const s = segments[i];
|
|
515
|
+
if (s !== undefined && s.startsWith('$'))
|
|
516
|
+
segments[i] = s.slice(1);
|
|
517
|
+
}
|
|
518
|
+
return segments.join('.');
|
|
519
|
+
}
|
|
520
|
+
// ─── PHP argument type inference ─────────────────────────────────────────────
|
|
521
|
+
/**
|
|
522
|
+
* Infer the PHP type of a call argument from its literal shape.
|
|
523
|
+
* Returns an empty string for non-literals (treated as "unknown" = any-match).
|
|
524
|
+
* Mirrors C#'s `inferArgType` helper.
|
|
525
|
+
*/
|
|
526
|
+
function inferPhpArgType(argNode) {
|
|
527
|
+
// argument node wraps the actual expression
|
|
528
|
+
const expr = argNode.firstNamedChild ?? argNode;
|
|
529
|
+
switch (expr.type) {
|
|
530
|
+
case 'integer':
|
|
531
|
+
return 'int';
|
|
532
|
+
case 'float':
|
|
533
|
+
return 'float';
|
|
534
|
+
case 'string':
|
|
535
|
+
case 'encapsed_string':
|
|
536
|
+
case 'heredoc':
|
|
537
|
+
case 'nowdoc':
|
|
538
|
+
return 'string';
|
|
539
|
+
case 'boolean':
|
|
540
|
+
case 'true':
|
|
541
|
+
case 'false':
|
|
542
|
+
return 'bool';
|
|
543
|
+
case 'null':
|
|
544
|
+
return 'null';
|
|
545
|
+
default:
|
|
546
|
+
return '';
|
|
547
|
+
}
|
|
548
|
+
}
|
|
549
|
+
// ─── PHPDoc synthesis ─────────────────────────────────────────────────────────
|
|
550
|
+
/** PHP 8+ attribute_list nodes that appear between PHPDoc and method. */
|
|
551
|
+
const SKIP_SIBLING_TYPES = new Set(['attribute_list', 'attribute', 'comment']);
|
|
552
|
+
/** Regex for PHPDoc @param: standard `@param Type $name` */
|
|
553
|
+
const PHPDOC_PARAM_RE = /@param\s+(\S+)\s+\$(\w+)/g;
|
|
554
|
+
/** Regex for PHPDoc @param: alternate `@param $name Type` */
|
|
555
|
+
const PHPDOC_PARAM_ALT_RE = /@param\s+\$(\w+)\s+(\S+)/g;
|
|
556
|
+
/** Regex for PHPDoc @return: `@return Type` */
|
|
557
|
+
const PHPDOC_RETURN_RE = /@return\s+(\S+)/;
|
|
558
|
+
/**
|
|
559
|
+
* Normalize a PHP type string to a simple class name for binding purposes.
|
|
560
|
+
* Returns null for primitives or uninformative types.
|
|
561
|
+
* Mirrors `normalizePhpType` in `interpret.ts` but operates on raw PHPDoc strings.
|
|
562
|
+
*/
|
|
563
|
+
function normalizePhpDocType(raw) {
|
|
564
|
+
let type = raw.trim();
|
|
565
|
+
// Strip nullable prefix
|
|
566
|
+
if (type.startsWith('?'))
|
|
567
|
+
type = type.slice(1).trim();
|
|
568
|
+
// Strip array suffix: User[] → User
|
|
569
|
+
if (type.endsWith('[]'))
|
|
570
|
+
type = type.slice(0, -2).trim();
|
|
571
|
+
// Strip union with null/false/void
|
|
572
|
+
if (type.includes('|')) {
|
|
573
|
+
const parts = type
|
|
574
|
+
.split('|')
|
|
575
|
+
.map((p) => p.trim())
|
|
576
|
+
.filter((p) => p !== 'null' && p !== 'false' && p !== 'void' && p !== 'mixed' && p !== '');
|
|
577
|
+
if (parts.length !== 1)
|
|
578
|
+
return null;
|
|
579
|
+
type = parts[0];
|
|
580
|
+
}
|
|
581
|
+
// Strip intersection: take first part
|
|
582
|
+
if (type.includes('&')) {
|
|
583
|
+
const first = type.split('&')[0].trim();
|
|
584
|
+
if (first === '')
|
|
585
|
+
return null;
|
|
586
|
+
type = first;
|
|
587
|
+
}
|
|
588
|
+
// Strip generic wrapper: Collection<User> → User
|
|
589
|
+
const genericMatch = type.match(/^\w[\w\\]*\s*<([^,<>]+)>$/);
|
|
590
|
+
if (genericMatch) {
|
|
591
|
+
type = genericMatch[1].trim();
|
|
592
|
+
// Strip array suffix again inside generic
|
|
593
|
+
if (type.endsWith('[]'))
|
|
594
|
+
type = type.slice(0, -2).trim();
|
|
595
|
+
}
|
|
596
|
+
// Strip namespace qualifier: \App\Models\User → User
|
|
597
|
+
if (type.includes('\\')) {
|
|
598
|
+
const segs = type.split('\\').filter(Boolean);
|
|
599
|
+
type = segs[segs.length - 1] ?? type;
|
|
600
|
+
}
|
|
601
|
+
// Reject primitives
|
|
602
|
+
if (PHP_PRIMITIVES.has(type.toLowerCase()))
|
|
603
|
+
return null;
|
|
604
|
+
// Must be a simple identifier
|
|
605
|
+
if (!/^\w+$/.test(type))
|
|
606
|
+
return null;
|
|
607
|
+
return type;
|
|
608
|
+
}
|
|
609
|
+
const PHP_PRIMITIVES = new Set([
|
|
610
|
+
'int',
|
|
611
|
+
'integer',
|
|
612
|
+
'float',
|
|
613
|
+
'double',
|
|
614
|
+
'string',
|
|
615
|
+
'bool',
|
|
616
|
+
'boolean',
|
|
617
|
+
'array',
|
|
618
|
+
'object',
|
|
619
|
+
'callable',
|
|
620
|
+
'iterable',
|
|
621
|
+
'null',
|
|
622
|
+
'void',
|
|
623
|
+
'never',
|
|
624
|
+
'mixed',
|
|
625
|
+
'false',
|
|
626
|
+
'true',
|
|
627
|
+
'self',
|
|
628
|
+
'static',
|
|
629
|
+
'parent',
|
|
630
|
+
]);
|
|
631
|
+
/**
|
|
632
|
+
* The comment siblings immediately preceding `node`, in SOURCE order (the
|
|
633
|
+
* nearest comment last), stopping at the first named sibling that is not a
|
|
634
|
+
* comment or a PHP 8+ attribute.
|
|
635
|
+
*
|
|
636
|
+
* The single implementation of that chain walk. Every PHPDoc reader in this
|
|
637
|
+
* file wants the same siblings under the same stop rule — `@param`/`@return` on
|
|
638
|
+
* a method, `@var` for a foreach element type, `@var` for a field type — and
|
|
639
|
+
* three hand-copied walks meant a fix to the stop rule (attributes between the
|
|
640
|
+
* docblock and the declaration, say) could land on one reader and not the
|
|
641
|
+
* others, which shows up as a field typed differently from its own foreach
|
|
642
|
+
* element type.
|
|
643
|
+
*/
|
|
644
|
+
function precedingCommentSiblings(node) {
|
|
645
|
+
const comments = [];
|
|
646
|
+
let sibling = node.previousSibling;
|
|
647
|
+
while (sibling !== null) {
|
|
648
|
+
if (sibling.type === 'comment') {
|
|
649
|
+
comments.unshift(sibling);
|
|
650
|
+
}
|
|
651
|
+
else if (sibling.isNamed && !SKIP_SIBLING_TYPES.has(sibling.type)) {
|
|
652
|
+
break;
|
|
653
|
+
}
|
|
654
|
+
sibling = sibling.previousSibling;
|
|
655
|
+
}
|
|
656
|
+
return comments;
|
|
657
|
+
}
|
|
658
|
+
/**
|
|
659
|
+
* First match of `re` over {@link precedingCommentSiblings}, searched from the
|
|
660
|
+
* NEAREST comment outward — a docblock written directly above the declaration
|
|
661
|
+
* wins over one further up, and an earlier comment is still reached when the
|
|
662
|
+
* nearest one carries no such tag.
|
|
663
|
+
*/
|
|
664
|
+
function nearestPrecedingCommentMatch(node, re) {
|
|
665
|
+
const comments = precedingCommentSiblings(node);
|
|
666
|
+
for (let i = comments.length - 1; i >= 0; i--) {
|
|
667
|
+
const m = re.exec(comments[i].text);
|
|
668
|
+
if (m !== null)
|
|
669
|
+
return m;
|
|
670
|
+
}
|
|
671
|
+
return null;
|
|
672
|
+
}
|
|
673
|
+
/**
|
|
674
|
+
* Collect comment text from siblings immediately before `fnNode`.
|
|
675
|
+
* Skips PHP 8+ attribute_list nodes.
|
|
676
|
+
*/
|
|
677
|
+
function collectPrecedingComments(fnNode) {
|
|
678
|
+
return precedingCommentSiblings(fnNode)
|
|
679
|
+
.map((comment) => comment.text)
|
|
680
|
+
.join('\n');
|
|
681
|
+
}
|
|
682
|
+
/**
|
|
683
|
+
* Synthesize PHPDoc @param and @return type-binding captures for a
|
|
684
|
+
* method_declaration or function_definition node.
|
|
685
|
+
*
|
|
686
|
+
* PHPDoc @param Type $name → `@type-binding.parameter` match (anchored at fn body/return_type).
|
|
687
|
+
* PHPDoc @return Type → `@type-binding.return` match (anchored at fn name).
|
|
688
|
+
*/
|
|
689
|
+
function synthesizePhpDocBindings(fnNode) {
|
|
690
|
+
if (fnNode.type !== 'method_declaration' && fnNode.type !== 'function_definition')
|
|
691
|
+
return [];
|
|
692
|
+
const commentBlock = collectPrecedingComments(fnNode);
|
|
693
|
+
if (commentBlock === '')
|
|
694
|
+
return [];
|
|
695
|
+
const out = [];
|
|
696
|
+
// Anchor for parameter type-bindings: the function body (or return_type as fallback).
|
|
697
|
+
// The binding must be inside the function scope so it's visible to body statements.
|
|
698
|
+
const bodyNode = fnNode.childForFieldName('body');
|
|
699
|
+
const anchorNode = bodyNode ?? fnNode;
|
|
700
|
+
// ── @param annotations ────────────────────────────────────────────────────
|
|
701
|
+
PHPDOC_PARAM_RE.lastIndex = 0;
|
|
702
|
+
let m;
|
|
703
|
+
const seenParams = new Set();
|
|
704
|
+
while ((m = PHPDOC_PARAM_RE.exec(commentBlock)) !== null) {
|
|
705
|
+
const rawType = m[1];
|
|
706
|
+
const paramName = '$' + m[2];
|
|
707
|
+
const typeName = normalizePhpDocType(rawType);
|
|
708
|
+
if (typeName === null)
|
|
709
|
+
continue;
|
|
710
|
+
seenParams.add(paramName);
|
|
711
|
+
out.push({
|
|
712
|
+
'@type-binding.parameter': (0, ast_helpers_js_1.nodeToCapture)('@type-binding.parameter', anchorNode),
|
|
713
|
+
'@type-binding.name': (0, ast_helpers_js_1.syntheticCapture)('@type-binding.name', anchorNode, paramName),
|
|
714
|
+
'@type-binding.type': (0, ast_helpers_js_1.syntheticCapture)('@type-binding.type', anchorNode, typeName),
|
|
715
|
+
});
|
|
716
|
+
}
|
|
717
|
+
// Also check alternate PHPDoc order: @param $name Type
|
|
718
|
+
PHPDOC_PARAM_ALT_RE.lastIndex = 0;
|
|
719
|
+
while ((m = PHPDOC_PARAM_ALT_RE.exec(commentBlock)) !== null) {
|
|
720
|
+
const paramName = '$' + m[1];
|
|
721
|
+
if (seenParams.has(paramName))
|
|
722
|
+
continue; // standard format takes priority
|
|
723
|
+
const rawType = m[2];
|
|
724
|
+
const typeName = normalizePhpDocType(rawType);
|
|
725
|
+
if (typeName === null)
|
|
726
|
+
continue;
|
|
727
|
+
out.push({
|
|
728
|
+
'@type-binding.parameter': (0, ast_helpers_js_1.nodeToCapture)('@type-binding.parameter', anchorNode),
|
|
729
|
+
'@type-binding.name': (0, ast_helpers_js_1.syntheticCapture)('@type-binding.name', anchorNode, paramName),
|
|
730
|
+
'@type-binding.type': (0, ast_helpers_js_1.syntheticCapture)('@type-binding.type', anchorNode, typeName),
|
|
731
|
+
});
|
|
732
|
+
}
|
|
733
|
+
// ── @return annotation ────────────────────────────────────────────────────
|
|
734
|
+
const returnMatch = PHPDOC_RETURN_RE.exec(commentBlock);
|
|
735
|
+
if (returnMatch !== null) {
|
|
736
|
+
const rawType = returnMatch[1];
|
|
737
|
+
const typeName = normalizePhpDocType(rawType);
|
|
738
|
+
if (typeName !== null) {
|
|
739
|
+
// @return bindings must be anchored at the method name and hoisted to Module scope
|
|
740
|
+
// by phpBindingScopeFor (which checks for @type-binding.return presence).
|
|
741
|
+
// Use the function_definition/method_declaration node itself as the anchor — it
|
|
742
|
+
// coincides with the innermost scope's range, so auto-hoist kicks in.
|
|
743
|
+
const nameNode = fnNode.childForFieldName('name') ?? fnNode;
|
|
744
|
+
out.push({
|
|
745
|
+
'@type-binding.return': (0, ast_helpers_js_1.nodeToCapture)('@type-binding.return', fnNode),
|
|
746
|
+
'@type-binding.name': (0, ast_helpers_js_1.syntheticCapture)('@type-binding.name', nameNode, nameNode.text),
|
|
747
|
+
'@type-binding.type': (0, ast_helpers_js_1.syntheticCapture)('@type-binding.type', nameNode, typeName),
|
|
748
|
+
});
|
|
749
|
+
}
|
|
750
|
+
}
|
|
751
|
+
return out;
|
|
752
|
+
}
|
|
753
|
+
// ─── Foreach synthesis ───────────────────────────────────────────────────────
|
|
754
|
+
/**
|
|
755
|
+
* Walk all `foreach_statement` nodes inside `fnNode` and synthesize
|
|
756
|
+
* `@type-binding.alias` captures binding the loop variable to the
|
|
757
|
+
* element type of the iterable.
|
|
758
|
+
*
|
|
759
|
+
* Supports:
|
|
760
|
+
* - `foreach ($users as $user)` — simple iterable variable
|
|
761
|
+
* - `foreach ($users as $k => $user)` — key→value pair
|
|
762
|
+
* - `foreach ($this->users as $user)` — member access iterable
|
|
763
|
+
* - `foreach (getUsers() as $user)` — NOT yet supported (needs return type)
|
|
764
|
+
*
|
|
765
|
+
* The element type is resolved by:
|
|
766
|
+
* 1. Looking up the iterable name in PHPDoc @param bindings already
|
|
767
|
+
* collected for this function (passed via typeBindingsByName).
|
|
768
|
+
* 2. Direct resolution when iterable's env type IS the element type
|
|
769
|
+
* (because PHPDoc normalizes `User[]` → `User` already).
|
|
770
|
+
*/
|
|
771
|
+
function synthesizeForeachBindings(fnNode) {
|
|
772
|
+
if (fnNode.type !== 'method_declaration' &&
|
|
773
|
+
fnNode.type !== 'function_definition' &&
|
|
774
|
+
fnNode.type !== 'anonymous_function' &&
|
|
775
|
+
fnNode.type !== 'arrow_function') {
|
|
776
|
+
return [];
|
|
777
|
+
}
|
|
778
|
+
const out = [];
|
|
779
|
+
// Build a mini type map from the function's PHPDoc @param annotations.
|
|
780
|
+
// This is re-parsed here (not cached from synthesizePhpDocBindings) for simplicity;
|
|
781
|
+
// the cost is negligible given the small comment sizes.
|
|
782
|
+
const commentBlock = collectPrecedingComments(fnNode);
|
|
783
|
+
const paramTypeMap = buildParamTypeMap(commentBlock);
|
|
784
|
+
// Walk the function body for foreach_statement nodes.
|
|
785
|
+
const bodyNode = fnNode.childForFieldName('body');
|
|
786
|
+
if (bodyNode === null)
|
|
787
|
+
return [];
|
|
788
|
+
collectForeachBindings(bodyNode, fnNode, paramTypeMap, out);
|
|
789
|
+
return out;
|
|
790
|
+
}
|
|
791
|
+
/** Build a map of `$paramName → elementTypeName` from PHPDoc @param in a comment block. */
|
|
792
|
+
function buildParamTypeMap(commentBlock) {
|
|
793
|
+
const map = new Map();
|
|
794
|
+
if (commentBlock === '')
|
|
795
|
+
return map;
|
|
796
|
+
PHPDOC_PARAM_RE.lastIndex = 0;
|
|
797
|
+
let m;
|
|
798
|
+
while ((m = PHPDOC_PARAM_RE.exec(commentBlock)) !== null) {
|
|
799
|
+
const rawType = m[1];
|
|
800
|
+
const paramName = '$' + m[2];
|
|
801
|
+
const typeName = normalizePhpDocType(rawType);
|
|
802
|
+
if (typeName !== null)
|
|
803
|
+
map.set(paramName, typeName);
|
|
804
|
+
}
|
|
805
|
+
PHPDOC_PARAM_ALT_RE.lastIndex = 0;
|
|
806
|
+
while ((m = PHPDOC_PARAM_ALT_RE.exec(commentBlock)) !== null) {
|
|
807
|
+
const paramName = '$' + m[1];
|
|
808
|
+
if (map.has(paramName))
|
|
809
|
+
continue;
|
|
810
|
+
const rawType = m[2];
|
|
811
|
+
const typeName = normalizePhpDocType(rawType);
|
|
812
|
+
if (typeName !== null)
|
|
813
|
+
map.set(paramName, typeName);
|
|
814
|
+
}
|
|
815
|
+
return map;
|
|
816
|
+
}
|
|
817
|
+
/**
|
|
818
|
+
* Walk a subtree and collect foreach_statement bindings.
|
|
819
|
+
* Recursively descends into all child nodes.
|
|
820
|
+
*/
|
|
821
|
+
function collectForeachBindings(node, fnNode, paramTypeMap, out) {
|
|
822
|
+
if (node.type === 'foreach_statement') {
|
|
823
|
+
const synth = synthesizeSingleForeach(node, fnNode, paramTypeMap);
|
|
824
|
+
if (synth !== null)
|
|
825
|
+
out.push(synth);
|
|
826
|
+
}
|
|
827
|
+
for (let i = 0; i < node.childCount; i++) {
|
|
828
|
+
const child = node.child(i);
|
|
829
|
+
if (child !== null) {
|
|
830
|
+
collectForeachBindings(child, fnNode, paramTypeMap, out);
|
|
831
|
+
}
|
|
832
|
+
}
|
|
833
|
+
}
|
|
834
|
+
/**
|
|
835
|
+
* Synthesize a single `@type-binding.alias` match for a `foreach_statement`.
|
|
836
|
+
*
|
|
837
|
+
* AST structure for foreach_statement (tree-sitter-php):
|
|
838
|
+
* foreach ( <iterable> as <value_or_pair> ) <body>
|
|
839
|
+
* Named children (excluding body): first = iterable, second = value or pair.
|
|
840
|
+
*/
|
|
841
|
+
function synthesizeSingleForeach(foreachNode, fnNode, paramTypeMap) {
|
|
842
|
+
// Collect non-body named children: [iterable, value_or_pair]
|
|
843
|
+
const bodyNode = foreachNode.childForFieldName('body');
|
|
844
|
+
const children = [];
|
|
845
|
+
for (let i = 0; i < foreachNode.namedChildCount; i++) {
|
|
846
|
+
const child = foreachNode.namedChild(i);
|
|
847
|
+
if (child !== null && child !== bodyNode)
|
|
848
|
+
children.push(child);
|
|
849
|
+
}
|
|
850
|
+
if (children.length < 2)
|
|
851
|
+
return null;
|
|
852
|
+
const iterableNode = children[0];
|
|
853
|
+
const valueOrPair = children[1];
|
|
854
|
+
// Determine the loop variable node
|
|
855
|
+
let loopVarNode;
|
|
856
|
+
if (valueOrPair.type === 'pair') {
|
|
857
|
+
// $key => $value — use the last named child of the pair
|
|
858
|
+
const lastChild = valueOrPair.namedChild(valueOrPair.namedChildCount - 1);
|
|
859
|
+
if (lastChild === null)
|
|
860
|
+
return null;
|
|
861
|
+
loopVarNode =
|
|
862
|
+
lastChild.type === 'by_ref' ? (lastChild.firstNamedChild ?? lastChild) : lastChild;
|
|
863
|
+
}
|
|
864
|
+
else {
|
|
865
|
+
loopVarNode =
|
|
866
|
+
valueOrPair.type === 'by_ref' ? (valueOrPair.firstNamedChild ?? valueOrPair) : valueOrPair;
|
|
867
|
+
}
|
|
868
|
+
// Loop variable must be a variable_name
|
|
869
|
+
if (loopVarNode.type !== 'variable_name')
|
|
870
|
+
return null;
|
|
871
|
+
const loopVarName = loopVarNode.text; // e.g. '$user'
|
|
872
|
+
// Resolve the element type from the iterable
|
|
873
|
+
let elementType = null;
|
|
874
|
+
if (iterableNode.type === 'variable_name') {
|
|
875
|
+
// foreach ($users as $user) — look up $users in param map
|
|
876
|
+
const iterableName = iterableNode.text; // e.g. '$users'
|
|
877
|
+
elementType = paramTypeMap.get(iterableName) ?? null;
|
|
878
|
+
}
|
|
879
|
+
else if (iterableNode.type === 'member_access_expression') {
|
|
880
|
+
// foreach ($this->users as $user) — property name is the field
|
|
881
|
+
const propNameNode = iterableNode.childForFieldName('name');
|
|
882
|
+
if (propNameNode !== null) {
|
|
883
|
+
// Property stored with $ prefix in paramTypeMap (rare for $this->prop patterns)
|
|
884
|
+
// Try both with and without $ prefix
|
|
885
|
+
const propKey = '$' + propNameNode.text;
|
|
886
|
+
elementType = paramTypeMap.get(propKey) ?? null;
|
|
887
|
+
if (elementType === null) {
|
|
888
|
+
// Try to find the property type from the enclosing class
|
|
889
|
+
elementType = findClassPropertyElementType(iterableNode, fnNode);
|
|
890
|
+
}
|
|
891
|
+
}
|
|
892
|
+
}
|
|
893
|
+
else if (iterableNode.type === 'function_call_expression') {
|
|
894
|
+
// foreach (getUsers() as $user) — use the function name as a type alias.
|
|
895
|
+
// The function's @return annotation produces a @type-binding.return binding
|
|
896
|
+
// in the Module scope (e.g. getUsers → User). The scope-extractor's
|
|
897
|
+
// followChainedRef will resolve $user → getUsers → User.
|
|
898
|
+
const funcNode = iterableNode.childForFieldName('function');
|
|
899
|
+
if (funcNode !== null && funcNode.type === 'name') {
|
|
900
|
+
elementType = funcNode.text; // e.g. 'getUsers' — chain will be resolved later
|
|
901
|
+
}
|
|
902
|
+
}
|
|
903
|
+
else if (iterableNode.type === 'member_call_expression') {
|
|
904
|
+
// foreach ($this->getUsers() as $user) — use the method name as a type alias.
|
|
905
|
+
const methodNameNode = iterableNode.childForFieldName('name');
|
|
906
|
+
if (methodNameNode !== null) {
|
|
907
|
+
elementType = methodNameNode.text; // e.g. 'getUsers'
|
|
908
|
+
}
|
|
909
|
+
}
|
|
910
|
+
if (elementType === null)
|
|
911
|
+
return null;
|
|
912
|
+
// Anchor the binding inside the foreach body so it's scoped to the loop.
|
|
913
|
+
const anchorNode = bodyNode ?? foreachNode;
|
|
914
|
+
return {
|
|
915
|
+
'@type-binding.alias': (0, ast_helpers_js_1.nodeToCapture)('@type-binding.alias', anchorNode),
|
|
916
|
+
'@type-binding.name': (0, ast_helpers_js_1.syntheticCapture)('@type-binding.name', anchorNode, loopVarName),
|
|
917
|
+
'@type-binding.type': (0, ast_helpers_js_1.syntheticCapture)('@type-binding.type', anchorNode, elementType),
|
|
918
|
+
};
|
|
919
|
+
}
|
|
920
|
+
/**
|
|
921
|
+
* Try to find the element type for `$this->property` member access by walking
|
|
922
|
+
* up from the foreach to the enclosing class and scanning the property declaration.
|
|
923
|
+
*/
|
|
924
|
+
function findClassPropertyElementType(memberAccessNode, fnNode) {
|
|
925
|
+
const propNameNode = memberAccessNode.childForFieldName('name');
|
|
926
|
+
if (propNameNode === null)
|
|
927
|
+
return null;
|
|
928
|
+
const propName = propNameNode.text;
|
|
929
|
+
// Walk up from fnNode to find the enclosing class declaration
|
|
930
|
+
let cur = fnNode.parent;
|
|
931
|
+
while (cur !== null) {
|
|
932
|
+
if (cur.type === 'class_declaration' || cur.type === 'trait_declaration') {
|
|
933
|
+
break;
|
|
934
|
+
}
|
|
935
|
+
cur = cur.parent;
|
|
936
|
+
}
|
|
937
|
+
if (cur === null)
|
|
938
|
+
return null;
|
|
939
|
+
// Find the property_declaration with matching variable_name '$propName'
|
|
940
|
+
const declList = cur.childForFieldName('body');
|
|
941
|
+
if (declList === null)
|
|
942
|
+
return null;
|
|
943
|
+
for (let i = 0; i < declList.namedChildCount; i++) {
|
|
944
|
+
const child = declList.namedChild(i);
|
|
945
|
+
if (child === null || child.type !== 'property_declaration')
|
|
946
|
+
continue;
|
|
947
|
+
for (let j = 0; j < child.namedChildCount; j++) {
|
|
948
|
+
const elem = child.namedChild(j);
|
|
949
|
+
if (elem === null || elem.type !== 'property_element')
|
|
950
|
+
continue;
|
|
951
|
+
const varNameNode = elem.firstNamedChild;
|
|
952
|
+
if (varNameNode === null || varNameNode.text !== '$' + propName)
|
|
953
|
+
continue;
|
|
954
|
+
// Found the property — get its element type from @var PHPDoc or native type
|
|
955
|
+
return extractPropertyElementType(child);
|
|
956
|
+
}
|
|
957
|
+
}
|
|
958
|
+
return null;
|
|
959
|
+
}
|
|
960
|
+
/**
|
|
961
|
+
* PHPDoc `@var`, with the optional variable name PHPStan/Psalm allow
|
|
962
|
+
* (`@var Repo<User> $repo`). `\S+` for the type deliberately: a docblock type is
|
|
963
|
+
* untyped text and everything past the first space is prose.
|
|
964
|
+
*
|
|
965
|
+
* ONE regex for both readings of the tag. The FIELD type
|
|
966
|
+
* ({@link synthesizePhpDocPropertyBindings}) needs group 2 to tell `@var Repo
|
|
967
|
+
* $other` from `@var Repo`; the foreach ELEMENT type
|
|
968
|
+
* ({@link extractPropertyElementType}) ignores it — and since the trailing group
|
|
969
|
+
* is optional it can never change what group 1 captures, so a second, narrower
|
|
970
|
+
* copy bought nothing but the chance of the two readings of one annotation
|
|
971
|
+
* drifting apart.
|
|
972
|
+
*/
|
|
973
|
+
const PHPDOC_VAR_RE = /@var\s+(\S+)(?:\s+\$(\w+))?/;
|
|
974
|
+
/**
|
|
975
|
+
* Extract element type from a property_declaration node:
|
|
976
|
+
* 1. PHPDoc @var annotation on a preceding comment sibling
|
|
977
|
+
* 2. PHP 7.4+ native type field (non-array)
|
|
978
|
+
*/
|
|
979
|
+
function extractPropertyElementType(propDecl) {
|
|
980
|
+
// Strategy 1: PHPDoc @var on a preceding comment sibling. The `$name` group
|
|
981
|
+
// is not consulted: an element type is asked for by the ONE foreach that
|
|
982
|
+
// already named this property, so a mismatched name cannot mis-attribute it.
|
|
983
|
+
const varTag = nearestPrecedingCommentMatch(propDecl, PHPDOC_VAR_RE);
|
|
984
|
+
if (varTag !== null)
|
|
985
|
+
return normalizePhpDocType(varTag[1]);
|
|
986
|
+
// Strategy 2: native type field — skip generic 'array'
|
|
987
|
+
const typeNode = propDecl.childForFieldName('type');
|
|
988
|
+
if (typeNode === null)
|
|
989
|
+
return null;
|
|
990
|
+
const typeName = typeNode.text.trim();
|
|
991
|
+
if (typeName === 'array' || typeName === '')
|
|
992
|
+
return null;
|
|
993
|
+
return normalizePhpDocType(typeName);
|
|
994
|
+
}
|
|
995
|
+
// ─── PHPDoc @var property synthesis ──────────────────────────────────────────
|
|
996
|
+
/**
|
|
997
|
+
* Container spellings that base-name erasure would turn into a PHANTOM class.
|
|
998
|
+
*
|
|
999
|
+
* Erasing `list<User>` to `list` names nothing — PHP has no `list` type — so the
|
|
1000
|
+
* binding could only ever bind a user class that happens to be called `list`,
|
|
1001
|
+
* i.e. exactly the wrong-edge direction. Every OTHER PHPDoc container erases to
|
|
1002
|
+
* a name `normalizePhpType` already rejects as a primitive (`array<int,User>` →
|
|
1003
|
+
* `array`, `iterable<User>` → `iterable`) or to a real class whose methods are
|
|
1004
|
+
* what the field's receiver actually calls (`Collection<User>` → `Collection`,
|
|
1005
|
+
* `Generator<User>` → `Generator`), so this set holds one entry, not a
|
|
1006
|
+
* catalogue.
|
|
1007
|
+
*
|
|
1008
|
+
* Compared CASE-FOLDED, not by listing spellings: a deny-set that must be kept
|
|
1009
|
+
* in sync by vigilance drifts (#2833, the same lesson python/interpret.ts
|
|
1010
|
+
* records for its own reduction).
|
|
1011
|
+
*/
|
|
1012
|
+
const PHPDOC_PHANTOM_CONTAINER_BASES = new Set(['list']);
|
|
1013
|
+
/**
|
|
1014
|
+
* Erase type ARGUMENTS from a docblock type, leaving the base name:
|
|
1015
|
+
* `Repo<User>` → `Repo`, `Repo<Repo<User>>` → `Repo`, `Repo<User>|null` →
|
|
1016
|
+
* `Repo|null`. Bracket-counting rather than a regex so a nested or
|
|
1017
|
+
* multi-argument spelling reduces in one pass; an unbalanced `<` simply
|
|
1018
|
+
* swallows the tail, which is the declining direction.
|
|
1019
|
+
*
|
|
1020
|
+
* NOT the shared `stripTemplateArguments`, and the difference is the UNION:
|
|
1021
|
+
* that one truncates at the first `<`, so `Repo<User>|null` becomes `Repo` and
|
|
1022
|
+
* the nullability is lost with the arguments. A docblock type is the one place
|
|
1023
|
+
* a union survives to the binding — `interpretPhpTypeBinding` runs
|
|
1024
|
+
* `normalizePhpType` over what this returns, and that is what strips `|null`
|
|
1025
|
+
* exactly as it does for a native `Repo|null` property. So a PHP docblock needs
|
|
1026
|
+
* the arguments gone and the rest of the spelling intact, which is a different
|
|
1027
|
+
* operation and not a candidate for a seventh caller of the shared one.
|
|
1028
|
+
*/
|
|
1029
|
+
function erasePhpDocTypeArguments(text) {
|
|
1030
|
+
let out = '';
|
|
1031
|
+
let depth = 0;
|
|
1032
|
+
for (const ch of text) {
|
|
1033
|
+
if (ch === '<')
|
|
1034
|
+
depth++;
|
|
1035
|
+
else if (ch === '>') {
|
|
1036
|
+
if (depth > 0)
|
|
1037
|
+
depth--;
|
|
1038
|
+
}
|
|
1039
|
+
else if (depth === 0)
|
|
1040
|
+
out += ch;
|
|
1041
|
+
}
|
|
1042
|
+
return out;
|
|
1043
|
+
}
|
|
1044
|
+
/**
|
|
1045
|
+
* The type name a property's PHPDoc `@var` should bind the FIELD to, or `null`
|
|
1046
|
+
* to decline.
|
|
1047
|
+
*
|
|
1048
|
+
* Two normalizations happen here and nowhere else, and each is forced:
|
|
1049
|
+
*
|
|
1050
|
+
* 1. TYPE-ARGUMENT ERASURE (`Repo<User>` → `Repo`). Every sibling language in
|
|
1051
|
+
* #2833 lets the as-written spelling reach `TypeRef.rawName` and leaves the
|
|
1052
|
+
* erasure to `resolveClassBindingForName`. PHP cannot: `normalizePhpType`
|
|
1053
|
+
* reduces `X<Y>` to `Y` — the CONTAINER-ELEMENT convention, pinned by
|
|
1054
|
+
* `test/integration/resolvers/php.test.ts` ("normalizePhpType
|
|
1055
|
+
* ('Collection<User>') must yield 'User', not 'Collection'") because the
|
|
1056
|
+
* foreach path depends on it. Measured: passing `Repo<User>` through binds
|
|
1057
|
+
* the field to `User` and `$this->repo->save()` emits `User::save` — a
|
|
1058
|
+
* WRONG edge, not a missing one. So a field's type arguments are erased
|
|
1059
|
+
* HERE, before that rule can read them, and the element convention is left
|
|
1060
|
+
* exactly as it was for `@param` / `@return` / foreach.
|
|
1061
|
+
*
|
|
1062
|
+
* 2. ARRAY DECLINE (`Repo[]` → nothing). A field annotated `Repo[]` holds an
|
|
1063
|
+
* ARRAY; typing it `Repo` is a wrong field type, and the collision is real
|
|
1064
|
+
* rather than theoretical — a repository class with a `find` / `filter` /
|
|
1065
|
+
* `map` method would claim `$this->repos->find(…)`. The element type is
|
|
1066
|
+
* already extracted separately for the one construct that wants it:
|
|
1067
|
+
* `extractPropertyElementType` reads the same `@var` for `foreach
|
|
1068
|
+
* ($this->repos as $r)`. Declining here keeps the two readings of one
|
|
1069
|
+
* annotation from colliding.
|
|
1070
|
+
*
|
|
1071
|
+
* Everything else is delegated: `interpretPhpTypeBinding` applies the SAME
|
|
1072
|
+
* `normalizePhpType` the native typed property (`private Repo $repo;`) goes
|
|
1073
|
+
* through, so nullable (`?Repo`), null-union (`Repo|null`), intersection,
|
|
1074
|
+
* fully-qualified (`\App\Models\Repo`, kept qualified on purpose — see that
|
|
1075
|
+
* function) and every primitive / `mixed` / `self` / `static` rejection behave
|
|
1076
|
+
* identically for the two spellings by construction, not by duplication.
|
|
1077
|
+
*/
|
|
1078
|
+
function phpDocPropertyFieldType(rawType) {
|
|
1079
|
+
const erased = erasePhpDocTypeArguments(rawType).trim();
|
|
1080
|
+
if (erased === '')
|
|
1081
|
+
return null;
|
|
1082
|
+
// Array-of: declined (see 2 above). Checked AFTER erasure so `Repo<User>[]`
|
|
1083
|
+
// is recognised as an array too.
|
|
1084
|
+
if (erased.endsWith('[]'))
|
|
1085
|
+
return null;
|
|
1086
|
+
if (PHPDOC_PHANTOM_CONTAINER_BASES.has(erased.toLowerCase()))
|
|
1087
|
+
return null;
|
|
1088
|
+
return erased;
|
|
1089
|
+
}
|
|
1090
|
+
/**
|
|
1091
|
+
* Emit the field type-binding a PHPDoc `@var` block declares on one UNTYPED
|
|
1092
|
+
* property declaration (`/** @var Repo */ private $repo;`), and record its
|
|
1093
|
+
* anchor id in `anchorIds`.
|
|
1094
|
+
*
|
|
1095
|
+
* PHP's own type story leans on docblocks for everything its native syntax
|
|
1096
|
+
* cannot spell — and generics are exactly that, since `private Repo<User>
|
|
1097
|
+
* $repo;` is a parse error. The native TYPED property already binds via the
|
|
1098
|
+
* `@type-binding.annotation` rule in `query.ts`; measured before this pass, the
|
|
1099
|
+
* docblock form bound NOTHING, so `$this->repo->save()` lost its edge for both
|
|
1100
|
+
* the generic spelling and its non-generic control (#2833).
|
|
1101
|
+
*
|
|
1102
|
+
* The emitted match is byte-identical in SHAPE to what that query rule emits —
|
|
1103
|
+
* `@type-binding.annotation` anchored on the `property_declaration`, with
|
|
1104
|
+
* `@type-binding.name` carrying the `$`-sigilled variable name. That is the
|
|
1105
|
+
* whole design: `interpretPhpTypeBinding` strips the sigil for source
|
|
1106
|
+
* `'annotation'`, `phpBindingScopeFor` places it on the same scope, and the
|
|
1107
|
+
* compound-receiver resolver finds it in `typeBindings` the way it always has.
|
|
1108
|
+
* No resolution-side code changes.
|
|
1109
|
+
*
|
|
1110
|
+
* Declines, each because the annotation cannot be ATTRIBUTED rather than
|
|
1111
|
+
* because the type is unusable:
|
|
1112
|
+
* - a property that already has a native `type:` — the query rule owns it,
|
|
1113
|
+
* and a docblock repeating it must not emit a second, competing binding;
|
|
1114
|
+
* - `private $a, $b;` — one `@var` cannot say which element it types;
|
|
1115
|
+
* - `@var Repo $other` naming a DIFFERENT property than the one it precedes.
|
|
1116
|
+
*/
|
|
1117
|
+
function emitPhpDocPropertyBinding(node, matches, anchorIds) {
|
|
1118
|
+
// A native type hint already produces the binding via query.ts.
|
|
1119
|
+
if (node.childForFieldName('type') !== null)
|
|
1120
|
+
return;
|
|
1121
|
+
const elements = node.namedChildren.filter((c) => c !== null && c.type === 'property_element');
|
|
1122
|
+
if (elements.length !== 1)
|
|
1123
|
+
return;
|
|
1124
|
+
const varNameNode = elements[0].childForFieldName('name') ?? elements[0].firstNamedChild;
|
|
1125
|
+
if (varNameNode === null || varNameNode.type !== 'variable_name')
|
|
1126
|
+
return;
|
|
1127
|
+
const raw = findPhpDocVarTag(node);
|
|
1128
|
+
if (raw === null)
|
|
1129
|
+
return;
|
|
1130
|
+
// `@var Repo $other` on `private $repo;` types neither — decline.
|
|
1131
|
+
if (raw.varName !== undefined && '$' + raw.varName !== varNameNode.text)
|
|
1132
|
+
return;
|
|
1133
|
+
const typeName = phpDocPropertyFieldType(raw.type);
|
|
1134
|
+
if (typeName === null)
|
|
1135
|
+
return;
|
|
1136
|
+
anchorIds.add(node.id);
|
|
1137
|
+
matches.push({
|
|
1138
|
+
'@type-binding.annotation': (0, ast_helpers_js_1.nodeToCapture)('@type-binding.annotation', node),
|
|
1139
|
+
'@type-binding.name': (0, ast_helpers_js_1.syntheticCapture)('@type-binding.name', varNameNode, varNameNode.text),
|
|
1140
|
+
'@type-binding.type': (0, ast_helpers_js_1.syntheticCapture)('@type-binding.type', varNameNode, typeName),
|
|
1141
|
+
});
|
|
1142
|
+
// …and the FIELD declaration, which the native rule emits as its own
|
|
1143
|
+
// separate match. Without it the property stays a `@declaration.variable`
|
|
1144
|
+
// named `$repo` — a Variable, not a class-owned member — and the type
|
|
1145
|
+
// binding alone is not enough: measured, `$this->repo->save()` resolved
|
|
1146
|
+
// while `save` was unique to one class and went UNRESOLVED as soon as a
|
|
1147
|
+
// second class declared a `save`, because narrowing a same-named method
|
|
1148
|
+
// needs the receiver's member to be owned. The native typed property
|
|
1149
|
+
// resolved the identical file. The `$` is stripped for the same reason it
|
|
1150
|
+
// is on the native path: PHP stores field names unsigilled so `$obj->repo`
|
|
1151
|
+
// looks up `repo`.
|
|
1152
|
+
matches.push({
|
|
1153
|
+
'@declaration.property': (0, ast_helpers_js_1.nodeToCapture)('@declaration.property', node),
|
|
1154
|
+
'@declaration.name': (0, ast_helpers_js_1.syntheticCapture)('@declaration.name', varNameNode, varNameNode.text.replace(/^\$/, '')),
|
|
1155
|
+
});
|
|
1156
|
+
}
|
|
1157
|
+
/**
|
|
1158
|
+
* The `@var` tag on the comment siblings immediately preceding `propDecl` —
|
|
1159
|
+
* the same chain, the same regex and the same nearest-first order
|
|
1160
|
+
* `extractPropertyElementType` reads the tag through, so the two readings of
|
|
1161
|
+
* one annotation cannot disagree about WHICH annotation they read.
|
|
1162
|
+
*/
|
|
1163
|
+
function findPhpDocVarTag(propDecl) {
|
|
1164
|
+
const m = nearestPrecedingCommentMatch(propDecl, PHPDOC_VAR_RE);
|
|
1165
|
+
return m === null ? null : { type: m[1], varName: m[2] };
|
|
1166
|
+
}
|