-
-
Notifications
You must be signed in to change notification settings - Fork 223
Description
Describe the bug
When Periphery detects code that is only referenced from #Preview macro blocks, it correctly identifies that code as unused. However, the current detection has two related problems:
-
False positives on preview-support code. If a declaration is referenced from multiple
#Previewblocks, and at least one of those previews is for a view that is used in the app, that declaration should be kept. Currently, all#Previewmacro expansions are un-retained equally, so code that exists to support a legitimate preview gets flagged as unused.For example, a helper like
previewPadding()or a view likeViewOnlyUsedInPreviewmight appear in both a "dead" preview (one that only previews unused views) and a "live" preview (one that previews a view used in the app). The helper should be kept because the live preview needs it, but Periphery flags it as unused. -
The
#Previewblock itself is never reported. When a#Previewblock exclusively references unused code (views not used anywhere in the app), Periphery flags the referenced views but does not flag the#Previewblock itself. If the warning is heeded and the view is removed, this would leave an orphaned preview in the codebase that the developer has no reason to keep. Ideally, Periphery would report the dead#Previewblock as unused (similar to behavior of aPreviewProviderpreview), giving the developer a clear signal to remove it along with the dead code it references.
The root cause is that the algorithm treats all #Preview blocks identically without distinguishing between previews that exercise live app code and previews that only reference otherwise-dead code.
Reproduction
The attached PreviewPeriphery Xcode project demonstrates the scenarios. The key file is ContentView.swift, which contains:
ContentViewandUsedView— views used by the app (via@mainentry point). Should be kept.ViewOnlyUsedInPreviewandpreviewPadding()— only referenced from previews, but one of those previews is forUsedView(live app code). Should be kept, but currently flagged.UnusedView,CompletelyUnusedView,DeadChainedView,DeadChainedHelper— views not used anywhere in the app, only referenced from dead previews. Should be flagged as unused.- Three dead
#Previewblocks — each exclusively references unused views. These should themselves be reported as unused, but currently are not. - Three live
#Previewblocks — each references at least one view that is used in the app (UsedVieworContentView). These should not be reported. - Old-style
PreviewProviderstructs for both a used and an unused view, to verify consistent behavior across preview styles.
Environment
periphery version: 3.4.0
Apple Swift version 6.2.3 (swiftlang-6.2.3.3.21 clang-1700.6.3.2)
Target: arm64-apple-macosx26.0
Xcode 26.2
Build version 17C52