Skip to content

Commit 250a876

Browse files
committed
Fix spurious return removal
1 parent 218848f commit 250a876

File tree

4 files changed

+68
-6
lines changed

4 files changed

+68
-6
lines changed

Sources/ParsingHelpers.swift

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -557,8 +557,7 @@ extension Formatter {
557557
default:
558558
return true
559559
}
560-
case .operator("->", .infix), .keyword("init"),
561-
.keyword("subscript"):
560+
case .operator("->", .infix), .keyword("init"), .keyword("subscript"), .keyword("throws"):
562561
return false
563562
case .endOfScope(">"):
564563
guard let startIndex = index(of: .startOfScope("<"), before: prev) else {
@@ -721,7 +720,8 @@ extension Formatter {
721720
guard let nextToken = next(.nonSpaceOrComment, after: braceIndex),
722721
!nextToken.isOperator(ofType: .infix),
723722
!nextToken.isOperator(ofType: .postfix),
724-
nextToken != .startOfScope("(")
723+
nextToken != .startOfScope("("),
724+
nextToken != .startOfScope("{")
725725
else {
726726
return isAfterBrace(index, braceIndex)
727727
}

Sources/Rules.swift

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3283,12 +3283,13 @@ public struct _FormatRules {
32833283
formatter.forEach(.startOfScope("{")) { startOfScopeIndex, _ in
32843284
// Closures always supported implicit returns, but other types of scopes
32853285
// only support implicit return in Swift 5.1+ (SE-0255)
3286-
if formatter.options.swiftVersion < "5.1", !formatter.isStartOfClosure(at: startOfScopeIndex) {
3286+
let isClosure = formatter.isStartOfClosure(at: startOfScopeIndex)
3287+
if formatter.options.swiftVersion < "5.1", !isClosure {
32873288
return
32883289
}
32893290

32903291
// Make sure this is a type of scope that supports implicit returns
3291-
if formatter.isConditionalStatement(at: startOfScopeIndex) ||
3292+
if !isClosure, formatter.isConditionalStatement(at: startOfScopeIndex) ||
32923293
["do", "else", "catch"].contains(formatter.lastSignificantKeyword(at: startOfScopeIndex, excluding: ["throws"]))
32933294
{
32943295
return
@@ -3304,7 +3305,7 @@ public struct _FormatRules {
33043305
}
33053306

33063307
// Make sure we aren't in a failable `init?`, where explicit return is required
3307-
if let lastSignificantKeywordIndex = formatter.indexOfLastSignificantKeyword(at: startOfScopeIndex),
3308+
if !isClosure, let lastSignificantKeywordIndex = formatter.indexOfLastSignificantKeyword(at: startOfScopeIndex),
33083309
formatter.tokens[lastSignificantKeywordIndex] == .keyword("init"),
33093310
let nextToken = formatter.next(.nonSpaceOrCommentOrLinebreak, after: lastSignificantKeywordIndex),
33103311
nextToken == .operator("?", .postfix)

Tests/ParsingHelpersTests.swift

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,14 @@ class ParsingHelpersTests: XCTestCase {
126126
XCTAssertFalse(formatter.isStartOfClosure(at: 18))
127127
}
128128

129+
func testClosureInIfCondition() {
130+
let formatter = Formatter(tokenize("""
131+
if let btn = btns.first { !$0.isHidden } {}
132+
"""))
133+
XCTAssertTrue(formatter.isStartOfClosure(at: 12))
134+
XCTAssertFalse(formatter.isStartOfClosure(at: 21))
135+
}
136+
129137
// functions
130138

131139
func testFunctionBracesNotTreatedAsClosure() {
@@ -574,6 +582,14 @@ class ParsingHelpersTests: XCTestCase {
574582
XCTAssert(formatter.isStartOfClosure(at: 5))
575583
}
576584

585+
func testBraceAfterTypedThrows() {
586+
let formatter = Formatter(tokenize("""
587+
do throws(Foo) {} catch {}
588+
"""))
589+
XCTAssertFalse(formatter.isStartOfClosure(at: 7))
590+
XCTAssertFalse(formatter.isStartOfClosure(at: 12))
591+
}
592+
577593
// MARK: isAccessorKeyword
578594

579595
func testDidSet() {
@@ -1650,6 +1666,14 @@ class ParsingHelpersTests: XCTestCase {
16501666
}
16511667
}
16521668

1669+
func testStartOfConditionalStatementConditionContainingUnParenthesizedClosure() {
1670+
let formatter = Formatter(tokenize("""
1671+
if let btn = btns.first { !$0.isHidden } {}
1672+
"""))
1673+
XCTAssertEqual(formatter.startOfConditionalStatement(at: 12), 0)
1674+
XCTAssertEqual(formatter.startOfConditionalStatement(at: 21), 0)
1675+
}
1676+
16531677
// MARK: isStartOfStatement
16541678

16551679
func testAsyncAfterFuncNotTreatedAsStartOfStatement() {

Tests/RulesTests+Redundancy.swift

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3008,6 +3008,43 @@ class RedundancyTests: RulesTests {
30083008
options: FormatOptions(swiftVersion: "5.1"), exclude: ["redundantProperty"])
30093009
}
30103010

3011+
func testNoRemoveRequiredReturnInIfClosure() {
3012+
let input = """
3013+
func findButton() -> Button? {
3014+
let btns = [top, content, bottom]
3015+
if let btn = btns.first { !$0.isHidden && $0.alpha > 0.01 } {
3016+
return btn
3017+
}
3018+
return btns.first
3019+
}
3020+
"""
3021+
let options = FormatOptions(swiftVersion: "5.1")
3022+
testFormatting(for: input, rule: FormatRules.redundantReturn, options: options)
3023+
}
3024+
3025+
func testRemoveRedundantReturnInIfClosure() {
3026+
let input = """
3027+
func findButton() -> Button? {
3028+
let btns = [top, content, bottom]
3029+
if let btn = btns.first { return !$0.isHidden && $0.alpha > 0.01 } {
3030+
print("hello")
3031+
}
3032+
return btns.first
3033+
}
3034+
"""
3035+
let output = """
3036+
func findButton() -> Button? {
3037+
let btns = [top, content, bottom]
3038+
if let btn = btns.first { !$0.isHidden && $0.alpha > 0.01 } {
3039+
print("hello")
3040+
}
3041+
return btns.first
3042+
}
3043+
"""
3044+
let options = FormatOptions(swiftVersion: "5.1")
3045+
testFormatting(for: input, output, rule: FormatRules.redundantReturn, options: options)
3046+
}
3047+
30113048
func testDisableNextRedundantReturn() {
30123049
let input = """
30133050
func foo() -> Foo {

0 commit comments

Comments
 (0)