From: Javier Sagredo Date: Thu, 7 May 2026 00:11:43 +0000 (+0200) Subject: Chain real fam-instance to predicate node when no class instance matches X-Git-Url: https://git.sagredo.dev/?a=commitdiff_plain;h=6b43ab8695ba2b98250d3d31cf500f7a386407de;p=classgraph.git Chain real fam-instance to predicate node when no class instance matches addFamilyLinksFromArgs only drew a fam-resolves chain edge when findMatchingInstances returned a satisfying class instance. For predicates like @Crypto (ProtoCrypto proto)@ or @Show (CannotForgeError proto)@ where the class lives in a package the user hasn't extracted, matched is empty and the fam-instance node ended up disconnected even though we know exactly which predicate it discharges. Symmetrise with the synthetic-placeholder branch above: fall back to chaining fi → originPredId so the fam-instance visibly belongs to that constraint, even when we can't surface the actual class-instance satisfier. Co-Authored-By: Claude Opus 4.7 (1M context) --- diff --git a/data/viewer.js b/data/viewer.js index f56ff8a..8986cc0 100644 --- a/data/viewer.js +++ b/data/viewer.js @@ -739,18 +739,37 @@ if (predClassQn && resolvedArgs) { const reduced = resolvedArgs.map(reduceTypeArg); const matched = findMatchingInstances(predClassQn, reduced); - for (const m of matched) { - const mId = ensureInstanceNode( - m, predClassQn, - 'inst:' + m._idx - ); - const resId = fiNodeId + '=>resolves=>' + mId; + if (matched.length > 0) { + for (const m of matched) { + const mId = ensureInstanceNode( + m, predClassQn, + 'inst:' + m._idx + ); + const resId = fiNodeId + '=>resolves=>' + mId; + if (!seenNodes.has(resId)) { + seenNodes.add(resId); + els.push({ group: 'edges', data: { + id: resId, + source: fiNodeId, + target: mId, + kind: 'fam-resolves', + }}); + } + } + } else if (originPredId) { + // No matching class instance is in our dumps — typically + // because the class lives in a package we didn't extract. + // Chain the fam-instance to the predicate node anyway so + // it isn't visually orphaned: the user can still see + // "this fam-instance is what discharges that constraint", + // we just can't surface the actual satisfier. + const resId = fiNodeId + '=>resolves=>' + originPredId; if (!seenNodes.has(resId)) { seenNodes.add(resId); els.push({ group: 'edges', data: { id: resId, source: fiNodeId, - target: mId, + target: originPredId, kind: 'fam-resolves', }}); }