mirror of
https://github.com/realm/SwiftLint.git
synced 2026-05-07 20:12:49 +00:00
Allow to configure only severity in a short form for every rule with a severity (#5509)
The README states that a configuration like `attributes: error` is valid to only set a different severity level for a rule (the `attributes` rule here). This was previously only possible for rules that were accompanied by a plain severity configuration. I don't think this broke with the automatic parsing code generation. For some rules it might have worked before, for others not. This change makes it consistently working for all rules.
This commit is contained in:
@@ -31,6 +31,11 @@
|
||||
[Julien Baillon](https://github.com/julien-baillon)
|
||||
[#5372](https://github.com/realm/SwiftLint/issues/5372)
|
||||
|
||||
* Allow to set the severity of rules (if they have one) in the short form
|
||||
`rule_name: warning|error` provided that no other attributes need to be
|
||||
configured.
|
||||
[SimplyDanny](https://github.com/SimplyDanny)
|
||||
|
||||
* Add new `ignore_one_liners` option to `switch_case_alignment`
|
||||
rule to ignore switch statements written in a single line.
|
||||
[tonell-m](https://github.com/tonell-m)
|
||||
|
||||
@@ -35,9 +35,27 @@ enum AutoApply: MemberMacro {
|
||||
let elementNames = annotatedVarDecls.compactMap {
|
||||
$0.0.bindings.first?.pattern.as(IdentifierPatternSyntax.self)?.identifier.text
|
||||
}
|
||||
let nonInlinedOptions = elementNames[..<firstIndexWithoutKey]
|
||||
var inlinedOptions = elementNames[firstIndexWithoutKey...]
|
||||
let isSeverityBased = configuration.inheritanceClause?.inheritedTypes.contains {
|
||||
$0.type.as(IdentifierTypeSyntax.self)?.name.text == "SeverityBasedRuleConfiguration"
|
||||
}
|
||||
if isSeverityBased == true {
|
||||
if nonInlinedOptions.contains("severityConfiguration") {
|
||||
inlinedOptions.append("severityConfiguration")
|
||||
} else {
|
||||
context.diagnose(SwiftLintCoreMacroError.severityBasedWithoutProperty.diagnose(at: configuration.name))
|
||||
}
|
||||
}
|
||||
return [
|
||||
DeclSyntax(try FunctionDeclSyntax("mutating func apply(configuration: Any) throws") {
|
||||
let inlinedOptions = elementNames[firstIndexWithoutKey...]
|
||||
for option in nonInlinedOptions {
|
||||
"""
|
||||
if $\(raw: option).key.isEmpty {
|
||||
$\(raw: option).key = "\(raw: option.snakeCased)"
|
||||
}
|
||||
"""
|
||||
}
|
||||
for option in inlinedOptions {
|
||||
"""
|
||||
do {
|
||||
@@ -51,16 +69,11 @@ enum AutoApply: MemberMacro {
|
||||
"""
|
||||
guard let configuration = configuration as? [String: Any] else {
|
||||
\(raw: inlinedOptions.isEmpty
|
||||
? "throw Issue.invalidConfiguration(ruleID: Parent.description.identifier)"
|
||||
? "throw Issue.invalidConfiguration(ruleID: Parent.identifier)"
|
||||
: "return")
|
||||
}
|
||||
"""
|
||||
for option in elementNames[..<firstIndexWithoutKey] {
|
||||
"""
|
||||
if $\(raw: option).key.isEmpty {
|
||||
$\(raw: option).key = "\(raw: option.snakeCased)"
|
||||
}
|
||||
"""
|
||||
for option in nonInlinedOptions {
|
||||
"""
|
||||
try \(raw: option).apply(configuration[$\(raw: option).key], ruleID: Parent.identifier)
|
||||
"""
|
||||
|
||||
@@ -14,6 +14,9 @@ struct SwiftLintCoreMacros: CompilerPlugin {
|
||||
|
||||
enum SwiftLintCoreMacroError: String, DiagnosticMessage {
|
||||
case notStruct = "Attribute can only be applied to structs"
|
||||
case severityBasedWithoutProperty = """
|
||||
Severity-based configuration without a 'severityConfiguration' property is invalid
|
||||
"""
|
||||
case notEnum = "Attribute can only be applied to enums"
|
||||
case noStringRawType = "Attribute can only be applied to enums with a 'String' raw type"
|
||||
case noBooleanLiteral = "Macro argument must be a boolean literal"
|
||||
|
||||
@@ -38,7 +38,7 @@ final class AutoApplyTests: XCTestCase {
|
||||
|
||||
mutating func apply(configuration: Any) throws {
|
||||
guard let configuration = configuration as? [String: Any] else {
|
||||
throw Issue.invalidConfiguration(ruleID: Parent.description.identifier)
|
||||
throw Issue.invalidConfiguration(ruleID: Parent.identifier)
|
||||
}
|
||||
if !supportedKeys.isSuperset(of: configuration.keys) {
|
||||
let unknownKeys = Set(configuration.keys).subtracting(supportedKeys)
|
||||
@@ -58,7 +58,7 @@ final class AutoApplyTests: XCTestCase {
|
||||
struct S {
|
||||
@ConfigurationElement
|
||||
var eA = 1
|
||||
@ConfigurationElement(value: 7)
|
||||
@ConfigurationElement(key: "name")
|
||||
var eB = 2
|
||||
}
|
||||
""",
|
||||
@@ -67,21 +67,21 @@ final class AutoApplyTests: XCTestCase {
|
||||
struct S {
|
||||
@ConfigurationElement
|
||||
var eA = 1
|
||||
@ConfigurationElement(value: 7)
|
||||
@ConfigurationElement(key: "name")
|
||||
var eB = 2
|
||||
|
||||
mutating func apply(configuration: Any) throws {
|
||||
guard let configuration = configuration as? [String: Any] else {
|
||||
throw Issue.invalidConfiguration(ruleID: Parent.description.identifier)
|
||||
}
|
||||
if $eA.key.isEmpty {
|
||||
$eA.key = "e_a"
|
||||
}
|
||||
try eA.apply(configuration[$eA.key], ruleID: Parent.identifier)
|
||||
try $eA.performAfterParseOperations()
|
||||
if $eB.key.isEmpty {
|
||||
$eB.key = "e_b"
|
||||
}
|
||||
guard let configuration = configuration as? [String: Any] else {
|
||||
throw Issue.invalidConfiguration(ruleID: Parent.identifier)
|
||||
}
|
||||
try eA.apply(configuration[$eA.key], ruleID: Parent.identifier)
|
||||
try $eA.performAfterParseOperations()
|
||||
try eB.apply(configuration[$eB.key], ruleID: Parent.identifier)
|
||||
try $eB.performAfterParseOperations()
|
||||
if !supportedKeys.isSuperset(of: configuration.keys) {
|
||||
@@ -119,6 +119,12 @@ final class AutoApplyTests: XCTestCase {
|
||||
var eC = 3
|
||||
|
||||
mutating func apply(configuration: Any) throws {
|
||||
if $eA.key.isEmpty {
|
||||
$eA.key = "e_a"
|
||||
}
|
||||
if $eC.key.isEmpty {
|
||||
$eC.key = "e_c"
|
||||
}
|
||||
do {
|
||||
try eB.apply(configuration, ruleID: Parent.identifier)
|
||||
try $eB.performAfterParseOperations()
|
||||
@@ -128,14 +134,8 @@ final class AutoApplyTests: XCTestCase {
|
||||
guard let configuration = configuration as? [String: Any] else {
|
||||
return
|
||||
}
|
||||
if $eA.key.isEmpty {
|
||||
$eA.key = "e_a"
|
||||
}
|
||||
try eA.apply(configuration[$eA.key], ruleID: Parent.identifier)
|
||||
try $eA.performAfterParseOperations()
|
||||
if $eC.key.isEmpty {
|
||||
$eC.key = "e_c"
|
||||
}
|
||||
try eC.apply(configuration[$eC.key], ruleID: Parent.identifier)
|
||||
try $eC.performAfterParseOperations()
|
||||
if !supportedKeys.isSuperset(of: configuration.keys) {
|
||||
@@ -148,4 +148,88 @@ final class AutoApplyTests: XCTestCase {
|
||||
macros: macros
|
||||
)
|
||||
}
|
||||
|
||||
func testSeverityBasedConfigurationWithoutSeverityProperty() {
|
||||
assertMacroExpansion(
|
||||
"""
|
||||
@AutoApply
|
||||
struct S: SeverityBasedRuleConfiguration {
|
||||
}
|
||||
""",
|
||||
expandedSource:
|
||||
"""
|
||||
struct S: SeverityBasedRuleConfiguration {
|
||||
|
||||
mutating func apply(configuration: Any) throws {
|
||||
guard let configuration = configuration as? [String: Any] else {
|
||||
throw Issue.invalidConfiguration(ruleID: Parent.identifier)
|
||||
}
|
||||
if !supportedKeys.isSuperset(of: configuration.keys) {
|
||||
let unknownKeys = Set(configuration.keys).subtracting(supportedKeys)
|
||||
throw Issue.invalidConfigurationKeys(ruleID: Parent.identifier, keys: unknownKeys)
|
||||
}
|
||||
}
|
||||
}
|
||||
""",
|
||||
diagnostics: [
|
||||
DiagnosticSpec(
|
||||
message: SwiftLintCoreMacroError.severityBasedWithoutProperty.message,
|
||||
line: 2,
|
||||
column: 8
|
||||
)
|
||||
],
|
||||
macros: macros)
|
||||
}
|
||||
|
||||
func testSeverityAppliedTwice() {
|
||||
// swiftlint:disable line_length
|
||||
assertMacroExpansion(
|
||||
"""
|
||||
@AutoApply
|
||||
struct S: SeverityBasedRuleConfiguration {
|
||||
@ConfigurationElement
|
||||
var severityConfiguration = .warning
|
||||
@ConfigurationElement
|
||||
var foo = 2
|
||||
}
|
||||
""",
|
||||
expandedSource:
|
||||
"""
|
||||
struct S: SeverityBasedRuleConfiguration {
|
||||
@ConfigurationElement
|
||||
var severityConfiguration = .warning
|
||||
@ConfigurationElement
|
||||
var foo = 2
|
||||
|
||||
mutating func apply(configuration: Any) throws {
|
||||
if $severityConfiguration.key.isEmpty {
|
||||
$severityConfiguration.key = "severity_configuration"
|
||||
}
|
||||
if $foo.key.isEmpty {
|
||||
$foo.key = "foo"
|
||||
}
|
||||
do {
|
||||
try severityConfiguration.apply(configuration, ruleID: Parent.identifier)
|
||||
try $severityConfiguration.performAfterParseOperations()
|
||||
} catch let issue as Issue where issue == Issue.nothingApplied(ruleID: Parent.identifier) {
|
||||
// Acceptable. Continue.
|
||||
}
|
||||
guard let configuration = configuration as? [String: Any] else {
|
||||
return
|
||||
}
|
||||
try severityConfiguration.apply(configuration[$severityConfiguration.key], ruleID: Parent.identifier)
|
||||
try $severityConfiguration.performAfterParseOperations()
|
||||
try foo.apply(configuration[$foo.key], ruleID: Parent.identifier)
|
||||
try $foo.performAfterParseOperations()
|
||||
if !supportedKeys.isSuperset(of: configuration.keys) {
|
||||
let unknownKeys = Set(configuration.keys).subtracting(supportedKeys)
|
||||
throw Issue.invalidConfigurationKeys(ruleID: Parent.identifier, keys: unknownKeys)
|
||||
}
|
||||
}
|
||||
}
|
||||
""",
|
||||
macros: macros
|
||||
)
|
||||
// swiftlint:enable line_length
|
||||
}
|
||||
}
|
||||
|
||||
@@ -46,7 +46,7 @@ class RuleConfigurationDescriptionTests: XCTestCase {
|
||||
// swiftlint:disable:next function_body_length
|
||||
func testDescriptionFromConfiguration() throws {
|
||||
var configuration = TestConfiguration()
|
||||
try configuration.apply(configuration: [:])
|
||||
try configuration.apply(configuration: Void()) // Configure to set keys.
|
||||
let description = RuleConfigurationDescription.from(configuration: configuration)
|
||||
|
||||
XCTAssertEqual(description.oneLiner(), """
|
||||
|
||||
@@ -42,6 +42,13 @@ class RuleConfigurationTests: SwiftLintTestCase {
|
||||
}
|
||||
}
|
||||
|
||||
func testSeverityWorksAsOnlyParameter() throws {
|
||||
var config = AttributesConfiguration()
|
||||
XCTAssertEqual(config.severity, .warning)
|
||||
try config.apply(configuration: "error")
|
||||
XCTAssertEqual(config.severity, .error)
|
||||
}
|
||||
|
||||
func testSeverityConfigurationFromString() {
|
||||
let config = "Warning"
|
||||
let comp = SeverityConfiguration<RuleMock>(.warning)
|
||||
|
||||
Reference in New Issue
Block a user