diff --git a/Sources/Rules/RedundantSendable.swift b/Sources/Rules/RedundantSendable.swift index e356cbee..03b7abaf 100644 --- a/Sources/Rules/RedundantSendable.swift +++ b/Sources/Rules/RedundantSendable.swift @@ -21,8 +21,16 @@ public extension FormatRule { switch typeDeclaration.visibility() { case .public, .open: return - case .internal, .package, .fileprivate, .private, nil: + case .internal, .package, .fileprivate, .private: break + case nil: + // A type with no explicit access modifier inside a public extension is effectively public + let isInPublicExtension = typeDeclaration.parentDeclarations.last.map { + $0.keyword == "extension" && $0.visibility() == .public + } ?? false + if isInPublicExtension { + return + } } guard let sendableConformance = typeDeclaration.conformances.first(where: { diff --git a/Tests/Rules/RedundantSendableTests.swift b/Tests/Rules/RedundantSendableTests.swift index 60dca028..7d97bc5e 100644 --- a/Tests/Rules/RedundantSendableTests.swift +++ b/Tests/Rules/RedundantSendableTests.swift @@ -93,6 +93,39 @@ final class RedundantSendableTests: XCTestCase { testFormatting(for: input, [output], rules: [.redundantSendable]) } + func testDoesNotRemoveSendableFromTypeInsidePublicExtension() { + let input = """ + public struct OuterStruct {} + + public extension OuterStruct { + struct InnerStruct: Sendable {} + enum InnerEnum: Sendable {} + } + """ + + testFormatting(for: input, rules: [.redundantSendable]) + } + + func testRemovesSendableFromTypeInsideInternalExtension() { + let input = """ + struct OuterStruct {} + + extension OuterStruct { + struct InnerStruct: Sendable {} + } + """ + + let output = """ + struct OuterStruct {} + + extension OuterStruct { + struct InnerStruct {} + } + """ + + testFormatting(for: input, [output], rules: [.redundantSendable]) + } + func testRemovesSendableFromPackageValueTypes() { let input = """ package struct PackageStruct: Sendable {