Skip to content

Commit 5cca489

Browse files
dylansturgallevato
authored andcommitted
Recursively apply NoEmptyTrailingClosureParentheses rule.
This rule wasn't being applied recursively, which meant it wouldn't be applied to the trailing closure of a function call where parens where removed.
1 parent 20162cd commit 5cca489

File tree

2 files changed

+63
-9
lines changed

2 files changed

+63
-9
lines changed

Sources/SwiftFormatRules/NoEmptyTrailingClosureParentheses.swift

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -22,22 +22,31 @@ import SwiftSyntax
2222
public final class NoEmptyTrailingClosureParentheses: SyntaxFormatRule {
2323

2424
public override func visit(_ node: FunctionCallExprSyntax) -> ExprSyntax {
25-
guard node.argumentList.count == 0 else { return ExprSyntax(node) }
25+
guard node.argumentList.count == 0 else { return super.visit(node) }
2626

27-
guard node.trailingClosure != nil && node.argumentList.isEmpty && node.leftParen != nil else {
28-
return ExprSyntax(node)
27+
guard let trailingClosure = node.trailingClosure,
28+
node.argumentList.isEmpty && node.leftParen != nil else
29+
{
30+
return super.visit(node)
2931
}
3032
guard let name = node.calledExpression.lastToken?.withoutTrivia() else {
31-
return ExprSyntax(node)
33+
return super.visit(node)
3234
}
3335

3436
diagnose(.removeEmptyTrailingParentheses(name: "\(name)"), on: node)
3537

38+
// Need to visit `calledExpression` before creating a new node so that the location data (column
39+
// and line numbers) is available.
40+
guard let rewrittenCalledExpr = ExprSyntax(visit(Syntax(node.calledExpression))) else {
41+
return super.visit(node)
42+
}
3643
let formattedExp = replaceTrivia(
37-
on: node.calledExpression,
38-
token: node.calledExpression.lastToken,
44+
on: rewrittenCalledExpr,
45+
token: rewrittenCalledExpr.lastToken,
3946
trailingTrivia: .spaces(1))
47+
let formattedClosure = visit(trailingClosure).as(ClosureExprSyntax.self)
4048
let result = node.withLeftParen(nil).withRightParen(nil).withCalledExpression(formattedExp)
49+
.withTrailingClosure(formattedClosure)
4150
return ExprSyntax(result)
4251
}
4352
}

Tests/SwiftFormatRulesTests/NoEmptyTrailingClosureParenthesesTests.swift

Lines changed: 48 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,21 @@ final class NoEmptyTrailingClosureParenthesesTests: LintOrFormatRuleTestCase {
1919
func myfunc(cls: MyClass) {
2020
cls.myBadClosure() { $0 }
2121
}
22+
DispatchQueue.main.async() {
23+
greetEnthusiastically() { "John" }
24+
DispatchQueue.main.async() {
25+
greetEnthusiastically() { "Willis" }
26+
}
27+
}
28+
DispatchQueue.global.async(inGroup: blah) {
29+
DispatchQueue.main.async() {
30+
greetEnthusiastically() { "Willis" }
31+
}
32+
DispatchQueue.main.async {
33+
greetEnthusiastically() { "Willis" }
34+
}
35+
}
36+
foo(bar() { baz })() { blah }
2237
""",
2338
expected: """
2439
func greetEnthusiastically(_ nameProvider: () -> String) {
@@ -35,9 +50,39 @@ final class NoEmptyTrailingClosureParenthesesTests: LintOrFormatRuleTestCase {
3550
func myfunc(cls: MyClass) {
3651
cls.myBadClosure { $0 }
3752
}
38-
""")
39-
XCTAssertDiagnosed(.removeEmptyTrailingParentheses(name: "greetEnthusiastically"))
40-
XCTAssertDiagnosed(.removeEmptyTrailingParentheses(name: "myBadClosure"))
53+
DispatchQueue.main.async {
54+
greetEnthusiastically { "John" }
55+
DispatchQueue.main.async {
56+
greetEnthusiastically { "Willis" }
57+
}
58+
}
59+
DispatchQueue.global.async(inGroup: blah) {
60+
DispatchQueue.main.async {
61+
greetEnthusiastically { "Willis" }
62+
}
63+
DispatchQueue.main.async {
64+
greetEnthusiastically { "Willis" }
65+
}
66+
}
67+
foo(bar { baz }) { blah }
68+
""",
69+
checkForUnassertedDiagnostics: true)
70+
XCTAssertDiagnosed(
71+
.removeEmptyTrailingParentheses(name: "greetEnthusiastically"), line: 7, column: 1)
72+
XCTAssertDiagnosed(.removeEmptyTrailingParentheses(name: "myBadClosure"), line: 13, column: 3)
4173
XCTAssertNotDiagnosed(.removeEmptyTrailingParentheses(name: "myClosure"))
74+
XCTAssertDiagnosed(.removeEmptyTrailingParentheses(name: "async"), line: 15, column: 1)
75+
XCTAssertDiagnosed(
76+
.removeEmptyTrailingParentheses(name: "greetEnthusiastically"), line: 16, column: 3)
77+
XCTAssertDiagnosed(.removeEmptyTrailingParentheses(name: "async"), line: 17, column: 3)
78+
XCTAssertDiagnosed(
79+
.removeEmptyTrailingParentheses(name: "greetEnthusiastically"), line: 18, column: 5)
80+
XCTAssertDiagnosed(.removeEmptyTrailingParentheses(name: "async"), line: 22, column: 3)
81+
XCTAssertDiagnosed(
82+
.removeEmptyTrailingParentheses(name: "greetEnthusiastically"), line: 23, column: 5)
83+
XCTAssertDiagnosed(
84+
.removeEmptyTrailingParentheses(name: "greetEnthusiastically"), line: 26, column: 5)
85+
XCTAssertDiagnosed(.removeEmptyTrailingParentheses(name: ")"), line: 29, column: 1)
86+
XCTAssertDiagnosed(.removeEmptyTrailingParentheses(name: "bar"), line: 29, column: 5)
4287
}
4388
}

0 commit comments

Comments
 (0)