Skip to content

Commit

Permalink
Filters out parameters within parameters
Browse files Browse the repository at this point in the history
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| | |
|        |---------| |---------| |----------------------------| |
----------------------------------------------------------------|
  • Loading branch information
ornithocoder committed Oct 19, 2017
1 parent 95b798f commit 8c95209
Show file tree
Hide file tree
Showing 3 changed files with 74 additions and 6 deletions.
30 changes: 30 additions & 0 deletions Rules.md
Original file line number Diff line number Diff line change
Expand Up @@ -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 }) { }
}
```

</details>
<details>
<summary>Triggering Examples</summary>
Expand Down Expand Up @@ -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 }) { }
}
```

</details>


Expand Down
30 changes: 24 additions & 6 deletions Source/SwiftLintFramework/Rules/MultilineParametersRule.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand All @@ -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<Int>()

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
}
Expand All @@ -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.offset..<($0.offset + $0.length)).contains(range.offset) && $0 != range }.isEmpty
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@

import Foundation

// swiftlint:disable type_body_length

internal struct MultilineParametersRuleExamples {
static let nonTriggeringExamples = [
"func foo() { }",
Expand Down Expand Up @@ -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" +
"}"
]

Expand Down Expand Up @@ -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" +
"}"
]
}

0 comments on commit 8c95209

Please sign in to comment.