Skip to content

Commit

Permalink
Implement realm#2888
Browse files Browse the repository at this point in the history
  • Loading branch information
fddecc authored and marcelofabri committed Oct 21, 2019
1 parent 3706f31 commit 0799462
Show file tree
Hide file tree
Showing 7 changed files with 251 additions and 0 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,10 @@
* Support building with Swift 5.1 on Linux.
[Marcelo Fabri](https://github.com/marcelofabri)
[#2874](https://github.com/realm/SwiftLint/issues/2874)
* Add `snake_case_raw_value_codable_string_enum` opt-in rule to enforce snake
cased raw values for camel cased Codable String enum cases.
[Marko Pejovic](https://github.com/00FA9A)
[#2888](https://github.com/realm/SwiftLint/issues/2888)

#### Bug Fixes

Expand Down
100 changes: 100 additions & 0 deletions Rules.md
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,7 @@
* [Quick Discouraged Call](#quick-discouraged-call)
* [Quick Discouraged Focused Test](#quick-discouraged-focused-test)
* [Quick Discouraged Pending Test](#quick-discouraged-pending-test)
* [Raw Value For Camel Cased Codable String Enum](#raw-alue-for-camel-cased-codable-enum)
* [Reduce Boolean](#reduce-boolean)
* [Reduce Into](#reduce-into)
* [Redundant Discardable Let](#redundant-discardable-let)
Expand Down Expand Up @@ -16873,6 +16874,105 @@ class TotoTests: QuickSpec {
## Raw Value For Camel Cased Codable Enum
Identifier | Enabled by default | Supports autocorrection | Kind | Analyzer | Minimum Swift Compiler Version
--- | --- | --- | --- | --- | ---
`raw_value_for_camel_cased_codable_enum` | Disabled | No | lint | No | 3.0.0
Camel cased Codable String enum cases should have explicitly assigned raw value.
### Examples
<details>
<summary>Non Triggering Examples</summary>
```swift
enum Numbers: Codable {
case int(Int)
case short(Int16)
}
```
```swift
enum Numbers: Int, Codable {
case one = 1
case two = 2
}
```
```swift
enum Numbers: Double, Codable {
case one = 1.1
case two = 2.2
}
```
```swift
enum Numbers: String, Codable {
case one = "one"
case two = "two"
}
```
```swift
enum Status: String {
case ok
case notAcceptable
case maybeAcceptable = "maybe_acceptable"
}
```
```swift
enum Status: Int, Codable {
case ok
case notAcceptable
case maybeAcceptable = -1
}
```
</details>
<details>
<summary>Triggering Examples</summary>
```swift
enum Status: String, Codable {
case ok
case ↓notAcceptable
case maybeAcceptable = "maybe_acceptable"
}
```
```swift
enum Status: String, Decodable {
case ok
case ↓notAcceptable
case maybeAcceptable = "maybe_acceptable"
}
```
```swift
enum Status: String, Encodable {
case ok
case ↓notAcceptable
case maybeAcceptable = "maybe_acceptable"
}
```
```swift
enum Status: String, Codable {
case ok
case ↓notAcceptable
case maybeAcceptable = "maybe_acceptable"
}
```
</details>
## Reduce Boolean
Identifier | Enabled by default | Supports autocorrection | Kind | Analyzer | Minimum Swift Compiler Version
Expand Down
1 change: 1 addition & 0 deletions Source/SwiftLintFramework/Models/MasterRuleList.swift
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,7 @@ public let masterRuleList = RuleList(rules: [
QuickDiscouragedCallRule.self,
QuickDiscouragedFocusedTestRule.self,
QuickDiscouragedPendingTestRule.self,
RawValueForCamelCasedCodableEnumRule.self,
ReduceBooleanRule.self,
ReduceIntoRule.self,
RedundantDiscardableLetRule.self,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
import SourceKittenFramework

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

public init() {}

public static let description = RuleDescription(
identifier: "raw_value_for_camel_cased_codable_enum",
name: "Raw Value For Camel Cased Codable Enum",
description: "Camel cased cases of Codable String enums should have 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 */; };
77DFF0E923442DE30041EEB4 /* RawValueForCamelCasedCodableEnumRule.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7723A4DE23442D7100F38590 /* RawValueForCamelCasedCodableEnumRule.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>"; };
7723A4DE23442D7100F38590 /* RawValueForCamelCasedCodableEnumRule.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RawValueForCamelCasedCodableEnumRule.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 @@ -1112,6 +1114,7 @@
E315B83B1DFA4BC500621B44 /* DynamicInlineRule.swift */,
62A3E95B209E078000547A86 /* EmptyXCTestMethodRule.swift */,
626B01B420A1735900D2C42F /* EmptyXCTestMethodRuleExamples.swift */,
7723A4DE23442D7100F38590 /* RawValueForCamelCasedCodableEnumRule.swift */,
D4E92D1E2137B4C9002EDD48 /* IdenticalOperandsRule.swift */,
D4441A27213279950020896F /* InertDeferRule.swift */,
C26330352073DAA200D7B4FD /* LowerACLThanParentRule.swift */,
Expand Down Expand Up @@ -2170,6 +2173,7 @@
1E3C2D711EE36C6F00C8386D /* PrivateOverFilePrivateRule.swift in Sources */,
188B3FF2207D61040073C2D6 /* ModifierOrderRule.swift in Sources */,
72EA17B61FD31F10009D5CE6 /* ExplicitACLRule.swift in Sources */,
77DFF0E923442DE30041EEB4 /* RawValueForCamelCasedCodableEnumRule.swift in Sources */,
B2902A0C1D66815600BFCCF7 /* PrivateUnitTestRule.swift in Sources */,
D47A51101DB2DD4800A4CC21 /* AttributesRule.swift in Sources */,
CE8178ED1EAC039D0063186E /* UnusedOptionalBindingConfiguration.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 @@ -1058,6 +1058,12 @@ extension QuickDiscouragedPendingTestRuleTests {
]
}

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

extension ReduceBooleanRuleTests {
static var allTests: [(String, (ReduceBooleanRuleTests) -> () throws -> Void)] = [
("testWithDefaultConfiguration", testWithDefaultConfiguration)
Expand Down Expand Up @@ -1704,6 +1710,7 @@ XCTMain([
testCase(QuickDiscouragedCallRuleTests.allTests),
testCase(QuickDiscouragedFocusedTestRuleTests.allTests),
testCase(QuickDiscouragedPendingTestRuleTests.allTests),
testCase(RawValueForCamelCasedCodableEnumRuleTests.allTests),
testCase(ReduceBooleanRuleTests.allTests),
testCase(ReduceIntoRuleTests.allTests),
testCase(RedundantDiscardableLetRuleTests.allTests),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -558,6 +558,12 @@ class QuickDiscouragedPendingTestRuleTests: XCTestCase {
}
}

class RawValueForCamelCasedCodableEnumRuleTests: XCTestCase {
func testWithDefaultConfiguration() {
verifyRule(RawValueForCamelCasedCodableEnumRule.description)
}
}

class ReduceBooleanRuleTests: XCTestCase {
func testWithDefaultConfiguration() {
verifyRule(ReduceBooleanRule.description)
Expand Down

0 comments on commit 0799462

Please sign in to comment.