From 8e8d4e45d5bf914cefab9d4a96e6803ed6a0a551 Mon Sep 17 00:00:00 2001 From: Copilot <198982749+Copilot@users.noreply.github.com> Date: Mon, 16 Feb 2026 10:29:19 -0800 Subject: [PATCH] Fix issue where `redundantViewBuilder` would remove `@ViewBuilder` from protocol members (#2364) Co-authored-by: calda <1811727+calda@users.noreply.github.com> --- Sources/Rules/RedundantViewBuilder.swift | 6 +++ Tests/Rules/RedundantViewBuilderTests.swift | 41 +++++++++++++++++++++ 2 files changed, 47 insertions(+) diff --git a/Sources/Rules/RedundantViewBuilder.swift b/Sources/Rules/RedundantViewBuilder.swift index ad12b010..b6e4f73e 100644 --- a/Sources/Rules/RedundantViewBuilder.swift +++ b/Sources/Rules/RedundantViewBuilder.swift @@ -20,6 +20,12 @@ public extension FormatRule { guard let viewBuilderIndex = formatter.indexOfViewBuilderAttribute(for: declaration) else { return } + // Never remove @ViewBuilder from protocol members, as conforming types + // rely on the implicit result builder being added + if formatter.isInsideProtocol(at: declaration.keywordIndex) { + return + } + let bodyScope: ClosedRange? let isBodyMember: Bool diff --git a/Tests/Rules/RedundantViewBuilderTests.swift b/Tests/Rules/RedundantViewBuilderTests.swift index bc5090f0..7a079bfa 100644 --- a/Tests/Rules/RedundantViewBuilderTests.swift +++ b/Tests/Rules/RedundantViewBuilderTests.swift @@ -497,4 +497,45 @@ final class RedundantViewBuilderTests: XCTestCase { """ testFormatting(for: input, output, rule: .redundantViewBuilder) } + + func testKeepViewBuilderOnProtocolMember() { + // Protocol members with @ViewBuilder should not have it removed, + // as conforming types rely on the implicit result builder + let input = """ + protocol Foo { + associatedtype MyFoo: View + + @ViewBuilder + var myBody: MyFoo { get } + } + """ + testFormatting(for: input, rule: .redundantViewBuilder) + } + + func testKeepViewBuilderOnProtocolFunction() { + let input = """ + protocol ViewProvider { + @ViewBuilder + func makeView() -> some View + } + """ + testFormatting(for: input, rule: .redundantViewBuilder) + } + + func testKeepViewBuilderOnProtocolComputedProperty() { + let input = """ + protocol ContentProvider { + @ViewBuilder + var content: some View { get } + } + + struct MyContent: ContentProvider { + var content: some View { + Text("Hello") + Text("World") + } + } + """ + testFormatting(for: input, rule: .redundantViewBuilder) + } }