From 6ec5806a936950a51d3eede098f7a1591c3475fc Mon Sep 17 00:00:00 2001 From: Marcelo Fabri Date: Wed, 5 Aug 2020 00:42:27 -0700 Subject: [PATCH] Trigger closure_parameter_position in free closures and capture lists --- CHANGELOG.md | 5 ++ .../Style/ClosureParameterPositionRule.swift | 80 ++++++++++++++++--- 2 files changed, 75 insertions(+), 10 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ccbc4bfc2a..b2804f34f9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -59,6 +59,11 @@ [Marcelo Fabri](https://github.com/marcelofabri) [#3186](https://github.com/realm/SwiftLint/issues/3186) +* `closure_parameter_position` now triggers in closures that are not inside a + function call and also validates captured variables. + [Marcelo Fabri](https://github.com/marcelofabri) + [#3225](https://github.com/realm/SwiftLint/issues/3225) + ## 0.39.2: Stay Home This is the last release to support building with Swift 5.0.x. diff --git a/Source/SwiftLintFramework/Rules/Style/ClosureParameterPositionRule.swift b/Source/SwiftLintFramework/Rules/Style/ClosureParameterPositionRule.swift index bb202e03a2..f01da2a670 100644 --- a/Source/SwiftLintFramework/Rules/Style/ClosureParameterPositionRule.swift +++ b/Source/SwiftLintFramework/Rules/Style/ClosureParameterPositionRule.swift @@ -34,22 +34,81 @@ public struct ClosureParameterPositionRule: ASTRule, ConfigurationProviderRule, """) ], triggeringExamples: [ - Example("[1, 2].map {\n ↓number in\n number + 1 \n}\n"), - Example("[1, 2].map {\n ↓number -> Int in\n number + 1 \n}\n"), - Example("[1, 2].map {\n (↓number: Int) -> Int in\n number + 1 \n}\n"), - Example("[1, 2].map {\n [weak self] ↓number in\n number + 1 \n}\n"), - Example("[1, 2].map { [weak self]\n ↓number in\n number + 1 \n}\n"), - Example("[1, 2].map({\n ↓number in\n number + 1 \n})\n"), - Example("[1, 2].something(closure: {\n ↓number in\n number + 1 \n})\n"), - Example("[1, 2].reduce(0) {\n ↓sum, ↓number in\n number + sum \n}\n") + Example(""" + [1, 2].map { + ↓number in + number + 1 + } + """), + Example(""" + [1, 2].map { + ↓number -> Int in + number + 1 + } + """), + Example(""" + [1, 2].map { + (↓number: Int) -> Int in + number + 1 + } + """), + Example(""" + [1, 2].map { + [weak ↓self] ↓number in + number + 1 + } + """), + Example(""" + [1, 2].map { [weak self] + ↓number in + number + 1 + } + """), + Example(""" + [1, 2].map({ + ↓number in + number + 1 + }) + """), + Example(""" + [1, 2].something(closure: { + ↓number in + number + 1 + }) + """), + Example(""" + [1, 2].reduce(0) { + ↓sum, ↓number in + number + sum + }) + """), + Example(""" + f.completionHandler = { + ↓thing in + doStuff() + } + """), + Example(""" + foo { + [weak ↓self] in + self?.bar() + } + """) ] ) private static let openBraceRegex = regex("\\{") + public func validate(file: SwiftLintFile) -> [StyleViolation] { + return file.structureDictionary.traverseDepthFirst { subDict in + guard let kind = self.kind(from: subDict) else { return nil } + return validate(file: file, kind: kind, dictionary: subDict) + }.unique.sorted(by: { $0.location < $1.location }) + } + public func validate(file: SwiftLintFile, kind: SwiftExpressionKind, dictionary: SourceKittenDictionary) -> [StyleViolation] { - guard kind == .call else { + guard kind == .closure || kind == .call else { return [] } @@ -61,7 +120,8 @@ public struct ClosureParameterPositionRule: ASTRule, ConfigurationProviderRule, return [] } - let parameters = dictionary.enclosedVarParameters + let parameters = dictionary.enclosedVarParameters + + dictionary.substructure.filter { $0.declarationKind == .varLocal } // capture lists let rangeStart = nameOffset + nameLength let regex = ClosureParameterPositionRule.openBraceRegex