From a4a12977c2ad9786acc9a9f0f47f9ab78cb2c838 Mon Sep 17 00:00:00 2001 From: Ornithologist Coder Date: Thu, 19 Oct 2017 07:07:14 +0200 Subject: [PATCH] Filters out parameters within parameters For a function that takes a closure as default value, the array of parameter reported by sourcekit also includes the parameters of that closure. In other words, a function like func foo(param1: Int, param2: Bool, param3: (Int) -> Void = { (x: Int) in }) { } reports 4 parameters (param 1, param2, param3, and x). This commit uses the bounds of the method/function parameters to filter out parameters within parameters. Below, a 1, a 2, and a 3 would be filtered out since they're inside parameter 3. ----------------------------------------------------------------| | |---------| |---------| |----------------------------| | | | param 1 | | param 2 | | param 3: |a 1| |a 2| |a 3| | | | |---------| |---------| |----------------------------| | ----------------------------------------------------------------| --- Rules.md | 30 +++++++++++++++++++ .../Rules/MultilineParametersRule.swift | 30 +++++++++++++++---- .../MultilineParametersRuleExamples.swift | 20 +++++++++++++ 3 files changed, 74 insertions(+), 6 deletions(-) diff --git a/Rules.md b/Rules.md index 94f457365d7..ee99219fa69 100644 --- a/Rules.md +++ b/Rules.md @@ -7062,6 +7062,22 @@ class Foo { } ``` +```swift +class Foo { + class func foo(param1: Int, + param2: Bool, + param3: @escaping (Int) -> Void = { (x: Int) in }) { } +} +``` + +```swift +class Foo { + class func foo(param1: Int, + param2: Bool, + param3: @escaping (Int, (Int) -> Void) -> Void = { (x: Int, f: (Int) -> Void) in }) { } +} +``` +
Triggering Examples @@ -7199,6 +7215,20 @@ class Foo { } ``` +```swift +class Foo { + class func ↓foo(param1: Int, + param2: Bool, param3: @escaping (Int, Int) -> Void = { _, _ in }) { } +} +``` + +```swift +class Foo { + class func ↓foo(param1: Int, + param2: Bool, param3: @escaping (Int) -> Void = { (x: Int) in }) { } +} +``` +
diff --git a/Source/SwiftLintFramework/Rules/MultilineParametersRule.swift b/Source/SwiftLintFramework/Rules/MultilineParametersRule.swift index 8854344400a..942602029fa 100644 --- a/Source/SwiftLintFramework/Rules/MultilineParametersRule.swift +++ b/Source/SwiftLintFramework/Rules/MultilineParametersRule.swift @@ -12,6 +12,8 @@ import SourceKittenFramework public struct MultilineParametersRule: ASTRule, OptInRule, ConfigurationProviderRule { public var configuration = SeverityConfiguration(.warning) + private typealias ParameterRange = (offset: Int, length: Int) + public init() {} public static let description = RuleDescription( @@ -34,16 +36,26 @@ public struct MultilineParametersRule: ASTRule, OptInRule, ConfigurationProvider return [] } + let parameterRanges = dictionary.substructure.flatMap { subStructure -> ParameterRange? in + guard + let offset = subStructure.offset, + let length = subStructure.length, + let kind = subStructure.kind, SwiftDeclarationKind(rawValue: kind) == .varParameter + else { + return nil + } + + return (offset, length) + } + var numberOfParameters = 0 var linesWithParameters = Set() - for structure in dictionary.substructure { + for range in parameterRanges { guard - let structureOffset = structure.offset, - structure.typeName != nil, - let structureKind = structure.kind, SwiftDeclarationKind(rawValue: structureKind) == .varParameter, - let (line, _) = file.contents.bridge().lineAndCharacter(forByteOffset: structureOffset), - offset..<(offset + length) ~= structureOffset + let (line, _) = file.contents.bridge().lineAndCharacter(forByteOffset: range.offset), + offset..<(offset + length) ~= range.offset, + isRange(range, withinRanges: parameterRanges) else { continue } @@ -63,4 +75,10 @@ public struct MultilineParametersRule: ASTRule, OptInRule, ConfigurationProvider severity: configuration.severity, location: Location(file: file, byteOffset: offset))] } + + // MARK: - Private + + private func isRange(_ range: ParameterRange, withinRanges ranges: [ParameterRange]) -> Bool { + return ranges.filter { $0 != range && ($0.offset..<($0.offset + $0.length)).contains(range.offset) }.isEmpty + } } diff --git a/Source/SwiftLintFramework/Rules/MultilineParametersRuleExamples.swift b/Source/SwiftLintFramework/Rules/MultilineParametersRuleExamples.swift index f5963eb5ea4..c11179095ee 100644 --- a/Source/SwiftLintFramework/Rules/MultilineParametersRuleExamples.swift +++ b/Source/SwiftLintFramework/Rules/MultilineParametersRuleExamples.swift @@ -8,6 +8,8 @@ import Foundation +// swiftlint:disable type_body_length + internal struct MultilineParametersRuleExamples { static let nonTriggeringExamples = [ "func foo() { }", @@ -122,6 +124,16 @@ internal struct MultilineParametersRuleExamples { " class func foo(param1: Int,\n" + " param2: @escaping ((Int) -> Void)? = { _ in },\n" + " param3: @escaping (Int, Int) -> Void = { _, _ in }) { }\n" + + "}", + "class Foo {\n" + + " class func foo(param1: Int,\n" + + " param2: Bool,\n" + + " param3: @escaping (Int) -> Void = { (x: Int) in }) { }\n" + + "}", + "class Foo {\n" + + " class func foo(param1: Int,\n" + + " param2: Bool,\n" + + " param3: @escaping (Int, (Int) -> Void) -> Void = { (x: Int, f: (Int) -> Void) in }) { }\n" + "}" ] @@ -201,6 +213,14 @@ internal struct MultilineParametersRuleExamples { "class Foo {\n" + " class func ↓foo(param1: Int, param2: Bool,\n" + " param3: [String]) { }\n" + + "}", + "class Foo {\n" + + " class func ↓foo(param1: Int,\n" + + " param2: Bool, param3: @escaping (Int, Int) -> Void = { _, _ in }) { }\n" + + "}", + "class Foo {\n" + + " class func ↓foo(param1: Int,\n" + + " param2: Bool, param3: @escaping (Int) -> Void = { (x: Int) in }) { }\n" + "}" ] }