ucn 5.3.4 → 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.
@@ -0,0 +1,26 @@
1
+ 'use strict';
2
+
3
+ // Shared by receiver resolution and the pure provenance validator.
4
+ const BUILTIN_RECEIVER_TYPES = new Set([
5
+ 'dict', 'list', 'set', 'tuple', 'str', 'int', 'float', 'bool', 'bytes', 'frozenset',
6
+ 'Mapping', 'MutableMapping', 'Sequence', 'MutableSequence',
7
+ 'Collection', 'Iterable', 'Iterator', 'KeysView', 'ValuesView', 'ItemsView',
8
+ 'IO', 'TextIO', 'BinaryIO', 'StringIO', 'BytesIO',
9
+ 'ZlibCompress', 'ZlibDecompress',
10
+ 'AsyncEvent',
11
+ 'Generator', 'AsyncGenerator', 'ContextManager', 'AsyncContextManager',
12
+ 'Array', 'String', 'Object', 'RegExp', 'Number', 'Boolean', 'Map', 'Set', 'Promise',
13
+ 'WeakMap', 'WeakSet',
14
+ 'string', 'number', 'boolean', 'bigint', 'symbol',
15
+ 'object', 'dynamic', 'decimal', 'byte', 'sbyte', 'char',
16
+ 'short', 'ushort', 'uint', 'long', 'ulong', 'double',
17
+ 'List', 'Dictionary', 'HashSet', 'Queue', 'Stack',
18
+ 'Task', 'ValueTask', 'IEnumerable', 'ICollection', 'IList',
19
+ ]);
20
+
21
+ function isProvenanceBuiltinReceiver(type, language) {
22
+ return BUILTIN_RECEIVER_TYPES.has(type) ||
23
+ (language === 'rust' && ['Option', 'Result', 'Vec'].includes(type));
24
+ }
25
+
26
+ module.exports = { BUILTIN_RECEIVER_TYPES, isProvenanceBuiltinReceiver };
@@ -0,0 +1,206 @@
1
+ 'use strict';
2
+
3
+ const path = require('path');
4
+ const { declarationIdentity, identityKey } = require('./provenance');
5
+ const { resolveRustImport } = require('./imports');
6
+
7
+ const TYPE_KINDS = new Set(['class', 'struct', 'enum', 'type', 'trait', 'interface']);
8
+
9
+ function parseGeneric(value) {
10
+ if (typeof value !== 'string') return null;
11
+ const text = value.trim(), start = text.indexOf('<');
12
+ if (start < 1 || !text.endsWith('>')) return null;
13
+ const args = [];
14
+ let depth = 0, current = '';
15
+ for (const character of text.slice(start + 1, -1)) {
16
+ if ('<(['.includes(character)) depth++;
17
+ else if ('>)]'.includes(character)) depth--;
18
+ if (character === ',' && depth === 0) { args.push(current.trim()); current = ''; }
19
+ else current += character;
20
+ if (depth < 0) return null;
21
+ }
22
+ if (depth) return null;
23
+ args.push(current.trim());
24
+ return args.every(Boolean) ? { head: text.slice(0, start).trim(), args } : null;
25
+ }
26
+
27
+ function projectTuple(text, projection) {
28
+ for (const position of projection) {
29
+ text = text.trim();
30
+ if (!Number.isInteger(position) || position < 0 || !text.startsWith('(') || !text.endsWith(')')) return null;
31
+ const tuple = parseGeneric(`Tuple<${text.slice(1, -1)}>`);
32
+ if (!tuple || position >= tuple.args.length) return null;
33
+ text = tuple.args[position];
34
+ }
35
+ return text;
36
+ }
37
+
38
+ /** Preserve the payload of a proven standard Result/Option annotation.
39
+ * Generic arguments retain their own declaration scope through alias hops;
40
+ * a project type merely named Result is not a standard wrapper.
41
+ */
42
+ function rustWrapperContract(index, text, file, options) {
43
+ const expression = parseGeneric(text);
44
+ if (!expression) return null;
45
+ const seen = new Set();
46
+ const chain = [];
47
+ const resolve = (head, args, context) => {
48
+ const key = `${context}:${head}`;
49
+ if (seen.has(key) || seen.size >= 12) return null;
50
+ seen.add(key);
51
+ const entry = index.files.get(context);
52
+ if (!entry) return null;
53
+ const standard = /^(?:std|core)::(?:result::Result|option::Option)$/.test(head);
54
+ if (standard) {
55
+ const root = head.split('::')[0];
56
+ if ((index.symbols.get(root) || []).some(d => d.file === context) ||
57
+ (entry.importBindings || []).some(b => (b.alias || b.name) === root)) return null;
58
+ const kind = head.split('::').at(-1);
59
+ if (args.length !== (kind === 'Result' ? 2 : 1)) return null;
60
+ chain.push({ standard: head, fromFile: entry.relativePath,
61
+ rootDeclarations: [], rootBindings: [] });
62
+ return { kind, item: args[0] };
63
+ }
64
+ if (head.includes('::')) {
65
+ const parts = head.split('::');
66
+ const name = parts.pop();
67
+ const prefix = parts.join('::');
68
+ const relative = entry.moduleResolved?.[prefix];
69
+ const destination = relative ? path.resolve(index.root, relative)
70
+ : resolveRustImport(prefix, context, index.root);
71
+ if (!destination || !index.files.has(destination)) return null;
72
+ chain.push({ fromFile: entry.relativePath, typePath: head,
73
+ toFile: path.relative(index.root, destination) });
74
+ return resolve(name, args, destination);
75
+ }
76
+ const local = (index.symbols.get(head) || []).filter(d =>
77
+ d.file === context && TYPE_KINDS.has(d.type));
78
+ if (local.length) {
79
+ if (local.length !== 1) return null;
80
+ const alias = local[0];
81
+ if (alias.type !== 'type' || !alias.aliasTypeText ||
82
+ !alias.aliasTypeParameters?.every(Boolean) ||
83
+ alias.aliasTypeParameters.length < args.length) return null;
84
+ const body = parseGeneric(alias.aliasTypeText);
85
+ if (!body) return null;
86
+ const parameters = new Map();
87
+ for (const [i, name] of alias.aliasTypeParameters.entries()) {
88
+ const fallback = alias.aliasTypeDefaults?.[i];
89
+ const value = args[i] || (fallback && (parameters.get(fallback) || { text: fallback, file: context }));
90
+ if (!value || (!args[i] && !parameters.has(fallback) &&
91
+ fallback.split(/[^\w]+/).some(token => parameters.has(token)))) return null;
92
+ parameters.set(name, value);
93
+ }
94
+ const forwarded = body.args.map(text => parameters.get(text) || { text, file: context });
95
+ // Nested generic substitution needs its own type-expression model.
96
+ // Do not resolve an unsubstituted parameter in the alias's scope.
97
+ if (body.args.some(text => !parameters.has(text) &&
98
+ text.split(/[^\w]+/).some(token => parameters.has(token)))) return null;
99
+ chain.push({ declaration: declarationIdentity(alias), type: alias.aliasTypeText,
100
+ parameters: alias.aliasTypeParameters, defaults: alias.aliasTypeDefaults,
101
+ arguments: args.map(argument => ({ text: argument.text, file: path.relative(index.root, argument.file) })) });
102
+ return resolve(body.head, forwarded, context);
103
+ }
104
+ const bindings = (entry.importBindings || []).filter(b => (b.alias || b.name) === head);
105
+ if (bindings.length) {
106
+ if (bindings.length !== 1) return null;
107
+ const binding = bindings[0];
108
+ const destination = entry.moduleResolved?.[binding.module];
109
+ chain.push({ fromFile: entry.relativePath, binding: { ...binding }, toFile: destination || null });
110
+ return destination
111
+ ? resolve(binding.module.split('::').at(-1), args, path.resolve(index.root, destination))
112
+ : resolve(binding.module, args, context);
113
+ }
114
+ if (head === 'Result' || head === 'Option') {
115
+ if ((entry.importBindings || []).some(b => b.name === '*' || b.module?.endsWith('::*'))) return null;
116
+ chain.push({ prelude: head, fromFile: entry.relativePath,
117
+ declarations: [], bindings: [], wildcardImports: [] });
118
+ return resolve(head === 'Result' ? 'std::result::Result' : 'std::option::Option', args, context);
119
+ }
120
+ return null;
121
+ };
122
+ const resolved = resolve(expression.head, expression.args.map(text => ({ text, file })), file);
123
+ if (!resolved) return null;
124
+ const wrapper = { kind: resolved.kind, annotation: { text, file: path.relative(index.root, file) }, chain };
125
+ const projection = options.projection || [];
126
+ const projected = projectTuple(resolved.item.text, projection);
127
+ const item = projected && options.parseType(projected);
128
+ if (!item) return options.allowUnknownPayload ? wrapper : null;
129
+ const origin = options.resolveType(resolved.item.file, item.name, item.qualifier);
130
+ const declarations = origin?.fromFile && (index.symbols.get(item.name) || []).filter(d =>
131
+ d.file === origin.fromFile && TYPE_KINDS.has(d.type));
132
+ if (declarations?.length !== 1) return options.allowUnknownPayload ? wrapper : null;
133
+ return { kind: resolved.kind, type: item.name, fromFile: origin.fromFile,
134
+ payload: { text: resolved.item.text, file: path.relative(index.root, resolved.item.file),
135
+ ...(projection.length && { projection, projected }),
136
+ declaration: declarationIdentity(declarations[0]) },
137
+ annotation: { text, file: path.relative(index.root, file) }, chain };
138
+ }
139
+
140
+ /** Replay the type arguments and scope hops without consulting the resolver. */
141
+ function validateRustWrapperContract(contract, ownershipOnly = false) {
142
+ const initial = parseGeneric(contract?.annotation?.text);
143
+ if (!initial || !contract.annotation.file || !Array.isArray(contract.chain) ||
144
+ contract.chain.length > 24 || !identityKey(contract.producer) ||
145
+ contract.producer.file !== contract.annotation.file ||
146
+ contract.producer.returnType !== contract.annotation.text) return false;
147
+ let head = initial.head, file = contract.annotation.file;
148
+ let args = initial.args.map(text => ({ text, file }));
149
+ const empty = value => Array.isArray(value) && value.length === 0;
150
+ let standard;
151
+ for (const hop of contract.chain) {
152
+ if (standard) return false;
153
+ if (hop.declaration) {
154
+ if (!identityKey(hop.declaration) || hop.declaration.name !== head || hop.declaration.file !== file ||
155
+ hop.declaration.kind !== 'type' || !Array.isArray(hop.parameters) ||
156
+ !hop.parameters.every(name => typeof name === 'string' && name) ||
157
+ hop.parameters.length < args.length ||
158
+ JSON.stringify(hop.arguments) !== JSON.stringify(args)) return false;
159
+ const body = parseGeneric(hop.type);
160
+ if (!body) return false;
161
+ const parameters = new Map();
162
+ for (const [i, name] of hop.parameters.entries()) {
163
+ const fallback = hop.defaults?.[i];
164
+ const value = args[i] || (fallback && (parameters.get(fallback) || { text: fallback, file }));
165
+ if (!value || (!args[i] && !parameters.has(fallback) &&
166
+ fallback.split(/[^\w]+/).some(token => parameters.has(token)))) return false;
167
+ parameters.set(name, value);
168
+ }
169
+ if (body.args.some(text => !parameters.has(text) &&
170
+ text.split(/[^\w]+/).some(token => parameters.has(token)))) return false;
171
+ args = body.args.map(text => parameters.get(text) || { text, file });
172
+ head = body.head;
173
+ } else if (hop.binding) {
174
+ if (hop.fromFile !== file || (hop.binding.alias || hop.binding.name) !== head ||
175
+ typeof hop.binding.module !== 'string') return false;
176
+ head = hop.toFile ? hop.binding.module.split('::').at(-1) : hop.binding.module;
177
+ file = hop.toFile || file;
178
+ } else if (hop.typePath) {
179
+ if (hop.fromFile !== file || hop.typePath !== head || !hop.toFile) return false;
180
+ head = head.split('::').at(-1);
181
+ file = hop.toFile;
182
+ } else if (hop.prelude) {
183
+ if (hop.fromFile !== file || hop.prelude !== head || !['Result', 'Option'].includes(head) ||
184
+ !empty(hop.declarations) || !empty(hop.bindings) || !empty(hop.wildcardImports)) return false;
185
+ head = head === 'Result' ? 'std::result::Result' : 'std::option::Option';
186
+ } else if (hop.standard) {
187
+ if (hop.fromFile !== file || hop.standard !== head ||
188
+ !/^(std|core)::(result::Result|option::Option)$/.test(head) ||
189
+ !empty(hop.rootDeclarations) || !empty(hop.rootBindings)) return false;
190
+ standard = head.split('::').at(-1);
191
+ } else return false;
192
+ }
193
+ if (!standard || standard !== contract.kind || args.length !== (standard === 'Result' ? 2 : 1)) return false;
194
+ if (ownershipOnly) return true;
195
+ if (args[0].text !== contract.payload?.text || args[0].file !== contract.payload?.file) return false;
196
+ const projected = projectTuple(args[0].text, contract.payload.projection || []);
197
+ if (!projected || (contract.payload.projection?.length && contract.payload.projected !== projected)) return false;
198
+ let payloadHead = projected.replace(/^&(?:\s*'\w+)?\s*/, '').replace(/^mut\s+/, '').trim();
199
+ payloadHead = (parseGeneric(payloadHead)?.head || payloadHead).split('::').at(-1);
200
+ if (payloadHead === 'Self') payloadHead = contract.producer.className;
201
+ return payloadHead === contract.type && identityKey(contract.payload.declaration) !== null &&
202
+ contract.payload.declaration?.name === contract.type &&
203
+ TYPE_KINDS.has(contract.payload.declaration?.kind);
204
+ }
205
+
206
+ module.exports = { rustWrapperContract, validateRustWrapperContract };
package/core/verify.js CHANGED
@@ -9,6 +9,7 @@ const { detectLanguage, getParser, getLanguageAdapter, safeParse, langTraits } =
9
9
  const { sameNode } = require('../languages/utils');
10
10
  const { escapeRegExp, codeUnitCompare, NON_CALLABLE_TYPES } = require('./shared');
11
11
  const { findAccessorReferences } = require('./accessors');
12
+ const { validateCallMismatch, declarationIdentity } = require('./provenance');
12
13
 
13
14
  function codeUnitColumnForByteColumn(line, byteColumn) {
14
15
  if (!Number.isInteger(byteColumn) || byteColumn < 0) return null;
@@ -1340,7 +1341,10 @@ function verify(index, name, options = {}) {
1340
1341
 
1341
1342
  // Convert caller results to usage-like objects for analyzeCallSite.
1342
1343
  // Carry callerFile/callerStartLine through so we can compute inTestCase.
1343
- const calls = callerResults.map(c => ({
1344
+ const invalidFamilyCalls = sweepUnverified.filter(c =>
1345
+ validateCallMismatch(c.provenance, declarationIdentity(def)));
1346
+ const calls = [...callerResults, ...invalidFamilyCalls].map(c => ({
1347
+ invalidOverload: invalidFamilyCalls.includes(c),
1344
1348
  file: c.file,
1345
1349
  relativePath: c.relativePath,
1346
1350
  line: c.line,
@@ -1448,12 +1452,12 @@ function verify(index, name, options = {}) {
1448
1452
  const countOk = hasRest
1449
1453
  ? argCount >= minArgs
1450
1454
  : (argCount >= minArgs && argCount <= expectedParamCount);
1451
- if (!countOk) {
1455
+ if (!countOk || call.invalidOverload) {
1452
1456
  mismatches.push({
1453
1457
  file: call.relativePath,
1454
1458
  line: call.line,
1455
1459
  expression: call.content.trim(),
1456
- expected: hasRest
1460
+ expected: call.invalidOverload && countOk ? 'a compatible overload signature' : hasRest
1457
1461
  ? `at least ${minArgs} arg(s)`
1458
1462
  : (minArgs === expectedParamCount
1459
1463
  ? `${expectedParamCount} arg(s)`
@@ -26,6 +26,8 @@ const OPTIONAL_HELPERS = Object.freeze([
26
26
  'findTestCallRanges',
27
27
  'getBuiltinCallReturnType',
28
28
  'getBuiltinFieldType',
29
+ 'findPytestFunctions',
30
+ 'findPythonModuleEvidence',
29
31
  'isPlatformConcreteCall',
30
32
  'isPlatformConcreteType',
31
33
  ]);
@@ -8,6 +8,9 @@
8
8
  * source fallback.
9
9
  */
10
10
 
11
+ const { typeOrigin } = require('./type-evidence');
12
+
13
+
11
14
  const {
12
15
  traverseTree,
13
16
  traverseTreeCached,
@@ -1946,6 +1949,10 @@ function buildVariableTypes(tree) {
1946
1949
  name: identity.name,
1947
1950
  type,
1948
1951
  staticType: declaratorStaticType(type, declarator),
1952
+ origin: typeOrigin(node.type === 'declaration' &&
1953
+ (declarator.type === 'identifier' ||
1954
+ declarator.childForFieldName('value')?.type === 'new_expression')
1955
+ ? 'constructor' : 'annotation', declarator),
1949
1956
  ...(pointeeType && { pointeeType }),
1950
1957
  declaredAt: node.type === 'parameter_declaration'
1951
1958
  ? scope.startIndex : declarator.startIndex,
@@ -2044,6 +2051,12 @@ function buildVariableTypes(tree) {
2044
2051
  return {
2045
2052
  get: (name, atNode) => resolveBinding(name, atNode)?.type,
2046
2053
  getStatic: (name, atNode) => resolveBinding(name, atNode)?.staticType,
2054
+ evidence: (name, atNode) => {
2055
+ const binding = resolveBinding(name, atNode);
2056
+ return { receiverTypeSource: binding?.origin?.source || 'unknown',
2057
+ ...(binding && { receiverTypeEvidence: { ...binding.origin, name, type: binding.type } }),
2058
+ };
2059
+ },
2047
2060
  getPointee: (name, atNode) =>
2048
2061
  resolveBinding(name, atNode)?.pointeeType,
2049
2062
  has: (name, atNode) => resolveBinding(name, atNode) !== undefined,
@@ -2501,7 +2514,7 @@ function findCallsInTree(code, parser, _options = {}, existingTree = null,
2501
2514
  }),
2502
2515
  ...(compileTimeOnly && { compileTimeOnly }),
2503
2516
  ...(macroArguments.length > 0 && { macroArguments }),
2504
- ...(directReceiverType && { receiverType: directReceiverType }),
2517
+ ...(directReceiverType && { receiverType: directReceiverType, ...variableTypes.evidence(receiverRoot, node) }),
2505
2518
  ...(receiverCall && {
2506
2519
  receiverCall,
2507
2520
  receiverIsChainRoot: true,
@@ -587,6 +587,9 @@ function enclosingClassName(node) {
587
587
  }
588
588
 
589
589
  /** Whether a bare identifier is a field/property/event of the enclosing type. */
590
+
591
+ const { ReceiverTypeMap, typeOrigin } = require('./type-evidence');
592
+
590
593
  function enclosingTypeDeclaresMember(node, memberName) {
591
594
  let typeNode = null;
592
595
  for (let parent = node?.parent; parent; parent = parent.parent) {
@@ -648,10 +651,10 @@ function variableScopeKey(node) {
648
651
  }
649
652
 
650
653
  function buildVariableTypes(tree, parser) {
651
- const byScope = new Map([['global', new Map()]]);
654
+ const byScope = new Map([['global', new ReceiverTypeMap()]]);
652
655
  const conflictsByScope = new Map([['global', new Set()]]);
653
656
  const scopeStack = [];
654
- const setType = (scope, name, type) => {
657
+ const setType = (scope, name, type, source = 'unknown', node = null) => {
655
658
  if (!name || !type) return;
656
659
  if (!conflictsByScope.has(scope)) conflictsByScope.set(scope, new Set());
657
660
  const conflicts = conflictsByScope.get(scope);
@@ -663,14 +666,14 @@ function buildVariableTypes(tree, parser) {
663
666
  conflicts.add(name);
664
667
  return;
665
668
  }
666
- types.set(name, type);
669
+ types.set(name, type, source, node);
667
670
  };
668
671
  traverseTree(tree.rootNode, node => {
669
672
  if (CALLABLE_SCOPE_NODES.has(node.type) &&
670
673
  !isControlFlowLocalArtifact(node)) {
671
674
  const key = node.startPosition.row + 1;
672
675
  scopeStack.push(key);
673
- if (!byScope.has(key)) byScope.set(key, new Map());
676
+ if (!byScope.has(key)) byScope.set(key, new ReceiverTypeMap());
674
677
  if (!conflictsByScope.has(key)) conflictsByScope.set(key, new Set());
675
678
  }
676
679
  const currentKey = scopeStack[scopeStack.length - 1] || 'global';
@@ -694,7 +697,7 @@ function buildVariableTypes(tree, parser) {
694
697
  const type =
695
698
  recoveredNode.childForFieldName('type')?.text ||
696
699
  recoveredNode.namedChild(0)?.text;
697
- setType(currentKey, name, type);
700
+ setType(currentKey, name, type, 'annotation', node);
698
701
  return true;
699
702
  });
700
703
  }
@@ -711,14 +714,14 @@ function buildVariableTypes(tree, parser) {
711
714
  if (artifactParameter) return true;
712
715
  const name = node.childForFieldName('name')?.text;
713
716
  const type = node.childForFieldName('type')?.text;
714
- setType(currentKey, name, type);
717
+ setType(currentKey, name, type, 'annotation', node);
715
718
  } else if (node.type === 'declaration_pattern' ||
716
719
  node.type === 'declaration_expression') {
717
720
  const name = node.childForFieldName('name')?.text ||
718
721
  node.namedChildren.at(-1)?.text;
719
722
  const type = node.childForFieldName('type')?.text ||
720
723
  node.namedChild(0)?.text;
721
- setType(currentKey, name, type);
724
+ setType(currentKey, name, type, 'annotation', node);
722
725
  } else if (node.type === 'variable_declaration') {
723
726
  // Class fields have their own declared-field receiver path; do not
724
727
  // leak them into the top-level-program local scope.
@@ -747,7 +750,7 @@ function buildVariableTypes(tree, parser) {
747
750
  const dynamicType = value?.type === 'object_creation_expression'
748
751
  ? value.childForFieldName('type')?.text : null;
749
752
  const type = dynamicType || (typeNode?.text !== 'var' ? typeNode?.text : null);
750
- setType(currentKey, name, type);
753
+ setType(currentKey, name, type, dynamicType ? 'constructor' : 'annotation', value || typeNode);
751
754
  }
752
755
  }
753
756
  return true;
@@ -1206,12 +1209,19 @@ function findCallsInCode(code, parser) {
1206
1209
  }
1207
1210
  }
1208
1211
  calls.push({
1212
+ callSite: typeOrigin('call', identity.nameNode || node),
1209
1213
  name: identity.name,
1210
1214
  line: identity.nameNode?.startPosition.row + 1 || node.startPosition.row + 1,
1211
1215
  isMethod: identity.isMethod,
1212
1216
  ...(identity.receiver && { receiver: identity.receiver }),
1213
1217
  ...(receiverIsTypeQualified && { receiverIsTypeQualified: true }),
1214
- ...(receiverType && { receiverType }),
1218
+ ...(receiverType && { receiverType, ...(unwrappedReceiverNode?.type === 'cast_expression' ? {
1219
+ receiverTypeSource: 'cast', receiverTypeEvidence: typeOrigin('cast', unwrappedReceiverNode),
1220
+ } : unwrappedReceiverNode?.type === 'object_creation_expression' ? {
1221
+ receiverTypeSource: 'constructor', receiverTypeEvidence: typeOrigin('constructor', unwrappedReceiverNode),
1222
+ } : literalReceiverType(unwrappedReceiverNode) ? {
1223
+ receiverTypeSource: 'literal', receiverTypeEvidence: typeOrigin('literal', unwrappedReceiverNode),
1224
+ } : variableTypes.fields(identity.receiver)) }),
1215
1225
  ...(receiverCastThis && { receiverCastThis: true }),
1216
1226
  ...(receiverType && receiverTypeInfo.namespace && {
1217
1227
  receiverTypeNamespace: receiverTypeInfo.namespace,
@@ -1255,6 +1265,7 @@ function findCallsInCode(code, parser) {
1255
1265
  }
1256
1266
  if (!callbackName) continue;
1257
1267
  calls.push({
1268
+ callSite: typeOrigin('call', value),
1258
1269
  name: callbackName,
1259
1270
  line: value.startPosition.row + 1,
1260
1271
  isMethod: !!callbackReceiver,
@@ -1274,6 +1285,7 @@ function findCallsInCode(code, parser) {
1274
1285
  const raw = typeNode.text.replace(/<.*>$/, '');
1275
1286
  const name = raw.split('.').pop();
1276
1287
  calls.push({
1288
+ callSite: typeOrigin('call', typeNode),
1277
1289
  name,
1278
1290
  line: typeNode.startPosition.row + 1,
1279
1291
  isMethod: false,