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
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions