Skip to content

feat(cargo-unused-deps): complete dependency analysis - #188

Open
martin-kolinek wants to merge 25 commits into
mainfrom
adopt-cargo-unused-deps
Open

martin-kolinek wants to merge 25 commits into
mainfrom
adopt-cargo-unused-deps

Conversation

@martin-kolinek

@martin-kolinek martin-kolinek commented Sep 17, 2026

Copy link
Copy Markdown
Collaborator

🤖

Summary

  • expand cargo-unused-deps from the workspace-catalog check into a replacement for cargo-udeps and cargo-machete
  • use rustc's unused_crate_dependencies diagnostics from default-target and all-target passes to detect unused and misplaced declarations
  • include doctest evidence through a rustdoc test-builder shim
  • add Cargo-style -p/--package, --workspace, and --exclude selection for package-level checks
  • always run the workspace-global catalog check, including when impact selects zero packages
  • bump cargo-unused-deps to 0.2.0 and apply its findings to the repository

Package selection contract

Package selectors scope only compiler-backed checks. With no selector, the tool runs the catalog check without compiling packages. This lets a future Anvil recipe invoke the tool even when impact returns --skip; a nonempty required impact set can be forwarded as repeated --package arguments, while an unscoped run uses --workspace.

Release sequencing

Anvil adoption is intentionally deferred until 0.2.0 is published. Generated Anvil setup installs its pinned tool from crates.io, so adopting the complete behavior in this same PR would make dogfood CI execute the catalog-only 0.1.0 release.

Validation

  • complete nightly unit and integration suite, including real compile and doctest fixtures
  • full checker against the ox-tools workspace
  • Clippy for cargo-unused-deps and the adjusted cargo-aprz manifest
  • cargo package verification for 0.2.0
  • cargo-coverage-gate: 100% line coverage
  • cargo-anvil regeneration dry run remains clean

Replace cargo-udeps with the manifest-only cargo-unused-deps check, wire its published 0.1.0 release into setup and impact scoping, and regenerate the dogfood artifacts.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

Copilot-Session: 3757bb12-095a-4879-9fcd-1cf7a62c8807
Copilot AI lite review requested due to automatic review settings September 17, 2026 08:59

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔵 Needs a closer look

It changes the default dependency-validation contract and regenerates cross-backend CI artifacts, so final human review is warranted.

Pull request overview

Migrates Anvil’s dependency check from cargo-udeps to the published manifest-level cargo-unused-deps tool.

Changes:

  • Adds stable, modified-scope unused-deps recipes and tool pinning.
  • Removes legacy udeps recipes and updates templates, workflows, snapshots, and docs.
  • Removes four uninherited workspace dependencies.
File summaries
File Description
README.md Documents the new dependency check.
Cargo.toml Removes unused workspace catalog entries.
.anvil.lock Updates generated artifact checksums.
justfiles/anvil/* Regenerates local Anvil recipes.
crates/cargo-anvil/templates/* Updates Anvil source templates.
crates/cargo-anvil/src/lib.rs Updates documented tool list.
crates/cargo-anvil/src/anvil/artifacts/justfile.rs Registers the new check and scope.
crates/cargo-anvil/tests/recipe_contracts.rs Adds recipe contract coverage.
crates/cargo-anvil/tests/snapshots/* Updates emitted-tree snapshots.
crates/cargo-anvil/README.md Regenerates crate documentation.
crates/cargo-anvil/docs/design/* Documents the behavior and scope change.
crates/cargo-unused-deps/docs/design/README.md Marks CI integration as adopted.
.github/workflows/* Regenerates workflow comments and wiring.
Review details
  • Files reviewed: 35/36 changed files
  • Comments generated: 0
  • Review effort level: Lite

💡 Configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

@codecov-commenter

Codecov Comments Bot (codecov-commenter) commented Sep 17, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 97.6%. Comparing base (1910173) to head (c39ca62).
⚠️ Report is 1 commits behind head on main.

❌ Your project status has failed because the head coverage (97.6%) is below the target coverage (100.0%). You can increase the head coverage or adjust the target coverage.

Additional details and impacted files
@@          Coverage Diff           @@
##            main    #188    +/-   ##
======================================
  Coverage   97.6%   97.6%            
======================================
  Files        304     307     +3     
  Lines      69683   70468   +785     
======================================
+ Hits       68016   68807   +791     
+ Misses      1667    1661     -6     
Flag Coverage Δ
linux 97.6% <100.0%> (+<0.1%) ⬆️
linux-arm 97.6% <100.0%> (+<0.1%) ⬆️
scheduled ?
windows 97.8% <100.0%> (+<0.1%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

Comment thread crates/cargo-anvil/templates/justfiles/anvil/checks/unused-deps.just Outdated
Comment thread crates/cargo-anvil/templates/justfiles/anvil/groups/pr-fast.just Outdated
Comment thread crates/cargo-unused-deps/docs/design/README.md Outdated
martin-kolinek and others added 10 commits September 17, 2026 16:03
Builds steps 2 and 3 of the design on top of the catalog check.

Compile evidence comes from rustc, not from parsing: the tool enables
`unused_crate_dependencies` for its own `cargo check --all-targets` and
reads the JSON diagnostics. The lint is set for that invocation only and
built into its own target dir, because the raw lint fires per unit -- in
the shared lint catalog it would warn on every correct build and fail
under `-D warnings`.

Aggregation is the part that turns those per-unit warnings into an
answer. Cargo compiles a library or binary twice under `--all-targets`,
plainly and with cfg(test), and their diagnostics are indistinguishable,
so the units are counted instead: the test-profile unit compiles a
superset of the plain unit's code, so when only one of them reports it
can only be the plain one. That is what lets a dependency used solely
from `#[cfg(test)]` code come out as misplaced rather than unused.

Doctests are covered by standing in for the compiler rustdoc uses.
`--test-builder` points at this binary, which runs the real rustc with
the lint on and keeps the diagnostics rustdoc discards on success. Two
things learned by measurement: the shim must inherit stdin, because
rustdoc feeds it the snippet there, and it must leave the error format
alone, because handing rustdoc JSON makes it treat every doctest as
failed. Attribution is per package, since a snippet on stdin carries no
crate identity -- and bin-only packages are skipped, because asking cargo
for their doctests is an error rather than an empty answer.

Package selection is cargo's own (-p/--workspace/--exclude), forwarded
verbatim to both child builds, while the catalog check always reads every
member: a subset cannot answer "no member inherits this".

Verified against this repository: the catalog is clean and exactly one
finding comes back -- `ohno`, declared by cargo-aprz and named nowhere in
its sources. `cargo udeps -p cargo-aprz` reports "All deps seem to have
been used", so this is a real defect its filename heuristic misses.

Not yet implemented, and marked so in the design: the optional/feature
check, and `--fix` for a misplaced dependency.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Review question: with several bin targets, or a lib plus bins, can the
counting rule convict a dependency that production code uses?

Not as implemented -- counters are keyed per target, and "used" is the
union across them -- but the design doc described the fold as one table
of dependency x unit kind, which reads exactly like the pooling that
would be wrong. Corrected, and pinned with a fixture that would fail
under pooling: a package with a library and two binaries where each
dependency is used in production by exactly one target, so every other
target reports it unused.

  liponly  used by the lib      -> reported unused by both binaries
  binonly  used by binary one   -> reported unused by the lib and binary two
  bintest  used by one's cfg(test) -> misplaced

Only `bintest` is reported. Also verified idempotence: with a warm target
directory cargo replays cached diagnostics, and artifacts replay with
them, so the ratios and the verdicts are unchanged across runs.

The doc now states the rule as counting per target, the monotonicity
argument that justifies reading a lone report as the plain unit's, and
why pooling across targets would be wrong.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Review question: can multiple test targets skew the counts? The per-target
keying already handles that -- several tests, benches and examples each
testify separately, verified -- but chasing it turned up a real false
positive one step over.

"Compiled twice" is not a property of libraries. A test, bench or example
declared `test = true` is also built plainly and under cfg(test), and the
development branch treated any report at all as "unused". So an example
whose cfg(test) code is the only user of a dev-dependency was convicted:
the plain unit reports, the test-profile unit does not, and one report
was enough.

Development targets now use the same rule as libraries -- fewer reports
than units means some unit loaded it. Both units of a development target
have dev-dependencies in scope, so no scope distinction is needed there;
the distinction still matters for libraries and binaries, where a
dev-dependency is in scope only for the cfg(test) unit and a lone report
is therefore that unit's.

Two regression tests: five development targets each used by exactly one
dependency, and an `[[example]] test = true` whose cfg(test) code is the
sole user. The second fails before this change.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Pushing on "one example does not prove the rule" found the assumption
that was wrong.

The claim was that the cfg(test) unit compiles a superset of the plain
unit's code, so a single report out of two units had to be the plain
one's. `#[cfg(not(test))]` code is excluded from the test unit, so it is
not a superset. A dependency used only from cfg(not(test)) code is used
by the library and reported by the cfg(test) unit -- and the tool moved
it to [dev-dependencies]. Reproduced before fixing.

The inference is gone. Compile evidence is now gathered twice: once over
default targets, where each code target has exactly one unit and a report
can only be that unit's, and once over --all-targets. "Did the library
use it" reads the first; "did anything use it" reads the second, where
`reports < scope` is not an assumption but a restatement of the lint's
semantics -- a unit reports exactly when the dependency was in scope and
went unused.

Both passes share a target directory, so the second reuses the first's
artifacts.

Adds the counter-example as a regression test, and rewrites the design's
rule section to separate the two questions, name the run each is read
from, and record why the superset argument is false.

Verified: 13 evidence tests, 28 catalog tests, and this repository still
reports exactly one finding.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Benchmarked against microsoft/oxidizer (72 crates, 618 locked packages)
and the doctest phase dominated: one `cargo test --doc -p <pkg>`
invocation cost 122s warm, and the tool was running one per package.

Doctest evidence can only ever spare a dependency, never accuse one, so
it is not needed for a package that produced no finding. The tool now
judges once without it, compiles the doctests of only the accused
packages, and judges again. Same verdicts, far less work: on oxidizer
that is 5 doctest builds instead of 72.

Cold run on oxidizer: 1118s -> 431s, identical output.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Applying the tool's own findings to microsoft/oxidizer and running its
test suite found a false positive it had produced: removing `uuid` from
http_extensions broke a doctest.

Same aggregation bug as the one fixed for targets, in the last place it
survived. Every doctest is a separate compilation with the package's
dev-dependencies in scope, so each doctest that does not mention a
dependency reports it unused. http_extensions has about 175 doctests and
one of them uses `uuid`; recording "some doctest reported it" as disuse
convicted it.

Doctest evidence now counts, like everything else: fewer reports than
doctests compiled means some doctest used it.

Three of the five reported dependency problems on oxidizer were this bug
-- uuid, foldhash and prost-types are all doctest-only. The remaining two
survive verification: `fetch` uses `bytes` only from tests, and
`rest_over_grpc_tests` never uses `http-body`.

Verified end to end: with the 14 catalog removals, `bytes` moved to
dev-dependencies and `http-body` deleted, oxidizer's full
`cargo test --workspace --all-features` passes.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
`liponly` was meant to be `libonly` -- the crate in the multi-target
fixture that only the library uses. The name appears in the test and in
the measured table in the design doc, so both are corrected.

No behaviour change; the fixture crate is generated by the test, so the
name is arbitrary, but a misspelled one makes the table harder to read
than it needs to be.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Add compiler and doctest evidence for unused and misplaced declarations. Keep the workspace catalog check global while Cargo-style package selectors scope only the per-package analysis.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

Copilot-Session: 3757bb12-095a-4879-9fcd-1cf7a62c8807
Always run the workspace catalog check, scope compiler evidence only to explicit Cargo-style package selectors, and cover the zero-package impact case. Harden the evidence tests, preserve strict coverage, and apply the findings to ox-tools.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

Copilot-Session: 3757bb12-095a-4879-9fcd-1cf7a62c8807
Copilot AI review requested due to automatic review settings September 17, 2026 14:50
@martin-kolinek martin-kolinek changed the title feat(cargo-anvil): adopt cargo-unused-deps feat(cargo-unused-deps): complete dependency analysis Sep 17, 2026

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

Package-scoped analysis can judge transitive workspace members, and several diagnostics, documentation, and release paths need correction.

Get a fresh assessment by requesting another Copilot review.

Review details

Suppressed comments (1)

crates/cargo-unused-deps/src/lib.rs:259

  • run now also returns failure when selected compile-backed checks find unused or misplaced dependencies (lines 281-292), but this contract still describes success solely in terms of catalog inheritance. Update the public docs to include the selected source checks so callers do not misinterpret a source-level failure.
/// Returns [`ExitCode::SUCCESS`] when every catalog entry is inherited by at
/// least one member -- or, under `--fix`, once the entries that were not have
/// been removed -- and [`ExitCode::FAILURE`] otherwise. Returning an exit code
  • Files reviewed: 14/15 changed files
  • Comments generated: 8
  • Review effort level: Lite

Comment thread crates/cargo-unused-deps/src/evidence.rs
Comment thread crates/cargo-unused-deps/src/lib.rs
Comment thread crates/cargo-unused-deps/src/lib.rs Outdated
Comment thread crates/cargo-unused-deps/src/verdict.rs Outdated
Comment thread crates/cargo-unused-deps/src/verdict.rs
Comment thread crates/cargo-unused-deps/Cargo.toml
Comment thread crates/cargo-unused-deps/docs/design/README.md Outdated
Comment thread crates/cargo-unused-deps/docs/design/README.md Outdated
Comment thread crates/cargo-unused-deps/src/evidence.rs Outdated
Comment thread crates/cargo-unused-deps/src/doctests.rs
Comment thread crates/cargo-unused-deps/src/lib.rs
Track explicitly selected package roots, render compiler failures, include proc-macro doctests, separate source suppressions from stale catalog entries, and keep the stable mutation path meaningful.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

Copilot-Session: 3757bb12-095a-4879-9fcd-1cf7a62c8807
Copilot AI review requested due to automatic review settings September 17, 2026 16:58
Run the repository release script to generate release metadata for the complete dependency analysis release.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

Copilot-Session: 3757bb12-095a-4879-9fcd-1cf7a62c8807

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

Shared evidence artifacts can produce incomplete diagnostics, and doctest evidence does not preserve duplicate package identities.

Get a fresh assessment by requesting another Copilot review.

Review details

Suppressed comments (1)

crates/cargo-unused-deps/Cargo.toml:7

  • This changes the published version to 0.2.0 while crates/cargo-unused-deps/CHANGELOG.md still contains only its header. The repository release procedure says release-crate.ps1 bumps the version and generates the changelog (README.md:79-80); please perform that release step, or leave the version bump for the dedicated release PR, so the published 0.2.0 has release notes.
version = "0.2.0"
  • Files reviewed: 16/17 changed files
  • Comments generated: 3
  • Review effort level: Lite

Comment thread crates/cargo-unused-deps/src/lib.rs Outdated
Comment thread crates/cargo-unused-deps/src/lib.rs Outdated
Comment thread crates/cargo-unused-deps/src/lib.rs
Comment thread crates/cargo-unused-deps/src/lib.rs
Comment thread crates/cargo-unused-deps/src/lib.rs Outdated

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

Evidence target directories are reused across runs without replayed lint diagnostics, while selector and doctest-wrapper behavior also diverge from the documented contract.

Get a fresh assessment by requesting another Copilot review.

Review details

Suppressed comments (2)

Previously missed (2) — in code that hasn't changed since the last review.

crates/cargo-unused-deps/src/doctests.rs:90

  • The doctest shim invokes RUSTC directly and never chains an existing RUSTC_WRAPPER. The compile-evidence path explicitly preserves that wrapper in crates/cargo-unused-deps/src/evidence.rs:166-169, so a caller with a custom compiler wrapper gets different compiler behavior (or loses required wrapper setup) during doctest collection. Preserve the wrapper chain here as well.
    crates/cargo-unused-deps/src/lib.rs:365
  • These selectors only match a literal name or an exact full name@version. The repository's existing Cargo-style selector implementation accepts Unix globs and partial version qualifiers (crates/cargo-each/src/select.rs:102-111 and :134-151), so valid selectors such as tokio-* or foo@0.1 are rejected here even though Cargo accepts them. Match the same package-spec semantics, with corresponding integration coverage.
  • Files reviewed: 16/17 changed files
  • Comments generated: 3
  • Review effort level: Lite

Comment thread crates/cargo-unused-deps/src/doctests.rs
Comment thread crates/cargo-unused-deps/src/lib.rs Outdated
Comment thread crates/cargo-unused-deps/docs/design/README.md Outdated
Force the rustdoc test-builder shim to emit stable human, no-color diagnostics before parsing unused dependency names.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

Copilot-Session: 3757bb12-095a-4879-9fcd-1cf7a62c8807
Regenerate the 0.2.0 changelog after diagnostic normalization.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

Copilot-Session: 3757bb12-095a-4879-9fcd-1cf7a62c8807
Copilot AI review requested due to automatic review settings September 17, 2026 18:34

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

Reused evidence artifacts can suppress findings on repeated runs, and selector/documentation contracts are inconsistent.

Get a fresh assessment by requesting another Copilot review.

Review details

Suppressed comments (3)

Previously missed (1) — in code that hasn't changed since the last review.

crates/cargo-unused-deps/src/lib.rs:365

  • resolve_selectors only matches literal names or a complete name@version string. This rejects the repository's existing Cargo-style selector forms such as tokio-* and partial versions like foo@0.1, even though the sibling cargo-each contract documents and implements them (crates/cargo-each/src/cli.rs:33-45, crates/cargo-each/src/select.rs:99-145). An impact or user invocation using these selectors fails before Cargo runs; implement the same matching semantics or narrow/document this CLI as non-Cargo-style.

crates/cargo-unused-deps/src/lib.rs:385

  • These target directories are stable for the workspace, so a second source_checks invocation reuses the first run's artifacts. The design explicitly notes that Cargo emits compiler-artifact for cached units without replaying lint diagnostics (docs/design/README.md:422-426); parse still increments units, while Evidence::used_by_plain_unit treats zero reports as use (src/evidence.rs:96-99). Thus the repeated-analysis case in tests/evidence_tests.rs:652-658 can lose an unused finding. Use a fresh target directory per analysis (or force fresh compiler diagnostics), not just separate directories for the two passes.
    let plain_target_dir = workspace.evidence_target_dir.join("plain");
    let all_target_dir = workspace.evidence_target_dir.join("all-targets");

crates/cargo-unused-deps/docs/design/README.md:276

  • This section says the tool has no stable-toolchain mode, but the CLI deliberately supports a stable catalog-only invocation when no selector is supplied (README.md:35-39, 56-60; design :556-564). That makes the design contract self-contradictory and can lead users to run nightly unnecessarily; qualify the statement to the compiler-backed checks.
There is exactly one source of truth about source-level use: **what the compiler
loaded**. The tool requires a nightly toolchain and gathers that evidence for every
unit, doctests included. It does not parse Rust, and it has no stable-toolchain mode —
a second, weaker analysis to fall back on would double the code and halve the
confidence in the result.
  • Files reviewed: 16/17 changed files
  • Comments generated: 1
  • Review effort level: Lite

Comment thread crates/cargo-unused-deps/src/doctests.rs
Use fresh target directories across invocations, key doctest evidence by package identity, narrow selector semantics, filter failure output to errors, and align public documentation.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

Copilot-Session: 3757bb12-095a-4879-9fcd-1cf7a62c8807
Regenerate the 0.2.0 changelog after evidence isolation fixes.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

Copilot-Session: 3757bb12-095a-4879-9fcd-1cf7a62c8807
Copilot AI review requested due to automatic review settings September 17, 2026 19:05

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

Target-specific dependency findings lose their declaration path and are reported as unconditional [dependencies] entries.

Get a fresh assessment by requesting another Copilot review.

Review details

Suppressed comments (3)

Previously missed (3) — in code that hasn't changed since the last review.

crates/cargo-unused-deps/docs/design/README.md:458

  • This rationale is internally contradictory: it claims the cfg(test) unit is a superset of the plain unit, but the later explanation correctly notes that #[cfg(not(test))] code is absent from the test unit. The false superset claim makes the one-report attribution rule misleading; the separate default-target pass, not monotonicity, is what identifies the plain unit.
    crates/cargo-unused-deps/docs/design/README.md:617
  • The exit-code table immediately above still describes only catalog-level unused entries, but this implementation also returns failure for selected packages' unused/misplaced findings and returns an error when compile or doctest evidence collection fails. Update the table so the design documents the new package-check failure paths introduced by this PR.
    crates/cargo-unused-deps/src/doctests.rs:105
  • This comment says the error format is left alone, but stable_diagnostic_args removes inherited --error-format, --color, and --json options and shim appends --error-format=human --color=never. Reword the comment to describe that normalization; otherwise it contradicts the behavior the parser relies on.
  • Files reviewed: 16/17 changed files
  • Comments generated: 1
  • Review effort level: Lite

Comment thread crates/cargo-unused-deps/src/detect.rs
Comment thread crates/cargo-unused-deps/src/lib.rs
Comment thread crates/cargo-unused-deps/src/doctests.rs Outdated
Preserve target-table predicates in findings, align shim documentation, and avoid the spellcheck tokenization failure in package-selector help.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

Copilot-Session: 3757bb12-095a-4879-9fcd-1cf7a62c8807
Regenerate the 0.2.0 changelog after target-context reporting.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

Copilot-Session: 3757bb12-095a-4879-9fcd-1cf7a62c8807
Copilot AI review requested due to automatic review settings September 18, 2026 09:58

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔵 Needs a closer look

Selected-check filtering can trigger unrelated doctest failures, and the --fix documentation overstates concurrent-edit safety.

Review details

Suppressed comments (3)

Previously missed (3) — in code that hasn't changed since the last review.

crates/cargo-unused-deps/src/lib.rs:94

  • write_back only re-reads the target and then performs persist/rename; an edit that lands between those operations can still be overwritten. The new documentation therefore overstates the guarantee by saying a concurrent edit is never clobbered. Restore the prior race-window caveat or add an OS-level lock/conditional replacement before making this claim.
    crates/cargo-unused-deps/src/lib.rs:406
  • candidates contains findings of both kinds before the later checks filter, so --check misplaced still runs cargo test --doc for packages whose only candidates are unused dev/build declarations. Because doctest_evidence propagates doctest failures, an unselected unused finding can make this invocation fail and it also defeats the requested check's cost boundary. Gather all candidates when unused is selected, but otherwise only normal declarations, since doctests can only turn those into Misplaced.
    crates/cargo-unused-deps/docs/design/README.md:561
  • The new package-selection contract is documented here, but the design's exit/reporting contract below still describes code 1 only for uninherited catalog entries and manifest errors. run now also returns failure for selected unused or misplaced declarations (and evidence-collection errors), so the design should update its exit-code and reporting sections to match the implemented source checks.
  • Files reviewed: 16/17 changed files
  • Comments generated: 0 new
  • Review effort level: Lite

Prevent PowerShell#26940 assembly-loader crashes by serializing recipe-contract interpreter startups under libtest and nextest while preserving intentional concurrency inside individual fixtures.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

Copilot-Session: 3757bb12-095a-4879-9fcd-1cf7a62c8807
Copilot AI review requested due to automatic review settings September 18, 2026 11:44

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔵 Needs a closer look

Documentation currently overstates concurrent-edit protection and JSON validation behavior compared with the implementation.

Review details

Suppressed comments (2)

Previously missed (2) — in code that hasn't changed since the last review.

crates/cargo-unused-deps/src/evidence.rs:161

  • This error contract is broader than the implementation: parse filters to lines beginning with { at lines 262–263 and the test at lines 359–368 explicitly accepts a not json line. Narrow the documentation to malformed JSON message lines (or change the parser if all non-JSON output should be rejected), so callers are not promised strict validation that the code does not perform.
    crates/cargo-unused-deps/src/lib.rs:94
  • The new wording promises that a concurrent edit is never clobbered, but write_back only compares the manifest at lines 598–603 and renames the replacement at line 623. An edit after that final read (or after a member recheck) can still be overwritten, so please narrow this to a best-effort pre-write check and keep the generated README/design wording consistent.
  • Files reviewed: 18/19 changed files
  • Comments generated: 0 new
  • Review effort level: Lite

Apply the generated nightly rustfmt output required by the fast check.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

Copilot-Session: 3757bb12-095a-4879-9fcd-1cf7a62c8807
Copilot AI review requested due to automatic review settings September 18, 2026 13:00

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔵 Needs a closer look

Public and design documentation still omit the new compiler-backed error, reporting, and exit behavior.

Review details

Suppressed comments (2)

Previously missed (2) — in code that hasn't changed since the last review.

crates/cargo-unused-deps/docs/design/README.md:561

  • This new package-level contract is not reflected in the exit/reporting contract below: the table still describes code 1 only for uninherited catalog entries, and the reporting text still promises one-line unused-entry output. source_checks also returns failure for selected unused/misplaced findings and emits multi-line remediation blocks, so update those sections to cover the compiler-backed checks.
    crates/cargo-unused-deps/src/lib.rs:299
  • The new source_checks call makes run propagate compiler and doctest evidence-collection errors, but the public run # Errors contract above still lists only manifest, workspace-enumeration, and fix failures. Document the cargo check/cargo test --doc failure paths so callers can distinguish evidence errors from ordinary findings.
  • Files reviewed: 18/19 changed files
  • Comments generated: 0 new
  • Review effort level: Lite

@github-actions

Copy link
Copy Markdown

⚠️ SemVer check advisory

Inconclusive comparisons

cargo semver-checks could not complete the following comparisons. These failures are informational because an unbuildable baseline is not evidence of a breaking API change.

cargo-aprz-lib (exit 101)

     Cloning 1910173d2c5eb596341960371a2ca25957292c4d
    Building cargo-aprz-lib v1.1.2 (current)
error: running cargo-doc on crate 'cargo-aprz-lib' failed with output:
-----
   Compiling proc-macro2 v1.0.107
   Compiling unicode-ident v1.0.26
   Compiling quote v1.0.47
   Compiling libc v0.2.189
    Checking memchr v2.8.3
    Checking cfg-if v1.0.5
    Checking smallvec v1.16.1
    Checking bytes v1.12.1
    Checking regex-syntax v0.8.11
    Checking once_cell v1.21.4
   Compiling syn v3.0.6
   Compiling thiserror v2.0.20
    Checking stable_deref_trait v1.2.1
   Compiling crc32fast v1.5.2
    Checking scopeguard v1.2.0
   Compiling crossbeam-utils v0.8.23
    Checking regex-automata v0.4.18
   Compiling parking_lot_core v0.9.12
   Compiling getrandom v0.4.3
    Checking zlib-rs v0.6.8
    Checking gix-trace v0.1.21
    Checking bitflags v2.13.2
    Checking fastrand v2.5.0
    Checking lock_api v0.4.14
   Compiling serde_core v1.0.229
    Checking tinyvec v1.13.3
    Checking parking_lot v0.12.5
    Checking bstr v1.13.1
    Checking unicode-normalization v0.1.25
    Checking itoa v1.0.18
    Checking gix-validate v0.11.4
    Checking gix-utils v0.3.6
    Checking crossbeam-channel v0.5.17
    Checking same-file v1.0.6
    Checking walkdir v2.5.0
   Compiling shlex v2.0.1
    Checking equivalent v1.0.2
   Compiling find-msvc-tools v0.1.12
    Checking foldhash v0.2.0
    Checking byteorder v1.5.0
    Checking allocator-api2 v0.2.21
    Checking prodash v31.0.0
   Compiling jobserver v0.1.35
    Checking gix-error v0.2.5
   Compiling cc v1.4.6
   Compiling version_check v0.9.5
   Compiling pkg-config v0.3.34
   Compiling generic-array v0.14.7
    Checking hashbrown v0.17.1
    Checking typenum v1.20.1
   Compiling heapless v0.8.0
   Compiling thiserror-impl v2.0.20
    Checking hash32 v0.3.1
    Checking faster-hex v0.10.0
   Compiling serde v1.0.229
    Checking cpufeatures v0.2.17
    Checking block-buffer v0.10.4
    Checking crypto-common v0.1.7
   Compiling serde_derive v1.0.229
    Checking digest v0.10.7
    Checking jiff-core v0.1.1
    Checking sha1 v0.10.7
    Checking pin-project-lite v0.2.17
    Checking sha1-checked v0.10.0
   Compiling synstructure v0.14.0
    Checking gix-path v0.12.6
    Checking gix-features v0.48.1
   Compiling rustix v1.1.5
    Checking jiff v0.2.37
    Checking gix-hash v0.25.1
    Checking linux-raw-sys v0.12.1
   Compiling zerofrom-derive v0.1.8
    Checking zerofrom v0.1.8
   Compiling yoke-derive v0.8.3
    Checking gix-date v0.15.6
   Compiling autocfg v1.5.1
    Checking gix-actor v0.41.2
    Checking gix-hashtable v0.15.2
    Checking futures-core v0.3.34
    Checking gix-object v0.61.0
    Checking yoke v0.8.3
   Compiling zerovec-derive v0.11.6
    Checking errno v0.3.14
   Compiling tokio-macros v2.7.2
    Checking signal-hook-registry v1.4.8
   Compiling displaydoc v0.2.7
    Checking mio v1.2.3
    Checking socket2 v0.6.5
    Checking memmap2 v0.9.11
   Compiling cmake v0.1.58
    Checking tokio v1.53.1
   Compiling fs_extra v1.3.0
    Checking zerovec v0.11.8
   Compiling rustversion v1.0.23
   Compiling dunce v1.0.5
   Compiling aws-lc-sys v0.45.0
    Checking futures-sink v0.3.34
    Checking tinystr v0.8.4
    Checking tempfile v3.27.0
    Checking gix-fs v0.21.2
    Checking gix-chunk v0.7.3
    Checking writeable v0.6.4
    Checking percent-encoding v2.3.2
    Checking litemap v0.8.3
   Compiling vcpkg v0.2.15
    Checking icu_locale_core v2.3.0
   Compiling libz-sys v1.1.29
    Checking gix-tempfile v23.0.2
    Checking potential_utf v0.1.6
    Checking zerotrie v0.2.5
    Checking gix-quote v0.7.2
    Checking tracing-core v0.1.36
    Checking nonempty v0.12.0
    Checking slab v0.4.12
   Compiling icu_normalizer_data v2.3.0
    Checking zeroize v1.9.0
   Compiling icu_properties_data v2.3.0
   Compiling aws-lc-rs v1.18.1
    Checking simd-adler32 v0.3.10
    Checking utf8_iter v1.0.4
    Checking tracing v0.1.44
    Checking icu_collections v2.3.0
    Checking icu_provider v2.3.1
    Checking http v1.5.0
    Checking adler2 v2.0.1
    Checking fnv v1.0.7
    Checking miniz_oxide v0.9.1
    Checking rustls-pki-types v1.15.1
    Checking gix-commitgraph v0.37.1
    Checking gix-glob v0.26.1
    Checking indexmap v2.14.2
    Checking futures-io v0.3.34
    Checking log v0.4.34
    Checking futures-task v0.3.34
    Checking futures-util v0.3.34
    Checking gix-revwalk v0.32.0
    Checking http-body v1.1.0
    Checking icu_properties v2.3.0
    Checking icu_normalizer v2.3.0
    Checking flate2 v1.1.10
    Checking tokio-util v0.7.19
    Checking gix-lock v23.0.1
   Compiling num-traits v0.2.19
   Compiling syn v2.0.119
   Compiling rustls v0.23.45
   Compiling zmij v1.0.23
   Compiling httparse v1.10.1
    Checking untrusted v0.9.0
    Checking idna_adapter v1.2.2
   Compiling encoding_rs v0.8.41
    Checking futures-channel v0.3.34
    Checking unicode-bom v2.0.3
    Checking subtle v2.6.1
    Checking tower-service v0.3.3
    Checking tower-layer v0.3.3
    Checking try-lock v0.2.5
    Checking atomic-waker v1.1.2
    Checking static_assertions v1.1.0
    Checking h2 v0.4.19
    Checking want v0.3.1
    Checking idna v1.1.0
    Checking form_urlencoded v1.2.2
    Checking gix-config-value v0.18.1
    Checking core_detect v1.0.0
   Compiling multiversion_no_op v1.0.0
    Checking compression-core v0.4.33
   Compiling serde_json v1.0.151
    Checking shell-words v1.1.1
    Checking simdutf8 v0.1.5
    Checking hyper v1.11.1
    Checking gix-command v0.9.2
    Checking compression-codecs v0.4.43
    Checking url v2.5.8
    Checking kstring v2.0.4
    Checking sync_wrapper v1.0.2
    Checking gix-sec v0.14.2
    Checking base64 v0.22.1
    Checking ipnet v2.12.2
    Checking openssl-probe v0.2.1
    Checking rustls-native-certs v0.8.4
    Checking tower v0.5.3
    Checking gix-attributes v0.33.2
    Checking hyper-util v0.1.20
    Checking async-compression v0.4.48
    Checking http-body-util v0.1.5
   Compiling heck v0.5.0
   Compiling crossbeam-epoch v0.9.21
    Checking tower-http v0.6.11
    Checking gix-ref v0.64.0
    Checking gix-url v0.36.2
    Checking gix-packetline v0.21.5
    Checking filetime v0.2.29
    Checking base64 v0.23.1
    Checking mime v0.3.17
   Compiling crossbeam-deque v0.8.8
    Checking either v1.18.0
    Checking gix-prompt v0.15.1
    Checking gix-traverse v0.58.0
    Checking gix-revision v0.46.0
   Compiling zstd-sys v2.1.0+zstd.1.5.7
    Checking gix-bitmap v0.3.3
    Checking hashbrown v0.16.1
    Checking fast_time v0.1.32
   Compiling camino v1.2.6
    Checking arrayvec v0.7.8
   Compiling rayon-core v1.13.0
    Checking uluru v3.1.0
    Checking nm_impl v0.1.50
    Checking gix-index v0.52.0
    Checking gix-refspec v0.42.0
    Checking clru v0.6.3
    Checking gix-credentials v0.38.2
    Checking gix-ignore v0.21.1
   Compiling memoffset v0.9.1
    Checking semver v1.0.28
    Checking winnow v1.0.4
    Checking utf8parse v0.2.2
   Compiling thiserror v1.0.69
   Compiling cfg_aliases v0.2.2
   Compiling nix v0.30.1
    Checking toml_parser v1.1.3+spec-1.1.0
    Checking anstyle-parse v1.0.0
    Checking gix-worktree v0.53.0
    Checking gix-pack v0.71.0
    Checking nm v0.1.50
    Checking gix-config v0.57.0
    Checking gix-filter v0.31.0
    Checking gix-pathspec v0.18.1
   Compiling thiserror-impl v1.0.69
   Compiling maybe-async v0.2.11
    Checking gix-shallow v0.12.1
    Checking gix-negotiate v0.32.0
    Checking arc-swap v1.9.2
   Compiling fs-err v3.3.1
    Checking toml_datetime v1.1.1+spec-1.1.0
    Checking serde_spanned v1.1.1
    Checking smol_str v0.3.6
    Checking io-close v0.3.7
    Checking colorchoice v1.0.5
   Compiling portable-atomic v1.15.0
   Compiling typeid v1.0.3
    Checking anstyle v1.0.14
    Checking anstyle-query v1.1.5
    Checking unicode-properties v0.1.4
    Checking toml_writer v1.1.2+spec-1.1.0
    Checking is_terminal_polyfill v1.70.2
    Checking bumpalo v3.20.3
   Compiling zstd-safe v8.0.0
    Checking bit-vec v0.8.0
    Checking zopfli v0.8.3
    Checking bit-set v0.8.0
    Checking toml v1.1.6+spec-1.1.0
    Checking ra-ap-rustc_lexer v0.160.0
error[E0080]: evaluation panicked: unicode-properties and unicode-ident must use the same Unicode version, `unicode_properties::UNICODE_VERSION` and `unicode_ident::UNICODE_VERSION` are different.
  --> /home/runner/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/ra-ap-rustc_lexer-0.160.0/src/lib.rs:47:9
   |
47 | /         panic!(
48 | |             "unicode-properties and unicode-ident must use the same Unicode version, \
49 | |             `unicode_properties::UNICODE_VERSION` and `unicode_ident::UNICODE_VERSION` are \
50 | |             different."
51 | |         );
   | |_________^ evaluation of `_` failed here

For more information about this error, try `rustc --explain E0080`.
error: could not compile `ra-ap-rustc_lexer` (lib) due to 1 previous error
warning: build failed, waiting for other jobs to finish...

-----

error: failed to build rustdoc for crate cargo-aprz-lib v1.1.2
note: this is usually due to a compilation error in the crate,
      and is unlikely to be a bug in cargo-semver-checks
note: the following command can be used to reproduce the error:
      cargo new --lib example &&
          cd example &&
          echo '[workspace]' >> Cargo.toml &&
          cargo add --path /home/runner/work/ox-tools/ox-tools/crates/cargo-aprz-lib --features internals &&
          cargo check &&
          cargo doc

error: aborting due to failure to build rustdoc for crate cargo-aprz-lib v1.1.2

@psandana

Copy link
Copy Markdown
Contributor

🤖 Pato's Pull Request Agent: [design/correctness] Feature-forwarding optional dependencies are reported as unused.

declared_dependencies collects every key in [dependencies], and judge_one (src/verdict.rs) has no special case for an optional dependency that exists only to forward a feature. Given

[dependencies]
serde = { version = "1", optional = true }
[features]
derive = ["serde/derive"]   # or a bare `dep:serde`

the --all-features evidence build (evidence::gather) enables derive, so serde is compiled and passed as --extern serde, but nothing in the crate names it. unused_crate_dependencies therefore fires and the tool emits Verdict::Unused ("no compiled unit loaded it. remove it…"), even though removing it drops the forwarded feature. This is a real false positive for a common pattern.

The README/design frame the allow-list around side-effect deps ("an allocator or -sys shim"), not feature forwarding, so users hitting this get a confusing suggestion to delete a load-bearing entry. Consider either skipping declarations that appear as dep:<name> or <name>/<feat> targets in any [features] value, or explicitly documenting feature-forwarding optional deps as an allow-list case. Non-blocking.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants