Describe the bug
Periphery falsely reports a type as unused when it is only referenced as a property type inside a nested struct. The type is used -- removing it would break compilation -- but Periphery does not follow property type references on child declarations during its used-declaration marking pass.
For example, consider a view model containing a nested section struct, where the section references an item type:
struct RiderItem: Identifiable, Hashable {
let id: UUID
let name: String
}
public struct RiderViewModel {
struct RiderSection: Identifiable, Hashable {
let id: UUID
var equipment: [RiderItem]
}
var sections: [RiderSection] = []
public mutating func addSection() {
sections.append(RiderSection(id: UUID(), equipment: []))
}
}
Periphery reports RiderItem as unused, even though RiderSection.equipment depends on it. The issue occurs because RiderItem is only referenced as a property type (var equipment: [RiderItem]) within the nested RiderSection struct. Swift's indexer associates that type reference with the property declaration itself, not with the containing struct. When Periphery marks RiderViewModel as used, it walks references and related declarations but never descends into child declarations to discover the property type reference to RiderItem.
The same problem applies more broadly: any type referenced exclusively as a property type on a child declaration of a used type will be missed.
Reproduction
- Create a Swift package or Xcode project containing the code above.
- Ensure
RiderViewModel is public (or otherwise retained).
- Run
periphery scan.
- Observe that
RiderItem is reported as unused.
Environment
Apple Swift version 6.2.3 (swiftlang-6.2.3.3.21 clang-1700.6.3.2)
Xcode 26.2 (Build version 17C52)
Describe the bug
Periphery falsely reports a type as unused when it is only referenced as a property type inside a nested struct. The type is used -- removing it would break compilation -- but Periphery does not follow property type references on child declarations during its used-declaration marking pass.
For example, consider a view model containing a nested section struct, where the section references an item type:
Periphery reports
RiderItemas unused, even thoughRiderSection.equipmentdepends on it. The issue occurs becauseRiderItemis only referenced as a property type (var equipment: [RiderItem]) within the nestedRiderSectionstruct. Swift's indexer associates that type reference with the property declaration itself, not with the containing struct. When Periphery marksRiderViewModelas used, it walks references and related declarations but never descends into child declarations to discover the property type reference toRiderItem.The same problem applies more broadly: any type referenced exclusively as a property type on a child declaration of a used type will be missed.
Reproduction
RiderViewModelis public (or otherwise retained).periphery scan.RiderItemis reported as unused.Environment