Skip to content

--cargo drops workspace-internal dep edges that use Cargo's package = "..." rename #1858

Description

@thejesh23

Summary

graphify --cargo silently drops every internal workspace dependency edge whose entry uses Cargo's package = "..." rename. The affected crate node still exists, but the crate_depends_on edge to it never appears in the graph.

Where

graphify/cargo_introspect.py:76-96:

for source_name, (source_id, manifest, data) in sorted(crates.items()):
    dependencies = data.get("dependencies", {})
    if not isinstance(dependencies, dict):
        continue
    source_file = manifest.relative_to(root_path).as_posix()
    for dependency_name in sorted(dependencies):
        target = crates.get(dependency_name)
        if target is None:
            continue
        edges.append({...})

The loop iterates the [dependencies] table KEYS. crates is keyed by each member's [package].name (line 63). Cargo's rename mechanism lets those diverge:

[dependencies]
db = { path = "../storage", package = "internal-storage" }

Here the key is db (the name used in use db::…;) but the target crate at ../storage/Cargo.toml has [package] name = "internal-storage". crates.get("db") returns None, the edge is skipped, and there is nothing in the log to say a dep was dropped.

This pattern is common in monorepos that want short local names (db, mq, auth) without colliding with crates.io names, and in workspaces that vendor two versions of the same crate under different local aliases. Documented in the Cargo book under Specifying dependencies → Renaming dependencies in Cargo.toml.

Reproduction

# workspace root Cargo.toml
[workspace]
members = ["storage", "app"]

# app/Cargo.toml
[package]
name = "app"
version = "0.1.0"
edition = "2021"

[dependencies]
db = { path = "../storage", package = "internal-storage" }

# storage/Cargo.toml
[package]
name = "internal-storage"
version = "0.1.0"
edition = "2021"

Run graphify with --cargo on the workspace root. Observe:

  • app node ✅
  • internal-storage node ✅
  • app -> internal-storage edge ❌ (missing)

Replace db = { path = "…", package = "internal-storage" } with internal-storage = { path = "…" } and the edge appears.

Suggested fix

When the dep entry is an inline table, honor its package override before looking up crates:

for dep_key, spec in sorted(dependencies.items()):
    real_name = dep_key
    if isinstance(spec, dict):
        pkg = spec.get("package")
        if isinstance(pkg, str) and pkg:
            real_name = pkg
    target = crates.get(real_name)
    if target is None:
        continue
    edges.append({...})

Same shape as the existing loop, but keys the lookup off the real package name.

Happy to open a PR with a fixture that adds a rename-based dep and asserts the edge is emitted.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions