]> Repositorios git - classgraph.git/commitdiff
Chain real fam-instance to predicate node when no class instance matches
authorJavier Sagredo <[email protected]>
Thu, 7 May 2026 00:11:43 +0000 (02:11 +0200)
committerJavier Sagredo <[email protected]>
Thu, 7 May 2026 00:11:43 +0000 (02:11 +0200)
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) <[email protected]>
data/viewer.js

index f56ff8a1df3350001508733e7bda832f3eed52f6..8986cc0ab3c778869255e0ddc8523afbb48c303b 100644 (file)
           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',
                 }});
               }