mirror of
https://github.com/realm/SwiftLint.git
synced 2026-06-06 20:18:40 +00:00
With the binding of configurations to their associated rule types "unknown configuration" errors can be made more specific mentioning also the rule's identifier in the printed message.
135 lines
5.3 KiB
Swift
135 lines
5.3 KiB
Swift
import SwiftSyntax
|
|
|
|
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 }\n"),
|
|
Example("let foo = { bar, _ in }\n"),
|
|
Example("let foo = { bar in }\n"),
|
|
Example("let foo = { bar -> Bool in return true }\n"),
|
|
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 })\n"),
|
|
Example("call(arg: { ↓(bar, _) in })\n"),
|
|
Example("let foo = { ↓(bar) -> Bool in return true }\n"),
|
|
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 })\n"): Example("call(arg: { bar in })\n"),
|
|
Example("call(arg: { ↓(bar, _) in })\n"): Example("call(arg: { bar, _ in })\n"),
|
|
Example("call(arg: { ↓(bar, _)in })\n"): Example("call(arg: { bar, _ in })\n"),
|
|
Example("let foo = { ↓(bar) -> Bool in return true }\n"):
|
|
Example("let foo = { bar -> Bool in return true }\n"),
|
|
Example("method { ↓(foo, bar) in }\n"): Example("method { foo, bar in }\n"),
|
|
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 makeVisitor(file: SwiftLintFile) -> ViolationsSyntaxVisitor {
|
|
Visitor(viewMode: .sourceAccurate)
|
|
}
|
|
|
|
func makeRewriter(file: SwiftLintFile) -> ViolationsSyntaxRewriter? {
|
|
Rewriter(
|
|
locationConverter: file.locationConverter,
|
|
disabledRegions: disabledRegions(file: file)
|
|
)
|
|
}
|
|
}
|
|
|
|
private final class Visitor: ViolationsSyntaxVisitor {
|
|
override func visitPost(_ node: ClosureSignatureSyntax) {
|
|
guard let clause = node.input?.as(ClosureParameterClauseSyntax.self),
|
|
clause.parameterList.isNotEmpty,
|
|
clause.parameterList.allSatisfy({ $0.type == nil }) else {
|
|
return
|
|
}
|
|
|
|
violations.append(clause.positionAfterSkippingLeadingTrivia)
|
|
}
|
|
}
|
|
|
|
private 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.input?.as(ClosureParameterClauseSyntax.self),
|
|
clause.parameterList.isNotEmpty,
|
|
clause.parameterList.allSatisfy({ $0.type == nil }),
|
|
!node.isContainedIn(regions: disabledRegions, locationConverter: locationConverter)
|
|
else {
|
|
return super.visit(node)
|
|
}
|
|
|
|
let items = clause.parameterList.enumerated().compactMap { idx, param -> ClosureParamSyntax? in
|
|
let name = param.firstName
|
|
let isLast = idx == clause.parameterList.count - 1
|
|
return ClosureParamSyntax(
|
|
name: name,
|
|
trailingComma: isLast ? nil : .commaToken(trailingTrivia: Trivia(pieces: [.spaces(1)]))
|
|
)
|
|
}
|
|
|
|
correctionPositions.append(clause.positionAfterSkippingLeadingTrivia)
|
|
|
|
let paramList = ClosureParamListSyntax(items).with(\.trailingTrivia, .spaces(1))
|
|
return super.visit(node.with(\.input, .init(paramList)))
|
|
}
|
|
}
|