-
Notifications
You must be signed in to change notification settings - Fork 2.2k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Explicit Enum Raw Value Rule #1788
Changes from all commits
742bb2f
831afc4
1929228
3d223cc
adfcbf4
5088bae
cba9493
c85c44a
fb193c9
5335060
73edd5c
6e6ae83
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,89 @@ | ||
// | ||
// ExplicitEnumRawValueRule.swift | ||
// SwiftLint | ||
// | ||
// Created by Mazyad Alabduljaleel on 8/19/17. | ||
// Copyright © 2017 Realm. All rights reserved. | ||
// | ||
|
||
import Foundation | ||
import SourceKittenFramework | ||
|
||
public struct ExplicitEnumRawValueRule: ASTRule, OptInRule, ConfigurationProviderRule { | ||
public var configuration = SeverityConfiguration(.warning) | ||
|
||
public init() {} | ||
|
||
public static let description = RuleDescription( | ||
identifier: "explicit_enum_raw_value", | ||
name: "Explicit Enum Raw Value", | ||
description: "Enums should be explicitly assigned their raw values.", | ||
kind: .idiomatic, | ||
nonTriggeringExamples: [ | ||
"enum Numbers {\n case int(Int)\n case short(Int16)\n}\n", | ||
"enum Numbers: Int {\n case one = 1\n case two = 2\n}\n", | ||
"enum Numbers: Double {\n case one = 1.1\n case two = 2.2\n}\n", | ||
"enum Numbers: String {\n case one = \"one\"\n case two = \"two\"\n}\n", | ||
"protocol Algebra {}\nenum Numbers: Algebra {\n case one\n}\n" | ||
], | ||
triggeringExamples: [ | ||
"enum Numbers: Int {\n case one = 10, ↓two, three = 30\n}\n", | ||
"enum Numbers: NSInteger {\n case ↓one\n}\n", | ||
"enum Numbers: String {\n case ↓one\n case ↓two\n}\n", | ||
"enum Numbers: String {\n case ↓one, two = \"two\"\n}\n", | ||
"enum Numbers: Decimal {\n case ↓one, ↓two\n}\n" | ||
] | ||
) | ||
|
||
public func validate(file: File, kind: SwiftDeclarationKind, | ||
dictionary: [String: SourceKitRepresentable]) -> [StyleViolation] { | ||
guard kind == .enum else { | ||
return [] | ||
} | ||
|
||
// Check if it's an enum which supports raw values | ||
let implicitRawValueTypes: [Any] = [ | ||
Int.self, Int8.self, Int16.self, Int32.self, Int64.self, | ||
UInt.self, UInt8.self, UInt16.self, UInt32.self, UInt64.self, | ||
Double.self, Float.self, Float80.self, Decimal.self, NSNumber.self, | ||
NSDecimalNumber.self, "NSInteger", String.self | ||
] | ||
|
||
let implicitRawValueSet = Set(implicitRawValueTypes.map(String.init(describing:))) | ||
let enumInheritedTypesSet = Set(dictionary.inheritedTypes) | ||
|
||
guard !implicitRawValueSet.isDisjoint(with: enumInheritedTypesSet) 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) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Could you please combine the 3 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Sure, I can see this logic using a single loop with There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Nevermind, I don't think my suggestion would improve things after all. |
||
.flatMap { substructureElements(of: $0, matching: .enumelement) } | ||
.flatMap(enumElementsMissingInitExpr) | ||
.flatMap { $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 enumElementsMissingInitExpr( | ||
_ enumElements: [[String: SourceKitRepresentable]]) -> [[String: SourceKitRepresentable]] { | ||
|
||
return enumElements | ||
.filter { !$0.elements.contains { $0.kind == "source.lang.swift.structure.elem.init_expr" } } | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
TIL that you can use
NSNumber
and other types as raw values 😅