diff --git a/Sources/SwiftFormatRules/DoNotUseSemicolons.swift b/Sources/SwiftFormatRules/DoNotUseSemicolons.swift index 19122d183..293593ed5 100644 --- a/Sources/SwiftFormatRules/DoNotUseSemicolons.swift +++ b/Sources/SwiftFormatRules/DoNotUseSemicolons.swift @@ -53,6 +53,7 @@ public final class DoNotUseSemicolons: SyntaxFormatRule { } var newItem = visitedItem + defer { newItems[idx] = newItem } // Check if the leading trivia for this statement needs a new line. if previousHadSemicolon, let firstToken = newItem.firstToken, @@ -68,6 +69,19 @@ public final class DoNotUseSemicolons: SyntaxFormatRule { // If there's a semicolon, diagnose and remove it. if let semicolon = item.semicolon { + + // Exception: do not remove the semicolon if it is separating a 'do' statement from a 'while' statement. + if Syntax(item).as(CodeBlockItemSyntax.self)?.children.first?.is(DoStmtSyntax.self) == true, + idx < node.count - 1 + { + let nextItem = node.children[node.children.index(after: item.index)] + if Syntax(nextItem).as(CodeBlockItemSyntax.self)? + .children.first?.is(WhileStmtSyntax.self) == true + { + continue + } + } + // This discards any trailingTrivia from the semicolon. That trivia is at most some spaces, // and the pretty printer adds any necessary spaces so it's safe to discard. newItem = newItem.withSemicolon(nil) @@ -77,7 +91,6 @@ public final class DoNotUseSemicolons: SyntaxFormatRule { diagnose(.removeSemicolon, on: semicolon) } } - newItems[idx] = newItem } return nodeCreator(newItems) } diff --git a/Tests/SwiftFormatRulesTests/DoNotUseSemicolonsTests.swift b/Tests/SwiftFormatRulesTests/DoNotUseSemicolonsTests.swift index f7cf75e9c..7768c5192 100644 --- a/Tests/SwiftFormatRulesTests/DoNotUseSemicolonsTests.swift +++ b/Tests/SwiftFormatRulesTests/DoNotUseSemicolonsTests.swift @@ -89,4 +89,41 @@ final class DoNotUseSemicolonsTests: LintOrFormatRuleTestCase { print("7") """) } + + func testSemicolonsSeparatingDoWhile() { + XCTAssertFormatting( + DoNotUseSemicolons.self, + input: """ + do { f() }; + while someCondition { g() } + + do { + f() + }; + + // Comment and whitespace separating blocks. + while someCondition { + g() + } + + do { f() }; + for _ in 0..<10 { g() } + """, + expected: """ + do { f() }; + while someCondition { g() } + + do { + f() + }; + + // Comment and whitespace separating blocks. + while someCondition { + g() + } + + do { f() } + for _ in 0..<10 { g() } + """) + } }