diff --git a/Rules.md b/Rules.md
index a2899d52..1cf737ba 100644
--- a/Rules.md
+++ b/Rules.md
@@ -69,6 +69,7 @@
* [redundantRawValues](#redundantRawValues)
* [redundantReturn](#redundantReturn)
* [redundantSelf](#redundantSelf)
+* [redundantSendable](#redundantSendable)
* [redundantStaticSelf](#redundantStaticSelf)
* [redundantSwiftTestingSuite](#redundantSwiftTestingSuite)
* [redundantThrows](#redundantThrows)
@@ -2929,6 +2930,29 @@ by using `--self init-only`:
+## redundantSendable
+
+Remove redundant explicit Sendable conformance from non-public structs and enums.
+
+
+Examples
+
+```diff
+- struct CacheEntry: Sendable {
++ struct CacheEntry {
+ let id: String
+ }
+
+- fileprivate enum ParsingState: Sendable {
++ fileprivate enum ParsingState {
+ case idle
+ case running
+ }
+```
+
+
+
+
## redundantStaticSelf
Remove explicit `Self` where applicable.
diff --git a/Sources/RuleRegistry.generated.swift b/Sources/RuleRegistry.generated.swift
index 09f9cf3b..1713676d 100644
--- a/Sources/RuleRegistry.generated.swift
+++ b/Sources/RuleRegistry.generated.swift
@@ -92,6 +92,7 @@ let ruleRegistry: [String: FormatRule] = [
"redundantRawValues": .redundantRawValues,
"redundantReturn": .redundantReturn,
"redundantSelf": .redundantSelf,
+ "redundantSendable": .redundantSendable,
"redundantStaticSelf": .redundantStaticSelf,
"redundantSwiftTestingSuite": .redundantSwiftTestingSuite,
"redundantThrows": .redundantThrows,
diff --git a/Sources/Rules/RedundantSendable.swift b/Sources/Rules/RedundantSendable.swift
new file mode 100644
index 00000000..e356cbee
--- /dev/null
+++ b/Sources/Rules/RedundantSendable.swift
@@ -0,0 +1,103 @@
+//
+// RedundantSendable.swift
+// SwiftFormat
+//
+// Created by Nacho Soto on 2/20/2026.
+//
+
+import Foundation
+
+public extension FormatRule {
+ static let redundantSendable = FormatRule(
+ help: "Remove redundant explicit Sendable conformance from non-public structs and enums."
+ ) { formatter in
+ let declarations = formatter.parseDeclarations()
+
+ declarations.forEachRecursiveDeclaration { declaration in
+ guard let typeDeclaration = declaration.asTypeDeclaration,
+ typeDeclaration.keyword == "struct" || typeDeclaration.keyword == "enum"
+ else { return }
+
+ switch typeDeclaration.visibility() {
+ case .public, .open:
+ return
+ case .internal, .package, .fileprivate, .private, nil:
+ break
+ }
+
+ guard let sendableConformance = typeDeclaration.conformances.first(where: {
+ formatter.isRedundantSendableConformance($0.conformance)
+ }) else { return }
+
+ formatter.removeConformance(
+ at: sendableConformance.index,
+ range: sendableConformance.conformance.range
+ )
+ }
+ } examples: {
+ """
+ ```diff
+ - struct CacheEntry: Sendable {
+ + struct CacheEntry {
+ let id: String
+ }
+
+ - fileprivate enum ParsingState: Sendable {
+ + fileprivate enum ParsingState {
+ case idle
+ case running
+ }
+ ```
+ """
+ }
+}
+
+extension Formatter {
+ func isRedundantSendableConformance(_ conformance: TypeName) -> Bool {
+ let significantTokens = conformance.tokens.filter { !$0.isSpaceOrCommentOrLinebreak }
+
+ guard !significantTokens.contains(where: { $0.isAttribute && $0.string == "@unchecked" }) else {
+ return false
+ }
+
+ if significantTokens == [.identifier("Sendable")] {
+ return true
+ }
+
+ guard significantTokens.count == 3,
+ significantTokens[0] == .identifier("Swift"),
+ significantTokens[2] == .identifier("Sendable")
+ else {
+ return false
+ }
+
+ let dotToken = significantTokens[1]
+ return dotToken.isOperator(".") || dotToken == .delimiter(".")
+ }
+
+ func removeConformance(at conformanceIndex: Int, range conformanceRange: ClosedRange) {
+ guard let previousTokenIndex = index(of: .nonSpaceOrCommentOrLinebreak, before: conformanceIndex),
+ let nextTokenIndex = index(of: .nonSpaceOrCommentOrLinebreak, after: conformanceRange.upperBound)
+ else { return }
+
+ let removalRange: ClosedRange
+ if tokens[nextTokenIndex] == .delimiter(",") {
+ let upperBound: Int
+ if token(at: nextTokenIndex + 1)?.isSpace == true {
+ upperBound = nextTokenIndex + 1
+ } else {
+ upperBound = nextTokenIndex
+ }
+ removalRange = conformanceIndex ... upperBound
+ } else {
+ removalRange = previousTokenIndex ... conformanceRange.upperBound
+ }
+
+ // Avoid removing inline comments attached to the conformance list.
+ guard !tokens[removalRange].contains(where: \.isComment) else {
+ return
+ }
+
+ removeTokens(in: removalRange)
+ }
+}
diff --git a/Tests/Rules/RedundantSendableTests.swift b/Tests/Rules/RedundantSendableTests.swift
new file mode 100644
index 00000000..60dca028
--- /dev/null
+++ b/Tests/Rules/RedundantSendableTests.swift
@@ -0,0 +1,119 @@
+//
+// RedundantSendableTests.swift
+// SwiftFormatTests
+//
+// Created by Nacho Soto on 2/20/2026.
+//
+
+import XCTest
+@testable import SwiftFormat
+
+final class RedundantSendableTests: XCTestCase {
+ func testRemovesSendableFromNestedAndTopLevelNonPublicValueTypes() {
+ let input = """
+ struct Outer {
+ enum NestedImplicitAccess: Sendable {}
+ private struct NestedPrivate: Sendable {}
+ @available(*, deprecated)
+ enum NestedAttributed: Sendable {}
+ }
+
+ struct TopLevelImplicitAccess: Sendable {}
+ fileprivate enum TopLevelFileprivate: Sendable {}
+ """
+
+ let output = """
+ struct Outer {
+ enum NestedImplicitAccess {}
+ private struct NestedPrivate {}
+ @available(*, deprecated)
+ enum NestedAttributed {}
+ }
+
+ struct TopLevelImplicitAccess {}
+ fileprivate enum TopLevelFileprivate {}
+ """
+
+ testFormatting(
+ for: input,
+ [output],
+ rules: [.redundantSendable],
+ exclude: [.enumNamespaces, .redundantFileprivate]
+ )
+ }
+
+ func testDoesNotRemoveSendableFromValidCases() {
+ let input = """
+ public struct PublicValue: Sendable {}
+ public enum PublicEnum: Sendable {}
+ private final class PrivateReference: Sendable {}
+ private struct PrivateUnchecked: @unchecked Sendable {}
+ struct NoExplicitSendable {}
+ struct Generic: Equatable where T: Sendable {}
+ """
+
+ testFormatting(for: input, rules: [.redundantSendable], exclude: [.simplifyGenericConstraints])
+ }
+
+ func testIgnoresCommentsAndStrings() {
+ let input = """
+ func demo() {
+ let example = \"\"\"
+ enum FakeNested: Sendable {}
+ private struct AlsoFake: Sendable {}
+ \"\"\"
+ _ = example
+ // struct CommentFake: Sendable {}
+ /*
+ fileprivate enum BlockCommentFake: Sendable {}
+ */
+ }
+ """
+
+ testFormatting(for: input, rules: [.redundantSendable], exclude: [.indent])
+ }
+
+ func testRemovesQualifiedSendableFromMixedConformanceLists() {
+ let input = """
+ struct First: Sendable, Codable {}
+ struct Middle: Codable, Swift.Sendable, Hashable {}
+ enum Last: CaseIterable, Sendable {
+ case value
+ }
+ """
+
+ let output = """
+ struct First: Codable {}
+ struct Middle: Codable, Hashable {}
+ enum Last: CaseIterable {
+ case value
+ }
+ """
+
+ testFormatting(for: input, [output], rules: [.redundantSendable])
+ }
+
+ func testRemovesSendableFromPackageValueTypes() {
+ let input = """
+ package struct PackageStruct: Sendable {
+ package let value: Int
+ }
+
+ package enum PackageEnum: Sendable {
+ case value(Int)
+ }
+ """
+
+ let output = """
+ package struct PackageStruct {
+ package let value: Int
+ }
+
+ package enum PackageEnum {
+ case value(Int)
+ }
+ """
+
+ testFormatting(for: input, [output], rules: [.redundantSendable])
+ }
+}