Skip to main content

graphify-swift-edges

Recover qualified Swift call edges that Graphify's tree-sitter pass records but never links — reconnecting cross-type call pathways in the knowledge graph.

The problem

Graphify builds a code knowledge graph with tree-sitter. For Swift it correctly records each type's methods (Type --method--> .foo()), but it does not link qualified call sites back to the callee's method node:

// Caller.swift
try PaymentStore.reconcile(id: row.id)          // static, fully resolvable
let rows = PaymentStore.shared.fetch(query: q)   // singleton, fully resolvable

These are fully-resolvable calls, yet in the resulting graph the callee type has zero incoming calls edges. Types that talk to each other through static or singleton methods become disconnected islands, so graphify path and graphify affected cannot trace the pathway between them — even though the call is right there in the source.

What this tool does

It re-parses the source with the same tree-sitter grammar, then resolves qualified calls deterministically against the graph's own symbol table and appends the missing calls edges:

  1. Build {TypeLabel → typeNode} and {(typeNode, method) → methodNode} from the graph's existing method edges.
  2. Walk every call_expression; keep navigation_expression callees of the form Type.method(…) or Type.shared.method(…) (PascalCase receiver).
  3. Resolve receiver → type node, attribute the caller by enclosing-type AST ancestry (not a line heuristic), and emit caller --calls--> methodNode.

Precision guards (why it doesn't invent edges)

An edge is emitted only when all hold — otherwise the call is skipped, never guessed:

  • the receiver label maps to exactly one type node (ambiguous labels are skipped);
  • that type actually owns a method of that name;
  • the receiver is type/singleton-qualified (unqualified foo() is skipped — see Scope).

Validation — GRDB.swift (479 files)

A large, real-world, open-source Swift codebase (different authors, heavy use of extensions, generics, and protocols), checkout including its test suite:

result
calls edges added 70
path PlayerListModel → AppDatabase "No path found" → 2-hop call edge
precision (independent type-def check) 69 / 70 (98.6%)

Reproduce:

graphify .                                              # build graphify-out/graph.json on GRDB
graphify-swift-edges graphify-out/graph.json . -o graphify-out/graph.json
graphify path "PlayerListModel" "AppDatabase"          # now a real 2-hop call edge

Two honest findings this surfaced:

  • Recall is codebase-dependent, and the guard is deliberately conservative. GRDB triggered 3,614 ambiguous-receiver skips because extension/generic-heavy types fragment into many same-label nodes (Database → 15 nodes; TableRecord/FetchRequest → 5 each) and the test suite defines many same-named fixtures (Player, Pet, Item). The tool skips all of these rather than guess — fewer edges, never wrong ones. Recovery scales with how many qualified calls have an unambiguous receiver, which varies a lot by codebase.
  • The single false positive is the documented residual mode. Pet.setup() resolved even though GRDB defines struct Pet in several test files: the upstream graph had collapsed them into one node, so the ambiguity guard couldn't see the collision. 1-in-70, confined to duplicated test fixtures — exactly what the IndexStoreDB route (Scope) would eliminate.

Precision was measured independently of the parser, via type-definition uniqueness in the raw source: a receiver whose name has exactly one class/struct/enum/actor/protocol definition cannot be a same-name collision.

Usage

uv tool install graphify-swift-edges      # or: pip install graphify-swift-edges
# after `graphify` has built graphify-out/graph.json:
graphify-swift-edges graphify-out/graph.json /path/to/swift/src -o graphify-out/graph.json

Then query the reconnected graph with graphify path (verified working):

graphify path "SomeCaller" "SomeCallee" --graph graphify-out/graph.json
# Shortest path (2 hops): SomeCaller --calls [RESOLVED_QUALIFIED]--> .method() <--method-- SomeCallee

affected caveat (measured): recovered edges point at method nodes. graphify affected on the owning type returns nothing (it reverse-traverses calls but not method), and on a method label it errors with No unique node match (labels like .fetch() are ambiguous across types). So use path for cross-type tracing, not affected, until Graphify's affected learns to hop method edges.

Scope (honest boundaries)

  • Recovers: type-qualified (Foo.bar()) and singleton-qualified (Foo.shared.bar()) calls.
  • Does NOT recover: instance-variable calls (let s = Foo(); s.bar()) — these need real type inference. The correct general fix is a SourceKit / IndexStoreDB extractor that reads Swift's precise semantic index (protocol dispatch, generics, inferred types included). That is the documented sequel, not this tool.
  • The residual false-positive mode (a same-name type whose duplicate definitions the upstream graph collapsed into one node) is rare and, in testing, confined to duplicated test fixtures.
  • Validated on one large open-source codebase; the resolution is language-general in principle (Kotlin, TS have the same qualified-call shape) but only Swift is implemented and measured here.

Tests

uv run --with pytest --with tree-sitter --with tree-sitter-swift python -m pytest

License

MIT

Metadata

Release files for graphify-swift-edges 0.1.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for graphify-swift-edges 0.1.0
File Size Uploaded
graphify_swift_edges-0.1.0.tar.gz 22.1 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for graphify-swift-edges 0.1.0
File Interpreter ABI Platform
graphify_swift_edges-0.1.0-py3-none-any.whl Python 3 none any Details

Total release size: 30.9 kB

Release files / graphify_swift_edges-0.1.0.tar.gz

Download URL graphify_swift_edges-0.1.0.tar.gz
Size 22.1 kB
Tags Source
SHA-256 checksum
How to use checksums
5be5b925820dee0691234f0cee6afc076da49661d7011441088949f38d2b9666
BLAKE2b-256 checksum
How to use checksums
1e69fd57e1cddab1cd23355a0311f341a2c0ed72f7ba92da9a59f17b7162e2aa
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/6.1.0 CPython/3.13.12

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Jun 7, 2026.

Transparency log

Release files / graphify_swift_edges-0.1.0-py3-none-any.whl

Download URL graphify_swift_edges-0.1.0-py3-none-any.whl
Size 8.8 kB
Tags Python 3
SHA-256 checksum
How to use checksums
18bf85e0ba9d2bf3054b9c42e405261dfbf5210e8e6a4f1f876d5ab64ee0a06a
BLAKE2b-256 checksum
How to use checksums
027733a66a38ed64026b2a44e5e0e93bde1fd19175d94a501df95b6d793037f6
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/6.1.0 CPython/3.13.12

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Jun 7, 2026.

Transparency log

Release history Release notifications | RSS feed

This release

0.1.0 This release

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page