142 lines
4.6 KiB
Swift
142 lines
4.6 KiB
Swift
import SwiftSyntax
|
|
|
|
final class BodyLengthRuleVisitor: ViolationsSyntaxVisitor {
|
|
private let kind: Kind
|
|
private let file: SwiftLintFile
|
|
private let configuration: SeverityLevelsConfiguration
|
|
private let locationConverter: SourceLocationConverter
|
|
|
|
enum Kind {
|
|
case closure
|
|
case function
|
|
case type
|
|
|
|
fileprivate var name: String {
|
|
switch self {
|
|
case .closure:
|
|
return "Closure"
|
|
case .function:
|
|
return "Function"
|
|
case .type:
|
|
return "Type"
|
|
}
|
|
}
|
|
}
|
|
|
|
init(kind: Kind, file: SwiftLintFile, configuration: SeverityLevelsConfiguration) {
|
|
self.kind = kind
|
|
self.file = file
|
|
self.configuration = configuration
|
|
locationConverter = file.locationConverter
|
|
super.init(viewMode: .sourceAccurate)
|
|
}
|
|
|
|
override func visitPost(_ node: EnumDeclSyntax) {
|
|
if kind == .type {
|
|
registerViolations(
|
|
getLeftBrace: { node.members.leftBrace },
|
|
getRightBrace: { node.members.rightBrace },
|
|
getViolationNode: { node.enumKeyword }
|
|
)
|
|
}
|
|
}
|
|
|
|
override func visitPost(_ node: ClassDeclSyntax) {
|
|
if kind == .type {
|
|
registerViolations(
|
|
getLeftBrace: { node.members.leftBrace },
|
|
getRightBrace: { node.members.rightBrace },
|
|
getViolationNode: { node.classKeyword }
|
|
)
|
|
}
|
|
}
|
|
|
|
override func visitPost(_ node: StructDeclSyntax) {
|
|
if kind == .type {
|
|
registerViolations(
|
|
getLeftBrace: { node.members.leftBrace },
|
|
getRightBrace: { node.members.rightBrace },
|
|
getViolationNode: { node.structKeyword }
|
|
)
|
|
}
|
|
}
|
|
|
|
override func visitPost(_ node: ActorDeclSyntax) {
|
|
if kind == .type {
|
|
registerViolations(
|
|
getLeftBrace: { node.members.leftBrace },
|
|
getRightBrace: { node.members.rightBrace },
|
|
getViolationNode: { node.actorKeyword }
|
|
)
|
|
}
|
|
}
|
|
|
|
override func visitPost(_ node: ClosureExprSyntax) {
|
|
if kind == .closure {
|
|
registerViolations(
|
|
getLeftBrace: { node.leftBrace },
|
|
getRightBrace: { node.rightBrace },
|
|
getViolationNode: { node.leftBrace }
|
|
)
|
|
}
|
|
}
|
|
|
|
override func visitPost(_ node: FunctionDeclSyntax) {
|
|
if kind == .function {
|
|
registerViolations(
|
|
getLeftBrace: { node.body?.leftBrace },
|
|
getRightBrace: { node.body?.rightBrace },
|
|
getViolationNode: { node.identifier }
|
|
)
|
|
}
|
|
}
|
|
|
|
override func visitPost(_ node: InitializerDeclSyntax) {
|
|
if kind == .function {
|
|
registerViolations(
|
|
getLeftBrace: { node.body?.leftBrace },
|
|
getRightBrace: { node.body?.rightBrace },
|
|
getViolationNode: { node.initKeyword }
|
|
)
|
|
}
|
|
}
|
|
|
|
private func registerViolations(
|
|
getLeftBrace: () -> TokenSyntax?, getRightBrace: () -> TokenSyntax?, getViolationNode: () -> some SyntaxProtocol
|
|
) {
|
|
guard
|
|
let leftBracePosition = getLeftBrace()?.positionAfterSkippingLeadingTrivia,
|
|
let leftBraceLine = locationConverter.location(for: leftBracePosition).line,
|
|
let rightBracePosition = getRightBrace()?.positionAfterSkippingLeadingTrivia,
|
|
let rightBraceLine = locationConverter.location(for: rightBracePosition).line
|
|
else {
|
|
return
|
|
}
|
|
|
|
let lineCount = file.bodyLineCountIgnoringCommentsAndWhitespace(leftBraceLine: leftBraceLine,
|
|
rightBraceLine: rightBraceLine)
|
|
let severity: ViolationSeverity, upperBound: Int
|
|
if let error = configuration.error, lineCount > error {
|
|
severity = .error
|
|
upperBound = error
|
|
} else if lineCount > configuration.warning {
|
|
severity = .warning
|
|
upperBound = configuration.warning
|
|
} else {
|
|
return
|
|
}
|
|
|
|
let reason = """
|
|
\(kind.name) body should span \(upperBound) lines or less excluding comments and whitespace: \
|
|
currently spans \(lineCount) lines
|
|
"""
|
|
|
|
let violation = ReasonedRuleViolation(
|
|
position: getViolationNode().positionAfterSkippingLeadingTrivia,
|
|
reason: reason,
|
|
severity: severity
|
|
)
|
|
violations.append(violation)
|
|
}
|
|
}
|