Skip to content

False-positive: redundant public on property implementing cross-module protocol requirement #1108

@AllDmeat

Description

@AllDmeat

Description

Periphery 3.7.2 incorrectly reports redundant public accessibility on a property that implements a public protocol requirement defined in another module. The property is used — via the protocol extension in the defining module — but Periphery does not track cross-module protocol witness usage.

Issue

When a public protocol in module A requires a property (static var bundle: Bundle), and module A also provides a protocol extension that uses that property, a conforming type in module B gets flagged for "redundant public accessibility" on the property — even though removing public would be a compile error (protocol requirement from a public protocol must be at least as accessible).

Example

Repository: https://github.com/AllDmeat/PeripheryIssueSample
Commit hash: e04b7a5

Protocol in SampleDS module: Storyboardable.swift

import UIKit

@MainActor
public protocol Storyboardable {
    static var bundle: Bundle { get }
}

extension Storyboardable where Self: UIViewController {
    public static func instantiateInitialFromStoryboard() -> Self {
        let name = String(describing: self)
        let storyboard = UIStoryboard(name: name, bundle: bundle) // ← bundle is used here
        return storyboard.instantiateInitialViewController() as! Self
    }
}

Conforming type in MyProject target: RedundantPublicExample.swift

import UIKit
import SampleDS

final class SampleViewController: UIViewController, Storyboardable {
    // ⚠️ Periphery reports: "Redundant public accessibility"
    // But removing `public` is a compile error — protocol is public.
    public static var bundle: Bundle { Bundle(for: SampleViewController.self) }
}

Usage in ViewController.swift

func showSample() {
    let vc = SampleViewController.instantiateInitialFromStoryboard()
    present(vc, animated: true)
}

The call chain: showSample()instantiateInitialFromStoryboard() (protocol extension in SampleDS) → reads bundle property. So bundle is used at runtime.

Periphery Output

RedundantPublicExample.swift:30:23: warning: Redundant public accessibility for property 'bundle' (not used outside of MyProject)

Expected Behavior

bundle should not be flagged as having redundant public accessibility, since:

  1. It implements a requirement of a public protocol from another module
  2. Removing public is a compile error
  3. The property is actively used via the protocol extension in SampleDS

Real-World Impact

In our iOS project (~50 modules), this pattern produces 13 identical false positives for Storyboardable.bundle and similar cross-module protocol conformances. These cannot be suppressed with // periphery:ignore without triggering "superfluous ignore comment" warnings in a second scan configuration.

Environment

  • Periphery version: 3.7.2
  • Xcode version: 26.3 (Build version 17C529)
  • Swift version: 6.2.4

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions