mirror of
https://github.com/realm/SwiftLint.git
synced 2026-06-06 20:18:40 +00:00
0e862ca9c4
and fix violations
140 lines
5.3 KiB
Swift
140 lines
5.3 KiB
Swift
import SourceKittenFramework
|
|
|
|
public struct ModifierOrderRule: ASTRule, OptInRule, ConfigurationProviderRule {
|
|
public var configuration = ModifierOrderConfiguration(
|
|
preferredModifierOrder: [
|
|
.override,
|
|
.acl,
|
|
.setterACL,
|
|
.dynamic,
|
|
.mutators,
|
|
.lazy,
|
|
.final,
|
|
.required,
|
|
.convenience,
|
|
.typeMethods,
|
|
.owned
|
|
]
|
|
)
|
|
|
|
public init() {}
|
|
|
|
public static let description = RuleDescription(
|
|
identifier: "modifier_order",
|
|
name: "Modifier Order",
|
|
description: "Modifier order should be consistent.",
|
|
kind: .style,
|
|
minSwiftVersion: .fourDotOne ,
|
|
nonTriggeringExamples: ModifierOrderRuleExamples.nonTriggeringExamples,
|
|
triggeringExamples: ModifierOrderRuleExamples.triggeringExamples
|
|
)
|
|
|
|
public func validate(file: File,
|
|
kind: SwiftDeclarationKind,
|
|
dictionary: [String: SourceKitRepresentable]) -> [StyleViolation] {
|
|
guard let offset = dictionary.offset else {
|
|
return []
|
|
}
|
|
|
|
let violatableModifiers = self.violatableModifiers(declaredModifiers: dictionary.modifierDescriptions)
|
|
let prioritizedModifiers = self.prioritizedModifiers(violatableModifiers: violatableModifiers)
|
|
let sortedByPriorityModifiers = prioritizedModifiers.sorted(
|
|
by: { lhs, rhs in lhs.priority < rhs.priority }
|
|
).map { $0.modifier }
|
|
|
|
let violatingModifiers = zip(
|
|
sortedByPriorityModifiers,
|
|
violatableModifiers
|
|
).filter { sortedModifier, unsortedModifier in
|
|
return sortedModifier != unsortedModifier
|
|
}
|
|
|
|
if let first = violatingModifiers.first {
|
|
let preferredModifier = first.0
|
|
let declaredModifier = first.1
|
|
let reason = "\(preferredModifier.keyword) modifier should be before \(declaredModifier.keyword)."
|
|
return [
|
|
StyleViolation(
|
|
ruleDescription: type(of: self).description,
|
|
severity: configuration.severityConfiguration.severity,
|
|
location: Location(file: file, byteOffset: offset),
|
|
reason: reason
|
|
)
|
|
]
|
|
} else {
|
|
return []
|
|
}
|
|
}
|
|
|
|
private func violatableModifiers(declaredModifiers: [ModifierDescription]) -> [ModifierDescription] {
|
|
let preferredModifierGroups = ([.atPrefixed] + configuration.preferredModifierOrder)
|
|
return declaredModifiers.filter { preferredModifierGroups.contains($0.group) }
|
|
}
|
|
|
|
private func prioritizedModifiers(
|
|
violatableModifiers: [ModifierDescription]
|
|
) -> [(priority: Int, modifier: ModifierDescription)] {
|
|
let prioritizedPreferredModifierGroups = ([.atPrefixed] + configuration.preferredModifierOrder).enumerated()
|
|
return violatableModifiers.reduce(
|
|
[(priority: Int, modifier: ModifierDescription)]()
|
|
) { prioritizedModifiers, modifier in
|
|
guard let priority = prioritizedPreferredModifierGroups.first(
|
|
where: { _, group in modifier.group == group }
|
|
)?.offset else {
|
|
return prioritizedModifiers
|
|
}
|
|
return prioritizedModifiers + [(priority: priority, modifier: modifier)]
|
|
}
|
|
}
|
|
}
|
|
|
|
private extension Dictionary where Key == String, Value == SourceKitRepresentable {
|
|
var modifierDescriptions: [ModifierDescription] {
|
|
let staticKinds = [SwiftDeclarationKind.functionMethodClass, .functionMethodStatic, .varClass, .varStatic]
|
|
let staticKindsAndOffsets = kindsAndOffsets(in: staticKinds).map { [$0] } ?? []
|
|
return (swiftAttributes + staticKindsAndOffsets)
|
|
.sorted {
|
|
guard let rhsOffset = $0.offset, let lhsOffset = $1.offset else {
|
|
return false
|
|
}
|
|
return rhsOffset < lhsOffset
|
|
}
|
|
.compactMap {
|
|
if let attribute = $0.attribute,
|
|
let modifierGroup = SwiftDeclarationAttributeKind.ModifierGroup(rawAttribute: attribute) {
|
|
return ModifierDescription(
|
|
keyword: attribute.lastComponentAfter("."),
|
|
group: modifierGroup
|
|
)
|
|
} else if let kind = $0.kind {
|
|
return ModifierDescription(
|
|
keyword: kind.lastComponentAfter("."),
|
|
group: .typeMethods
|
|
)
|
|
}
|
|
return nil
|
|
}
|
|
}
|
|
|
|
private func kindsAndOffsets(in declarationKinds: [SwiftDeclarationKind]) -> [String: SourceKitRepresentable]? {
|
|
guard let kind = kind, let offset = offset,
|
|
let declarationKind = SwiftDeclarationKind(rawValue: kind),
|
|
declarationKinds.contains(declarationKind) else {
|
|
return nil
|
|
}
|
|
|
|
return ["key.kind": kind, "key.offset": Int64(offset)]
|
|
}
|
|
}
|
|
|
|
private extension String {
|
|
func lastComponentAfter(_ charachter: String) -> String {
|
|
return components(separatedBy: charachter).last ?? ""
|
|
}
|
|
}
|
|
|
|
private struct ModifierDescription: Equatable {
|
|
let keyword: String
|
|
let group: SwiftDeclarationAttributeKind.ModifierGroup
|
|
}
|