From 38a3eddb435019894a0f4f3b496be6905c9443ad Mon Sep 17 00:00:00 2001 From: Zev Eisenberg Date: Wed, 23 Sep 2026 16:56:44 -0400 Subject: [PATCH 1/7] Add new sorted_collection_members rule to optionally require that the members of a collection literal remain sorted. --- .swiftlint.yml | 1 + .../Models/BuiltInRules.swift | 1 + ...SortedCollectionMembersConfiguration.swift | 10 +++ .../Style/SortedCollectionMembersRule.swift | 85 +++++++++++++++++++ .../SortedCollectionMembersRuleTests.swift | 12 +++ Tests/GeneratedTests/GeneratedTests_08.swift | 12 +-- Tests/GeneratedTests/GeneratedTests_09.swift | 16 ++-- Tests/GeneratedTests/GeneratedTests_10.swift | 16 ++-- Tests/GeneratedTests/GeneratedTests_11.swift | 8 ++ .../Resources/default_rule_configurations.yml | 6 ++ 10 files changed, 145 insertions(+), 22 deletions(-) create mode 100644 Source/SwiftLintBuiltInRules/Rules/RuleConfigurations/SortedCollectionMembersConfiguration.swift create mode 100644 Source/SwiftLintBuiltInRules/Rules/Style/SortedCollectionMembersRule.swift create mode 100644 Tests/BuiltInRulesTests/SortedCollectionMembersRuleTests.swift diff --git a/.swiftlint.yml b/.swiftlint.yml index 0f6d61786c..17319cdaf1 100644 --- a/.swiftlint.yml +++ b/.swiftlint.yml @@ -44,6 +44,7 @@ disabled_rules: - prefer_nimble - prefixed_toplevel_constant - required_deinit + - sorted_collection_members - sorted_enum_cases - strict_fileprivate - switch_case_on_newline diff --git a/Source/SwiftLintBuiltInRules/Models/BuiltInRules.swift b/Source/SwiftLintBuiltInRules/Models/BuiltInRules.swift index 51b2a4eee4..99780db068 100644 --- a/Source/SwiftLintBuiltInRules/Models/BuiltInRules.swift +++ b/Source/SwiftLintBuiltInRules/Models/BuiltInRules.swift @@ -199,6 +199,7 @@ public let builtInRules: [any Rule.Type] = [ ShorthandOperatorRule.self, ShorthandOptionalBindingRule.self, SingleTestClassRule.self, + SortedCollectionMembersRule.self, SortedEnumCasesRule.self, SortedFirstLastRule.self, SortedImportsRule.self, diff --git a/Source/SwiftLintBuiltInRules/Rules/RuleConfigurations/SortedCollectionMembersConfiguration.swift b/Source/SwiftLintBuiltInRules/Rules/RuleConfigurations/SortedCollectionMembersConfiguration.swift new file mode 100644 index 0000000000..a85ed0bb09 --- /dev/null +++ b/Source/SwiftLintBuiltInRules/Rules/RuleConfigurations/SortedCollectionMembersConfiguration.swift @@ -0,0 +1,10 @@ +import SwiftLintCore + +@AutoConfigParser +struct SortedCollectionMembersConfiguration: SeverityBasedRuleConfiguration { + @ConfigurationElement(key: "severity") + private(set) var severityConfiguration = SeverityConfiguration(.warning) + + @ConfigurationElement(key: "reverse") + private(set) var reverse = false +} diff --git a/Source/SwiftLintBuiltInRules/Rules/Style/SortedCollectionMembersRule.swift b/Source/SwiftLintBuiltInRules/Rules/Style/SortedCollectionMembersRule.swift new file mode 100644 index 0000000000..a12cb289c7 --- /dev/null +++ b/Source/SwiftLintBuiltInRules/Rules/Style/SortedCollectionMembersRule.swift @@ -0,0 +1,85 @@ +import Foundation +import SwiftLintCore +import SwiftSyntax + +@SwiftSyntaxRule(optIn: true) +struct SortedCollectionMembersRule: Rule { + var configuration = SortedCollectionMembersConfiguration() + + static let description: RuleDescription = RuleDescription( + identifier: "sorted_collection_members", + name: "Sorted Collection Members", + description: "Please keep the elements of this collection literal sorted", + kind: .style, + nonTriggeringExamples: #examples([ + "[1, 2, 3]", + "[a, b, c]", + "[]", + "[1]", + "[a]", + """ + ["a", "b", "c"] + """, + """ + ["a"] + """, + """ + [ + .thingA, + .thingB, + // comments are ignored in sort checking + .thingC, + ] + """ + ]), + triggeringExamples: #examples([ + "[1, ↓3, 2]", + """ + [↓"b", "c", "a"] + """, + """ + [ + ↓.thingBNoComment, + .thingCNoComment, + .thingANoComment, + ] + """, + """ + [ + ↓.thingBWithComment, + // Comments are not counted when evaluating sort order + .thingCWithComment, + .thingAWithComment, + ] + """, + ]) + ) +} + +// TODO: add dictionary literal support +// TODO: support reverse sort +// TODO: support case-insensitive sort? +// TODO: seems like /*>*/ syntax has an off-by-one compared with ↓ syntax +// TODO: see if we can enforce that this rule must be opt-in, and shouldn't be enabled globally. +// TODO: find a way to opt out of visiting nodes that do not apply to us. + +private extension SortedCollectionMembersRule { + final class Visitor: ViolationsSyntaxVisitor { + override func visit(_ node: ArrayExprSyntax) -> SyntaxVisitorContinueKind { + let sortedNames = node.elements + .map(\.expression.trimmedDescription) + .sorted() + + let originalAndSorted = zip(zip(node.elements.indices, node.elements), sortedNames) + for ((originalIndex, originalElement), sortedName) in originalAndSorted { + if originalElement.expression.trimmedDescription != sortedName { + violations.append(node.elements[originalIndex].positionAfterSkippingLeadingTrivia) + // break on the first sorting violation because everything after it is necessarily not sorted + break + } + } + + return .visitChildren + } + } +} diff --git a/Tests/BuiltInRulesTests/SortedCollectionMembersRuleTests.swift b/Tests/BuiltInRulesTests/SortedCollectionMembersRuleTests.swift new file mode 100644 index 0000000000..671ccd4857 --- /dev/null +++ b/Tests/BuiltInRulesTests/SortedCollectionMembersRuleTests.swift @@ -0,0 +1,12 @@ +import TestHelpers +import Testing + +@testable import SwiftLintBuiltInRules + +@Suite(.rulesRegistered) +struct SortedCollectionMembersRuleTests { + @Test + func verify() { + verifyRule(SortedCollectionMembersRule.description, ruleConfiguration: []) + } +} diff --git a/Tests/GeneratedTests/GeneratedTests_08.swift b/Tests/GeneratedTests/GeneratedTests_08.swift index 9a9e613da4..cfb31ecf08 100644 --- a/Tests/GeneratedTests/GeneratedTests_08.swift +++ b/Tests/GeneratedTests/GeneratedTests_08.swift @@ -186,25 +186,25 @@ struct SingleTestClassRuleGeneratedTests { } @Suite(.rulesRegistered) -struct SortedEnumCasesRuleGeneratedTests { +struct SortedCollectionMembersRuleGeneratedTests { @Test func withDefaultConfiguration() { - verifyRule(SortedEnumCasesRule.description) + verifyRule(SortedCollectionMembersRule.description) } } @Suite(.rulesRegistered) -struct SortedFirstLastRuleGeneratedTests { +struct SortedEnumCasesRuleGeneratedTests { @Test func withDefaultConfiguration() { - verifyRule(SortedFirstLastRule.description) + verifyRule(SortedEnumCasesRule.description) } } @Suite(.rulesRegistered) -struct SortedImportsRuleGeneratedTests { +struct SortedFirstLastRuleGeneratedTests { @Test func withDefaultConfiguration() { - verifyRule(SortedImportsRule.description) + verifyRule(SortedFirstLastRule.description) } } diff --git a/Tests/GeneratedTests/GeneratedTests_09.swift b/Tests/GeneratedTests/GeneratedTests_09.swift index 63724b0191..f891cb0f10 100644 --- a/Tests/GeneratedTests/GeneratedTests_09.swift +++ b/Tests/GeneratedTests/GeneratedTests_09.swift @@ -9,6 +9,14 @@ import Testing @testable import SwiftLintBuiltInRules @testable import SwiftLintCore +@Suite(.rulesRegistered) +struct SortedImportsRuleGeneratedTests { + @Test + func withDefaultConfiguration() { + verifyRule(SortedImportsRule.description) + } +} + @Suite(.rulesRegistered) struct StatementPositionRuleGeneratedTests { @Test @@ -200,11 +208,3 @@ struct UnhandledThrowingTaskRuleGeneratedTests { verifyRule(UnhandledThrowingTaskRule.description) } } - -@Suite(.rulesRegistered) -struct UnneededBreakInSwitchRuleGeneratedTests { - @Test - func withDefaultConfiguration() { - verifyRule(UnneededBreakInSwitchRule.description) - } -} diff --git a/Tests/GeneratedTests/GeneratedTests_10.swift b/Tests/GeneratedTests/GeneratedTests_10.swift index 6e8193c399..6b51b6d07c 100644 --- a/Tests/GeneratedTests/GeneratedTests_10.swift +++ b/Tests/GeneratedTests/GeneratedTests_10.swift @@ -9,6 +9,14 @@ import Testing @testable import SwiftLintBuiltInRules @testable import SwiftLintCore +@Suite(.rulesRegistered) +struct UnneededBreakInSwitchRuleGeneratedTests { + @Test + func withDefaultConfiguration() { + verifyRule(UnneededBreakInSwitchRule.description) + } +} + @Suite(.rulesRegistered) struct UnneededEscapingRuleGeneratedTests { @Test @@ -200,11 +208,3 @@ struct VoidFunctionInTernaryConditionRuleGeneratedTests { verifyRule(VoidFunctionInTernaryConditionRule.description) } } - -@Suite(.rulesRegistered) -struct VoidReturnRuleGeneratedTests { - @Test - func withDefaultConfiguration() { - verifyRule(VoidReturnRule.description) - } -} diff --git a/Tests/GeneratedTests/GeneratedTests_11.swift b/Tests/GeneratedTests/GeneratedTests_11.swift index 61a9b2b37e..8d40b52c40 100644 --- a/Tests/GeneratedTests/GeneratedTests_11.swift +++ b/Tests/GeneratedTests/GeneratedTests_11.swift @@ -9,6 +9,14 @@ import Testing @testable import SwiftLintBuiltInRules @testable import SwiftLintCore +@Suite(.rulesRegistered) +struct VoidReturnRuleGeneratedTests { + @Test + func withDefaultConfiguration() { + verifyRule(VoidReturnRule.description) + } +} + @Suite(.rulesRegistered) struct WeakDelegateRuleGeneratedTests { @Test diff --git a/Tests/IntegrationTests/Resources/default_rule_configurations.yml b/Tests/IntegrationTests/Resources/default_rule_configurations.yml index cc2f59c8b2..3b1db7c8f0 100644 --- a/Tests/IntegrationTests/Resources/default_rule_configurations.yml +++ b/Tests/IntegrationTests/Resources/default_rule_configurations.yml @@ -1141,6 +1141,12 @@ single_test_class: meta: opt-in: true correctable: false +sorted_collection_members: + severity: warning + reverse: false + meta: + opt-in: true + correctable: false sorted_enum_cases: severity: warning meta: From 4961292189dcf6e0c5a2fc664a60469e5f0dab37 Mon Sep 17 00:00:00 2001 From: Zev Eisenberg Date: Wed, 23 Sep 2026 17:07:04 -0400 Subject: [PATCH 2/7] Support reversible sorting. --- .../Style/SortedCollectionMembersRule.swift | 37 ++++++++++++++++++- 1 file changed, 35 insertions(+), 2 deletions(-) diff --git a/Source/SwiftLintBuiltInRules/Rules/Style/SortedCollectionMembersRule.swift b/Source/SwiftLintBuiltInRules/Rules/Style/SortedCollectionMembersRule.swift index a12cb289c7..8c6ae34f54 100644 --- a/Source/SwiftLintBuiltInRules/Rules/Style/SortedCollectionMembersRule.swift +++ b/Source/SwiftLintBuiltInRules/Rules/Style/SortedCollectionMembersRule.swift @@ -14,6 +14,7 @@ struct SortedCollectionMembersRule: Rule { nonTriggeringExamples: #examples([ "[1, 2, 3]", "[a, b, c]", + "[c, b, a]".asExample(configuration: reverseSort), "[]", "[1]", "[a]", @@ -21,6 +22,9 @@ struct SortedCollectionMembersRule: Rule { ["a", "b", "c"] """, """ + ["c", "b", "a"] + """.asExample(configuration: reverseSort), + """ ["a"] """, """ @@ -30,14 +34,27 @@ struct SortedCollectionMembersRule: Rule { // comments are ignored in sort checking .thingC, ] + """, """ + [ + .thingC, + .thingB, + // comments are ignored in sort checking + .thingA, + ] + """.asExample(configuration: reverseSort) ]), triggeringExamples: #examples([ "[1, ↓3, 2]", + "[↓1, 3, 2]".asExample(configuration: reverseSort), + "[↓1, 2, 3]".asExample(configuration: reverseSort), """ [↓"b", "c", "a"] """, """ + [↓"b", "c", "a"] + """.asExample(configuration: reverseSort), + """ [ ↓.thingBNoComment, .thingCNoComment, @@ -45,6 +62,13 @@ struct SortedCollectionMembersRule: Rule { ] """, """ + [ + ↓.thingANoComment, + .thingBNoComment, + .thingCNoComment, + ] + """.asExample(configuration: reverseSort), + """ [ ↓.thingBWithComment, // Comments are not counted when evaluating sort order @@ -52,12 +76,21 @@ struct SortedCollectionMembersRule: Rule { .thingAWithComment, ] """, + """ + [ + ↓.thingAWithComment, + .thingBWithComment, + // Comments are not counted when evaluating sort order + .thingCWithComment, + ] + """.asExample(configuration: reverseSort), ]) ) } +private let reverseSort: [String: any Sendable] = ["reverse": true] + // TODO: add dictionary literal support -// TODO: support reverse sort // TODO: support case-insensitive sort? // TODO: seems like /*>*/ syntax has an off-by-one compared with ↓ syntax // TODO: see if we can enforce that this rule must be opt-in, and shouldn't be enabled globally. @@ -68,7 +101,7 @@ private extension SortedCollectionMembersRule { override func visit(_ node: ArrayExprSyntax) -> SyntaxVisitorContinueKind { let sortedNames = node.elements .map(\.expression.trimmedDescription) - .sorted() + .sorted(by: configuration.reverse ? (>) : (<)) let originalAndSorted = zip(zip(node.elements.indices, node.elements), sortedNames) for ((originalIndex, originalElement), sortedName) in originalAndSorted { From d20d6a03ec10accd602a904a50ac0452f8ac991a Mon Sep 17 00:00:00 2001 From: Zev Eisenberg Date: Wed, 23 Sep 2026 17:30:10 -0400 Subject: [PATCH 3/7] Add integration-style test. --- .../Style/SortedCollectionMembersRule.swift | 41 +++++++++++++++++++ 1 file changed, 41 insertions(+) diff --git a/Source/SwiftLintBuiltInRules/Rules/Style/SortedCollectionMembersRule.swift b/Source/SwiftLintBuiltInRules/Rules/Style/SortedCollectionMembersRule.swift index 8c6ae34f54..012ba8ea28 100644 --- a/Source/SwiftLintBuiltInRules/Rules/Style/SortedCollectionMembersRule.swift +++ b/Source/SwiftLintBuiltInRules/Rules/Style/SortedCollectionMembersRule.swift @@ -84,6 +84,46 @@ struct SortedCollectionMembersRule: Rule { .thingCWithComment, ] """.asExample(configuration: reverseSort), + // integration-style test + """ + let package = Package( + name: "Packages", + platforms: [ + ↓.macOS(.v10_15), + .iOS(.v26), + ], + products: [ + ↓.library(name: "Library2", type: type, targets: ["Library2"]), + .library(name: "Library1", type: type, targets: ["Library1"]), + ], + dependencies: [ + ↓.package( + url: "https://github.com/qux/quiz", + exact: "4.5.6" + ), + .package( + url: "https://github.com/foo/bar", + exact: "1.2.3" + ), + ], + targets: [ + ↓.target( + name: "CoolFeatureB", + dependencies: [ + "SomeDependency", + .product(name: "AnotherDependency", package: "another-dependency"), + ] + ), + .target( + name: "CoolFeatureA", + dependencies: [ + ↓.dependencyB, + .dependencyA, + ] + ), + ] + ) + """, ]) ) } @@ -95,6 +135,7 @@ private let reverseSort: [String: any Sendable] = ["reverse": true] // TODO: seems like /*>*/ syntax has an off-by-one compared with ↓ syntax // TODO: see if we can enforce that this rule must be opt-in, and shouldn't be enabled globally. // TODO: find a way to opt out of visiting nodes that do not apply to us. +// TODO: test that inline override works, so a file where this is enabled can opt-out for a single array private extension SortedCollectionMembersRule { final class Visitor: ViolationsSyntaxVisitor { From b678fc3c6652f2a7480ea081d0cb04b6ca6210ae Mon Sep 17 00:00:00 2001 From: Zev Eisenberg Date: Thu, 24 Sep 2026 15:55:01 -0400 Subject: [PATCH 4/7] Stop being so prescriptive. --- .../Rules/Style/SortedCollectionMembersRule.swift | 1 - 1 file changed, 1 deletion(-) diff --git a/Source/SwiftLintBuiltInRules/Rules/Style/SortedCollectionMembersRule.swift b/Source/SwiftLintBuiltInRules/Rules/Style/SortedCollectionMembersRule.swift index 012ba8ea28..2348a44ead 100644 --- a/Source/SwiftLintBuiltInRules/Rules/Style/SortedCollectionMembersRule.swift +++ b/Source/SwiftLintBuiltInRules/Rules/Style/SortedCollectionMembersRule.swift @@ -133,7 +133,6 @@ private let reverseSort: [String: any Sendable] = ["reverse": true] // TODO: add dictionary literal support // TODO: support case-insensitive sort? // TODO: seems like /*>*/ syntax has an off-by-one compared with ↓ syntax -// TODO: see if we can enforce that this rule must be opt-in, and shouldn't be enabled globally. // TODO: find a way to opt out of visiting nodes that do not apply to us. // TODO: test that inline override works, so a file where this is enabled can opt-out for a single array From 8a1de411eeb2162725798fa781b1f0a867bab5f4 Mon Sep 17 00:00:00 2001 From: Zev Eisenberg Date: Thu, 24 Sep 2026 15:55:09 -0400 Subject: [PATCH 5/7] Add dictionary literal support. --- .../Style/SortedCollectionMembersRule.swift | 115 +++++++++++++++++- 1 file changed, 113 insertions(+), 2 deletions(-) diff --git a/Source/SwiftLintBuiltInRules/Rules/Style/SortedCollectionMembersRule.swift b/Source/SwiftLintBuiltInRules/Rules/Style/SortedCollectionMembersRule.swift index 2348a44ead..d3bb72f301 100644 --- a/Source/SwiftLintBuiltInRules/Rules/Style/SortedCollectionMembersRule.swift +++ b/Source/SwiftLintBuiltInRules/Rules/Style/SortedCollectionMembersRule.swift @@ -12,6 +12,8 @@ struct SortedCollectionMembersRule: Rule { description: "Please keep the elements of this collection literal sorted", kind: .style, nonTriggeringExamples: #examples([ + // Arrays + "[1, 2, 3]", "[a, b, c]", "[c, b, a]".asExample(configuration: reverseSort), @@ -42,9 +44,45 @@ struct SortedCollectionMembersRule: Rule { // comments are ignored in sort checking .thingA, ] - """.asExample(configuration: reverseSort) + """.asExample(configuration: reverseSort), + + // Dictionaries + + "[1: 1, 2: 2, 3: 3]", + "[a: 200, b: 10, c: 0]", + "[c: 0, b: 0, a: 0]".asExample(configuration: reverseSort), + "[:]", + "[1: 1]", + "[a: 1]", + """ + ["a": "A", "b": "B", "c": "C"] + """, + """ + ["c": "C", "b": "B", "a": "A"] + """.asExample(configuration: reverseSort), + """ + ["a": "A"] + """, + """ + [ + .thingA: "A", + .thingB: "B", + // comments are ignored in sort checking + .thingC: "C", + ] + """, + """ + [ + .thingC: "C", + .thingB: "B", + // comments are ignored in sort checking + .thingA: "A", + ] + """.asExample(configuration: reverseSort), ]), triggeringExamples: #examples([ + // Arrays + "[1, ↓3, 2]", "[↓1, 3, 2]".asExample(configuration: reverseSort), "[↓1, 2, 3]".asExample(configuration: reverseSort), @@ -124,13 +162,58 @@ struct SortedCollectionMembersRule: Rule { ] ) """, + + // Dictionaries are unordered, but you may still want to sort dictionary + // literals for code style reasons, or if you're using them to construct + // an instance of KeyValuePairs or some other ordered dictionary-like + // structure. + + "[1: 1, ↓3: 3, 2: 2]", + "[↓1: 1, 3: 3, 2: 2]".asExample(configuration: reverseSort), + "[↓1: 1, 2: 2, 3: 3]".asExample(configuration: reverseSort), + """ + [↓"b": 0, "c": 0, "a": 0] + """, + """ + [↓"b": 0, "c": 0, "a": 0] + """.asExample(configuration: reverseSort), + """ + [ + ↓.thingBNoComment: 0, + .thingCNoComment: 0, + .thingANoComment: 0, + ] + """, + """ + [ + ↓.thingANoComment: 0, + .thingBNoComment: 0, + .thingCNoComment: 0, + ] + """.asExample(configuration: reverseSort), + """ + [ + ↓.thingBWithComment: 0, + // Comments are not counted when evaluating sort order + .thingCWithComment: 0, + .thingAWithComment: 0, + ] + """, + """ + [ + ↓.thingAWithComment: 0, + .thingBWithComment: 0, + // Comments are not counted when evaluating sort order + .thingCWithComment: 0, + ] + """.asExample(configuration: reverseSort), + ]) ) } private let reverseSort: [String: any Sendable] = ["reverse": true] -// TODO: add dictionary literal support // TODO: support case-insensitive sort? // TODO: seems like /*>*/ syntax has an off-by-one compared with ↓ syntax // TODO: find a way to opt out of visiting nodes that do not apply to us. @@ -154,5 +237,33 @@ private extension SortedCollectionMembersRule { return .visitChildren } + + override func visit(_ node: DictionaryExprSyntax) -> SyntaxVisitorContinueKind { + let content = node.content + + let elements: DictionaryElementListSyntax + switch content { + case .colon: + // empty dictionary, so there's nothing to sort + return .visitChildren + case .elements(let dictionaryElementListSyntax): + elements = dictionaryElementListSyntax + } + + let sortedNames = elements + .map(\.key.trimmedDescription) + .sorted(by: configuration.reverse ? (>) : (<)) + + let originalAndSorted = zip(zip(elements.indices, elements), sortedNames) + for ((originalIndex, originalElement), sortedName) in originalAndSorted { + if originalElement.key.trimmedDescription != sortedName { + violations.append(elements[originalIndex].positionAfterSkippingLeadingTrivia) + // break on the first sorting violation because everything after it is necessarily not sorted + break + } + } + + return .visitChildren + } } } From 557c8278f8504a4163367ddc5a2d54916fc95ccc Mon Sep 17 00:00:00 2001 From: Zev Eisenberg Date: Thu, 24 Sep 2026 15:56:59 -0400 Subject: [PATCH 6/7] Decide not to support case-insensitive sort for now. --- .../Rules/Style/SortedCollectionMembersRule.swift | 1 - 1 file changed, 1 deletion(-) diff --git a/Source/SwiftLintBuiltInRules/Rules/Style/SortedCollectionMembersRule.swift b/Source/SwiftLintBuiltInRules/Rules/Style/SortedCollectionMembersRule.swift index d3bb72f301..3facc06ab8 100644 --- a/Source/SwiftLintBuiltInRules/Rules/Style/SortedCollectionMembersRule.swift +++ b/Source/SwiftLintBuiltInRules/Rules/Style/SortedCollectionMembersRule.swift @@ -214,7 +214,6 @@ struct SortedCollectionMembersRule: Rule { private let reverseSort: [String: any Sendable] = ["reverse": true] -// TODO: support case-insensitive sort? // TODO: seems like /*>*/ syntax has an off-by-one compared with ↓ syntax // TODO: find a way to opt out of visiting nodes that do not apply to us. // TODO: test that inline override works, so a file where this is enabled can opt-out for a single array From 7f11d34968ff80d99d012a85d168de51f1cf1468 Mon Sep 17 00:00:00 2001 From: Zev Eisenberg Date: Thu, 24 Sep 2026 16:50:03 -0400 Subject: [PATCH 7/7] Self-lint. --- .../Style/SortedCollectionMembersRule.swift | 25 ++++++++----------- 1 file changed, 11 insertions(+), 14 deletions(-) diff --git a/Source/SwiftLintBuiltInRules/Rules/Style/SortedCollectionMembersRule.swift b/Source/SwiftLintBuiltInRules/Rules/Style/SortedCollectionMembersRule.swift index 3facc06ab8..898d8b0d00 100644 --- a/Source/SwiftLintBuiltInRules/Rules/Style/SortedCollectionMembersRule.swift +++ b/Source/SwiftLintBuiltInRules/Rules/Style/SortedCollectionMembersRule.swift @@ -6,7 +6,7 @@ import SwiftSyntax struct SortedCollectionMembersRule: Rule { var configuration = SortedCollectionMembersConfiguration() - static let description: RuleDescription = RuleDescription( + static let description = RuleDescription( identifier: "sorted_collection_members", name: "Sorted Collection Members", description: "Please keep the elements of this collection literal sorted", @@ -207,7 +207,6 @@ struct SortedCollectionMembersRule: Rule { .thingCWithComment: 0, ] """.asExample(configuration: reverseSort), - ]) ) } @@ -226,12 +225,11 @@ private extension SortedCollectionMembersRule { .sorted(by: configuration.reverse ? (>) : (<)) let originalAndSorted = zip(zip(node.elements.indices, node.elements), sortedNames) - for ((originalIndex, originalElement), sortedName) in originalAndSorted { - if originalElement.expression.trimmedDescription != sortedName { - violations.append(node.elements[originalIndex].positionAfterSkippingLeadingTrivia) - // break on the first sorting violation because everything after it is necessarily not sorted - break - } + for ((originalIndex, originalElement), sortedName) in originalAndSorted + where originalElement.expression.trimmedDescription != sortedName { + violations.append(node.elements[originalIndex].positionAfterSkippingLeadingTrivia) + // break on the first sorting violation because everything after it is necessarily not sorted + break } return .visitChildren @@ -254,12 +252,11 @@ private extension SortedCollectionMembersRule { .sorted(by: configuration.reverse ? (>) : (<)) let originalAndSorted = zip(zip(elements.indices, elements), sortedNames) - for ((originalIndex, originalElement), sortedName) in originalAndSorted { - if originalElement.key.trimmedDescription != sortedName { - violations.append(elements[originalIndex].positionAfterSkippingLeadingTrivia) - // break on the first sorting violation because everything after it is necessarily not sorted - break - } + for ((originalIndex, originalElement), sortedName) in originalAndSorted + where originalElement.key.trimmedDescription != sortedName { + violations.append(elements[originalIndex].positionAfterSkippingLeadingTrivia) + // break on the first sorting violation because everything after it is necessarily not sorted + break } return .visitChildren