mirror of
https://github.com/realm/SwiftLint.git
synced 2026-06-06 20:18:40 +00:00
With the binding of configurations to their associated rule types "unknown configuration" errors can be made more specific mentioning also the rule's identifier in the printed message.
186 lines
6.4 KiB
Swift
186 lines
6.4 KiB
Swift
import Foundation
|
|
import SourceKittenFramework
|
|
|
|
struct MultilineParametersBracketsRule: OptInRule, ConfigurationProviderRule {
|
|
var configuration = SeverityConfiguration<Self>(.warning)
|
|
|
|
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: [
|
|
Example("""
|
|
func foo(param1: String, param2: String, param3: String)
|
|
"""),
|
|
Example("""
|
|
func foo(
|
|
param1: String, param2: String, param3: String
|
|
)
|
|
"""),
|
|
Example("""
|
|
func foo(
|
|
param1: String,
|
|
param2: String,
|
|
param3: String
|
|
)
|
|
"""),
|
|
Example("""
|
|
class SomeType {
|
|
func foo(param1: String, param2: String, param3: String)
|
|
}
|
|
"""),
|
|
Example("""
|
|
class SomeType {
|
|
func foo(
|
|
param1: String, param2: String, param3: String
|
|
)
|
|
}
|
|
"""),
|
|
Example("""
|
|
class SomeType {
|
|
func foo(
|
|
param1: String,
|
|
param2: String,
|
|
param3: String
|
|
)
|
|
}
|
|
"""),
|
|
Example("""
|
|
func foo<T>(param1: T, param2: String, param3: String) -> T { /* some code */ }
|
|
"""),
|
|
Example("""
|
|
func foo(a: [Int] = [
|
|
1
|
|
])
|
|
""")
|
|
],
|
|
triggeringExamples: [
|
|
Example("""
|
|
func foo(↓param1: String, param2: String,
|
|
param3: String
|
|
)
|
|
"""),
|
|
Example("""
|
|
func foo(
|
|
param1: String,
|
|
param2: String,
|
|
param3: String↓)
|
|
"""),
|
|
Example("""
|
|
class SomeType {
|
|
func foo(↓param1: String, param2: String,
|
|
param3: String
|
|
)
|
|
}
|
|
"""),
|
|
Example("""
|
|
class SomeType {
|
|
func foo(
|
|
param1: String,
|
|
param2: String,
|
|
param3: String↓)
|
|
}
|
|
"""),
|
|
Example("""
|
|
func foo<T>(↓param1: T, param2: String,
|
|
param3: String
|
|
) -> T
|
|
""")
|
|
]
|
|
)
|
|
|
|
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,
|
|
case let nameByteRange = ByteRange(location: nameOffset, length: nameLength),
|
|
let functionName = file.stringView.substringWithByteRange(nameByteRange)
|
|
else {
|
|
return []
|
|
}
|
|
|
|
let parameters = substructure.substructure.filter { $0.declarationKind == .varParameter }
|
|
let parameterBodies = parameters.compactMap { $0.content(in: file) }
|
|
let parametersNewlineCount = parameterBodies.map { body in
|
|
return body.countOccurrences(of: "\n")
|
|
}.reduce(0, +)
|
|
let declarationNewlineCount = functionName.countOccurrences(of: "\n")
|
|
let isMultiline = declarationNewlineCount > parametersNewlineCount
|
|
|
|
if isMultiline && parameters.isNotEmpty {
|
|
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 firstParamByteRange = parameters.first?.byteRange,
|
|
let firstParamRange = file.stringView.byteRangeToNSRange(firstParamByteRange)
|
|
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: Self.description,
|
|
severity: configuration.severity,
|
|
location: Location(file: file, characterOffset: invalidMatch.range.location + 1)
|
|
)
|
|
}
|
|
|
|
private func closingBracketViolation(parameters: [SourceKittenDictionary],
|
|
file: SwiftLintFile) -> StyleViolation? {
|
|
guard
|
|
let lastParamByteRange = parameters.last?.byteRange,
|
|
let lastParamRange = file.stringView.byteRangeToNSRange(lastParamByteRange)
|
|
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: Self.description,
|
|
severity: configuration.severity,
|
|
location: Location(file: file, characterOffset: characterOffset)
|
|
)
|
|
}
|
|
}
|