diff --git a/CHANGELOG.md b/CHANGELOG.md index 2bde5dbbd..9552dca52 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -60,6 +60,11 @@ [Marcelo Fabri](https://github.com/marcelofabri) [#3293](https://github.com/realm/SwiftLint/issues/3293) +* `unused_closure_parameter` rule now validates closures outside of + function calls. + [Marcelo Fabri](https://github.com/marcelofabri) + [#1082](https://github.com/realm/SwiftLint/issues/1082) + #### Bug Fixes * Fix parsing of Xcode 12 compiler logs for analyzer rules. diff --git a/Source/SwiftLintFramework/Rules/Lint/UnusedClosureParameterRule.swift b/Source/SwiftLintFramework/Rules/Lint/UnusedClosureParameterRule.swift index 70ef57e49..1effbd381 100644 --- a/Source/SwiftLintFramework/Rules/Lint/UnusedClosureParameterRule.swift +++ b/Source/SwiftLintFramework/Rules/Lint/UnusedClosureParameterRule.swift @@ -12,6 +12,7 @@ public struct UnusedClosureParameterRule: SubstitutionCorrectableASTRule, Config name: "Unused Closure Parameter", description: "Unused parameter in a closure should be replaced with _.", kind: .lint, + minSwiftVersion: .fourDotTwo, nonTriggeringExamples: [ Example("[1, 2].map { $0 + 1 }\n"), Example("[1, 2].map({ $0 + 1 })\n"), @@ -49,6 +50,11 @@ public struct UnusedClosureParameterRule: SubstitutionCorrectableASTRule, Config viewModel?.profileImage.didSet(weak: self) { (self, profileImage) in self.profileImageView.image = profileImage } + """), + Example(""" + let failure: Failure = { task, error in + observer.sendFailed(error, task) + } """) ], triggeringExamples: [ @@ -66,6 +72,11 @@ public struct UnusedClosureParameterRule: SubstitutionCorrectableASTRule, Config viewModel?.profileImage.didSet(weak: self) { (↓self, profileImage) in profileImageView.image = profileImage } + """), + Example(""" + let failure: Failure = { ↓task, error in + observer.sendFailed(error) + } """) ], corrections: [ @@ -104,7 +115,17 @@ public struct UnusedClosureParameterRule: SubstitutionCorrectableASTRule, Config } """), Example("class C {\n #if true\n func f() {\n [1, 2].map { ↓number in\n return 3\n }\n }\n #endif\n}"): - Example("class C {\n #if true\n func f() {\n [1, 2].map { _ in\n return 3\n }\n }\n #endif\n}") + Example("class C {\n #if true\n func f() {\n [1, 2].map { _ in\n return 3\n }\n }\n #endif\n}"), + Example(""" + let failure: Failure = { ↓task, error in + observer.sendFailed(error) + } + """): + Example(""" + let failure: Failure = { _, error in + observer.sendFailed(error) + } + """) ] ) @@ -130,21 +151,15 @@ public struct UnusedClosureParameterRule: SubstitutionCorrectableASTRule, Config private func violationRanges(in file: SwiftLintFile, dictionary: SourceKittenDictionary, kind: SwiftExpressionKind) -> [(range: NSRange, name: String)] { - guard kind == .call, - !isClosure(dictionary: dictionary), - let offset = dictionary.offset, - let length = dictionary.length, - let nameOffset = dictionary.nameOffset, - let nameLength = dictionary.nameLength, - let bodyLength = dictionary.bodyLength, - bodyLength > 0 + guard kind == .closure, + let offset = dictionary.bodyOffset, + let length = dictionary.bodyLength, + length > 0 else { return [] } - let rangeStart = nameOffset + nameLength - let rangeLength = (offset + length) - (nameOffset + nameLength) - let byteRange = ByteRange(location: rangeStart, length: rangeLength) + let byteRange = ByteRange(location: offset, length: length) let parameters = dictionary.enclosedVarParameters let contents = file.stringView @@ -195,11 +210,4 @@ public struct UnusedClosureParameterRule: SubstitutionCorrectableASTRule, Config return (range, name) } } - - private func isClosure(dictionary: SourceKittenDictionary) -> Bool { - return dictionary.name.flatMap { name -> Bool in - let range = name.fullNSRange - return regex("\\A[\\s\\(]*?\\{").firstMatch(in: name, options: [], range: range) != nil - } ?? false - } }