SwiftLint/Source/SwiftLintFramework/Rules/Metrics/FunctionBodyLengthRule.swift

64 lines
2.3 KiB
Swift

import SourceKittenFramework
public struct FunctionBodyLengthRule: ASTRule, ConfigurationProviderRule {
public var configuration = SeverityLevelsConfiguration(warning: 40, error: 100)
public init() {}
public static let description = RuleDescription(
identifier: "function_body_length",
name: "Function Body Length",
description: "Functions bodies should not span too many lines.",
kind: .metrics
)
public func validate(file: SwiftLintFile, kind: SwiftDeclarationKind,
dictionary: SourceKittenDictionary) -> [StyleViolation] {
guard let input = RuleInput(file: file, kind: kind, dictionary: dictionary) else {
return []
}
for parameter in configuration.params {
let (exceeds, lineCount) = file.exceedsLineCountExcludingCommentsAndWhitespace(
input.startLine, input.endLine, parameter.value
)
guard exceeds else { continue }
return [
StyleViolation(
ruleDescription: Self.description, severity: parameter.severity,
location: Location(file: file, byteOffset: input.offset),
reason: """
Function body should span \(configuration.warning) lines or less excluding comments and \
whitespace: currently spans \(lineCount) lines
"""
)
]
}
return []
}
}
private struct RuleInput {
let offset: ByteCount
let startLine: Int
let endLine: Int
init?(file: SwiftLintFile, kind: SwiftDeclarationKind, dictionary: SourceKittenDictionary) {
guard SwiftDeclarationKind.functionKinds.contains(kind),
let offset = dictionary.offset,
let bodyOffset = dictionary.bodyOffset,
let bodyLength = dictionary.bodyLength,
case let contentsNSString = file.stringView,
let startLine = contentsNSString.lineAndCharacter(forByteOffset: bodyOffset)?.line,
let endLine = contentsNSString.lineAndCharacter(forByteOffset: bodyOffset + bodyLength)?.line
else {
return nil
}
self.offset = offset
self.startLine = startLine
self.endLine = endLine
}
}