185 lines
6.2 KiB
Swift
185 lines
6.2 KiB
Swift
import Foundation
|
|
import SourceKittenFramework
|
|
|
|
public struct MultilineParametersBracketsRule: OptInRule, ConfigurationProviderRule, AutomaticTestableRule {
|
|
public var configuration = SeverityConfiguration(.warning)
|
|
|
|
public init() {}
|
|
|
|
public static let description = RuleDescription(
|
|
identifier: "multiline_parameters_brackets",
|
|
name: "Multiline Parameters Brackets",
|
|
description: "Multiline parameters should have their surrounding brackets in a new line.",
|
|
kind: .style,
|
|
nonTriggeringExamples: [
|
|
"""
|
|
func foo(param1: String, param2: String, param3: String)
|
|
""",
|
|
"""
|
|
func foo(
|
|
param1: String, param2: String, param3: String
|
|
)
|
|
""",
|
|
"""
|
|
func foo(
|
|
param1: String,
|
|
param2: String,
|
|
param3: String
|
|
)
|
|
""",
|
|
"""
|
|
class SomeType {
|
|
func foo(param1: String, param2: String, param3: String)
|
|
}
|
|
""",
|
|
"""
|
|
class SomeType {
|
|
func foo(
|
|
param1: String, param2: String, param3: String
|
|
)
|
|
}
|
|
""",
|
|
"""
|
|
class SomeType {
|
|
func foo(
|
|
param1: String,
|
|
param2: String,
|
|
param3: String
|
|
)
|
|
}
|
|
""",
|
|
"""
|
|
func foo<T>(param1: T, param2: String, param3: String) -> T { /* some code */ }
|
|
"""
|
|
],
|
|
triggeringExamples: [
|
|
"""
|
|
func foo(↓param1: String, param2: String,
|
|
param3: String
|
|
)
|
|
""",
|
|
"""
|
|
func foo(
|
|
param1: String,
|
|
param2: String,
|
|
param3: String↓)
|
|
""",
|
|
"""
|
|
class SomeType {
|
|
func foo(↓param1: String, param2: String,
|
|
param3: String
|
|
)
|
|
}
|
|
""",
|
|
"""
|
|
class SomeType {
|
|
func foo(
|
|
param1: String,
|
|
param2: String,
|
|
param3: String↓)
|
|
}
|
|
""",
|
|
"""
|
|
func foo<T>(↓param1: T, param2: String,
|
|
param3: String
|
|
) -> T
|
|
"""
|
|
]
|
|
)
|
|
|
|
public func validate(file: SwiftLintFile) -> [StyleViolation] {
|
|
return violations(in: file.structureDictionary, file: file)
|
|
}
|
|
|
|
private func violations(in substructure: SourceKittenDictionary, file: SwiftLintFile) -> [StyleViolation] {
|
|
var violations = [StyleViolation]()
|
|
|
|
// find violations at current level
|
|
if let kind = substructure.declarationKind,
|
|
SwiftDeclarationKind.functionKinds.contains(kind) {
|
|
guard
|
|
let nameOffset = substructure.nameOffset,
|
|
let nameLength = substructure.nameLength,
|
|
let functionName = file.stringView.substringWithByteRange(start: nameOffset, length: nameLength)
|
|
else {
|
|
return []
|
|
}
|
|
|
|
let isMultiline = functionName.contains("\n")
|
|
|
|
let parameters = substructure.substructure.filter { $0.declarationKind == .varParameter }
|
|
if isMultiline && !parameters.isEmpty {
|
|
if let openingBracketViolation = openingBracketViolation(parameters: parameters, file: file) {
|
|
violations.append(openingBracketViolation)
|
|
}
|
|
|
|
if let closingBracketViolation = closingBracketViolation(parameters: parameters, file: file) {
|
|
violations.append(closingBracketViolation)
|
|
}
|
|
}
|
|
}
|
|
|
|
// find violations at deeper levels
|
|
for substructure in substructure.substructure {
|
|
violations += self.violations(in: substructure, file: file)
|
|
}
|
|
|
|
return violations
|
|
}
|
|
|
|
private func openingBracketViolation(parameters: [SourceKittenDictionary],
|
|
file: SwiftLintFile) -> StyleViolation? {
|
|
guard
|
|
let firstParamByteOffset = parameters.first?.offset,
|
|
let firstParamByteLength = parameters.first?.length,
|
|
let firstParamRange = file.stringView.byteRangeToNSRange(
|
|
start: firstParamByteOffset,
|
|
length: firstParamByteLength
|
|
)
|
|
else {
|
|
return nil
|
|
}
|
|
|
|
let prefix = file.stringView.nsString.substring(to: firstParamRange.lowerBound)
|
|
let invalidRegex = regex("\\([ \\t]*\\z")
|
|
|
|
guard let invalidMatch = invalidRegex.firstMatch(in: prefix, options: [], range: prefix.fullNSRange) else {
|
|
return nil
|
|
}
|
|
|
|
return StyleViolation(
|
|
ruleDescription: type(of: self).description,
|
|
severity: configuration.severity,
|
|
location: Location(file: file, characterOffset: invalidMatch.range.location + 1)
|
|
)
|
|
}
|
|
|
|
private func closingBracketViolation(parameters: [SourceKittenDictionary],
|
|
file: SwiftLintFile) -> StyleViolation? {
|
|
guard
|
|
let lastParamByteOffset = parameters.last?.offset,
|
|
let lastParamByteLength = parameters.last?.length,
|
|
let lastParamRange = file.stringView.byteRangeToNSRange(
|
|
start: lastParamByteOffset,
|
|
length: lastParamByteLength
|
|
)
|
|
else {
|
|
return nil
|
|
}
|
|
|
|
let suffix = file.stringView.nsString.substring(from: lastParamRange.upperBound)
|
|
let invalidRegex = regex("\\A[ \\t]*\\)")
|
|
|
|
guard let invalidMatch = invalidRegex.firstMatch(in: suffix, options: [], range: suffix.fullNSRange) else {
|
|
return nil
|
|
}
|
|
|
|
let characterOffset = lastParamRange.upperBound + invalidMatch.range.upperBound - 1
|
|
return StyleViolation(
|
|
ruleDescription: type(of: self).description,
|
|
severity: configuration.severity,
|
|
location: Location(file: file, characterOffset: characterOffset)
|
|
)
|
|
}
|
|
}
|