ucn 5.3.3 → 5.3.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/languages/go.js CHANGED
@@ -5,6 +5,9 @@
5
5
  * struct/interface types, and const/var declarations.
6
6
  */
7
7
 
8
+ const { ReceiverTypeMap, typeOrigin } = require('./type-evidence');
9
+
10
+
8
11
  const {
9
12
  traverseTree,
10
13
  traverseTreeCached,
@@ -761,7 +764,7 @@ function findCallsInCode(code, parser, options = {}) {
761
764
  // unless a lexical declaration shadows the name (`var v *Viper`;
762
765
  // `func Set(...) { v.Set(...) }`). They are stored separately so local
763
766
  // scope restoration cannot erase them.
764
- const packageTypes = new Map();
767
+ const packageTypes = new ReceiverTypeMap();
765
768
  const packageTypeQualifiers = new Map();
766
769
  // Names whose scope type is a New*-prefix GUESS (fix #266) — per scope
767
770
  const scopeGuesses = new Map();
@@ -835,7 +838,7 @@ function findCallsInCode(code, parser, options = {}) {
835
838
  // Also returns funcParamNames: parameter names with function types (func(...) ...)
836
839
  // so calls to them can be skipped (they're local parameter calls, not global function calls).
837
840
  const buildScopeTypeMap = (node) => {
838
- const typeMap = new Map();
841
+ const typeMap = new ReceiverTypeMap();
839
842
  const typeQualifierMap = new Map();
840
843
  const funcParamNames = new Set();
841
844
  const containerMap = new Map();
@@ -851,7 +854,7 @@ function findCallsInCode(code, parser, options = {}) {
851
854
  const typeNode = param.childForFieldName('type');
852
855
  const typeName = extractTypeName(typeNode);
853
856
  if (nameNode && typeName) {
854
- typeMap.set(nameNode.text, typeName);
857
+ typeMap.set(nameNode.text, typeName, 'annotation', param);
855
858
  const qualifier = extractTypeQualifier(typeNode);
856
859
  if (qualifier) typeQualifierMap.set(nameNode.text, qualifier);
857
860
  }
@@ -888,7 +891,7 @@ function findCallsInCode(code, parser, options = {}) {
888
891
  if (typeName) {
889
892
  const qualifier = extractTypeQualifier(typeNode);
890
893
  for (const nn of nameNodes) {
891
- typeMap.set(nn.text, typeName);
894
+ typeMap.set(nn.text, typeName, 'annotation', node);
892
895
  if (qualifier) typeQualifierMap.set(nn.text, qualifier);
893
896
  }
894
897
  }
@@ -956,17 +959,17 @@ function findCallsInCode(code, parser, options = {}) {
956
959
  };
957
960
 
958
961
  // Look up variable type from scope chain
959
- const getReceiverType = (varName, refNode) => {
962
+ const getReceiverType = (varName, refNode, evidence = false) => {
960
963
  const bindingScope = refNode
961
964
  ? lexicalBindingScopeStart(refNode, varName) : null;
962
965
  for (let i = functionStack.length - 1; i >= 0; i--) {
963
966
  const scopeStart = functionStack[i].startLine;
964
967
  const typeMap = scopeTypes.get(scopeStart);
965
- if (typeMap?.has(varName)) return typeMap.get(varName);
968
+ if (typeMap?.has(varName)) return evidence ? typeMap.fields(varName) : typeMap.get(varName);
966
969
  if (bindingScope === scopeStart) return undefined;
967
970
  }
968
971
  return refNode && isShadowedByLocal(refNode, varName)
969
- ? undefined : packageTypes.get(varName);
972
+ ? undefined : (evidence ? packageTypes.fields(varName) : packageTypes.get(varName));
970
973
  };
971
974
  const getReceiverTypeQualifier = (varName, refNode) => {
972
975
  const bindingScope = refNode
@@ -1239,6 +1242,82 @@ function findCallsInCode(code, parser, options = {}) {
1239
1242
  const isShadowedByLocal = (refNode, name) =>
1240
1243
  lexicalBindingScopeStart(refNode, name) != null;
1241
1244
 
1245
+ // Go package declarations are in scope throughout the file, including
1246
+ // function bodies preceding the declaration. Locals still enter in order.
1247
+ const recordVarDeclaration = (node, scopeKey) => {
1248
+ const varTypeMap = scopeKey == null ? packageTypes : scopeTypes.get(scopeKey);
1249
+ const varQualifierMap = scopeKey == null
1250
+ ? packageTypeQualifiers : scopeTypeQualifiers.get(scopeKey);
1251
+ if (varTypeMap) {
1252
+ const varContainerMap = scopeKey == null
1253
+ ? packageContainerTypes : scopeContainerTypes.get(scopeKey);
1254
+ const recordSpec = (spec) => {
1255
+ if (spec.type !== 'var_spec') return;
1256
+ const declaredType = spec.childForFieldName('type');
1257
+ // Container-typed declaration (fix #300): `var routes
1258
+ // []*Route` feeds range-element typing.
1259
+ if (declaredType && CONTAINER_TYPE_NODES.has(declaredType.type) &&
1260
+ varContainerMap) {
1261
+ for (let j = 0; j < spec.namedChildCount; j++) {
1262
+ const id = spec.namedChild(j);
1263
+ if (id.type === 'identifier') {
1264
+ varContainerMap.set(id.text, declaredType.text);
1265
+ }
1266
+ }
1267
+ return;
1268
+ }
1269
+ let typeName = extractTypeName(spec.childForFieldName('type'));
1270
+ let qualifier = typeName
1271
+ ? extractTypeQualifier(spec.childForFieldName('type')) : null;
1272
+ let origin = declaredType;
1273
+ let source = 'annotation';
1274
+ if (!typeName) {
1275
+ // var cstDialer = Dialer{...} — the initializer's
1276
+ // composite-literal type IS the variable's type
1277
+ // (fix #298, websocket-measured: `d := cstDialer`
1278
+ // copies then call DialContext). Single-value specs
1279
+ // only; multi-value pairing stays untyped.
1280
+ const valueNode = spec.childForFieldName('value');
1281
+ let init = valueNode?.type === 'expression_list' &&
1282
+ valueNode.namedChildCount === 1
1283
+ ? valueNode.namedChild(0) : null;
1284
+ if (init?.type === 'unary_expression') {
1285
+ init = init.namedChildCount > 0 ? init.namedChild(0) : null;
1286
+ }
1287
+ if (init?.type === 'composite_literal') {
1288
+ const tn = init.childForFieldName('type');
1289
+ typeName = extractTypeName(tn);
1290
+ qualifier = extractTypeQualifier(tn);
1291
+ origin = init;
1292
+ source = 'constructor';
1293
+ }
1294
+ if (!typeName) return;
1295
+ }
1296
+ for (let j = 0; j < spec.namedChildCount; j++) {
1297
+ const id = spec.namedChild(j);
1298
+ if (id.type === 'identifier') {
1299
+ varTypeMap.set(id.text, typeName, source, origin);
1300
+ if (qualifier) varQualifierMap?.set(id.text, qualifier);
1301
+ else varQualifierMap?.delete(id.text);
1302
+ if (scopeKey != null) {
1303
+ scopeGuesses.get(scopeKey)?.delete(id.text);
1304
+ }
1305
+ }
1306
+ }
1307
+ };
1308
+ for (let i = 0; i < node.namedChildCount; i++) {
1309
+ const c = node.namedChild(i);
1310
+ if (c.type === 'var_spec') recordSpec(c);
1311
+ else if (c.type === 'var_spec_list') {
1312
+ for (let j = 0; j < c.namedChildCount; j++) recordSpec(c.namedChild(j));
1313
+ }
1314
+ }
1315
+ }
1316
+ };
1317
+ for (const declaration of tree.rootNode.namedChildren) {
1318
+ if (declaration.type === 'var_declaration') recordVarDeclaration(declaration, null);
1319
+ }
1320
+
1242
1321
  traverseTree(tree.rootNode, (node) => {
1243
1322
  // Track function entry
1244
1323
  if (isFunctionNode(node)) {
@@ -1280,7 +1359,7 @@ function findCallsInCode(code, parser, options = {}) {
1280
1359
  const scopeKey = functionStack[functionStack.length - 1].startLine;
1281
1360
  const typeMap = scopeTypes.get(scopeKey);
1282
1361
  if (typeMap) {
1283
- typeMap.set(valueVar, el.type);
1362
+ typeMap.set(valueVar, el.type, 'flow', node);
1284
1363
  const qualifiers = scopeTypeQualifiers.get(scopeKey);
1285
1364
  if (el.qualifier) qualifiers?.set(valueVar, el.qualifier);
1286
1365
  else qualifiers?.delete(valueVar);
@@ -1425,7 +1504,7 @@ function findCallsInCode(code, parser, options = {}) {
1425
1504
  }
1426
1505
  }
1427
1506
  if (typeName) {
1428
- typeMap.set(names[vi], typeName);
1507
+ typeMap.set(names[vi], typeName, typeGuessed ? 'guess' : val.type === 'type_assertion_expression' ? 'type-assertion' : ['composite_literal', 'unary_expression'].includes(val.type) ? 'constructor' : ['identifier', 'index_expression'].includes(val.type) ? 'flow' : 'unknown', val);
1429
1508
  const qualifiers = scopeTypeQualifiers.get(scopeKey);
1430
1509
  if (typeQualifier) qualifiers?.set(names[vi], typeQualifier);
1431
1510
  else qualifiers?.delete(names[vi]);
@@ -1445,78 +1524,8 @@ function findCallsInCode(code, parser, options = {}) {
1445
1524
  }
1446
1525
  }
1447
1526
 
1448
- // Explicitly typed var declarations: `var buf bytes.Buffer`,
1449
- // `var sb strings.Builder` (fix #220, cobra-measured — sb.String()
1450
- // on an untyped receiver fell to single-owner confirmation). Same
1451
- // semantics as parameter annotations: the declared type is the
1452
- // receiver's compile-time type.
1453
- if (node.type === 'var_declaration') {
1454
- const scopeKey = functionStack.length > 0
1455
- ? functionStack[functionStack.length - 1].startLine : null;
1456
- const varTypeMap = scopeKey == null ? packageTypes : scopeTypes.get(scopeKey);
1457
- const varQualifierMap = scopeKey == null
1458
- ? packageTypeQualifiers : scopeTypeQualifiers.get(scopeKey);
1459
- if (varTypeMap) {
1460
- const varContainerMap = scopeKey == null
1461
- ? packageContainerTypes : scopeContainerTypes.get(scopeKey);
1462
- const recordSpec = (spec) => {
1463
- if (spec.type !== 'var_spec') return;
1464
- const declaredType = spec.childForFieldName('type');
1465
- // Container-typed declaration (fix #300): `var routes
1466
- // []*Route` feeds range-element typing.
1467
- if (declaredType && CONTAINER_TYPE_NODES.has(declaredType.type) &&
1468
- varContainerMap) {
1469
- for (let j = 0; j < spec.namedChildCount; j++) {
1470
- const id = spec.namedChild(j);
1471
- if (id.type === 'identifier') {
1472
- varContainerMap.set(id.text, declaredType.text);
1473
- }
1474
- }
1475
- return;
1476
- }
1477
- let typeName = extractTypeName(spec.childForFieldName('type'));
1478
- let qualifier = typeName
1479
- ? extractTypeQualifier(spec.childForFieldName('type')) : null;
1480
- if (!typeName) {
1481
- // var cstDialer = Dialer{...} — the initializer's
1482
- // composite-literal type IS the variable's type
1483
- // (fix #298, websocket-measured: `d := cstDialer`
1484
- // copies then call DialContext). Single-value specs
1485
- // only; multi-value pairing stays untyped.
1486
- const valueNode = spec.childForFieldName('value');
1487
- let init = valueNode?.type === 'expression_list' &&
1488
- valueNode.namedChildCount === 1
1489
- ? valueNode.namedChild(0) : null;
1490
- if (init?.type === 'unary_expression') {
1491
- init = init.namedChildCount > 0 ? init.namedChild(0) : null;
1492
- }
1493
- if (init?.type === 'composite_literal') {
1494
- const tn = init.childForFieldName('type');
1495
- typeName = extractTypeName(tn);
1496
- qualifier = extractTypeQualifier(tn);
1497
- }
1498
- if (!typeName) return;
1499
- }
1500
- for (let j = 0; j < spec.namedChildCount; j++) {
1501
- const id = spec.namedChild(j);
1502
- if (id.type === 'identifier') {
1503
- varTypeMap.set(id.text, typeName);
1504
- if (qualifier) varQualifierMap?.set(id.text, qualifier);
1505
- else varQualifierMap?.delete(id.text);
1506
- if (scopeKey != null) {
1507
- scopeGuesses.get(scopeKey)?.delete(id.text);
1508
- }
1509
- }
1510
- }
1511
- };
1512
- for (let i = 0; i < node.namedChildCount; i++) {
1513
- const c = node.namedChild(i);
1514
- if (c.type === 'var_spec') recordSpec(c);
1515
- else if (c.type === 'var_spec_list') {
1516
- for (let j = 0; j < c.namedChildCount; j++) recordSpec(c.namedChild(j));
1517
- }
1518
- }
1519
- }
1527
+ if (node.type === 'var_declaration' && functionStack.length > 0) {
1528
+ recordVarDeclaration(node, functionStack.at(-1).startLine);
1520
1529
  }
1521
1530
 
1522
1531
  // Track local closures: atoi := func(...) { ... } or
@@ -1688,6 +1697,7 @@ function findCallsInCode(code, parser, options = {}) {
1688
1697
  // Distinguish pkg.Func() (package-qualified) from obj.Method()
1689
1698
  // If receiver is a known import alias, this is a package call, not a method call
1690
1699
  const isPkgCall = receiver && importAliases.has(receiver);
1700
+ let receiverTypeEvidence;
1691
1701
  let receiverType = (!isPkgCall && receiver)
1692
1702
  ? getReceiverType(receiver, operandNode) : undefined;
1693
1703
  let receiverTypeQualifier = receiverType
@@ -1700,6 +1710,7 @@ function findCallsInCode(code, parser, options = {}) {
1700
1710
  const lit = literalReceiverInfo(operandNode);
1701
1711
  if (lit) {
1702
1712
  receiverType = lit.receiverType;
1713
+ receiverTypeEvidence = typeOrigin('constructor', operandNode);
1703
1714
  receiverTypeQualifier = lit.receiverTypeQualifier;
1704
1715
  }
1705
1716
  }
@@ -1804,7 +1815,7 @@ function findCallsInCode(code, parser, options = {}) {
1804
1815
  callEnd: node.endIndex,
1805
1816
  isMethod: !isPkgCall,
1806
1817
  receiver,
1807
- ...(receiverType && { receiverType }),
1818
+ ...(receiverType && { receiverType, ...(receiverTypeEvidence ? { receiverTypeSource: 'constructor', receiverTypeEvidence } : getReceiverType(receiver, node, true) || { receiverTypeSource: 'unknown' }) }),
1808
1819
  ...(receiverTypeQualifier && { receiverTypeQualifier }),
1809
1820
  ...(receiverType && isGuessedType(receiver) && { receiverTypeGuessed: true }),
1810
1821
  ...(receiverIndexedSource && {
@@ -1908,6 +1919,7 @@ function findCallsInCode(code, parser, options = {}) {
1908
1919
  const operandNode = node.childForFieldName('operand');
1909
1920
  if (fieldNode && operandNode) {
1910
1921
  const receiver = operandNode.type === 'identifier' ? operandNode.text : undefined;
1922
+ let receiverTypeEvidence;
1911
1923
  let receiverType = receiver
1912
1924
  ? getReceiverType(receiver, operandNode) : undefined;
1913
1925
  let receiverTypeQualifier = receiverType
@@ -1918,6 +1930,7 @@ function findCallsInCode(code, parser, options = {}) {
1918
1930
  const lit = literalReceiverInfo(operandNode);
1919
1931
  if (lit) {
1920
1932
  receiverType = lit.receiverType;
1933
+ receiverTypeEvidence = typeOrigin('constructor', operandNode);
1921
1934
  receiverTypeQualifier = lit.receiverTypeQualifier;
1922
1935
  }
1923
1936
  }
@@ -1930,7 +1943,7 @@ function findCallsInCode(code, parser, options = {}) {
1930
1943
  column: fieldNode.startPosition.column,
1931
1944
  isMethod: true,
1932
1945
  receiver,
1933
- ...(receiverType && { receiverType }),
1946
+ ...(receiverType && { receiverType, ...(receiverTypeEvidence ? { receiverTypeSource: 'constructor', receiverTypeEvidence } : getReceiverType(receiver, node, true) || { receiverTypeSource: 'unknown' }) }),
1934
1947
  ...(receiverTypeQualifier && { receiverTypeQualifier }),
1935
1948
  ...(receiverType && receiver && isGuessedType(receiver) && { receiverTypeGuessed: true }),
1936
1949
  enclosingFunction,
@@ -1981,6 +1994,7 @@ function findCallsInCode(code, parser, options = {}) {
1981
1994
  const operandNode = rhs.childForFieldName('operand');
1982
1995
  if (fieldNode && operandNode) {
1983
1996
  const receiver = operandNode.type === 'identifier' ? operandNode.text : undefined;
1997
+ let receiverTypeEvidence;
1984
1998
  let receiverType = receiver
1985
1999
  ? getReceiverType(receiver, operandNode) : undefined;
1986
2000
  let receiverTypeQualifier = receiverType
@@ -1990,6 +2004,7 @@ function findCallsInCode(code, parser, options = {}) {
1990
2004
  const lit = literalReceiverInfo(operandNode);
1991
2005
  if (lit) {
1992
2006
  receiverType = lit.receiverType;
2007
+ receiverTypeEvidence = typeOrigin('constructor', operandNode);
1993
2008
  receiverTypeQualifier = lit.receiverTypeQualifier;
1994
2009
  }
1995
2010
  }
@@ -2000,7 +2015,7 @@ function findCallsInCode(code, parser, options = {}) {
2000
2015
  column: fieldNode.startPosition.column,
2001
2016
  isMethod: true,
2002
2017
  receiver,
2003
- ...(receiverType && { receiverType }),
2018
+ ...(receiverType && { receiverType, ...(receiverTypeEvidence ? { receiverTypeSource: 'constructor', receiverTypeEvidence } : getReceiverType(receiver, node, true) || { receiverTypeSource: 'unknown' }) }),
2004
2019
  ...(receiverTypeQualifier && { receiverTypeQualifier }),
2005
2020
  ...(receiverType && receiver && isGuessedType(receiver) && { receiverTypeGuessed: true }),
2006
2021
  enclosingFunction,
@@ -2048,6 +2063,7 @@ function findCallsInCode(code, parser, options = {}) {
2048
2063
  const operandNode = val.childForFieldName('operand');
2049
2064
  if (!fieldNode || !operandNode) continue;
2050
2065
  const receiver = operandNode.type === 'identifier' ? operandNode.text : undefined;
2066
+ let receiverTypeEvidence;
2051
2067
  let receiverType = receiver
2052
2068
  ? getReceiverType(receiver, operandNode) : undefined;
2053
2069
  let receiverTypeQualifier = receiverType
@@ -2056,6 +2072,7 @@ function findCallsInCode(code, parser, options = {}) {
2056
2072
  const lit = literalReceiverInfo(operandNode);
2057
2073
  if (lit) {
2058
2074
  receiverType = lit.receiverType;
2075
+ receiverTypeEvidence = typeOrigin('constructor', operandNode);
2059
2076
  receiverTypeQualifier = lit.receiverTypeQualifier;
2060
2077
  }
2061
2078
  }
@@ -2066,7 +2083,7 @@ function findCallsInCode(code, parser, options = {}) {
2066
2083
  column: fieldNode.startPosition.column,
2067
2084
  isMethod: true,
2068
2085
  receiver,
2069
- ...(receiverType && { receiverType }),
2086
+ ...(receiverType && { receiverType, ...(receiverTypeEvidence ? { receiverTypeSource: 'constructor', receiverTypeEvidence } : getReceiverType(receiver, node, true) || { receiverTypeSource: 'unknown' }) }),
2070
2087
  ...(receiverTypeQualifier && { receiverTypeQualifier }),
2071
2088
  ...(receiverType && receiver && isGuessedType(receiver) && { receiverTypeGuessed: true }),
2072
2089
  enclosingFunction: getCurrentEnclosingFunction(),
@@ -2136,6 +2153,7 @@ function findCallsInCode(code, parser, options = {}) {
2136
2153
  const operandNode = valueNode.childForFieldName('operand');
2137
2154
  if (fieldNode && operandNode) {
2138
2155
  const receiver = operandNode.type === 'identifier' ? operandNode.text : undefined;
2156
+ let receiverTypeEvidence;
2139
2157
  let receiverType = receiver
2140
2158
  ? getReceiverType(receiver, operandNode) : undefined;
2141
2159
  let receiverTypeQualifier = receiverType
@@ -2145,6 +2163,7 @@ function findCallsInCode(code, parser, options = {}) {
2145
2163
  const lit = literalReceiverInfo(operandNode);
2146
2164
  if (lit) {
2147
2165
  receiverType = lit.receiverType;
2166
+ receiverTypeEvidence = typeOrigin('constructor', operandNode);
2148
2167
  receiverTypeQualifier = lit.receiverTypeQualifier;
2149
2168
  }
2150
2169
  }
@@ -2155,7 +2174,7 @@ function findCallsInCode(code, parser, options = {}) {
2155
2174
  column: fieldNode.startPosition.column,
2156
2175
  isMethod: true,
2157
2176
  receiver,
2158
- ...(receiverType && { receiverType }),
2177
+ ...(receiverType && { receiverType, ...(receiverTypeEvidence ? { receiverTypeSource: 'constructor', receiverTypeEvidence } : getReceiverType(receiver, node, true) || { receiverTypeSource: 'unknown' }) }),
2159
2178
  ...(receiverTypeQualifier && { receiverTypeQualifier }),
2160
2179
  ...(receiverType && receiver && isGuessedType(receiver) && { receiverTypeGuessed: true }),
2161
2180
  enclosingFunction,
package/languages/java.js CHANGED
@@ -5,6 +5,9 @@
5
5
  * declarations, and static final constants.
6
6
  */
7
7
 
8
+ const { ReceiverTypeMap, typeOrigin } = require('./type-evidence');
9
+
10
+
8
11
  const {
9
12
  traverseTree,
10
13
  traverseTreeCached,
@@ -1029,7 +1032,7 @@ function findCallsInCode(code, parser) {
1029
1032
 
1030
1033
  // Build type map from method/constructor parameters
1031
1034
  const buildScopeTypeMap = (node) => {
1032
- const typeMap = new Map();
1035
+ const typeMap = new ReceiverTypeMap();
1033
1036
  const rawTypeMap = new Map();
1034
1037
  const qualifierMap = new Map();
1035
1038
  const paramsNode = node.childForFieldName('parameters');
@@ -1041,7 +1044,7 @@ function findCallsInCode(code, parser) {
1041
1044
  const typeNode = param.childForFieldName('type');
1042
1045
  const typeName = extractTypeName(typeNode);
1043
1046
  if (nameNode && typeName) {
1044
- typeMap.set(nameNode.text, typeName);
1047
+ typeMap.set(nameNode.text, typeName, 'annotation', typeNode);
1045
1048
  rawTypeMap.set(nameNode.text, typeNode.text);
1046
1049
  const qualifier = extractTypeQualifier(typeNode);
1047
1050
  if (qualifier) qualifierMap.set(nameNode.text, qualifier);
@@ -1076,10 +1079,10 @@ function findCallsInCode(code, parser) {
1076
1079
  };
1077
1080
 
1078
1081
  // Look up variable type from scope chain
1079
- const getReceiverType = (varName) => {
1082
+ const getReceiverType = (varName, evidence = false) => {
1080
1083
  for (let i = functionStack.length - 1; i >= 0; i--) {
1081
1084
  const typeMap = scopeTypes.get(functionStack[i].startLine);
1082
- if (typeMap?.has(varName)) return typeMap.get(varName);
1085
+ if (typeMap?.has(varName)) return evidence ? typeMap.fields(varName) : typeMap.get(varName);
1083
1086
  }
1084
1087
  return undefined;
1085
1088
  };
@@ -1476,7 +1479,7 @@ function findCallsInCode(code, parser) {
1476
1479
  rawHad: rawTypeMap?.has(nameNode.text),
1477
1480
  rawPrevious: rawTypeMap?.get(nameNode.text),
1478
1481
  });
1479
- typeMap.set(nameNode.text, typeName);
1482
+ typeMap.set(nameNode.text, typeName, 'annotation', typeNode);
1480
1483
  rawTypeMap?.set(nameNode.text, typeNode.text);
1481
1484
  const qualifierMap = scopeTypeQualifiers.get(scopeKey);
1482
1485
  if (typeQualifier) qualifierMap?.set(nameNode.text, typeQualifier);
@@ -1525,7 +1528,9 @@ function findCallsInCode(code, parser) {
1525
1528
  packageQualifier = rootText;
1526
1529
  }
1527
1530
  }
1528
- const receiverType = castReceiverType ||
1531
+ const inlineConstructorType = receiverNode?.type === 'object_creation_expression'
1532
+ ? extractTypeName(receiverNode.childForFieldName('type')) : undefined;
1533
+ const receiverType = castReceiverType || inlineConstructorType ||
1529
1534
  ((receiver && receiver !== 'this') ? getReceiverType(receiver) : undefined);
1530
1535
  const receiverTypeQualifier = packageQualifier ||
1531
1536
  (!castReceiverType && receiver ? getReceiverTypeQualifier(receiver) : undefined);
@@ -1597,6 +1602,7 @@ function findCallsInCode(code, parser) {
1597
1602
  : `chain:${receiverPath.owner}#${receiverPath.methods.join('#')}`)
1598
1603
  : null;
1599
1604
  calls.push({
1605
+ callSite: typeOrigin('call', nameNode),
1600
1606
  name: nameNode.text,
1601
1607
  // Multi-line chains (builder.x()\n.y()) must report each
1602
1608
  // method's OWN name line, not the chain-start line — the
@@ -1604,7 +1610,10 @@ function findCallsInCode(code, parser) {
1604
1610
  line: nameNode.startPosition.row + 1,
1605
1611
  isMethod: !!objNode,
1606
1612
  receiver,
1607
- ...(receiverType && { receiverType }),
1613
+ ...(receiverType && { receiverType, ...(getReceiverType(receiver, true) ||
1614
+ (castReceiverType ? { receiverTypeSource: 'cast', receiverTypeEvidence: typeOrigin('cast', objNode) } :
1615
+ inlineConstructorType ? { receiverTypeSource: 'constructor', receiverTypeEvidence: typeOrigin('constructor', receiverNode) } :
1616
+ { receiverTypeSource: 'unknown' })) }),
1608
1617
  ...(receiverTypeQualifier && { receiverTypeQualifier }),
1609
1618
  ...(receiverIsTypeQualified && { receiverIsTypeQualified: true }),
1610
1619
  ...(castReceiverType && { receiverTypeCast: true }),
@@ -1656,6 +1665,7 @@ function findCallsInCode(code, parser) {
1656
1665
  const enclosingFunction = getCurrentEnclosingFunction();
1657
1666
  const ctorArgs = getCallArgs(node);
1658
1667
  calls.push({
1668
+ callSite: typeOrigin('call', node),
1659
1669
  name: targetClass,
1660
1670
  line: node.startPosition.row + 1,
1661
1671
  isMethod: false,
@@ -1686,6 +1696,7 @@ function findCallsInCode(code, parser) {
1686
1696
  if (enumName) {
1687
1697
  const ctorArgs = getCallArgs(node);
1688
1698
  calls.push({
1699
+ callSite: typeOrigin('call', node),
1689
1700
  name: enumName,
1690
1701
  line: node.startPosition.row + 1,
1691
1702
  isMethod: false,
@@ -1725,6 +1736,7 @@ function findCallsInCode(code, parser) {
1725
1736
  const enclosingFunction = getCurrentEnclosingFunction();
1726
1737
  const ctorArgs = getCallArgs(node);
1727
1738
  calls.push({
1739
+ callSite: typeOrigin('call', typeNode),
1728
1740
  name: typeName,
1729
1741
  line: node.startPosition.row + 1,
1730
1742
  isMethod: false,
@@ -1747,11 +1759,12 @@ function findCallsInCode(code, parser) {
1747
1759
  const receiverType = (receiver && receiver !== 'this') ? getReceiverType(receiver) : undefined;
1748
1760
  const enclosingFunction = getCurrentEnclosingFunction();
1749
1761
  calls.push({
1762
+ callSite: typeOrigin('call', nameNode),
1750
1763
  name: nameNode.text,
1751
1764
  line: node.startPosition.row + 1,
1752
1765
  isMethod: !!receiver,
1753
1766
  receiver,
1754
- ...(receiverType && { receiverType }),
1767
+ ...(receiverType && { receiverType, ...(getReceiverType(receiver, true) || { receiverTypeSource: 'unknown' }) }),
1755
1768
  isFunctionReference: true,
1756
1769
  isPotentialCallback: true,
1757
1770
  enclosingFunction
@@ -1788,7 +1801,7 @@ function findCallsInCode(code, parser) {
1788
1801
  if (nameNode && typeName) {
1789
1802
  const scopeKey = functionStack[functionStack.length - 1].startLine;
1790
1803
  const typeMap = scopeTypes.get(scopeKey);
1791
- if (typeMap) typeMap.set(nameNode.text, typeName);
1804
+ if (typeMap) typeMap.set(nameNode.text, typeName, valueNode?.type === 'object_creation_expression' ? 'constructor' : 'annotation', valueNode || declTypeNode);
1792
1805
  const rawTypeMap = scopeRawTypes.get(scopeKey);
1793
1806
  const rawType = valueNode?.type === 'object_creation_expression'
1794
1807
  ? valueNode.childForFieldName('type')?.text
@@ -1822,7 +1835,7 @@ function findCallsInCode(code, parser) {
1822
1835
  if (resNameNode && typeName) {
1823
1836
  const scopeKey = functionStack[functionStack.length - 1].startLine;
1824
1837
  const typeMap = scopeTypes.get(scopeKey);
1825
- if (typeMap) typeMap.set(resNameNode.text, typeName);
1838
+ if (typeMap) typeMap.set(resNameNode.text, typeName, 'with-binding', node);
1826
1839
  const rawTypeMap = scopeRawTypes.get(scopeKey);
1827
1840
  const rawType = resValueNode?.type === 'object_creation_expression'
1828
1841
  ? resValueNode.childForFieldName('type')?.text