From d6a54f28ebee86a0e6773ea8cb025a0e78ed3e54 Mon Sep 17 00:00:00 2001 From: David Roman <2538074+davdroman@users.noreply.github.com> Date: Tue, 1 Sep 2026 15:27:39 +0100 Subject: [PATCH 1/6] CI: enable SwiftFormat linting --- .github/workflows/ci.yml | 23 ++++++++++------------- .mise.toml | 2 ++ 2 files changed, 12 insertions(+), 13 deletions(-) create mode 100644 .mise.toml diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 6105daa..ff4c80d 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -15,21 +15,18 @@ concurrency: cancel-in-progress: true jobs: - # TODO: Re-enable when --ifdef no-indent works as intended - # lint: - # name: Lint - # runs-on: macos-latest - # steps: - # - name: Git Checkout - # uses: actions/checkout@v5 + lint: + name: Lint + runs-on: macos-latest + steps: + - name: Git Checkout + uses: actions/checkout@v5 - # - name: Install SwiftFormat - # run: | - # brew unlink swiftformat - # brew install swiftformat --HEAD + - name: Install Mise tools + uses: jdx/mise-action@v3 - # - name: SwiftFormat Lint - # run: swiftformat --lint . --reporter github-actions-log + - name: SwiftFormat Lint + run: mise exec -- swiftformat --lint . --reporter github-actions-log apple: if: github.event_name != 'pull_request' || !contains(github.event.pull_request.title, '[skip ci]') diff --git a/.mise.toml b/.mise.toml new file mode 100644 index 0000000..1e35be0 --- /dev/null +++ b/.mise.toml @@ -0,0 +1,2 @@ +[tools] +swiftformat = "latest" From 02fc4f891c3dc6d03bfc0a4352dc8bacf1b70583 Mon Sep 17 00:00:00 2001 From: David Roman <2538074+davdroman@users.noreply.github.com> Date: Tue, 1 Sep 2026 18:05:19 +0100 Subject: [PATCH 2/6] Run SwiftFormat --- .swiftformat | 1 + .../AssociatedMacroTests.swift | 36 +++++++++---------- Tests/AssociationTests/ClassTypeTests.swift | 6 ++-- .../PatternBindingSyntax+Tests.swift | 16 ++++----- Tests/SwizzlingTests/SwizzleMacroTests.swift | 14 ++++---- Tests/SwizzlingTests/SwizzleTests.swift | 16 ++++----- 6 files changed, 45 insertions(+), 44 deletions(-) diff --git a/.swiftformat b/.swiftformat index db635c4..3c33396 100644 --- a/.swiftformat +++ b/.swiftformat @@ -27,4 +27,5 @@ --disable redundantViewBuilder --disable unusedArguments --disable wrapFunctionBodies +--disable wrapIfStatementBodies --disable wrapPropertyBodies diff --git a/Tests/AssociationTests/AssociatedMacroTests.swift b/Tests/AssociationTests/AssociatedMacroTests.swift index 026ae1e..174ae9f 100644 --- a/Tests/AssociationTests/AssociatedMacroTests.swift +++ b/Tests/AssociationTests/AssociatedMacroTests.swift @@ -21,11 +21,11 @@ // SOFTWARE. #if canImport(AssociationMacro) +@testable import AssociationMacro import MacroTesting import SnapshotTesting import SwiftSyntax import Testing -@testable import AssociationMacro @Suite( .macros( @@ -220,7 +220,7 @@ struct AssociatedTests { } @Test - func stringWithOtherPolicy() { + func `string with other policy`() { assertMacro { """ @Associated(.retain(.nonatomic)) @@ -266,7 +266,7 @@ struct AssociatedTests { } @Test - func optionalString() { + func `optional string`() { assertMacro { """ @Associated(.retain(.nonatomic)) @@ -302,7 +302,7 @@ struct AssociatedTests { } @Test - func optionalGenericsString() { + func `optional String using generic syntax`() { assertMacro { """ @Associated(.retain(.nonatomic)) @@ -338,7 +338,7 @@ struct AssociatedTests { } @Test - func implicitlyUnwrappedOptionalString() { + func `implicitly unwrapped optional string`() { assertMacro { """ @Associated(.retain(.nonatomic)) @@ -374,7 +374,7 @@ struct AssociatedTests { } @Test - func optionalStringWithInitialValue() { + func `optional string with initial value`() { assertMacro { """ @Associated(.retain(.nonatomic)) @@ -476,7 +476,7 @@ struct AssociatedTests { } @Test - func intArray() { + func `Int array`() { assertMacro { """ @Associated(.retain(.nonatomic)) @@ -522,7 +522,7 @@ struct AssociatedTests { } @Test - func optionalBool() { + func `optional Bool`() { assertMacro { """ @Associated(.retain(.nonatomic)) @@ -604,7 +604,7 @@ struct AssociatedTests { } @Test - func willSet() { + func `willSet`() { assertMacro { """ @Associated(.retain(.nonatomic)) @@ -666,7 +666,7 @@ struct AssociatedTests { } @Test - func didSet() { + func `didSet`() { assertMacro { """ @Associated(.retain(.nonatomic)) @@ -726,7 +726,7 @@ struct AssociatedTests { } @Test - func willSetAndDidSet() { + func `willSet and didSet`() { assertMacro { """ @Associated(.retain(.nonatomic)) @@ -800,7 +800,7 @@ struct AssociatedTests { } @Test - func willSetWithArgument() { + func `willSet with argument`() { assertMacro { """ @Associated(.retain(.nonatomic)) @@ -862,7 +862,7 @@ struct AssociatedTests { } @Test - func didSetWithArgument() { + func `didSet with argument`() { assertMacro { """ @Associated(.retain(.nonatomic)) @@ -922,7 +922,7 @@ struct AssociatedTests { } @Test - func modernWritingStyle() { + func `modern writing style`() { assertMacro { """ @Associated(.copy(.nonatomic)) @@ -970,7 +970,7 @@ struct AssociatedTests { // MARK: Diagnostics test @Test - func diagnosticsDeclarationType() { + func `requires a property declaration`() { assertMacro { """ @Associated(.retain(.nonatomic)) @@ -986,7 +986,7 @@ struct AssociatedTests { } @Test - func diagnosticsGetterAndSetter() { + func `rejects implemented getter and setter`() { assertMacro { """ @Associated(.retain(.nonatomic)) @@ -1009,7 +1009,7 @@ struct AssociatedTests { } @Test - func diagnosticsInitialValue() { + func `requires an initial value`() { assertMacro { """ @Associated(.retain(.nonatomic)) @@ -1025,7 +1025,7 @@ struct AssociatedTests { } @Test - func diagnosticsSpecifyType() { + func `requires an explicit type`() { assertMacro { """ @Associated(.retain(.nonatomic)) diff --git a/Tests/AssociationTests/ClassTypeTests.swift b/Tests/AssociationTests/ClassTypeTests.swift index 734b2c5..1df2578 100644 --- a/Tests/AssociationTests/ClassTypeTests.swift +++ b/Tests/AssociationTests/ClassTypeTests.swift @@ -46,7 +46,7 @@ class ClassType { @Suite struct Tests { @Test - func keysUnique() { + func `keys are unique`() { let keys = [ ClassType.__associated_intKey, ClassType.__associated_optionalDoubleKey, @@ -117,13 +117,13 @@ class ClassType { } @Test - func setDefaultValue() { + func `default value is stable`() { let item = ClassType() #expect(item.classType === item.classType) } @Test - func testProtocol() { + func `associated property declared in protocol`() { let item = ClassType() #expect(item.definedInProtocol == "hello") diff --git a/Tests/AssociationTests/PatternBindingSyntax+Tests.swift b/Tests/AssociationTests/PatternBindingSyntax+Tests.swift index 5982d01..d2cc499 100644 --- a/Tests/AssociationTests/PatternBindingSyntax+Tests.swift +++ b/Tests/AssociationTests/PatternBindingSyntax+Tests.swift @@ -21,15 +21,15 @@ // SOFTWARE. #if canImport(AssociationMacro) +@testable import AssociationMacro import SwiftSyntax import SwiftSyntaxBuilder import Testing -@testable import AssociationMacro @Suite struct PatternBindingSyntaxTests { @Test - func testSetter() { + func `reads setter`() { let setter = AccessorDeclSyntax(accessorSpecifier: .keyword(.set), body: .init(statements: CodeBlockItemListSyntax {})) let binding: PatternBindingSyntax = .init( @@ -45,7 +45,7 @@ struct PatternBindingSyntaxTests { } @Test - func testGetter() throws { + func `reads getter`() throws { let getter = AccessorDeclSyntax(accessorSpecifier: .keyword(.get), body: .init(statements: CodeBlockItemListSyntax {})) var binding: PatternBindingSyntax = .init( @@ -73,7 +73,7 @@ struct PatternBindingSyntaxTests { } @Test - func setSetter() { + func `sets setter`() { let setter = AccessorDeclSyntax(accessorSpecifier: .keyword(.set), body: .init(statements: CodeBlockItemListSyntax {})) var binding: PatternBindingSyntax = .init( pattern: IdentifierPatternSyntax(identifier: .identifier("value")), @@ -108,7 +108,7 @@ struct PatternBindingSyntaxTests { } @Test - func setGetter() { + func `sets getter`() { let getter = AccessorDeclSyntax(accessorSpecifier: .keyword(.get), body: .init(statements: CodeBlockItemListSyntax {})) var binding: PatternBindingSyntax = .init( pattern: IdentifierPatternSyntax(identifier: .identifier("value")), @@ -157,7 +157,7 @@ struct PatternBindingSyntaxTests { } @Test - func testWillSet() { + func `willSet`() { let `willSet` = AccessorDeclSyntax(accessorSpecifier: .keyword(.willSet), body: .init(statements: CodeBlockItemListSyntax {})) let binding: PatternBindingSyntax = .init( @@ -173,7 +173,7 @@ struct PatternBindingSyntaxTests { } @Test - func testDidSet() { + func `didSet`() { let `didSet` = AccessorDeclSyntax(accessorSpecifier: .keyword(.didSet), body: .init(statements: CodeBlockItemListSyntax {})) let binding: PatternBindingSyntax = .init( @@ -189,7 +189,7 @@ struct PatternBindingSyntaxTests { } @Test - func setWillSet() { + func `sets willSet`() { let `willSet` = AccessorDeclSyntax(accessorSpecifier: .keyword(.willSet), body: .init(statements: CodeBlockItemListSyntax {})) var binding: PatternBindingSyntax = .init( diff --git a/Tests/SwizzlingTests/SwizzleMacroTests.swift b/Tests/SwizzlingTests/SwizzleMacroTests.swift index 4eb58f2..770e123 100644 --- a/Tests/SwizzlingTests/SwizzleMacroTests.swift +++ b/Tests/SwizzlingTests/SwizzleMacroTests.swift @@ -2,8 +2,8 @@ import MacroTesting import SnapshotTesting import SwiftSyntax -import Testing @testable import SwizzlingMacro +import Testing @Suite( .macros( @@ -19,7 +19,7 @@ struct SwizzleMacroTests {} /// Test property swizzling extension SwizzleMacroTests { @Test(arguments: ["self", "$self", "`self`"]) - func swizzleGetter(selfLabel: String) { + func `swizzle getter`(selfLabel: String) { assertMacro { """ #swizzle( @@ -52,7 +52,7 @@ extension SwizzleMacroTests { } @Test(arguments: ["self", "$self", "`self`"]) - func swizzleSetter(selfLabel: String) { + func `swizzle setter`(selfLabel: String) { assertMacro { """ #swizzle( @@ -86,7 +86,7 @@ extension SwizzleMacroTests { /// Test function swizzling extension SwizzleMacroTests { @Test(arguments: ["self", "$self", "`self`"]) - func swizzleFunctionWithoutParametersWithoutReturn(selfLabel: String) { + func `swizzle function without parameters or a return value`(selfLabel: String) { assertMacro { """ #swizzle( @@ -116,7 +116,7 @@ extension SwizzleMacroTests { } @Test(arguments: ["self", "$self", "`self`"]) - func swizzleFunctionWithoutParametersWithReturn(selfLabel: String) { + func `swizzle function without parameters and with a return value`(selfLabel: String) { assertMacro { """ #swizzle( @@ -149,7 +149,7 @@ extension SwizzleMacroTests { } @Test(arguments: ["param", "params"], ["self", "$self", "`self`"]) - func swizzleFunctionWithParametersWithoutReturn(paramOrParams: String, selfLabel: String) { + func `swizzle function with parameters and without a return value`(paramOrParams: String, selfLabel: String) { assertMacro { """ #swizzle( @@ -180,7 +180,7 @@ extension SwizzleMacroTests { } @Test(arguments: ["self", "$self", "`self`"]) - func swizzleFunctionWithParametersWithReturn(selfLabel: String) { + func `swizzle function with parameters and a return value`(selfLabel: String) { assertMacro { """ #swizzle( diff --git a/Tests/SwizzlingTests/SwizzleTests.swift b/Tests/SwizzlingTests/SwizzleTests.swift index 35ea89e..f83d391 100644 --- a/Tests/SwizzlingTests/SwizzleTests.swift +++ b/Tests/SwizzlingTests/SwizzleTests.swift @@ -28,7 +28,7 @@ struct SwizzleTests { extension SwizzleTests { @Test - func swizzleGetter() throws { + func `swizzle getter`() throws { let sut = SUT() #expect(sut.state == 0) @@ -46,7 +46,7 @@ extension SwizzleTests { extension SwizzleTests { @Test - func swizzleSetter() throws { + func `swizzle setter`() throws { let sut = SUT() #expect(sut.state == 0) @@ -65,7 +65,7 @@ extension SwizzleTests { extension SwizzleTests { @Test - func swizzleFunctionWithoutParametersWithoutReturn() throws { + func `swizzle function without parameters or a return value`() throws { let sut = SUT() let hook = try #swizzle(SUT.functionWithoutParamsWithoutReturn) { $self in @@ -86,7 +86,7 @@ extension SwizzleTests { } @Test - func swizzleFunctionWithoutParametersWithReturn() throws { + func `swizzle function without parameters and with a return value`() throws { let hook = try #swizzle( SUT.functionWithoutParamsWithReturn, returning: Int.self, @@ -108,7 +108,7 @@ extension SwizzleTests { } @Test - func swizzleFunctionWithParametersWithoutReturn() throws { + func `swizzle function with parameters and without a return value`() throws { let hook = try #swizzle( SUT.functionWithParamsWithoutReturn, param: Int.self, @@ -127,7 +127,7 @@ extension SwizzleTests { } @Test - func swizzleFunctionWithParametersWithReturn() throws { + func `swizzle function with parameters and a return value`() throws { let hook = try #swizzle( SUT.functionWithParamsWithReturn, param: Int.self, @@ -157,7 +157,7 @@ struct SwizzleLifetimeTests { weak static var hook: AnyHook? = nil @Test - func swizzlePersistsAcrossScopes_entryPoint() throws { + func `swizzle persists across scopes entry point`() throws { let sut = SUT() #expect(sut.state == 0) @@ -170,7 +170,7 @@ struct SwizzleLifetimeTests { } @Test - func swizzlePersistsAcrossScopes_exitPoint() throws { + func `swizzle persists across scopes exit point`() throws { let sut = SUT() #expect(sut.state == 1) From bcaef6dd8757f6e99c47d5a1d84399839365e0a9 Mon Sep 17 00:00:00 2001 From: David Roman <2538074+davdroman@users.noreply.github.com> Date: Tue, 1 Sep 2026 18:59:07 +0100 Subject: [PATCH 3/6] CI: use standard Mise project files --- mise.lock | 43 +++++++++++++++++++++++++++++++++++++++++ .mise.toml => mise.toml | 0 2 files changed, 43 insertions(+) create mode 100644 mise.lock rename .mise.toml => mise.toml (100%) diff --git a/mise.lock b/mise.lock new file mode 100644 index 0000000..1df1a01 --- /dev/null +++ b/mise.lock @@ -0,0 +1,43 @@ +# @generated - this file is auto-generated by `mise lock` https://mise.jdx.dev/dev-tools/mise-lock.html + +[[tools.swiftformat]] +version = "0.63.0" +backend = "github:nicklockwood/SwiftFormat" + +[tools.swiftformat.options] +no_app = "true" + +[tools.swiftformat."platforms.linux-arm64"] +checksum = "sha256:b0335af32e2c5944a17b3e6d916ff4552eb757ed88f68b80fd19415824850717" +url = "https://github.com/nicklockwood/SwiftFormat/releases/download/0.63.0/swiftformat_linux_aarch64.zip" +url_api = "https://api.github.com/repos/nicklockwood/SwiftFormat/releases/assets/536986531" + +[tools.swiftformat."platforms.linux-arm64-musl"] +checksum = "sha256:b0335af32e2c5944a17b3e6d916ff4552eb757ed88f68b80fd19415824850717" +url = "https://github.com/nicklockwood/SwiftFormat/releases/download/0.63.0/swiftformat_linux_aarch64.zip" +url_api = "https://api.github.com/repos/nicklockwood/SwiftFormat/releases/assets/536986531" + +[tools.swiftformat."platforms.linux-x64"] +checksum = "sha256:b4a3cbb8c852a0baaf9adf853e221ff1dabf921a3d8957a602e0bda3af8470f1" +url = "https://github.com/nicklockwood/SwiftFormat/releases/download/0.63.0/swiftformat_linux.zip" +url_api = "https://api.github.com/repos/nicklockwood/SwiftFormat/releases/assets/536986504" + +[tools.swiftformat."platforms.linux-x64-musl"] +checksum = "sha256:b4a3cbb8c852a0baaf9adf853e221ff1dabf921a3d8957a602e0bda3af8470f1" +url = "https://github.com/nicklockwood/SwiftFormat/releases/download/0.63.0/swiftformat_linux.zip" +url_api = "https://api.github.com/repos/nicklockwood/SwiftFormat/releases/assets/536986504" + +[tools.swiftformat."platforms.macos-arm64"] +checksum = "sha256:28c7802e11fa5ae113d903066439c6bb1be20a8ac1ad9709c42616a7e273fb0f" +url = "https://github.com/nicklockwood/SwiftFormat/releases/download/0.63.0/swiftformat.zip" +url_api = "https://api.github.com/repos/nicklockwood/SwiftFormat/releases/assets/536986489" + +[tools.swiftformat."platforms.macos-x64"] +checksum = "sha256:28c7802e11fa5ae113d903066439c6bb1be20a8ac1ad9709c42616a7e273fb0f" +url = "https://github.com/nicklockwood/SwiftFormat/releases/download/0.63.0/swiftformat.zip" +url_api = "https://api.github.com/repos/nicklockwood/SwiftFormat/releases/assets/536986489" + +[tools.swiftformat."platforms.windows-x64"] +checksum = "sha256:28c7802e11fa5ae113d903066439c6bb1be20a8ac1ad9709c42616a7e273fb0f" +url = "https://github.com/nicklockwood/SwiftFormat/releases/download/0.63.0/swiftformat.zip" +url_api = "https://api.github.com/repos/nicklockwood/SwiftFormat/releases/assets/536986489" diff --git a/.mise.toml b/mise.toml similarity index 100% rename from .mise.toml rename to mise.toml From 5658f02d33c1d0c29c3564f3126cf2599ade815b Mon Sep 17 00:00:00 2001 From: David Roman <2538074+davdroman@users.noreply.github.com> Date: Tue, 1 Sep 2026 19:18:44 +0100 Subject: [PATCH 4/6] CI: track SwiftFormat 0.x --- mise.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mise.toml b/mise.toml index 1e35be0..3b953c0 100644 --- a/mise.toml +++ b/mise.toml @@ -1,2 +1,2 @@ [tools] -swiftformat = "latest" +swiftformat = "0" From 84d58bbe86a2132a3ab39b85fe93d4d5286db2fe Mon Sep 17 00:00:00 2001 From: David Roman <2538074+davdroman@users.noreply.github.com> Date: Tue, 1 Sep 2026 20:35:06 +0100 Subject: [PATCH 5/6] CI: restrict lint job credentials --- .github/workflows/ci.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index ff4c80d..d5eab01 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -17,10 +17,14 @@ concurrency: jobs: lint: name: Lint + permissions: + contents: read runs-on: macos-latest steps: - name: Git Checkout uses: actions/checkout@v5 + with: + persist-credentials: false - name: Install Mise tools uses: jdx/mise-action@v3 From 6b0c87fa9e2401199a46daccd619177beb2dbdaa Mon Sep 17 00:00:00 2001 From: David Roman <2538074+davdroman@users.noreply.github.com> Date: Wed, 2 Sep 2026 10:36:04 +0100 Subject: [PATCH 6/6] CI: use read-only workflow permissions --- .github/workflows/ci.yml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index d5eab01..46536be 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -14,11 +14,12 @@ concurrency: group: ci-${{ github.ref }} cancel-in-progress: true +permissions: + contents: read + jobs: lint: name: Lint - permissions: - contents: read runs-on: macos-latest steps: - name: Git Checkout