From aced7d3d4c07b278ae45a8fadf64c965a200af36 Mon Sep 17 00:00:00 2001 From: Rene Leonhardt <65483435+reneleonhardt@users.noreply.github.com> Date: Sun, 2 Aug 2026 14:42:50 +0200 Subject: [PATCH] fix(scanner): Resolve Rust dev dependencies in source targets Resolve Cargo dev dependencies from every non-build target while keeping build dependencies scoped to custom-build targets. Signed-off-by: Rene Leonhardt <65483435+reneleonhardt@users.noreply.github.com> Co-Authored-By: GPT-5.6 Sol --- scanner/rustcargo_test.go | 21 ++++++++++++++++++--- scanner/rustgraph.go | 4 +--- 2 files changed, 19 insertions(+), 6 deletions(-) diff --git a/scanner/rustcargo_test.go b/scanner/rustcargo_test.go index a8b1bc1..8578c3b 100644 --- a/scanner/rustcargo_test.go +++ b/scanner/rustcargo_test.go @@ -219,9 +219,13 @@ func TestCargoMetadataScopesDependenciesByCallerAndTargetKind(t *testing.T) { files := map[string]string{ "Cargo.toml": "[workspace]\nmembers = [\"app\"]\n", "app/Cargo.toml": cargoTestManifest("app"), - "app/src/lib.rs": "pub fn call() {}\n", + "app/src/lib.rs": "#[cfg(test)] mod unit_tests;\npub fn call() {}\n", + "app/src/main.rs": "fn main() {}\n", + "app/src/unit_tests.rs": "#[test] fn unit() {}\n", "app/build.rs": "fn main() {}\n", "app/tests/integration.rs": "fn test() {}\n", + "app/examples/demo.rs": "fn main() {}\n", + "app/benches/measure.rs": "fn main() {}\n", "normal/Cargo.toml": "[package]\nname = \"normal\"\nversion = \"0.1.0\"\n", "normal/src/lib.rs": "pub mod api;\n", "normal/src/api.rs": "pub fn run() {}\n", @@ -241,8 +245,11 @@ func TestCargoMetadataScopesDependenciesByCallerAndTargetKind(t *testing.T) { packages := []map[string]any{ cargoPackageWithTargets(root, "app", "app", []map[string]any{ cargoTargetJSON(root, "app/src/lib.rs", "app", rustTargetLib), + cargoTargetJSON(root, "app/src/main.rs", "app-bin", rustTargetBin), cargoTargetJSON(root, "app/build.rs", "build-script-build", rustTargetCustomBuild), cargoTargetJSON(root, "app/tests/integration.rs", "integration", rustTargetTest), + cargoTargetJSON(root, "app/examples/demo.rs", "demo", rustTargetExample), + cargoTargetJSON(root, "app/benches/measure.rs", "measure", rustTargetBench), }, dependencies), cargoPackage(root, "normal", "normal", "normal", nil), cargoPackage(root, "build-dep", "build-dep", "build_dep", nil), @@ -255,18 +262,26 @@ func TestCargoMetadataScopesDependenciesByCallerAndTargetKind(t *testing.T) { {Path: "dev_dep::api::run", Kind: "rust-path"}, } analyses := []FileAnalysis{ - {Path: "app/src/lib.rs", Language: "rust", References: refs}, + {Path: "app/src/lib.rs", Language: "rust", References: append([]ImportReference{{Path: "unit_tests", Kind: "rust-module"}}, refs...)}, + {Path: "app/src/main.rs", Language: "rust", References: refs}, + {Path: "app/src/unit_tests.rs", Language: "rust", References: refs}, {Path: "app/build.rs", Language: "rust", References: refs}, {Path: "app/tests/integration.rs", Language: "rust", References: refs}, + {Path: "app/examples/demo.rs", Language: "rust", References: refs}, + {Path: "app/benches/measure.rs", Language: "rust", References: refs}, } graph, err := buildFileGraphFromAnalysesWithCargoMetadata(context.Background(), root, analyses, func(context.Context, string) ([]byte, error) { return metadata, nil }) if err != nil { t.Fatal(err) } for from, want := range map[string][]string{ - "app/src/lib.rs": {"normal/src/api.rs"}, + "app/src/lib.rs": {"app/src/unit_tests.rs", "dev-dep/src/api.rs", "normal/src/api.rs"}, + "app/src/main.rs": {"dev-dep/src/api.rs", "normal/src/api.rs"}, + "app/src/unit_tests.rs": {"dev-dep/src/api.rs", "normal/src/api.rs"}, "app/build.rs": {"build-dep/src/api.rs"}, "app/tests/integration.rs": {"dev-dep/src/api.rs", "normal/src/api.rs"}, + "app/examples/demo.rs": {"dev-dep/src/api.rs", "normal/src/api.rs"}, + "app/benches/measure.rs": {"dev-dep/src/api.rs", "normal/src/api.rs"}, } { got := append([]string(nil), graph.Imports[from]...) sort.Strings(got) diff --git a/scanner/rustgraph.go b/scanner/rustgraph.go index 39d18ba..d47b95b 100644 --- a/scanner/rustgraph.go +++ b/scanner/rustgraph.go @@ -11,7 +11,7 @@ import ( const ( rustCoverageStatus = "partial" - rustCoverageNote = "Rust macro-generated, cfg-gated dev-dependency, string-routed, and #[path] module edges may be unresolved" + rustCoverageNote = "Rust macro-generated, string-routed, and #[path] module edges may be unresolved" rustTargetLib = "lib" rustTargetBin = "bin" @@ -431,8 +431,6 @@ func rustDependencyEligible(dependencyKind, targetKind string) bool { switch dependencyKind { case "build": return targetKind == rustTargetCustomBuild - case "dev": - return targetKind == rustTargetTest || targetKind == rustTargetExample || targetKind == rustTargetBench default: return targetKind != rustTargetCustomBuild }