cgraphx 2.0.2 → 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.
Files changed (680) hide show
  1. package/dist/core/code/engine/cli/analyze-config.js +320 -0
  2. package/dist/core/code/engine/cli/analyze.js +1086 -0
  3. package/dist/core/code/engine/cli/cli-message.js +83 -0
  4. package/dist/core/code/engine/cli/detect-changes-format.js +58 -0
  5. package/dist/core/code/engine/cli/embedding-dims.js +43 -0
  6. package/dist/core/code/engine/cli/format-elapsed.js +12 -0
  7. package/dist/core/code/engine/cli/help-i18n.js +144 -0
  8. package/dist/core/code/engine/cli/i18n/en.js +110 -0
  9. package/dist/core/code/engine/cli/i18n/index.js +47 -0
  10. package/dist/core/code/engine/cli/i18n/resources.js +9 -0
  11. package/dist/core/code/engine/cli/i18n/zh-CN.js +110 -0
  12. package/dist/core/code/engine/cli/lazy-action.js +67 -0
  13. package/dist/core/code/engine/cli/optional-grammars.js +136 -0
  14. package/dist/core/code/engine/cli/resolve-invocation.js +76 -0
  15. package/dist/core/code/engine/cli/status.js +156 -0
  16. package/dist/core/code/engine/cli/tool.js +384 -0
  17. package/dist/core/code/engine/config/ignore-service.js +511 -0
  18. package/dist/core/code/engine/config/supported-languages.js +17 -0
  19. package/dist/core/code/engine/core/analysis-features.js +64 -0
  20. package/dist/core/code/engine/core/analyzer-identity.js +2171 -0
  21. package/dist/core/code/engine/core/git-staleness.js +180 -0
  22. package/dist/core/code/engine/core/graph/graph.js +180 -0
  23. package/dist/core/code/engine/core/graph/import-cycles.js +106 -0
  24. package/dist/core/code/engine/core/graph/types.js +2 -0
  25. package/dist/core/code/engine/core/index-freshness.js +12 -0
  26. package/dist/core/code/engine/core/ingestion/binding-accumulator.js +341 -0
  27. package/dist/core/code/engine/core/ingestion/call-extractors/configs/c-cpp.js +168 -0
  28. package/dist/core/code/engine/core/ingestion/call-extractors/configs/csharp.js +9 -0
  29. package/dist/core/code/engine/core/ingestion/call-extractors/configs/dart.js +8 -0
  30. package/dist/core/code/engine/core/ingestion/call-extractors/configs/go.js +8 -0
  31. package/dist/core/code/engine/core/ingestion/call-extractors/configs/jvm.js +54 -0
  32. package/dist/core/code/engine/core/ingestion/call-extractors/configs/php.js +8 -0
  33. package/dist/core/code/engine/core/ingestion/call-extractors/configs/python.js +8 -0
  34. package/dist/core/code/engine/core/ingestion/call-extractors/configs/ruby.js +8 -0
  35. package/dist/core/code/engine/core/ingestion/call-extractors/configs/rust.js +8 -0
  36. package/dist/core/code/engine/core/ingestion/call-extractors/configs/swift.js +8 -0
  37. package/dist/core/code/engine/core/ingestion/call-extractors/configs/typescript-javascript.js +11 -0
  38. package/dist/core/code/engine/core/ingestion/call-extractors/generic.js +62 -0
  39. package/dist/core/code/engine/core/ingestion/call-processor.js +503 -0
  40. package/dist/core/code/engine/core/ingestion/call-routing.js +98 -0
  41. package/dist/core/code/engine/core/ingestion/call-types.js +3 -0
  42. package/dist/core/code/engine/core/ingestion/cfg/callee-cell-format.js +45 -0
  43. package/dist/core/code/engine/core/ingestion/cfg/cfg-builder.js +202 -0
  44. package/dist/core/code/engine/core/ingestion/cfg/collect.js +81 -0
  45. package/dist/core/code/engine/core/ingestion/cfg/control-dependence.js +185 -0
  46. package/dist/core/code/engine/core/ingestion/cfg/control-flow-context.js +130 -0
  47. package/dist/core/code/engine/core/ingestion/cfg/emit.js +646 -0
  48. package/dist/core/code/engine/core/ingestion/cfg/post-dominators.js +182 -0
  49. package/dist/core/code/engine/core/ingestion/cfg/reaching-def-reason-codec.js +139 -0
  50. package/dist/core/code/engine/core/ingestion/cfg/reaching-defs-graph.js +322 -0
  51. package/dist/core/code/engine/core/ingestion/cfg/reaching-defs.js +792 -0
  52. package/dist/core/code/engine/core/ingestion/cfg/synthetic-escape.js +305 -0
  53. package/dist/core/code/engine/core/ingestion/cfg/traversal-result.js +6 -0
  54. package/dist/core/code/engine/core/ingestion/cfg/types.js +14 -0
  55. package/dist/core/code/engine/core/ingestion/cfg/visitors/c-cpp-harvest.js +545 -0
  56. package/dist/core/code/engine/core/ingestion/cfg/visitors/c-cpp.js +590 -0
  57. package/dist/core/code/engine/core/ingestion/cfg/visitors/call-site-harvest.js +356 -0
  58. package/dist/core/code/engine/core/ingestion/cfg/visitors/csharp-harvest.js +593 -0
  59. package/dist/core/code/engine/core/ingestion/cfg/visitors/csharp.js +871 -0
  60. package/dist/core/code/engine/core/ingestion/cfg/visitors/dart-harvest.js +874 -0
  61. package/dist/core/code/engine/core/ingestion/cfg/visitors/dart.js +840 -0
  62. package/dist/core/code/engine/core/ingestion/cfg/visitors/go-harvest.js +625 -0
  63. package/dist/core/code/engine/core/ingestion/cfg/visitors/go.js +642 -0
  64. package/dist/core/code/engine/core/ingestion/cfg/visitors/java-harvest.js +517 -0
  65. package/dist/core/code/engine/core/ingestion/cfg/visitors/java.js +816 -0
  66. package/dist/core/code/engine/core/ingestion/cfg/visitors/kotlin-harvest.js +723 -0
  67. package/dist/core/code/engine/core/ingestion/cfg/visitors/kotlin.js +813 -0
  68. package/dist/core/code/engine/core/ingestion/cfg/visitors/php-harvest.js +630 -0
  69. package/dist/core/code/engine/core/ingestion/cfg/visitors/php.js +725 -0
  70. package/dist/core/code/engine/core/ingestion/cfg/visitors/python-harvest.js +776 -0
  71. package/dist/core/code/engine/core/ingestion/cfg/visitors/python.js +562 -0
  72. package/dist/core/code/engine/core/ingestion/cfg/visitors/ruby-harvest.js +591 -0
  73. package/dist/core/code/engine/core/ingestion/cfg/visitors/ruby.js +760 -0
  74. package/dist/core/code/engine/core/ingestion/cfg/visitors/rust-harvest.js +877 -0
  75. package/dist/core/code/engine/core/ingestion/cfg/visitors/rust.js +562 -0
  76. package/dist/core/code/engine/core/ingestion/cfg/visitors/scope-tree-harvest.js +120 -0
  77. package/dist/core/code/engine/core/ingestion/cfg/visitors/swift-harvest.js +683 -0
  78. package/dist/core/code/engine/core/ingestion/cfg/visitors/swift.js +791 -0
  79. package/dist/core/code/engine/core/ingestion/cfg/visitors/typescript-harvest.js +1060 -0
  80. package/dist/core/code/engine/core/ingestion/cfg/visitors/typescript.js +587 -0
  81. package/dist/core/code/engine/core/ingestion/class-extractors/configs/c-cpp.js +77 -0
  82. package/dist/core/code/engine/core/ingestion/class-extractors/configs/csharp.js +24 -0
  83. package/dist/core/code/engine/core/ingestion/class-extractors/configs/dart.js +10 -0
  84. package/dist/core/code/engine/core/ingestion/class-extractors/configs/go.js +28 -0
  85. package/dist/core/code/engine/core/ingestion/class-extractors/configs/jvm.js +67 -0
  86. package/dist/core/code/engine/core/ingestion/class-extractors/configs/php.js +10 -0
  87. package/dist/core/code/engine/core/ingestion/class-extractors/configs/python.js +10 -0
  88. package/dist/core/code/engine/core/ingestion/class-extractors/configs/ruby.js +13 -0
  89. package/dist/core/code/engine/core/ingestion/class-extractors/configs/rust.js +10 -0
  90. package/dist/core/code/engine/core/ingestion/class-extractors/configs/swift.js +21 -0
  91. package/dist/core/code/engine/core/ingestion/class-extractors/configs/typescript-javascript.js +31 -0
  92. package/dist/core/code/engine/core/ingestion/class-extractors/generic.js +144 -0
  93. package/dist/core/code/engine/core/ingestion/class-types.js +2 -0
  94. package/dist/core/code/engine/core/ingestion/cluster-enricher.js +174 -0
  95. package/dist/core/code/engine/core/ingestion/community-processor.js +604 -0
  96. package/dist/core/code/engine/core/ingestion/constants.js +26 -0
  97. package/dist/core/code/engine/core/ingestion/cpp-ue-preprocessor.js +263 -0
  98. package/dist/core/code/engine/core/ingestion/csharp-namespace-gate.js +133 -0
  99. package/dist/core/code/engine/core/ingestion/di-extractors/index.js +38 -0
  100. package/dist/core/code/engine/core/ingestion/di-extractors/spring.js +310 -0
  101. package/dist/core/code/engine/core/ingestion/emit-references.js +244 -0
  102. package/dist/core/code/engine/core/ingestion/entry-point-scoring.js +201 -0
  103. package/dist/core/code/engine/core/ingestion/export-detection.js +244 -0
  104. package/dist/core/code/engine/core/ingestion/field-extractor.js +29 -0
  105. package/dist/core/code/engine/core/ingestion/field-extractors/configs/c-cpp.js +107 -0
  106. package/dist/core/code/engine/core/ingestion/field-extractors/configs/csharp.js +124 -0
  107. package/dist/core/code/engine/core/ingestion/field-extractors/configs/dart.js +99 -0
  108. package/dist/core/code/engine/core/ingestion/field-extractors/configs/go.js +102 -0
  109. package/dist/core/code/engine/core/ingestion/field-extractors/configs/helpers.js +198 -0
  110. package/dist/core/code/engine/core/ingestion/field-extractors/configs/jvm.js +172 -0
  111. package/dist/core/code/engine/core/ingestion/field-extractors/configs/php.js +67 -0
  112. package/dist/core/code/engine/core/ingestion/field-extractors/configs/python.js +94 -0
  113. package/dist/core/code/engine/core/ingestion/field-extractors/configs/ruby.js +79 -0
  114. package/dist/core/code/engine/core/ingestion/field-extractors/configs/rust.js +55 -0
  115. package/dist/core/code/engine/core/ingestion/field-extractors/configs/swift.js +93 -0
  116. package/dist/core/code/engine/core/ingestion/field-extractors/configs/typescript-javascript.js +59 -0
  117. package/dist/core/code/engine/core/ingestion/field-extractors/generic.js +147 -0
  118. package/dist/core/code/engine/core/ingestion/field-extractors/typescript.js +266 -0
  119. package/dist/core/code/engine/core/ingestion/field-types.js +3 -0
  120. package/dist/core/code/engine/core/ingestion/filesystem-walker.js +136 -0
  121. package/dist/core/code/engine/core/ingestion/finalize-orchestrator.js +159 -0
  122. package/dist/core/code/engine/core/ingestion/framework-detection.js +432 -0
  123. package/dist/core/code/engine/core/ingestion/frameworks/spring/analysis-features.js +38 -0
  124. package/dist/core/code/engine/core/ingestion/frameworks/spring/annotation-arguments.js +234 -0
  125. package/dist/core/code/engine/core/ingestion/frameworks/spring/aop-candidates.js +88 -0
  126. package/dist/core/code/engine/core/ingestion/frameworks/spring/aop.js +487 -0
  127. package/dist/core/code/engine/core/ingestion/frameworks/spring/auto-configuration.js +21 -0
  128. package/dist/core/code/engine/core/ingestion/frameworks/spring/bean-candidates.js +189 -0
  129. package/dist/core/code/engine/core/ingestion/frameworks/spring/bean-catalog.js +32 -0
  130. package/dist/core/code/engine/core/ingestion/frameworks/spring/bean-factories.js +73 -0
  131. package/dist/core/code/engine/core/ingestion/frameworks/spring/conditionals.js +323 -0
  132. package/dist/core/code/engine/core/ingestion/frameworks/spring/config-bindings.js +119 -0
  133. package/dist/core/code/engine/core/ingestion/frameworks/spring/di-metadata.js +385 -0
  134. package/dist/core/code/engine/core/ingestion/frameworks/spring/resource-injection.js +96 -0
  135. package/dist/core/code/engine/core/ingestion/import-resolvers/configs/c-cpp.js +17 -0
  136. package/dist/core/code/engine/core/ingestion/import-resolvers/configs/csharp.js +46 -0
  137. package/dist/core/code/engine/core/ingestion/import-resolvers/configs/dart.js +59 -0
  138. package/dist/core/code/engine/core/ingestion/import-resolvers/configs/go.js +30 -0
  139. package/dist/core/code/engine/core/ingestion/import-resolvers/configs/jvm.js +73 -0
  140. package/dist/core/code/engine/core/ingestion/import-resolvers/configs/php.js +19 -0
  141. package/dist/core/code/engine/core/ingestion/import-resolvers/configs/python.js +45 -0
  142. package/dist/core/code/engine/core/ingestion/import-resolvers/configs/ruby.js +20 -0
  143. package/dist/core/code/engine/core/ingestion/import-resolvers/configs/rust.js +58 -0
  144. package/dist/core/code/engine/core/ingestion/import-resolvers/configs/swift.js +94 -0
  145. package/dist/core/code/engine/core/ingestion/import-resolvers/configs/typescript-javascript.js +26 -0
  146. package/dist/core/code/engine/core/ingestion/import-resolvers/csharp.js +128 -0
  147. package/dist/core/code/engine/core/ingestion/import-resolvers/go.js +50 -0
  148. package/dist/core/code/engine/core/ingestion/import-resolvers/jvm.js +112 -0
  149. package/dist/core/code/engine/core/ingestion/import-resolvers/php.js +80 -0
  150. package/dist/core/code/engine/core/ingestion/import-resolvers/python.js +75 -0
  151. package/dist/core/code/engine/core/ingestion/import-resolvers/resolver-factory.js +36 -0
  152. package/dist/core/code/engine/core/ingestion/import-resolvers/ruby.js +20 -0
  153. package/dist/core/code/engine/core/ingestion/import-resolvers/rust.js +79 -0
  154. package/dist/core/code/engine/core/ingestion/import-resolvers/standard.js +180 -0
  155. package/dist/core/code/engine/core/ingestion/import-resolvers/types.js +7 -0
  156. package/dist/core/code/engine/core/ingestion/import-resolvers/utils.js +153 -0
  157. package/dist/core/code/engine/core/ingestion/import-target-adapter.js +99 -0
  158. package/dist/core/code/engine/core/ingestion/language-config.js +391 -0
  159. package/dist/core/code/engine/core/ingestion/language-provider.js +25 -0
  160. package/dist/core/code/engine/core/ingestion/languages/c/arity-metadata.js +98 -0
  161. package/dist/core/code/engine/core/ingestion/languages/c/arity.js +21 -0
  162. package/dist/core/code/engine/core/ingestion/languages/c/capture-side-channel.js +69 -0
  163. package/dist/core/code/engine/core/ingestion/languages/c/captures.js +189 -0
  164. package/dist/core/code/engine/core/ingestion/languages/c/header-scan.js +58 -0
  165. package/dist/core/code/engine/core/ingestion/languages/c/import-decomposer.js +68 -0
  166. package/dist/core/code/engine/core/ingestion/languages/c/import-target.js +103 -0
  167. package/dist/core/code/engine/core/ingestion/languages/c/index.js +33 -0
  168. package/dist/core/code/engine/core/ingestion/languages/c/interpret.js +53 -0
  169. package/dist/core/code/engine/core/ingestion/languages/c/merge-bindings.js +26 -0
  170. package/dist/core/code/engine/core/ingestion/languages/c/query.js +210 -0
  171. package/dist/core/code/engine/core/ingestion/languages/c/scope-resolver.js +112 -0
  172. package/dist/core/code/engine/core/ingestion/languages/c/simple-hooks.js +24 -0
  173. package/dist/core/code/engine/core/ingestion/languages/c/static-linkage.js +109 -0
  174. package/dist/core/code/engine/core/ingestion/languages/c-cpp.js +506 -0
  175. package/dist/core/code/engine/core/ingestion/languages/cpp/adl.js +804 -0
  176. package/dist/core/code/engine/core/ingestion/languages/cpp/arity-metadata.js +258 -0
  177. package/dist/core/code/engine/core/ingestion/languages/cpp/arity.js +37 -0
  178. package/dist/core/code/engine/core/ingestion/languages/cpp/capture-side-channel.js +89 -0
  179. package/dist/core/code/engine/core/ingestion/languages/cpp/captures.js +1975 -0
  180. package/dist/core/code/engine/core/ingestion/languages/cpp/constraint-extractor.js +311 -0
  181. package/dist/core/code/engine/core/ingestion/languages/cpp/constraint-filter.js +210 -0
  182. package/dist/core/code/engine/core/ingestion/languages/cpp/conversion-rank.js +163 -0
  183. package/dist/core/code/engine/core/ingestion/languages/cpp/file-local-linkage.js +327 -0
  184. package/dist/core/code/engine/core/ingestion/languages/cpp/header-scan.js +53 -0
  185. package/dist/core/code/engine/core/ingestion/languages/cpp/import-decomposer.js +134 -0
  186. package/dist/core/code/engine/core/ingestion/languages/cpp/import-target.js +16 -0
  187. package/dist/core/code/engine/core/ingestion/languages/cpp/index.js +33 -0
  188. package/dist/core/code/engine/core/ingestion/languages/cpp/inline-namespaces.js +379 -0
  189. package/dist/core/code/engine/core/ingestion/languages/cpp/interpret.js +239 -0
  190. package/dist/core/code/engine/core/ingestion/languages/cpp/member-lookup.js +470 -0
  191. package/dist/core/code/engine/core/ingestion/languages/cpp/merge-bindings.js +32 -0
  192. package/dist/core/code/engine/core/ingestion/languages/cpp/query.js +763 -0
  193. package/dist/core/code/engine/core/ingestion/languages/cpp/range-bindings.js +230 -0
  194. package/dist/core/code/engine/core/ingestion/languages/cpp/scope-resolver.js +354 -0
  195. package/dist/core/code/engine/core/ingestion/languages/cpp/simple-hooks.js +67 -0
  196. package/dist/core/code/engine/core/ingestion/languages/cpp/two-phase-lookup.js +348 -0
  197. package/dist/core/code/engine/core/ingestion/languages/cpp/type-classifier.js +56 -0
  198. package/dist/core/code/engine/core/ingestion/languages/cpp/user-defined-conversions.js +128 -0
  199. package/dist/core/code/engine/core/ingestion/languages/csharp/accessor-unwrap.js +67 -0
  200. package/dist/core/code/engine/core/ingestion/languages/csharp/arity-metadata.js +49 -0
  201. package/dist/core/code/engine/core/ingestion/languages/csharp/arity.js +40 -0
  202. package/dist/core/code/engine/core/ingestion/languages/csharp/cache-stats.js +32 -0
  203. package/dist/core/code/engine/core/ingestion/languages/csharp/captures.js +557 -0
  204. package/dist/core/code/engine/core/ingestion/languages/csharp/import-decomposer.js +96 -0
  205. package/dist/core/code/engine/core/ingestion/languages/csharp/import-target.js +176 -0
  206. package/dist/core/code/engine/core/ingestion/languages/csharp/index.js +95 -0
  207. package/dist/core/code/engine/core/ingestion/languages/csharp/interpret.js +150 -0
  208. package/dist/core/code/engine/core/ingestion/languages/csharp/merge-bindings.js +58 -0
  209. package/dist/core/code/engine/core/ingestion/languages/csharp/namespace-siblings.js +708 -0
  210. package/dist/core/code/engine/core/ingestion/languages/csharp/qualified-type-names.js +62 -0
  211. package/dist/core/code/engine/core/ingestion/languages/csharp/query.js +578 -0
  212. package/dist/core/code/engine/core/ingestion/languages/csharp/receiver-binding.js +142 -0
  213. package/dist/core/code/engine/core/ingestion/languages/csharp/resolution-config.js +18 -0
  214. package/dist/core/code/engine/core/ingestion/languages/csharp/scope-resolver.js +84 -0
  215. package/dist/core/code/engine/core/ingestion/languages/csharp/simple-hooks.js +81 -0
  216. package/dist/core/code/engine/core/ingestion/languages/csharp.js +204 -0
  217. package/dist/core/code/engine/core/ingestion/languages/dart/arity-metadata.js +38 -0
  218. package/dist/core/code/engine/core/ingestion/languages/dart/arity.js +34 -0
  219. package/dist/core/code/engine/core/ingestion/languages/dart/built-ins.js +37 -0
  220. package/dist/core/code/engine/core/ingestion/languages/dart/cache-stats.js +30 -0
  221. package/dist/core/code/engine/core/ingestion/languages/dart/captures.js +1096 -0
  222. package/dist/core/code/engine/core/ingestion/languages/dart/expand-wildcards.js +34 -0
  223. package/dist/core/code/engine/core/ingestion/languages/dart/extension-type-preprocess.js +33 -0
  224. package/dist/core/code/engine/core/ingestion/languages/dart/import-target.js +68 -0
  225. package/dist/core/code/engine/core/ingestion/languages/dart/index.js +45 -0
  226. package/dist/core/code/engine/core/ingestion/languages/dart/interpret.js +101 -0
  227. package/dist/core/code/engine/core/ingestion/languages/dart/merge-bindings.js +42 -0
  228. package/dist/core/code/engine/core/ingestion/languages/dart/query.js +246 -0
  229. package/dist/core/code/engine/core/ingestion/languages/dart/receiver-binding.js +90 -0
  230. package/dist/core/code/engine/core/ingestion/languages/dart/scope-resolver.js +197 -0
  231. package/dist/core/code/engine/core/ingestion/languages/dart/signature-bindings.js +54 -0
  232. package/dist/core/code/engine/core/ingestion/languages/dart/simple-hooks.js +61 -0
  233. package/dist/core/code/engine/core/ingestion/languages/dart.js +138 -0
  234. package/dist/core/code/engine/core/ingestion/languages/go/arity-metadata.js +71 -0
  235. package/dist/core/code/engine/core/ingestion/languages/go/arity.js +17 -0
  236. package/dist/core/code/engine/core/ingestion/languages/go/cache-stats.js +21 -0
  237. package/dist/core/code/engine/core/ingestion/languages/go/captures.js +492 -0
  238. package/dist/core/code/engine/core/ingestion/languages/go/expand-wildcards.js +97 -0
  239. package/dist/core/code/engine/core/ingestion/languages/go/generic-type-parameters.js +146 -0
  240. package/dist/core/code/engine/core/ingestion/languages/go/import-decomposer.js +47 -0
  241. package/dist/core/code/engine/core/ingestion/languages/go/import-target.js +70 -0
  242. package/dist/core/code/engine/core/ingestion/languages/go/index.js +39 -0
  243. package/dist/core/code/engine/core/ingestion/languages/go/interface-impls.js +955 -0
  244. package/dist/core/code/engine/core/ingestion/languages/go/interpret.js +177 -0
  245. package/dist/core/code/engine/core/ingestion/languages/go/merge-bindings.js +21 -0
  246. package/dist/core/code/engine/core/ingestion/languages/go/method-owners.js +131 -0
  247. package/dist/core/code/engine/core/ingestion/languages/go/namespace-mirror.js +56 -0
  248. package/dist/core/code/engine/core/ingestion/languages/go/package-clause.js +79 -0
  249. package/dist/core/code/engine/core/ingestion/languages/go/package-siblings.js +83 -0
  250. package/dist/core/code/engine/core/ingestion/languages/go/query.js +298 -0
  251. package/dist/core/code/engine/core/ingestion/languages/go/range-binding.js +127 -0
  252. package/dist/core/code/engine/core/ingestion/languages/go/receiver-binding.js +24 -0
  253. package/dist/core/code/engine/core/ingestion/languages/go/scope-resolver.js +75 -0
  254. package/dist/core/code/engine/core/ingestion/languages/go/simple-hooks.js +31 -0
  255. package/dist/core/code/engine/core/ingestion/languages/go/type-binding.js +279 -0
  256. package/dist/core/code/engine/core/ingestion/languages/go.js +160 -0
  257. package/dist/core/code/engine/core/ingestion/languages/index.js +66 -0
  258. package/dist/core/code/engine/core/ingestion/languages/java/analysis-features.js +16 -0
  259. package/dist/core/code/engine/core/ingestion/languages/java/arity-metadata.js +43 -0
  260. package/dist/core/code/engine/core/ingestion/languages/java/arity.js +27 -0
  261. package/dist/core/code/engine/core/ingestion/languages/java/cache-stats.js +32 -0
  262. package/dist/core/code/engine/core/ingestion/languages/java/capture-side-channel.js +123 -0
  263. package/dist/core/code/engine/core/ingestion/languages/java/captures.js +791 -0
  264. package/dist/core/code/engine/core/ingestion/languages/java/import-decomposer.js +88 -0
  265. package/dist/core/code/engine/core/ingestion/languages/java/import-target.js +103 -0
  266. package/dist/core/code/engine/core/ingestion/languages/java/index.js +43 -0
  267. package/dist/core/code/engine/core/ingestion/languages/java/interpret.js +146 -0
  268. package/dist/core/code/engine/core/ingestion/languages/java/merge-bindings.js +43 -0
  269. package/dist/core/code/engine/core/ingestion/languages/java/package-facts.js +16 -0
  270. package/dist/core/code/engine/core/ingestion/languages/java/package-siblings.js +11 -0
  271. package/dist/core/code/engine/core/ingestion/languages/java/query.js +319 -0
  272. package/dist/core/code/engine/core/ingestion/languages/java/receiver-binding.js +98 -0
  273. package/dist/core/code/engine/core/ingestion/languages/java/scope-resolver.js +213 -0
  274. package/dist/core/code/engine/core/ingestion/languages/java/simple-hooks.js +39 -0
  275. package/dist/core/code/engine/core/ingestion/languages/java/spring-aop.js +53 -0
  276. package/dist/core/code/engine/core/ingestion/languages/java/spring-bean-metadata.js +11 -0
  277. package/dist/core/code/engine/core/ingestion/languages/java/spring-conditionals.js +52 -0
  278. package/dist/core/code/engine/core/ingestion/languages/java/spring-config-bindings.js +222 -0
  279. package/dist/core/code/engine/core/ingestion/languages/java/spring-di.js +165 -0
  280. package/dist/core/code/engine/core/ingestion/languages/java.js +192 -0
  281. package/dist/core/code/engine/core/ingestion/languages/javascript/arity.js +15 -0
  282. package/dist/core/code/engine/core/ingestion/languages/javascript/captures.js +1122 -0
  283. package/dist/core/code/engine/core/ingestion/languages/javascript/import-target.js +56 -0
  284. package/dist/core/code/engine/core/ingestion/languages/javascript/index.js +109 -0
  285. package/dist/core/code/engine/core/ingestion/languages/javascript/interpret.js +45 -0
  286. package/dist/core/code/engine/core/ingestion/languages/javascript/merge-bindings.js +21 -0
  287. package/dist/core/code/engine/core/ingestion/languages/javascript/query.js +659 -0
  288. package/dist/core/code/engine/core/ingestion/languages/javascript/scope-resolver.js +78 -0
  289. package/dist/core/code/engine/core/ingestion/languages/javascript/simple-hooks.js +44 -0
  290. package/dist/core/code/engine/core/ingestion/languages/jvm/package-facts.js +46 -0
  291. package/dist/core/code/engine/core/ingestion/languages/jvm/package-siblings.js +200 -0
  292. package/dist/core/code/engine/core/ingestion/languages/kotlin/arity-metadata.js +23 -0
  293. package/dist/core/code/engine/core/ingestion/languages/kotlin/arity.js +18 -0
  294. package/dist/core/code/engine/core/ingestion/languages/kotlin/cache-stats.js +21 -0
  295. package/dist/core/code/engine/core/ingestion/languages/kotlin/capture-side-channel.js +160 -0
  296. package/dist/core/code/engine/core/ingestion/languages/kotlin/captures.js +1262 -0
  297. package/dist/core/code/engine/core/ingestion/languages/kotlin/companion-scopes.js +72 -0
  298. package/dist/core/code/engine/core/ingestion/languages/kotlin/import-decomposer.js +40 -0
  299. package/dist/core/code/engine/core/ingestion/languages/kotlin/import-target.js +135 -0
  300. package/dist/core/code/engine/core/ingestion/languages/kotlin/index.js +26 -0
  301. package/dist/core/code/engine/core/ingestion/languages/kotlin/interpret.js +75 -0
  302. package/dist/core/code/engine/core/ingestion/languages/kotlin/merge-bindings.js +28 -0
  303. package/dist/core/code/engine/core/ingestion/languages/kotlin/owners.js +134 -0
  304. package/dist/core/code/engine/core/ingestion/languages/kotlin/package-facts.js +16 -0
  305. package/dist/core/code/engine/core/ingestion/languages/kotlin/package-siblings.js +11 -0
  306. package/dist/core/code/engine/core/ingestion/languages/kotlin/query.js +243 -0
  307. package/dist/core/code/engine/core/ingestion/languages/kotlin/receiver-binding.js +103 -0
  308. package/dist/core/code/engine/core/ingestion/languages/kotlin/scope-resolver.js +207 -0
  309. package/dist/core/code/engine/core/ingestion/languages/kotlin/simple-hooks.js +42 -0
  310. package/dist/core/code/engine/core/ingestion/languages/kotlin/spring-aop.js +68 -0
  311. package/dist/core/code/engine/core/ingestion/languages/kotlin/spring-bean-metadata.js +11 -0
  312. package/dist/core/code/engine/core/ingestion/languages/kotlin/spring-conditionals.js +53 -0
  313. package/dist/core/code/engine/core/ingestion/languages/kotlin/spring-di.js +304 -0
  314. package/dist/core/code/engine/core/ingestion/languages/kotlin.js +189 -0
  315. package/dist/core/code/engine/core/ingestion/languages/php/arity-metadata.js +66 -0
  316. package/dist/core/code/engine/core/ingestion/languages/php/arity.js +43 -0
  317. package/dist/core/code/engine/core/ingestion/languages/php/cache-stats.js +32 -0
  318. package/dist/core/code/engine/core/ingestion/languages/php/captures.js +1166 -0
  319. package/dist/core/code/engine/core/ingestion/languages/php/import-decomposer.js +238 -0
  320. package/dist/core/code/engine/core/ingestion/languages/php/import-target.js +213 -0
  321. package/dist/core/code/engine/core/ingestion/languages/php/index.js +85 -0
  322. package/dist/core/code/engine/core/ingestion/languages/php/interpret.js +256 -0
  323. package/dist/core/code/engine/core/ingestion/languages/php/merge-bindings.js +50 -0
  324. package/dist/core/code/engine/core/ingestion/languages/php/namespace-siblings.js +353 -0
  325. package/dist/core/code/engine/core/ingestion/languages/php/query.js +391 -0
  326. package/dist/core/code/engine/core/ingestion/languages/php/receiver-binding.js +135 -0
  327. package/dist/core/code/engine/core/ingestion/languages/php/scope-resolver.js +361 -0
  328. package/dist/core/code/engine/core/ingestion/languages/php/simple-hooks.js +116 -0
  329. package/dist/core/code/engine/core/ingestion/languages/php.js +302 -0
  330. package/dist/core/code/engine/core/ingestion/languages/python/arity-metadata.js +49 -0
  331. package/dist/core/code/engine/core/ingestion/languages/python/arity.js +41 -0
  332. package/dist/core/code/engine/core/ingestion/languages/python/cache-stats.js +34 -0
  333. package/dist/core/code/engine/core/ingestion/languages/python/captures.js +299 -0
  334. package/dist/core/code/engine/core/ingestion/languages/python/depends-references.js +68 -0
  335. package/dist/core/code/engine/core/ingestion/languages/python/import-decomposer.js +115 -0
  336. package/dist/core/code/engine/core/ingestion/languages/python/import-target.js +440 -0
  337. package/dist/core/code/engine/core/ingestion/languages/python/index-stats.js +30 -0
  338. package/dist/core/code/engine/core/ingestion/languages/python/index.js +96 -0
  339. package/dist/core/code/engine/core/ingestion/languages/python/interpret.js +430 -0
  340. package/dist/core/code/engine/core/ingestion/languages/python/merge-bindings.js +47 -0
  341. package/dist/core/code/engine/core/ingestion/languages/python/query.js +323 -0
  342. package/dist/core/code/engine/core/ingestion/languages/python/receiver-binding.js +310 -0
  343. package/dist/core/code/engine/core/ingestion/languages/python/scope-resolver.js +80 -0
  344. package/dist/core/code/engine/core/ingestion/languages/python/simple-hooks.js +53 -0
  345. package/dist/core/code/engine/core/ingestion/languages/python.js +140 -0
  346. package/dist/core/code/engine/core/ingestion/languages/ruby/arity.js +41 -0
  347. package/dist/core/code/engine/core/ingestion/languages/ruby/cache-stats.js +21 -0
  348. package/dist/core/code/engine/core/ingestion/languages/ruby/captures.js +864 -0
  349. package/dist/core/code/engine/core/ingestion/languages/ruby/import-target.js +88 -0
  350. package/dist/core/code/engine/core/ingestion/languages/ruby/index.js +29 -0
  351. package/dist/core/code/engine/core/ingestion/languages/ruby/interpret.js +115 -0
  352. package/dist/core/code/engine/core/ingestion/languages/ruby/merge-bindings.js +21 -0
  353. package/dist/core/code/engine/core/ingestion/languages/ruby/query.js +349 -0
  354. package/dist/core/code/engine/core/ingestion/languages/ruby/receiver-binding.js +70 -0
  355. package/dist/core/code/engine/core/ingestion/languages/ruby/scope-resolver.js +263 -0
  356. package/dist/core/code/engine/core/ingestion/languages/ruby/simple-hooks.js +68 -0
  357. package/dist/core/code/engine/core/ingestion/languages/ruby.js +215 -0
  358. package/dist/core/code/engine/core/ingestion/languages/rust/arity.js +16 -0
  359. package/dist/core/code/engine/core/ingestion/languages/rust/cache-stats.js +21 -0
  360. package/dist/core/code/engine/core/ingestion/languages/rust/captures.js +304 -0
  361. package/dist/core/code/engine/core/ingestion/languages/rust/import-decomposer.js +167 -0
  362. package/dist/core/code/engine/core/ingestion/languages/rust/import-target.js +108 -0
  363. package/dist/core/code/engine/core/ingestion/languages/rust/index.js +29 -0
  364. package/dist/core/code/engine/core/ingestion/languages/rust/interpret.js +201 -0
  365. package/dist/core/code/engine/core/ingestion/languages/rust/merge-bindings.js +21 -0
  366. package/dist/core/code/engine/core/ingestion/languages/rust/method-owners.js +76 -0
  367. package/dist/core/code/engine/core/ingestion/languages/rust/module-path.js +222 -0
  368. package/dist/core/code/engine/core/ingestion/languages/rust/qualified-call.js +482 -0
  369. package/dist/core/code/engine/core/ingestion/languages/rust/query.js +280 -0
  370. package/dist/core/code/engine/core/ingestion/languages/rust/range-binding.js +687 -0
  371. package/dist/core/code/engine/core/ingestion/languages/rust/receiver-binding.js +148 -0
  372. package/dist/core/code/engine/core/ingestion/languages/rust/scope-resolver.js +151 -0
  373. package/dist/core/code/engine/core/ingestion/languages/rust/simple-hooks.js +32 -0
  374. package/dist/core/code/engine/core/ingestion/languages/rust.js +183 -0
  375. package/dist/core/code/engine/core/ingestion/languages/swift/arity-metadata.js +44 -0
  376. package/dist/core/code/engine/core/ingestion/languages/swift/arity.js +45 -0
  377. package/dist/core/code/engine/core/ingestion/languages/swift/base-type.js +30 -0
  378. package/dist/core/code/engine/core/ingestion/languages/swift/cache-stats.js +32 -0
  379. package/dist/core/code/engine/core/ingestion/languages/swift/captures.js +594 -0
  380. package/dist/core/code/engine/core/ingestion/languages/swift/conditional-directive-preprocess.js +256 -0
  381. package/dist/core/code/engine/core/ingestion/languages/swift/implicit-imports.js +60 -0
  382. package/dist/core/code/engine/core/ingestion/languages/swift/import-decomposer.js +87 -0
  383. package/dist/core/code/engine/core/ingestion/languages/swift/import-target.js +84 -0
  384. package/dist/core/code/engine/core/ingestion/languages/swift/index.js +56 -0
  385. package/dist/core/code/engine/core/ingestion/languages/swift/interpret.js +93 -0
  386. package/dist/core/code/engine/core/ingestion/languages/swift/merge-bindings.js +51 -0
  387. package/dist/core/code/engine/core/ingestion/languages/swift/query.js +226 -0
  388. package/dist/core/code/engine/core/ingestion/languages/swift/receiver-binding.js +169 -0
  389. package/dist/core/code/engine/core/ingestion/languages/swift/scope-resolver.js +192 -0
  390. package/dist/core/code/engine/core/ingestion/languages/swift/sibling-type-bindings.js +68 -0
  391. package/dist/core/code/engine/core/ingestion/languages/swift/signature-bindings.js +69 -0
  392. package/dist/core/code/engine/core/ingestion/languages/swift/simple-hooks.js +65 -0
  393. package/dist/core/code/engine/core/ingestion/languages/swift/target-grouping.js +97 -0
  394. package/dist/core/code/engine/core/ingestion/languages/swift/target-siblings.js +74 -0
  395. package/dist/core/code/engine/core/ingestion/languages/swift.js +246 -0
  396. package/dist/core/code/engine/core/ingestion/languages/typescript/arity-metadata.js +106 -0
  397. package/dist/core/code/engine/core/ingestion/languages/typescript/arity.js +57 -0
  398. package/dist/core/code/engine/core/ingestion/languages/typescript/array-callback.js +58 -0
  399. package/dist/core/code/engine/core/ingestion/languages/typescript/cache-stats.js +34 -0
  400. package/dist/core/code/engine/core/ingestion/languages/typescript/captures.js +956 -0
  401. package/dist/core/code/engine/core/ingestion/languages/typescript/cjs-export-assignment.js +535 -0
  402. package/dist/core/code/engine/core/ingestion/languages/typescript/cjs-module-exports.js +196 -0
  403. package/dist/core/code/engine/core/ingestion/languages/typescript/import-decomposer.js +374 -0
  404. package/dist/core/code/engine/core/ingestion/languages/typescript/import-target.js +65 -0
  405. package/dist/core/code/engine/core/ingestion/languages/typescript/index.js +108 -0
  406. package/dist/core/code/engine/core/ingestion/languages/typescript/interpret.js +344 -0
  407. package/dist/core/code/engine/core/ingestion/languages/typescript/merge-bindings.js +161 -0
  408. package/dist/core/code/engine/core/ingestion/languages/typescript/nuxt-auto-imports.js +325 -0
  409. package/dist/core/code/engine/core/ingestion/languages/typescript/query.js +1328 -0
  410. package/dist/core/code/engine/core/ingestion/languages/typescript/receiver-binding.js +201 -0
  411. package/dist/core/code/engine/core/ingestion/languages/typescript/scope-resolver.js +293 -0
  412. package/dist/core/code/engine/core/ingestion/languages/typescript/simple-hooks.js +139 -0
  413. package/dist/core/code/engine/core/ingestion/languages/typescript.js +455 -0
  414. package/dist/core/code/engine/core/ingestion/languages/vue/captures.js +70 -0
  415. package/dist/core/code/engine/core/ingestion/languages/vue/import-target.js +61 -0
  416. package/dist/core/code/engine/core/ingestion/languages/vue/index.js +55 -0
  417. package/dist/core/code/engine/core/ingestion/languages/vue/scope-resolver.js +295 -0
  418. package/dist/core/code/engine/core/ingestion/languages/vue.js +96 -0
  419. package/dist/core/code/engine/core/ingestion/local-symbol-pruner.js +68 -0
  420. package/dist/core/code/engine/core/ingestion/method-extractors/configs/c-cpp.js +387 -0
  421. package/dist/core/code/engine/core/ingestion/method-extractors/configs/csharp.js +290 -0
  422. package/dist/core/code/engine/core/ingestion/method-extractors/configs/dart.js +392 -0
  423. package/dist/core/code/engine/core/ingestion/method-extractors/configs/go.js +179 -0
  424. package/dist/core/code/engine/core/ingestion/method-extractors/configs/jvm.js +350 -0
  425. package/dist/core/code/engine/core/ingestion/method-extractors/configs/php.js +306 -0
  426. package/dist/core/code/engine/core/ingestion/method-extractors/configs/python.js +312 -0
  427. package/dist/core/code/engine/core/ingestion/method-extractors/configs/ruby.js +289 -0
  428. package/dist/core/code/engine/core/ingestion/method-extractors/configs/rust.js +198 -0
  429. package/dist/core/code/engine/core/ingestion/method-extractors/configs/swift.js +286 -0
  430. package/dist/core/code/engine/core/ingestion/method-extractors/configs/typescript-javascript.js +341 -0
  431. package/dist/core/code/engine/core/ingestion/method-extractors/generic.js +209 -0
  432. package/dist/core/code/engine/core/ingestion/method-types.js +3 -0
  433. package/dist/core/code/engine/core/ingestion/model/field-registry.js +41 -0
  434. package/dist/core/code/engine/core/ingestion/model/index.js +52 -0
  435. package/dist/core/code/engine/core/ingestion/model/method-registry.js +138 -0
  436. package/dist/core/code/engine/core/ingestion/model/owned-members-lookup.js +46 -0
  437. package/dist/core/code/engine/core/ingestion/model/registration-table.js +234 -0
  438. package/dist/core/code/engine/core/ingestion/model/resolve.js +183 -0
  439. package/dist/core/code/engine/core/ingestion/model/scope-resolution-indexes.js +43 -0
  440. package/dist/core/code/engine/core/ingestion/model/semantic-model.js +179 -0
  441. package/dist/core/code/engine/core/ingestion/model/symbol-table.js +216 -0
  442. package/dist/core/code/engine/core/ingestion/model/type-registry.js +84 -0
  443. package/dist/core/code/engine/core/ingestion/mro-processor.js +709 -0
  444. package/dist/core/code/engine/core/ingestion/parsing-processor.js +312 -0
  445. package/dist/core/code/engine/core/ingestion/pipeline-phases/communities.js +69 -0
  446. package/dist/core/code/engine/core/ingestion/pipeline-phases/cross-file.js +71 -0
  447. package/dist/core/code/engine/core/ingestion/pipeline-phases/di.js +338 -0
  448. package/dist/core/code/engine/core/ingestion/pipeline-phases/http-api-calls.js +312 -0
  449. package/dist/core/code/engine/core/ingestion/pipeline-phases/index.js +54 -0
  450. package/dist/core/code/engine/core/ingestion/pipeline-phases/mro.js +40 -0
  451. package/dist/core/code/engine/core/ingestion/pipeline-phases/orm.js +78 -0
  452. package/dist/core/code/engine/core/ingestion/pipeline-phases/parse-impl.js +1286 -0
  453. package/dist/core/code/engine/core/ingestion/pipeline-phases/parse.js +41 -0
  454. package/dist/core/code/engine/core/ingestion/pipeline-phases/processes.js +193 -0
  455. package/dist/core/code/engine/core/ingestion/pipeline-phases/prune-local-symbols.js +29 -0
  456. package/dist/core/code/engine/core/ingestion/pipeline-phases/registry.js +52 -0
  457. package/dist/core/code/engine/core/ingestion/pipeline-phases/routes.js +409 -0
  458. package/dist/core/code/engine/core/ingestion/pipeline-phases/rpc-edges.js +301 -0
  459. package/dist/core/code/engine/core/ingestion/pipeline-phases/runner.js +207 -0
  460. package/dist/core/code/engine/core/ingestion/pipeline-phases/scan.js +49 -0
  461. package/dist/core/code/engine/core/ingestion/pipeline-phases/spring-aop.js +442 -0
  462. package/dist/core/code/engine/core/ingestion/pipeline-phases/spring-auto-configuration.js +264 -0
  463. package/dist/core/code/engine/core/ingestion/pipeline-phases/spring-config.js +440 -0
  464. package/dist/core/code/engine/core/ingestion/pipeline-phases/structure.js +38 -0
  465. package/dist/core/code/engine/core/ingestion/pipeline-phases/tools.js +89 -0
  466. package/dist/core/code/engine/core/ingestion/pipeline-phases/types.js +40 -0
  467. package/dist/core/code/engine/core/ingestion/pipeline.js +152 -0
  468. package/dist/core/code/engine/core/ingestion/process-processor.js +325 -0
  469. package/dist/core/code/engine/core/ingestion/resolve-references.js +205 -0
  470. package/dist/core/code/engine/core/ingestion/route-extractors/constant-resolver.js +135 -0
  471. package/dist/core/code/engine/core/ingestion/route-extractors/django-root-discovery.js +221 -0
  472. package/dist/core/code/engine/core/ingestion/route-extractors/django.js +428 -0
  473. package/dist/core/code/engine/core/ingestion/route-extractors/expo.js +39 -0
  474. package/dist/core/code/engine/core/ingestion/route-extractors/fastapi-router-bindings.js +264 -0
  475. package/dist/core/code/engine/core/ingestion/route-extractors/laravel.js +501 -0
  476. package/dist/core/code/engine/core/ingestion/route-extractors/middleware.js +175 -0
  477. package/dist/core/code/engine/core/ingestion/route-extractors/nextjs.js +81 -0
  478. package/dist/core/code/engine/core/ingestion/route-extractors/php.js +25 -0
  479. package/dist/core/code/engine/core/ingestion/route-extractors/python-const-resolver.js +307 -0
  480. package/dist/core/code/engine/core/ingestion/route-extractors/response-shapes.js +299 -0
  481. package/dist/core/code/engine/core/ingestion/route-extractors/route-path.js +71 -0
  482. package/dist/core/code/engine/core/ingestion/route-extractors/spring-shared.js +310 -0
  483. package/dist/core/code/engine/core/ingestion/route-extractors/spring.js +441 -0
  484. package/dist/core/code/engine/core/ingestion/scope-extractor-bridge.js +60 -0
  485. package/dist/core/code/engine/core/ingestion/scope-extractor.js +1373 -0
  486. package/dist/core/code/engine/core/ingestion/scope-resolution/contract/scope-resolver.js +282 -0
  487. package/dist/core/code/engine/core/ingestion/scope-resolution/graph-bridge/callee-id-sink.js +72 -0
  488. package/dist/core/code/engine/core/ingestion/scope-resolution/graph-bridge/edges.js +194 -0
  489. package/dist/core/code/engine/core/ingestion/scope-resolution/graph-bridge/ids.js +472 -0
  490. package/dist/core/code/engine/core/ingestion/scope-resolution/graph-bridge/imports-to-edges.js +49 -0
  491. package/dist/core/code/engine/core/ingestion/scope-resolution/graph-bridge/method-dispatch.js +43 -0
  492. package/dist/core/code/engine/core/ingestion/scope-resolution/graph-bridge/node-lookup.js +274 -0
  493. package/dist/core/code/engine/core/ingestion/scope-resolution/graph-bridge/references-to-edges.js +93 -0
  494. package/dist/core/code/engine/core/ingestion/scope-resolution/passes/callable-value-flow.js +1240 -0
  495. package/dist/core/code/engine/core/ingestion/scope-resolution/passes/compound-receiver.js +1174 -0
  496. package/dist/core/code/engine/core/ingestion/scope-resolution/passes/free-call-fallback.js +873 -0
  497. package/dist/core/code/engine/core/ingestion/scope-resolution/passes/imported-return-types.js +226 -0
  498. package/dist/core/code/engine/core/ingestion/scope-resolution/passes/mro.js +107 -0
  499. package/dist/core/code/engine/core/ingestion/scope-resolution/passes/overload-narrowing.js +441 -0
  500. package/dist/core/code/engine/core/ingestion/scope-resolution/passes/property-dispatch.js +122 -0
  501. package/dist/core/code/engine/core/ingestion/scope-resolution/passes/receiver-bound-calls.js +1720 -0
  502. package/dist/core/code/engine/core/ingestion/scope-resolution/pipeline/phase.js +395 -0
  503. package/dist/core/code/engine/core/ingestion/scope-resolution/pipeline/reconcile-ownership.js +208 -0
  504. package/dist/core/code/engine/core/ingestion/scope-resolution/pipeline/registry.js +49 -0
  505. package/dist/core/code/engine/core/ingestion/scope-resolution/pipeline/run.js +632 -0
  506. package/dist/core/code/engine/core/ingestion/scope-resolution/pipeline/validate-bindings-immutability.js +112 -0
  507. package/dist/core/code/engine/core/ingestion/scope-resolution/resolution-outcome.js +41 -0
  508. package/dist/core/code/engine/core/ingestion/scope-resolution/scope/namespace-targets.js +81 -0
  509. package/dist/core/code/engine/core/ingestion/scope-resolution/scope/walkers.js +1835 -0
  510. package/dist/core/code/engine/core/ingestion/scope-resolution/unresolved-receivers.js +242 -0
  511. package/dist/core/code/engine/core/ingestion/scope-resolution/utils/definition-id.js +22 -0
  512. package/dist/core/code/engine/core/ingestion/scope-resolution/workspace-index.js +151 -0
  513. package/dist/core/code/engine/core/ingestion/structure-processor.js +40 -0
  514. package/dist/core/code/engine/core/ingestion/tree-sitter-queries.js +2244 -0
  515. package/dist/core/code/engine/core/ingestion/ts-js-hoc-utils.js +115 -0
  516. package/dist/core/code/engine/core/ingestion/type-env.js +1136 -0
  517. package/dist/core/code/engine/core/ingestion/type-extractors/c-cpp.js +555 -0
  518. package/dist/core/code/engine/core/ingestion/type-extractors/csharp.js +570 -0
  519. package/dist/core/code/engine/core/ingestion/type-extractors/dart.js +372 -0
  520. package/dist/core/code/engine/core/ingestion/type-extractors/go.js +508 -0
  521. package/dist/core/code/engine/core/ingestion/type-extractors/jvm.js +875 -0
  522. package/dist/core/code/engine/core/ingestion/type-extractors/php.js +537 -0
  523. package/dist/core/code/engine/core/ingestion/type-extractors/python.js +477 -0
  524. package/dist/core/code/engine/core/ingestion/type-extractors/ruby.js +380 -0
  525. package/dist/core/code/engine/core/ingestion/type-extractors/rust.js +502 -0
  526. package/dist/core/code/engine/core/ingestion/type-extractors/shared.js +843 -0
  527. package/dist/core/code/engine/core/ingestion/type-extractors/swift.js +490 -0
  528. package/dist/core/code/engine/core/ingestion/type-extractors/types.js +2 -0
  529. package/dist/core/code/engine/core/ingestion/type-extractors/typescript.js +690 -0
  530. package/dist/core/code/engine/core/ingestion/utils/ast-helpers.js +1693 -0
  531. package/dist/core/code/engine/core/ingestion/utils/call-analysis.js +779 -0
  532. package/dist/core/code/engine/core/ingestion/utils/callable-flow-captures.js +932 -0
  533. package/dist/core/code/engine/core/ingestion/utils/callable-labels.js +49 -0
  534. package/dist/core/code/engine/core/ingestion/utils/deferred-resolution-profile.js +151 -0
  535. package/dist/core/code/engine/core/ingestion/utils/effective-ram.js +67 -0
  536. package/dist/core/code/engine/core/ingestion/utils/env.js +60 -0
  537. package/dist/core/code/engine/core/ingestion/utils/event-loop.js +9 -0
  538. package/dist/core/code/engine/core/ingestion/utils/graph-sort.js +103 -0
  539. package/dist/core/code/engine/core/ingestion/utils/heap-probe.js +45 -0
  540. package/dist/core/code/engine/core/ingestion/utils/heritage-marker.js +47 -0
  541. package/dist/core/code/engine/core/ingestion/utils/line-base.js +24 -0
  542. package/dist/core/code/engine/core/ingestion/utils/max-file-size.js +59 -0
  543. package/dist/core/code/engine/core/ingestion/utils/method-props.js +198 -0
  544. package/dist/core/code/engine/core/ingestion/utils/qualified-name.js +73 -0
  545. package/dist/core/code/engine/core/ingestion/utils/receiver-chain-captures.js +60 -0
  546. package/dist/core/code/engine/core/ingestion/utils/receiver-chain-codec.js +188 -0
  547. package/dist/core/code/engine/core/ingestion/utils/scope-tree-walk.js +36 -0
  548. package/dist/core/code/engine/core/ingestion/utils/symbol-labels.js +48 -0
  549. package/dist/core/code/engine/core/ingestion/utils/template-arguments.js +187 -0
  550. package/dist/core/code/engine/core/ingestion/utils/type-parameters.js +209 -0
  551. package/dist/core/code/engine/core/ingestion/utils/verbose.js +6 -0
  552. package/dist/core/code/engine/core/ingestion/variable-extractors/configs/c-cpp.js +133 -0
  553. package/dist/core/code/engine/core/ingestion/variable-extractors/configs/csharp.js +66 -0
  554. package/dist/core/code/engine/core/ingestion/variable-extractors/configs/dart.js +111 -0
  555. package/dist/core/code/engine/core/ingestion/variable-extractors/configs/go.js +153 -0
  556. package/dist/core/code/engine/core/ingestion/variable-extractors/configs/jvm.js +145 -0
  557. package/dist/core/code/engine/core/ingestion/variable-extractors/configs/php.js +61 -0
  558. package/dist/core/code/engine/core/ingestion/variable-extractors/configs/python.js +104 -0
  559. package/dist/core/code/engine/core/ingestion/variable-extractors/configs/ruby.js +55 -0
  560. package/dist/core/code/engine/core/ingestion/variable-extractors/configs/rust.js +79 -0
  561. package/dist/core/code/engine/core/ingestion/variable-extractors/configs/swift.js +91 -0
  562. package/dist/core/code/engine/core/ingestion/variable-extractors/configs/typescript-javascript.js +86 -0
  563. package/dist/core/code/engine/core/ingestion/variable-extractors/generic.js +111 -0
  564. package/dist/core/code/engine/core/ingestion/variable-types.js +3 -0
  565. package/dist/core/code/engine/core/ingestion/vue-sfc-extractor.js +544 -0
  566. package/dist/core/code/engine/core/ingestion/workers/callable-id.js +122 -0
  567. package/dist/core/code/engine/core/ingestion/workers/clone-safety.js +470 -0
  568. package/dist/core/code/engine/core/ingestion/workers/parse-worker.js +2456 -0
  569. package/dist/core/code/engine/core/ingestion/workers/post-result.js +90 -0
  570. package/dist/core/code/engine/core/ingestion/workers/quarantine.js +41 -0
  571. package/dist/core/code/engine/core/ingestion/workers/result-merge.js +59 -0
  572. package/dist/core/code/engine/core/ingestion/workers/worker-pool.js +1725 -0
  573. package/dist/core/code/engine/core/ingestion/workspace-config.js +139 -0
  574. package/dist/core/code/engine/core/lbug/conn-lock.js +72 -0
  575. package/dist/core/code/engine/core/lbug/csv-generator.js +710 -0
  576. package/dist/core/code/engine/core/lbug/cypher-escape.js +24 -0
  577. package/dist/core/code/engine/core/lbug/extension-load-error.js +339 -0
  578. package/dist/core/code/engine/core/lbug/extension-loader.js +261 -0
  579. package/dist/core/code/engine/core/lbug/graph-emit-sink.js +584 -0
  580. package/dist/core/code/engine/core/lbug/lbug-adapter.js +2703 -0
  581. package/dist/core/code/engine/core/lbug/lbug-config.js +1029 -0
  582. package/dist/core/code/engine/core/lbug/native-check.js +435 -0
  583. package/dist/core/code/engine/core/lbug/pool-adapter.js +973 -0
  584. package/dist/core/code/engine/core/lbug/query-params.js +25 -0
  585. package/dist/core/code/engine/core/lbug/query-result-utils.js +31 -0
  586. package/dist/core/code/engine/core/lbug/rel-pair-routing.js +373 -0
  587. package/dist/core/code/engine/core/lbug/schema.js +771 -0
  588. package/dist/core/code/engine/core/lbug/shutdown-helpers.js +40 -0
  589. package/dist/core/code/engine/core/lbug/sidecar-recovery.js +716 -0
  590. package/dist/core/code/engine/core/lbug/stdio-capture.js +49 -0
  591. package/dist/core/code/engine/core/lbug/sync-csv-writer.js +115 -0
  592. package/dist/core/code/engine/core/lbug/wal-checkpoint-driver.js +215 -0
  593. package/dist/core/code/engine/core/lbug/wal-driver-state.js +28 -0
  594. package/dist/core/code/engine/core/logger.js +339 -0
  595. package/dist/core/code/engine/core/platform/capabilities.js +91 -0
  596. package/dist/core/code/engine/core/run-analyze.js +1098 -0
  597. package/dist/core/code/engine/core/tree-sitter/parser-loader.js +281 -0
  598. package/dist/core/code/engine/core/tree-sitter/safe-parse.js +258 -0
  599. package/dist/core/code/engine/core/tree-sitter/vendored-grammars.js +64 -0
  600. package/dist/core/code/engine/lib/utils.js +121 -0
  601. package/dist/core/code/engine/mcp/core/lbug-adapter.js +27 -0
  602. package/dist/core/code/engine/mcp/local/aop-metadata.js +230 -0
  603. package/dist/core/code/engine/mcp/local/bean-metadata.js +49 -0
  604. package/dist/core/code/engine/mcp/local/limits.js +15 -0
  605. package/dist/core/code/engine/mcp/local/line-display.js +6 -0
  606. package/dist/core/code/engine/mcp/local/local-backend.js +4142 -0
  607. package/dist/core/code/engine/storage/branch-index.js +72 -0
  608. package/dist/core/code/engine/storage/file-hash.js +95 -0
  609. package/dist/core/code/engine/storage/fs-atomic.js +34 -0
  610. package/dist/core/code/engine/storage/git.js +555 -0
  611. package/dist/core/code/engine/storage/index-lock.js +664 -0
  612. package/dist/core/code/engine/storage/parse-cache.js +650 -0
  613. package/dist/core/code/engine/storage/parsedfile-store.js +620 -0
  614. package/dist/core/code/engine/storage/repo-manager.js +1061 -0
  615. package/dist/core/code/engine/storage/scope-index-store.js +247 -0
  616. package/dist/core/code/engine/types/pipeline.js +2 -0
  617. package/dist/core/code/scripts/install-duckdb-extension.mjs +125 -0
  618. package/dist/core/code/scripts/resolve-analyze-cmd.cjs +346 -0
  619. package/dist/core/code/shared/graph/types.js +8 -0
  620. package/dist/core/code/shared/index.js +105 -0
  621. package/dist/core/code/shared/integrations/circuit-breaker.js +242 -0
  622. package/dist/core/code/shared/integrations/resilient-fetch.js +224 -0
  623. package/dist/core/code/shared/integrations/retry.js +70 -0
  624. package/dist/core/code/shared/integrations/understand-quickly.js +145 -0
  625. package/dist/core/code/shared/language-detection.js +162 -0
  626. package/dist/core/code/shared/languages.js +27 -0
  627. package/dist/core/code/shared/lbug/schema-constants.js +98 -0
  628. package/dist/core/code/shared/mro-strategy.js +2 -0
  629. package/dist/core/code/shared/pipeline.js +5 -0
  630. package/dist/core/code/shared/scope-resolution/callable-flow-site.js +11 -0
  631. package/dist/core/code/shared/scope-resolution/def-index.js +53 -0
  632. package/dist/core/code/shared/scope-resolution/evidence-weights.js +87 -0
  633. package/dist/core/code/shared/scope-resolution/finalize-algorithm.js +807 -0
  634. package/dist/core/code/shared/scope-resolution/language-classification.js +46 -0
  635. package/dist/core/code/shared/scope-resolution/method-dispatch-index.js +100 -0
  636. package/dist/core/code/shared/scope-resolution/module-scope-index.js +59 -0
  637. package/dist/core/code/shared/scope-resolution/origin-priority.js +23 -0
  638. package/dist/core/code/shared/scope-resolution/parsed-file.js +54 -0
  639. package/dist/core/code/shared/scope-resolution/position-index.js +136 -0
  640. package/dist/core/code/shared/scope-resolution/qualified-name-index.js +77 -0
  641. package/dist/core/code/shared/scope-resolution/reference-site.js +24 -0
  642. package/dist/core/code/shared/scope-resolution/registries/class-registry.js +32 -0
  643. package/dist/core/code/shared/scope-resolution/registries/context.js +52 -0
  644. package/dist/core/code/shared/scope-resolution/registries/evidence.js +152 -0
  645. package/dist/core/code/shared/scope-resolution/registries/field-registry.js +33 -0
  646. package/dist/core/code/shared/scope-resolution/registries/lookup-core.js +392 -0
  647. package/dist/core/code/shared/scope-resolution/registries/lookup-qualified.js +58 -0
  648. package/dist/core/code/shared/scope-resolution/registries/macro-registry.js +34 -0
  649. package/dist/core/code/shared/scope-resolution/registries/method-registry.js +34 -0
  650. package/dist/core/code/shared/scope-resolution/registries/tie-breaks.js +63 -0
  651. package/dist/core/code/shared/scope-resolution/resolve-type-ref.js +128 -0
  652. package/dist/core/code/shared/scope-resolution/scope-id.js +49 -0
  653. package/dist/core/code/shared/scope-resolution/scope-tree.js +225 -0
  654. package/dist/core/code/shared/scope-resolution/symbol-definition.js +12 -0
  655. package/dist/core/code/shared/scope-resolution/types.js +25 -0
  656. package/dist/core/code/shared/test-helpers.js +17 -0
  657. package/dist/core/code/vendor/leiden/index.cjs +355 -0
  658. package/dist/core/code/vendor/leiden/utils.cjs +419 -0
  659. package/dist/core/timeline/cli.d.ts.map +1 -1
  660. package/dist/core/timeline/cli.js +13 -6
  661. package/dist/core/timeline/cli.js.map +1 -1
  662. package/dist/core/timeline/debris.d.ts +20 -0
  663. package/dist/core/timeline/debris.d.ts.map +1 -0
  664. package/dist/core/timeline/debris.js +124 -0
  665. package/dist/core/timeline/debris.js.map +1 -0
  666. package/dist/core/timeline/hook-runner.d.ts.map +1 -1
  667. package/dist/core/timeline/hook-runner.js +3 -1
  668. package/dist/core/timeline/hook-runner.js.map +1 -1
  669. package/dist/core/timeline/hooks.d.ts.map +1 -1
  670. package/dist/core/timeline/hooks.js +13 -5
  671. package/dist/core/timeline/hooks.js.map +1 -1
  672. package/dist/core/timeline/installer.d.ts +2 -0
  673. package/dist/core/timeline/installer.d.ts.map +1 -1
  674. package/dist/core/timeline/installer.js +12 -0
  675. package/dist/core/timeline/installer.js.map +1 -1
  676. package/dist/core/timeline/project-root.d.ts +21 -0
  677. package/dist/core/timeline/project-root.d.ts.map +1 -0
  678. package/dist/core/timeline/project-root.js +83 -0
  679. package/dist/core/timeline/project-root.js.map +1 -0
  680. 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
+ }