mirror of
https://github.com/realm/SwiftLint.git
synced 2026-05-07 20:12:49 +00:00
Make use of macros to generate configuration parsing code (#5250)
This commit is contained in:
@@ -69,6 +69,7 @@ file_name:
|
||||
excluded:
|
||||
- Exports.swift
|
||||
- GeneratedTests.swift
|
||||
- RuleConfigurationMacros.swift
|
||||
- SwiftSyntax+SwiftLint.swift
|
||||
- TestHelpers.swift
|
||||
|
||||
|
||||
@@ -3,10 +3,20 @@ load(
|
||||
"@build_bazel_rules_swift//swift:swift.bzl",
|
||||
"swift_binary",
|
||||
"swift_library",
|
||||
"swift_compiler_plugin"
|
||||
)
|
||||
|
||||
# Targets
|
||||
|
||||
swift_compiler_plugin(
|
||||
name = "SwiftLintCoreMacros",
|
||||
srcs = glob(["Source/SwiftLintCoreMacros/*.swift"]),
|
||||
deps = [
|
||||
"@SwiftSyntax//:SwiftCompilerPlugin_opt",
|
||||
"@SwiftSyntax//:SwiftSyntaxMacros_opt",
|
||||
],
|
||||
)
|
||||
|
||||
swift_library(
|
||||
name = "SwiftLintCore",
|
||||
srcs = glob(["Source/SwiftLintCore/**/*.swift"]),
|
||||
@@ -25,6 +35,9 @@ swift_library(
|
||||
"@platforms//os:linux": ["@com_github_krzyzanowskim_cryptoswift//:CryptoSwift"],
|
||||
"//conditions:default": [":DyldWarningWorkaround"],
|
||||
}),
|
||||
plugins = [
|
||||
":SwiftLintCoreMacros",
|
||||
],
|
||||
)
|
||||
|
||||
swift_library(
|
||||
|
||||
@@ -133,6 +133,9 @@ package: build
|
||||
--version "$(VERSION_STRING)" \
|
||||
"$(OUTPUT_PACKAGE)"
|
||||
|
||||
bazel_test:
|
||||
bazel test --test_output=errors //Tests/...
|
||||
|
||||
bazel_release:
|
||||
bazel build :release
|
||||
mv bazel-bin/bazel.tar.gz bazel-bin/bazel.tar.gz.sha256 .
|
||||
|
||||
+13
-2
@@ -1,4 +1,5 @@
|
||||
// swift-tools-version:5.9
|
||||
import CompilerPluginSupport
|
||||
import PackageDescription
|
||||
|
||||
let package = Package(
|
||||
@@ -55,6 +56,7 @@ let package = Package(
|
||||
.product(name: "SwiftSyntaxBuilder", package: "swift-syntax"),
|
||||
.product(name: "SwiftyTextTable", package: "SwiftyTextTable"),
|
||||
.product(name: "Yams", package: "Yams"),
|
||||
"SwiftLintCoreMacros"
|
||||
]
|
||||
),
|
||||
.target(
|
||||
@@ -88,7 +90,8 @@ let package = Package(
|
||||
name: "SwiftLintFrameworkTests",
|
||||
dependencies: [
|
||||
"SwiftLintFramework",
|
||||
"SwiftLintTestHelpers"
|
||||
"SwiftLintTestHelpers",
|
||||
"SwiftLintCoreMacros"
|
||||
],
|
||||
exclude: [
|
||||
"Resources",
|
||||
@@ -119,6 +122,14 @@ let package = Package(
|
||||
name: "SwiftLintBinary",
|
||||
url: "https://github.com/realm/SwiftLint/releases/download/0.53.0/SwiftLintBinary-macos.artifactbundle.zip",
|
||||
checksum: "03416a4f75f023e10f9a76945806ddfe70ca06129b895455cc773c5c7d86b73e"
|
||||
)
|
||||
),
|
||||
.macro(
|
||||
name: "SwiftLintCoreMacros",
|
||||
dependencies: [
|
||||
.product(name: "SwiftSyntaxMacros", package: "swift-syntax"),
|
||||
.product(name: "SwiftCompilerPlugin", package: "swift-syntax")
|
||||
],
|
||||
path: "Source/SwiftLintCoreMacros"
|
||||
),
|
||||
]
|
||||
)
|
||||
|
||||
@@ -229,7 +229,7 @@ private extension ClassDeclSyntax {
|
||||
func hasParent(configuredIn config: PrivateUnitTestConfiguration) -> Bool {
|
||||
inheritanceClause?.inheritedTypes.contains { type in
|
||||
if let name = type.type.as(IdentifierTypeSyntax.self)?.name.text {
|
||||
return config.regex.numberOfMatches(in: name, range: name.fullNSRange) > 0
|
||||
return config.regex.regex.numberOfMatches(in: name, range: name.fullNSRange) > 0
|
||||
|| config.testParentClasses.contains(name)
|
||||
}
|
||||
return false
|
||||
|
||||
+1
-23
@@ -1,5 +1,6 @@
|
||||
import SwiftLintCore
|
||||
|
||||
@AutoApply
|
||||
struct AttributesConfiguration: SeverityBasedRuleConfiguration, Equatable {
|
||||
typealias Parent = AttributesRule
|
||||
|
||||
@@ -11,27 +12,4 @@ struct AttributesConfiguration: SeverityBasedRuleConfiguration, Equatable {
|
||||
private(set) var alwaysOnSameLine = Set<String>(["@IBAction", "@NSManaged"])
|
||||
@ConfigurationElement(key: "always_on_line_above")
|
||||
private(set) var alwaysOnNewLine = Set<String>()
|
||||
|
||||
mutating func apply(configuration: Any) throws {
|
||||
guard let configuration = configuration as? [String: Any] else {
|
||||
throw Issue.unknownConfiguration(ruleID: Parent.identifier)
|
||||
}
|
||||
|
||||
if let attributesWithArgumentsAlwaysOnNewLine
|
||||
= configuration[$attributesWithArgumentsAlwaysOnNewLine] as? Bool {
|
||||
self.attributesWithArgumentsAlwaysOnNewLine = attributesWithArgumentsAlwaysOnNewLine
|
||||
}
|
||||
|
||||
if let alwaysOnSameLine = configuration[$alwaysOnSameLine] as? [String] {
|
||||
self.alwaysOnSameLine = Set(alwaysOnSameLine)
|
||||
}
|
||||
|
||||
if let alwaysOnNewLine = configuration[$alwaysOnNewLine] as? [String] {
|
||||
self.alwaysOnNewLine = Set(alwaysOnNewLine)
|
||||
}
|
||||
|
||||
if let severityString = configuration[$severityConfiguration] as? String {
|
||||
try severityConfiguration.apply(configuration: severityString)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+1
-18
@@ -1,5 +1,6 @@
|
||||
import SwiftLintCore
|
||||
|
||||
@AutoApply
|
||||
struct BlanketDisableCommandConfiguration: SeverityBasedRuleConfiguration, Equatable {
|
||||
typealias Parent = BlanketDisableCommandRule
|
||||
|
||||
@@ -15,22 +16,4 @@ struct BlanketDisableCommandConfiguration: SeverityBasedRuleConfiguration, Equat
|
||||
]
|
||||
@ConfigurationElement(key: "always_blanket_disable")
|
||||
private(set) var alwaysBlanketDisableRuleIdentifiers: Set<String> = []
|
||||
|
||||
mutating func apply(configuration: Any) throws {
|
||||
guard let configuration = configuration as? [String: Any] else {
|
||||
throw Issue.unknownConfiguration(ruleID: Parent.identifier)
|
||||
}
|
||||
|
||||
if let severityString = configuration[$severityConfiguration] as? String {
|
||||
try severityConfiguration.apply(configuration: severityString)
|
||||
}
|
||||
|
||||
if let allowedRuleIdentifiers = configuration[$allowedRuleIdentifiers] as? [String] {
|
||||
self.allowedRuleIdentifiers = Set(allowedRuleIdentifiers)
|
||||
}
|
||||
|
||||
if let alwaysBlanketDisableRuleIdentifiers = configuration[$alwaysBlanketDisableRuleIdentifiers] as? [String] {
|
||||
self.alwaysBlanketDisableRuleIdentifiers = Set(alwaysBlanketDisableRuleIdentifiers)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+1
-12
@@ -1,5 +1,6 @@
|
||||
import SwiftLintCore
|
||||
|
||||
@AutoApply
|
||||
struct CollectionAlignmentConfiguration: SeverityBasedRuleConfiguration, Equatable {
|
||||
typealias Parent = CollectionAlignmentRule
|
||||
|
||||
@@ -7,16 +8,4 @@ struct CollectionAlignmentConfiguration: SeverityBasedRuleConfiguration, Equatab
|
||||
private(set) var severityConfiguration = SeverityConfiguration<Parent>(.warning)
|
||||
@ConfigurationElement(key: "align_colons")
|
||||
private(set) var alignColons = false
|
||||
|
||||
mutating func apply(configuration: Any) throws {
|
||||
guard let configuration = configuration as? [String: Any] else {
|
||||
throw Issue.unknownConfiguration(ruleID: Parent.identifier)
|
||||
}
|
||||
|
||||
alignColons = configuration[$alignColons] as? Bool ?? false
|
||||
|
||||
if let severityString = configuration[$severityConfiguration] as? String {
|
||||
try severityConfiguration.apply(configuration: severityString)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import SwiftLintCore
|
||||
|
||||
@AutoApply
|
||||
struct ColonConfiguration: SeverityBasedRuleConfiguration, Equatable {
|
||||
typealias Parent = ColonRule
|
||||
|
||||
@@ -9,17 +10,4 @@ struct ColonConfiguration: SeverityBasedRuleConfiguration, Equatable {
|
||||
private(set) var flexibleRightSpacing = false
|
||||
@ConfigurationElement(key: "apply_to_dictionaries")
|
||||
private(set) var applyToDictionaries = true
|
||||
|
||||
mutating func apply(configuration: Any) throws {
|
||||
guard let configuration = configuration as? [String: Any] else {
|
||||
throw Issue.unknownConfiguration(ruleID: Parent.identifier)
|
||||
}
|
||||
|
||||
flexibleRightSpacing = configuration[$flexibleRightSpacing] as? Bool == true
|
||||
applyToDictionaries = configuration[$applyToDictionaries] as? Bool ?? true
|
||||
|
||||
if let severityString = configuration[$severityConfiguration] as? String {
|
||||
try severityConfiguration.apply(configuration: severityString)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+3
-18
@@ -1,32 +1,17 @@
|
||||
import SwiftLintCore
|
||||
|
||||
@AutoApply
|
||||
struct ComputedAccessorsOrderConfiguration: SeverityBasedRuleConfiguration, Equatable {
|
||||
typealias Parent = ComputedAccessorsOrderRule
|
||||
|
||||
enum Order: String, AcceptableByConfigurationElement {
|
||||
@MakeAcceptableByConfigurationElement
|
||||
enum Order: String {
|
||||
case getSet = "get_set"
|
||||
case setGet = "set_get"
|
||||
|
||||
func asOption() -> OptionType { .symbol(rawValue) }
|
||||
}
|
||||
|
||||
@ConfigurationElement(key: "severity")
|
||||
private(set) var severityConfiguration = SeverityConfiguration<Parent>(.warning)
|
||||
@ConfigurationElement(key: "order")
|
||||
private(set) var order = Order.getSet
|
||||
|
||||
mutating func apply(configuration: Any) throws {
|
||||
guard let configuration = configuration as? [String: Any] else {
|
||||
throw Issue.unknownConfiguration(ruleID: Parent.identifier)
|
||||
}
|
||||
|
||||
if let orderString = configuration[$order] as? String,
|
||||
let order = Order(rawValue: orderString) {
|
||||
self.order = order
|
||||
}
|
||||
|
||||
if let severityString = configuration[$severityConfiguration] as? String {
|
||||
try severityConfiguration.apply(configuration: severityString)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+1
-12
@@ -1,5 +1,6 @@
|
||||
import SwiftLintCore
|
||||
|
||||
@AutoApply
|
||||
struct ConditionalReturnsOnNewlineConfiguration: SeverityBasedRuleConfiguration, Equatable {
|
||||
typealias Parent = ConditionalReturnsOnNewlineRule
|
||||
|
||||
@@ -7,16 +8,4 @@ struct ConditionalReturnsOnNewlineConfiguration: SeverityBasedRuleConfiguration,
|
||||
private(set) var severityConfiguration = SeverityConfiguration<Parent>(.warning)
|
||||
@ConfigurationElement(key: "if_only")
|
||||
private(set) var ifOnly = false
|
||||
|
||||
mutating func apply(configuration: Any) throws {
|
||||
guard let configuration = configuration as? [String: Any] else {
|
||||
throw Issue.unknownConfiguration(ruleID: Parent.identifier)
|
||||
}
|
||||
|
||||
ifOnly = configuration[$ifOnly] as? Bool ?? false
|
||||
|
||||
if let severityString = configuration[$severityConfiguration] as? String {
|
||||
try severityConfiguration.apply(configuration: severityString)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+5
-35
@@ -1,6 +1,7 @@
|
||||
import SourceKittenFramework
|
||||
import SwiftLintCore
|
||||
|
||||
@AutoApply
|
||||
struct CyclomaticComplexityConfiguration: RuleConfiguration, Equatable {
|
||||
typealias Parent = CyclomaticComplexityRule
|
||||
|
||||
@@ -10,50 +11,19 @@ struct CyclomaticComplexityConfiguration: RuleConfiguration, Equatable {
|
||||
.guard,
|
||||
.for,
|
||||
.repeatWhile,
|
||||
.while,
|
||||
.case
|
||||
.while
|
||||
]
|
||||
|
||||
@ConfigurationElement
|
||||
private(set) var length = SeverityLevelsConfiguration<Parent>(warning: 10, error: 20)
|
||||
private(set) var complexityStatements = Self.defaultComplexityStatements
|
||||
|
||||
@ConfigurationElement(key: "ignores_case_statements")
|
||||
private(set) var ignoresCaseStatements = false {
|
||||
didSet {
|
||||
if ignoresCaseStatements {
|
||||
complexityStatements.remove(.case)
|
||||
} else {
|
||||
complexityStatements.insert(.case)
|
||||
}
|
||||
}
|
||||
}
|
||||
private(set) var ignoresCaseStatements = false
|
||||
|
||||
var params: [RuleParameter<Int>] {
|
||||
return length.params
|
||||
}
|
||||
|
||||
mutating func apply(configuration: Any) throws {
|
||||
if let configurationArray = [Int].array(of: configuration),
|
||||
configurationArray.isNotEmpty {
|
||||
let warning = configurationArray[0]
|
||||
let error = (configurationArray.count > 1) ? configurationArray[1] : nil
|
||||
length = SeverityLevelsConfiguration<Parent>(warning: warning, error: error)
|
||||
} else if let configDict = configuration as? [String: Any], configDict.isNotEmpty {
|
||||
for (string, value) in configDict {
|
||||
switch (string, value) {
|
||||
case ("error", let intValue as Int):
|
||||
length.error = intValue
|
||||
case ("warning", let intValue as Int):
|
||||
length.warning = intValue
|
||||
case ($ignoresCaseStatements, let boolValue as Bool):
|
||||
ignoresCaseStatements = boolValue
|
||||
default:
|
||||
throw Issue.unknownConfiguration(ruleID: Parent.identifier)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
throw Issue.unknownConfiguration(ruleID: Parent.identifier)
|
||||
}
|
||||
var complexityStatements: Set<StatementKind> {
|
||||
Self.defaultComplexityStatements.union(ignoresCaseStatements ? [] : [.case])
|
||||
}
|
||||
}
|
||||
|
||||
+8
-23
@@ -1,37 +1,22 @@
|
||||
import SwiftLintCore
|
||||
|
||||
private func toExplicitInitMethod(typeName: String) -> String {
|
||||
return "\(typeName).init"
|
||||
}
|
||||
// swiftlint:disable:next blanket_disable_command
|
||||
// swiftlint:disable let_var_whitespace
|
||||
|
||||
@AutoApply
|
||||
struct DiscouragedDirectInitConfiguration: SeverityBasedRuleConfiguration, Equatable {
|
||||
typealias Parent = DiscouragedDirectInitRule
|
||||
|
||||
@ConfigurationElement(key: "severity")
|
||||
var severityConfiguration = SeverityConfiguration<Parent>(.warning)
|
||||
|
||||
private static let defaultDiscouragedInits = [
|
||||
@ConfigurationElement(
|
||||
key: "types",
|
||||
postprocessor: { $0.formUnion($0.map { name in "\(name).init" }) }
|
||||
)
|
||||
private(set) var discouragedInits: Set = [
|
||||
"Bundle",
|
||||
"NSError",
|
||||
"UIDevice"
|
||||
]
|
||||
|
||||
@ConfigurationElement(key: "types")
|
||||
private(set) var discouragedInits = Set(
|
||||
Self.defaultDiscouragedInits + Self.defaultDiscouragedInits.map(toExplicitInitMethod)
|
||||
)
|
||||
|
||||
mutating func apply(configuration: Any) throws {
|
||||
guard let configuration = configuration as? [String: Any] else {
|
||||
throw Issue.unknownConfiguration(ruleID: Parent.identifier)
|
||||
}
|
||||
|
||||
if let severityString = configuration[$severityConfiguration] as? String {
|
||||
try severityConfiguration.apply(configuration: severityString)
|
||||
}
|
||||
|
||||
if let types = [String].array(of: configuration[$discouragedInits]) {
|
||||
discouragedInits = Set(types + types.map(toExplicitInitMethod))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+1
-12
@@ -1,5 +1,6 @@
|
||||
import SwiftLintCore
|
||||
|
||||
@AutoApply
|
||||
struct EmptyCountConfiguration: SeverityBasedRuleConfiguration, Equatable {
|
||||
typealias Parent = EmptyCountRule
|
||||
|
||||
@@ -7,16 +8,4 @@ struct EmptyCountConfiguration: SeverityBasedRuleConfiguration, Equatable {
|
||||
private(set) var severityConfiguration = SeverityConfiguration<Parent>(.error)
|
||||
@ConfigurationElement(key: "only_after_dot")
|
||||
private(set) var onlyAfterDot = false
|
||||
|
||||
mutating func apply(configuration: Any) throws {
|
||||
guard let configuration = configuration as? [String: Any] else {
|
||||
throw Issue.unknownConfiguration(ruleID: Parent.identifier)
|
||||
}
|
||||
|
||||
if let severityString = configuration[$severityConfiguration] as? String {
|
||||
try severityConfiguration.apply(configuration: severityString)
|
||||
}
|
||||
|
||||
onlyAfterDot = configuration[$onlyAfterDot] as? Bool ?? false
|
||||
}
|
||||
}
|
||||
|
||||
+15
-33
@@ -1,5 +1,6 @@
|
||||
import SwiftLintCore
|
||||
|
||||
@AutoApply
|
||||
struct ExpiringTodoConfiguration: RuleConfiguration, Equatable {
|
||||
typealias Parent = ExpiringTodoRule
|
||||
typealias Severity = SeverityConfiguration<Parent>
|
||||
@@ -10,6 +11,20 @@ struct ExpiringTodoConfiguration: RuleConfiguration, Equatable {
|
||||
fileprivate(set) var opening: String
|
||||
fileprivate(set) var closing: String
|
||||
|
||||
init(fromAny value: Any, context ruleID: String) throws {
|
||||
guard let dateDelimiters = value as? [String: String],
|
||||
let openingDelimiter = dateDelimiters["opening"],
|
||||
let closingDelimiter = dateDelimiters["closing"] else {
|
||||
throw Issue.invalidConfiguration(ruleID: ruleID)
|
||||
}
|
||||
self.init(opening: openingDelimiter, closing: closingDelimiter)
|
||||
}
|
||||
|
||||
init(opening: String, closing: String) {
|
||||
self.opening = opening
|
||||
self.closing = closing
|
||||
}
|
||||
|
||||
func asOption() -> OptionType {
|
||||
.nest {
|
||||
"opening" => .string(opening)
|
||||
@@ -38,37 +53,4 @@ struct ExpiringTodoConfiguration: RuleConfiguration, Equatable {
|
||||
/// The separator used for regex detection of the expiry-date string
|
||||
@ConfigurationElement(key: "date_separator")
|
||||
private(set) var dateSeparator = "/"
|
||||
|
||||
mutating func apply(configuration: Any) throws {
|
||||
guard let configurationDict = configuration as? [String: Any] else {
|
||||
throw Issue.unknownConfiguration(ruleID: Parent.identifier)
|
||||
}
|
||||
|
||||
if let approachingExpiryConfiguration = configurationDict[$approachingExpirySeverity] {
|
||||
try approachingExpirySeverity.apply(configuration: approachingExpiryConfiguration)
|
||||
}
|
||||
if let expiredConfiguration = configurationDict[$expiredSeverity] {
|
||||
try expiredSeverity.apply(configuration: expiredConfiguration)
|
||||
}
|
||||
if let badFormattingConfiguration = configurationDict[$badFormattingSeverity] {
|
||||
try badFormattingSeverity.apply(configuration: badFormattingConfiguration)
|
||||
}
|
||||
if let approachingExpiryThreshold = configurationDict[$approachingExpiryThreshold] as? Int {
|
||||
self.approachingExpiryThreshold = approachingExpiryThreshold
|
||||
}
|
||||
if let dateFormat = configurationDict[$dateFormat] as? String {
|
||||
self.dateFormat = dateFormat
|
||||
}
|
||||
if let dateDelimiters = configurationDict[$dateDelimiters] as? [String: String] {
|
||||
if let openingDelimiter = dateDelimiters["opening"] {
|
||||
self.dateDelimiters.opening = openingDelimiter
|
||||
}
|
||||
if let closingDelimiter = dateDelimiters["closing"] {
|
||||
self.dateDelimiters.closing = closingDelimiter
|
||||
}
|
||||
}
|
||||
if let dateSeparator = configurationDict[$dateSeparator] as? String {
|
||||
self.dateSeparator = dateSeparator
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+1
-14
@@ -1,3 +1,4 @@
|
||||
@AutoApply
|
||||
struct ExplicitInitConfiguration: SeverityBasedRuleConfiguration, Equatable {
|
||||
typealias Parent = ExplicitInitRule
|
||||
|
||||
@@ -5,18 +6,4 @@ struct ExplicitInitConfiguration: SeverityBasedRuleConfiguration, Equatable {
|
||||
private(set) var severityConfiguration = SeverityConfiguration<Parent>(.warning)
|
||||
@ConfigurationElement(key: "include_bare_init")
|
||||
private(set) var includeBareInit = false
|
||||
|
||||
mutating func apply(configuration: Any) throws {
|
||||
guard let configuration = configuration as? [String: Any] else {
|
||||
throw Issue.unknownConfiguration(ruleID: Parent.identifier)
|
||||
}
|
||||
|
||||
if let severityString = configuration[$severityConfiguration] as? String {
|
||||
try severityConfiguration.apply(configuration: severityString)
|
||||
}
|
||||
|
||||
if let includeBareInit = configuration[$includeBareInit] as? Bool {
|
||||
self.includeBareInit = includeBareInit
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+3
-21
@@ -1,17 +1,17 @@
|
||||
import SwiftLintCore
|
||||
|
||||
@AutoApply
|
||||
struct ExplicitTypeInterfaceConfiguration: SeverityBasedRuleConfiguration, Equatable {
|
||||
typealias Parent = ExplicitTypeInterfaceRule
|
||||
|
||||
enum VariableKind: String, CaseIterable, AcceptableByConfigurationElement {
|
||||
@MakeAcceptableByConfigurationElement
|
||||
enum VariableKind: String, CaseIterable {
|
||||
case instance
|
||||
case local
|
||||
case `static`
|
||||
case `class`
|
||||
|
||||
static let all = Set(allCases)
|
||||
|
||||
func asOption() -> SwiftLintCore.OptionType { .symbol(rawValue) }
|
||||
}
|
||||
|
||||
@ConfigurationElement(key: "severity")
|
||||
@@ -24,22 +24,4 @@ struct ExplicitTypeInterfaceConfiguration: SeverityBasedRuleConfiguration, Equat
|
||||
var allowedKinds: Set<VariableKind> {
|
||||
VariableKind.all.subtracting(excluded)
|
||||
}
|
||||
|
||||
mutating func apply(configuration: Any) throws {
|
||||
guard let configuration = configuration as? [String: Any] else {
|
||||
throw Issue.unknownConfiguration(ruleID: Parent.identifier)
|
||||
}
|
||||
for (key, value) in configuration {
|
||||
switch (key, value) {
|
||||
case ($severityConfiguration, let severityString as String):
|
||||
try severityConfiguration.apply(configuration: severityString)
|
||||
case ($excluded, let excludedStrings as [String]):
|
||||
self.excluded = excludedStrings.compactMap(VariableKind.init).unique
|
||||
case ($allowRedundancy, let allowRedundancy as Bool):
|
||||
self.allowRedundancy = allowRedundancy
|
||||
default:
|
||||
throw Issue.unknownConfiguration(ruleID: Parent.identifier)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -34,33 +34,33 @@ struct FileHeaderConfiguration: SeverityBasedRuleConfiguration, Equatable {
|
||||
// Cache the created regexes if possible.
|
||||
// If the pattern contains the SWIFTLINT_CURRENT_FILENAME placeholder,
|
||||
// the regex will be recompiled for each validated file.
|
||||
if let requiredString = configuration[$requiredString] {
|
||||
if let requiredString = configuration[$requiredString.key] {
|
||||
self.requiredString = requiredString
|
||||
if !requiredString.contains(Self.fileNamePlaceholder) {
|
||||
_requiredRegex = try NSRegularExpression(pattern: requiredString,
|
||||
options: Self.stringRegexOptions)
|
||||
}
|
||||
} else if let requiredPattern = configuration[$requiredPattern] {
|
||||
} else if let requiredPattern = configuration[$requiredPattern.key] {
|
||||
self.requiredPattern = requiredPattern
|
||||
if !requiredPattern.contains(Self.fileNamePlaceholder) {
|
||||
_requiredRegex = try .cached(pattern: requiredPattern)
|
||||
}
|
||||
}
|
||||
|
||||
if let forbiddenString = configuration[$forbiddenString] {
|
||||
if let forbiddenString = configuration[$forbiddenString.key] {
|
||||
self.forbiddenString = forbiddenString
|
||||
if !forbiddenString.contains(Self.fileNamePlaceholder) {
|
||||
_forbiddenRegex = try NSRegularExpression(pattern: forbiddenString,
|
||||
options: Self.stringRegexOptions)
|
||||
}
|
||||
} else if let forbiddenPattern = configuration[$forbiddenPattern] {
|
||||
} else if let forbiddenPattern = configuration[$forbiddenPattern.key] {
|
||||
self.forbiddenPattern = forbiddenPattern
|
||||
if !forbiddenPattern.contains(Self.fileNamePlaceholder) {
|
||||
_forbiddenRegex = try .cached(pattern: forbiddenPattern)
|
||||
}
|
||||
}
|
||||
|
||||
if let severityString = configuration[$severityConfiguration] {
|
||||
if let severityString = configuration[$severityConfiguration.key] {
|
||||
try severityConfiguration.apply(configuration: severityString)
|
||||
}
|
||||
}
|
||||
|
||||
+1
-24
@@ -1,5 +1,6 @@
|
||||
import SwiftLintCore
|
||||
|
||||
@AutoApply
|
||||
struct FileLengthConfiguration: RuleConfiguration, Equatable {
|
||||
typealias Parent = FileLengthRule
|
||||
|
||||
@@ -7,28 +8,4 @@ struct FileLengthConfiguration: RuleConfiguration, Equatable {
|
||||
private(set) var severityConfiguration = SeverityLevelsConfiguration<Parent>(warning: 400, error: 1000)
|
||||
@ConfigurationElement(key: "ignore_comment_only_lines")
|
||||
private(set) var ignoreCommentOnlyLines = false
|
||||
|
||||
mutating func apply(configuration: Any) throws {
|
||||
if let configurationArray = [Int].array(of: configuration),
|
||||
configurationArray.isNotEmpty {
|
||||
let warning = configurationArray[0]
|
||||
let error = (configurationArray.count > 1) ? configurationArray[1] : nil
|
||||
severityConfiguration = SeverityLevelsConfiguration(warning: warning, error: error)
|
||||
} else if let configDict = configuration as? [String: Any], configDict.isNotEmpty {
|
||||
for (string, value) in configDict {
|
||||
switch (string, value) {
|
||||
case ("error", let intValue as Int):
|
||||
severityConfiguration.error = intValue
|
||||
case ("warning", let intValue as Int):
|
||||
severityConfiguration.warning = intValue
|
||||
case ($ignoreCommentOnlyLines, let boolValue as Bool):
|
||||
ignoreCommentOnlyLines = boolValue
|
||||
default:
|
||||
throw Issue.unknownConfiguration(ruleID: Parent.identifier)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
throw Issue.unknownConfiguration(ruleID: Parent.identifier)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,38 +1,17 @@
|
||||
import SwiftLintCore
|
||||
|
||||
@AutoApply
|
||||
struct FileNameConfiguration: SeverityBasedRuleConfiguration, Equatable {
|
||||
typealias Parent = FileNameRule
|
||||
|
||||
@ConfigurationElement(key: "severity")
|
||||
private(set) var severityConfiguration = SeverityConfiguration<Parent>(.warning)
|
||||
@ConfigurationElement(key: "excluded")
|
||||
private(set) var excluded = Set<String>(["main.swift", "LinuxMain.swift"])
|
||||
private(set) var excluded: Set = ["main.swift", "LinuxMain.swift"]
|
||||
@ConfigurationElement(key: "prefix_pattern")
|
||||
private(set) var prefixPattern = ""
|
||||
@ConfigurationElement(key: "suffix_pattern")
|
||||
private(set) var suffixPattern = "\\+.*"
|
||||
@ConfigurationElement(key: "nested_type_separator")
|
||||
private(set) var nestedTypeSeparator = "."
|
||||
|
||||
mutating func apply(configuration: Any) throws {
|
||||
guard let configurationDict = configuration as? [String: Any] else {
|
||||
throw Issue.unknownConfiguration(ruleID: Parent.identifier)
|
||||
}
|
||||
|
||||
if let severity = configurationDict[$severityConfiguration] {
|
||||
try severityConfiguration.apply(configuration: severity)
|
||||
}
|
||||
if let excluded = [String].array(of: configurationDict[$excluded]) {
|
||||
self.excluded = Set(excluded)
|
||||
}
|
||||
if let prefixPattern = configurationDict[$prefixPattern] as? String {
|
||||
self.prefixPattern = prefixPattern
|
||||
}
|
||||
if let suffixPattern = configurationDict[$suffixPattern] as? String {
|
||||
self.suffixPattern = suffixPattern
|
||||
}
|
||||
if let nestedTypeSeparator = configurationDict[$nestedTypeSeparator] as? String {
|
||||
self.nestedTypeSeparator = nestedTypeSeparator
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+1
-13
@@ -1,5 +1,6 @@
|
||||
import SwiftLintCore
|
||||
|
||||
@AutoApply
|
||||
struct FileNameNoSpaceConfiguration: SeverityBasedRuleConfiguration, Equatable {
|
||||
typealias Parent = FileNameNoSpaceRule
|
||||
|
||||
@@ -7,17 +8,4 @@ struct FileNameNoSpaceConfiguration: SeverityBasedRuleConfiguration, Equatable {
|
||||
private(set) var severityConfiguration = SeverityConfiguration<Parent>.warning
|
||||
@ConfigurationElement(key: "excluded")
|
||||
private(set) var excluded = Set<String>()
|
||||
|
||||
mutating func apply(configuration: Any) throws {
|
||||
guard let configurationDict = configuration as? [String: Any] else {
|
||||
throw Issue.unknownConfiguration(ruleID: Parent.identifier)
|
||||
}
|
||||
|
||||
if let severity = configurationDict[$severityConfiguration] {
|
||||
try severityConfiguration.apply(configuration: severity)
|
||||
}
|
||||
if let excluded = [String].array(of: configurationDict[$excluded]) {
|
||||
self.excluded = Set(excluded)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+10
-34
@@ -1,18 +1,18 @@
|
||||
import SwiftLintCore
|
||||
|
||||
enum FileType: String, AcceptableByConfigurationElement {
|
||||
case supportingType = "supporting_type"
|
||||
case mainType = "main_type"
|
||||
case `extension` = "extension"
|
||||
case previewProvider = "preview_provider"
|
||||
case libraryContentProvider = "library_content_provider"
|
||||
|
||||
func asOption() -> OptionType { .symbol(rawValue) }
|
||||
}
|
||||
|
||||
@AutoApply
|
||||
struct FileTypesOrderConfiguration: SeverityBasedRuleConfiguration, Equatable {
|
||||
typealias Parent = FileTypesOrderRule
|
||||
|
||||
@MakeAcceptableByConfigurationElement
|
||||
enum FileType: String {
|
||||
case supportingType = "supporting_type"
|
||||
case mainType = "main_type"
|
||||
case `extension` = "extension"
|
||||
case previewProvider = "preview_provider"
|
||||
case libraryContentProvider = "library_content_provider"
|
||||
}
|
||||
|
||||
@ConfigurationElement(key: "severity")
|
||||
private(set) var severityConfiguration = SeverityConfiguration<Parent>(.warning)
|
||||
@ConfigurationElement(key: "order")
|
||||
@@ -23,28 +23,4 @@ struct FileTypesOrderConfiguration: SeverityBasedRuleConfiguration, Equatable {
|
||||
[.previewProvider],
|
||||
[.libraryContentProvider]
|
||||
]
|
||||
|
||||
mutating func apply(configuration: Any) throws {
|
||||
guard let configuration = configuration as? [String: Any] else {
|
||||
throw Issue.unknownConfiguration(ruleID: Parent.identifier)
|
||||
}
|
||||
|
||||
var customOrder = [[FileType]]()
|
||||
if let custom = configuration[$order] as? [Any] {
|
||||
for entry in custom {
|
||||
if let singleEntry = entry as? String {
|
||||
if let fileType = FileType(rawValue: singleEntry) {
|
||||
customOrder.append([fileType])
|
||||
}
|
||||
} else if let arrayEntry = entry as? [String] {
|
||||
let fileTypes = arrayEntry.compactMap { FileType(rawValue: $0) }
|
||||
customOrder.append(fileTypes)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if customOrder.isNotEmpty {
|
||||
self.order = customOrder
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import SwiftLintCore
|
||||
|
||||
@AutoApply
|
||||
struct ForWhereConfiguration: SeverityBasedRuleConfiguration, Equatable {
|
||||
typealias Parent = ForWhereRule
|
||||
|
||||
@@ -7,16 +8,4 @@ struct ForWhereConfiguration: SeverityBasedRuleConfiguration, Equatable {
|
||||
private(set) var severityConfiguration = SeverityConfiguration<Parent>(.warning)
|
||||
@ConfigurationElement(key: "allow_for_as_filter")
|
||||
private(set) var allowForAsFilter = false
|
||||
|
||||
mutating func apply(configuration: Any) throws {
|
||||
guard let configuration = configuration as? [String: Any] else {
|
||||
throw Issue.unknownConfiguration(ruleID: Parent.identifier)
|
||||
}
|
||||
|
||||
allowForAsFilter = configuration[$allowForAsFilter] as? Bool ?? false
|
||||
|
||||
if let severityString = configuration[$severityConfiguration] as? String {
|
||||
try severityConfiguration.apply(configuration: severityString)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+1
-24
@@ -1,5 +1,6 @@
|
||||
import SwiftLintCore
|
||||
|
||||
@AutoApply
|
||||
struct FunctionParameterCountConfiguration: RuleConfiguration, Equatable {
|
||||
typealias Parent = FunctionParameterCountRule
|
||||
|
||||
@@ -7,28 +8,4 @@ struct FunctionParameterCountConfiguration: RuleConfiguration, Equatable {
|
||||
private(set) var severityConfiguration = SeverityLevelsConfiguration<Parent>(warning: 5, error: 8)
|
||||
@ConfigurationElement(key: "ignores_default_parameters")
|
||||
private(set) var ignoresDefaultParameters = true
|
||||
|
||||
mutating func apply(configuration: Any) throws {
|
||||
if let configurationArray = [Int].array(of: configuration),
|
||||
configurationArray.isNotEmpty {
|
||||
let warning = configurationArray[0]
|
||||
let error = (configurationArray.count > 1) ? configurationArray[1] : nil
|
||||
severityConfiguration = SeverityLevelsConfiguration(warning: warning, error: error)
|
||||
} else if let configDict = configuration as? [String: Any], configDict.isNotEmpty {
|
||||
for (string, value) in configDict {
|
||||
switch (string, value) {
|
||||
case ("error", let intValue as Int):
|
||||
severityConfiguration.error = intValue
|
||||
case ("warning", let intValue as Int):
|
||||
severityConfiguration.warning = intValue
|
||||
case ($ignoresDefaultParameters, let boolValue as Bool):
|
||||
ignoresDefaultParameters = boolValue
|
||||
default:
|
||||
throw Issue.unknownConfiguration(ruleID: Parent.identifier)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
throw Issue.unknownConfiguration(ruleID: Parent.identifier)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+3
-23
@@ -1,17 +1,17 @@
|
||||
import SwiftLintCore
|
||||
|
||||
@AutoApply
|
||||
struct ImplicitReturnConfiguration: SeverityBasedRuleConfiguration, Equatable {
|
||||
typealias Parent = ImplicitReturnRule
|
||||
|
||||
enum ReturnKind: String, CaseIterable, AcceptableByConfigurationElement, Comparable {
|
||||
@MakeAcceptableByConfigurationElement
|
||||
enum ReturnKind: String, CaseIterable, Comparable {
|
||||
case closure
|
||||
case function
|
||||
case getter
|
||||
case `subscript`
|
||||
case initializer
|
||||
|
||||
func asOption() -> OptionType { .symbol(rawValue) }
|
||||
|
||||
static func < (lhs: Self, rhs: Self) -> Bool {
|
||||
lhs.rawValue < rhs.rawValue
|
||||
}
|
||||
@@ -28,26 +28,6 @@ struct ImplicitReturnConfiguration: SeverityBasedRuleConfiguration, Equatable {
|
||||
self.includedKinds = includedKinds
|
||||
}
|
||||
|
||||
mutating func apply(configuration: Any) throws {
|
||||
guard let configuration = configuration as? [String: Any] else {
|
||||
throw Issue.unknownConfiguration(ruleID: Parent.identifier)
|
||||
}
|
||||
|
||||
if let includedKinds = configuration[$includedKinds] as? [String] {
|
||||
self.includedKinds = try Set(includedKinds.map {
|
||||
guard let kind = ReturnKind(rawValue: $0) else {
|
||||
throw Issue.unknownConfiguration(ruleID: Parent.identifier)
|
||||
}
|
||||
|
||||
return kind
|
||||
})
|
||||
}
|
||||
|
||||
if let severityString = configuration[$severityConfiguration] as? String {
|
||||
try severityConfiguration.apply(configuration: severityString)
|
||||
}
|
||||
}
|
||||
|
||||
func isKindIncluded(_ kind: ReturnKind) -> Bool {
|
||||
return self.includedKinds.contains(kind)
|
||||
}
|
||||
|
||||
+3
-27
@@ -1,42 +1,18 @@
|
||||
import SwiftLintCore
|
||||
|
||||
@AutoApply
|
||||
struct ImplicitlyUnwrappedOptionalConfiguration: SeverityBasedRuleConfiguration, Equatable {
|
||||
typealias Parent = ImplicitlyUnwrappedOptionalRule
|
||||
|
||||
// swiftlint:disable:next type_name
|
||||
enum ImplicitlyUnwrappedOptionalModeConfiguration: String, AcceptableByConfigurationElement {
|
||||
@MakeAcceptableByConfigurationElement
|
||||
enum ImplicitlyUnwrappedOptionalModeConfiguration: String { // swiftlint:disable:this type_name
|
||||
case all = "all"
|
||||
case allExceptIBOutlets = "all_except_iboutlets"
|
||||
case weakExceptIBOutlets = "weak_except_iboutlets"
|
||||
|
||||
init(value: Any) throws {
|
||||
if let string = (value as? String)?.lowercased(),
|
||||
let value = Self(rawValue: string) {
|
||||
self = value
|
||||
} else {
|
||||
throw Issue.unknownConfiguration(ruleID: Parent.identifier)
|
||||
}
|
||||
}
|
||||
|
||||
func asOption() -> OptionType { .symbol(rawValue) }
|
||||
}
|
||||
|
||||
@ConfigurationElement(key: "severity")
|
||||
private(set) var severityConfiguration = SeverityConfiguration<Parent>.warning
|
||||
@ConfigurationElement(key: "mode")
|
||||
private(set) var mode = ImplicitlyUnwrappedOptionalModeConfiguration.allExceptIBOutlets
|
||||
|
||||
mutating func apply(configuration: Any) throws {
|
||||
guard let configuration = configuration as? [String: Any] else {
|
||||
throw Issue.unknownConfiguration(ruleID: Parent.identifier)
|
||||
}
|
||||
|
||||
if let modeString = configuration[$mode] {
|
||||
try mode = ImplicitlyUnwrappedOptionalModeConfiguration(value: modeString)
|
||||
}
|
||||
|
||||
if let severityString = configuration[$severityConfiguration] as? String {
|
||||
try severityConfiguration.apply(configuration: severityString)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+20
-38
@@ -1,8 +1,20 @@
|
||||
import SwiftLintCore
|
||||
|
||||
@AutoApply
|
||||
struct InclusiveLanguageConfiguration: SeverityBasedRuleConfiguration, Equatable {
|
||||
typealias Parent = InclusiveLanguageRule
|
||||
|
||||
private static let defaultTerms: Set<String> = [
|
||||
"whitelist",
|
||||
"blacklist",
|
||||
"master",
|
||||
"slave"
|
||||
]
|
||||
|
||||
private static let defaultAllowedTerms: Set<String> = [
|
||||
"mastercard"
|
||||
]
|
||||
|
||||
@ConfigurationElement(key: "severity")
|
||||
private(set) var severityConfiguration = SeverityConfiguration<Parent>(.warning)
|
||||
@ConfigurationElement(key: "additional_terms")
|
||||
@@ -11,46 +23,16 @@ struct InclusiveLanguageConfiguration: SeverityBasedRuleConfiguration, Equatable
|
||||
private(set) var overrideTerms: Set<String>?
|
||||
@ConfigurationElement(key: "override_allowed_terms")
|
||||
private(set) var overrideAllowedTerms: Set<String>?
|
||||
private(set) var allTerms: [String]
|
||||
private(set) var allAllowedTerms: Set<String>
|
||||
|
||||
private let defaultTerms: Set<String> = [
|
||||
"whitelist",
|
||||
"blacklist",
|
||||
"master",
|
||||
"slave"
|
||||
]
|
||||
|
||||
private let defaultAllowedTerms: Set<String> = [
|
||||
"mastercard"
|
||||
]
|
||||
|
||||
init() {
|
||||
self.allTerms = defaultTerms.sorted()
|
||||
self.allAllowedTerms = defaultAllowedTerms
|
||||
var allTerms: [String] {
|
||||
let allTerms = overrideTerms ?? Self.defaultTerms
|
||||
return allTerms.union(additionalTerms ?? [])
|
||||
.map { $0.lowercased() }
|
||||
.unique
|
||||
.sorted()
|
||||
}
|
||||
|
||||
mutating func apply(configuration: Any) throws {
|
||||
guard let configuration = configuration as? [String: Any] else {
|
||||
throw Issue.unknownConfiguration(ruleID: Parent.identifier)
|
||||
}
|
||||
|
||||
if let severityString = configuration[$severityConfiguration] {
|
||||
try severityConfiguration.apply(configuration: severityString)
|
||||
}
|
||||
|
||||
additionalTerms = lowercasedSet(for: $additionalTerms, from: configuration)
|
||||
overrideTerms = lowercasedSet(for: $overrideTerms, from: configuration)
|
||||
overrideAllowedTerms = lowercasedSet(for: $overrideAllowedTerms, from: configuration)
|
||||
|
||||
var allTerms = overrideTerms ?? defaultTerms
|
||||
allTerms.formUnion(additionalTerms ?? [])
|
||||
self.allTerms = allTerms.sorted()
|
||||
allAllowedTerms = overrideAllowedTerms ?? defaultAllowedTerms
|
||||
}
|
||||
|
||||
private func lowercasedSet(for key: String, from config: [String: Any]) -> Set<String>? {
|
||||
guard let list = config[key] as? [String] else { return nil }
|
||||
return Set(list.map { $0.lowercased() })
|
||||
var allAllowedTerms: Set<String> {
|
||||
Set((overrideAllowedTerms ?? Self.defaultAllowedTerms).map { $0.lowercased() })
|
||||
}
|
||||
}
|
||||
|
||||
+8
-27
@@ -1,11 +1,18 @@
|
||||
import SwiftLintCore
|
||||
|
||||
// swiftlint:disable:next blanket_disable_command
|
||||
// swiftlint:disable let_var_whitespace
|
||||
|
||||
@AutoApply
|
||||
struct IndentationWidthConfiguration: SeverityBasedRuleConfiguration, Equatable {
|
||||
typealias Parent = IndentationWidthRule
|
||||
|
||||
@ConfigurationElement(key: "severity")
|
||||
private(set) var severityConfiguration = SeverityConfiguration<Parent>.warning
|
||||
@ConfigurationElement(key: "indentation_width")
|
||||
@ConfigurationElement(
|
||||
key: "indentation_width",
|
||||
postprocessor: { if $0 < 1 { throw Issue.invalidConfiguration(ruleID: Parent.identifier) } }
|
||||
)
|
||||
private(set) var indentationWidth = 4
|
||||
@ConfigurationElement(key: "include_comments")
|
||||
private(set) var includeComments = true
|
||||
@@ -13,30 +20,4 @@ struct IndentationWidthConfiguration: SeverityBasedRuleConfiguration, Equatable
|
||||
private(set) var includeCompilerDirectives = true
|
||||
@ConfigurationElement(key: "include_multiline_strings")
|
||||
private(set) var includeMultilineStrings = true
|
||||
|
||||
mutating func apply(configuration: Any) throws {
|
||||
guard let configurationDict = configuration as? [String: Any] else {
|
||||
throw Issue.unknownConfiguration(ruleID: Parent.identifier)
|
||||
}
|
||||
|
||||
if let config = configurationDict[$severityConfiguration] {
|
||||
try severityConfiguration.apply(configuration: config)
|
||||
}
|
||||
|
||||
if let indentationWidth = configurationDict[$indentationWidth] as? Int, indentationWidth >= 1 {
|
||||
self.indentationWidth = indentationWidth
|
||||
}
|
||||
|
||||
if let includeComments = configurationDict[$includeComments] as? Bool {
|
||||
self.includeComments = includeComments
|
||||
}
|
||||
|
||||
if let includeCompilerDirectives = configurationDict[$includeCompilerDirectives] as? Bool {
|
||||
self.includeCompilerDirectives = includeCompilerDirectives
|
||||
}
|
||||
|
||||
if let includeMultilineStrings = configurationDict[$includeMultilineStrings] as? Bool {
|
||||
self.includeMultilineStrings = includeMultilineStrings
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+1
-56
@@ -1,5 +1,6 @@
|
||||
import SwiftLintCore
|
||||
|
||||
@AutoApply
|
||||
struct LineLengthConfiguration: RuleConfiguration, Equatable {
|
||||
typealias Parent = LineLengthRule
|
||||
|
||||
@@ -17,60 +18,4 @@ struct LineLengthConfiguration: RuleConfiguration, Equatable {
|
||||
var params: [RuleParameter<Int>] {
|
||||
return length.params
|
||||
}
|
||||
|
||||
mutating func apply(configuration: Any) throws {
|
||||
if applyArray(configuration: configuration) {
|
||||
return
|
||||
}
|
||||
try applyDictionary(configuration: configuration)
|
||||
}
|
||||
|
||||
/// Applies configuration as an array of integers. Returns true if did apply.
|
||||
///
|
||||
/// - parameter configuration: The untyped configuration value to apply.
|
||||
///
|
||||
/// - returns: True if the configuration was successfuly applied.
|
||||
private mutating func applyArray(configuration: Any) -> Bool {
|
||||
guard let configurationArray = [Int].array(of: configuration),
|
||||
configurationArray.isNotEmpty else {
|
||||
return false
|
||||
}
|
||||
|
||||
let warning = configurationArray[0]
|
||||
let error = (configurationArray.count > 1) ? configurationArray[1] : nil
|
||||
length = SeverityLevelsConfiguration(warning: warning, error: error)
|
||||
return true
|
||||
}
|
||||
|
||||
/// Applies configuration as a dictionary. Throws if configuration couldn't be applied.
|
||||
///
|
||||
/// - parameter configuration: The untyped configuration value to apply.
|
||||
///
|
||||
/// - throws: Throws if the configuration value isn't properly formatted.
|
||||
private mutating func applyDictionary(configuration: Any) throws {
|
||||
let error = Issue.unknownConfiguration(ruleID: Parent.identifier)
|
||||
guard let configDict = configuration as? [String: Any],
|
||||
configDict.isNotEmpty else {
|
||||
throw error
|
||||
}
|
||||
|
||||
for (string, value) in configDict {
|
||||
switch (string, value) {
|
||||
case ("error", let intValue as Int):
|
||||
length.error = intValue
|
||||
case ("warning", let intValue as Int):
|
||||
length.warning = intValue
|
||||
case ($ignoresFunctionDeclarations, let boolValue as Bool):
|
||||
ignoresFunctionDeclarations = boolValue
|
||||
case ($ignoresComments, let boolValue as Bool):
|
||||
ignoresComments = boolValue
|
||||
case ($ignoresURLs, let boolValue as Bool):
|
||||
ignoresURLs = boolValue
|
||||
case ($ignoresInterpolatedStrings, let boolValue as Bool):
|
||||
ignoresInterpolatedStrings = boolValue
|
||||
default:
|
||||
throw error
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+6
-6
@@ -23,9 +23,9 @@ struct MissingDocsConfiguration: RuleConfiguration, Equatable {
|
||||
severity.rawValue => .list(values.map(\.value.description).sorted().map { .symbol($0) })
|
||||
}
|
||||
}
|
||||
$excludesExtensions => .flag(excludesExtensions)
|
||||
$excludesInheritedTypes => .flag(excludesInheritedTypes)
|
||||
$excludesTrivialInit => .flag(excludesTrivialInit)
|
||||
$excludesExtensions.key => .flag(excludesExtensions)
|
||||
$excludesInheritedTypes.key => .flag(excludesInheritedTypes)
|
||||
$excludesTrivialInit.key => .flag(excludesTrivialInit)
|
||||
}
|
||||
|
||||
mutating func apply(configuration: Any) throws {
|
||||
@@ -33,15 +33,15 @@ struct MissingDocsConfiguration: RuleConfiguration, Equatable {
|
||||
throw Issue.unknownConfiguration(ruleID: Parent.identifier)
|
||||
}
|
||||
|
||||
if let shouldExcludeExtensions = dict[$excludesExtensions] as? Bool {
|
||||
if let shouldExcludeExtensions = dict[$excludesExtensions.key] as? Bool {
|
||||
excludesExtensions = shouldExcludeExtensions
|
||||
}
|
||||
|
||||
if let shouldExcludeInheritedTypes = dict[$excludesInheritedTypes] as? Bool {
|
||||
if let shouldExcludeInheritedTypes = dict[$excludesInheritedTypes.key] as? Bool {
|
||||
excludesInheritedTypes = shouldExcludeInheritedTypes
|
||||
}
|
||||
|
||||
if let excludesTrivialInit = dict[$excludesTrivialInit] as? Bool {
|
||||
if let excludesTrivialInit = dict[$excludesTrivialInit.key] as? Bool {
|
||||
self.excludesTrivialInit = excludesTrivialInit
|
||||
}
|
||||
|
||||
|
||||
+9
-21
@@ -1,6 +1,7 @@
|
||||
import SourceKittenFramework
|
||||
import SwiftLintCore
|
||||
|
||||
@AutoApply
|
||||
struct ModifierOrderConfiguration: SeverityBasedRuleConfiguration, Equatable {
|
||||
typealias Parent = ModifierOrderRule
|
||||
|
||||
@@ -20,29 +21,16 @@ struct ModifierOrderConfiguration: SeverityBasedRuleConfiguration, Equatable {
|
||||
.typeMethods,
|
||||
.owned
|
||||
]
|
||||
|
||||
mutating func apply(configuration: Any) throws {
|
||||
guard let configuration = configuration as? [String: Any] else {
|
||||
throw Issue.unknownConfiguration(ruleID: Parent.identifier)
|
||||
}
|
||||
|
||||
if let preferredModifierOrder = configuration[$preferredModifierOrder] as? [String] {
|
||||
self.preferredModifierOrder = try preferredModifierOrder.map {
|
||||
guard let modifierGroup = SwiftDeclarationAttributeKind.ModifierGroup(rawValue: $0),
|
||||
modifierGroup != .atPrefixed else {
|
||||
throw Issue.unknownConfiguration(ruleID: Parent.identifier)
|
||||
}
|
||||
|
||||
return modifierGroup
|
||||
}
|
||||
}
|
||||
|
||||
if let severityString = configuration[$severityConfiguration] as? String {
|
||||
try severityConfiguration.apply(configuration: severityString)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension SwiftDeclarationAttributeKind.ModifierGroup: AcceptableByConfigurationElement {
|
||||
public init(fromAny value: Any, context ruleID: String) throws {
|
||||
if let value = value as? String, let newSelf = Self(rawValue: value), newSelf != .atPrefixed {
|
||||
self = newSelf
|
||||
} else {
|
||||
throw Issue.unknownConfiguration(ruleID: ruleID)
|
||||
}
|
||||
}
|
||||
|
||||
public func asOption() -> OptionType { .symbol(rawValue) }
|
||||
}
|
||||
|
||||
+3
-34
@@ -1,24 +1,14 @@
|
||||
import SwiftLintCore
|
||||
|
||||
@AutoApply
|
||||
struct MultilineArgumentsConfiguration: SeverityBasedRuleConfiguration, Equatable {
|
||||
typealias Parent = MultilineArgumentsRule
|
||||
|
||||
enum FirstArgumentLocation: String, AcceptableByConfigurationElement {
|
||||
@MakeAcceptableByConfigurationElement
|
||||
enum FirstArgumentLocation: String {
|
||||
case anyLine = "any_line"
|
||||
case sameLine = "same_line"
|
||||
case nextLine = "next_line"
|
||||
|
||||
init(value: Any) throws {
|
||||
guard
|
||||
let string = (value as? String)?.lowercased(),
|
||||
let value = Self(rawValue: string) else {
|
||||
throw Issue.unknownConfiguration(ruleID: Parent.identifier)
|
||||
}
|
||||
|
||||
self = value
|
||||
}
|
||||
|
||||
func asOption() -> OptionType { .symbol(rawValue) }
|
||||
}
|
||||
|
||||
@ConfigurationElement(key: "severity")
|
||||
@@ -27,25 +17,4 @@ struct MultilineArgumentsConfiguration: SeverityBasedRuleConfiguration, Equatabl
|
||||
private(set) var firstArgumentLocation = FirstArgumentLocation.anyLine
|
||||
@ConfigurationElement(key: "only_enforce_after_first_closure_on_first_line")
|
||||
private(set) var onlyEnforceAfterFirstClosureOnFirstLine = false
|
||||
|
||||
mutating func apply(configuration: Any) throws {
|
||||
let error = Issue.unknownConfiguration(ruleID: Parent.identifier)
|
||||
|
||||
guard let configuration = configuration as? [String: Any] else {
|
||||
throw error
|
||||
}
|
||||
|
||||
for (string, value) in configuration {
|
||||
switch (string, value) {
|
||||
case ($firstArgumentLocation, _):
|
||||
try firstArgumentLocation = FirstArgumentLocation(value: value)
|
||||
case ($severityConfiguration, let stringValue as String):
|
||||
try severityConfiguration.apply(configuration: stringValue)
|
||||
case ($onlyEnforceAfterFirstClosureOnFirstLine, let boolValue as Bool):
|
||||
onlyEnforceAfterFirstClosureOnFirstLine = boolValue
|
||||
default:
|
||||
throw error
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+1
-12
@@ -1,5 +1,6 @@
|
||||
import SwiftLintCore
|
||||
|
||||
@AutoApply
|
||||
struct MultilineParametersConfiguration: SeverityBasedRuleConfiguration, Equatable {
|
||||
typealias Parent = MultilineParametersRule
|
||||
|
||||
@@ -7,16 +8,4 @@ struct MultilineParametersConfiguration: SeverityBasedRuleConfiguration, Equatab
|
||||
private(set) var severityConfiguration = SeverityConfiguration<Parent>(.warning)
|
||||
@ConfigurationElement(key: "allows_single_line")
|
||||
private(set) var allowsSingleLine = true
|
||||
|
||||
mutating func apply(configuration: Any) throws {
|
||||
guard let configuration = configuration as? [String: Any] else {
|
||||
throw Issue.unknownConfiguration(ruleID: Parent.identifier)
|
||||
}
|
||||
|
||||
allowsSingleLine = configuration[$allowsSingleLine] as? Bool ?? true
|
||||
|
||||
if let severityString = configuration[$severityConfiguration] as? String {
|
||||
try severityConfiguration.apply(configuration: severityString)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,7 +11,7 @@ struct NameConfiguration<Parent: Rule>: RuleConfiguration, Equatable {
|
||||
@ConfigurationElement(key: "max_length")
|
||||
private(set) var maxLength = SeverityLevels(warning: 0, error: 0)
|
||||
@ConfigurationElement(key: "excluded")
|
||||
private(set) var excludedRegularExpressions = Set<NSRegularExpression>()
|
||||
private(set) var excludedRegularExpressions = Set<RegularExpression>()
|
||||
@ConfigurationElement(key: "allowed_symbols")
|
||||
private(set) var allowedSymbols = Set<String>()
|
||||
@ConfigurationElement(key: "unallowed_symbols_severity")
|
||||
@@ -42,7 +42,7 @@ struct NameConfiguration<Parent: Rule>: RuleConfiguration, Equatable {
|
||||
minLength = SeverityLevels(warning: minLengthWarning, error: minLengthError)
|
||||
maxLength = SeverityLevels(warning: maxLengthWarning, error: maxLengthError)
|
||||
self.excludedRegularExpressions = Set(excluded.compactMap {
|
||||
try? NSRegularExpression.cached(pattern: "^\($0)$")
|
||||
try? RegularExpression(pattern: "^\($0)$")
|
||||
})
|
||||
self.allowedSymbols = Set(allowedSymbols)
|
||||
self.unallowedSymbolsSeverity = unallowedSymbolsSeverity
|
||||
@@ -54,26 +54,26 @@ struct NameConfiguration<Parent: Rule>: RuleConfiguration, Equatable {
|
||||
throw Issue.unknownConfiguration(ruleID: Parent.identifier)
|
||||
}
|
||||
|
||||
if let minLengthConfiguration = configurationDict[$minLength] {
|
||||
if let minLengthConfiguration = configurationDict[$minLength.key] {
|
||||
try minLength.apply(configuration: minLengthConfiguration)
|
||||
}
|
||||
if let maxLengthConfiguration = configurationDict[$maxLength] {
|
||||
if let maxLengthConfiguration = configurationDict[$maxLength.key] {
|
||||
try maxLength.apply(configuration: maxLengthConfiguration)
|
||||
}
|
||||
if let excluded = [String].array(of: configurationDict[$excludedRegularExpressions]) {
|
||||
if let excluded = [String].array(of: configurationDict[$excludedRegularExpressions.key]) {
|
||||
self.excludedRegularExpressions = Set(excluded.compactMap {
|
||||
try? NSRegularExpression.cached(pattern: "^\($0)$")
|
||||
try? RegularExpression(pattern: "^\($0)$")
|
||||
})
|
||||
}
|
||||
if let allowedSymbols = [String].array(of: configurationDict[$allowedSymbols]) {
|
||||
if let allowedSymbols = [String].array(of: configurationDict[$allowedSymbols.key]) {
|
||||
self.allowedSymbols = Set(allowedSymbols)
|
||||
}
|
||||
if let unallowedSymbolsSeverity = configurationDict[$unallowedSymbolsSeverity] {
|
||||
if let unallowedSymbolsSeverity = configurationDict[$unallowedSymbolsSeverity.key] {
|
||||
try self.unallowedSymbolsSeverity.apply(configuration: unallowedSymbolsSeverity)
|
||||
}
|
||||
if let validatesStartWithLowercase = configurationDict[$validatesStartWithLowercase] as? String {
|
||||
if let validatesStartWithLowercase = configurationDict[$validatesStartWithLowercase.key] as? String {
|
||||
try self.validatesStartWithLowercase.apply(configuration: validatesStartWithLowercase)
|
||||
} else if let validatesStartWithLowercase = configurationDict[$validatesStartWithLowercase] as? Bool {
|
||||
} else if let validatesStartWithLowercase = configurationDict[$validatesStartWithLowercase.key] as? Bool {
|
||||
// TODO: [05/10/2025] Remove deprecation warning after ~2 years.
|
||||
self.validatesStartWithLowercase = validatesStartWithLowercase ? .error : .off
|
||||
Issue.genericWarning(
|
||||
@@ -106,7 +106,7 @@ extension NameConfiguration {
|
||||
extension NameConfiguration {
|
||||
func shouldExclude(name: String) -> Bool {
|
||||
excludedRegularExpressions.contains {
|
||||
!$0.matches(in: name, options: [], range: NSRange(name.startIndex..., in: name)).isEmpty
|
||||
!$0.regex.matches(in: name, options: [], range: NSRange(name.startIndex..., in: name)).isEmpty
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import SwiftLintCore
|
||||
|
||||
@AutoApply
|
||||
struct NestingConfiguration: RuleConfiguration, Equatable {
|
||||
typealias Parent = NestingRule
|
||||
typealias Severity = SeverityLevelsConfiguration<Parent>
|
||||
@@ -13,23 +14,6 @@ struct NestingConfiguration: RuleConfiguration, Equatable {
|
||||
@ConfigurationElement(key: "always_allow_one_type_in_functions")
|
||||
private(set) var alwaysAllowOneTypeInFunctions = false
|
||||
|
||||
mutating func apply(configuration: Any) throws {
|
||||
guard let configurationDict = configuration as? [String: Any] else {
|
||||
throw Issue.unknownConfiguration(ruleID: Parent.identifier)
|
||||
}
|
||||
|
||||
if let typeLevelConfiguration = configurationDict[$typeLevel] {
|
||||
try typeLevel.apply(configuration: typeLevelConfiguration)
|
||||
}
|
||||
if let functionLevelConfiguration = configurationDict[$functionLevel] {
|
||||
try functionLevel.apply(configuration: functionLevelConfiguration)
|
||||
}
|
||||
checkNestingInClosuresAndStatements =
|
||||
configurationDict[$checkNestingInClosuresAndStatements] as? Bool ?? true
|
||||
alwaysAllowOneTypeInFunctions =
|
||||
configurationDict[$alwaysAllowOneTypeInFunctions] as? Bool ?? false
|
||||
}
|
||||
|
||||
func severity(with config: Severity, for level: Int) -> ViolationSeverity? {
|
||||
if let error = config.error, level > error {
|
||||
return .error
|
||||
|
||||
+3
-20
@@ -1,34 +1,17 @@
|
||||
import SwiftLintCore
|
||||
|
||||
// swiftlint:disable:next type_name
|
||||
@AutoApply // swiftlint:disable:next type_name
|
||||
struct NonOverridableClassDeclarationConfiguration: SeverityBasedRuleConfiguration, Equatable {
|
||||
typealias Parent = NonOverridableClassDeclarationRule
|
||||
|
||||
enum FinalClassModifier: String, AcceptableByConfigurationElement {
|
||||
@MakeAcceptableByConfigurationElement
|
||||
enum FinalClassModifier: String {
|
||||
case finalClass = "final class"
|
||||
case `static` = "static"
|
||||
|
||||
func asOption() -> OptionType { .symbol(rawValue) }
|
||||
}
|
||||
|
||||
@ConfigurationElement(key: "severity")
|
||||
private(set) var severityConfiguration = SeverityConfiguration<Parent>.warning
|
||||
@ConfigurationElement(key: "final_class_modifier")
|
||||
private(set) var finalClassModifier = FinalClassModifier.finalClass
|
||||
|
||||
mutating func apply(configuration: Any) throws {
|
||||
guard let configuration = configuration as? [String: Any] else {
|
||||
throw Issue.unknownConfiguration(ruleID: Parent.identifier)
|
||||
}
|
||||
if let severityString = configuration[$severityConfiguration] as? String {
|
||||
try severityConfiguration.apply(configuration: severityString)
|
||||
}
|
||||
if let value = configuration[$finalClassModifier] as? String {
|
||||
if let modifier = FinalClassModifier(rawValue: value) {
|
||||
finalClassModifier = modifier
|
||||
} else {
|
||||
throw Issue.unknownConfiguration(ruleID: Parent.identifier)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+25
-28
@@ -1,8 +1,32 @@
|
||||
import SwiftLintCore
|
||||
|
||||
@AutoApply
|
||||
struct NumberSeparatorConfiguration: SeverityBasedRuleConfiguration, Equatable {
|
||||
typealias Parent = NumberSeparatorRule
|
||||
|
||||
struct ExcludeRange: AcceptableByConfigurationElement, Equatable {
|
||||
private let min: Double
|
||||
private let max: Double
|
||||
|
||||
func asOption() -> OptionType {
|
||||
.symbol("\(min) ..< \(max)")
|
||||
}
|
||||
|
||||
init(fromAny value: Any, context ruleID: String) throws {
|
||||
guard let values = value as? [String: Any],
|
||||
let min = values["min"] as? Double,
|
||||
let max = values["max"] as? Double else {
|
||||
throw Issue.invalidConfiguration(ruleID: ruleID)
|
||||
}
|
||||
self.min = min
|
||||
self.max = max
|
||||
}
|
||||
|
||||
func contains(_ value: Double) -> Bool {
|
||||
min <= value && value < max
|
||||
}
|
||||
}
|
||||
|
||||
@ConfigurationElement(key: "severity")
|
||||
private(set) var severityConfiguration = SeverityConfiguration<Parent>(.warning)
|
||||
@ConfigurationElement(key: "minimum_length")
|
||||
@@ -10,32 +34,5 @@ struct NumberSeparatorConfiguration: SeverityBasedRuleConfiguration, Equatable {
|
||||
@ConfigurationElement(key: "minimum_fraction_length")
|
||||
private(set) var minimumFractionLength: Int?
|
||||
@ConfigurationElement(key: "exclude_ranges")
|
||||
private(set) var excludeRanges = [Range<Double>]()
|
||||
|
||||
mutating func apply(configuration: Any) throws {
|
||||
guard let configuration = configuration as? [String: Any] else {
|
||||
throw Issue.unknownConfiguration(ruleID: Parent.identifier)
|
||||
}
|
||||
|
||||
if let minimumLength = configuration[$minimumLength] as? Int {
|
||||
self.minimumLength = minimumLength
|
||||
}
|
||||
|
||||
if let minimumFractionLength = configuration[$minimumFractionLength] as? Int {
|
||||
self.minimumFractionLength = minimumFractionLength
|
||||
}
|
||||
|
||||
if let excludeRanges = configuration[$excludeRanges] as? [[String: Any]] {
|
||||
self.excludeRanges = excludeRanges.compactMap { dict in
|
||||
guard let min = dict["min"] as? Double, let max = dict["max"] as? Double else {
|
||||
return nil
|
||||
}
|
||||
return min ..< max
|
||||
}
|
||||
}
|
||||
|
||||
if let severityString = configuration[$severityConfiguration] as? String {
|
||||
try severityConfiguration.apply(configuration: severityString)
|
||||
}
|
||||
}
|
||||
private(set) var excludeRanges = [ExcludeRange]()
|
||||
}
|
||||
|
||||
+1
-13
@@ -2,6 +2,7 @@ import SwiftLintCore
|
||||
|
||||
typealias DiscouragedObjectLiteralConfiguration = ObjectLiteralConfiguration<DiscouragedObjectLiteralRule>
|
||||
|
||||
@AutoApply
|
||||
struct ObjectLiteralConfiguration<Parent: Rule>: SeverityBasedRuleConfiguration, Equatable {
|
||||
@ConfigurationElement(key: "severity")
|
||||
private(set) var severityConfiguration = SeverityConfiguration<Parent>(.warning)
|
||||
@@ -9,17 +10,4 @@ struct ObjectLiteralConfiguration<Parent: Rule>: SeverityBasedRuleConfiguration,
|
||||
private(set) var imageLiteral = true
|
||||
@ConfigurationElement(key: "color_literal")
|
||||
private(set) var colorLiteral = true
|
||||
|
||||
mutating func apply(configuration: Any) throws {
|
||||
guard let configuration = configuration as? [String: Any] else {
|
||||
throw Issue.unknownConfiguration(ruleID: Parent.identifier)
|
||||
}
|
||||
|
||||
imageLiteral = configuration[$imageLiteral] as? Bool ?? true
|
||||
colorLiteral = configuration[$colorLiteral] as? Bool ?? true
|
||||
|
||||
if let severityString = configuration[$severityConfiguration] as? String {
|
||||
try severityConfiguration.apply(configuration: severityString)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+1
-12
@@ -1,5 +1,6 @@
|
||||
import SwiftLintCore
|
||||
|
||||
@AutoApply
|
||||
struct OpeningBraceConfiguration: SeverityBasedRuleConfiguration, Equatable {
|
||||
typealias Parent = OpeningBraceRule
|
||||
|
||||
@@ -7,16 +8,4 @@ struct OpeningBraceConfiguration: SeverityBasedRuleConfiguration, Equatable {
|
||||
private(set) var severityConfiguration = SeverityConfiguration<Parent>(.warning)
|
||||
@ConfigurationElement(key: "allow_multiline_func")
|
||||
private(set) var allowMultilineFunc = false
|
||||
|
||||
mutating func apply(configuration: Any) throws {
|
||||
guard let configuration = configuration as? [String: Any] else {
|
||||
throw Issue.unknownConfiguration(ruleID: Parent.identifier)
|
||||
}
|
||||
|
||||
if let severityString = configuration[$severityConfiguration] as? String {
|
||||
try severityConfiguration.apply(configuration: severityString)
|
||||
}
|
||||
|
||||
allowMultilineFunc = configuration[$allowMultilineFunc] as? Bool ?? false
|
||||
}
|
||||
}
|
||||
|
||||
+1
-15
@@ -1,5 +1,6 @@
|
||||
import SwiftLintCore
|
||||
|
||||
@AutoApply
|
||||
struct OperatorUsageWhitespaceConfiguration: SeverityBasedRuleConfiguration, Equatable {
|
||||
typealias Parent = OperatorUsageWhitespaceRule
|
||||
|
||||
@@ -11,19 +12,4 @@ struct OperatorUsageWhitespaceConfiguration: SeverityBasedRuleConfiguration, Equ
|
||||
private(set) var skipAlignedConstants = true
|
||||
@ConfigurationElement(key: "allowed_no_space_operators")
|
||||
private(set) var allowedNoSpaceOperators = ["...", "..<"]
|
||||
|
||||
mutating func apply(configuration: Any) throws {
|
||||
guard let configuration = configuration as? [String: Any] else {
|
||||
throw Issue.unknownConfiguration(ruleID: Parent.identifier)
|
||||
}
|
||||
|
||||
linesLookAround = configuration[$linesLookAround] as? Int ?? 2
|
||||
skipAlignedConstants = configuration[$skipAlignedConstants] as? Bool ?? true
|
||||
allowedNoSpaceOperators =
|
||||
configuration[$allowedNoSpaceOperators] as? [String] ?? ["...", "..<"]
|
||||
|
||||
if let severityString = configuration[$severityConfiguration] as? String {
|
||||
try severityConfiguration.apply(configuration: severityString)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+5
-30
@@ -1,9 +1,10 @@
|
||||
import SwiftLintCore
|
||||
|
||||
@AutoApply
|
||||
struct OverriddenSuperCallConfiguration: SeverityBasedRuleConfiguration, Equatable {
|
||||
typealias Parent = OverriddenSuperCallRule
|
||||
|
||||
private let defaultIncluded = [
|
||||
private static let defaultIncluded = [
|
||||
// NSObject
|
||||
"awakeFromNib()",
|
||||
"prepareForInterfaceBuilder()",
|
||||
@@ -42,38 +43,12 @@ struct OverriddenSuperCallConfiguration: SeverityBasedRuleConfiguration, Equatab
|
||||
@ConfigurationElement(key: "included")
|
||||
private(set) var included = ["*"]
|
||||
|
||||
private(set) var resolvedMethodNames: [String]
|
||||
|
||||
init() {
|
||||
resolvedMethodNames = defaultIncluded
|
||||
}
|
||||
|
||||
mutating func apply(configuration: Any) throws {
|
||||
guard let configuration = configuration as? [String: Any] else {
|
||||
throw Issue.unknownConfiguration(ruleID: Parent.identifier)
|
||||
}
|
||||
|
||||
if let severityString = configuration[$severityConfiguration] as? String {
|
||||
try severityConfiguration.apply(configuration: severityString)
|
||||
}
|
||||
|
||||
if let excluded = [String].array(of: configuration[$excluded]) {
|
||||
self.excluded = excluded
|
||||
}
|
||||
|
||||
if let included = [String].array(of: configuration[$included]) {
|
||||
self.included = included
|
||||
}
|
||||
|
||||
resolvedMethodNames = calculateResolvedMethodNames()
|
||||
}
|
||||
|
||||
private func calculateResolvedMethodNames() -> [String] {
|
||||
var resolvedMethodNames: [String] {
|
||||
var names: [String] = []
|
||||
if included.contains("*") && !excluded.contains("*") {
|
||||
names += defaultIncluded
|
||||
names += Self.defaultIncluded
|
||||
}
|
||||
names += included.filter({ $0 != "*" })
|
||||
names += included.filter { $0 != "*" }
|
||||
names = names.filter { !excluded.contains($0) }
|
||||
return names
|
||||
}
|
||||
|
||||
+1
-12
@@ -1,5 +1,6 @@
|
||||
import SwiftLintCore
|
||||
|
||||
@AutoApply
|
||||
struct PrefixedTopLevelConstantConfiguration: SeverityBasedRuleConfiguration, Equatable {
|
||||
typealias Parent = PrefixedTopLevelConstantRule
|
||||
|
||||
@@ -7,16 +8,4 @@ struct PrefixedTopLevelConstantConfiguration: SeverityBasedRuleConfiguration, Eq
|
||||
private(set) var severityConfiguration = SeverityConfiguration<Parent>(.warning)
|
||||
@ConfigurationElement(key: "only_private")
|
||||
private(set) var onlyPrivateMembers = false
|
||||
|
||||
mutating func apply(configuration: Any) throws {
|
||||
guard let configuration = configuration as? [String: Any] else {
|
||||
throw Issue.unknownConfiguration(ruleID: Parent.identifier)
|
||||
}
|
||||
|
||||
onlyPrivateMembers = (configuration[$onlyPrivateMembers] as? Bool == true)
|
||||
|
||||
if let severityString = configuration[$severityConfiguration] as? String {
|
||||
try severityConfiguration.apply(configuration: severityString)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+1
-12
@@ -1,5 +1,6 @@
|
||||
import SwiftLintCore
|
||||
|
||||
@AutoApply
|
||||
struct PrivateOutletConfiguration: SeverityBasedRuleConfiguration, Equatable {
|
||||
typealias Parent = PrivateOutletRule
|
||||
|
||||
@@ -7,16 +8,4 @@ struct PrivateOutletConfiguration: SeverityBasedRuleConfiguration, Equatable {
|
||||
private(set) var severityConfiguration = SeverityConfiguration<Parent>(.warning)
|
||||
@ConfigurationElement(key: "allow_private_set")
|
||||
private(set) var allowPrivateSet = false
|
||||
|
||||
mutating func apply(configuration: Any) throws {
|
||||
guard let configuration = configuration as? [String: Any] else {
|
||||
throw Issue.unknownConfiguration(ruleID: Parent.identifier)
|
||||
}
|
||||
|
||||
allowPrivateSet = (configuration[$allowPrivateSet] as? Bool == true)
|
||||
|
||||
if let severityString = configuration[$severityConfiguration] as? String {
|
||||
try severityConfiguration.apply(configuration: severityString)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+1
-12
@@ -1,5 +1,6 @@
|
||||
import SwiftLintCore
|
||||
|
||||
@AutoApply
|
||||
struct PrivateOverFilePrivateConfiguration: SeverityBasedRuleConfiguration, Equatable {
|
||||
typealias Parent = PrivateOverFilePrivateRule
|
||||
|
||||
@@ -7,16 +8,4 @@ struct PrivateOverFilePrivateConfiguration: SeverityBasedRuleConfiguration, Equa
|
||||
var severityConfiguration = SeverityConfiguration<Parent>(.warning)
|
||||
@ConfigurationElement(key: "validate_extensions")
|
||||
var validateExtensions = false
|
||||
|
||||
mutating func apply(configuration: Any) throws {
|
||||
guard let configuration = configuration as? [String: Any] else {
|
||||
throw Issue.unknownConfiguration(ruleID: Parent.identifier)
|
||||
}
|
||||
|
||||
if let severityString = configuration[$severityConfiguration] as? String {
|
||||
try severityConfiguration.apply(configuration: severityString)
|
||||
}
|
||||
|
||||
validateExtensions = configuration[$validateExtensions] as? Bool ?? false
|
||||
}
|
||||
}
|
||||
|
||||
+7
-7
@@ -10,26 +10,26 @@ struct PrivateUnitTestConfiguration: SeverityBasedRuleConfiguration, Equatable {
|
||||
private(set) var testParentClasses: Set<String> = ["QuickSpec", "XCTestCase"]
|
||||
|
||||
@ConfigurationElement(key: "regex")
|
||||
private(set) var regex = SwiftLintCore.regex("XCTestCase")
|
||||
private(set) var regex: RegularExpression = "XCTestCase"
|
||||
|
||||
mutating func apply(configuration: Any) throws {
|
||||
guard let configurationDict = configuration as? [String: Any] else {
|
||||
throw Issue.unknownConfiguration(ruleID: Parent.identifier)
|
||||
}
|
||||
if let extraTestParentClasses = configurationDict[$testParentClasses] as? [String] {
|
||||
if let extraTestParentClasses = configurationDict[$testParentClasses.key] as? [String] {
|
||||
self.testParentClasses.formUnion(extraTestParentClasses)
|
||||
}
|
||||
if let regexString = configurationDict[$regex] as? String {
|
||||
if let regexString = configurationDict[$regex.key] as? String {
|
||||
// TODO: [01/09/2025] Remove deprecation warning after ~2 years and use `UnitTestConfiguration`
|
||||
// instead of this configuration.
|
||||
queuedPrintError(
|
||||
"""
|
||||
warning: '\($regex)' has been replaced by a list of explicit parent class names. They can be \
|
||||
configured in the '\($testParentClasses)' option. '\($regex)' will be completely removed \
|
||||
warning: '\($regex.key)' has been replaced by a list of explicit parent class names. They can be \
|
||||
configured in the '\($testParentClasses.key)' option. '\($regex.key)' will be completely removed \
|
||||
in a future release.
|
||||
"""
|
||||
)
|
||||
regex = try .cached(pattern: regexString)
|
||||
regex = try RegularExpression(pattern: regexString)
|
||||
}
|
||||
if configurationDict["included"] is String {
|
||||
// TODO: [01/09/2025] Remove deprecation warning after ~2 years and replace this configuration by
|
||||
@@ -52,7 +52,7 @@ struct PrivateUnitTestConfiguration: SeverityBasedRuleConfiguration, Equatable {
|
||||
"warning: 'message' is ignored from now on. You may remove it from the configuration file."
|
||||
)
|
||||
}
|
||||
if let severityString = configurationDict[$severityConfiguration] as? String {
|
||||
if let severityString = configurationDict[$severityConfiguration.key] as? String {
|
||||
try severityConfiguration.apply(configuration: severityString)
|
||||
}
|
||||
}
|
||||
|
||||
+4
-23
@@ -1,5 +1,6 @@
|
||||
import SwiftLintCore
|
||||
|
||||
@AutoApply
|
||||
struct ProhibitedSuperConfiguration: SeverityBasedRuleConfiguration, Equatable {
|
||||
typealias Parent = ProhibitedSuperRule
|
||||
|
||||
@@ -10,7 +11,7 @@ struct ProhibitedSuperConfiguration: SeverityBasedRuleConfiguration, Equatable {
|
||||
@ConfigurationElement(key: "included")
|
||||
private(set) var included = ["*"]
|
||||
|
||||
private(set) var resolvedMethodNames = [
|
||||
private static let methodNames = [
|
||||
// NSFileProviderExtension
|
||||
"providePlaceholder(at:completionHandler:)",
|
||||
// NSTextInput
|
||||
@@ -21,30 +22,10 @@ struct ProhibitedSuperConfiguration: SeverityBasedRuleConfiguration, Equatable {
|
||||
"loadView()"
|
||||
]
|
||||
|
||||
mutating func apply(configuration: Any) throws {
|
||||
guard let configuration = configuration as? [String: Any] else {
|
||||
throw Issue.unknownConfiguration(ruleID: Parent.identifier)
|
||||
}
|
||||
|
||||
if let severityString = configuration[$severityConfiguration] as? String {
|
||||
try severityConfiguration.apply(configuration: severityString)
|
||||
}
|
||||
|
||||
if let excluded = [String].array(of: configuration[$excluded]) {
|
||||
self.excluded = excluded
|
||||
}
|
||||
|
||||
if let included = [String].array(of: configuration[$included]) {
|
||||
self.included = included
|
||||
}
|
||||
|
||||
resolvedMethodNames = calculateResolvedMethodNames()
|
||||
}
|
||||
|
||||
private func calculateResolvedMethodNames() -> [String] {
|
||||
var resolvedMethodNames: [String] {
|
||||
var names = [String]()
|
||||
if included.contains("*") && !excluded.contains("*") {
|
||||
names += resolvedMethodNames
|
||||
names += Self.methodNames
|
||||
}
|
||||
names += included.filter { $0 != "*" }
|
||||
names = names.filter { !excluded.contains($0) }
|
||||
|
||||
+1
-12
@@ -1,5 +1,6 @@
|
||||
import SwiftLintCore
|
||||
|
||||
@AutoApply
|
||||
struct SelfBindingConfiguration: SeverityBasedRuleConfiguration, Equatable {
|
||||
typealias Parent = SelfBindingRule
|
||||
|
||||
@@ -7,16 +8,4 @@ struct SelfBindingConfiguration: SeverityBasedRuleConfiguration, Equatable {
|
||||
private(set) var severityConfiguration = SeverityConfiguration<Parent>(.warning)
|
||||
@ConfigurationElement(key: "bind_identifier")
|
||||
private(set) var bindIdentifier = "self"
|
||||
|
||||
mutating func apply(configuration: Any) throws {
|
||||
guard let configuration = configuration as? [String: Any] else {
|
||||
throw Issue.unknownConfiguration(ruleID: Parent.identifier)
|
||||
}
|
||||
|
||||
if let severityString = configuration[$severityConfiguration] as? String {
|
||||
try severityConfiguration.apply(configuration: severityString)
|
||||
}
|
||||
|
||||
bindIdentifier = configuration[$bindIdentifier] as? String ?? "self"
|
||||
}
|
||||
}
|
||||
|
||||
+5
-23
@@ -1,38 +1,20 @@
|
||||
import SwiftLintCore
|
||||
|
||||
struct SortedImportsConfiguration: RuleConfiguration, Equatable {
|
||||
@AutoApply
|
||||
struct SortedImportsConfiguration: SeverityBasedRuleConfiguration, Equatable {
|
||||
typealias Parent = SortedImportsRule
|
||||
|
||||
enum SortedImportsGroupingConfiguration: String, AcceptableByConfigurationElement {
|
||||
@MakeAcceptableByConfigurationElement
|
||||
enum SortedImportsGroupingConfiguration: String {
|
||||
/// Sorts import lines based on any import attributes (e.g. `@testable`, `@_exported`, etc.), followed by a case
|
||||
/// insensitive comparison of the imported module name.
|
||||
case attributes
|
||||
/// Sorts import lines based on a case insensitive comparison of the imported module name.
|
||||
case names
|
||||
|
||||
func asOption() -> OptionType { .symbol(rawValue) }
|
||||
}
|
||||
|
||||
@ConfigurationElement(key: "severity")
|
||||
private(set) var severity = SeverityConfiguration<Parent>(.warning)
|
||||
private(set) var severityConfiguration = SeverityConfiguration<Parent>(.warning)
|
||||
@ConfigurationElement(key: "grouping")
|
||||
private(set) var grouping = SortedImportsGroupingConfiguration.names
|
||||
|
||||
mutating func apply(configuration: Any) throws {
|
||||
guard let configuration = configuration as? [String: Any] else {
|
||||
throw Issue.unknownConfiguration(ruleID: Parent.identifier)
|
||||
}
|
||||
|
||||
if let rawGrouping = configuration[$grouping] {
|
||||
guard let rawGrouping = rawGrouping as? String,
|
||||
let grouping = SortedImportsGroupingConfiguration(rawValue: rawGrouping) else {
|
||||
throw Issue.unknownConfiguration(ruleID: Parent.identifier)
|
||||
}
|
||||
self.grouping = grouping
|
||||
}
|
||||
|
||||
if let severityString = configuration[$severity] as? String {
|
||||
try severity.apply(configuration: severityString)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+3
-24
@@ -1,38 +1,17 @@
|
||||
import SwiftLintCore
|
||||
|
||||
@AutoApply
|
||||
struct StatementPositionConfiguration: SeverityBasedRuleConfiguration, Equatable {
|
||||
typealias Parent = StatementPositionRule
|
||||
|
||||
enum StatementModeConfiguration: String, AcceptableByConfigurationElement {
|
||||
@MakeAcceptableByConfigurationElement
|
||||
enum StatementModeConfiguration: String {
|
||||
case `default` = "default"
|
||||
case uncuddledElse = "uncuddled_else"
|
||||
|
||||
init(value: Any) throws {
|
||||
if let string = (value as? String)?.lowercased(),
|
||||
let value = Self(rawValue: string) {
|
||||
self = value
|
||||
} else {
|
||||
throw Issue.unknownConfiguration(ruleID: Parent.identifier)
|
||||
}
|
||||
}
|
||||
|
||||
func asOption() -> OptionType { .symbol(rawValue) }
|
||||
}
|
||||
|
||||
@ConfigurationElement(key: "severity")
|
||||
private(set) var severityConfiguration = SeverityConfiguration<Parent>.warning
|
||||
@ConfigurationElement(key: "statement_mode")
|
||||
private(set) var statementMode = StatementModeConfiguration.default
|
||||
|
||||
mutating func apply(configuration: Any) throws {
|
||||
guard let configurationDict = configuration as? [String: Any] else {
|
||||
throw Issue.unknownConfiguration(ruleID: Parent.identifier)
|
||||
}
|
||||
if let statementModeConfiguration = configurationDict[$statementMode] {
|
||||
try statementMode = StatementModeConfiguration(value: statementModeConfiguration)
|
||||
}
|
||||
if let severity = configurationDict[$severityConfiguration] {
|
||||
try severityConfiguration.apply(configuration: severity)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+1
-12
@@ -1,5 +1,6 @@
|
||||
import SwiftLintCore
|
||||
|
||||
@AutoApply
|
||||
struct SwitchCaseAlignmentConfiguration: SeverityBasedRuleConfiguration, Equatable {
|
||||
typealias Parent = SwitchCaseAlignmentRule
|
||||
|
||||
@@ -7,16 +8,4 @@ struct SwitchCaseAlignmentConfiguration: SeverityBasedRuleConfiguration, Equatab
|
||||
private(set) var severityConfiguration = SeverityConfiguration<Parent>(.warning)
|
||||
@ConfigurationElement(key: "indented_cases")
|
||||
private(set) var indentedCases = false
|
||||
|
||||
mutating func apply(configuration: Any) throws {
|
||||
guard let configuration = configuration as? [String: Any] else {
|
||||
throw Issue.unknownConfiguration(ruleID: Parent.identifier)
|
||||
}
|
||||
|
||||
indentedCases = configuration[$indentedCases] as? Bool ?? false
|
||||
|
||||
if let severityString = configuration[$severityConfiguration] as? String {
|
||||
try severityConfiguration.apply(configuration: severityString)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+9
-20
@@ -1,5 +1,9 @@
|
||||
import SwiftLintCore
|
||||
|
||||
// swiftlint:disable:next blanket_disable_command
|
||||
// swiftlint:disable let_var_whitespace
|
||||
|
||||
@AutoApply
|
||||
struct TestCaseAccessibilityConfiguration: SeverityBasedRuleConfiguration, Equatable {
|
||||
typealias Parent = TestCaseAccessibilityRule
|
||||
|
||||
@@ -7,24 +11,9 @@ struct TestCaseAccessibilityConfiguration: SeverityBasedRuleConfiguration, Equat
|
||||
private(set) var severityConfiguration = SeverityConfiguration<Parent>(.warning)
|
||||
@ConfigurationElement(key: "allowed_prefixes")
|
||||
private(set) var allowedPrefixes: Set<String> = []
|
||||
@ConfigurationElement(key: "test_parent_classes")
|
||||
private(set) var testParentClasses: Set<String> = ["QuickSpec", "XCTestCase"]
|
||||
|
||||
mutating func apply(configuration: Any) throws {
|
||||
guard let configuration = configuration as? [String: Any] else {
|
||||
throw Issue.unknownConfiguration(ruleID: Parent.identifier)
|
||||
}
|
||||
|
||||
if let severityString = configuration[$severityConfiguration] as? String {
|
||||
try severityConfiguration.apply(configuration: severityString)
|
||||
}
|
||||
|
||||
if let allowedPrefixes = configuration[$allowedPrefixes] as? [String] {
|
||||
self.allowedPrefixes = Set(allowedPrefixes)
|
||||
}
|
||||
|
||||
if let extraTestParentClasses = configuration[$testParentClasses] as? [String] {
|
||||
self.testParentClasses.formUnion(extraTestParentClasses)
|
||||
}
|
||||
}
|
||||
@ConfigurationElement(
|
||||
key: "test_parent_classes",
|
||||
postprocessor: { $0.formUnion(["QuickSpec", "XCTestCase"]) }
|
||||
)
|
||||
private(set) var testParentClasses = Set<String>()
|
||||
}
|
||||
|
||||
@@ -1,31 +1,17 @@
|
||||
import SwiftLintCore
|
||||
|
||||
@AutoApply
|
||||
struct TodoConfiguration: SeverityBasedRuleConfiguration, Equatable {
|
||||
typealias Parent = TodoRule
|
||||
|
||||
enum TodoKeyword: String, CaseIterable, AcceptableByConfigurationElement {
|
||||
@MakeAcceptableByConfigurationElement
|
||||
enum TodoKeyword: String, CaseIterable {
|
||||
case todo = "TODO"
|
||||
case fixme = "FIXME"
|
||||
|
||||
func asOption() -> OptionType { .symbol(rawValue) }
|
||||
}
|
||||
|
||||
@ConfigurationElement(key: "severity")
|
||||
private(set) var severityConfiguration = SeverityConfiguration<Parent>(.warning)
|
||||
@ConfigurationElement(key: "only")
|
||||
private(set) var only = TodoKeyword.allCases
|
||||
|
||||
mutating func apply(configuration: Any) throws {
|
||||
guard let configuration = configuration as? [String: Any] else {
|
||||
throw Issue.unknownConfiguration(ruleID: Parent.identifier)
|
||||
}
|
||||
|
||||
if let severityString = configuration[$severityConfiguration] as? String {
|
||||
try severityConfiguration.apply(configuration: severityString)
|
||||
}
|
||||
|
||||
if let onlyStrings = configuration[$only] as? [String] {
|
||||
self.only = onlyStrings.compactMap { TodoKeyword(rawValue: $0) }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+1
-12
@@ -1,5 +1,6 @@
|
||||
import SwiftLintCore
|
||||
|
||||
@AutoApply
|
||||
struct TrailingClosureConfiguration: SeverityBasedRuleConfiguration, Equatable {
|
||||
typealias Parent = TrailingClosureRule
|
||||
|
||||
@@ -7,16 +8,4 @@ struct TrailingClosureConfiguration: SeverityBasedRuleConfiguration, Equatable {
|
||||
private(set) var severityConfiguration = SeverityConfiguration<Parent>(.warning)
|
||||
@ConfigurationElement(key: "only_single_muted_parameter")
|
||||
private(set) var onlySingleMutedParameter = false
|
||||
|
||||
mutating func apply(configuration: Any) throws {
|
||||
guard let configuration = configuration as? [String: Any] else {
|
||||
throw Issue.unknownConfiguration(ruleID: Parent.identifier)
|
||||
}
|
||||
|
||||
onlySingleMutedParameter = (configuration[$onlySingleMutedParameter] as? Bool == true)
|
||||
|
||||
if let severityString = configuration[$severityConfiguration] as? String {
|
||||
try severityConfiguration.apply(configuration: severityString)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+1
-12
@@ -1,5 +1,6 @@
|
||||
import SwiftLintCore
|
||||
|
||||
@AutoApply
|
||||
struct TrailingCommaConfiguration: SeverityBasedRuleConfiguration, Equatable {
|
||||
typealias Parent = TrailingCommaRule
|
||||
|
||||
@@ -7,16 +8,4 @@ struct TrailingCommaConfiguration: SeverityBasedRuleConfiguration, Equatable {
|
||||
private(set) var severityConfiguration = SeverityConfiguration<Parent>(.warning)
|
||||
@ConfigurationElement(key: "mandatory_comma")
|
||||
private(set) var mandatoryComma = false
|
||||
|
||||
mutating func apply(configuration: Any) throws {
|
||||
guard let configuration = configuration as? [String: Any] else {
|
||||
throw Issue.unknownConfiguration(ruleID: Parent.identifier)
|
||||
}
|
||||
|
||||
mandatoryComma = (configuration[$mandatoryComma] as? Bool == true)
|
||||
|
||||
if let severityString = configuration[$severityConfiguration] as? String {
|
||||
try severityConfiguration.apply(configuration: severityString)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+1
-13
@@ -1,5 +1,6 @@
|
||||
import SwiftLintCore
|
||||
|
||||
@AutoApply
|
||||
struct TrailingWhitespaceConfiguration: SeverityBasedRuleConfiguration, Equatable {
|
||||
typealias Parent = TrailingWhitespaceRule
|
||||
|
||||
@@ -9,17 +10,4 @@ struct TrailingWhitespaceConfiguration: SeverityBasedRuleConfiguration, Equatabl
|
||||
private(set) var ignoresEmptyLines = false
|
||||
@ConfigurationElement(key: "ignores_comments")
|
||||
private(set) var ignoresComments = true
|
||||
|
||||
mutating func apply(configuration: Any) throws {
|
||||
guard let configuration = configuration as? [String: Any] else {
|
||||
throw Issue.unknownConfiguration(ruleID: Parent.identifier)
|
||||
}
|
||||
|
||||
ignoresEmptyLines = (configuration[$ignoresEmptyLines] as? Bool == true)
|
||||
ignoresComments = (configuration[$ignoresComments] as? Bool == true)
|
||||
|
||||
if let severityString = configuration[$severityConfiguration] as? String {
|
||||
try severityConfiguration.apply(configuration: severityString)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+3
-27
@@ -1,6 +1,7 @@
|
||||
import SwiftLintCore
|
||||
|
||||
enum TypeContent: String, AcceptableByConfigurationElement {
|
||||
@MakeAcceptableByConfigurationElement
|
||||
enum TypeContent: String {
|
||||
case `case` = "case"
|
||||
case typeAlias = "type_alias"
|
||||
case associatedType = "associated_type"
|
||||
@@ -16,10 +17,9 @@ enum TypeContent: String, AcceptableByConfigurationElement {
|
||||
case otherMethod = "other_method"
|
||||
case `subscript` = "subscript"
|
||||
case deinitializer = "deinitializer"
|
||||
|
||||
func asOption() -> OptionType { .symbol(rawValue) }
|
||||
}
|
||||
|
||||
@AutoApply
|
||||
struct TypeContentsOrderConfiguration: SeverityBasedRuleConfiguration, Equatable {
|
||||
typealias Parent = TypeContentsOrderRule
|
||||
|
||||
@@ -42,28 +42,4 @@ struct TypeContentsOrderConfiguration: SeverityBasedRuleConfiguration, Equatable
|
||||
[.subscript],
|
||||
[.deinitializer]
|
||||
]
|
||||
|
||||
mutating func apply(configuration: Any) throws {
|
||||
guard let configuration = configuration as? [String: Any] else {
|
||||
throw Issue.unknownConfiguration(ruleID: Parent.identifier)
|
||||
}
|
||||
|
||||
if let severityValue = configuration[$severityConfiguration] as? String {
|
||||
try severityConfiguration.apply(configuration: severityValue)
|
||||
}
|
||||
|
||||
if let custom = configuration[$order] as? [Any] {
|
||||
order.removeAll()
|
||||
for entry in custom {
|
||||
if let singleEntry = entry as? String {
|
||||
if let typeContent = TypeContent(rawValue: singleEntry) {
|
||||
order.append([typeContent])
|
||||
}
|
||||
} else if let arrayEntry = entry as? [String] {
|
||||
let typeContents = arrayEntry.compactMap { TypeContent(rawValue: $0) }
|
||||
order.append(typeContents)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import SwiftLintCore
|
||||
|
||||
@AutoApply
|
||||
struct TypeNameConfiguration: RuleConfiguration, Equatable {
|
||||
typealias Parent = TypeNameRule
|
||||
|
||||
@@ -10,15 +11,4 @@ struct TypeNameConfiguration: RuleConfiguration, Equatable {
|
||||
maxLengthError: 1000)
|
||||
@ConfigurationElement(key: "validate_protocols")
|
||||
private(set) var validateProtocols = true
|
||||
|
||||
mutating func apply(configuration: Any) throws {
|
||||
guard let configuration = configuration as? [String: Any] else {
|
||||
throw Issue.unknownConfiguration(ruleID: Parent.identifier)
|
||||
}
|
||||
try nameConfiguration.apply(configuration: configuration)
|
||||
|
||||
if let validateProtocols = configuration["validate_protocols"] as? Bool {
|
||||
self.validateProtocols = validateProtocols
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,23 +5,16 @@ typealias EmptyXCTestMethodConfiguration = UnitTestConfiguration<EmptyXCTestMeth
|
||||
typealias SingleTestClassConfiguration = UnitTestConfiguration<SingleTestClassRule>
|
||||
typealias NoMagicNumbersConfiguration = UnitTestConfiguration<NoMagicNumbersRule>
|
||||
|
||||
// swiftlint:disable:next blanket_disable_command
|
||||
// swiftlint:disable let_var_whitespace
|
||||
|
||||
@AutoApply
|
||||
struct UnitTestConfiguration<Parent: Rule>: SeverityBasedRuleConfiguration, Equatable {
|
||||
@ConfigurationElement(key: "severity")
|
||||
private(set) var severityConfiguration = SeverityConfiguration<Parent>(.warning)
|
||||
@ConfigurationElement(key: "test_parent_classes")
|
||||
private(set) var testParentClasses: Set<String> = ["QuickSpec", "XCTestCase"]
|
||||
|
||||
mutating func apply(configuration: Any) throws {
|
||||
guard let configuration = configuration as? [String: Any] else {
|
||||
throw Issue.unknownConfiguration(ruleID: Parent.identifier)
|
||||
}
|
||||
|
||||
if let severityString = configuration[$severityConfiguration] as? String {
|
||||
try severityConfiguration.apply(configuration: severityString)
|
||||
}
|
||||
|
||||
if let extraTestParentClasses = configuration[$testParentClasses] as? [String] {
|
||||
self.testParentClasses.formUnion(extraTestParentClasses)
|
||||
}
|
||||
}
|
||||
@ConfigurationElement(
|
||||
key: "test_parent_classes",
|
||||
postprocessor: { $0.formUnion(["QuickSpec", "XCTestCase"]) }
|
||||
)
|
||||
private(set) var testParentClasses = Set<String>()
|
||||
}
|
||||
|
||||
+9
-25
@@ -1,5 +1,9 @@
|
||||
import SwiftLintCore
|
||||
|
||||
// swiftlint:disable:next blanket_disable_command
|
||||
// swiftlint:disable let_var_whitespace
|
||||
|
||||
@AutoApply
|
||||
struct UnusedDeclarationConfiguration: SeverityBasedRuleConfiguration, Equatable {
|
||||
typealias Parent = UnusedDeclarationRule
|
||||
|
||||
@@ -7,29 +11,9 @@ struct UnusedDeclarationConfiguration: SeverityBasedRuleConfiguration, Equatable
|
||||
private(set) var severityConfiguration = SeverityConfiguration<Parent>.error
|
||||
@ConfigurationElement(key: "include_public_and_open")
|
||||
private(set) var includePublicAndOpen = false
|
||||
@ConfigurationElement(key: "related_usrs_to_skip")
|
||||
private(set) var relatedUSRsToSkip = Set(["s:7SwiftUI15PreviewProviderP"])
|
||||
|
||||
mutating func apply(configuration: Any) throws {
|
||||
guard let configDict = configuration as? [String: Any], configDict.isNotEmpty else {
|
||||
throw Issue.unknownConfiguration(ruleID: Parent.identifier)
|
||||
}
|
||||
|
||||
for (string, value) in configDict {
|
||||
switch (string, value) {
|
||||
case ($severityConfiguration, let stringValue as String):
|
||||
try severityConfiguration.apply(configuration: stringValue)
|
||||
case ($includePublicAndOpen, let boolValue as Bool):
|
||||
includePublicAndOpen = boolValue
|
||||
case ($relatedUSRsToSkip, let value):
|
||||
if let usrs = [String].array(of: value) {
|
||||
relatedUSRsToSkip.formUnion(usrs)
|
||||
} else {
|
||||
throw Issue.unknownConfiguration(ruleID: Parent.identifier)
|
||||
}
|
||||
default:
|
||||
throw Issue.unknownConfiguration(ruleID: Parent.identifier)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ConfigurationElement(
|
||||
key: "related_usrs_to_skip",
|
||||
postprocessor: { $0.insert("s:7SwiftUI15PreviewProviderP") }
|
||||
)
|
||||
private(set) var relatedUSRsToSkip = Set<String>()
|
||||
}
|
||||
|
||||
+2
-20
@@ -8,7 +8,7 @@ struct TransitiveModuleConfiguration<Parent: Rule>: Equatable, AcceptableByConfi
|
||||
/// The set of modules that can be transitively imported by `importedModule`.
|
||||
let transitivelyImportedModules: [String]
|
||||
|
||||
init(configuration: Any) throws {
|
||||
init(fromAny configuration: Any, context ruleID: String) throws {
|
||||
guard let configurationDict = configuration as? [String: Any],
|
||||
Set(configurationDict.keys) == ["module", "allowed_transitive_imports"],
|
||||
let importedModule = configurationDict["module"] as? String,
|
||||
@@ -27,6 +27,7 @@ struct TransitiveModuleConfiguration<Parent: Rule>: Equatable, AcceptableByConfi
|
||||
}
|
||||
}
|
||||
|
||||
@AutoApply
|
||||
struct UnusedImportConfiguration: SeverityBasedRuleConfiguration, Equatable {
|
||||
typealias Parent = UnusedImportRule
|
||||
|
||||
@@ -39,23 +40,4 @@ struct UnusedImportConfiguration: SeverityBasedRuleConfiguration, Equatable {
|
||||
/// A set of modules to never remove the imports of.
|
||||
@ConfigurationElement(key: "always_keep_imports")
|
||||
private(set) var alwaysKeepImports = [String]()
|
||||
|
||||
mutating func apply(configuration: Any) throws {
|
||||
guard let configurationDict = configuration as? [String: Any] else {
|
||||
throw Issue.unknownConfiguration(ruleID: Parent.identifier)
|
||||
}
|
||||
|
||||
if let severity = configurationDict[$severityConfiguration] {
|
||||
try severityConfiguration.apply(configuration: severity)
|
||||
}
|
||||
if let requireExplicitImports = configurationDict[$requireExplicitImports] as? Bool {
|
||||
self.requireExplicitImports = requireExplicitImports
|
||||
}
|
||||
if let allowedTransitiveImports = configurationDict[$allowedTransitiveImports] as? [Any] {
|
||||
self.allowedTransitiveImports = try allowedTransitiveImports.map(TransitiveModuleConfiguration.init)
|
||||
}
|
||||
if let alwaysKeepImports = configurationDict[$alwaysKeepImports] as? [String] {
|
||||
self.alwaysKeepImports = alwaysKeepImports
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+1
-14
@@ -1,5 +1,6 @@
|
||||
import SwiftLintCore
|
||||
|
||||
@AutoApply
|
||||
struct UnusedOptionalBindingConfiguration: SeverityBasedRuleConfiguration, Equatable {
|
||||
typealias Parent = UnusedOptionalBindingRule
|
||||
|
||||
@@ -7,18 +8,4 @@ struct UnusedOptionalBindingConfiguration: SeverityBasedRuleConfiguration, Equat
|
||||
private(set) var severityConfiguration = SeverityConfiguration<Parent>(.warning)
|
||||
@ConfigurationElement(key: "ignore_optional_try")
|
||||
private(set) var ignoreOptionalTry = false
|
||||
|
||||
mutating func apply(configuration: Any) throws {
|
||||
guard let configuration = configuration as? [String: Any] else {
|
||||
throw Issue.unknownConfiguration(ruleID: Parent.identifier)
|
||||
}
|
||||
|
||||
if let ignoreOptionalTry = configuration[$ignoreOptionalTry] as? Bool {
|
||||
self.ignoreOptionalTry = ignoreOptionalTry
|
||||
}
|
||||
|
||||
if let severityString = configuration[$severityConfiguration] as? String {
|
||||
try severityConfiguration.apply(configuration: severityString)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+1
-18
@@ -1,6 +1,6 @@
|
||||
import SwiftLintCore
|
||||
|
||||
// swiftlint:disable:next type_name
|
||||
@AutoApply // swiftlint:disable:next type_name
|
||||
struct VerticalWhitespaceClosingBracesConfiguration: SeverityBasedRuleConfiguration, Equatable {
|
||||
typealias Parent = VerticalWhitespaceClosingBracesRule
|
||||
|
||||
@@ -8,21 +8,4 @@ struct VerticalWhitespaceClosingBracesConfiguration: SeverityBasedRuleConfigurat
|
||||
private(set) var severityConfiguration = SeverityConfiguration<Parent>(.warning)
|
||||
@ConfigurationElement(key: "only_enforce_before_trivial_lines")
|
||||
private(set) var onlyEnforceBeforeTrivialLines = false
|
||||
|
||||
mutating func apply(configuration: Any) throws {
|
||||
guard let configuration = configuration as? [String: Any] else {
|
||||
throw Issue.unknownConfiguration(ruleID: Parent.identifier)
|
||||
}
|
||||
|
||||
for (string, value) in configuration {
|
||||
switch (string, value) {
|
||||
case ($severityConfiguration, let stringValue as String):
|
||||
try severityConfiguration.apply(configuration: stringValue)
|
||||
case ($onlyEnforceBeforeTrivialLines, let boolValue as Bool):
|
||||
onlyEnforceBeforeTrivialLines = boolValue
|
||||
default:
|
||||
throw Issue.unknownConfiguration(ruleID: Parent.identifier)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+1
-14
@@ -1,5 +1,6 @@
|
||||
import SwiftLintCore
|
||||
|
||||
@AutoApply
|
||||
struct VerticalWhitespaceConfiguration: SeverityBasedRuleConfiguration, Equatable {
|
||||
typealias Parent = VerticalWhitespaceRule
|
||||
|
||||
@@ -7,18 +8,4 @@ struct VerticalWhitespaceConfiguration: SeverityBasedRuleConfiguration, Equatabl
|
||||
private(set) var severityConfiguration = SeverityConfiguration<Parent>(.warning)
|
||||
@ConfigurationElement(key: "max_empty_lines")
|
||||
private(set) var maxEmptyLines = 1
|
||||
|
||||
mutating func apply(configuration: Any) throws {
|
||||
guard let configuration = configuration as? [String: Any] else {
|
||||
throw Issue.unknownConfiguration(ruleID: Parent.identifier)
|
||||
}
|
||||
|
||||
if let maxEmptyLines = configuration[$maxEmptyLines] as? Int {
|
||||
self.maxEmptyLines = maxEmptyLines
|
||||
}
|
||||
|
||||
if let severityString = configuration[$severityConfiguration] as? String {
|
||||
try severityConfiguration.apply(configuration: severityString)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+7
-21
@@ -1,31 +1,17 @@
|
||||
import SwiftLintCore
|
||||
|
||||
@AutoApply
|
||||
struct XCTSpecificMatcherConfiguration: SeverityBasedRuleConfiguration, Equatable {
|
||||
typealias Parent = XCTSpecificMatcherRule
|
||||
|
||||
@MakeAcceptableByConfigurationElement
|
||||
enum Matcher: String, CaseIterable {
|
||||
case oneArgumentAsserts = "one-argument-asserts"
|
||||
case twoArgumentAsserts = "two-argument-asserts"
|
||||
}
|
||||
|
||||
@ConfigurationElement(key: "severity")
|
||||
private(set) var severityConfiguration = SeverityConfiguration<Parent>(.warning)
|
||||
@ConfigurationElement(key: "matchers")
|
||||
private(set) var matchers = Matcher.allCases
|
||||
|
||||
enum Matcher: String, Hashable, CaseIterable, AcceptableByConfigurationElement {
|
||||
case oneArgumentAsserts = "one-argument-asserts"
|
||||
case twoArgumentAsserts = "two-argument-asserts"
|
||||
|
||||
func asOption() -> OptionType { .symbol(rawValue) }
|
||||
}
|
||||
|
||||
mutating func apply(configuration: Any) throws {
|
||||
guard let configuration = configuration as? [String: Any] else {
|
||||
throw Issue.unknownConfiguration(ruleID: Parent.identifier)
|
||||
}
|
||||
|
||||
if let severityString = configuration[$severityConfiguration] as? String {
|
||||
try severityConfiguration.apply(configuration: severityString)
|
||||
}
|
||||
|
||||
if let matchers = configuration[$matchers] as? [String] {
|
||||
self.matchers = matchers.compactMap(Matcher.init)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import Foundation
|
||||
import SourceKittenFramework
|
||||
|
||||
struct FileTypesOrderRule: ConfigurationProviderRule, OptInRule {
|
||||
private typealias FileTypeOffset = (fileType: FileType, offset: ByteCount)
|
||||
private typealias FileTypeOffset = (fileType: FileTypesOrderConfiguration.FileType, offset: ByteCount)
|
||||
|
||||
struct FileTypesOrderRule: ConfigurationProviderRule, OptInRule {
|
||||
var configuration = FileTypesOrderConfiguration()
|
||||
|
||||
static let description = RuleDescription(
|
||||
@@ -173,7 +173,7 @@ private extension SourceKittenDictionary {
|
||||
}
|
||||
|
||||
private extension Array where Element == SourceKittenDictionary {
|
||||
func offsets(for fileType: FileType) -> [(fileType: FileType, offset: ByteCount)] {
|
||||
func offsets(for fileType: FileTypesOrderConfiguration.FileType) -> [FileTypeOffset] {
|
||||
self.compactMap { substructure in
|
||||
guard let offset = substructure.offset else { return nil }
|
||||
return (fileType, offset)
|
||||
|
||||
@@ -70,7 +70,7 @@ struct SortedImportsRule: CorrectableRule, ConfigurationProviderRule, OptInRule
|
||||
return violatingOffsets(inGroups: groups).map { index -> StyleViolation in
|
||||
let location = Location(file: file, characterOffset: index)
|
||||
return StyleViolation(ruleDescription: Self.description,
|
||||
severity: configuration.severity.severity,
|
||||
severity: configuration.severity,
|
||||
location: location)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,6 +4,26 @@ import SourceKittenFramework
|
||||
private var regexCache = [RegexCacheKey: NSRegularExpression]()
|
||||
private let regexCacheLock = NSLock()
|
||||
|
||||
public struct RegularExpression: Hashable, Comparable, ExpressibleByStringLiteral {
|
||||
public let regex: NSRegularExpression
|
||||
|
||||
public init(pattern: String, options: NSRegularExpression.Options? = nil) throws {
|
||||
regex = try .cached(pattern: pattern)
|
||||
}
|
||||
public init(stringLiteral value: String) {
|
||||
// swiftlint:disable:next force_try
|
||||
try! self.init(pattern: value)
|
||||
}
|
||||
|
||||
var pattern: String { regex.pattern }
|
||||
|
||||
var numberOfCaptureGroups: Int { regex.numberOfCaptureGroups }
|
||||
|
||||
public static func < (lhs: Self, rhs: Self) -> Bool {
|
||||
lhs.pattern < rhs.pattern
|
||||
}
|
||||
}
|
||||
|
||||
private struct RegexCacheKey: Hashable {
|
||||
let pattern: String
|
||||
let options: NSRegularExpression.Options
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
/// Macro to be attached to rule configurations. It generates the configuration parsing logic
|
||||
/// automatically based on the defined `@ConfigurationElement`s.
|
||||
@attached(member, names: named(apply))
|
||||
public macro AutoApply() = #externalMacro(
|
||||
module: "SwiftLintCoreMacros",
|
||||
type: "AutoApply")
|
||||
|
||||
/// Macro that lets an enum with a ``String`` raw type automatically conform to ``AcceptableByConfigurationElement``.
|
||||
@attached(extension, conformances: AcceptableByConfigurationElement, names: named(init), named(asOption))
|
||||
public macro MakeAcceptableByConfigurationElement() = #externalMacro(
|
||||
module: "SwiftLintCoreMacros",
|
||||
type: "MakeAcceptableByConfigurationElement")
|
||||
@@ -71,6 +71,19 @@ public struct RuleConfigurationDescription: Equatable {
|
||||
}
|
||||
return Self(options: options)
|
||||
}
|
||||
|
||||
func allowedKeys() -> [String] {
|
||||
options.flatMap { option -> [String] in
|
||||
switch option.value {
|
||||
case let .nested(nestedConfiguration) where option.key.isEmpty:
|
||||
nestedConfiguration.allowedKeys()
|
||||
case .empty:
|
||||
[]
|
||||
default:
|
||||
[option.key]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension RuleConfigurationDescription: Documentable {
|
||||
@@ -280,7 +293,8 @@ public extension OptionType {
|
||||
|
||||
/// Create an option defined by nested configuration description.
|
||||
///
|
||||
/// - Parameter description: A configuration description buildable by applying the result builder syntax.
|
||||
/// - Parameters:
|
||||
/// - description: A configuration description buildable by applying the result builder syntax.
|
||||
///
|
||||
/// - Returns: A configuration option with a value being another configuration description.
|
||||
static func nest(@RuleConfigurationDescriptionBuilder _ description: () -> RuleConfigurationDescription) -> Self {
|
||||
@@ -297,6 +311,13 @@ private protocol AnyConfigurationElement {
|
||||
|
||||
/// Type of an object that can be used as a configuration element.
|
||||
public protocol AcceptableByConfigurationElement {
|
||||
/// Initializer taking a value from a configuration to create an element of `Self`.
|
||||
///
|
||||
/// - Parameters:
|
||||
/// - value: Value from a configuration.
|
||||
/// - ruleID: The rule's identifier in which context the configuration parsing runs.
|
||||
init(fromAny value: Any, context ruleID: String) throws
|
||||
|
||||
/// Make the object an option.
|
||||
///
|
||||
/// - Returns: Option representing the object.
|
||||
@@ -304,17 +325,29 @@ public protocol AcceptableByConfigurationElement {
|
||||
|
||||
/// Make the object a description.
|
||||
///
|
||||
/// - Parameter key: Name of the option to be put into the description.
|
||||
/// - Parameters:
|
||||
/// - key: Name of the option to be put into the description.
|
||||
///
|
||||
/// - Returns: Configuration description of this object.
|
||||
func asDescription(with key: String) -> RuleConfigurationDescription
|
||||
|
||||
/// Update the object.
|
||||
///
|
||||
/// - Parameter value: New underlying data for the object.
|
||||
mutating func apply(_ value: Any?, ruleID: String) throws
|
||||
}
|
||||
|
||||
/// Default implementations which are shortcuts applicable for most of the types conforming to the protocol.
|
||||
public extension AcceptableByConfigurationElement {
|
||||
func asDescription(with key: String) -> RuleConfigurationDescription {
|
||||
// By default, this method is just a shortcut applicable for most of the types conforming to the protocol.
|
||||
RuleConfigurationDescription(options: [key => asOption()])
|
||||
}
|
||||
|
||||
mutating func apply(_ value: Any?, ruleID: String) throws {
|
||||
if let value {
|
||||
self = try Self(fromAny: value, context: ruleID)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// An option type that does not need a key when used in a ``ConfigurationElement``. Its value will be inlined.
|
||||
@@ -357,23 +390,31 @@ public protocol InlinableOptionType: AcceptableByConfigurationElement {}
|
||||
/// error: 2
|
||||
/// ```
|
||||
@propertyWrapper
|
||||
public struct ConfigurationElement<T: AcceptableByConfigurationElement & Equatable>: Equatable {
|
||||
public class ConfigurationElement<T: AcceptableByConfigurationElement & Equatable> {
|
||||
/// Wrapped option value.
|
||||
public var wrappedValue: T
|
||||
|
||||
/// The option's name. This field can only be accessed by the element's name prefixed with a `$`.
|
||||
public var projectedValue: String { key }
|
||||
public var projectedValue: ConfigurationElement { self }
|
||||
|
||||
private let key: String
|
||||
/// Name of this configuration entry.
|
||||
public let key: String
|
||||
|
||||
private let postprocessor: (inout T) throws -> Void
|
||||
|
||||
/// Default constructor.
|
||||
///
|
||||
/// - Parameters:
|
||||
/// - value: Value to be wrapped.
|
||||
/// - key: Name of the option.
|
||||
public init(wrappedValue value: T, key: String) {
|
||||
/// - postprocessor: Function to be applied to the wrapped value after parsing to validate and modify it.
|
||||
public init(wrappedValue value: T, key: String, postprocessor: @escaping (inout T) throws -> Void = { _ in }) {
|
||||
self.wrappedValue = value
|
||||
self.key = key
|
||||
self.postprocessor = postprocessor
|
||||
|
||||
// Validate and modify the set value immediately. An exception means invalid defaults.
|
||||
try! performAfterParseOperations() // swiftlint:disable:this force_try
|
||||
}
|
||||
|
||||
/// Constructor for optional values.
|
||||
@@ -381,7 +422,7 @@ public struct ConfigurationElement<T: AcceptableByConfigurationElement & Equatab
|
||||
/// It allows to skip explicit initialization with `nil` of the property.
|
||||
///
|
||||
/// - Parameter value: Value to be wrapped.
|
||||
public init<Wrapped>(key: String) where T == Wrapped? {
|
||||
public convenience init<Wrapped>(key: String) where T == Wrapped? {
|
||||
self.init(wrappedValue: nil, key: key)
|
||||
}
|
||||
|
||||
@@ -390,10 +431,16 @@ public struct ConfigurationElement<T: AcceptableByConfigurationElement & Equatab
|
||||
/// ``InlinableOptionType``s are allowed to have an empty key. The configuration will be inlined into its
|
||||
/// parent configuration in this specific case.
|
||||
///
|
||||
/// - Parameter value: Value to be wrapped.
|
||||
public init(wrappedValue value: T) where T: InlinableOptionType {
|
||||
/// - Parameters:
|
||||
/// - value: Value to be wrapped.
|
||||
public convenience init(wrappedValue value: T) where T: InlinableOptionType {
|
||||
self.init(wrappedValue: value, key: "")
|
||||
}
|
||||
|
||||
/// Run operations to validate and modify the parsed value.
|
||||
public func performAfterParseOperations() throws {
|
||||
try postprocessor(&wrappedValue)
|
||||
}
|
||||
}
|
||||
|
||||
extension ConfigurationElement: AnyConfigurationElement {
|
||||
@@ -402,6 +449,12 @@ extension ConfigurationElement: AnyConfigurationElement {
|
||||
}
|
||||
}
|
||||
|
||||
extension ConfigurationElement: Equatable {
|
||||
public static func == (lhs: ConfigurationElement, rhs: ConfigurationElement) -> Bool {
|
||||
lhs.wrappedValue == rhs.wrappedValue && lhs.key == rhs.key
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: AcceptableByConfigurationElement conformances
|
||||
|
||||
extension Optional: AcceptableByConfigurationElement where Wrapped: AcceptableByConfigurationElement {
|
||||
@@ -411,6 +464,10 @@ extension Optional: AcceptableByConfigurationElement where Wrapped: AcceptableBy
|
||||
}
|
||||
return .empty
|
||||
}
|
||||
|
||||
public init(fromAny value: Any, context ruleID: String) throws {
|
||||
self = try Wrapped(fromAny: value, context: ruleID)
|
||||
}
|
||||
}
|
||||
|
||||
struct Symbol: Equatable, AcceptableByConfigurationElement {
|
||||
@@ -419,11 +476,12 @@ struct Symbol: Equatable, AcceptableByConfigurationElement {
|
||||
func asOption() -> OptionType {
|
||||
.symbol(value)
|
||||
}
|
||||
}
|
||||
|
||||
extension OptionType: AcceptableByConfigurationElement {
|
||||
public func asOption() -> OptionType {
|
||||
self
|
||||
init(fromAny value: Any, context ruleID: String) throws {
|
||||
guard let value = value as? String else {
|
||||
throw Issue.invalidConfiguration(ruleID: ruleID)
|
||||
}
|
||||
self.value = value
|
||||
}
|
||||
}
|
||||
|
||||
@@ -431,47 +489,85 @@ extension Bool: AcceptableByConfigurationElement {
|
||||
public func asOption() -> OptionType {
|
||||
.flag(self)
|
||||
}
|
||||
|
||||
public init(fromAny value: Any, context ruleID: String) throws {
|
||||
guard let value = value as? Self else {
|
||||
throw Issue.invalidConfiguration(ruleID: ruleID)
|
||||
}
|
||||
self = value
|
||||
}
|
||||
}
|
||||
|
||||
extension String: AcceptableByConfigurationElement {
|
||||
public func asOption() -> OptionType {
|
||||
.string(self)
|
||||
}
|
||||
|
||||
public init(fromAny value: Any, context ruleID: String) throws {
|
||||
guard let value = value as? Self else {
|
||||
throw Issue.invalidConfiguration(ruleID: ruleID)
|
||||
}
|
||||
self = value
|
||||
}
|
||||
}
|
||||
|
||||
extension Array: AcceptableByConfigurationElement where Element: AcceptableByConfigurationElement {
|
||||
public func asOption() -> OptionType {
|
||||
.list(map { $0.asOption() })
|
||||
}
|
||||
|
||||
public init(fromAny value: Any, context ruleID: String) throws {
|
||||
let values = value as? [Any] ?? [value]
|
||||
self = try values.map { try Element(fromAny: $0, context: ruleID) }
|
||||
}
|
||||
}
|
||||
|
||||
extension Set: AcceptableByConfigurationElement where Element: AcceptableByConfigurationElement & Comparable {
|
||||
public func asOption() -> OptionType {
|
||||
sorted().asOption()
|
||||
}
|
||||
|
||||
public init(fromAny value: Any, context ruleID: String) throws {
|
||||
self = Set(try [Element].init(fromAny: value, context: ruleID))
|
||||
}
|
||||
}
|
||||
|
||||
extension Int: AcceptableByConfigurationElement {
|
||||
public func asOption() -> OptionType {
|
||||
.integer(self)
|
||||
}
|
||||
|
||||
public init(fromAny value: Any, context ruleID: String) throws {
|
||||
guard let value = value as? Self else {
|
||||
throw Issue.invalidConfiguration(ruleID: ruleID)
|
||||
}
|
||||
self = value
|
||||
}
|
||||
}
|
||||
|
||||
extension Double: AcceptableByConfigurationElement {
|
||||
public func asOption() -> OptionType {
|
||||
.float(self)
|
||||
}
|
||||
}
|
||||
|
||||
extension NSRegularExpression: AcceptableByConfigurationElement {
|
||||
public func asOption() -> OptionType {
|
||||
.string(pattern)
|
||||
public init(fromAny value: Any, context ruleID: String) throws {
|
||||
guard let value = value as? Self else {
|
||||
throw Issue.invalidConfiguration(ruleID: ruleID)
|
||||
}
|
||||
self = value
|
||||
}
|
||||
}
|
||||
|
||||
extension Range: AcceptableByConfigurationElement {
|
||||
extension RegularExpression: AcceptableByConfigurationElement {
|
||||
public func asOption() -> OptionType {
|
||||
.symbol("\(lowerBound) ..< \(upperBound)")
|
||||
.string(pattern)
|
||||
}
|
||||
|
||||
public init(fromAny value: Any, context ruleID: String) throws {
|
||||
guard let value = value as? String else {
|
||||
throw Issue.invalidConfiguration(ruleID: ruleID)
|
||||
}
|
||||
self = try Self(pattern: value)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -488,6 +584,16 @@ public extension RuleConfiguration {
|
||||
}
|
||||
return RuleConfigurationDescription(options: [key => asOption()])
|
||||
}
|
||||
|
||||
mutating func apply(_ value: Any?, ruleID: String) throws {
|
||||
if let value {
|
||||
try apply(configuration: value)
|
||||
}
|
||||
}
|
||||
|
||||
init(fromAny value: Any, context ruleID: String) throws {
|
||||
throw Issue.genericError("Do not call this initializer")
|
||||
}
|
||||
}
|
||||
|
||||
public extension SeverityConfiguration {
|
||||
|
||||
@@ -22,7 +22,7 @@ public struct SeverityConfiguration<Parent: Rule>: SeverityBasedRuleConfiguratio
|
||||
public mutating func apply(configuration: Any) throws {
|
||||
let configString = configuration as? String
|
||||
let configDict = configuration as? [String: Any]
|
||||
guard let severityString: String = configString ?? configDict?[$severity] as? String,
|
||||
guard let severityString: String = configString ?? configDict?[$severity.key] as? String,
|
||||
let severity = ViolationSeverity(rawValue: severityString.lowercased()) else {
|
||||
throw Issue.unknownConfiguration(ruleID: Parent.description.identifier)
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
/// The magnitude of a `StyleViolation`.
|
||||
@MakeAcceptableByConfigurationElement
|
||||
public enum ViolationSeverity: String, Comparable, Codable, InlinableOptionType {
|
||||
/// Non-fatal. If using SwiftLint as an Xcode build phase, Xcode will mark the build as having succeeded.
|
||||
case warning
|
||||
@@ -10,6 +11,4 @@ public enum ViolationSeverity: String, Comparable, Codable, InlinableOptionType
|
||||
public static func < (lhs: ViolationSeverity, rhs: ViolationSeverity) -> Bool {
|
||||
return lhs == .warning && rhs == .error
|
||||
}
|
||||
|
||||
public func asOption() -> OptionType { .symbol(rawValue) }
|
||||
}
|
||||
|
||||
@@ -44,3 +44,10 @@ public extension RuleConfiguration where Self: Equatable {
|
||||
public extension RuleConfiguration {
|
||||
var parameterDescription: RuleConfigurationDescription? { nil }
|
||||
}
|
||||
|
||||
public extension RuleConfiguration {
|
||||
/// All keys supported by this configuration.
|
||||
var supportedKeys: Set<String> {
|
||||
Set(RuleConfigurationDescription.from(configuration: self).allowedKeys())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,7 +11,7 @@ public struct RegexConfiguration<Parent: Rule>: SeverityBasedRuleConfiguration,
|
||||
public var message = "Regex matched"
|
||||
/// The regular expression to apply to trigger violations for this custom rule.
|
||||
@ConfigurationElement(key: "regex")
|
||||
var regex: NSRegularExpression!
|
||||
var regex: RegularExpression!
|
||||
/// Regular expressions to include when matching the file path.
|
||||
public var included: [NSRegularExpression] = []
|
||||
/// Regular expressions to exclude when matching the file path.
|
||||
@@ -58,11 +58,11 @@ public struct RegexConfiguration<Parent: Rule>: SeverityBasedRuleConfiguration,
|
||||
|
||||
public mutating func apply(configuration: Any) throws {
|
||||
guard let configurationDict = configuration as? [String: Any],
|
||||
let regexString = configurationDict[$regex] as? String else {
|
||||
let regexString = configurationDict[$regex.key] as? String else {
|
||||
throw Issue.unknownConfiguration(ruleID: Parent.description.identifier)
|
||||
}
|
||||
|
||||
regex = try .cached(pattern: regexString)
|
||||
regex = try RegularExpression(pattern: regexString)
|
||||
|
||||
if let includedString = configurationDict["included"] as? String {
|
||||
included = [try .cached(pattern: includedString)]
|
||||
@@ -86,7 +86,7 @@ public struct RegexConfiguration<Parent: Rule>: SeverityBasedRuleConfiguration,
|
||||
if let message = configurationDict["message"] as? String {
|
||||
self.message = message
|
||||
}
|
||||
if let severityString = configurationDict[$severityConfiguration] as? String {
|
||||
if let severityString = configurationDict[$severityConfiguration.key] as? String {
|
||||
try severityConfiguration.apply(configuration: severityString)
|
||||
}
|
||||
if let captureGroup = configurationDict["capture_group"] as? Int {
|
||||
|
||||
@@ -29,12 +29,27 @@ public struct SeverityLevelsConfiguration<Parent: Rule>: RuleConfiguration, Equa
|
||||
if let configurationArray = [Int].array(of: configuration), configurationArray.isNotEmpty {
|
||||
warning = configurationArray[0]
|
||||
error = (configurationArray.count > 1) ? configurationArray[1] : nil
|
||||
} else if let configDict = configuration as? [String: Int?],
|
||||
configDict.isNotEmpty, Set(configDict.keys).isSubset(of: [$warning, $error]) {
|
||||
warning = (configDict[$warning] as? Int) ?? warning
|
||||
error = configDict[$error] as? Int
|
||||
} else if let configDict = configuration as? [String: Any?] {
|
||||
if let warningValue = configDict[$warning.key] {
|
||||
if let warning = warningValue as? Int {
|
||||
self.warning = warning
|
||||
} else {
|
||||
throw Issue.invalidConfiguration(ruleID: Parent.description.identifier)
|
||||
}
|
||||
}
|
||||
if let errorValue = configDict[$error.key] {
|
||||
if errorValue == nil {
|
||||
self.error = nil
|
||||
} else if let error = errorValue as? Int {
|
||||
self.error = error
|
||||
} else {
|
||||
throw Issue.invalidConfiguration(ruleID: Parent.description.identifier)
|
||||
}
|
||||
} else {
|
||||
self.error = nil
|
||||
}
|
||||
} else {
|
||||
throw Issue.unknownConfiguration(ruleID: Parent.description.identifier)
|
||||
throw Issue.invalidConfiguration(ruleID: Parent.description.identifier)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,158 @@
|
||||
import SwiftDiagnostics
|
||||
import SwiftSyntax
|
||||
import SwiftSyntaxMacros
|
||||
|
||||
private let configurationElementName = "ConfigurationElement"
|
||||
private let acceptableByConfigurationElementName = "AcceptableByConfigurationElement"
|
||||
|
||||
private enum RuleConfigurationMacroError: String, DiagnosticMessage {
|
||||
case notStruct = "Attribute can only be applied to structs"
|
||||
case notEnum = "Attribute can only be applied to enums"
|
||||
case noStringRawType = "Attribute can only be applied to enums with a 'String' raw type"
|
||||
|
||||
var message: String {
|
||||
rawValue
|
||||
}
|
||||
|
||||
var diagnosticID: MessageID {
|
||||
MessageID(domain: "SwiftLint", id: "RuleConfigurationMacro.\(self)")
|
||||
}
|
||||
|
||||
var severity: DiagnosticSeverity {
|
||||
.error
|
||||
}
|
||||
|
||||
func diagnose(at node: some SyntaxProtocol) -> Diagnostic {
|
||||
Diagnostic(node: Syntax(node), message: self)
|
||||
}
|
||||
}
|
||||
|
||||
struct AutoApply: MemberMacro {
|
||||
static func expansion(
|
||||
of node: AttributeSyntax,
|
||||
providingMembersOf declaration: some DeclGroupSyntax,
|
||||
in context: some MacroExpansionContext
|
||||
) throws -> [DeclSyntax] {
|
||||
guard let configuration = declaration.as(StructDeclSyntax.self) else {
|
||||
context.diagnose(RuleConfigurationMacroError.notStruct.diagnose(at: declaration))
|
||||
return []
|
||||
}
|
||||
var annotatedVarDecls = configuration.memberBlock.members
|
||||
.compactMap {
|
||||
if let varDecl = $0.decl.as(VariableDeclSyntax.self),
|
||||
let annotation = varDecl.configurationElementAnnotation {
|
||||
return (varDecl, annotation)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
let firstIndexWithoutKey = annotatedVarDecls
|
||||
.partition { _, annotation in
|
||||
if case let .argumentList(arguments) = annotation.arguments {
|
||||
return arguments.contains { $0.label?.text == "key" } == true
|
||||
}
|
||||
return false
|
||||
}
|
||||
let elementNames = annotatedVarDecls.compactMap {
|
||||
$0.0.bindings.first?.pattern.as(IdentifierPatternSyntax.self)?.identifier.text
|
||||
}
|
||||
let elementsWithoutKeyUpdate = elementNames[..<firstIndexWithoutKey]
|
||||
.map {
|
||||
"""
|
||||
try \($0).apply(configuration, ruleID: Parent.identifier)
|
||||
"""
|
||||
}
|
||||
let elementsWithKeyUpdate = elementNames[firstIndexWithoutKey...]
|
||||
.map {
|
||||
"""
|
||||
try \($0).apply(configuration[$\($0).key], ruleID: Parent.identifier)
|
||||
try $\($0).performAfterParseOperations()
|
||||
"""
|
||||
}
|
||||
let configBinding = elementsWithKeyUpdate.isEmpty ? "_" : "configuration"
|
||||
return [
|
||||
"""
|
||||
mutating func apply(configuration: Any) throws {
|
||||
\(raw: elementsWithoutKeyUpdate.joined(separator: "\n"))
|
||||
guard let \(raw: configBinding) = configuration as? [String: Any] else {
|
||||
\(raw: elementsWithoutKeyUpdate.isEmpty
|
||||
? "throw Issue.invalidConfiguration(ruleID: Parent.description.identifier)"
|
||||
: "return")
|
||||
}
|
||||
\(raw: elementsWithKeyUpdate.joined(separator: "\n"))
|
||||
if !supportedKeys.isSuperset(of: configuration.keys) {
|
||||
let unknownKeys = Set(configuration.keys).subtracting(supportedKeys)
|
||||
throw Issue.invalidConfigurationKeys(unknownKeys.sorted())
|
||||
}
|
||||
}
|
||||
"""
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
struct MakeAcceptableByConfigurationElement: ExtensionMacro {
|
||||
static func expansion(
|
||||
of node: AttributeSyntax,
|
||||
attachedTo declaration: some DeclGroupSyntax,
|
||||
providingExtensionsOf type: some TypeSyntaxProtocol,
|
||||
conformingTo protocols: [TypeSyntax],
|
||||
in context: some MacroExpansionContext
|
||||
) throws -> [ExtensionDeclSyntax] {
|
||||
guard let enumDecl = declaration.as(EnumDeclSyntax.self) else {
|
||||
context.diagnose(RuleConfigurationMacroError.notEnum.diagnose(at: declaration))
|
||||
return []
|
||||
}
|
||||
guard enumDecl.hasStringRawType else {
|
||||
context.diagnose(RuleConfigurationMacroError.noStringRawType.diagnose(at: declaration))
|
||||
return []
|
||||
}
|
||||
let accessLevel = enumDecl.accessLevel
|
||||
return [
|
||||
try ExtensionDeclSyntax("""
|
||||
extension \(type): \(raw: acceptableByConfigurationElementName) {
|
||||
\(raw: accessLevel)func asOption() -> OptionType { .symbol(rawValue) }
|
||||
\(raw: accessLevel)init(fromAny value: Any, context ruleID: String) throws {
|
||||
if let value = value as? String, let newSelf = Self(rawValue: value) {
|
||||
self = newSelf
|
||||
} else {
|
||||
throw Issue.unknownConfiguration(ruleID: ruleID)
|
||||
}
|
||||
}
|
||||
}
|
||||
""")
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
private extension VariableDeclSyntax {
|
||||
var configurationElementAnnotation: AttributeSyntax? {
|
||||
let attribute = attributes.first {
|
||||
if let attr = $0.as(AttributeSyntax.self), let attrId = attr.attributeName.as(IdentifierTypeSyntax.self) {
|
||||
return attrId.name.text == configurationElementName
|
||||
}
|
||||
return false
|
||||
}
|
||||
return if case let .attribute(unwrapped) = attribute { unwrapped } else { nil }
|
||||
}
|
||||
}
|
||||
|
||||
private extension EnumDeclSyntax {
|
||||
var hasStringRawType: Bool {
|
||||
if let inheritanceClause {
|
||||
return inheritanceClause.inheritedTypes.contains {
|
||||
$0.type.as(IdentifierTypeSyntax.self)?.name.text == "String"
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
var accessLevel: String {
|
||||
modifiers.compactMap {
|
||||
switch $0.name.tokenKind {
|
||||
case .keyword(.public): "public "
|
||||
case .keyword(.package): "package "
|
||||
case .keyword(.private): "private "
|
||||
default: nil
|
||||
}
|
||||
}.first ?? ""
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
import SwiftCompilerPlugin
|
||||
import SwiftSyntaxMacros
|
||||
|
||||
@main
|
||||
struct SwiftLintCoreMacros: CompilerPlugin {
|
||||
let providingMacros: [Macro.Type] = [
|
||||
AutoApply.self,
|
||||
MakeAcceptableByConfigurationElement.self
|
||||
]
|
||||
}
|
||||
@@ -17,7 +17,7 @@ class CustomRulesTests: SwiftLintTestCase {
|
||||
var comp = Configuration(identifier: "my_custom_rule")
|
||||
comp.name = "MyCustomRule"
|
||||
comp.message = "Message"
|
||||
comp.regex = regex("regex")
|
||||
comp.regex = "regex"
|
||||
comp.severityConfiguration = SeverityConfiguration(.error)
|
||||
comp.excludedMatchKinds = SyntaxKind.allKinds.subtracting([.comment])
|
||||
var compRules = CustomRulesConfiguration()
|
||||
@@ -44,7 +44,7 @@ class CustomRulesTests: SwiftLintTestCase {
|
||||
var comp = Configuration(identifier: "my_custom_rule")
|
||||
comp.name = "MyCustomRule"
|
||||
comp.message = "Message"
|
||||
comp.regex = regex("regex")
|
||||
comp.regex = "regex"
|
||||
comp.severityConfiguration = SeverityConfiguration(.error)
|
||||
comp.excludedMatchKinds = Set<SyntaxKind>([.comment])
|
||||
var compRules = CustomRulesConfiguration()
|
||||
|
||||
@@ -43,6 +43,7 @@ class CyclomaticComplexityConfigurationTests: SwiftLintTestCase {
|
||||
let error2 = 40
|
||||
let length2 = SeverityLevelsConfiguration<CyclomaticComplexityRule>(warning: warning2, error: error2)
|
||||
let config2: [String: Int] = ["warning": warning2, "error": error2]
|
||||
let length3 = SeverityLevelsConfiguration<CyclomaticComplexityRule>(warning: warning2)
|
||||
let config3: [String: Bool] = ["ignores_case_statements": false]
|
||||
|
||||
try configuration.apply(configuration: config1)
|
||||
@@ -54,22 +55,21 @@ class CyclomaticComplexityConfigurationTests: SwiftLintTestCase {
|
||||
XCTAssertTrue(configuration.ignoresCaseStatements)
|
||||
|
||||
try configuration.apply(configuration: config3)
|
||||
XCTAssertEqual(configuration.length, length2)
|
||||
XCTAssertEqual(configuration.length, length3)
|
||||
XCTAssertFalse(configuration.ignoresCaseStatements)
|
||||
}
|
||||
|
||||
func testCyclomaticComplexityConfigurationThrowsOnBadConfigValues() {
|
||||
let badConfigs: [[String: Any]] = [
|
||||
["warning": true],
|
||||
["ignores_case_statements": 300],
|
||||
["unsupported_key": "unsupported key is unsupported"]
|
||||
["ignores_case_statements": 300]
|
||||
]
|
||||
|
||||
for badConfig in badConfigs {
|
||||
var configuration = CyclomaticComplexityConfiguration(
|
||||
length: SeverityLevelsConfiguration<CyclomaticComplexityRule>(warning: 100, error: 150)
|
||||
)
|
||||
checkError(Issue.unknownConfiguration(ruleID: CyclomaticComplexityRule.description.identifier)) {
|
||||
checkError(Issue.invalidConfiguration(ruleID: CyclomaticComplexityRule.description.identifier)) {
|
||||
try configuration.apply(configuration: badConfig)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,14 +21,14 @@ class ExplicitTypeInterfaceConfigurationTests: SwiftLintTestCase {
|
||||
|
||||
func testInvalidKeyInCustomConfiguration() {
|
||||
var config = ExplicitTypeInterfaceConfiguration()
|
||||
checkError(Issue.unknownConfiguration(ruleID: ExplicitTypeInterfaceRule.description.identifier)) {
|
||||
checkError(Issue.invalidConfigurationKeys(["invalidKey"])) {
|
||||
try config.apply(configuration: ["invalidKey": "error"])
|
||||
}
|
||||
}
|
||||
|
||||
func testInvalidTypeOfCustomConfiguration() {
|
||||
var config = ExplicitTypeInterfaceConfiguration()
|
||||
checkError(Issue.unknownConfiguration(ruleID: ExplicitTypeInterfaceRule.description.identifier)) {
|
||||
checkError(Issue.invalidConfiguration(ruleID: ExplicitTypeInterfaceRule.description.identifier)) {
|
||||
try config.apply(configuration: "invalidKey")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,8 +1,17 @@
|
||||
@testable import SwiftLintBuiltInRules
|
||||
import SwiftLintTestHelpers
|
||||
import XCTest
|
||||
|
||||
class IndentationWidthRuleTests: SwiftLintTestCase {
|
||||
// MARK: Examples
|
||||
func testInvalidIndentation() {
|
||||
var testee = IndentationWidthConfiguration()
|
||||
for indentation in [0, -1, -5] {
|
||||
checkError(Issue.invalidConfiguration(ruleID: IndentationWidthRule.description.identifier)) {
|
||||
try testee.apply(configuration: ["indentation_width": indentation])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// It's not okay to have the first line indented.
|
||||
func testFirstLineIndentation() {
|
||||
assert1Violation(in: " firstLine")
|
||||
|
||||
@@ -62,7 +62,7 @@ class LineLengthConfigurationTests: SwiftLintTestCase {
|
||||
func testLineLengthConfigurationThrowsOnBadConfig() {
|
||||
let config = "unknown"
|
||||
var configuration = LineLengthConfiguration(length: severityLevels)
|
||||
checkError(Issue.unknownConfiguration(ruleID: LineLengthRule.description.identifier)) {
|
||||
checkError(Issue.invalidConfiguration(ruleID: LineLengthRule.description.identifier)) {
|
||||
try configuration.apply(configuration: config)
|
||||
}
|
||||
}
|
||||
@@ -70,13 +70,12 @@ class LineLengthConfigurationTests: SwiftLintTestCase {
|
||||
func testLineLengthConfigurationThrowsOnBadConfigValues() {
|
||||
let badConfigs: [[String: Any]] = [
|
||||
["warning": true],
|
||||
["ignores_function_declarations": 300],
|
||||
["unsupported_key": "unsupported key is unsupported"]
|
||||
["ignores_function_declarations": 300]
|
||||
]
|
||||
|
||||
for badConfig in badConfigs {
|
||||
var configuration = LineLengthConfiguration(length: severityLevels)
|
||||
checkError(Issue.unknownConfiguration(ruleID: LineLengthRule.description.identifier)) {
|
||||
checkError(Issue.invalidConfiguration(ruleID: LineLengthRule.description.identifier)) {
|
||||
try configuration.apply(configuration: badConfig)
|
||||
}
|
||||
}
|
||||
@@ -122,7 +121,7 @@ class LineLengthConfigurationTests: SwiftLintTestCase {
|
||||
let length2 = SeverityLevelsConfiguration<LineLengthRule>(warning: warning2, error: error2)
|
||||
let config2: [String: Int] = ["warning": warning2, "error": error2]
|
||||
|
||||
let length3 = SeverityLevelsConfiguration<LineLengthRule>(warning: warning2, error: error2)
|
||||
let length3 = SeverityLevelsConfiguration<LineLengthRule>(warning: warning2)
|
||||
let config3: [String: Bool] = ["ignores_urls": false,
|
||||
"ignores_function_declarations": false,
|
||||
"ignores_comments": false]
|
||||
|
||||
@@ -1,10 +1,14 @@
|
||||
@testable import SwiftLintCore
|
||||
import SwiftLintTestHelpers
|
||||
import XCTest
|
||||
|
||||
// swiftlint:disable:next blanket_disable_command
|
||||
// swiftlint:disable let_var_whitespace
|
||||
// swiftlint:disable file_length
|
||||
|
||||
// swiftlint:disable:next type_body_length
|
||||
class RuleConfigurationDescriptionTests: XCTestCase {
|
||||
@AutoApply
|
||||
private struct TestConfiguration: RuleConfiguration {
|
||||
typealias Parent = RuleMock // swiftlint:disable:this nesting
|
||||
|
||||
@@ -13,17 +17,22 @@ class RuleConfigurationDescriptionTests: XCTestCase {
|
||||
@ConfigurationElement(key: "string")
|
||||
var string = "value"
|
||||
@ConfigurationElement(key: "symbol")
|
||||
var symbol = Symbol(value: "value")
|
||||
var symbol = try! Symbol(fromAny: "value", context: "rule") // swiftlint:disable:this force_try
|
||||
@ConfigurationElement(key: "integer")
|
||||
var integer = 2
|
||||
@ConfigurationElement(key: "nil")
|
||||
var `nil`: Int?
|
||||
@ConfigurationElement(key: "null")
|
||||
var null: Int?
|
||||
@ConfigurationElement(key: "double")
|
||||
var double = 2.1
|
||||
@ConfigurationElement(key: "severity")
|
||||
var severity = ViolationSeverity.warning
|
||||
@ConfigurationElement(key: "list")
|
||||
var list: [OptionType?] = [.flag(true), .string("value")]
|
||||
@ConfigurationElement(
|
||||
key: "list",
|
||||
postprocessor: { list in list = list.map { $0.uppercased() } }
|
||||
)
|
||||
var list = ["string1", "string2"]
|
||||
@ConfigurationElement(key: "set")
|
||||
var set: Set<Int> = [1, 2, 3]
|
||||
@ConfigurationElement
|
||||
var severityConfig = SeverityConfiguration<Parent>(.error)
|
||||
@ConfigurationElement(key: "SEVERITY")
|
||||
@@ -33,8 +42,6 @@ class RuleConfigurationDescriptionTests: XCTestCase {
|
||||
@ConfigurationElement(key: "levels")
|
||||
var nestedSeverityLevels = SeverityLevelsConfiguration<Parent>(warning: 3, error: nil)
|
||||
|
||||
mutating func apply(configuration: Any) throws {}
|
||||
|
||||
func isEqualTo(_ ruleConfiguration: some RuleConfiguration) -> Bool { false }
|
||||
}
|
||||
|
||||
@@ -49,7 +56,8 @@ class RuleConfigurationDescriptionTests: XCTestCase {
|
||||
integer: 2; \
|
||||
double: 2.1; \
|
||||
severity: warning; \
|
||||
list: [true, "value"]; \
|
||||
list: ["STRING1", "STRING2"]; \
|
||||
set: [1, 2, 3]; \
|
||||
severity: error; \
|
||||
SEVERITY: warning; \
|
||||
warning: 1; \
|
||||
@@ -116,7 +124,15 @@ class RuleConfigurationDescriptionTests: XCTestCase {
|
||||
list
|
||||
</td>
|
||||
<td>
|
||||
[true, "value"]
|
||||
["STRING1", "STRING2"]
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
set
|
||||
</td>
|
||||
<td>
|
||||
[1, 2, 3]
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
@@ -184,7 +200,8 @@ class RuleConfigurationDescriptionTests: XCTestCase {
|
||||
integer: 2
|
||||
double: 2.1
|
||||
severity: warning
|
||||
list: [true, "value"]
|
||||
list: ["STRING1", "STRING2"]
|
||||
set: [1, 2, 3]
|
||||
severity: error
|
||||
SEVERITY: warning
|
||||
warning: 1
|
||||
@@ -441,6 +458,52 @@ class RuleConfigurationDescriptionTests: XCTestCase {
|
||||
""")
|
||||
}
|
||||
|
||||
func testUpdate() throws {
|
||||
var configuration = TestConfiguration()
|
||||
|
||||
try configuration.apply(configuration: [
|
||||
"flag": false,
|
||||
"string": "new value",
|
||||
"symbol": "new symbol",
|
||||
"integer": 5,
|
||||
"null": 0,
|
||||
"double": 5.1,
|
||||
"severity": "error",
|
||||
"list": ["string3", "string4"],
|
||||
"set": [4, 5, 6],
|
||||
"SEVERITY": "error",
|
||||
"warning": 12,
|
||||
"levels": ["warning": 6, "error": 7]
|
||||
])
|
||||
|
||||
XCTAssertFalse(configuration.flag)
|
||||
XCTAssertEqual(configuration.string, "new value")
|
||||
XCTAssertEqual(configuration.symbol, try Symbol(fromAny: "new symbol", context: "rule"))
|
||||
XCTAssertEqual(configuration.integer, 5)
|
||||
XCTAssertEqual(configuration.null, 0)
|
||||
XCTAssertEqual(configuration.double, 5.1)
|
||||
XCTAssertEqual(configuration.severity, .error)
|
||||
XCTAssertEqual(configuration.list, ["STRING3", "STRING4"])
|
||||
XCTAssertEqual(configuration.set, [4, 5, 6])
|
||||
XCTAssertEqual(configuration.severityConfig, .error)
|
||||
XCTAssertEqual(configuration.renamedSeverityConfig, .error)
|
||||
XCTAssertEqual(configuration.inlinedSeverityLevels, SeverityLevelsConfiguration(warning: 12))
|
||||
XCTAssertEqual(configuration.nestedSeverityLevels, SeverityLevelsConfiguration(warning: 6, error: 7))
|
||||
}
|
||||
|
||||
func testInvalidKeys() throws {
|
||||
var configuration = TestConfiguration()
|
||||
|
||||
checkError(Issue.invalidConfigurationKeys(["unknown", "unsupported"])) {
|
||||
try configuration.apply(configuration: [
|
||||
"severity": "error",
|
||||
"warning": 3,
|
||||
"unknown": 1,
|
||||
"unsupported": true
|
||||
])
|
||||
}
|
||||
}
|
||||
|
||||
private func description(@RuleConfigurationDescriptionBuilder _ content: () -> RuleConfigurationDescription)
|
||||
-> RuleConfigurationDescription { content() }
|
||||
}
|
||||
|
||||
@@ -37,7 +37,7 @@ class RuleConfigurationTests: SwiftLintTestCase {
|
||||
func testNestingConfigurationThrowsOnBadConfig() {
|
||||
let config = 17
|
||||
var nestingConfig = defaultNestingConfiguration
|
||||
checkError(Issue.unknownConfiguration(ruleID: NestingRule.description.identifier)) {
|
||||
checkError(Issue.invalidConfiguration(ruleID: NestingRule.description.identifier)) {
|
||||
try nestingConfig.apply(configuration: config)
|
||||
}
|
||||
}
|
||||
@@ -120,7 +120,7 @@ class RuleConfigurationTests: SwiftLintTestCase {
|
||||
let config = "unknown"
|
||||
var configuration = TrailingWhitespaceConfiguration(ignoresEmptyLines: false,
|
||||
ignoresComments: true)
|
||||
checkError(Issue.unknownConfiguration(ruleID: TrailingWhitespaceRule.description.identifier)) {
|
||||
checkError(Issue.invalidConfiguration(ruleID: TrailingWhitespaceRule.description.identifier)) {
|
||||
try configuration.apply(configuration: config)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -78,7 +78,7 @@ class RuleTests: SwiftLintTestCase {
|
||||
func testSeverityLevelRuleInitsWithConfigDictionary() {
|
||||
let config = ["warning": 17, "error": 7]
|
||||
let rule = try? RuleWithLevelsMock(configuration: config)
|
||||
var comp = RuleWithLevelsMock()
|
||||
let comp = RuleWithLevelsMock()
|
||||
comp.configuration.warning = 17
|
||||
comp.configuration.error = 7
|
||||
XCTAssertEqual(rule?.isEqualTo(comp), true)
|
||||
@@ -87,7 +87,7 @@ class RuleTests: SwiftLintTestCase {
|
||||
func testSeverityLevelRuleInitsWithWarningOnlyConfigDictionary() {
|
||||
let config = ["warning": 17]
|
||||
let rule = try? RuleWithLevelsMock(configuration: config)
|
||||
var comp = RuleWithLevelsMock()
|
||||
let comp = RuleWithLevelsMock()
|
||||
comp.configuration.warning = 17
|
||||
comp.configuration.error = nil
|
||||
XCTAssertEqual(rule?.isEqualTo(comp), true)
|
||||
@@ -96,7 +96,7 @@ class RuleTests: SwiftLintTestCase {
|
||||
func testSeverityLevelRuleInitsWithErrorOnlyConfigDictionary() {
|
||||
let config = ["error": 17]
|
||||
let rule = try? RuleWithLevelsMock(configuration: config)
|
||||
var comp = RuleWithLevelsMock()
|
||||
let comp = RuleWithLevelsMock()
|
||||
comp.configuration.error = 17
|
||||
XCTAssertEqual(rule?.isEqualTo(comp), true)
|
||||
}
|
||||
@@ -104,7 +104,7 @@ class RuleTests: SwiftLintTestCase {
|
||||
func testSeverityLevelRuleInitsWithConfigArray() {
|
||||
let config = [17, 7] as Any
|
||||
let rule = try? RuleWithLevelsMock(configuration: config)
|
||||
var comp = RuleWithLevelsMock()
|
||||
let comp = RuleWithLevelsMock()
|
||||
comp.configuration.warning = 17
|
||||
comp.configuration.error = 7
|
||||
XCTAssertEqual(rule?.isEqualTo(comp), true)
|
||||
@@ -113,7 +113,7 @@ class RuleTests: SwiftLintTestCase {
|
||||
func testSeverityLevelRuleInitsWithSingleValueConfigArray() {
|
||||
let config = [17] as Any
|
||||
let rule = try? RuleWithLevelsMock(configuration: config)
|
||||
var comp = RuleWithLevelsMock()
|
||||
let comp = RuleWithLevelsMock()
|
||||
comp.configuration.warning = 17
|
||||
comp.configuration.error = nil
|
||||
XCTAssertEqual(rule?.isEqualTo(comp), true)
|
||||
@@ -122,7 +122,7 @@ class RuleTests: SwiftLintTestCase {
|
||||
func testSeverityLevelRuleInitsWithLiteral() {
|
||||
let config = 17 as Any
|
||||
let rule = try? RuleWithLevelsMock(configuration: config)
|
||||
var comp = RuleWithLevelsMock()
|
||||
let comp = RuleWithLevelsMock()
|
||||
comp.configuration.warning = 17
|
||||
comp.configuration.error = nil
|
||||
XCTAssertEqual(rule?.isEqualTo(comp), true)
|
||||
|
||||
@@ -3,21 +3,17 @@ import XCTest
|
||||
|
||||
class UnusedDeclarationConfigurationTests: XCTestCase {
|
||||
func testParseConfiguration() throws {
|
||||
var testee = UnusedDeclarationConfiguration(
|
||||
severityConfiguration: .warning,
|
||||
includePublicAndOpen: false,
|
||||
relatedUSRsToSkip: []
|
||||
)
|
||||
var testee = UnusedDeclarationConfiguration()
|
||||
let config = [
|
||||
"severity": "error",
|
||||
"severity": "warning",
|
||||
"include_public_and_open": true,
|
||||
"related_usrs_to_skip": ["a", "b"]
|
||||
] as [String: Any]
|
||||
|
||||
try testee.apply(configuration: config)
|
||||
|
||||
XCTAssertEqual(testee.severityConfiguration.severity, .error)
|
||||
XCTAssertEqual(testee.severityConfiguration.severity, .warning)
|
||||
XCTAssertTrue(testee.includePublicAndOpen)
|
||||
XCTAssertEqual(testee.relatedUSRsToSkip, ["a", "b"])
|
||||
XCTAssertEqual(testee.relatedUSRsToSkip, ["a", "b", "s:7SwiftUI15PreviewProviderP"])
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user