packages: add a find-unused verb for package references no source uses - #175
Merged
Merged
Conversation
…s [minor] Neither `packages update` nor `packages migrate-cpm` answers which PackageReferences are declared but never actually used, and CPM makes an unused entry invisible at the project level. `ktsu packages find-unused --path .` scans every .csproj beneath a path, so it works for a single project, a solution, or a workspace of cached repos. A reference counts as used when a using directive, an MSBuild Using item, or the raw source names the package, allowing for the namespace and the package id rarely matching exactly: a namespace beneath the id, a parent of it, or an id carrying a glued suffix such as LibreHardwareMonitorLib all resolve. References that cannot appear in source by design are classified build-time only rather than unused, so analyzers, MSBuild task packages, PrivateAssets ="all" references and any whose compile assets are suppressed stay out of the report instead of filling it with false positives. They are listed only under --show-build-time. Orphaned PackageVersion entries are reported too, counting Directory.Build.props/.targets as referencing files so the packages they add to every project are not mistaken for orphans. Reports only; removing a reference stays a human decision. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01ApcD2dHnUMQG8DDb4ixJ6S
Each test pins one rule: reporting a genuinely unused reference, the three namespace-to-id shapes that count as use, the glued-suffix case, holding back analyzers and build-time-only references, ignoring bin/obj output, scanning a workspace versus a single project, and orphaned PackageVersion detection including the Directory.Build.props exemption. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01ApcD2dHnUMQG8DDb4ixJ6S
…body [patch] Both scans walked every descendant and filtered inside the loop. Moving the filter into the pipeline leaves each loop doing only its own work, and lets the set take the results directly. Behaviour is unchanged. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01ApcD2dHnUMQG8DDb4ixJ6S
…patch] The analyzer was well covered but the service and command around it were not, leaving new-code coverage at 59.9% against an 80% gate. What a report-only verb prints is its whole deliverable, so the gap was in the part that matters most. PackagesServiceTests covers both FindUnusedAsync overloads and each branch of the rendering: findings listed, nothing found, no projects, a missing path, build-time-only references hidden until asked for, and the orphaned PackageVersion section. PackagesFindUnusedCommandTests covers the verb's file-versus-directory dispatch and its flag. The console-capture helper RepoServiceTests already had moves to ConsoleCapture so both classes share one copy rather than duplicating it. New-code coverage measured locally at 93.6% (277/296 added lines). Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01ApcD2dHnUMQG8DDb4ixJ6S
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.



Fixes #123
What this adds
ktsu packages find-unused --path .reportsPackageReferences that the declaring project's own source never refers to. It scans every.csprojbeneath the given path, so the same verb serves a single project, a solution, or a workspace of cached repos.How "used" is decided
A package counts as used when a
usingdirective, an MSBuildUsingitem, or the raw source names it. Package ids and the namespaces they ship rarely match exactly, so three shapes resolve:Spectre.Console.RenderingforSpectre.ConsoleMicrosoft.Extensions.Loggingfor…Logging.AbstractionsLibreHardwareMonitor.HardwareforLibreHardwareMonitorLibThe glued-suffix rule came out of running this against KtsuTools itself, where
LibreHardwareMonitorLibwas initially reported despiteMachineMonitorService.csusing it. Stripping only ever adds a candidate, so it cannot lose a match.Avoiding false positives
The acceptance criteria call out that the report is worthless if it is full of things that cannot appear in source. Those are classified
BuildTimeOnlyrather than unused and kept out of the main table, surfacing only under--show-build-time:PrivateAssets="all"referencesExcludeAssetscontainingcompile/all, orIncludeAssetsthat omits compile assetsOrphaned
PackageVersionentries inDirectory.Packages.propsare reported as a second section, since the issue notes CPM makes an unused entry invisible at the project level.Directory.Build.props/.targetscount as referencing files, so the packages they add to every project (Polyfill, SourceLink here) are not mistaken for orphans.Reports only — no removal, per the acceptance criteria.
Testing
149/149 passing, build clean with 0 warnings, new-code coverage 93.4% per SonarCloud.
UnusedPackageAnalyzerTests(15) pins the classification rules. Each was proven load-bearing by mutating the behaviour it guards and confirming that test, and only that test, failed:HoldsBackAnalyzersAndBuildTimeOnlyReferencesbin/objexclusion removedIgnoresBuildOutputUnderBinAndObjTreatsAParentNamespaceAsUseDirectory.Build.*scan removedDoesNotOrphanAPackageVersionThatDirectoryBuildPropsReferencesTreatsAGluedIdSuffixAsUsePackagesServiceTests(8) andPackagesFindUnusedCommandTests(4) cover what the verb actually prints — which for a report-only command is the whole deliverable: bothFindUnusedAsyncoverloads, findings listed, nothing found, no projects, a missing path, build-time-only hidden until asked for, the orphanedPackageVersionsection, and the command's file-versus-directory dispatch.The console-capture helper
RepoServiceTestsalready had moved to a sharedConsoleCapturerather than being duplicated.Note on the findings above
Running the verb against this repo surfaces 24 apparently-unused references, including
ktsu.Semantics.Stringsacross 11 projects and the directSemanticsreferences onFileDedupethat #138 flagged as a possible tidy-up. Those are left alone here — this PR adds the reporting, and acting on it stays a separate, human call.🤖 Generated with Claude Code
https://claude.ai/code/session_01ApcD2dHnUMQG8DDb4ixJ6S