121 lines
3.4 KiB
Swift
121 lines
3.4 KiB
Swift
import SwiftSyntax
|
|
|
|
struct SortedEnumCasesRule: ConfigurationProviderRule, SwiftSyntaxRule, OptInRule {
|
|
var configuration = SeverityConfiguration(.warning)
|
|
|
|
init() {}
|
|
|
|
static let description = RuleDescription(
|
|
identifier: "sorted_enum_cases",
|
|
name: "Sorted Enum Cases",
|
|
description: "Enum cases should be sorted",
|
|
kind: .style,
|
|
nonTriggeringExamples: [
|
|
Example("""
|
|
enum foo {
|
|
case a
|
|
case b
|
|
case c
|
|
}
|
|
"""),
|
|
Example("""
|
|
enum foo {
|
|
case a, b, c
|
|
}
|
|
"""),
|
|
Example("""
|
|
enum foo {
|
|
case a
|
|
case b, c
|
|
}
|
|
"""),
|
|
Example("""
|
|
enum foo {
|
|
case a(foo: Foo)
|
|
case b(String), c
|
|
}
|
|
"""),
|
|
Example("""
|
|
@frozen
|
|
enum foo {
|
|
case b
|
|
case a
|
|
case c, f, d
|
|
}
|
|
""")
|
|
],
|
|
triggeringExamples: [
|
|
Example("""
|
|
enum foo {
|
|
↓case b
|
|
↓case a
|
|
case c
|
|
}
|
|
"""),
|
|
Example("""
|
|
enum foo {
|
|
case ↓b, ↓a, c
|
|
}
|
|
"""),
|
|
Example("""
|
|
enum foo {
|
|
↓case b, c
|
|
↓case a
|
|
}
|
|
"""),
|
|
Example("""
|
|
enum foo {
|
|
case a
|
|
case b, ↓d, ↓c
|
|
}
|
|
"""),
|
|
Example("""
|
|
enum foo {
|
|
case a(foo: Foo)
|
|
case ↓c, ↓b(String)
|
|
}
|
|
""")
|
|
]
|
|
)
|
|
|
|
func makeVisitor(file: SwiftLintFile) -> ViolationsSyntaxVisitor {
|
|
Visitor(viewMode: .sourceAccurate)
|
|
}
|
|
}
|
|
|
|
private extension SortedEnumCasesRule {
|
|
final class Visitor: ViolationsSyntaxVisitor {
|
|
override var skippableDeclarations: [DeclSyntaxProtocol.Type] {
|
|
return .allExcept(EnumDeclSyntax.self)
|
|
}
|
|
|
|
override func visit(_ node: EnumDeclSyntax) -> SyntaxVisitorContinueKind {
|
|
guard !node.attributes.contains(attributeNamed: "frozen") else {
|
|
return .skipChildren
|
|
}
|
|
|
|
let cases = node.members.members.compactMap { $0.decl.as(EnumCaseDeclSyntax.self) }
|
|
let sortedCases = cases
|
|
.sorted(by: { $0.elements.first!.identifier.text < $1.elements.first!.identifier.text })
|
|
|
|
zip(sortedCases, cases).forEach { sortedCase, currentCase in
|
|
if sortedCase.elements.first?.identifier.text != currentCase.elements.first?.identifier.text {
|
|
violations.append(currentCase.positionAfterSkippingLeadingTrivia)
|
|
}
|
|
}
|
|
|
|
return .visitChildren
|
|
}
|
|
|
|
override func visitPost(_ node: EnumCaseDeclSyntax) {
|
|
let sortedElements = node.elements.sorted(by: { $0.identifier.text < $1.identifier.text })
|
|
|
|
zip(sortedElements, node.elements).forEach { sortedElement, currentElement in
|
|
if sortedElement.identifier.text != currentElement.identifier.text {
|
|
violations.append(currentElement.positionAfterSkippingLeadingTrivia)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|