@optave/codegraph 3.11.1 → 3.12.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (176) hide show
  1. package/README.md +8 -8
  2. package/dist/db/migrations.d.ts.map +1 -1
  3. package/dist/db/migrations.js +7 -0
  4. package/dist/db/migrations.js.map +1 -1
  5. package/dist/domain/analysis/module-map.d.ts +2 -0
  6. package/dist/domain/analysis/module-map.d.ts.map +1 -1
  7. package/dist/domain/analysis/module-map.js +24 -2
  8. package/dist/domain/analysis/module-map.js.map +1 -1
  9. package/dist/domain/graph/builder/call-resolver.d.ts +73 -0
  10. package/dist/domain/graph/builder/call-resolver.d.ts.map +1 -0
  11. package/dist/domain/graph/builder/call-resolver.js +292 -0
  12. package/dist/domain/graph/builder/call-resolver.js.map +1 -0
  13. package/dist/domain/graph/builder/cha.d.ts +61 -0
  14. package/dist/domain/graph/builder/cha.d.ts.map +1 -0
  15. package/dist/domain/graph/builder/cha.js +143 -0
  16. package/dist/domain/graph/builder/cha.js.map +1 -0
  17. package/dist/domain/graph/builder/context.d.ts +3 -0
  18. package/dist/domain/graph/builder/context.d.ts.map +1 -1
  19. package/dist/domain/graph/builder/context.js +2 -0
  20. package/dist/domain/graph/builder/context.js.map +1 -1
  21. package/dist/domain/graph/builder/helpers.d.ts +17 -1
  22. package/dist/domain/graph/builder/helpers.d.ts.map +1 -1
  23. package/dist/domain/graph/builder/helpers.js +159 -5
  24. package/dist/domain/graph/builder/helpers.js.map +1 -1
  25. package/dist/domain/graph/builder/incremental.d.ts.map +1 -1
  26. package/dist/domain/graph/builder/incremental.js +147 -54
  27. package/dist/domain/graph/builder/incremental.js.map +1 -1
  28. package/dist/domain/graph/builder/stages/build-edges.d.ts +2 -0
  29. package/dist/domain/graph/builder/stages/build-edges.d.ts.map +1 -1
  30. package/dist/domain/graph/builder/stages/build-edges.js +932 -110
  31. package/dist/domain/graph/builder/stages/build-edges.js.map +1 -1
  32. package/dist/domain/graph/builder/stages/detect-changes.d.ts.map +1 -1
  33. package/dist/domain/graph/builder/stages/detect-changes.js +2 -1
  34. package/dist/domain/graph/builder/stages/detect-changes.js.map +1 -1
  35. package/dist/domain/graph/builder/stages/native-orchestrator.d.ts.map +1 -1
  36. package/dist/domain/graph/builder/stages/native-orchestrator.js +501 -14
  37. package/dist/domain/graph/builder/stages/native-orchestrator.js.map +1 -1
  38. package/dist/domain/graph/builder/stages/resolve-imports.d.ts +1 -0
  39. package/dist/domain/graph/builder/stages/resolve-imports.d.ts.map +1 -1
  40. package/dist/domain/graph/builder/stages/resolve-imports.js +9 -0
  41. package/dist/domain/graph/builder/stages/resolve-imports.js.map +1 -1
  42. package/dist/domain/graph/journal.js +1 -1
  43. package/dist/domain/graph/journal.js.map +1 -1
  44. package/dist/domain/graph/resolver/points-to.d.ts +53 -0
  45. package/dist/domain/graph/resolver/points-to.d.ts.map +1 -0
  46. package/dist/domain/graph/resolver/points-to.js +213 -0
  47. package/dist/domain/graph/resolver/points-to.js.map +1 -0
  48. package/dist/domain/graph/resolver/ts-resolver.d.ts +9 -0
  49. package/dist/domain/graph/resolver/ts-resolver.d.ts.map +1 -0
  50. package/dist/domain/graph/resolver/ts-resolver.js +476 -0
  51. package/dist/domain/graph/resolver/ts-resolver.js.map +1 -0
  52. package/dist/domain/graph/watcher.d.ts.map +1 -1
  53. package/dist/domain/graph/watcher.js +5 -2
  54. package/dist/domain/graph/watcher.js.map +1 -1
  55. package/dist/domain/parser.d.ts +10 -1
  56. package/dist/domain/parser.d.ts.map +1 -1
  57. package/dist/domain/parser.js +39 -7
  58. package/dist/domain/parser.js.map +1 -1
  59. package/dist/domain/wasm-worker-entry.js +25 -0
  60. package/dist/domain/wasm-worker-entry.js.map +1 -1
  61. package/dist/domain/wasm-worker-pool.d.ts.map +1 -1
  62. package/dist/domain/wasm-worker-pool.js +32 -0
  63. package/dist/domain/wasm-worker-pool.js.map +1 -1
  64. package/dist/domain/wasm-worker-protocol.d.ts +14 -1
  65. package/dist/domain/wasm-worker-protocol.d.ts.map +1 -1
  66. package/dist/extractors/c.js +3 -3
  67. package/dist/extractors/c.js.map +1 -1
  68. package/dist/extractors/clojure.js +1 -1
  69. package/dist/extractors/clojure.js.map +1 -1
  70. package/dist/extractors/cpp.js +3 -3
  71. package/dist/extractors/cpp.js.map +1 -1
  72. package/dist/extractors/csharp.d.ts.map +1 -1
  73. package/dist/extractors/csharp.js +37 -8
  74. package/dist/extractors/csharp.js.map +1 -1
  75. package/dist/extractors/cuda.js +3 -3
  76. package/dist/extractors/cuda.js.map +1 -1
  77. package/dist/extractors/elixir.js +6 -6
  78. package/dist/extractors/elixir.js.map +1 -1
  79. package/dist/extractors/fsharp.js +1 -1
  80. package/dist/extractors/fsharp.js.map +1 -1
  81. package/dist/extractors/go.js +5 -5
  82. package/dist/extractors/go.js.map +1 -1
  83. package/dist/extractors/haskell.js +1 -1
  84. package/dist/extractors/haskell.js.map +1 -1
  85. package/dist/extractors/java.js +2 -2
  86. package/dist/extractors/java.js.map +1 -1
  87. package/dist/extractors/javascript.d.ts +2 -0
  88. package/dist/extractors/javascript.d.ts.map +1 -1
  89. package/dist/extractors/javascript.js +1674 -64
  90. package/dist/extractors/javascript.js.map +1 -1
  91. package/dist/extractors/kotlin.js +5 -5
  92. package/dist/extractors/kotlin.js.map +1 -1
  93. package/dist/extractors/lua.js +1 -1
  94. package/dist/extractors/lua.js.map +1 -1
  95. package/dist/extractors/objc.js +3 -3
  96. package/dist/extractors/objc.js.map +1 -1
  97. package/dist/extractors/ocaml.js +1 -1
  98. package/dist/extractors/ocaml.js.map +1 -1
  99. package/dist/extractors/php.js +2 -2
  100. package/dist/extractors/php.js.map +1 -1
  101. package/dist/extractors/python.js +7 -7
  102. package/dist/extractors/python.js.map +1 -1
  103. package/dist/extractors/ruby.js +2 -2
  104. package/dist/extractors/ruby.js.map +1 -1
  105. package/dist/extractors/scala.js +1 -1
  106. package/dist/extractors/scala.js.map +1 -1
  107. package/dist/extractors/solidity.js +1 -1
  108. package/dist/extractors/solidity.js.map +1 -1
  109. package/dist/extractors/swift.js +4 -4
  110. package/dist/extractors/swift.js.map +1 -1
  111. package/dist/extractors/zig.js +4 -4
  112. package/dist/extractors/zig.js.map +1 -1
  113. package/dist/features/structure.d.ts.map +1 -1
  114. package/dist/features/structure.js +121 -16
  115. package/dist/features/structure.js.map +1 -1
  116. package/dist/infrastructure/config.d.ts +10 -0
  117. package/dist/infrastructure/config.d.ts.map +1 -1
  118. package/dist/infrastructure/config.js +15 -0
  119. package/dist/infrastructure/config.js.map +1 -1
  120. package/dist/infrastructure/native.d.ts +11 -0
  121. package/dist/infrastructure/native.d.ts.map +1 -1
  122. package/dist/infrastructure/native.js +78 -5
  123. package/dist/infrastructure/native.js.map +1 -1
  124. package/dist/presentation/queries-cli/overview.d.ts.map +1 -1
  125. package/dist/presentation/queries-cli/overview.js +5 -0
  126. package/dist/presentation/queries-cli/overview.js.map +1 -1
  127. package/dist/types.d.ts +184 -0
  128. package/dist/types.d.ts.map +1 -1
  129. package/grammars/tree-sitter-erlang.wasm +0 -0
  130. package/package.json +9 -9
  131. package/src/db/migrations.ts +7 -0
  132. package/src/domain/analysis/module-map.ts +29 -1
  133. package/src/domain/graph/builder/call-resolver.ts +351 -0
  134. package/src/domain/graph/builder/cha.ts +175 -0
  135. package/src/domain/graph/builder/context.ts +3 -0
  136. package/src/domain/graph/builder/helpers.ts +175 -5
  137. package/src/domain/graph/builder/incremental.ts +186 -66
  138. package/src/domain/graph/builder/stages/build-edges.ts +1146 -146
  139. package/src/domain/graph/builder/stages/detect-changes.ts +3 -1
  140. package/src/domain/graph/builder/stages/native-orchestrator.ts +583 -20
  141. package/src/domain/graph/builder/stages/resolve-imports.ts +14 -0
  142. package/src/domain/graph/journal.ts +1 -1
  143. package/src/domain/graph/resolver/points-to.ts +254 -0
  144. package/src/domain/graph/resolver/ts-resolver.ts +536 -0
  145. package/src/domain/graph/watcher.ts +4 -2
  146. package/src/domain/parser.ts +43 -5
  147. package/src/domain/wasm-worker-entry.ts +25 -0
  148. package/src/domain/wasm-worker-pool.ts +21 -0
  149. package/src/domain/wasm-worker-protocol.ts +14 -0
  150. package/src/extractors/c.ts +3 -3
  151. package/src/extractors/clojure.ts +1 -1
  152. package/src/extractors/cpp.ts +3 -3
  153. package/src/extractors/csharp.ts +33 -9
  154. package/src/extractors/cuda.ts +3 -3
  155. package/src/extractors/elixir.ts +6 -6
  156. package/src/extractors/fsharp.ts +1 -1
  157. package/src/extractors/go.ts +5 -5
  158. package/src/extractors/haskell.ts +1 -1
  159. package/src/extractors/java.ts +2 -2
  160. package/src/extractors/javascript.ts +1802 -66
  161. package/src/extractors/kotlin.ts +5 -5
  162. package/src/extractors/lua.ts +1 -1
  163. package/src/extractors/objc.ts +3 -3
  164. package/src/extractors/ocaml.ts +1 -1
  165. package/src/extractors/php.ts +2 -2
  166. package/src/extractors/python.ts +7 -7
  167. package/src/extractors/ruby.ts +2 -2
  168. package/src/extractors/scala.ts +1 -1
  169. package/src/extractors/solidity.ts +1 -1
  170. package/src/extractors/swift.ts +4 -4
  171. package/src/extractors/zig.ts +4 -4
  172. package/src/features/structure.ts +143 -23
  173. package/src/infrastructure/config.ts +15 -0
  174. package/src/infrastructure/native.ts +87 -5
  175. package/src/presentation/queries-cli/overview.ts +15 -1
  176. package/src/types.ts +194 -0
@@ -109,8 +109,10 @@ function grammarPath(name: string): string {
109
109
 
110
110
  const COMMON_QUERY_PATTERNS: string[] = [
111
111
  '(function_declaration name: (identifier) @fn_name) @fn_node',
112
+ '(generator_function_declaration name: (identifier) @fn_name) @fn_node',
112
113
  '(variable_declarator name: (identifier) @varfn_name value: (arrow_function) @varfn_value)',
113
114
  '(variable_declarator name: (identifier) @varfn_name value: (function_expression) @varfn_value)',
115
+ '(variable_declarator name: (identifier) @varfn_name value: (generator_function) @varfn_value)',
114
116
  '(method_definition name: (property_identifier) @meth_name) @meth_node',
115
117
  '(method_definition name: (private_property_identifier) @meth_name) @meth_node',
116
118
  '(import_statement source: (string) @imp_source) @imp_node',
@@ -127,6 +129,7 @@ const JS_CLASS_PATTERN: string = '(class_declaration name: (identifier) @cls_nam
127
129
 
128
130
  const TS_EXTRA_PATTERNS: string[] = [
129
131
  '(class_declaration name: (type_identifier) @cls_name) @cls_node',
132
+ '(abstract_class_declaration name: (type_identifier) @cls_name) @cls_node',
130
133
  '(interface_declaration name: (type_identifier) @iface_name) @iface_node',
131
134
  '(type_alias_declaration name: (type_identifier) @type_name) @type_node',
132
135
  ];
@@ -801,6 +804,28 @@ function serializeExtractorOutput(
801
804
  _lineCount: code.split('\n').length,
802
805
  dataflow: symbols.dataflow,
803
806
  astNodes,
807
+ ...(symbols.fnRefBindings?.length ? { fnRefBindings: symbols.fnRefBindings } : {}),
808
+ ...(symbols.paramBindings?.length ? { paramBindings: symbols.paramBindings } : {}),
809
+ ...(symbols.arrayElemBindings?.length ? { arrayElemBindings: symbols.arrayElemBindings } : {}),
810
+ ...(symbols.spreadArgBindings?.length ? { spreadArgBindings: symbols.spreadArgBindings } : {}),
811
+ ...(symbols.forOfBindings?.length ? { forOfBindings: symbols.forOfBindings } : {}),
812
+ ...(symbols.arrayCallbackBindings?.length
813
+ ? { arrayCallbackBindings: symbols.arrayCallbackBindings }
814
+ : {}),
815
+ ...(symbols.objectRestParamBindings?.length
816
+ ? { objectRestParamBindings: symbols.objectRestParamBindings }
817
+ : {}),
818
+ ...(symbols.objectPropBindings?.length
819
+ ? { objectPropBindings: symbols.objectPropBindings }
820
+ : {}),
821
+ ...(symbols.newExpressions?.length ? { newExpressions: symbols.newExpressions } : {}),
822
+ ...(symbols.definePropertyReceivers?.size
823
+ ? { definePropertyReceivers: Array.from(symbols.definePropertyReceivers.entries()) }
824
+ : {}),
825
+ ...(symbols.returnTypeMap?.size
826
+ ? { returnTypeMap: Array.from(symbols.returnTypeMap.entries()) }
827
+ : {}),
828
+ ...(symbols.callAssignments?.length ? { callAssignments: symbols.callAssignments } : {}),
804
829
  };
805
830
  }
806
831
 
@@ -106,6 +106,27 @@ function deserializeResult(ser: SerializedExtractorOutput | null): ExtractorOutp
106
106
  // {line, kind, name, text?, receiver?} shape — see engine.ts:822 where the
107
107
  // visitor output is cast the same way.
108
108
  if (ser.astNodes !== undefined) out.astNodes = ser.astNodes as unknown as ASTNodeRow[];
109
+ if (ser.fnRefBindings?.length) out.fnRefBindings = ser.fnRefBindings;
110
+ if (ser.paramBindings?.length) out.paramBindings = ser.paramBindings;
111
+ if (ser.arrayElemBindings?.length) out.arrayElemBindings = ser.arrayElemBindings;
112
+ if (ser.spreadArgBindings?.length) out.spreadArgBindings = ser.spreadArgBindings;
113
+ if (ser.forOfBindings?.length) out.forOfBindings = ser.forOfBindings;
114
+ if (ser.arrayCallbackBindings?.length) out.arrayCallbackBindings = ser.arrayCallbackBindings;
115
+ if (ser.objectRestParamBindings?.length)
116
+ out.objectRestParamBindings = ser.objectRestParamBindings;
117
+ if (ser.objectPropBindings?.length) out.objectPropBindings = ser.objectPropBindings;
118
+ if (ser.newExpressions?.length) out.newExpressions = ser.newExpressions;
119
+ if (ser.definePropertyReceivers?.length) {
120
+ const m = new Map<string, string>();
121
+ for (const [k, v] of ser.definePropertyReceivers) m.set(k, v);
122
+ out.definePropertyReceivers = m;
123
+ }
124
+ if (ser.returnTypeMap?.length) {
125
+ const returnTypeMap = new Map<string, TypeMapEntry>();
126
+ for (const [k, v] of ser.returnTypeMap) returnTypeMap.set(k, v);
127
+ out.returnTypeMap = returnTypeMap;
128
+ }
129
+ if (ser.callAssignments?.length) out.callAssignments = ser.callAssignments;
109
130
  return out;
110
131
  }
111
132
 
@@ -12,6 +12,7 @@
12
12
 
13
13
  import type {
14
14
  Call,
15
+ CallAssignment,
15
16
  ClassRelation,
16
17
  DataflowResult,
17
18
  Definition,
@@ -62,6 +63,19 @@ export interface SerializedExtractorOutput {
62
63
  text?: string;
63
64
  receiver?: string;
64
65
  }>;
66
+ fnRefBindings?: import('../types.js').FnRefBinding[];
67
+ arrayElemBindings?: import('../types.js').ArrayElemBinding[];
68
+ spreadArgBindings?: import('../types.js').SpreadArgBinding[];
69
+ forOfBindings?: import('../types.js').ForOfBinding[];
70
+ arrayCallbackBindings?: import('../types.js').ArrayCallbackBinding[];
71
+ objectRestParamBindings?: import('../types.js').ObjectRestParamBinding[];
72
+ objectPropBindings?: import('../types.js').ObjectPropBinding[];
73
+ paramBindings?: import('../types.js').ParamBinding[];
74
+ newExpressions?: readonly string[];
75
+ /** Serialized definePropertyReceivers map (funcName → receiverVarName) as tuple array. */
76
+ definePropertyReceivers?: Array<[string, string]>;
77
+ returnTypeMap?: Array<[string, TypeMapEntry]>;
78
+ callAssignments?: CallAssignment[];
65
79
  }
66
80
 
67
81
  export interface WorkerParseResponseOk {
@@ -189,7 +189,7 @@ function extractCParameters(paramListNode: TreeSitterNode | null): SubDeclaratio
189
189
  if (!paramListNode) return params;
190
190
  for (let i = 0; i < paramListNode.childCount; i++) {
191
191
  const param = paramListNode.child(i);
192
- if (!param || param.type !== 'parameter_declaration') continue;
192
+ if (param?.type !== 'parameter_declaration') continue;
193
193
  const nameNode = param.childForFieldName('declarator');
194
194
  if (nameNode) {
195
195
  const name = unwrapCDeclaratorName(nameNode);
@@ -205,7 +205,7 @@ function extractStructFields(structNode: TreeSitterNode): SubDeclaration[] {
205
205
  if (!body) return fields;
206
206
  for (let i = 0; i < body.childCount; i++) {
207
207
  const member = body.child(i);
208
- if (!member || member.type !== 'field_declaration') continue;
208
+ if (member?.type !== 'field_declaration') continue;
209
209
  const nameNode = member.childForFieldName('declarator');
210
210
  if (nameNode) {
211
211
  const name = unwrapCDeclaratorName(nameNode);
@@ -221,7 +221,7 @@ function extractEnumEntries(enumNode: TreeSitterNode): SubDeclaration[] {
221
221
  if (!body) return entries;
222
222
  for (let i = 0; i < body.childCount; i++) {
223
223
  const member = body.child(i);
224
- if (!member || member.type !== 'enumerator') continue;
224
+ if (member?.type !== 'enumerator') continue;
225
225
  const nameNode = member.childForFieldName('name');
226
226
  if (nameNode) {
227
227
  entries.push({ name: nameNode.text, kind: 'constant', line: member.startPosition.row + 1 });
@@ -224,7 +224,7 @@ function extractClojureParams(defnNode: TreeSitterNode): SubDeclaration[] {
224
224
  // Find the parameter vector [x y z]
225
225
  for (let i = 0; i < defnNode.childCount; i++) {
226
226
  const child = defnNode.child(i);
227
- if (!child || child.type !== 'vec_lit') continue;
227
+ if (child?.type !== 'vec_lit') continue;
228
228
  for (let j = 0; j < child.childCount; j++) {
229
229
  const param = child.child(j);
230
230
  if (!param) continue;
@@ -292,7 +292,7 @@ function extractCppParameters(paramListNode: TreeSitterNode | null): SubDeclarat
292
292
  if (!paramListNode) return params;
293
293
  for (let i = 0; i < paramListNode.childCount; i++) {
294
294
  const param = paramListNode.child(i);
295
- if (!param || param.type !== 'parameter_declaration') continue;
295
+ if (param?.type !== 'parameter_declaration') continue;
296
296
  const nameNode = param.childForFieldName('declarator');
297
297
  if (nameNode) {
298
298
  const name = unwrapCppDeclaratorName(nameNode);
@@ -309,7 +309,7 @@ function extractCppClassFields(classNode: TreeSitterNode): SubDeclaration[] {
309
309
  if (!body) return fields;
310
310
  for (let i = 0; i < body.childCount; i++) {
311
311
  const member = body.child(i);
312
- if (!member || member.type !== 'field_declaration') continue;
312
+ if (member?.type !== 'field_declaration') continue;
313
313
  const nameNode = member.childForFieldName('declarator');
314
314
  if (nameNode) {
315
315
  const name = unwrapCppDeclaratorName(nameNode);
@@ -330,7 +330,7 @@ function extractCppEnumEntries(enumNode: TreeSitterNode): SubDeclaration[] {
330
330
  if (!body) return entries;
331
331
  for (let i = 0; i < body.childCount; i++) {
332
332
  const member = body.child(i);
333
- if (!member || member.type !== 'enumerator') continue;
333
+ if (member?.type !== 'enumerator') continue;
334
334
  const nameNode = member.childForFieldName('name');
335
335
  if (nameNode) {
336
336
  entries.push({ name: nameNode.text, kind: 'constant', line: member.startPosition.row + 1 });
@@ -28,6 +28,7 @@ export function extractCSharpSymbols(tree: TreeSitterTree, _filePath: string): E
28
28
  classes: [],
29
29
  exports: [],
30
30
  typeMap: new Map(),
31
+ newExpressions: [],
31
32
  };
32
33
 
33
34
  walkCSharpNode(tree.rootNode, ctx);
@@ -252,7 +253,12 @@ function handleCsObjectCreation(node: TreeSitterNode, ctx: ExtractorOutput): voi
252
253
  typeNode.type === 'generic_name'
253
254
  ? typeNode.childForFieldName('name')?.text || typeNode.child(0)?.text
254
255
  : typeNode.text;
255
- if (typeName) ctx.calls.push({ name: typeName, line: node.startPosition.row + 1 });
256
+ if (typeName) {
257
+ ctx.calls.push({ name: typeName, line: node.startPosition.row + 1 });
258
+ // Phase 8.5 (RTA): record instantiated type for CHA filtering, matching
259
+ // the JS extractor's newExpressions extraction for cross-engine parity.
260
+ (ctx.newExpressions as string[]).push(typeName);
261
+ }
256
262
  }
257
263
 
258
264
  const CS_PARENT_TYPES = [
@@ -273,7 +279,7 @@ function extractCSharpParameters(paramListNode: TreeSitterNode | null): SubDecla
273
279
  if (!paramListNode) return params;
274
280
  for (let i = 0; i < paramListNode.childCount; i++) {
275
281
  const param = paramListNode.child(i);
276
- if (!param || param.type !== 'parameter') continue;
282
+ if (param?.type !== 'parameter') continue;
277
283
  const nameNode = param.childForFieldName('name');
278
284
  if (nameNode) {
279
285
  params.push({ name: nameNode.text, kind: 'parameter', line: param.startPosition.row + 1 });
@@ -288,12 +294,12 @@ function extractCSharpClassFields(classNode: TreeSitterNode): SubDeclaration[] {
288
294
  if (!body) return fields;
289
295
  for (let i = 0; i < body.childCount; i++) {
290
296
  const member = body.child(i);
291
- if (!member || member.type !== 'field_declaration') continue;
297
+ if (member?.type !== 'field_declaration') continue;
292
298
  const varDecl = findChild(member, 'variable_declaration');
293
299
  if (!varDecl) continue;
294
300
  for (let j = 0; j < varDecl.childCount; j++) {
295
301
  const child = varDecl.child(j);
296
- if (!child || child.type !== 'variable_declarator') continue;
302
+ if (child?.type !== 'variable_declarator') continue;
297
303
  const nameNode = child.childForFieldName('name');
298
304
  if (nameNode) {
299
305
  fields.push({
@@ -326,16 +332,34 @@ function extractCSharpTypeMap(node: TreeSitterNode, ctx: ExtractorOutput): void
326
332
  /** Extract type info from a variable_declaration node (local vars with explicit types). */
327
333
  function handleCSharpVarDecl(node: TreeSitterNode, ctx: ExtractorOutput): void {
328
334
  const typeNode = node.childForFieldName('type') || node.child(0);
329
- if (!typeNode || typeNode.type === 'var_keyword') return;
335
+ if (!typeNode) return;
336
+
337
+ if (typeNode.type === 'implicit_type') {
338
+ // var x = new Foo() — infer type from object_creation_expression initializer
339
+ if (!ctx.typeMap) return;
340
+ for (let i = 0; i < node.childCount; i++) {
341
+ const child = node.child(i);
342
+ if (child?.type !== 'variable_declarator') continue;
343
+ const nameNode = child.childForFieldName('name') || child.child(0);
344
+ if (nameNode?.type !== 'identifier') continue;
345
+ const objCreation = findChild(child, 'object_creation_expression');
346
+ if (!objCreation) continue;
347
+ const ctorTypeNode = objCreation.childForFieldName('type');
348
+ if (!ctorTypeNode) continue;
349
+ const ctorType = extractCSharpTypeName(ctorTypeNode);
350
+ if (ctorType) setTypeMapEntry(ctx.typeMap, nameNode.text, ctorType, 1.0);
351
+ }
352
+ return;
353
+ }
354
+
330
355
  const typeName = extractCSharpTypeName(typeNode);
331
356
  if (!typeName) return;
332
357
  for (let i = 0; i < node.childCount; i++) {
333
358
  const child = node.child(i);
334
- if (!child || child.type !== 'variable_declarator') continue;
359
+ if (child?.type !== 'variable_declarator') continue;
335
360
  const nameNode = child.childForFieldName('name') || child.child(0);
336
- if (nameNode && nameNode.type === 'identifier' && ctx.typeMap) {
337
- setTypeMapEntry(ctx.typeMap, nameNode.text, typeName, 0.9);
338
- }
361
+ if (nameNode?.type !== 'identifier' || !ctx.typeMap) continue;
362
+ if (typeName) setTypeMapEntry(ctx.typeMap, nameNode.text, typeName, 0.9);
339
363
  }
340
364
  }
341
365
 
@@ -262,7 +262,7 @@ function extractCudaParameters(paramListNode: TreeSitterNode | null): SubDeclara
262
262
  if (!paramListNode) return params;
263
263
  for (let i = 0; i < paramListNode.childCount; i++) {
264
264
  const param = paramListNode.child(i);
265
- if (!param || param.type !== 'parameter_declaration') continue;
265
+ if (param?.type !== 'parameter_declaration') continue;
266
266
  const nameNode = param.childForFieldName('declarator');
267
267
  if (nameNode) {
268
268
  // Reuse the field-name drill helper so function-type parameters like
@@ -282,7 +282,7 @@ function extractCudaClassFields(classNode: TreeSitterNode): SubDeclaration[] {
282
282
  if (!body) return fields;
283
283
  for (let i = 0; i < body.childCount; i++) {
284
284
  const member = body.child(i);
285
- if (!member || member.type !== 'field_declaration') continue;
285
+ if (member?.type !== 'field_declaration') continue;
286
286
  const nameNode = member.childForFieldName('declarator');
287
287
  if (!nameNode) continue;
288
288
  // Skip method declarations — a `field_declaration` whose declarator
@@ -380,7 +380,7 @@ function extractCudaEnumEntries(enumNode: TreeSitterNode): SubDeclaration[] {
380
380
  if (!body) return entries;
381
381
  for (let i = 0; i < body.childCount; i++) {
382
382
  const member = body.child(i);
383
- if (!member || member.type !== 'enumerator') continue;
383
+ if (member?.type !== 'enumerator') continue;
384
384
  const nameNode = member.childForFieldName('name');
385
385
  if (nameNode) {
386
386
  entries.push({ name: nameNode.text, kind: 'constant', line: member.startPosition.row + 1 });
@@ -122,9 +122,9 @@ function collectModuleMembers(
122
122
  ): void {
123
123
  for (let i = 0; i < doBlock.childCount; i++) {
124
124
  const child = doBlock.child(i);
125
- if (!child || child.type !== 'call') continue;
125
+ if (child?.type !== 'call') continue;
126
126
  const target = child.childForFieldName('target');
127
- if (!target || target.type !== 'identifier') continue;
127
+ if (target?.type !== 'identifier') continue;
128
128
 
129
129
  if (target.text === 'def' || target.text === 'defp') {
130
130
  const fnName = extractFunctionName(child);
@@ -184,7 +184,7 @@ function extractElixirParams(defCallNode: TreeSitterNode): SubDeclaration[] {
184
184
 
185
185
  for (let i = 0; i < args.childCount; i++) {
186
186
  const child = args.child(i);
187
- if (!child || child.type !== 'call') continue;
187
+ if (child?.type !== 'call') continue;
188
188
  const innerArgs = findChild(child, 'arguments');
189
189
  if (!innerArgs) continue;
190
190
  for (let j = 0; j < innerArgs.childCount; j++) {
@@ -277,13 +277,13 @@ function pushElixirMapValues(node: TreeSitterNode, stack: TreeSitterNode[]): voi
277
277
  const parts: TreeSitterNode[] = [];
278
278
  for (let i = 0; i < node.childCount; i++) {
279
279
  const content = node.child(i);
280
- if (!content || content.type !== 'map_content') continue;
280
+ if (content?.type !== 'map_content') continue;
281
281
  for (let j = 0; j < content.childCount; j++) {
282
282
  const kws = content.child(j);
283
- if (!kws || kws.type !== 'keywords') continue;
283
+ if (kws?.type !== 'keywords') continue;
284
284
  for (let k = 0; k < kws.childCount; k++) {
285
285
  const pair = kws.child(k);
286
- if (!pair || pair.type !== 'pair') continue;
286
+ if (pair?.type !== 'pair') continue;
287
287
  for (let p = 0; p < pair.childCount; p++) {
288
288
  const part = pair.child(p);
289
289
  if (!part || part.type === 'keyword') continue;
@@ -303,7 +303,7 @@ function handleValueDefinition(
303
303
  currentModule: string | null,
304
304
  ): void {
305
305
  const first = node.child(0);
306
- if (!first || first.type !== 'val') return;
306
+ if (first?.type !== 'val') return;
307
307
 
308
308
  const declLeft = findChild(node, 'value_declaration_left');
309
309
  if (!declLeft) return;
@@ -111,7 +111,7 @@ function extractGoReceiverType(receiver: TreeSitterNode): string | null {
111
111
  function handleGoTypeDecl(node: TreeSitterNode, ctx: ExtractorOutput): void {
112
112
  for (let i = 0; i < node.childCount; i++) {
113
113
  const spec = node.child(i);
114
- if (!spec || spec.type !== 'type_spec') continue;
114
+ if (spec?.type !== 'type_spec') continue;
115
115
  const nameNode = spec.childForFieldName('name');
116
116
  const typeNode = spec.childForFieldName('type');
117
117
  if (!nameNode || !typeNode) continue;
@@ -213,7 +213,7 @@ function extractGoImportSpec(spec: TreeSitterNode, ctx: ExtractorOutput): void {
213
213
  function handleGoConstDecl(node: TreeSitterNode, ctx: ExtractorOutput): void {
214
214
  for (let i = 0; i < node.childCount; i++) {
215
215
  const spec = node.child(i);
216
- if (!spec || spec.type !== 'const_spec') continue;
216
+ if (spec?.type !== 'const_spec') continue;
217
217
  const constName = spec.childForFieldName('name');
218
218
  if (constName) {
219
219
  ctx.definitions.push({
@@ -288,7 +288,7 @@ function inferAddressOfComposite(
288
288
  ): boolean {
289
289
  if (rhs.type !== 'unary_expression') return false;
290
290
  const operand = rhs.childForFieldName('operand');
291
- if (!operand || operand.type !== 'composite_literal') return false;
291
+ if (operand?.type !== 'composite_literal') return false;
292
292
  const typeNode = operand.childForFieldName('type');
293
293
  if (!typeNode) return false;
294
294
  const typeName = extractGoTypeName(typeNode);
@@ -409,7 +409,7 @@ function extractGoParameters(paramListNode: TreeSitterNode | null): SubDeclarati
409
409
  if (!paramListNode) return params;
410
410
  for (let i = 0; i < paramListNode.childCount; i++) {
411
411
  const param = paramListNode.child(i);
412
- if (!param || param.type !== 'parameter_declaration') continue;
412
+ if (param?.type !== 'parameter_declaration') continue;
413
413
  // A parameter_declaration may have multiple identifiers (e.g., `a, b int`)
414
414
  for (let j = 0; j < param.childCount; j++) {
415
415
  const child = param.child(j);
@@ -494,7 +494,7 @@ function extractStructFields(structTypeNode: TreeSitterNode): SubDeclaration[] {
494
494
  if (!fieldList) return fields;
495
495
  for (let i = 0; i < fieldList.childCount; i++) {
496
496
  const field = fieldList.child(i);
497
- if (!field || field.type !== 'field_declaration') continue;
497
+ if (field?.type !== 'field_declaration') continue;
498
498
  const nameNode = field.childForFieldName('name');
499
499
  if (nameNode) {
500
500
  fields.push({ name: nameNode.text, kind: 'property', line: field.startPosition.row + 1 });
@@ -123,7 +123,7 @@ function collectHaskellPatternBindings(node: TreeSitterNode, out: SubDeclaration
123
123
  case 'record':
124
124
  for (let i = 0; i < node.childCount; i++) {
125
125
  const fp = node.child(i);
126
- if (!fp || fp.type !== 'field_pattern') continue;
126
+ if (fp?.type !== 'field_pattern') continue;
127
127
  for (let j = 0; j < fp.childCount; j++) {
128
128
  const g = fp.child(j);
129
129
  if (g && g.type !== 'field_name') collectHaskellPatternBindings(g, out);
@@ -330,7 +330,7 @@ function extractClassFields(classNode: TreeSitterNode): SubDeclaration[] {
330
330
  if (!body) return fields;
331
331
  for (let i = 0; i < body.childCount; i++) {
332
332
  const member = body.child(i);
333
- if (!member || member.type !== 'field_declaration') continue;
333
+ if (member?.type !== 'field_declaration') continue;
334
334
  extractFieldDeclarators(member, fields);
335
335
  }
336
336
  return fields;
@@ -341,7 +341,7 @@ function extractFieldDeclarators(member: TreeSitterNode, fields: SubDeclaration[
341
341
  const vis = extractModifierVisibility(member);
342
342
  for (let j = 0; j < member.childCount; j++) {
343
343
  const child = member.child(j);
344
- if (!child || child.type !== 'variable_declarator') continue;
344
+ if (child?.type !== 'variable_declarator') continue;
345
345
  const nameNode = child.childForFieldName('name');
346
346
  if (nameNode) {
347
347
  fields.push({