Files
SwiftLint/Source/SwiftLintBuiltInRules/Rules/Style/UnneededParenthesesInClosureArgumentRule.swift
T
Danny Mösch 58928b7e40 Enforce any on existential types (#5273)
This makes syntactically clear which types are rather expensive.
2023-10-12 08:37:23 +02:00

133 lines
5.4 KiB
Swift

import SwiftSyntax
@SwiftSyntaxRule
struct UnneededParenthesesInClosureArgumentRule: ConfigurationProviderRule, SwiftSyntaxCorrectableRule, OptInRule {
var configuration = SeverityConfiguration<Self>(.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 }")
]
)
func makeRewriter(file: SwiftLintFile) -> (some ViolationsSyntaxRewriter)? {
Rewriter(
locationConverter: file.locationConverter,
disabledRegions: disabledRegions(file: file)
)
}
}
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: SyntaxRewriter, ViolationsSyntaxRewriter {
private(set) var correctionPositions: [AbsolutePosition] = []
let locationConverter: SourceLocationConverter
let disabledRegions: [SourceRange]
init(locationConverter: SourceLocationConverter, disabledRegions: [SourceRange]) {
self.locationConverter = locationConverter
self.disabledRegions = disabledRegions
}
override func visit(_ node: ClosureSignatureSyntax) -> ClosureSignatureSyntax {
guard
let clause = node.parameterClause?.as(ClosureParameterClauseSyntax.self),
clause.parameters.isNotEmpty,
clause.parameters.allSatisfy({ $0.type == nil }),
!node.isContainedIn(regions: disabledRegions, locationConverter: locationConverter)
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)]))
)
}
correctionPositions.append(clause.positionAfterSkippingLeadingTrivia)
let paramList = ClosureShorthandParameterListSyntax(items).with(\.trailingTrivia, .spaces(1))
return super.visit(node.with(\.parameterClause, .init(paramList)))
}
}
}