gitnexus 1.4.7 → 1.4.9

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 (242) hide show
  1. package/README.md +29 -1
  2. package/dist/cli/ai-context.d.ts +1 -1
  3. package/dist/cli/ai-context.js +1 -1
  4. package/dist/cli/analyze.d.ts +2 -0
  5. package/dist/cli/analyze.js +54 -21
  6. package/dist/cli/index-repo.d.ts +15 -0
  7. package/dist/cli/index-repo.js +115 -0
  8. package/dist/cli/index.js +13 -3
  9. package/dist/cli/setup.js +90 -10
  10. package/dist/cli/wiki.d.ts +4 -0
  11. package/dist/cli/wiki.js +174 -53
  12. package/dist/config/supported-languages.d.ts +33 -1
  13. package/dist/config/supported-languages.js +32 -0
  14. package/dist/core/embeddings/embedder.d.ts +6 -1
  15. package/dist/core/embeddings/embedder.js +65 -5
  16. package/dist/core/embeddings/embedding-pipeline.js +11 -9
  17. package/dist/core/embeddings/http-client.d.ts +31 -0
  18. package/dist/core/embeddings/http-client.js +179 -0
  19. package/dist/core/embeddings/index.d.ts +1 -0
  20. package/dist/core/embeddings/index.js +1 -0
  21. package/dist/core/embeddings/types.d.ts +1 -1
  22. package/dist/core/graph/graph.js +9 -1
  23. package/dist/core/graph/types.d.ts +11 -2
  24. package/dist/core/ingestion/call-processor.d.ts +66 -2
  25. package/dist/core/ingestion/call-processor.js +650 -30
  26. package/dist/core/ingestion/call-routing.d.ts +9 -18
  27. package/dist/core/ingestion/call-routing.js +0 -19
  28. package/dist/core/ingestion/cobol/cobol-copy-expander.d.ts +57 -0
  29. package/dist/core/ingestion/cobol/cobol-copy-expander.js +385 -0
  30. package/dist/core/ingestion/cobol/cobol-preprocessor.d.ts +210 -0
  31. package/dist/core/ingestion/cobol/cobol-preprocessor.js +1509 -0
  32. package/dist/core/ingestion/cobol/jcl-parser.d.ts +68 -0
  33. package/dist/core/ingestion/cobol/jcl-parser.js +217 -0
  34. package/dist/core/ingestion/cobol/jcl-processor.d.ts +33 -0
  35. package/dist/core/ingestion/cobol/jcl-processor.js +229 -0
  36. package/dist/core/ingestion/cobol-processor.d.ts +54 -0
  37. package/dist/core/ingestion/cobol-processor.js +1186 -0
  38. package/dist/core/ingestion/entry-point-scoring.d.ts +17 -0
  39. package/dist/core/ingestion/entry-point-scoring.js +52 -28
  40. package/dist/core/ingestion/export-detection.d.ts +47 -8
  41. package/dist/core/ingestion/export-detection.js +29 -50
  42. package/dist/core/ingestion/field-extractor.d.ts +29 -0
  43. package/dist/core/ingestion/field-extractor.js +25 -0
  44. package/dist/core/ingestion/field-extractors/configs/c-cpp.d.ts +3 -0
  45. package/dist/core/ingestion/field-extractors/configs/c-cpp.js +108 -0
  46. package/dist/core/ingestion/field-extractors/configs/csharp.d.ts +8 -0
  47. package/dist/core/ingestion/field-extractors/configs/csharp.js +73 -0
  48. package/dist/core/ingestion/field-extractors/configs/dart.d.ts +8 -0
  49. package/dist/core/ingestion/field-extractors/configs/dart.js +76 -0
  50. package/dist/core/ingestion/field-extractors/configs/go.d.ts +11 -0
  51. package/dist/core/ingestion/field-extractors/configs/go.js +64 -0
  52. package/dist/core/ingestion/field-extractors/configs/helpers.d.ts +44 -0
  53. package/dist/core/ingestion/field-extractors/configs/helpers.js +134 -0
  54. package/dist/core/ingestion/field-extractors/configs/jvm.d.ts +3 -0
  55. package/dist/core/ingestion/field-extractors/configs/jvm.js +118 -0
  56. package/dist/core/ingestion/field-extractors/configs/php.d.ts +8 -0
  57. package/dist/core/ingestion/field-extractors/configs/php.js +67 -0
  58. package/dist/core/ingestion/field-extractors/configs/python.d.ts +12 -0
  59. package/dist/core/ingestion/field-extractors/configs/python.js +91 -0
  60. package/dist/core/ingestion/field-extractors/configs/ruby.d.ts +16 -0
  61. package/dist/core/ingestion/field-extractors/configs/ruby.js +75 -0
  62. package/dist/core/ingestion/field-extractors/configs/rust.d.ts +9 -0
  63. package/dist/core/ingestion/field-extractors/configs/rust.js +55 -0
  64. package/dist/core/ingestion/field-extractors/configs/swift.d.ts +8 -0
  65. package/dist/core/ingestion/field-extractors/configs/swift.js +63 -0
  66. package/dist/core/ingestion/field-extractors/configs/typescript-javascript.d.ts +3 -0
  67. package/dist/core/ingestion/field-extractors/configs/typescript-javascript.js +60 -0
  68. package/dist/core/ingestion/field-extractors/generic.d.ts +46 -0
  69. package/dist/core/ingestion/field-extractors/generic.js +111 -0
  70. package/dist/core/ingestion/field-extractors/typescript.d.ts +77 -0
  71. package/dist/core/ingestion/field-extractors/typescript.js +291 -0
  72. package/dist/core/ingestion/field-types.d.ts +59 -0
  73. package/dist/core/ingestion/field-types.js +2 -0
  74. package/dist/core/ingestion/framework-detection.d.ts +97 -2
  75. package/dist/core/ingestion/framework-detection.js +114 -14
  76. package/dist/core/ingestion/heritage-processor.js +62 -66
  77. package/dist/core/ingestion/import-processor.d.ts +9 -10
  78. package/dist/core/ingestion/import-processor.js +150 -196
  79. package/dist/core/ingestion/{resolvers → import-resolvers}/csharp.d.ts +6 -9
  80. package/dist/core/ingestion/{resolvers → import-resolvers}/csharp.js +20 -2
  81. package/dist/core/ingestion/import-resolvers/dart.d.ts +7 -0
  82. package/dist/core/ingestion/import-resolvers/dart.js +44 -0
  83. package/dist/core/ingestion/{resolvers → import-resolvers}/go.d.ts +4 -5
  84. package/dist/core/ingestion/{resolvers → import-resolvers}/go.js +17 -0
  85. package/dist/core/ingestion/{resolvers → import-resolvers}/jvm.d.ts +10 -1
  86. package/dist/core/ingestion/import-resolvers/jvm.js +159 -0
  87. package/dist/core/ingestion/import-resolvers/php.d.ts +25 -0
  88. package/dist/core/ingestion/import-resolvers/php.js +80 -0
  89. package/dist/core/ingestion/{resolvers → import-resolvers}/python.d.ts +9 -3
  90. package/dist/core/ingestion/{resolvers → import-resolvers}/python.js +35 -3
  91. package/dist/core/ingestion/{resolvers → import-resolvers}/ruby.d.ts +5 -2
  92. package/dist/core/ingestion/{resolvers → import-resolvers}/ruby.js +7 -2
  93. package/dist/core/ingestion/{resolvers → import-resolvers}/rust.d.ts +5 -2
  94. package/dist/core/ingestion/{resolvers → import-resolvers}/rust.js +41 -2
  95. package/dist/core/ingestion/{resolvers → import-resolvers}/standard.d.ts +15 -7
  96. package/dist/core/ingestion/{resolvers → import-resolvers}/standard.js +22 -3
  97. package/dist/core/ingestion/import-resolvers/swift.d.ts +7 -0
  98. package/dist/core/ingestion/import-resolvers/swift.js +23 -0
  99. package/dist/core/ingestion/import-resolvers/types.d.ts +44 -0
  100. package/dist/core/ingestion/import-resolvers/types.js +6 -0
  101. package/dist/core/ingestion/{resolvers → import-resolvers}/utils.d.ts +2 -0
  102. package/dist/core/ingestion/{resolvers → import-resolvers}/utils.js +7 -0
  103. package/dist/core/ingestion/language-config.d.ts +6 -0
  104. package/dist/core/ingestion/language-config.js +13 -0
  105. package/dist/core/ingestion/language-provider.d.ts +121 -0
  106. package/dist/core/ingestion/language-provider.js +24 -0
  107. package/dist/core/ingestion/languages/c-cpp.d.ts +12 -0
  108. package/dist/core/ingestion/languages/c-cpp.js +71 -0
  109. package/dist/core/ingestion/languages/cobol.d.ts +1 -0
  110. package/dist/core/ingestion/languages/cobol.js +26 -0
  111. package/dist/core/ingestion/languages/csharp.d.ts +8 -0
  112. package/dist/core/ingestion/languages/csharp.js +49 -0
  113. package/dist/core/ingestion/languages/dart.d.ts +12 -0
  114. package/dist/core/ingestion/languages/dart.js +58 -0
  115. package/dist/core/ingestion/languages/go.d.ts +11 -0
  116. package/dist/core/ingestion/languages/go.js +28 -0
  117. package/dist/core/ingestion/languages/index.d.ts +38 -0
  118. package/dist/core/ingestion/languages/index.js +63 -0
  119. package/dist/core/ingestion/languages/java.d.ts +9 -0
  120. package/dist/core/ingestion/languages/java.js +29 -0
  121. package/dist/core/ingestion/languages/kotlin.d.ts +9 -0
  122. package/dist/core/ingestion/languages/kotlin.js +53 -0
  123. package/dist/core/ingestion/languages/php.d.ts +8 -0
  124. package/dist/core/ingestion/languages/php.js +145 -0
  125. package/dist/core/ingestion/languages/python.d.ts +12 -0
  126. package/dist/core/ingestion/languages/python.js +39 -0
  127. package/dist/core/ingestion/languages/ruby.d.ts +9 -0
  128. package/dist/core/ingestion/languages/ruby.js +44 -0
  129. package/dist/core/ingestion/languages/rust.d.ts +12 -0
  130. package/dist/core/ingestion/languages/rust.js +44 -0
  131. package/dist/core/ingestion/languages/swift.d.ts +12 -0
  132. package/dist/core/ingestion/languages/swift.js +133 -0
  133. package/dist/core/ingestion/languages/typescript.d.ts +10 -0
  134. package/dist/core/ingestion/languages/typescript.js +60 -0
  135. package/dist/core/ingestion/markdown-processor.d.ts +17 -0
  136. package/dist/core/ingestion/markdown-processor.js +124 -0
  137. package/dist/core/ingestion/mro-processor.js +22 -18
  138. package/dist/core/ingestion/named-binding-processor.d.ts +18 -0
  139. package/dist/core/ingestion/named-binding-processor.js +42 -0
  140. package/dist/core/ingestion/named-bindings/csharp.d.ts +3 -0
  141. package/dist/core/ingestion/named-bindings/csharp.js +37 -0
  142. package/dist/core/ingestion/named-bindings/java.d.ts +3 -0
  143. package/dist/core/ingestion/named-bindings/java.js +29 -0
  144. package/dist/core/ingestion/named-bindings/kotlin.d.ts +3 -0
  145. package/dist/core/ingestion/named-bindings/kotlin.js +36 -0
  146. package/dist/core/ingestion/named-bindings/php.d.ts +3 -0
  147. package/dist/core/ingestion/named-bindings/php.js +61 -0
  148. package/dist/core/ingestion/named-bindings/python.d.ts +3 -0
  149. package/dist/core/ingestion/named-bindings/python.js +49 -0
  150. package/dist/core/ingestion/named-bindings/rust.d.ts +3 -0
  151. package/dist/core/ingestion/named-bindings/rust.js +64 -0
  152. package/dist/core/ingestion/named-bindings/types.d.ts +16 -0
  153. package/dist/core/ingestion/named-bindings/types.js +6 -0
  154. package/dist/core/ingestion/named-bindings/typescript.d.ts +3 -0
  155. package/dist/core/ingestion/named-bindings/typescript.js +58 -0
  156. package/dist/core/ingestion/parsing-processor.d.ts +6 -2
  157. package/dist/core/ingestion/parsing-processor.js +125 -85
  158. package/dist/core/ingestion/pipeline.d.ts +10 -0
  159. package/dist/core/ingestion/pipeline.js +1235 -317
  160. package/dist/core/ingestion/resolution-context.d.ts +5 -0
  161. package/dist/core/ingestion/resolution-context.js +8 -5
  162. package/dist/core/ingestion/route-extractors/expo.d.ts +1 -0
  163. package/dist/core/ingestion/route-extractors/expo.js +36 -0
  164. package/dist/core/ingestion/route-extractors/middleware.d.ts +47 -0
  165. package/dist/core/ingestion/route-extractors/middleware.js +143 -0
  166. package/dist/core/ingestion/route-extractors/nextjs.d.ts +3 -0
  167. package/dist/core/ingestion/route-extractors/nextjs.js +76 -0
  168. package/dist/core/ingestion/route-extractors/php.d.ts +7 -0
  169. package/dist/core/ingestion/route-extractors/php.js +21 -0
  170. package/dist/core/ingestion/route-extractors/response-shapes.d.ts +20 -0
  171. package/dist/core/ingestion/route-extractors/response-shapes.js +290 -0
  172. package/dist/core/ingestion/symbol-table.d.ts +16 -0
  173. package/dist/core/ingestion/symbol-table.js +20 -6
  174. package/dist/core/ingestion/tree-sitter-queries.d.ts +10 -9
  175. package/dist/core/ingestion/tree-sitter-queries.js +274 -11
  176. package/dist/core/ingestion/type-env.d.ts +42 -18
  177. package/dist/core/ingestion/type-env.js +481 -106
  178. package/dist/core/ingestion/type-extractors/c-cpp.d.ts +5 -0
  179. package/dist/core/ingestion/type-extractors/c-cpp.js +119 -0
  180. package/dist/core/ingestion/type-extractors/csharp.js +149 -16
  181. package/dist/core/ingestion/type-extractors/dart.d.ts +15 -0
  182. package/dist/core/ingestion/type-extractors/dart.js +371 -0
  183. package/dist/core/ingestion/type-extractors/jvm.js +169 -66
  184. package/dist/core/ingestion/type-extractors/rust.js +35 -1
  185. package/dist/core/ingestion/type-extractors/shared.d.ts +1 -15
  186. package/dist/core/ingestion/type-extractors/shared.js +14 -112
  187. package/dist/core/ingestion/type-extractors/swift.js +338 -7
  188. package/dist/core/ingestion/type-extractors/types.d.ts +40 -8
  189. package/dist/core/ingestion/type-extractors/typescript.js +141 -9
  190. package/dist/core/ingestion/utils/ast-helpers.d.ts +83 -0
  191. package/dist/core/ingestion/utils/ast-helpers.js +817 -0
  192. package/dist/core/ingestion/utils/call-analysis.d.ts +73 -0
  193. package/dist/core/ingestion/utils/call-analysis.js +527 -0
  194. package/dist/core/ingestion/utils/event-loop.d.ts +5 -0
  195. package/dist/core/ingestion/utils/event-loop.js +5 -0
  196. package/dist/core/ingestion/utils/language-detection.d.ts +9 -0
  197. package/dist/core/ingestion/utils/language-detection.js +70 -0
  198. package/dist/core/ingestion/utils/verbose.d.ts +1 -0
  199. package/dist/core/ingestion/utils/verbose.js +7 -0
  200. package/dist/core/ingestion/workers/parse-worker.d.ts +55 -5
  201. package/dist/core/ingestion/workers/parse-worker.js +415 -225
  202. package/dist/core/lbug/csv-generator.js +51 -1
  203. package/dist/core/lbug/lbug-adapter.d.ts +10 -0
  204. package/dist/core/lbug/lbug-adapter.js +75 -4
  205. package/dist/core/lbug/schema.d.ts +8 -4
  206. package/dist/core/lbug/schema.js +65 -4
  207. package/dist/core/tree-sitter/parser-loader.js +7 -1
  208. package/dist/core/wiki/cursor-client.d.ts +31 -0
  209. package/dist/core/wiki/cursor-client.js +127 -0
  210. package/dist/core/wiki/generator.d.ts +28 -9
  211. package/dist/core/wiki/generator.js +115 -18
  212. package/dist/core/wiki/graph-queries.d.ts +4 -0
  213. package/dist/core/wiki/graph-queries.js +7 -1
  214. package/dist/core/wiki/llm-client.d.ts +2 -0
  215. package/dist/core/wiki/llm-client.js +8 -4
  216. package/dist/core/wiki/prompts.d.ts +3 -3
  217. package/dist/core/wiki/prompts.js +6 -0
  218. package/dist/mcp/core/embedder.js +11 -3
  219. package/dist/mcp/core/lbug-adapter.d.ts +5 -0
  220. package/dist/mcp/core/lbug-adapter.js +23 -2
  221. package/dist/mcp/local/local-backend.d.ts +38 -5
  222. package/dist/mcp/local/local-backend.js +804 -63
  223. package/dist/mcp/resources.js +2 -0
  224. package/dist/mcp/tools.js +73 -4
  225. package/dist/server/api.d.ts +19 -1
  226. package/dist/server/api.js +66 -6
  227. package/dist/storage/git.d.ts +12 -0
  228. package/dist/storage/git.js +21 -0
  229. package/dist/storage/repo-manager.d.ts +3 -0
  230. package/package.json +25 -16
  231. package/dist/core/ingestion/named-binding-extraction.d.ts +0 -61
  232. package/dist/core/ingestion/named-binding-extraction.js +0 -363
  233. package/dist/core/ingestion/resolvers/index.d.ts +0 -18
  234. package/dist/core/ingestion/resolvers/index.js +0 -13
  235. package/dist/core/ingestion/resolvers/jvm.js +0 -87
  236. package/dist/core/ingestion/resolvers/php.d.ts +0 -15
  237. package/dist/core/ingestion/resolvers/php.js +0 -35
  238. package/dist/core/ingestion/type-extractors/index.d.ts +0 -22
  239. package/dist/core/ingestion/type-extractors/index.js +0 -31
  240. package/dist/core/ingestion/utils.d.ts +0 -138
  241. package/dist/core/ingestion/utils.js +0 -1290
  242. package/scripts/patch-tree-sitter-swift.cjs +0 -74
@@ -1,4 +1,5 @@
1
- import { extractSimpleTypeName, extractVarName, findChildByType, extractGenericTypeArgs, resolveIterableElementType, methodToTypeArgPosition, extractElementTypeFromString } from './shared.js';
1
+ import { findChild } from '../utils/ast-helpers.js';
2
+ import { extractSimpleTypeName, extractVarName, extractGenericTypeArgs, resolveIterableElementType, methodToTypeArgPosition, extractElementTypeFromString } from './shared.js';
2
3
  // ── Java ──────────────────────────────────────────────────────────────────
3
4
  const JAVA_DECLARATION_NODE_TYPES = new Set([
4
5
  'local_variable_declaration',
@@ -78,7 +79,7 @@ const scanJavaConstructorBinding = (node) => {
78
79
  return undefined;
79
80
  if (typeNode.text !== 'var')
80
81
  return undefined;
81
- const declarator = findChildByType(node, 'variable_declarator');
82
+ const declarator = findChild(node, 'variable_declarator');
82
83
  if (!declarator)
83
84
  return undefined;
84
85
  const nameNode = declarator.childForFieldName('name');
@@ -293,6 +294,39 @@ const extractJavaPatternBinding = (node) => {
293
294
  return undefined;
294
295
  return { varName, typeName };
295
296
  };
297
+ /** Infer the type of a literal AST node for Java/Kotlin overload disambiguation. */
298
+ const inferJvmLiteralType = (node) => {
299
+ switch (node.type) {
300
+ case 'decimal_integer_literal':
301
+ case 'integer_literal':
302
+ case 'hex_integer_literal':
303
+ case 'octal_integer_literal':
304
+ case 'binary_integer_literal':
305
+ // Check for long suffix
306
+ if (node.text.endsWith('L') || node.text.endsWith('l'))
307
+ return 'long';
308
+ return 'int';
309
+ case 'decimal_floating_point_literal':
310
+ case 'real_literal':
311
+ if (node.text.endsWith('f') || node.text.endsWith('F'))
312
+ return 'float';
313
+ return 'double';
314
+ case 'string_literal':
315
+ case 'line_string_literal':
316
+ case 'multi_line_string_literal':
317
+ return 'String';
318
+ case 'character_literal':
319
+ return 'char';
320
+ case 'true':
321
+ case 'false':
322
+ case 'boolean_literal':
323
+ return 'boolean';
324
+ case 'null_literal':
325
+ return 'null';
326
+ default:
327
+ return undefined;
328
+ }
329
+ };
296
330
  export const javaTypeConfig = {
297
331
  declarationNodeTypes: JAVA_DECLARATION_NODE_TYPES,
298
332
  forLoopNodeTypes: JAVA_FOR_LOOP_NODE_TYPES,
@@ -304,6 +338,7 @@ export const javaTypeConfig = {
304
338
  extractForLoopBinding: extractJavaForLoopBinding,
305
339
  extractPendingAssignment: extractJavaPendingAssignment,
306
340
  extractPatternBinding: extractJavaPatternBinding,
341
+ inferLiteralType: inferJvmLiteralType,
307
342
  };
308
343
  // ── Kotlin ────────────────────────────────────────────────────────────────
309
344
  const KOTLIN_DECLARATION_NODE_TYPES = new Set([
@@ -314,10 +349,11 @@ const KOTLIN_DECLARATION_NODE_TYPES = new Set([
314
349
  const extractKotlinDeclaration = (node, env) => {
315
350
  if (node.type === 'property_declaration') {
316
351
  // Kotlin property_declaration: name/type are inside a variable_declaration child
317
- const varDecl = findChildByType(node, 'variable_declaration');
352
+ const varDecl = findChild(node, 'variable_declaration');
318
353
  if (varDecl) {
319
- const nameNode = findChildByType(varDecl, 'simple_identifier');
320
- const typeNode = findChildByType(varDecl, 'user_type');
354
+ const nameNode = findChild(varDecl, 'simple_identifier');
355
+ const typeNode = findChild(varDecl, 'user_type')
356
+ ?? findChild(varDecl, 'nullable_type');
321
357
  if (!nameNode || !typeNode)
322
358
  return;
323
359
  const varName = extractVarName(nameNode);
@@ -328,9 +364,9 @@ const extractKotlinDeclaration = (node, env) => {
328
364
  }
329
365
  // Fallback: try direct fields
330
366
  const nameNode = node.childForFieldName('name')
331
- ?? findChildByType(node, 'simple_identifier');
367
+ ?? findChild(node, 'simple_identifier');
332
368
  const typeNode = node.childForFieldName('type')
333
- ?? findChildByType(node, 'user_type');
369
+ ?? findChild(node, 'user_type');
334
370
  if (!nameNode || !typeNode)
335
371
  return;
336
372
  const varName = extractVarName(nameNode);
@@ -340,8 +376,8 @@ const extractKotlinDeclaration = (node, env) => {
340
376
  }
341
377
  else if (node.type === 'variable_declaration') {
342
378
  // variable_declaration directly inside functions
343
- const nameNode = findChildByType(node, 'simple_identifier');
344
- const typeNode = findChildByType(node, 'user_type');
379
+ const nameNode = findChild(node, 'simple_identifier');
380
+ const typeNode = findChild(node, 'user_type');
345
381
  if (nameNode && typeNode) {
346
382
  const varName = extractVarName(nameNode);
347
383
  const typeName = extractSimpleTypeName(typeNode);
@@ -353,7 +389,7 @@ const extractKotlinDeclaration = (node, env) => {
353
389
  /** Kotlin: parameter / formal_parameter → type name.
354
390
  * Kotlin's tree-sitter grammar uses positional children (simple_identifier, user_type)
355
391
  * rather than named fields (name, type) on `parameter` nodes, so we fall back to
356
- * findChildByType when childForFieldName returns null. */
392
+ * findChild when childForFieldName returns null. */
357
393
  const extractKotlinParameter = (node, env) => {
358
394
  let nameNode = null;
359
395
  let typeNode = null;
@@ -367,9 +403,10 @@ const extractKotlinParameter = (node, env) => {
367
403
  }
368
404
  // Fallback: Kotlin `parameter` nodes use positional children, not named fields
369
405
  if (!nameNode)
370
- nameNode = findChildByType(node, 'simple_identifier');
406
+ nameNode = findChild(node, 'simple_identifier');
371
407
  if (!typeNode)
372
- typeNode = findChildByType(node, 'user_type');
408
+ typeNode = findChild(node, 'user_type')
409
+ ?? findChild(node, 'nullable_type');
373
410
  if (!nameNode || !typeNode)
374
411
  return;
375
412
  const varName = extractVarName(nameNode);
@@ -377,48 +414,61 @@ const extractKotlinParameter = (node, env) => {
377
414
  if (varName && typeName)
378
415
  env.set(varName, typeName);
379
416
  };
380
- /** Kotlin: val user = User() infer type from call_expression when callee is a known class.
381
- * Kotlin constructors are syntactically identical to function calls, so we verify
382
- * against classNames (which may include cross-file SymbolTable lookups). */
383
- const extractKotlinInitializer = (node, env, classNames) => {
417
+ /** Find the constructor callee name in a Kotlin property_declaration's initializer.
418
+ * Returns the class name if the callee is a verified class constructor, undefined otherwise. */
419
+ const findKotlinConstructorCallee = (node, classNames) => {
384
420
  if (node.type !== 'property_declaration')
385
- return;
386
- // Skip if there's an explicit type annotation — Tier 0 already handled it
387
- const varDecl = findChildByType(node, 'variable_declaration');
388
- if (varDecl && findChildByType(varDecl, 'user_type'))
389
- return;
390
- // Get the initializer value — the call_expression after '='
421
+ return undefined;
391
422
  const value = node.childForFieldName('value')
392
- ?? findChildByType(node, 'call_expression');
423
+ ?? findChild(node, 'call_expression');
393
424
  if (!value || value.type !== 'call_expression')
394
- return;
395
- // The callee is the first child of call_expression (simple_identifier for direct calls)
425
+ return undefined;
396
426
  const callee = value.firstNamedChild;
397
427
  if (!callee || callee.type !== 'simple_identifier')
398
- return;
428
+ return undefined;
399
429
  const calleeName = callee.text;
400
430
  if (!calleeName || !classNames.has(calleeName))
431
+ return undefined;
432
+ return calleeName;
433
+ };
434
+ /** Kotlin: val user = User() — infer type from call_expression when callee is a known class.
435
+ * Kotlin constructors are syntactically identical to function calls, so we verify
436
+ * against classNames (which may include cross-file SymbolTable lookups). */
437
+ const extractKotlinInitializer = (node, env, classNames) => {
438
+ // Skip if there's an explicit type annotation — Tier 0 already handled it
439
+ const varDecl = findChild(node, 'variable_declaration');
440
+ if (varDecl && findChild(varDecl, 'user_type'))
441
+ return;
442
+ const calleeName = findKotlinConstructorCallee(node, classNames);
443
+ if (!calleeName)
401
444
  return;
402
445
  // Extract the variable name from the variable_declaration inside property_declaration
403
446
  const nameNode = varDecl
404
- ? findChildByType(varDecl, 'simple_identifier')
405
- : findChildByType(node, 'simple_identifier');
447
+ ? findChild(varDecl, 'simple_identifier')
448
+ : findChild(node, 'simple_identifier');
406
449
  if (!nameNode)
407
450
  return;
408
451
  const varName = extractVarName(nameNode);
409
452
  if (varName)
410
453
  env.set(varName, calleeName);
411
454
  };
455
+ /** Kotlin: detect constructor type from call_expression in typed declarations.
456
+ * Unlike extractKotlinInitializer (which SKIPS typed declarations), this detects
457
+ * the constructor type EVEN when a type annotation exists, enabling virtual dispatch
458
+ * for patterns like `val a: Animal = Dog()`. */
459
+ const detectKotlinConstructorType = (node, classNames) => {
460
+ return findKotlinConstructorCallee(node, classNames);
461
+ };
412
462
  /** Kotlin: val x = User(...) — constructor binding for property_declaration with call_expression */
413
463
  const scanKotlinConstructorBinding = (node) => {
414
464
  if (node.type !== 'property_declaration')
415
465
  return undefined;
416
- const varDecl = findChildByType(node, 'variable_declaration');
466
+ const varDecl = findChild(node, 'variable_declaration');
417
467
  if (!varDecl)
418
468
  return undefined;
419
- if (findChildByType(varDecl, 'user_type'))
469
+ if (findChild(varDecl, 'user_type'))
420
470
  return undefined;
421
- const callExpr = findChildByType(node, 'call_expression');
471
+ const callExpr = findChild(node, 'call_expression');
422
472
  if (!callExpr)
423
473
  return undefined;
424
474
  const callee = callExpr.firstNamedChild;
@@ -440,7 +490,7 @@ const scanKotlinConstructorBinding = (node) => {
440
490
  }
441
491
  if (!calleeName)
442
492
  return undefined;
443
- const nameNode = findChildByType(varDecl, 'simple_identifier');
493
+ const nameNode = findChild(varDecl, 'simple_identifier');
444
494
  if (!nameNode)
445
495
  return undefined;
446
496
  return { varName: nameNode.text, calleeName };
@@ -453,7 +503,7 @@ const KOTLIN_FOR_LOOP_NODE_TYPES = new Set([
453
503
  * Handles the type_projection wrapper that Kotlin uses for generic type arguments. */
454
504
  const extractKotlinElementTypeFromTypeNode = (typeNode, pos = 'last') => {
455
505
  if (typeNode.type === 'user_type') {
456
- const argsNode = findChildByType(typeNode, 'type_arguments');
506
+ const argsNode = findChild(typeNode, 'type_arguments');
457
507
  if (argsNode && argsNode.namedChildCount >= 1) {
458
508
  const targetArg = pos === 'first'
459
509
  ? argsNode.namedChild(0)
@@ -476,16 +526,16 @@ const findKotlinParamElementType = (iterableName, startNode, pos = 'last') => {
476
526
  let current = startNode.parent;
477
527
  while (current) {
478
528
  if (current.type === 'function_declaration') {
479
- const paramsNode = findChildByType(current, 'function_value_parameters');
529
+ const paramsNode = findChild(current, 'function_value_parameters');
480
530
  if (paramsNode) {
481
531
  for (let i = 0; i < paramsNode.namedChildCount; i++) {
482
532
  const param = paramsNode.namedChild(i);
483
533
  if (!param || param.type !== 'parameter')
484
534
  continue;
485
- const nameNode = findChildByType(param, 'simple_identifier');
535
+ const nameNode = findChild(param, 'simple_identifier');
486
536
  if (nameNode?.text !== iterableName)
487
537
  continue;
488
- const typeNode = findChildByType(param, 'user_type');
538
+ const typeNode = findChild(param, 'user_type');
489
539
  if (typeNode)
490
540
  return extractKotlinElementTypeFromTypeNode(typeNode, pos);
491
541
  }
@@ -500,17 +550,17 @@ const findKotlinParamElementType = (iterableName, startNode, pos = 'last') => {
500
550
  * Tier 1c: for `for (user in users)` without annotation, resolves from iterable. */
501
551
  const extractKotlinForLoopBinding = (node, ctx) => {
502
552
  const { scopeEnv, declarationTypeNodes, scope, returnTypeLookup } = ctx;
503
- const varDecl = findChildByType(node, 'variable_declaration');
553
+ const varDecl = findChild(node, 'variable_declaration');
504
554
  if (!varDecl)
505
555
  return;
506
- const nameNode = findChildByType(varDecl, 'simple_identifier');
556
+ const nameNode = findChild(varDecl, 'simple_identifier');
507
557
  if (!nameNode)
508
558
  return;
509
559
  const varName = extractVarName(nameNode);
510
560
  if (!varName)
511
561
  return;
512
562
  // Explicit type annotation (existing behavior): for (user: User in users)
513
- const typeNode = findChildByType(varDecl, 'user_type');
563
+ const typeNode = findChild(varDecl, 'user_type');
514
564
  if (typeNode) {
515
565
  const typeName = extractSimpleTypeName(typeNode);
516
566
  if (typeName)
@@ -540,9 +590,9 @@ const extractKotlinForLoopBinding = (node, ctx) => {
540
590
  if (child.type === 'navigation_expression') {
541
591
  // data.keys → navigation_expression > simple_identifier(data) + navigation_suffix > simple_identifier(keys)
542
592
  const obj = child.firstNamedChild;
543
- const suffix = findChildByType(child, 'navigation_suffix');
544
- const prop = suffix ? findChildByType(suffix, 'simple_identifier') : null;
545
- const hasCallSuffix = suffix ? findChildByType(suffix, 'call_suffix') !== null : false;
593
+ const suffix = findChild(child, 'navigation_suffix');
594
+ const prop = suffix ? findChild(suffix, 'simple_identifier') : null;
595
+ const hasCallSuffix = suffix ? findChild(suffix, 'call_suffix') !== null : false;
546
596
  // Always try object as iterable + property as method first (handles data.values, data.keys).
547
597
  // For bare property access without call_suffix, also save property as fallback
548
598
  // (handles this.users, repo.items where the property IS the iterable).
@@ -562,9 +612,9 @@ const extractKotlinForLoopBinding = (node, ctx) => {
562
612
  const obj = callee.firstNamedChild;
563
613
  if (obj?.type === 'simple_identifier')
564
614
  iterableName = obj.text;
565
- const suffix = findChildByType(callee, 'navigation_suffix');
615
+ const suffix = findChild(callee, 'navigation_suffix');
566
616
  if (suffix) {
567
- const prop = findChildByType(suffix, 'simple_identifier');
617
+ const prop = findChild(suffix, 'simple_identifier');
568
618
  if (prop)
569
619
  methodName = prop.text;
570
620
  }
@@ -606,7 +656,7 @@ const extractKotlinForLoopBinding = (node, ctx) => {
606
656
  const extractKotlinPendingAssignment = (node, scopeEnv) => {
607
657
  if (node.type === 'property_declaration') {
608
658
  // Find the variable name from variable_declaration child
609
- const varDecl = findChildByType(node, 'variable_declaration');
659
+ const varDecl = findChild(node, 'variable_declaration');
610
660
  if (!varDecl)
611
661
  return undefined;
612
662
  const nameNode = varDecl.firstNamedChild;
@@ -658,7 +708,7 @@ const extractKotlinPendingAssignment = (node, scopeEnv) => {
658
708
  }
659
709
  if (node.type === 'variable_declaration') {
660
710
  // variable_declaration directly inside functions: simple_identifier children
661
- const nameNode = findChildByType(node, 'simple_identifier');
711
+ const nameNode = findChild(node, 'simple_identifier');
662
712
  if (!nameNode)
663
713
  return undefined;
664
714
  const lhs = nameNode.text;
@@ -717,32 +767,83 @@ const findAncestorByType = (node, type) => {
717
767
  }
718
768
  return undefined;
719
769
  };
720
- const extractKotlinPatternBinding = (node) => {
721
- if (node.type !== 'type_test')
722
- return undefined;
723
- const typeNode = node.lastNamedChild;
724
- if (!typeNode)
725
- return undefined;
726
- const typeName = extractSimpleTypeName(typeNode);
727
- if (!typeName)
728
- return undefined;
729
- const whenExpr = findAncestorByType(node, 'when_expression');
730
- if (!whenExpr)
731
- return undefined;
732
- const whenSubject = whenExpr.namedChild(0);
733
- const subject = whenSubject?.firstNamedChild ?? whenSubject;
734
- if (!subject)
735
- return undefined;
736
- const varName = extractVarName(subject);
737
- if (!varName)
770
+ const extractKotlinPatternBinding = (node, scopeEnv, declarationTypeNodes, scope) => {
771
+ // Kotlin when/is smart casts (existing behavior)
772
+ if (node.type === 'type_test') {
773
+ const typeNode = node.lastNamedChild;
774
+ if (!typeNode)
775
+ return undefined;
776
+ const typeName = extractSimpleTypeName(typeNode);
777
+ if (!typeName)
778
+ return undefined;
779
+ const whenExpr = findAncestorByType(node, 'when_expression');
780
+ if (!whenExpr)
781
+ return undefined;
782
+ const whenSubject = whenExpr.namedChild(0);
783
+ const subject = whenSubject?.firstNamedChild ?? whenSubject;
784
+ if (!subject)
785
+ return undefined;
786
+ const varName = extractVarName(subject);
787
+ if (!varName)
788
+ return undefined;
789
+ return { varName, typeName };
790
+ }
791
+ // Null-check narrowing: if (x != null) { ... }
792
+ // Kotlin AST: equality_expression > simple_identifier, "!=" [anon], "null" [anon]
793
+ // Note: `null` is an anonymous node in tree-sitter-kotlin, not `null_literal`.
794
+ if (node.type === 'equality_expression') {
795
+ const op = node.children.find(c => !c.isNamed && c.text === '!=');
796
+ if (!op)
797
+ return undefined;
798
+ // `null` is anonymous in Kotlin grammar — use positional child scan
799
+ let varNode;
800
+ let hasNull = false;
801
+ for (let i = 0; i < node.childCount; i++) {
802
+ const c = node.child(i);
803
+ if (!c)
804
+ continue;
805
+ if (c.type === 'simple_identifier')
806
+ varNode = c;
807
+ if (!c.isNamed && c.text === 'null')
808
+ hasNull = true;
809
+ }
810
+ if (!varNode || !hasNull)
811
+ return undefined;
812
+ const varName = varNode.text;
813
+ const resolvedType = scopeEnv.get(varName);
814
+ if (!resolvedType)
815
+ return undefined;
816
+ // Check if the original declaration type was nullable (ends with ?)
817
+ const declTypeNode = declarationTypeNodes.get(`${scope}\0${varName}`);
818
+ if (!declTypeNode)
819
+ return undefined;
820
+ const declText = declTypeNode.text;
821
+ if (!declText.includes('?') && !declText.includes('null'))
822
+ return undefined;
823
+ // Find the if-body: walk up to if_expression, then find control_structure_body
824
+ const ifExpr = findAncestorByType(node, 'if_expression');
825
+ if (!ifExpr)
826
+ return undefined;
827
+ // The consequence is the first control_structure_body child
828
+ for (let i = 0; i < ifExpr.childCount; i++) {
829
+ const child = ifExpr.child(i);
830
+ if (child?.type === 'control_structure_body') {
831
+ return {
832
+ varName,
833
+ typeName: resolvedType,
834
+ narrowingRange: { startIndex: child.startIndex, endIndex: child.endIndex },
835
+ };
836
+ }
837
+ }
738
838
  return undefined;
739
- return { varName, typeName };
839
+ }
840
+ return undefined;
740
841
  };
741
842
  export const kotlinTypeConfig = {
742
843
  allowPatternBindingOverwrite: true,
743
844
  declarationNodeTypes: KOTLIN_DECLARATION_NODE_TYPES,
744
845
  forLoopNodeTypes: KOTLIN_FOR_LOOP_NODE_TYPES,
745
- patternBindingNodeTypes: new Set(['type_test']),
846
+ patternBindingNodeTypes: new Set(['type_test', 'equality_expression']),
746
847
  extractDeclaration: extractKotlinDeclaration,
747
848
  extractParameter: extractKotlinParameter,
748
849
  extractInitializer: extractKotlinInitializer,
@@ -750,4 +851,6 @@ export const kotlinTypeConfig = {
750
851
  extractForLoopBinding: extractKotlinForLoopBinding,
751
852
  extractPendingAssignment: extractKotlinPendingAssignment,
752
853
  extractPatternBinding: extractKotlinPatternBinding,
854
+ inferLiteralType: inferJvmLiteralType,
855
+ detectConstructorType: detectKotlinConstructorType,
753
856
  };
@@ -205,7 +205,8 @@ const scanConstructorBinding = (node) => {
205
205
  return undefined;
206
206
  return { varName: patternNode.text, calleeName };
207
207
  };
208
- /** Rust: let alias = u; → let_declaration with pattern + value fields */
208
+ /** Rust: let alias = u; → let_declaration with pattern + value fields.
209
+ * Also handles struct destructuring: `let Point { x, y } = p` → N fieldAccess items. */
209
210
  const extractPendingAssignment = (node, scopeEnv) => {
210
211
  if (node.type !== 'let_declaration')
211
212
  return undefined;
@@ -213,6 +214,39 @@ const extractPendingAssignment = (node, scopeEnv) => {
213
214
  const value = node.childForFieldName('value');
214
215
  if (!pattern || !value)
215
216
  return undefined;
217
+ // Struct pattern destructuring: `let Point { x, y } = receiver`
218
+ // struct_pattern has a type child (struct name) and field_pattern children
219
+ if (pattern.type === 'struct_pattern' && value.type === 'identifier') {
220
+ const receiver = value.text;
221
+ const items = [];
222
+ for (let j = 0; j < pattern.namedChildCount; j++) {
223
+ const field = pattern.namedChild(j);
224
+ if (!field)
225
+ continue;
226
+ if (field.type === 'field_pattern') {
227
+ // `Point { x: local_x }` → field_pattern with name + pattern children
228
+ const nameNode = field.childForFieldName('name');
229
+ const patNode = field.childForFieldName('pattern');
230
+ if (nameNode && patNode) {
231
+ const fieldName = nameNode.text;
232
+ const varName = extractVarName(patNode);
233
+ if (varName && !scopeEnv.has(varName)) {
234
+ items.push({ kind: 'fieldAccess', lhs: varName, receiver, field: fieldName });
235
+ }
236
+ }
237
+ else if (nameNode) {
238
+ // Shorthand: `Point { x }` → field_pattern with only name (varName = fieldName)
239
+ const varName = nameNode.text;
240
+ if (!scopeEnv.has(varName)) {
241
+ items.push({ kind: 'fieldAccess', lhs: varName, receiver, field: varName });
242
+ }
243
+ }
244
+ }
245
+ }
246
+ if (items.length > 0)
247
+ return items;
248
+ return undefined;
249
+ }
216
250
  const lhs = extractVarName(pattern);
217
251
  if (!lhs || scopeEnv.has(lhs))
218
252
  return undefined;
@@ -1,4 +1,4 @@
1
- import type { SyntaxNode } from '../utils.js';
1
+ import type { SyntaxNode } from '../utils/ast-helpers.js';
2
2
  /** Which type argument to extract from a multi-arg generic container.
3
3
  * - 'first': key type (e.g., K from Map<K,V>) — used for .keys(), .keySet()
4
4
  * - 'last': value type (e.g., V from Map<K,V>) — used for .values(), .items(), .iter() */
@@ -108,8 +108,6 @@ export declare const unwrapAwait: (node: SyntaxNode | null) => SyntaxNode | null
108
108
  * Navigates to the 'function' field (or first named child) and extracts a simple type name.
109
109
  */
110
110
  export declare const extractCalleeName: (callNode: SyntaxNode) => string | undefined;
111
- /** Find the first named child with the given node type */
112
- export declare const findChildByType: (node: SyntaxNode, type: string) => SyntaxNode | null;
113
111
  /**
114
112
  * Extract element type from a container type string.
115
113
  * Uses bracket-balanced parsing (no regex) for generic argument extraction.
@@ -130,16 +128,4 @@ export declare const findChildByType: (node: SyntaxNode, type: string) => Syntax
130
128
  */
131
129
  export declare function extractElementTypeFromString(typeStr: string, pos?: TypeArgPosition): string | undefined;
132
130
  export declare const extractReturnTypeName: (raw: string, depth?: number) => string | undefined;
133
- /**
134
- * Extract the declared type of a property/field from its AST definition node.
135
- * Handles cross-language patterns:
136
- * - TypeScript: `name: Type` → type_annotation child
137
- * - Java: `Type name` → type child on field_declaration
138
- * - C#: `Type Name { get; set; }` → type child on property_declaration
139
- * - Go: `Name Type` → type child on field_declaration
140
- * - Kotlin: `var name: Type` → variable_declaration child with type field
141
- *
142
- * Returns the normalized type name, or undefined if no type can be extracted.
143
- */
144
- export declare const extractPropertyDeclaredType: (definitionNode: SyntaxNode | null) => string | undefined;
145
131
  export {};
@@ -237,7 +237,11 @@ export const extractSimpleTypeName = (typeNode, depth = 0) => {
237
237
  }
238
238
  // Primitive/predefined types: string, int, float, bool, number, unknown, any
239
239
  // PHP: primitive_type; TS/JS: predefined_type
240
- if (typeNode.type === 'primitive_type' || typeNode.type === 'predefined_type') {
240
+ // Java: integral_type (int/long/short/byte), floating_point_type (float/double),
241
+ // boolean_type (boolean), void_type (void)
242
+ if (typeNode.type === 'primitive_type' || typeNode.type === 'predefined_type'
243
+ || typeNode.type === 'integral_type' || typeNode.type === 'floating_point_type'
244
+ || typeNode.type === 'boolean_type' || typeNode.type === 'void_type') {
241
245
  return typeNode.text;
242
246
  }
243
247
  // PHP named_type / optional_type
@@ -274,6 +278,12 @@ export const extractVarName = (node) => {
274
278
  if (inner)
275
279
  return extractVarName(inner);
276
280
  }
281
+ // Swift: pattern node wraps a simple_identifier
282
+ if (node.type === 'pattern') {
283
+ const inner = node.firstNamedChild;
284
+ if (inner)
285
+ return extractVarName(inner);
286
+ }
277
287
  return undefined;
278
288
  };
279
289
  /** Node types for function/method parameters with type annotations */
@@ -454,15 +464,6 @@ export const extractCalleeName = (callNode) => {
454
464
  return undefined;
455
465
  return extractSimpleTypeName(func);
456
466
  };
457
- /** Find the first named child with the given node type */
458
- export const findChildByType = (node, type) => {
459
- for (let i = 0; i < node.namedChildCount; i++) {
460
- const child = node.namedChild(i);
461
- if (child?.type === type)
462
- return child;
463
- }
464
- return null;
465
- };
466
467
  // Internal helper: extract the first comma-separated argument from a string,
467
468
  // respecting nested angle-bracket and square-bracket depth.
468
469
  function extractFirstArg(args) {
@@ -706,105 +707,6 @@ export const extractReturnTypeName = (raw, depth = 0) => {
706
707
  return undefined;
707
708
  return text;
708
709
  };
709
- // ── Property declared-type extraction ────────────────────────────────────
710
- // Shared between parse-worker (worker path) and parsing-processor (sequential path).
711
- /**
712
- * Extract the declared type of a property/field from its AST definition node.
713
- * Handles cross-language patterns:
714
- * - TypeScript: `name: Type` → type_annotation child
715
- * - Java: `Type name` → type child on field_declaration
716
- * - C#: `Type Name { get; set; }` → type child on property_declaration
717
- * - Go: `Name Type` → type child on field_declaration
718
- * - Kotlin: `var name: Type` → variable_declaration child with type field
719
- *
720
- * Returns the normalized type name, or undefined if no type can be extracted.
721
- */
722
- export const extractPropertyDeclaredType = (definitionNode) => {
723
- if (!definitionNode)
724
- return undefined;
725
- // Strategy 1: Look for a `type` or `type_annotation` named field
726
- const typeNode = definitionNode.childForFieldName?.('type');
727
- if (typeNode) {
728
- const typeName = extractSimpleTypeName(typeNode);
729
- if (typeName)
730
- return typeName;
731
- // Fallback: use the raw text (for complex types like User[] or List<User>)
732
- const text = typeNode.text?.trim();
733
- if (text && text.length < 100)
734
- return text;
735
- }
736
- // Strategy 2: Walk children looking for type_annotation (TypeScript pattern)
737
- for (let i = 0; i < definitionNode.childCount; i++) {
738
- const child = definitionNode.child(i);
739
- if (!child)
740
- continue;
741
- if (child.type === 'type_annotation') {
742
- // Type annotation has the actual type as a child
743
- for (let j = 0; j < child.childCount; j++) {
744
- const typeChild = child.child(j);
745
- if (typeChild && typeChild.type !== ':') {
746
- const typeName = extractSimpleTypeName(typeChild);
747
- if (typeName)
748
- return typeName;
749
- const text = typeChild.text?.trim();
750
- if (text && text.length < 100)
751
- return text;
752
- }
753
- }
754
- }
755
- }
756
- // Strategy 3: For Java field_declaration, the type is a sibling of variable_declarator
757
- // AST: (field_declaration type: (type_identifier) declarator: (variable_declarator ...))
758
- const parentDecl = definitionNode.parent;
759
- if (parentDecl) {
760
- const parentType = parentDecl.childForFieldName?.('type');
761
- if (parentType) {
762
- const typeName = extractSimpleTypeName(parentType);
763
- if (typeName)
764
- return typeName;
765
- }
766
- }
767
- // Strategy 4: Kotlin property_declaration — type is nested inside variable_declaration child
768
- // AST: (property_declaration (variable_declaration (simple_identifier) ":" (user_type (type_identifier))))
769
- // Kotlin's variable_declaration has NO named 'type' field — children are all positional.
770
- for (let i = 0; i < definitionNode.childCount; i++) {
771
- const child = definitionNode.child(i);
772
- if (child?.type === 'variable_declaration') {
773
- // Try named field first (works for other languages sharing this strategy)
774
- const varType = child.childForFieldName?.('type');
775
- if (varType) {
776
- const typeName = extractSimpleTypeName(varType);
777
- if (typeName)
778
- return typeName;
779
- const text = varType.text?.trim();
780
- if (text && text.length < 100)
781
- return text;
782
- }
783
- // Fallback: walk unnamed children for user_type / type_identifier (Kotlin)
784
- for (let j = 0; j < child.namedChildCount; j++) {
785
- const varChild = child.namedChild(j);
786
- if (varChild && (varChild.type === 'user_type' || varChild.type === 'type_identifier'
787
- || varChild.type === 'nullable_type' || varChild.type === 'generic_type')) {
788
- const typeName = extractSimpleTypeName(varChild);
789
- if (typeName)
790
- return typeName;
791
- }
792
- }
793
- }
794
- }
795
- // Strategy 5: PHP @var PHPDoc — look for preceding comment with @var Type
796
- // Handles pre-PHP-7.4 code: /** @var Address */ public $address;
797
- const prevSibling = definitionNode.previousNamedSibling ?? definitionNode.parent?.previousNamedSibling;
798
- if (prevSibling?.type === 'comment') {
799
- const commentText = prevSibling.text;
800
- const varMatch = commentText?.match(/@var\s+([A-Z][\w\\]*)/);
801
- if (varMatch) {
802
- // Strip namespace prefix: \App\Models\User → User
803
- const raw = varMatch[1];
804
- const base = raw.includes('\\') ? raw.split('\\').pop() : raw;
805
- if (base && /^[A-Z]\w*$/.test(base))
806
- return base;
807
- }
808
- }
809
- return undefined;
810
- };
710
+ // extractPropertyDeclaredType removed all 14 languages register a FieldExtractor
711
+ // via defineLanguage() which is the single source of truth for Property metadata
712
+ // (declaredType, visibility, isStatic, isReadonly).