Skip to content

Commit

Permalink
Implement realm#2888
Browse files Browse the repository at this point in the history
  • Loading branch information
fddecc committed Oct 2, 2019
1 parent f211694 commit 6a540c2
Show file tree
Hide file tree
Showing 5 changed files with 147 additions and 0 deletions.
1 change: 1 addition & 0 deletions Source/SwiftLintFramework/Models/MasterRuleList.swift
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,7 @@ public let masterRuleList = RuleList(rules: [
ReturnArrowWhitespaceRule.self,
ShorthandOperatorRule.self,
SingleTestClassRule.self,
SnakeCaseRawValueCodableStringEnumRule.self,
SortedFirstLastRule.self,
SortedImportsRule.self,
StatementPositionRule.self,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
import SourceKittenFramework

public struct SnakeCaseRawValueCodableStringEnumRule: ASTRule, OptInRule, ConfigurationProviderRule,
AutomaticTestableRule {
public var configuration = SeverityConfiguration(.warning)

public init() {}

public static let description = RuleDescription(
identifier: "snake_case_raw_value_codable_string_enum",
name: "Snake Case RawValue Codable String Enum",
description: "Camel cased enum cases should have snake case raw value.",
kind: .lint,
nonTriggeringExamples: [
"""
enum Numbers: Codable {
case int(Int)
case short(Int16)
}
""",
"""
enum Numbers: Int, Codable {
case one = 1
case two = 2
}
""",
"""
enum Numbers: Double, Codable {
case one = 1.1
case two = 2.2
}
""",
"""
enum Numbers: String, Codable {
case one = "one"
case two = "two"
}
""",
"""
enum Status: String {
case ok
case notAcceptable
case maybeAcceptable = "maybe_acceptable"
}
""",
"""
enum Status: Int, Codable {
case ok
case notAcceptable
case maybeAcceptable = -1
}
"""
],
triggeringExamples: [
"""
enum Status: String, Codable {
case ok
case ↓notAcceptable
case maybeAcceptable = "maybe_acceptable"
}
""",
"""
enum Status: String, Decodable {
case ok
case ↓notAcceptable
case maybeAcceptable = "maybe_acceptable"
}
""",
"""
enum Status: String, Encodable {
case ok
case ↓notAcceptable
case maybeAcceptable = "maybe_acceptable"
}
""",
"""
enum Status: String, Codable {
case ok
case ↓notAcceptable
case maybeAcceptable = "maybe_acceptable"
}
"""
]
)

public func validate(file: File,
kind: SwiftDeclarationKind,
dictionary: [String: SourceKitRepresentable]) -> [StyleViolation] {
guard kind == .enum else { return [] }

let codableTypesSet = Set(["Codable", "Decodable", "Encodable"])
let enumInheritedTypesSet = Set(dictionary.inheritedTypes)

guard
enumInheritedTypesSet.contains("String"),
!enumInheritedTypesSet.isDisjoint(with: codableTypesSet)
else { return [] }

let violations = violatingOffsetsForEnum(dictionary: dictionary)
return violations.map {
StyleViolation(ruleDescription: type(of: self).description,
severity: configuration.severity,
location: Location(file: file, byteOffset: $0))
}
}

private func violatingOffsetsForEnum(dictionary: [String: SourceKitRepresentable]) -> [Int] {
let locs = substructureElements(of: dictionary, matching: .enumcase)
.compactMap { substructureElements(of: $0, matching: .enumelement) }
.flatMap(camelCasedEnumCasesMissingRawValue)
.compactMap { $0.offset }

return locs
}

private func substructureElements(of dict: [String: SourceKitRepresentable],
matching kind: SwiftDeclarationKind) -> [[String: SourceKitRepresentable]] {
return dict.substructure.filter { $0.kind.flatMap(SwiftDeclarationKind.init) == kind }
}

private func camelCasedEnumCasesMissingRawValue(
_ enumElements: [[String: SourceKitRepresentable]]) -> [[String: SourceKitRepresentable]] {
return enumElements
.filter { substructure in
guard let name = substructure.name, name != name.lowercased() else { return false }
return !substructure.elements.contains { $0.kind == "source.lang.swift.structure.elem.init_expr" }
}
}
}
4 changes: 4 additions & 0 deletions SwiftLint.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,7 @@
7565E5F12262BA0900B0597C /* UnusedCaptureListRule.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7565E5F02262BA0900B0597C /* UnusedCaptureListRule.swift */; };
756B585D2138ECD300D1A4E9 /* CollectionAlignmentRule.swift in Sources */ = {isa = PBXBuildFile; fileRef = 756B585C2138ECD300D1A4E9 /* CollectionAlignmentRule.swift */; };
756C0779222EA4F400A111F4 /* ReduceIntoRule.swift in Sources */ = {isa = PBXBuildFile; fileRef = 756C0777222EA49400A111F4 /* ReduceIntoRule.swift */; };
776CEF772344083F00E36792 /* SnakeCaseRawValueCodableStringEnumRule.swift in Sources */ = {isa = PBXBuildFile; fileRef = 776CEF762344083F00E36792 /* SnakeCaseRawValueCodableStringEnumRule.swift */; };
787CDE39208E7D41005F3D2F /* SwitchCaseAlignmentConfiguration.swift in Sources */ = {isa = PBXBuildFile; fileRef = 787CDE38208E7D41005F3D2F /* SwitchCaseAlignmentConfiguration.swift */; };
787CDE3B208F9C34005F3D2F /* SwitchCaseAlignmentRuleTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 787CDE3A208F9C34005F3D2F /* SwitchCaseAlignmentRuleTests.swift */; };
78F032461D7C877E00BE709A /* OverriddenSuperCallRule.swift in Sources */ = {isa = PBXBuildFile; fileRef = 78F032441D7C877800BE709A /* OverriddenSuperCallRule.swift */; };
Expand Down Expand Up @@ -629,6 +630,7 @@
756B585C2138ECD300D1A4E9 /* CollectionAlignmentRule.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CollectionAlignmentRule.swift; sourceTree = "<group>"; };
756C0777222EA49400A111F4 /* ReduceIntoRule.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReduceIntoRule.swift; sourceTree = "<group>"; };
7578C915214173BE0080FEC9 /* CollectionAlignmentRuleTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CollectionAlignmentRuleTests.swift; sourceTree = "<group>"; };
776CEF762344083F00E36792 /* SnakeCaseRawValueCodableStringEnumRule.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SnakeCaseRawValueCodableStringEnumRule.swift; sourceTree = "<group>"; };
787CDE38208E7D41005F3D2F /* SwitchCaseAlignmentConfiguration.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SwitchCaseAlignmentConfiguration.swift; sourceTree = "<group>"; };
787CDE3A208F9C34005F3D2F /* SwitchCaseAlignmentRuleTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SwitchCaseAlignmentRuleTests.swift; sourceTree = "<group>"; };
78F032441D7C877800BE709A /* OverriddenSuperCallRule.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = OverriddenSuperCallRule.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -1152,6 +1154,7 @@
D442541E1DB87C3D00492EA4 /* ValidIBInspectableRule.swift */,
094384FF1D5D2382009168CF /* WeakDelegateRule.swift */,
1872906F1FC37A9B0016BEA2 /* YodaConditionRule.swift */,
776CEF762344083F00E36792 /* SnakeCaseRawValueCodableStringEnumRule.swift */,
);
path = Lint;
sourceTree = "<group>";
Expand Down Expand Up @@ -2002,6 +2005,7 @@
29FC197921382C07006D208C /* DuplicateImportsRule.swift in Sources */,
E816194E1BFBFEAB00946723 /* ForceTryRule.swift in Sources */,
8F2CC1CB20A6A070006ED34F /* FileNameConfiguration.swift in Sources */,
776CEF772344083F00E36792 /* SnakeCaseRawValueCodableStringEnumRule.swift in Sources */,
D4CFC5D2209EC95A00668488 /* FunctionDefaultParameterAtEndRule.swift in Sources */,
E88198541BEA945100333A11 /* CommaRule.swift in Sources */,
D4DA1DFE1E1A10DB0037413D /* NumberSeparatorConfiguration.swift in Sources */,
Expand Down
7 changes: 7 additions & 0 deletions Tests/LinuxMain.swift
Original file line number Diff line number Diff line change
Expand Up @@ -1241,6 +1241,12 @@ extension SingleTestClassRuleTests {
]
}

extension SnakeCaseRawValueCodableStringEnumRuleTests {
static var allTests: [(String, (SnakeCaseRawValueCodableStringEnumRuleTests) -> () throws -> Void)] = [
("testWithDefaultConfiguration", testWithDefaultConfiguration)
]
}

extension SortedFirstLastRuleTests {
static var allTests: [(String, (SortedFirstLastRuleTests) -> () throws -> Void)] = [
("testWithDefaultConfiguration", testWithDefaultConfiguration)
Expand Down Expand Up @@ -1724,6 +1730,7 @@ XCTMain([
testCase(RulesTests.allTests),
testCase(ShorthandOperatorRuleTests.allTests),
testCase(SingleTestClassRuleTests.allTests),
testCase(SnakeCaseRawValueCodableStringEnumRuleTests.allTests),
testCase(SortedFirstLastRuleTests.allTests),
testCase(SortedImportsRuleTests.allTests),
testCase(SourceKitCrashTests.allTests),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -642,6 +642,12 @@ class SingleTestClassRuleTests: XCTestCase {
}
}

class SnakeCaseRawValueCodableStringEnumRuleTests: XCTestCase {
func testWithDefaultConfiguration() {
verifyRule(SnakeCaseRawValueCodableStringEnumRule.description)
}
}

class SortedFirstLastRuleTests: XCTestCase {
func testWithDefaultConfiguration() {
verifyRule(SortedFirstLastRule.description)
Expand Down

0 comments on commit 6a540c2

Please sign in to comment.