gitnexus 1.4.6 → 1.4.8

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 (99) hide show
  1. package/README.md +22 -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.js +2 -1
  7. package/dist/cli/setup.js +78 -1
  8. package/dist/config/supported-languages.d.ts +30 -0
  9. package/dist/config/supported-languages.js +30 -0
  10. package/dist/core/embeddings/embedder.d.ts +6 -1
  11. package/dist/core/embeddings/embedder.js +65 -5
  12. package/dist/core/embeddings/embedding-pipeline.js +11 -9
  13. package/dist/core/embeddings/http-client.d.ts +31 -0
  14. package/dist/core/embeddings/http-client.js +179 -0
  15. package/dist/core/embeddings/index.d.ts +1 -0
  16. package/dist/core/embeddings/index.js +1 -0
  17. package/dist/core/embeddings/types.d.ts +1 -1
  18. package/dist/core/graph/types.d.ts +4 -3
  19. package/dist/core/ingestion/ast-helpers.d.ts +80 -0
  20. package/dist/core/ingestion/ast-helpers.js +738 -0
  21. package/dist/core/ingestion/call-analysis.d.ts +73 -0
  22. package/dist/core/ingestion/call-analysis.js +490 -0
  23. package/dist/core/ingestion/call-processor.d.ts +55 -2
  24. package/dist/core/ingestion/call-processor.js +673 -108
  25. package/dist/core/ingestion/call-routing.d.ts +23 -2
  26. package/dist/core/ingestion/call-routing.js +21 -0
  27. package/dist/core/ingestion/entry-point-scoring.js +36 -26
  28. package/dist/core/ingestion/framework-detection.d.ts +10 -2
  29. package/dist/core/ingestion/framework-detection.js +49 -12
  30. package/dist/core/ingestion/heritage-processor.js +47 -49
  31. package/dist/core/ingestion/import-processor.d.ts +1 -1
  32. package/dist/core/ingestion/import-processor.js +103 -194
  33. package/dist/core/ingestion/import-resolution.d.ts +101 -0
  34. package/dist/core/ingestion/import-resolution.js +251 -0
  35. package/dist/core/ingestion/language-config.d.ts +3 -0
  36. package/dist/core/ingestion/language-config.js +13 -0
  37. package/dist/core/ingestion/markdown-processor.d.ts +17 -0
  38. package/dist/core/ingestion/markdown-processor.js +124 -0
  39. package/dist/core/ingestion/mro-processor.js +8 -3
  40. package/dist/core/ingestion/named-binding-extraction.d.ts +9 -43
  41. package/dist/core/ingestion/named-binding-extraction.js +89 -79
  42. package/dist/core/ingestion/parsing-processor.d.ts +3 -2
  43. package/dist/core/ingestion/parsing-processor.js +27 -60
  44. package/dist/core/ingestion/pipeline.d.ts +10 -0
  45. package/dist/core/ingestion/pipeline.js +425 -4
  46. package/dist/core/ingestion/resolution-context.d.ts +5 -0
  47. package/dist/core/ingestion/resolution-context.js +7 -4
  48. package/dist/core/ingestion/resolvers/index.d.ts +1 -1
  49. package/dist/core/ingestion/resolvers/index.js +1 -1
  50. package/dist/core/ingestion/resolvers/jvm.d.ts +2 -1
  51. package/dist/core/ingestion/resolvers/jvm.js +25 -9
  52. package/dist/core/ingestion/resolvers/php.d.ts +14 -0
  53. package/dist/core/ingestion/resolvers/php.js +43 -3
  54. package/dist/core/ingestion/resolvers/utils.d.ts +5 -0
  55. package/dist/core/ingestion/resolvers/utils.js +16 -0
  56. package/dist/core/ingestion/symbol-table.d.ts +29 -3
  57. package/dist/core/ingestion/symbol-table.js +42 -9
  58. package/dist/core/ingestion/tree-sitter-queries.d.ts +12 -12
  59. package/dist/core/ingestion/tree-sitter-queries.js +243 -2
  60. package/dist/core/ingestion/type-env.d.ts +28 -1
  61. package/dist/core/ingestion/type-env.js +451 -72
  62. package/dist/core/ingestion/type-extractors/c-cpp.d.ts +5 -0
  63. package/dist/core/ingestion/type-extractors/c-cpp.js +146 -2
  64. package/dist/core/ingestion/type-extractors/csharp.js +189 -16
  65. package/dist/core/ingestion/type-extractors/go.js +45 -0
  66. package/dist/core/ingestion/type-extractors/index.d.ts +1 -1
  67. package/dist/core/ingestion/type-extractors/index.js +1 -1
  68. package/dist/core/ingestion/type-extractors/jvm.js +244 -69
  69. package/dist/core/ingestion/type-extractors/php.js +31 -4
  70. package/dist/core/ingestion/type-extractors/python.js +89 -17
  71. package/dist/core/ingestion/type-extractors/ruby.js +17 -2
  72. package/dist/core/ingestion/type-extractors/rust.js +72 -4
  73. package/dist/core/ingestion/type-extractors/shared.d.ts +12 -2
  74. package/dist/core/ingestion/type-extractors/shared.js +115 -13
  75. package/dist/core/ingestion/type-extractors/swift.js +7 -6
  76. package/dist/core/ingestion/type-extractors/types.d.ts +54 -11
  77. package/dist/core/ingestion/type-extractors/typescript.js +171 -9
  78. package/dist/core/ingestion/utils.d.ts +2 -95
  79. package/dist/core/ingestion/utils.js +3 -892
  80. package/dist/core/ingestion/workers/parse-worker.d.ts +36 -11
  81. package/dist/core/ingestion/workers/parse-worker.js +116 -95
  82. package/dist/core/lbug/csv-generator.js +18 -1
  83. package/dist/core/lbug/lbug-adapter.d.ts +12 -0
  84. package/dist/core/lbug/lbug-adapter.js +71 -4
  85. package/dist/core/lbug/schema.d.ts +6 -4
  86. package/dist/core/lbug/schema.js +27 -3
  87. package/dist/mcp/core/embedder.js +11 -3
  88. package/dist/mcp/core/lbug-adapter.d.ts +22 -0
  89. package/dist/mcp/core/lbug-adapter.js +178 -23
  90. package/dist/mcp/local/local-backend.d.ts +22 -0
  91. package/dist/mcp/local/local-backend.js +136 -32
  92. package/dist/mcp/resources.js +13 -0
  93. package/dist/mcp/server.js +26 -4
  94. package/dist/mcp/tools.js +17 -7
  95. package/dist/server/api.d.ts +19 -1
  96. package/dist/server/api.js +66 -6
  97. package/dist/storage/git.d.ts +12 -0
  98. package/dist/storage/git.js +21 -0
  99. package/package.json +12 -4
@@ -1,2 +1,7 @@
1
+ import type { SyntaxNode } from '../utils.js';
1
2
  import type { LanguageTypeConfig } from './types.js';
3
+ /** Extract the first type name from a template_argument_list child.
4
+ * Unwraps type_descriptor wrappers common in tree-sitter-cpp ASTs.
5
+ * Returns undefined if no template arguments or no type found. */
6
+ export declare const extractFirstTemplateTypeArg: (parentNode: SyntaxNode) => string | undefined;
2
7
  export declare const typeConfig: LanguageTypeConfig;
@@ -2,6 +2,28 @@ import { extractSimpleTypeName, extractVarName, resolveIterableElementType, meth
2
2
  const DECLARATION_NODE_TYPES = new Set([
3
3
  'declaration',
4
4
  ]);
5
+ /** Smart pointer factory function names that create a typed object. */
6
+ const SMART_PTR_FACTORIES = new Set([
7
+ 'make_shared', 'make_unique', 'make_shared_for_overwrite',
8
+ ]);
9
+ /** Smart pointer wrapper type names. When the declared type is a smart pointer,
10
+ * the inner template type is extracted for virtual dispatch comparison. */
11
+ const SMART_PTR_WRAPPERS = new Set(['shared_ptr', 'unique_ptr', 'weak_ptr']);
12
+ /** Extract the first type name from a template_argument_list child.
13
+ * Unwraps type_descriptor wrappers common in tree-sitter-cpp ASTs.
14
+ * Returns undefined if no template arguments or no type found. */
15
+ export const extractFirstTemplateTypeArg = (parentNode) => {
16
+ const templateArgs = parentNode.children.find((c) => c.type === 'template_argument_list');
17
+ if (!templateArgs?.firstNamedChild)
18
+ return undefined;
19
+ let argNode = templateArgs.firstNamedChild;
20
+ if (argNode.type === 'type_descriptor') {
21
+ const inner = argNode.childForFieldName('type');
22
+ if (inner)
23
+ argNode = inner;
24
+ }
25
+ return extractSimpleTypeName(argNode) ?? undefined;
26
+ };
5
27
  /** C++: Type x = ...; Type* x; Type& x; */
6
28
  const extractDeclaration = (node, env) => {
7
29
  const typeNode = node.childForFieldName('type');
@@ -89,6 +111,29 @@ const extractInitializer = (node, env, classNames) => {
89
111
  if (text && classNames.has(text))
90
112
  env.set(varName, text);
91
113
  }
114
+ else {
115
+ // auto x = std::make_shared<Dog>() — smart pointer factory via template_function.
116
+ // AST: call_expression > function: qualified_identifier > template_function
117
+ // or: call_expression > function: template_function (unqualified)
118
+ const templateFunc = func.type === 'template_function'
119
+ ? func
120
+ : (func.type === 'qualified_identifier' || func.type === 'scoped_identifier')
121
+ ? func.namedChildren.find((c) => c.type === 'template_function') ?? null
122
+ : null;
123
+ if (templateFunc) {
124
+ const nameNode = templateFunc.firstNamedChild;
125
+ if (nameNode) {
126
+ const funcName = (nameNode.type === 'qualified_identifier' || nameNode.type === 'scoped_identifier')
127
+ ? nameNode.lastNamedChild?.text ?? ''
128
+ : nameNode.text;
129
+ if (SMART_PTR_FACTORIES.has(funcName)) {
130
+ const typeName = extractFirstTemplateTypeArg(templateFunc);
131
+ if (typeName)
132
+ env.set(varName, typeName);
133
+ }
134
+ }
135
+ }
136
+ }
92
137
  return;
93
138
  }
94
139
  // auto x = User{} — compound_literal_expression (brace initialization)
@@ -186,7 +231,7 @@ const extractPendingAssignment = (node, scopeEnv) => {
186
231
  if (!declarator || declarator.type !== 'init_declarator')
187
232
  return undefined;
188
233
  const value = declarator.childForFieldName('value');
189
- if (!value || value.type !== 'identifier')
234
+ if (!value)
190
235
  return undefined;
191
236
  const nameNode = declarator.childForFieldName('declarator');
192
237
  if (!nameNode)
@@ -198,7 +243,32 @@ const extractPendingAssignment = (node, scopeEnv) => {
198
243
  const lhs = extractVarName(finalName);
199
244
  if (!lhs || scopeEnv.has(lhs))
200
245
  return undefined;
201
- return { kind: 'copy', lhs, rhs: value.text };
246
+ if (value.type === 'identifier')
247
+ return { kind: 'copy', lhs, rhs: value.text };
248
+ // field_expression RHS → fieldAccess (a.field)
249
+ if (value.type === 'field_expression') {
250
+ const obj = value.firstNamedChild;
251
+ const field = value.lastNamedChild;
252
+ if (obj?.type === 'identifier' && field?.type === 'field_identifier') {
253
+ return { kind: 'fieldAccess', lhs, receiver: obj.text, field: field.text };
254
+ }
255
+ }
256
+ // call_expression RHS
257
+ if (value.type === 'call_expression') {
258
+ const funcNode = value.childForFieldName('function');
259
+ if (funcNode?.type === 'identifier') {
260
+ return { kind: 'callResult', lhs, callee: funcNode.text };
261
+ }
262
+ // method call with receiver: call_expression → function: field_expression
263
+ if (funcNode?.type === 'field_expression') {
264
+ const obj = funcNode.firstNamedChild;
265
+ const field = funcNode.lastNamedChild;
266
+ if (obj?.type === 'identifier' && field?.type === 'field_identifier') {
267
+ return { kind: 'methodCallResult', lhs, receiver: obj.text, method: field.text };
268
+ }
269
+ }
270
+ }
271
+ return undefined;
202
272
  };
203
273
  // --- For-loop Tier 1c ---
204
274
  const FOR_LOOP_NODE_TYPES = new Set(['for_range_loop']);
@@ -373,6 +443,77 @@ const extractForLoopBinding = (node, { scopeEnv, declarationTypeNodes, scope })
373
443
  if (elementType)
374
444
  scopeEnv.set(varName, elementType);
375
445
  };
446
+ /** Infer the type of a literal AST node for C++ overload disambiguation. */
447
+ const inferLiteralType = (node) => {
448
+ switch (node.type) {
449
+ case 'number_literal': {
450
+ const t = node.text;
451
+ // Float suffixes
452
+ if (t.endsWith('f') || t.endsWith('F'))
453
+ return 'float';
454
+ if (t.includes('.') || t.includes('e') || t.includes('E'))
455
+ return 'double';
456
+ // Long suffix
457
+ if (t.endsWith('L') || t.endsWith('l') || t.endsWith('LL') || t.endsWith('ll'))
458
+ return 'long';
459
+ return 'int';
460
+ }
461
+ case 'string_literal':
462
+ case 'raw_string_literal':
463
+ case 'concatenated_string':
464
+ return 'string';
465
+ case 'char_literal':
466
+ return 'char';
467
+ case 'true':
468
+ case 'false':
469
+ return 'bool';
470
+ case 'null':
471
+ case 'nullptr':
472
+ return 'null';
473
+ default:
474
+ return undefined;
475
+ }
476
+ };
477
+ /** C++: detect constructor type from smart pointer factory calls (make_shared<Dog>()).
478
+ * Extracts the template type argument as the constructor type for virtual dispatch. */
479
+ const detectCppConstructorType = (node, classNames) => {
480
+ // Navigate to the initializer value in the declaration
481
+ const declarator = node.childForFieldName('declarator');
482
+ const initDecl = declarator?.type === 'init_declarator' ? declarator : undefined;
483
+ if (!initDecl)
484
+ return undefined;
485
+ const value = initDecl.childForFieldName('value');
486
+ if (!value || value.type !== 'call_expression')
487
+ return undefined;
488
+ // Check for template_function pattern: make_shared<Dog>()
489
+ const func = value.childForFieldName('function');
490
+ if (!func || func.type !== 'template_function')
491
+ return undefined;
492
+ // Extract function name (possibly qualified: std::make_shared)
493
+ const nameNode = func.firstNamedChild;
494
+ if (!nameNode)
495
+ return undefined;
496
+ let funcName;
497
+ if (nameNode.type === 'qualified_identifier' || nameNode.type === 'scoped_identifier') {
498
+ funcName = nameNode.lastNamedChild?.text ?? '';
499
+ }
500
+ else {
501
+ funcName = nameNode.text;
502
+ }
503
+ if (!SMART_PTR_FACTORIES.has(funcName))
504
+ return undefined;
505
+ // Extract template type argument
506
+ return extractFirstTemplateTypeArg(func);
507
+ };
508
+ /** Unwrap a C++ smart pointer declared type to its inner template type.
509
+ * E.g., shared_ptr<Animal> → Animal. Returns the original name if not a smart pointer. */
510
+ const unwrapCppDeclaredType = (declaredType, typeNode) => {
511
+ if (!SMART_PTR_WRAPPERS.has(declaredType))
512
+ return declaredType;
513
+ if (typeNode.type !== 'template_type')
514
+ return declaredType;
515
+ return extractFirstTemplateTypeArg(typeNode) ?? declaredType;
516
+ };
376
517
  export const typeConfig = {
377
518
  declarationNodeTypes: DECLARATION_NODE_TYPES,
378
519
  forLoopNodeTypes: FOR_LOOP_NODE_TYPES,
@@ -382,4 +523,7 @@ export const typeConfig = {
382
523
  scanConstructorBinding,
383
524
  extractForLoopBinding,
384
525
  extractPendingAssignment,
526
+ inferLiteralType,
527
+ detectConstructorType: detectCppConstructorType,
528
+ unwrapDeclaredType: unwrapCppDeclaredType,
385
529
  };
@@ -1,4 +1,5 @@
1
- import { extractSimpleTypeName, extractVarName, findChildByType, unwrapAwait, resolveIterableElementType, methodToTypeArgPosition, extractElementTypeFromString } from './shared.js';
1
+ import { extractSimpleTypeName, extractVarName, unwrapAwait, resolveIterableElementType, methodToTypeArgPosition, extractElementTypeFromString } from './shared.js';
2
+ import { findChild } from '../resolvers/utils.js';
2
3
  /** Known container property accessors that operate on the container itself (e.g., dict.Keys, dict.Values) */
3
4
  const KNOWN_CONTAINER_PROPS = new Set(['Keys', 'Values']);
4
5
  const DECLARATION_NODE_TYPES = new Set([
@@ -43,8 +44,8 @@ const extractDeclaration = (node, env) => {
43
44
  // tree-sitter-c-sharp may put object_creation_expression as direct child
44
45
  // or inside equals_value_clause depending on grammar version
45
46
  if (declarators.length === 1) {
46
- const initializer = findChildByType(declarators[0], 'object_creation_expression')
47
- ?? findChildByType(declarators[0], 'equals_value_clause')?.firstNamedChild;
47
+ const initializer = findChild(declarators[0], 'object_creation_expression')
48
+ ?? findChild(declarators[0], 'equals_value_clause')?.firstNamedChild;
48
49
  if (initializer?.type === 'object_creation_expression') {
49
50
  const ctorType = initializer.childForFieldName('type');
50
51
  if (ctorType)
@@ -159,7 +160,7 @@ const extractCSharpElementTypeFromTypeNode = (typeNode, pos = 'last', depth = 0)
159
160
  // generic_name: List<User>, IEnumerable<User>, Dictionary<string, User>
160
161
  // C# uses generic_name (not generic_type)
161
162
  if (typeNode.type === 'generic_name') {
162
- const argList = findChildByType(typeNode, 'type_argument_list');
163
+ const argList = findChild(typeNode, 'type_argument_list');
163
164
  if (argList && argList.namedChildCount >= 1) {
164
165
  if (pos === 'first') {
165
166
  const firstArg = argList.namedChild(0);
@@ -307,21 +308,89 @@ const extractForLoopBinding = (node, { scopeEnv, declarationTypeNodes, scope, re
307
308
  * declaration_pattern, or when the type/name cannot be extracted.
308
309
  * No scopeEnv lookup is needed — the pattern explicitly declares the new variable's type.
309
310
  */
310
- const extractPatternBinding = (node) => {
311
+ /**
312
+ * Find the if-body (consequence) block for a C# null-check.
313
+ * Walks up from the expression to find the enclosing if_statement,
314
+ * then returns its first block child (the truthy branch body).
315
+ */
316
+ const findCSharpIfConsequenceBlock = (expr) => {
317
+ let current = expr.parent;
318
+ while (current) {
319
+ if (current.type === 'if_statement') {
320
+ // C# if_statement consequence is the 'consequence' field or first block child
321
+ const consequence = current.childForFieldName('consequence');
322
+ if (consequence)
323
+ return consequence;
324
+ for (let i = 0; i < current.childCount; i++) {
325
+ const child = current.child(i);
326
+ if (child?.type === 'block')
327
+ return child;
328
+ }
329
+ return undefined;
330
+ }
331
+ if (current.type === 'block' || current.type === 'method_declaration'
332
+ || current.type === 'constructor_declaration' || current.type === 'local_function_statement'
333
+ || current.type === 'lambda_expression')
334
+ return undefined;
335
+ current = current.parent;
336
+ }
337
+ return undefined;
338
+ };
339
+ /** Check if a C# declaration type node represents a nullable type.
340
+ * Checks for nullable_type AST node or '?' in the type text (e.g., User?). */
341
+ const isCSharpNullableDecl = (declTypeNode) => {
342
+ if (declTypeNode.type === 'nullable_type')
343
+ return true;
344
+ return declTypeNode.text.includes('?');
345
+ };
346
+ const extractPatternBinding = (node, scopeEnv, declarationTypeNodes, scope) => {
311
347
  // is_pattern_expression: `obj is User user` — has a declaration_pattern child
348
+ // Also handles `x is not null` for null-check narrowing
312
349
  if (node.type === 'is_pattern_expression') {
313
350
  const pattern = node.childForFieldName('pattern');
314
- if (pattern?.type !== 'declaration_pattern' && pattern?.type !== 'recursive_pattern')
351
+ if (!pattern)
315
352
  return undefined;
316
- const typeNode = pattern.childForFieldName('type');
317
- const nameNode = pattern.childForFieldName('name');
318
- if (!typeNode || !nameNode)
319
- return undefined;
320
- const typeName = extractSimpleTypeName(typeNode);
321
- const varName = extractVarName(nameNode);
322
- if (!typeName || !varName)
323
- return undefined;
324
- return { varName, typeName };
353
+ // Standard type pattern: `obj is User user`
354
+ if (pattern.type === 'declaration_pattern' || pattern.type === 'recursive_pattern') {
355
+ const typeNode = pattern.childForFieldName('type');
356
+ const nameNode = pattern.childForFieldName('name');
357
+ if (!typeNode || !nameNode)
358
+ return undefined;
359
+ const typeName = extractSimpleTypeName(typeNode);
360
+ const varName = extractVarName(nameNode);
361
+ if (!typeName || !varName)
362
+ return undefined;
363
+ return { varName, typeName };
364
+ }
365
+ // Null-check: `x is not null` — negated_pattern > constant_pattern > null_literal
366
+ if (pattern.type === 'negated_pattern') {
367
+ const inner = pattern.firstNamedChild;
368
+ if (inner?.type === 'constant_pattern') {
369
+ const literal = inner.firstNamedChild ?? inner.firstChild;
370
+ if (literal?.type === 'null_literal' || literal?.text === 'null') {
371
+ const expr = node.childForFieldName('expression');
372
+ if (!expr || expr.type !== 'identifier')
373
+ return undefined;
374
+ const varName = expr.text;
375
+ const resolvedType = scopeEnv.get(varName);
376
+ if (!resolvedType)
377
+ return undefined;
378
+ // Verify the original declaration was nullable
379
+ const declTypeNode = declarationTypeNodes.get(`${scope}\0${varName}`);
380
+ if (!declTypeNode || !isCSharpNullableDecl(declTypeNode))
381
+ return undefined;
382
+ const ifBody = findCSharpIfConsequenceBlock(node);
383
+ if (!ifBody)
384
+ return undefined;
385
+ return {
386
+ varName,
387
+ typeName: resolvedType,
388
+ narrowingRange: { startIndex: ifBody.startIndex, endIndex: ifBody.endIndex },
389
+ };
390
+ }
391
+ }
392
+ }
393
+ return undefined;
325
394
  }
326
395
  // declaration_pattern / recursive_pattern: standalone in switch statements and switch expressions
327
396
  // `case User u:` or `User u =>` or `User { Name: "Alice" } u =>`
@@ -337,6 +406,41 @@ const extractPatternBinding = (node) => {
337
406
  return undefined;
338
407
  return { varName, typeName };
339
408
  }
409
+ // Null-check: `x != null` — binary_expression with != operator
410
+ if (node.type === 'binary_expression') {
411
+ const op = node.children.find(c => !c.isNamed && c.text === '!=');
412
+ if (!op)
413
+ return undefined;
414
+ const left = node.namedChild(0);
415
+ const right = node.namedChild(1);
416
+ if (!left || !right)
417
+ return undefined;
418
+ let varNode;
419
+ if (left.type === 'identifier' && (right.type === 'null_literal' || right.text === 'null')) {
420
+ varNode = left;
421
+ }
422
+ else if (right.type === 'identifier' && (left.type === 'null_literal' || left.text === 'null')) {
423
+ varNode = right;
424
+ }
425
+ if (!varNode)
426
+ return undefined;
427
+ const varName = varNode.text;
428
+ const resolvedType = scopeEnv.get(varName);
429
+ if (!resolvedType)
430
+ return undefined;
431
+ // Verify the original declaration was nullable
432
+ const declTypeNode = declarationTypeNodes.get(`${scope}\0${varName}`);
433
+ if (!declTypeNode || !isCSharpNullableDecl(declTypeNode))
434
+ return undefined;
435
+ const ifBody = findCSharpIfConsequenceBlock(node);
436
+ if (!ifBody)
437
+ return undefined;
438
+ return {
439
+ varName,
440
+ typeName: resolvedType,
441
+ narrowingRange: { startIndex: ifBody.startIndex, endIndex: ifBody.endIndex },
442
+ };
443
+ }
340
444
  return undefined;
341
445
  };
342
446
  /** C#: var alias = u → variable_declarator with name + equals_value_clause.
@@ -367,17 +471,86 @@ const extractPendingAssignment = (node, scopeEnv) => {
367
471
  if (valueNode && valueNode !== nameNode && (valueNode.type === 'identifier' || valueNode.type === 'simple_identifier')) {
368
472
  return { kind: 'copy', lhs, rhs: valueNode.text };
369
473
  }
474
+ // member_access_expression RHS → fieldAccess (a.Field)
475
+ if (valueNode?.type === 'member_access_expression') {
476
+ const expr = valueNode.childForFieldName('expression');
477
+ const name = valueNode.childForFieldName('name');
478
+ if (expr?.type === 'identifier' && name?.type === 'identifier') {
479
+ return { kind: 'fieldAccess', lhs, receiver: expr.text, field: name.text };
480
+ }
481
+ }
482
+ // invocation_expression RHS
483
+ if (valueNode?.type === 'invocation_expression') {
484
+ const funcNode = valueNode.firstNamedChild;
485
+ if (funcNode?.type === 'identifier_name' || funcNode?.type === 'identifier') {
486
+ return { kind: 'callResult', lhs, callee: funcNode.text };
487
+ }
488
+ // method call with receiver → methodCallResult: a.GetC()
489
+ if (funcNode?.type === 'member_access_expression') {
490
+ const expr = funcNode.childForFieldName('expression');
491
+ const name = funcNode.childForFieldName('name');
492
+ if (expr?.type === 'identifier' && name?.type === 'identifier') {
493
+ return { kind: 'methodCallResult', lhs, receiver: expr.text, method: name.text };
494
+ }
495
+ }
496
+ }
497
+ // await_expression → unwrap and check inner
498
+ if (valueNode?.type === 'await_expression') {
499
+ const inner = valueNode.firstNamedChild;
500
+ if (inner?.type === 'invocation_expression') {
501
+ const funcNode = inner.firstNamedChild;
502
+ if (funcNode?.type === 'identifier_name' || funcNode?.type === 'identifier') {
503
+ return { kind: 'callResult', lhs, callee: funcNode.text };
504
+ }
505
+ if (funcNode?.type === 'member_access_expression') {
506
+ const expr = funcNode.childForFieldName('expression');
507
+ const name = funcNode.childForFieldName('name');
508
+ if (expr?.type === 'identifier' && name?.type === 'identifier') {
509
+ return { kind: 'methodCallResult', lhs, receiver: expr.text, method: name.text };
510
+ }
511
+ }
512
+ }
513
+ }
370
514
  }
371
515
  return undefined;
372
516
  };
517
+ /** Infer the type of a literal AST node for C# overload disambiguation. */
518
+ const inferLiteralType = (node) => {
519
+ switch (node.type) {
520
+ case 'integer_literal':
521
+ if (node.text.endsWith('L') || node.text.endsWith('l'))
522
+ return 'long';
523
+ return 'int';
524
+ case 'real_literal':
525
+ if (node.text.endsWith('f') || node.text.endsWith('F'))
526
+ return 'float';
527
+ if (node.text.endsWith('m') || node.text.endsWith('M'))
528
+ return 'decimal';
529
+ return 'double';
530
+ case 'string_literal':
531
+ case 'verbatim_string_literal':
532
+ case 'raw_string_literal':
533
+ case 'interpolated_string_expression':
534
+ return 'string';
535
+ case 'character_literal':
536
+ return 'char';
537
+ case 'boolean_literal':
538
+ return 'bool';
539
+ case 'null_literal':
540
+ return 'null';
541
+ default:
542
+ return undefined;
543
+ }
544
+ };
373
545
  export const typeConfig = {
374
546
  declarationNodeTypes: DECLARATION_NODE_TYPES,
375
547
  forLoopNodeTypes: FOR_LOOP_NODE_TYPES,
376
- patternBindingNodeTypes: new Set(['is_pattern_expression', 'declaration_pattern', 'recursive_pattern']),
548
+ patternBindingNodeTypes: new Set(['is_pattern_expression', 'declaration_pattern', 'recursive_pattern', 'binary_expression']),
377
549
  extractDeclaration,
378
550
  extractParameter,
379
551
  scanConstructorBinding,
380
552
  extractForLoopBinding,
381
553
  extractPendingAssignment,
382
554
  extractPatternBinding,
555
+ inferLiteralType,
383
556
  };
@@ -419,6 +419,29 @@ const extractPendingAssignment = (node, scopeEnv) => {
419
419
  return undefined;
420
420
  if (rhsNode.type === 'identifier')
421
421
  return { kind: 'copy', lhs, rhs: rhsNode.text };
422
+ // selector_expression RHS → fieldAccess (a.field)
423
+ if (rhsNode.type === 'selector_expression') {
424
+ const operand = rhsNode.childForFieldName('operand');
425
+ const field = rhsNode.childForFieldName('field');
426
+ if (operand?.type === 'identifier' && field) {
427
+ return { kind: 'fieldAccess', lhs, receiver: operand.text, field: field.text };
428
+ }
429
+ }
430
+ // call_expression RHS
431
+ if (rhsNode.type === 'call_expression') {
432
+ const funcNode = rhsNode.childForFieldName('function');
433
+ if (funcNode?.type === 'identifier') {
434
+ return { kind: 'callResult', lhs, callee: funcNode.text };
435
+ }
436
+ // method call with receiver: call_expression → function: selector_expression
437
+ if (funcNode?.type === 'selector_expression') {
438
+ const operand = funcNode.childForFieldName('operand');
439
+ const field = funcNode.childForFieldName('field');
440
+ if (operand?.type === 'identifier' && field) {
441
+ return { kind: 'methodCallResult', lhs, receiver: operand.text, method: field.text };
442
+ }
443
+ }
444
+ }
422
445
  return undefined;
423
446
  }
424
447
  if (node.type === 'var_spec' || node.type === 'var_declaration') {
@@ -452,6 +475,28 @@ const extractPendingAssignment = (node, scopeEnv) => {
452
475
  const rhsNode = exprList?.firstNamedChild;
453
476
  if (rhsNode?.type === 'identifier')
454
477
  return { kind: 'copy', lhs, rhs: rhsNode.text };
478
+ // selector_expression RHS → fieldAccess
479
+ if (rhsNode?.type === 'selector_expression') {
480
+ const operand = rhsNode.childForFieldName('operand');
481
+ const field = rhsNode.childForFieldName('field');
482
+ if (operand?.type === 'identifier' && field) {
483
+ return { kind: 'fieldAccess', lhs, receiver: operand.text, field: field.text };
484
+ }
485
+ }
486
+ // call_expression RHS
487
+ if (rhsNode?.type === 'call_expression') {
488
+ const funcNode = rhsNode.childForFieldName('function');
489
+ if (funcNode?.type === 'identifier') {
490
+ return { kind: 'callResult', lhs, callee: funcNode.text };
491
+ }
492
+ if (funcNode?.type === 'selector_expression') {
493
+ const operand = funcNode.childForFieldName('operand');
494
+ const field = funcNode.childForFieldName('field');
495
+ if (operand?.type === 'identifier' && field) {
496
+ return { kind: 'methodCallResult', lhs, receiver: operand.text, method: field.text };
497
+ }
498
+ }
499
+ }
455
500
  }
456
501
  }
457
502
  return undefined;
@@ -19,4 +19,4 @@ export declare const typeConfigs: {
19
19
  ruby: LanguageTypeConfig;
20
20
  };
21
21
  export type { LanguageTypeConfig, TypeBindingExtractor, ParameterExtractor, ConstructorBindingScanner, ForLoopExtractor, PendingAssignmentExtractor, PatternBindingExtractor, } from './types.js';
22
- export { TYPED_PARAMETER_TYPES, extractSimpleTypeName, extractGenericTypeArgs, extractVarName, findChildByType, extractRubyConstructorAssignment } from './shared.js';
22
+ export { TYPED_PARAMETER_TYPES, extractSimpleTypeName, extractGenericTypeArgs, extractVarName, extractRubyConstructorAssignment } from './shared.js';
@@ -28,4 +28,4 @@ export const typeConfigs = {
28
28
  [SupportedLanguages.PHP]: phpConfig,
29
29
  [SupportedLanguages.Ruby]: rubyConfig,
30
30
  };
31
- export { TYPED_PARAMETER_TYPES, extractSimpleTypeName, extractGenericTypeArgs, extractVarName, findChildByType, extractRubyConstructorAssignment } from './shared.js';
31
+ export { TYPED_PARAMETER_TYPES, extractSimpleTypeName, extractGenericTypeArgs, extractVarName, extractRubyConstructorAssignment } from './shared.js';