* Interface/registry-dispatch announcement — #687 extended to GRAPH-visible * polymorphism (the body-scan can't see it: `nodeType.execute()` is textually * an ordinary call; the polymorphism lives in the `implements`/`extends` edges). * * A method the agent named that resolves to a large
(cg: CodeGraph, candidates: Array<{ token: string; family: Node[] }>, named: Map<string, Node>)
| 2181 | * TRUE graph-wide implementer count, NOT their frequency in the sample. |
| 2182 | */ |
| 2183 | private buildPolymorphicBoundaries(cg: CodeGraph, candidates: Array<{ token: string; family: Node[] }>, named: Map<string, Node>): string { |
| 2184 | const CLASSY = new Set(['class', 'struct', 'interface', 'trait', 'protocol', 'abstract']); |
| 2185 | const MIN_IMPL = 8; // a supertype needs >= this many implementers to count as "polymorphic" |
| 2186 | const MIN_SUPPORT = 2; // >= this many sampled definers must share the supertype (ties it to the token) |
| 2187 | const SAMPLE = 40; // family members inspected per token |
| 2188 | const MAX_NOTES = 3; |
| 2189 | const rel = (p: string) => p.replace(/\\/g, '/'); |
| 2190 | const containerOf = (m: Node): Node | null => { |
| 2191 | try { const ce = cg.getIncomingEdges(m.id).find((e) => e.kind === 'contains'); return ce ? cg.getNode(ce.source) : null; } |
| 2192 | catch { return null; } |
| 2193 | }; |
| 2194 | const notes: string[] = []; |
| 2195 | const seenSuper = new Set<string>(); |
| 2196 | for (const { token, family } of candidates) { |
| 2197 | if (notes.length >= MAX_NOTES) break; |
| 2198 | // supertype id → how many sampled definers share it + a few example definers |
| 2199 | const supers = new Map<string, { node: Node; count: number; targets: Node[] }>(); |
| 2200 | for (const m of family.slice(0, SAMPLE)) { |
| 2201 | const container = containerOf(m); |
| 2202 | if (!container || !CLASSY.has(container.kind)) continue; |
| 2203 | let sups: Node[] = []; |
| 2204 | try { |
| 2205 | sups = cg.getOutgoingEdges(container.id) |
| 2206 | .filter((e) => e.kind === 'implements' || e.kind === 'extends') |
| 2207 | .map((e) => { try { return cg.getNode(e.target); } catch { return null; } }) |
| 2208 | .filter((n): n is Node => !!n && CLASSY.has(n.kind) && (n.name?.length || 0) >= 3); |
| 2209 | } catch { /* no supertypes — free function or unresolved */ } |
| 2210 | for (const s of sups) { |
| 2211 | const e = supers.get(s.id) || { node: s, count: 0, targets: [] }; |
| 2212 | e.count++; |
| 2213 | if (e.targets.length < 6) e.targets.push(m); |
| 2214 | supers.set(s.id, e); |
| 2215 | } |
| 2216 | } |
| 2217 | // Pick the supertype with the most TRUE implementers (graph-wide), among |
| 2218 | // those genuinely shared by the token's definers. |
| 2219 | let best: { node: Node; impl: number; targets: Node[] } | null = null; |
| 2220 | for (const { node, count, targets } of supers.values()) { |
| 2221 | if (count < MIN_SUPPORT) continue; |
| 2222 | let impl = 0; |
| 2223 | try { impl = cg.getIncomingEdges(node.id).filter((e) => e.kind === 'implements' || e.kind === 'extends').length; } |
| 2224 | catch { /* leave 0 — gated out below */ } |
| 2225 | if (impl < MIN_IMPL) continue; |
| 2226 | if (!best || impl > best.impl) best = { node, impl, targets }; |
| 2227 | } |
| 2228 | if (!best || seenSuper.has(best.node.id)) continue; |
| 2229 | seenSuper.add(best.node.id); |
| 2230 | const namedNames = new Set([...named.values()].map((n) => n.name)); |
| 2231 | const eg = best.targets.slice(0, 4).map((m) => { |
| 2232 | const cont = containerOf(m); |
| 2233 | const disp = cont ? `${cont.name}.${m.name}` : (m.qualifiedName || m.name); |
| 2234 | const mark = cont && namedNames.has(cont.name) ? ' ← you named this' : ''; |
| 2235 | return `\`${disp}\` (${rel(m.filePath)}:${m.startLine})${mark}`; |
| 2236 | }); |
| 2237 | const more = best.impl > eg.length ? ` +${best.impl - eg.length} more` : ''; |
| 2238 | notes.push(`- \`${token}\` → runtime dispatch to **${best.impl}** types implementing \`${best.node.name}\` — the static path ends here, the target is chosen at runtime. e.g. ${eg.join(', ')}${more}`); |
| 2239 | } |
| 2240 | if (notes.length === 0) return ''; |
no test coverage detected