import SwiftSyntax @SwiftSyntaxRule(explicitRewriter: true, optIn: true) struct UnneededParenthesesInClosureArgumentRule: Rule { var configuration = SeverityConfiguration(.warning) static let description = RuleDescription( identifier: "unneeded_parentheses_in_closure_argument", name: "Unneeded Parentheses in Closure Argument", description: "Parentheses are not needed when declaring closure arguments", kind: .style, nonTriggeringExamples: [ Example("let foo = { (bar: Int) in }"), Example("let foo = { bar, _ in }"), Example("let foo = { bar in }"), Example("let foo = { bar -> Bool in return true }"), Example(""" DispatchQueue.main.async { () -> Void in doSomething() } """), Example(""" registerFilter(name) { any, args throws -> Any? in doSomething(any, args) } """, excludeFromDocumentation: true), ], triggeringExamples: [ Example("call(arg: { ↓(bar) in })"), Example("call(arg: { ↓(bar, _) in })"), Example("let foo = { ↓(bar) -> Bool in return true }"), Example("foo.map { ($0, $0) }.forEach { ↓(x, y) in }"), Example("foo.bar { [weak self] ↓(x, y) in }"), Example(""" [].first { ↓(temp) in [].first { ↓(temp) in [].first { ↓(temp) in _ = temp return false } return false } return false } """), Example(""" [].first { temp in [].first { ↓(temp) in [].first { ↓(temp) in _ = temp return false } return false } return false } """), Example(""" registerFilter(name) { ↓(any, args) throws -> Any? in doSomething(any, args) } """, excludeFromDocumentation: true), ], corrections: [ Example("call(arg: { ↓(bar) in })"): Example("call(arg: { bar in })"), Example("call(arg: { ↓(bar, _) in })"): Example("call(arg: { bar, _ in })"), Example("call(arg: { ↓(bar, _)in })"): Example("call(arg: { bar, _ in })"), Example("let foo = { ↓(bar) -> Bool in return true }"): Example("let foo = { bar -> Bool in return true }"), Example("method { ↓(foo, bar) in }"): Example("method { foo, bar in }"), Example("foo.map { ($0, $0) }.forEach { ↓(x, y) in }"): Example("foo.map { ($0, $0) }.forEach { x, y in }"), Example("foo.bar { [weak self] ↓(x, y) in }"): Example("foo.bar { [weak self] x, y in }"), ] ) } private extension UnneededParenthesesInClosureArgumentRule { final class Visitor: ViolationsSyntaxVisitor { override func visitPost(_ node: ClosureSignatureSyntax) { guard let clause = node.parameterClause?.as(ClosureParameterClauseSyntax.self), clause.parameters.isNotEmpty, clause.parameters.allSatisfy({ $0.type == nil }) else { return } violations.append(clause.positionAfterSkippingLeadingTrivia) } } final class Rewriter: ViolationsSyntaxRewriter { override func visit(_ node: ClosureSignatureSyntax) -> ClosureSignatureSyntax { guard let clause = node.parameterClause?.as(ClosureParameterClauseSyntax.self), clause.parameters.isNotEmpty, clause.parameters.allSatisfy({ $0.type == nil }) else { return super.visit(node) } let items = clause.parameters.enumerated().compactMap { idx, param -> ClosureShorthandParameterSyntax? in let name = param.firstName let isLast = idx == clause.parameters.count - 1 return ClosureShorthandParameterSyntax( name: name, trailingComma: isLast ? nil : .commaToken(trailingTrivia: Trivia(pieces: [.spaces(1)])) ) } numberOfCorrections += 1 let paramList = ClosureShorthandParameterListSyntax(items).with(\.trailingTrivia, .spaces(1)) return super.visit(node.with(\.parameterClause, .init(paramList))) } } }