Skip to content

Low call-edge density in graph output (~0.57 calls/function on Swift sample) #140

@cadespivey

Description

@cadespivey

Summary

On a ~3,854-function Swift codebase, the graph exposes only ~2,192 calls edges — a ratio of ~0.57 calls per function. That's sparse for a running application and limits the usefulness of any downstream analysis that needs intra-process call-chains (blast-radius transitive closure, dead-code, find-callers).

Framing this as an observation / discussion rather than a definite bug — I don't have a good baseline for what the expected ratio "should" be across languages, and Swift's dynamic dispatch + protocol-witness indirection makes static call-graph extraction legitimately hard. But flagging it because several downstream features appear to suffer from the sparsity (see also #138 on transitive closure).

Not Swift-specific in principle — the question of "how many call edges do you capture per function, and what's known to be missed" applies to every language you parse.

Environment

  • supermodel 0.6.10, macOS arm64
  • Swift (SwiftPM multi-module app, ~382 files)

Data

Relationship-type histogram from the graph JSON (fresh supermodel analyze):

belongsTo              4012
defines_function       3854   ← decl→container, one per function
calls                  2192   ← THE call graph
declares_class         1211
imports                 990
contains_file           400
child_directory         143
defines                  79
partOf                   16
...

calls / Function nodes = 2,192 / 3,854 ≈ 0.57 calls per function.

Why it matters

  • audit impact-analysis table populates directDependents but Impact analysis: transitiveDependents always 0 while directDependents is populated #138's transitive-closure zero is likely downstream of sparse call edges.
  • supermodel focus Sources/.../DataStoreImpl.swift (a 4,069-line file with 174 declarations) returns only imports — no callers, no callees. Could be a per-file slicing issue, but it's at least consistent with "the file has no outgoing call edges in the graph."
  • find <symbol> returns declarations but no usages/callers.

Questions for maintainers

  1. Do you publish a target call-edge density (calls/function) per language that you consider "healthy" extraction? If I fall below it, I know the parser underperformed on my codebase.
  2. Are intra-file / intra-type calls expected to be represented as calls edges, or only cross-file? The sparse count would be consistent with "cross-file only."
  3. For Swift specifically: is the call-graph extractor AST-only, or does it use any type resolution (SourceKit / swift-syntax + semantic) to handle protocol dispatch and generic calls? AST-only would plausibly explain a low ratio.

Happy to provide the cached graph JSON if it helps triage.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions