ucn 5.5.1 → 5.5.3

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 (97) hide show
  1. package/.claude/skills/ucn/SKILL.md +35 -9
  2. package/.claude/skills/ucn/references/commands.md +40 -18
  3. package/.claude/skills/ucn/references/resolution-rules.md +126 -20
  4. package/.claude/skills/ucn/references/trust-contract.md +5 -3
  5. package/README.md +32 -21
  6. package/cli/index.js +2 -1
  7. package/core/accessors.js +341 -54
  8. package/core/account.js +74 -20
  9. package/core/analysis.js +257 -125
  10. package/core/ast-analysis.js +12 -3
  11. package/core/bridge.js +15 -65
  12. package/core/brief.js +8 -4
  13. package/core/build-worker.js +2 -1
  14. package/core/cache.js +137 -6
  15. package/core/call-name-cache.js +134 -0
  16. package/core/callable-scope.js +21 -0
  17. package/core/callers.js +4726 -940
  18. package/core/check.js +22 -5
  19. package/core/class-identity.js +399 -12
  20. package/core/client-receivers.js +189 -0
  21. package/core/constructor-calls.js +831 -0
  22. package/core/constructor-hierarchy.js +147 -0
  23. package/core/constructor-protocol.js +196 -0
  24. package/core/contract-membership.js +346 -140
  25. package/core/cpp-conversion-constraints.js +123 -0
  26. package/core/cpp-conversion-syntax.js +216 -0
  27. package/core/cpp-conversions.js +414 -0
  28. package/core/cpp-scope.js +245 -44
  29. package/core/deadcode.js +61 -16
  30. package/core/decorator-binding.js +137 -3
  31. package/core/enum-reference-syntax.js +134 -0
  32. package/core/execute.js +17 -1
  33. package/core/external-macros.js +42 -10
  34. package/core/go-packages.js +171 -0
  35. package/core/graph-build.js +2 -0
  36. package/core/graph.js +75 -59
  37. package/core/ignore-rules.js +2 -1
  38. package/core/imports.js +145 -19
  39. package/core/index-ir.js +41 -4
  40. package/core/inprocess-clients.js +41 -15
  41. package/core/ir.js +27 -6
  42. package/core/macro-expansion.js +38 -2
  43. package/core/out-of-class-members.js +182 -0
  44. package/core/output/analysis-ext.js +4 -2
  45. package/core/output/analysis.js +89 -20
  46. package/core/output/graph.js +12 -3
  47. package/core/output/lines.js +4 -3
  48. package/core/output/public.js +2 -0
  49. package/core/output/reporting.js +12 -1
  50. package/core/output/shared.js +2 -0
  51. package/core/output/tracing.js +28 -22
  52. package/core/output-budget.js +2 -2
  53. package/core/parallel-build.js +52 -1
  54. package/core/project.js +217 -112
  55. package/core/provenance-constructor-base.js +161 -0
  56. package/core/provenance-facts.js +257 -27
  57. package/core/provenance-overload.js +458 -54
  58. package/core/provenance-type.js +487 -0
  59. package/core/provenance.js +234 -15
  60. package/core/public-command.js +17 -2
  61. package/core/query-bodies.js +159 -0
  62. package/core/reflection.js +270 -2
  63. package/core/rename-token-syntax.js +111 -0
  64. package/core/reporting.js +13 -7
  65. package/core/request-helpers.js +63 -7
  66. package/core/rust-macro-expansion.js +151 -19
  67. package/core/rust-modules.js +1 -1
  68. package/core/rust-names.js +607 -0
  69. package/core/search.js +70 -17
  70. package/core/shared.js +98 -1
  71. package/core/source-memo.js +201 -0
  72. package/core/text-usage-syntax.js +38 -0
  73. package/core/tracing.js +7 -1
  74. package/core/type-denotation.js +124 -37
  75. package/core/type-references.js +615 -246
  76. package/core/value-references.js +2777 -0
  77. package/core/verify.js +1097 -281
  78. package/languages/adapter.js +28 -4
  79. package/languages/c-family.js +1118 -164
  80. package/languages/constructor-bases.js +105 -0
  81. package/languages/cpp-platform.js +26 -0
  82. package/languages/cpp-site-syntax.js +83 -0
  83. package/languages/csharp-scope.js +439 -0
  84. package/languages/csharp.js +428 -68
  85. package/languages/go.js +385 -207
  86. package/languages/html.js +4 -2
  87. package/languages/index.js +397 -2
  88. package/languages/java-platform.js +226 -0
  89. package/languages/java-unicode.js +269 -0
  90. package/languages/java.js +461 -117
  91. package/languages/javascript.js +418 -66
  92. package/languages/lexical-scope.js +190 -23
  93. package/languages/python.js +404 -54
  94. package/languages/rust.js +510 -82
  95. package/languages/utils.js +67 -8
  96. package/mcp/server.js +8 -2
  97. package/package.json +3 -3
@@ -0,0 +1,831 @@
1
+ 'use strict';
2
+
3
+ const path = require('path');
4
+ const { langTraits } = require('../languages');
5
+ const { ownerDefinitionOf } = require('./class-identity');
6
+ const { declarationIdentity, identityKey, sameDeclaration, validateConfirmation } = require('./provenance');
7
+ const { captureOverload, projectFact, refusedProof } = require('./provenance-overload');
8
+ const { codeUnitCompare, NON_CALLABLE_TYPES } = require('./shared');
9
+
10
+ // Modules read lazily (load cycles), each resolved once: a require() per
11
+ // call in hot paths was measurable (fix #436).
12
+ let lazyProvenanceFactsModuleCache = null;
13
+ const lazyProvenanceFactsModule = () => lazyProvenanceFactsModuleCache || (lazyProvenanceFactsModuleCache = require('./provenance-facts'));
14
+ let lazyProvenanceTypeModuleCache = null;
15
+ const lazyProvenanceTypeModule = () => lazyProvenanceTypeModuleCache || (lazyProvenanceTypeModuleCache = require('./provenance-type'));
16
+
17
+ const isConstructor = (def, index) => !def?.modifiers?.includes('destructor') &&
18
+ (def?.type === 'constructor' && !def.modifiers?.includes('static') || !!(def?.className && index && !NON_CALLABLE_TYPES.has(def.type) &&
19
+ langTraits(index.files.get(def.file)?.language)?.constructorProtocol?.initializer === def.name));
20
+ const position = site => site.provenance?.facts?.site || site.callSite || site;
21
+ const siteKey = site => `${site.file}\0${site.line}\0${position(site).column ?? ''}`;
22
+ const sameSite = (call, site) => {
23
+ if (call.line !== site.line) return false;
24
+ const a = { start: call.callStart ?? call.callSite?.start, column: call.column ?? call.callSite?.column };
25
+ const b = position(site);
26
+ if (a.start != null && b.start != null) return a.start === b.start;
27
+ if (b.start != null && a.column == null) return false;
28
+ return a.column == null || b.column == null || a.column === b.column;
29
+ };
30
+ const order = (a, b) => codeUnitCompare(a.relativePath || a.file, b.relativePath || b.file) ||
31
+ a.line - b.line || (position(a).column ?? 0) - (position(b).column ?? 0);
32
+
33
+ // Call records of one line, in record order (fix #418: every projected site
34
+ // scanned all of its file's records).
35
+ function callsOnLine(index, file, line) {
36
+ const calls = index.getCachedCalls(file) || [];
37
+ const memo = index._opMemo?.('constructionCallsByLine', () => new Map());
38
+ let saved = memo?.get(file);
39
+ if (!saved || saved.calls !== calls) {
40
+ const byLine = new Map();
41
+ for (const call of calls) {
42
+ let list = byLine.get(call.line);
43
+ if (!list) byLine.set(call.line, list = []);
44
+ list.push(call);
45
+ }
46
+ saved = { calls, byLine };
47
+ memo?.set(file, saved);
48
+ }
49
+ return saved.byLine.get(line) || [];
50
+ }
51
+
52
+ function ownMembers(index, owner) {
53
+ // One owner-identity scan of the file per class and operation (fix #418:
54
+ // every construction site of a class rescanned its whole file). A
55
+ // witness view records the scan's reads itself.
56
+ const memo = index.isProvenanceFactIndex ? null : index._opMemo?.('constructorOwnMembers', () => new WeakMap());
57
+ const saved = memo?.get(owner);
58
+ const symbols = index.files.get(owner.file)?.symbols || [];
59
+ if (saved && saved.symbols === symbols) return saved.members;
60
+ // The parts of a C# partial type are one type (fix #430): a part
61
+ // without constructors (`partial class JValue` in JValue.Async.cs) has
62
+ // the constructors the other parts declare.
63
+ const parts = lazyProvenanceFactsModule().partialTypeParts(index, owner) || [owner];
64
+ const members = parts.flatMap(part => {
65
+ const identity = declarationIdentity(part);
66
+ return (index.files.get(part.file)?.symbols || []).filter(def =>
67
+ sameDeclaration(declarationIdentity(ownerDefinitionOf(index, def)), identity));
68
+ });
69
+ memo?.set(owner, { symbols, members });
70
+ return members;
71
+ }
72
+
73
+ function ownConstructors(index, owner) { return ownMembers(index, owner).filter(def => isConstructor(def, index)); }
74
+
75
+ // The constructor a type's header declares without a body declaration of
76
+ // its own (fix #419): a C# primary constructor, a Java record's implicit
77
+ // canonical constructor (parser `primaryParams`). It is a member of the
78
+ // constructor family that overload selection can choose, never a target:
79
+ // a construction it receives is a class dependency only. Its identity
80
+ // spans the type's declaration; the binding id keeps it apart from a
81
+ // declared constructor on the same lines.
82
+ const HEADER_CONSTRUCTORS = new WeakMap();
83
+ function headerConstructor(owner, declared) {
84
+ if (!Array.isArray(owner?.primaryParams) || !declared?.length) return null;
85
+ const saved = HEADER_CONSTRUCTORS.get(owner);
86
+ if (saved && saved.params === owner.primaryParams && saved.sibling === declared[0]) return saved.definition;
87
+ const sibling = declared[0];
88
+ const definition = {
89
+ name: owner.name, type: 'constructor', className: owner.name, file: owner.file,
90
+ relativePath: owner.relativePath, startLine: owner.startLine, endLine: owner.endLine,
91
+ ...(sibling.namespace && { namespace: sibling.namespace }),
92
+ ...(sibling.enclosingType && { enclosingType: sibling.enclosingType }),
93
+ ...(sibling.lexicalScopeStartLine && { lexicalScopeStartLine: sibling.lexicalScopeStartLine,
94
+ lexicalScopeEndLine: sibling.lexicalScopeEndLine }),
95
+ params: owner.primaryParams.map(param => [param.type, param.name].filter(Boolean).join(' ')).join(', '),
96
+ paramsStructured: owner.primaryParams,
97
+ modifiers: (owner.modifiers || []).filter(modifier => ACCESS_MODIFIERS.has(modifier)),
98
+ isConstructor: true, headerConstructor: true,
99
+ bindingId: `${owner.relativePath}:constructor:${owner.startLine}:header`,
100
+ };
101
+ HEADER_CONSTRUCTORS.set(owner, { params: owner.primaryParams, sibling, definition });
102
+ return definition;
103
+ }
104
+ const ACCESS_MODIFIERS = new Set(['public', 'protected', 'private', 'internal']);
105
+
106
+ // The constructor a `this(...)` delegation runs in: the declared
107
+ // constructor of the owner whose source extent holds the site. A
108
+ // constructor never delegates to itself (Java JLS 8.8.7, C# CS0516, C++
109
+ // [class.base.init]), so it leaves the candidate family (fix #419). A site
110
+ // in an out-of-class definition runs in the declaration it defines (fix
111
+ // #421): `{ member, definition }` names both.
112
+ function delegatingConstructor(index, owner, declared, call, file) {
113
+ const start = call.callStart ?? call.callSite?.start;
114
+ const sameFile = def => !file || def.file === file;
115
+ const holding = declared.filter(def => sameFile(def) && (Number.isFinite(start) && def.sourceSpan
116
+ ? def.sourceSpan.start <= start && start < def.sourceSpan.end
117
+ : def.startLine === call.enclosingFunction?.startLine && def.endLine === call.enclosingFunction?.endLine));
118
+ if (holding.length === 1) return { member: holding[0] };
119
+ if (holding.length || !file) return null;
120
+ const { enclosingDefinition, declarationOf } = require('./out-of-class-members');
121
+ const definition = enclosingDefinition(index, file, call, owner);
122
+ const member = definition && declarationOf(index, definition);
123
+ return member && declared.includes(member) ? { member, definition } : null;
124
+ }
125
+
126
+ // These spellings only select candidates. Every site must replay the
127
+ // language's value/type binding before it can reach a class definition.
128
+ function constructionAliases(index) {
129
+ const build = () => {
130
+ const aliases = new Map();
131
+ const add = (name, target) => {
132
+ if (!name || !target) return;
133
+ let plain = '', depth = 0;
134
+ for (const char of target) {
135
+ if (char === '<') depth++;
136
+ else if (char === '>') depth--;
137
+ else if (depth === 0) plain += char;
138
+ }
139
+ const terminal = plain.split('::').pop().split('.').pop().trim();
140
+ if (!aliases.has(name)) aliases.set(name, new Set());
141
+ aliases.get(name).add(terminal);
142
+ };
143
+ for (const [, entry] of index.files) {
144
+ for (const alias of entry.moduleValueAliases || []) {
145
+ add(alias.name, alias.target);
146
+ for (const alternative of alias.alternatives || []) add(alias.name, alternative.target || alternative.imported);
147
+ }
148
+ for (const def of entry.symbols || []) if (def.type === 'type') add(def.name, def.aliasOf);
149
+ for (const binding of entry.importBindings || []) {
150
+ if (binding.kind === 'using' && !binding.alias) add(binding.name, binding.module);
151
+ else if (binding.alias) add(binding.alias, binding.name);
152
+ }
153
+ }
154
+ return aliases;
155
+ };
156
+ // Only indexed declarations and import bindings contribute; rebuild and
157
+ // incremental updates invalidate this inventory. Witnesses read their facts.
158
+ if (index.isProvenanceFactIndex) return build();
159
+ return index._constructionAliases || (index._constructionAliases = build());
160
+ }
161
+
162
+ // The type names a spelling can reach through the alias dictionary. A
163
+ // witness records this closure as one fact rather than every file the
164
+ // dictionary scans (fix #418: a scan per witness was quadratic).
165
+ function constructionTypeNames(index, name) {
166
+ const memo = index.isProvenanceFactIndex ? null : index._opMemo?.('constructionTypeNames');
167
+ const saved = memo?.get(name);
168
+ if (saved) return saved.slice();
169
+ const aliases = constructionAliases(index);
170
+ const names = new Set([name]);
171
+ for (let round = 0; round < 12; round++) {
172
+ const size = names.size;
173
+ for (const current of [...names]) for (const target of aliases.get(current) || []) names.add(target);
174
+ if (size === names.size) break;
175
+ }
176
+ const result = [...names];
177
+ memo?.set(name, result);
178
+ return result.slice();
179
+ }
180
+
181
+ // Every spelling whose alias closure (`constructionTypeNames`) holds one of
182
+ // `typeNames`: the closure read backwards, to the same depth.
183
+ function constructionSpellingsReaching(index, typeNames) {
184
+ const aliases = constructionAliases(index);
185
+ const reverse = new Map();
186
+ for (const [spelling, targets] of aliases) {
187
+ for (const target of targets) {
188
+ if (!reverse.has(target)) reverse.set(target, []);
189
+ reverse.get(target).push(spelling);
190
+ }
191
+ }
192
+ const reached = new Set(typeNames);
193
+ let frontier = [...reached];
194
+ for (let round = 0; round < 12 && frontier.length; round++) {
195
+ const next = [];
196
+ for (const current of frontier) {
197
+ for (const spelling of reverse.get(current) || []) {
198
+ if (!reached.has(spelling)) { reached.add(spelling); next.push(spelling); }
199
+ }
200
+ }
201
+ frontier = next;
202
+ }
203
+ return reached;
204
+ }
205
+
206
+ function constructionTypeCandidates(index, name) {
207
+ return projectFact(index, `construction-type-names:${name}`, live => constructionTypeNames(live, name))
208
+ .flatMap(current => index.symbols.get(current) || [])
209
+ .filter(def => ['class', 'struct', 'record', 'enum', 'interface'].includes(def.type));
210
+ }
211
+
212
+ function constructionAliasNames(index, targets) {
213
+ const names = new Set(targets.filter(def => ['class', 'struct', 'record', 'enum'].includes(def.type) &&
214
+ langTraits(index.files.get(def.file)?.language)?.hasConstructors).map(def => def.name));
215
+ if (!names.size) return new Set();
216
+ // Targets with the same constructor-capable names have the same alias
217
+ // surfaces. Binding to a particular definition is still proved later.
218
+ const memo = index.isProvenanceFactIndex ? null : index._opMemo?.('constructionAliasNames');
219
+ const key = JSON.stringify([...names].sort(codeUnitCompare));
220
+ const saved = memo?.get(key);
221
+ if (saved) return new Set(saved);
222
+ const aliases = constructionAliases(index), surfaces = new Set();
223
+ for (let round = 0; round < 12; round++) {
224
+ let changed = false;
225
+ for (const [name, spelled] of aliases) {
226
+ if (!surfaces.has(name) && [...spelled].some(target => names.has(target))) {
227
+ names.add(name); surfaces.add(name); changed = true;
228
+ }
229
+ }
230
+ if (!changed) break;
231
+ }
232
+ memo?.set(key, surfaces);
233
+ return new Set(surfaces);
234
+ }
235
+
236
+ function constructionAliasPossibilities(index, file, call) {
237
+ const language = index.files.get(file)?.language, traits = langTraits(language);
238
+ if (!traits?.hasConstructors || !isConstruction(call, null, language) || call.localShadow ||
239
+ call.isMethod && !call.receiverIsModule && !call.isConstructor && !(call.isPathCall && traits.functionalTypeConstruction) ||
240
+ !constructionAliases(index).has(call.name)) return [];
241
+ const family = traits.nameReferenceFamily || language;
242
+ const candidates = constructionTypeCandidates(index, call.name).filter(def =>
243
+ (langTraits(index.files.get(def.file)?.language)?.nameReferenceFamily || index.files.get(def.file)?.language) === family);
244
+ if (!candidates.length) return [];
245
+ if (traits.functionalTypeConstruction) {
246
+ const { TypeReferenceResolver } = require('./type-references');
247
+ const { resolveQualifier, includeClosure } = require('./cpp-scope');
248
+ const context = new TypeReferenceResolver(index, candidates[0], candidates[0].name).cppSiteContext(file, call.line);
249
+ const qualifier = call.constructorQualifier || call.receiver;
250
+ const found = resolveQualifier(index, { file, line: call.line, ...context },
251
+ qualifier ? `${qualifier}::${call.name}` : call.name, includeClosure(index, file), 0, { declarations: true });
252
+ if (found.kind === 'namespace' || found.kind === 'type' && !found.visible ||
253
+ found.kind === 'unknown' && ['no-declaration', 'not-visible'].includes(found.why)) return [];
254
+ } else if (traits.classesCallableWithoutNew && !valueAliasInScope(index, file, call.name, call.receiver)) return [];
255
+ return candidates;
256
+ }
257
+
258
+ // The project-wide alias dictionary discovers spellings, not bindings. A
259
+ // module's own values and imports decide whether this call can use one.
260
+ function valueAliasInScope(index, file, name, qualifier, seen = new Set()) {
261
+ const key = `${file}\0${qualifier || ''}\0${name}`;
262
+ if (seen.has(key) || seen.size >= 12) return true;
263
+ const entry = index.files.get(file);
264
+ if (!entry) return true;
265
+ const visited = new Set(seen).add(key);
266
+ if (!qualifier && ((entry.moduleValueAliases || []).some(alias => alias.name === name) ||
267
+ (entry.symbols || []).some(def => def.name === name && !def.className &&
268
+ ['class', 'struct', 'record', 'enum', 'type'].includes(def.type)))) return true;
269
+ const parts = qualifier?.split('.') || [];
270
+ const bound = parts.shift() || name;
271
+ const links = (entry.importBindings || []).filter(binding => (binding.alias || binding.name) === bound);
272
+ if (!links.length) return false;
273
+ return links.some(binding => {
274
+ const relative = entry.moduleResolved?.[`${binding.module}.${binding.name}`] || entry.moduleResolved?.[binding.module];
275
+ if (!relative) return true;
276
+ const target = path.resolve(index.root, relative);
277
+ return valueAliasInScope(index, target, qualifier ? name : binding.name,
278
+ parts.join('.') || null, visited);
279
+ });
280
+ }
281
+
282
+ function constructionAliasBinding(index, file, call, targets) {
283
+ const language = index.files.get(file)?.language;
284
+ // An expansion's argument token can declare a constructor as well as
285
+ // supply a call. Its ordinary source type lookup does not prove which
286
+ // role the expanded record has; retain the expansion's existing path.
287
+ const functional = langTraits(language)?.functionalTypeConstruction && !call.macroExpansion;
288
+ if (!langTraits(language)?.hasConstructors || !isConstruction(call, null, language) || call.localShadow || call.isMethod && !call.receiverIsModule &&
289
+ !call.isConstructor && !(call.isPathCall && langTraits(language)?.functionalTypeConstruction) ||
290
+ !constructionAliases(index).has(call.name) && !call.constructorQualifier && !call.implicitBase &&
291
+ !call.isConstructor && !functional) return null;
292
+ let candidates = targets;
293
+ if (!candidates) {
294
+ candidates = constructionTypeCandidates(index, call.name);
295
+ } else {
296
+ // Only a type the call's spelling can name (its own name, or one an
297
+ // alias of it reaches) can be the construction's class (fix #421: a
298
+ // type query looked every call record of a file up at its site).
299
+ const spelled = projectFact(index, `construction-type-names:${call.name}`, live => constructionTypeNames(live, call.name));
300
+ candidates = candidates.filter(def => spelled.includes(def.name));
301
+ }
302
+ if (!candidates.length) return null;
303
+ if (call.implicitBase) return require('./provenance-constructor-base').implicitBaseBinding(index, file, call, candidates);
304
+ const { occurrenceIdentity } = lazyProvenanceFactsModule();
305
+ const site = occurrenceIdentity(index.files.get(file).relativePath, call);
306
+ const query = { file, relativePath: site.file, line: site.line, column: site.column,
307
+ name: call.name, qualifier: call.constructorQualifier || call.receiver || null, localShadow: !!call.localShadow,
308
+ construction: true, functional: !call.isConstructor && !!langTraits(language)?.functionalTypeConstruction };
309
+ if (langTraits(language)?.functionalTypeConstruction) {
310
+ try {
311
+ // The witness replays this same lookup. Other owner identities
312
+ // cannot satisfy it and need no separate capture attempt.
313
+ const bound = lazyProvenanceTypeModule().cppConstructionClasses(index, query);
314
+ candidates = candidates.filter(owner => bound.length > 0 && bound.every(def =>
315
+ sameDeclaration(declarationIdentity(def), declarationIdentity(owner))));
316
+ } catch (error) {
317
+ // A refused optional lookup leaves the ordinary proof attempts.
318
+ refusedProof(error);
319
+ }
320
+ }
321
+ const family = langTraits(language)?.nameReferenceFamily || language;
322
+ const found = candidates.filter(def => ['class', 'struct', 'record', 'enum'].includes(def.type) &&
323
+ (langTraits(index.files.get(def.file)?.language)?.nameReferenceFamily || index.files.get(def.file)?.language) === family).map(owner => ({
324
+ owner, witness: lazyProvenanceTypeModule().captureTypeLookup(index, query, owner),
325
+ })).filter(entry => entry.witness);
326
+ if (found.length !== 1) return null;
327
+ const { owner, witness } = found[0];
328
+ const facts = { language, site, targets: [declarationIdentity(owner)], constructionBinding: witness };
329
+ return { owner, provenance: { rule: 'construction-type', rules: ['construction-type'], facts } };
330
+ }
331
+
332
+ const OWN_CLASS_KINDS = new Set(['class', 'struct', 'record']);
333
+ const namedAt = (def, line) => def.nameLine === line || def.startLine === line;
334
+
335
+ /**
336
+ * The class a construction through a method's own receiver builds (fix
337
+ * #434): `cls(..)` in a Python classmethod, `type(self)(..)`,
338
+ * `self.__class__(..)`, JS `new this(..)` in a static method and `new
339
+ * this.constructor(..)`. The site builds the method's class, or a subclass
340
+ * of it when the method runs through one; the witness replays the method's
341
+ * class from the recorded declarations.
342
+ */
343
+ function ownClassBinding(index, file, call) {
344
+ const own = call.ownClass;
345
+ const entry = index.files.get(file);
346
+ if (!own || !entry || !langTraits(entry.language)?.ownClassMethod) return null;
347
+ const owner = (entry.symbols || []).find(def => def.name === own.name && OWN_CLASS_KINDS.has(def.type) &&
348
+ namedAt(def, own.line));
349
+ if (!owner) return null;
350
+ const { occurrenceIdentity } = lazyProvenanceFactsModule();
351
+ const site = occurrenceIdentity(entry.relativePath, call);
352
+ const witness = lazyProvenanceTypeModule().captureTypeLookup(index, { file, relativePath: site.file,
353
+ line: site.line, column: site.column, name: call.name, ownClass: own, construction: true }, owner);
354
+ if (!witness) return null;
355
+ return { owner, provenance: { rule: 'construction-type', rules: ['construction-type'],
356
+ facts: { language: entry.language, site, targets: [declarationIdentity(owner)], constructionBinding: witness } } };
357
+ }
358
+
359
+ // Ancestors of the owners by resolved base identity, same name family, in
360
+ // breadth-first order (fix #434).
361
+ function resolvedAncestors(index, owners) {
362
+ const { classDefsNamed, parentRefsOf } = require('./class-identity');
363
+ const keyOf = def => classDefsNamed(index, def.name).entries.find(entry => entry.def === def)?.key || null;
364
+ const seen = new Set(owners.map(def => identityKey(declarationIdentity(def))));
365
+ const out = [];
366
+ const queue = owners.map(def => ({ name: def.name, key: keyOf(def), def }));
367
+ while (queue.length && out.length < 64) {
368
+ const ref = queue.shift();
369
+ if (!ref.key) continue;
370
+ for (const parent of parentRefsOf(index, ref)) {
371
+ if (parent.external || !parent.key) continue;
372
+ const def = classDefsNamed(index, parent.name).entries.find(entry => entry.key === parent.key)?.def;
373
+ if (!def || nameFamily(index, def) !== nameFamily(index, ref.def)) continue;
374
+ const id = identityKey(declarationIdentity(def));
375
+ if (seen.has(id)) continue;
376
+ seen.add(id);
377
+ out.push(def);
378
+ queue.push({ name: def.name, key: parent.key, def });
379
+ }
380
+ }
381
+ return out;
382
+ }
383
+
384
+ function isConstruction(call, site, language) {
385
+ return !site?.isFunctionReference && !site?.isTypeReference && !call.isFunctionReference &&
386
+ !call.isPotentialCallback && !call.isTypeReference && (call.isConstructor ||
387
+ langTraits(language)?.classesCallableWithoutNew || langTraits(language)?.functionalTypeConstruction);
388
+ }
389
+
390
+ function constructionTypeParameter(index, file, call) {
391
+ const language = index.files.get(file)?.language;
392
+ if (!langTraits(language)?.hasConstructors || langTraits(language)?.typeSystem !== 'nominal' ||
393
+ !isConstruction(call, null, language) || ['implicit', 'delegation', 'enum'].includes(call.constructorSyntax)) return false;
394
+ if (!call.isConstructor && !constructionTypeCandidates(index, call.name).length) return false;
395
+ const site = lazyProvenanceFactsModule().occurrenceIdentity(index.files.get(file).relativePath, call);
396
+ return lazyProvenanceTypeModule().constructionNameScope(index, { file, line: site.line, column: site.column,
397
+ name: call.name, qualifier: call.constructorQualifier || call.receiver || null }) === false;
398
+ }
399
+
400
+ function protocolFacts(index, owner) {
401
+ const memo = index._opMemo?.('constructorProtocolFacts', () => new WeakMap());
402
+ if (memo?.has(owner)) return memo.get(owner);
403
+ const facts = require('./constructor-protocol').collectConstructorProtocol(index, owner);
404
+ memo?.set(owner, facts);
405
+ return facts;
406
+ }
407
+
408
+ // The construction's target-independent evidence: the constructor family,
409
+ // its overload witness and the site's type and name-scope witnesses. A
410
+ // query over several constructors of one class reads each site once per
411
+ // operation (fix #418); every witness is replayed against each target set.
412
+ function constructionEvidence(index, owner, call, classBinding) {
413
+ const memo = classBinding && typeof classBinding === 'object' && call && typeof call === 'object' &&
414
+ !index.isProvenanceFactIndex ? index._opMemo?.('constructionEvidence', () => new WeakMap()) : null;
415
+ let byCall = memo?.get(classBinding);
416
+ if (memo && !byCall) memo.set(classBinding, byCall = new WeakMap());
417
+ let byOwner = byCall?.get(call);
418
+ if (byCall && !byOwner) byCall.set(call, byOwner = new Map());
419
+ const key = byOwner ? identityKey(declarationIdentity(owner)) : null;
420
+ if (key !== null && byOwner.has(key)) return byOwner.get(key);
421
+ const evidence = constructionEvidenceUncached(index, owner, call, classBinding);
422
+ if (key !== null) byOwner.set(key, evidence);
423
+ return evidence;
424
+ }
425
+
426
+ function constructionEvidenceUncached(index, owner, call, classBinding) {
427
+ let members = ownConstructors(index, owner), declaring = owner, hierarchy = null, delegating = null,
428
+ delegatingDefinition = null;
429
+ if (!members.length) {
430
+ const inherited = require('./constructor-hierarchy').captureHierarchy(index, owner);
431
+ if (!inherited) return { unresolved: true };
432
+ if (inherited.none) {
433
+ // No class of the order declares a constructor. Only an
434
+ // allocation protocol the language checks can still run project
435
+ // code; a construction then depends on its class alone.
436
+ const language = index.files.get(owner.file).language;
437
+ const wrapped = langTraits(language)?.decoratorsWrapCallables && owner.decorators?.length;
438
+ const protocol = langTraits(language)?.constructorProtocol;
439
+ const plain = !wrapped && (!protocol || require('./constructor-protocol').validateConstructorProtocol(
440
+ protocolFacts(index, owner), declarationIdentity(owner), [], language));
441
+ return plain ? { noBody: true } : { unresolved: true };
442
+ }
443
+ members = inherited.members; declaring = inherited.declaring; hierarchy = inherited.witness;
444
+ } else {
445
+ const header = headerConstructor(owner, members);
446
+ if (call.ctorDelegation === 'this') {
447
+ // A same-type delegation runs in one of the owner's own
448
+ // constructors; any other enclosing scope means the site does
449
+ // not belong to this owner's family.
450
+ const siteFile = classBinding?.facts?.site?.file;
451
+ const found = delegatingConstructor(index, owner, members, call,
452
+ siteFile ? path.resolve(index.root, siteFile) : null);
453
+ if (!found) return { unresolved: true, delegation: true };
454
+ delegating = found.member;
455
+ delegatingDefinition = found.definition || null;
456
+ members = members.filter(def => def !== delegating);
457
+ }
458
+ if (header) members = [...members, header];
459
+ if (!members.length) return { unresolved: true, delegation: true, delegating, delegatingDefinition };
460
+ }
461
+ const language = index.files.get(owner.file).language;
462
+ const protocol = protocolFacts(index, owner);
463
+ const { selectProvenanceOverload } = require('./callers');
464
+ const overloadCall = langTraits(language)?.inheritedOverloadSignatures ||
465
+ langTraits(language)?.userDefinedConversions === 'constructors-and-operators'
466
+ ? { ...call, argumentFile: classBinding?.facts?.site?.file || call.argumentFile } : call;
467
+ const overload = captureOverload(index, overloadCall, members, language, selectProvenanceOverload);
468
+ const selected = overload?.selected;
469
+ const fallback = !selected && members.length === 1 ? declarationIdentity(members[0]) : null;
470
+ const chosen = selected || fallback;
471
+ const site = classBinding?.facts?.site;
472
+ const functional = !call.isConstructor && langTraits(language)?.functionalTypeConstruction;
473
+ const nameScope = langTraits(language)?.typeSystem === 'nominal' &&
474
+ !['implicit', 'delegation', 'enum'].includes(call.constructorSyntax) && site
475
+ ? lazyProvenanceTypeModule().captureTypeLookup(index, {
476
+ file: path.resolve(index.root, site.file), relativePath: site.file,
477
+ line: site.line, column: site.column, name: call.name,
478
+ qualifier: call.constructorQualifier || call.receiver || null, nameScopeOnly: true,
479
+ }, owner) : null;
480
+ const typeLookup = call.ownClass && site
481
+ ? lazyProvenanceTypeModule().captureTypeLookup(index, {
482
+ file: path.resolve(index.root, site.file), relativePath: site.file,
483
+ line: site.line, column: site.column, name: call.name, ownClass: call.ownClass, construction: true,
484
+ }, owner)
485
+ : (call.isConstructor || functional || langTraits(language)?.classesCallableWithoutNew) && site &&
486
+ (functional || langTraits(language)?.typeSystem === 'structural' ||
487
+ validateConfirmation(classBinding, declarationIdentity(owner)).verdict !== 'establishes-target')
488
+ ? lazyProvenanceTypeModule().captureTypeLookup(index, {
489
+ file: path.resolve(index.root, site.file), relativePath: site.file,
490
+ line: site.line, column: site.column, name: call.name,
491
+ qualifier: call.constructorQualifier || call.receiver || null, functional: !!functional, construction: true,
492
+ localShadow: !!call.localShadow,
493
+ }, owner) : null;
494
+ return { members, declaring, hierarchy, delegating, delegatingDefinition, language, protocol, overload, selected,
495
+ chosen, nameScope, typeLookup };
496
+ }
497
+
498
+ // The out-of-class definition a delegation runs in and the signature it
499
+ // shares with its in-class declaration (fix #421): the replay checks that
500
+ // the site lies in that definition and that the two declare one constructor.
501
+ function delegatingDefinitionFacts(evidence) {
502
+ const { _cFamilySignatureKey } = require('./callers');
503
+ return { delegatingDefinition: { ...declarationIdentity(evidence.delegatingDefinition),
504
+ signature: _cFamilySignatureKey(evidence.delegatingDefinition) },
505
+ delegatingSignature: _cFamilySignatureKey(evidence.delegating) };
506
+ }
507
+
508
+ function constructionChoice(index, owner, call, classBinding, targets) {
509
+ const evidence = constructionEvidence(index, owner, call, classBinding);
510
+ if (evidence.noBody) return { noBody: true };
511
+ if (evidence.unresolved) {
512
+ const facts = { language: index.files.get(owner.file)?.language, site: classBinding?.facts?.site,
513
+ targets: (targets || []).map(declarationIdentity) };
514
+ // The delegating constructor is never its own delegation's target,
515
+ // even when no other member of the family remains (fix #419).
516
+ if (evidence.delegating) {
517
+ const parts = lazyProvenanceFactsModule().partialTypeParts(index, owner);
518
+ facts.constructorCall = { owner: declarationIdentity(owner), members: [],
519
+ ...(parts && { parts: parts.map(declarationIdentity) }),
520
+ delegating: declarationIdentity(evidence.delegating),
521
+ ...(evidence.delegatingDefinition && delegatingDefinitionFacts(evidence)), classBinding,
522
+ call: { isConstructor: !!call.isConstructor, syntax: call.constructorSyntax || null, delegation: 'this' } };
523
+ const provenance = { rule: 'constructor-call', rules: ['constructor-call'], facts };
524
+ if (targets && validateConfirmation(provenance, targets.map(declarationIdentity)).verdict === 'establishes-other') {
525
+ return { other: true, reason: 'constructor-delegation-self', provenance };
526
+ }
527
+ }
528
+ return { reason: evidence.delegation ? 'constructor-delegation-unresolved' : 'constructor-inheritance-unresolved',
529
+ provenance: { rule: 'constructor-call', rules: ['constructor-call'], facts } };
530
+ }
531
+ const { members, declaring, hierarchy, delegating, delegatingDefinition, language, protocol, overload, selected, chosen,
532
+ nameScope, typeLookup } = evidence;
533
+ targets ||= members;
534
+ const parts = lazyProvenanceFactsModule().partialTypeParts(index, declaring);
535
+ const facts = { language, site: classBinding?.facts?.site, targets: targets.map(declarationIdentity),
536
+ constructorCall: { owner: declarationIdentity(declaring), members: members.map(declarationIdentity),
537
+ ...(parts && { parts: parts.map(declarationIdentity) }),
538
+ ...(hierarchy && { constructedOwner: declarationIdentity(owner), hierarchy }),
539
+ ...(delegating && { delegating: declarationIdentity(delegating) }),
540
+ ...(delegatingDefinition && delegatingDefinitionFacts(evidence)),
541
+ classBinding, call: { isConstructor: !!call.isConstructor,
542
+ isFunctionReference: !!call.isFunctionReference, isPotentialCallback: !!call.isPotentialCallback,
543
+ syntax: call.constructorSyntax || null, ...(delegating && { delegation: 'this' }) },
544
+ selected: chosen, overload, protocol, typeLookup, nameScope } };
545
+ const provenance = { rule: 'constructor-call', rules: ['constructor-call'], facts };
546
+ let checked = chosen && validateConfirmation(provenance, targets.map(declarationIdentity));
547
+ if (!chosen && delegating) {
548
+ const self = validateConfirmation(provenance, targets.map(declarationIdentity));
549
+ if (self.diagnostic === 'constructor-delegation-self') checked = self;
550
+ }
551
+ if (checked?.verdict === 'establishes-other') {
552
+ return { other: true, ...(checked.diagnostic === 'constructor-delegation-self' && { reason: checked.diagnostic }), provenance };
553
+ }
554
+ const wrappedClass = langTraits(language)?.decoratorsWrapCallables && owner.decorators?.length;
555
+ const reason = checked?.diagnostic === 'constructor-default-unresolved' ? 'constructor-default-unresolved'
556
+ : checked?.diagnostic === 'unproven-construction-name-scope' ? 'constructor-name-scope-unresolved'
557
+ : wrappedClass || checked?.diagnostic === 'constructor-protocol-unresolved' ? 'constructor-protocol-unresolved'
558
+ : checked?.diagnostic === 'constructor-definition-ambiguous' ? 'constructor-definition-ambiguous'
559
+ : checked?.diagnostic === 'constructor-functional-binding' ? 'constructor-functional-binding'
560
+ : overload?.outcome === 'no-fit' ? 'constructor-overload-no-fit'
561
+ : overload?.outcome === 'ambiguous' ? 'constructor-overload-ambiguous'
562
+ : checked?.verdict !== 'establishes-target' ? 'constructor-proof-incomplete' : null;
563
+ provenance.validation = checked?.verdict || 'incomplete';
564
+ if (checked?.diagnostic) provenance.diagnostic = checked.diagnostic;
565
+ // An undecided overload choice whose arguments no pinned constructor
566
+ // can take by count runs another constructor (fix #430: a one-argument
567
+ // `new JValue(x)` never runs `JValue(object?, JTokenType)`).
568
+ // A C++ braced list may run an initializer-list constructor whatever its
569
+ // element count, so only parenthesized and `new T(..)` arguments count.
570
+ if (reason === 'constructor-overload-ambiguous' && langTraits(language)?.hasArityOverloads &&
571
+ call.constructorSyntax !== 'list' && !langTraits(language)?.packExpansionArguments &&
572
+ Number.isInteger(call.argCount) && !call.argSpread && targets.length > 0 &&
573
+ targets.every(target => isConstructor(target, index) && !target.headerConstructor &&
574
+ !require('./callers')._callArityCompatible(call, [target], language))) {
575
+ return { other: true, reason: 'constructor-overload-other', provenance };
576
+ }
577
+ return { members, definition: selected && members.find(def => sameDeclaration(selected, declarationIdentity(def))),
578
+ provenance, reason };
579
+ }
580
+
581
+ // Syntax whose site runs a constructor body without constructing the class
582
+ // at a source spelling of its own: a delegation (`this(..)`, `super(..)`,
583
+ // a mem-initializer), an omitted base initializer, an enum constant.
584
+ const BODY_ONLY_SYNTAX = new Set(['delegation', 'implicit', 'enum']);
585
+
586
+ const groupOrder = (a, b) => codeUnitCompare(a.definition.relativePath, b.definition.relativePath) ||
587
+ a.definition.startLine - b.definition.startLine ||
588
+ (a.definition.sourceSpan?.start ?? Infinity) - (b.definition.sourceSpan?.start ?? Infinity) ||
589
+ codeUnitCompare(identityKey(declarationIdentity(a.definition)), identityKey(declarationIdentity(b.definition)));
590
+
591
+ /**
592
+ * The constructor bodies a class callee's sites run (fix #421). A
593
+ * construction (`new C(..)`, `C(..)`) stays a dependency on the class,
594
+ * certain whenever its class binding is: its proven constructor joins as an
595
+ * initializer edge (`initializers`), and an undecided one is a review of the
596
+ * class edge (`initializerReviews`), never a demotion of it. A delegation,
597
+ * an omitted base initializer or an enum constant runs a constructor body
598
+ * only: its sites leave the class edge (`groups`, `unverified`).
599
+ */
600
+ function projectConstructorCalleeSites(index, owner, siteIds, readSite) {
601
+ if (!siteIds?.length || !ownConstructors(index, owner).length &&
602
+ !langTraits(index.files.get(owner.file)?.language)?.constructorInheritance) return null;
603
+ const language = index.files.get(owner.file)?.language;
604
+ const remaining = [], unverified = [], groups = new Map(), initializers = new Map(), initializerReviews = [];
605
+ for (const siteId of siteIds) {
606
+ const { call, site } = readSite(siteId);
607
+ if (!isConstruction(call, site, language)) { remaining.push(siteId); continue; }
608
+ const bodyOnly = BODY_ONLY_SYNTAX.has(call.constructorSyntax);
609
+ if (!bodyOnly) remaining.push(siteId);
610
+ const choice = constructionChoice(index, owner, call, site.provenance);
611
+ // The type header's own constructor has no declaration (fix #419),
612
+ // and a class whose whole order declares none runs no project body
613
+ // (fix #421): a construction remains a dependency on the class.
614
+ if (choice.noBody && !bodyOnly) continue;
615
+ if (!choice.reason && choice.definition?.headerConstructor) {
616
+ if (bodyOnly) remaining.push(siteId);
617
+ continue;
618
+ }
619
+ if (choice.reason || !choice.definition) {
620
+ const reason = choice.reason || 'constructor-proof-incomplete';
621
+ if (!bodyOnly) {
622
+ initializerReviews.push({ siteId, call, reason, provenance: choice.provenance });
623
+ continue;
624
+ }
625
+ // A Python class call still depends on its proven class binding
626
+ // when its allocator/decorator protocol cannot select a body.
627
+ // The constructor-handle query retains that uncertainty.
628
+ if (['constructor-protocol-unresolved', 'constructor-inheritance-unresolved'].includes(choice.reason) &&
629
+ validateConfirmation(site.provenance, declarationIdentity(owner)).verdict === 'establishes-target') {
630
+ remaining.push(siteId);
631
+ continue;
632
+ }
633
+ unverified.push({ siteId, call, reason, provenance: choice.provenance });
634
+ continue;
635
+ }
636
+ // The edge names the definition holding the selected constructor's
637
+ // body (an in-class declaration's out-of-class definition, fix
638
+ // #421); its proof establishes the declaration.
639
+ const definition = require('./out-of-class-members').bodyDefinitionOf(index, choice.definition) || choice.definition;
640
+ const key = identityKey(declarationIdentity(definition));
641
+ const target = bodyOnly ? groups : initializers;
642
+ if (!target.has(key)) target.set(key, { definition, sites: [] });
643
+ target.get(key).sites.push({ ...site, targetDef: declarationIdentity(choice.definition),
644
+ provenance: choice.provenance, tier: 'confirmed' });
645
+ }
646
+ if (remaining.length === siteIds.length && !initializers.size && !initializerReviews.length) return null;
647
+ return { remaining, unverified, groups: [...groups.values()].sort(groupOrder),
648
+ initializers: [...initializers.values()].sort(groupOrder), initializerReviews };
649
+ }
650
+
651
+ // A class's construction sites, read once per operation for every
652
+ // constructor of the class a query projects (fix #418). Options carrying a
653
+ // profile, statistics or a callback keep their own read.
654
+ function classConstructionCalls(index, owner, options) {
655
+ const read = () => index.findCallers(owner.name, { ...options, profile: undefined,
656
+ constructorProjectionSource: true, targetDefinitions: [owner], collectAccount: true,
657
+ includeMethods: true, needsTotal: true });
658
+ if (options.profile || options.stats) return read();
659
+ const memo = index._opMemo?.('classConstructionCalls', () => new Map());
660
+ if (!memo) return read();
661
+ // An option that is not plain data (the candidate-file Set of a HOT
662
+ // ranking) is keyed by its identity within the operation; a function
663
+ // keeps its own read (fix #435: the key threw for a Set, so every
664
+ // constructor of a ranking re-read its class).
665
+ const identities = index._opMemo('classConstructionOptionIds', () => ({ ids: new WeakMap(), next: 0 }));
666
+ let unkeyed = false;
667
+ const key = identityKey(declarationIdentity(owner)) + '\0' + JSON.stringify(Object.keys(options).sort()
668
+ .filter(name => name !== 'targetDefinitions' && name !== 'profile')
669
+ .map(name => [name, options[name]]), (_name, value) => {
670
+ if (typeof value === 'function' || typeof value === 'symbol') {
671
+ unkeyed = true;
672
+ return null;
673
+ }
674
+ if (value && typeof value === 'object' && !Array.isArray(value) &&
675
+ Object.getPrototypeOf(value) !== Object.prototype) {
676
+ if (!identities.ids.has(value)) identities.ids.set(value, `\0object:${identities.next++}`);
677
+ return identities.ids.get(value);
678
+ }
679
+ return value;
680
+ });
681
+ if (unkeyed) return read();
682
+ if (!memo.has(key)) memo.set(key, read());
683
+ return memo.get(key);
684
+ }
685
+
686
+ const nameFamily = (index, def) => {
687
+ const language = index.files.get(def.file)?.language;
688
+ return langTraits(language)?.nameReferenceFamily || language;
689
+ };
690
+
691
+ // Whether a class the inheritance graph lists under `parentName` derives
692
+ // from this owner (fix #434). The graph is keyed by the parent's spelling
693
+ // only: a class of another language never derives from the owner, and a
694
+ // base that resolves to another definition of the name is another class.
695
+ // An unresolved base stays a candidate.
696
+ function extendsOwner(index, candidate, parentName, owner) {
697
+ if (nameFamily(index, candidate) !== nameFamily(index, owner)) return false;
698
+ const { classDefsNamed, parentRefsOf } = require('./class-identity');
699
+ const ownerKey = classDefsNamed(index, owner.name).entries.find(entry => entry.def === owner)?.key;
700
+ const childKey = classDefsNamed(index, candidate.name).entries.find(entry => entry.def === candidate)?.key;
701
+ if (!ownerKey || !childKey) return true;
702
+ const spelled = parentRefsOf(index, { name: candidate.name, key: childKey, def: candidate })
703
+ .filter(parent => parent.name === parentName);
704
+ // A base naming no class definition (a value alias of the owner) stays
705
+ // a candidate.
706
+ return !spelled.length || spelled.some(parent => !parent.external && (!parent.key || parent.key === ownerKey ||
707
+ classDefsNamed(index, parent.name).entries.every(entry => entry.key !== parent.key)));
708
+ }
709
+
710
+ /** Add class constructions to a constructor's ordinary, explicitly named calls. */
711
+ function projectConstructorCallers(index, name, options, result) {
712
+ if (options.constructorProjectionSource || options.constructorProjection === false) return result;
713
+ const targets = options.targetDefinitions || index.symbols.get(name) || [];
714
+ if (!targets.length || !targets.every(def => isConstructor(def, index))) return result;
715
+ const owners = [];
716
+ for (const target of targets) {
717
+ const owner = ownerDefinitionOf(index, target);
718
+ if (owner && !owners.some(other => sameDeclaration(declarationIdentity(owner), declarationIdentity(other)))) owners.push(owner);
719
+ }
720
+ for (let at = 0; at < owners.length; at++) {
721
+ const owner = owners[at];
722
+ if (!langTraits(index.files.get(owner.file)?.language)?.constructorInheritance) continue;
723
+ const parentNames = [owner.name, ...constructionAliasNames(index, [owner])];
724
+ for (const parentName of parentNames) {
725
+ for (const child of index.extendedByGraph.get(parentName) || []) {
726
+ const name = typeof child === 'string' ? child : child.name;
727
+ for (const candidate of index.symbols.get(name) || []) {
728
+ if (candidate.type !== 'class' || child.file && candidate.file !== child.file ||
729
+ owners.some(other => sameDeclaration(declarationIdentity(candidate), declarationIdentity(other))) ||
730
+ !extendsOwner(index, candidate, parentName, owner)) continue;
731
+ owners.push(candidate);
732
+ }
733
+ }
734
+ }
735
+ }
736
+ const promoted = [], uncertain = [], excluded = [], replaced = new Set();
737
+ for (const owner of owners) {
738
+ const language = index.files.get(owner.file).language;
739
+ const classCalls = classConstructionCalls(index, owner, options);
740
+ const rows = [...classCalls, ...(classCalls.shadowEntries || []), ...(classCalls.unverifiedEntries || [])];
741
+ for (const site of rows) {
742
+ let records = callsOnLine(index, site.file, site.line).filter(call => sameSite(call, site));
743
+ if (records.length > 1 && records.some(call => call.implicitBase)) records = records.filter(call => call.implicitBase &&
744
+ call.name === (site.calledAs || owner.name) && constructionAliasBinding(index, site.file, call, [owner]));
745
+ // `type(self)(..)` starts where its inner `type(self)` call does.
746
+ if (records.length > 1 && site.ownClassConstruction) records = records.filter(call => call.ownClass);
747
+ if (records.length !== 1) continue;
748
+ let call = records[0];
749
+ if (!isConstruction(call, site, language)) continue;
750
+ // A target-typed `new(..)` whose slot (an argument, a member it
751
+ // is assigned to) is resolved at query time carries no type name
752
+ // in its record: the class binding named it (fix #426).
753
+ if (call.targetTyped && call.targetSlot) {
754
+ call = { ...call, name: site.calledAs || owner.name };
755
+ }
756
+ replaced.add(siteKey(site));
757
+ const choice = constructionChoice(index, owner, call, site.provenance, targets);
758
+ // No project constructor body runs here, so none of the targets.
759
+ if (choice.noBody) continue;
760
+ if (choice.other) {
761
+ excluded.push({ file: site.file, line: site.line, reason: choice.reason || 'constructor-overload-other',
762
+ provenance: choice.provenance });
763
+ continue;
764
+ }
765
+ const reason = site.tier === 'unverified' ? site.reason || 'constructor-owner-unresolved' : choice.reason;
766
+ const row = { ...site, calledAs: site.calledAs || owner.name,
767
+ ...(call.implicitBase && { implicitConstruction: true }),
768
+ provenance: choice.provenance, ...(reason ? { tier: 'unverified', reason } : { tier: 'confirmed' }) };
769
+ (reason ? uncertain : promoted).push(row);
770
+ }
771
+ }
772
+ // A construction through an ancestor method's own receiver (`cls(..)`,
773
+ // `type(self)(..)`, `new this(..)`) builds the class the method runs
774
+ // through: possibly an owner below that ancestor (fix #434).
775
+ if (options.collectAccount && owners.some(owner => langTraits(index.files.get(owner.file)?.language)?.ownClassMethod)) {
776
+ for (const ancestor of resolvedAncestors(index, owners)) {
777
+ const entry = index.files.get(ancestor.file);
778
+ for (const call of index.getCachedCalls(ancestor.file) || []) {
779
+ if (call.ownClass?.name !== ancestor.name || !namedAt(ancestor, call.ownClass.line)) continue;
780
+ const binding = ownClassBinding(index, ancestor.file, call);
781
+ if (!binding || !sameDeclaration(declarationIdentity(binding.owner), declarationIdentity(ancestor))) continue;
782
+ const caller = index.findEnclosingFunction(ancestor.file, call.line, true, call);
783
+ uncertain.push({ file: ancestor.file, relativePath: entry.relativePath, line: call.line,
784
+ ...(Number.isInteger(call.callSite?.column) && { column: call.callSite.column }),
785
+ content: index._getFileLines(ancestor.file)?.[call.line - 1] ?? '',
786
+ callerName: caller?.name || null, callerFile: caller ? ancestor.file : null,
787
+ callerStartLine: caller?.startLine ?? null, callerEndLine: caller?.endLine ?? null,
788
+ isMethod: false, calledAs: call.ownClass.spelling, ownClassConstruction: true,
789
+ provenance: binding.provenance, tier: 'unverified', reason: 'possible-dispatch' });
790
+ }
791
+ }
792
+ }
793
+ const confirmed = [];
794
+ for (const site of [...result, ...(result.shadowEntries || [])].filter(site => !replaced.has(siteKey(site)))) {
795
+ const language = index.files.get(site.file)?.language;
796
+ const initializer = langTraits(language)?.constructorProtocol?.initializer;
797
+ const records = callsOnLine(index, site.file, site.line).filter(call => sameSite(call, site));
798
+ if (targets.every(target => target.name === initializer) ||
799
+ records.length === 1 && (records[0].isConstructor || records[0].constructorSyntax === 'delegation') &&
800
+ validateConfirmation(site.provenance, targets.map(declarationIdentity)).verdict === 'establishes-target') {
801
+ confirmed.push(site);
802
+ } else {
803
+ uncertain.push({ ...site, tier: 'unverified', reason: 'constructor-call-unproven' });
804
+ }
805
+ }
806
+ const unverified = (result.unverifiedEntries || []).filter(site => !replaced.has(siteKey(site)));
807
+ const unique = rows => [...new Map(rows.map(row => [siteKey(row), row])).values()].sort(order);
808
+ const all = unique([...confirmed, ...promoted]);
809
+ const limit = options.maxResults || Infinity;
810
+ // A row read beyond the enrichment limit carries no line text; one that
811
+ // moves into the visible slice is enriched like the others (fix #421).
812
+ const visible = all.slice(0, limit).map(row => row.content != null ? row : {
813
+ ...row, content: index._getFileLines(row.file)?.[row.line - 1] ?? '' });
814
+ result.splice(0, result.length, ...visible);
815
+ result.shadowEntries = all.slice(limit);
816
+ result.totalCount = all.length;
817
+ if (options.collectAccount) {
818
+ result.unverifiedEntries = unique([...unverified, ...uncertain]);
819
+ const replacedLines = new Set([...promoted, ...uncertain].map(site => `${site.file}\0${site.line}`));
820
+ result.accountRaw.excludedEntries = [
821
+ ...result.accountRaw.excludedEntries.filter(site => !replacedLines.has(`${site.file}\0${site.line}`)), ...excluded,
822
+ ];
823
+ result.accountRaw.unverifiedLines = result.accountRaw.unverifiedLines.filter(site =>
824
+ !replacedLines.has(`${site.file}\0${site.line}`));
825
+ }
826
+ return result;
827
+ }
828
+
829
+ module.exports = { isConstructor, ownConstructors, isConstruction, constructionChoice, constructionTypeParameter,
830
+ projectConstructorCallers, projectConstructorCalleeSites, constructionAliasNames, constructionAliasBinding, ownClassBinding,
831
+ constructionTypeCandidates, constructionTypeNames, constructionSpellingsReaching, constructionAliasPossibilities, ownMembers };