From 183e7e0c144f3c2c717273ec5ecb3958f7daa9ab Mon Sep 17 00:00:00 2001 From: Jonathan Grynspan Date: Mon, 10 Nov 2025 10:43:16 -0500 Subject: [PATCH 01/12] Adopt `@section` and `@used`. This PR adopts the `@section` and `@used` attributes now available in the Swift 6.3 toolchain. These attributes replace the experimental `@_section` and `@_used` attributes verbatim and they are always enabled (so no need to check `hasFeature(SymbolLinkageMarkers)`.) Because these attributes are only available with the Swift 6.3 toolchain, we do not use them when building with Swift 6.2. Instead, if you build with Swift 6.2 you continue to use the "legacy" test discovery mechanism based on type metadata emission. The "legacy" mechanism will be removed in a future update. Resolves #1371. --- Documentation/Porting.md | 9 +++++-- Package.swift | 5 ---- Sources/TestingMacros/ConditionMacro.swift | 6 +---- .../TestingMacros/SuiteDeclarationMacro.swift | 6 +---- .../Support/TestContentGeneration.swift | 12 ++++----- .../TestingMacros/TestDeclarationMacro.swift | 6 +---- .../_TestDiscovery/TestContentRecord.swift | 2 +- .../TestDeclarationMacroTests.swift | 9 ++++--- Tests/TestingTests/DiscoveryTests.swift | 27 ++++--------------- 9 files changed, 26 insertions(+), 56 deletions(-) diff --git a/Documentation/Porting.md b/Documentation/Porting.md index 4773e2823..365551d4c 100644 --- a/Documentation/Porting.md +++ b/Documentation/Porting.md @@ -193,7 +193,7 @@ to load that information: ``` You will also need to update the `makeTestContentRecordDecl()` function in the -`TestingMacros` target to emit the correct `@_section` attribute for your +`TestingMacros` target to emit the correct `@section` attribute for your platform. If your platform uses the ELF image format and supports the `dl_iterate_phdr()` function, add it to the existing `#elseif os(Linux) || ...` case. Otherwise, add a new case for your platform: @@ -203,7 +203,7 @@ case. Otherwise, add a new case for your platform: +++ b/Sources/TestingMacros/Support/TestContentGeneration.swift // ... + #elseif os(Classic) -+ @_section(".rsrc,swft,__swift5_tests") ++ @section(".rsrc,swft,__swift5_tests") #else @__testing(warning: "Platform-specific implementation missing: test content section name unavailable") #endif @@ -214,6 +214,11 @@ directly into test authors' test targets, so you will not be able to use compiler conditionals defined in the Swift Testing package (including those that start with `"SWT_"`). +> [!NOTE] +> We are not using `objectFormat()` yet to maintain compatibility with the Swift +> 6.2 toolchain. We will migrate to `objectFormat()` when we drop Swift 6.2 +> toolchain support (presumably after Swift 6.3 ships). + ## Runtime test discovery with static linkage If your platform does not support dynamic linking and loading, you will need to diff --git a/Package.swift b/Package.swift index a82241527..19ebefc4c 100644 --- a/Package.swift +++ b/Package.swift @@ -385,11 +385,6 @@ extension Array where Element == PackageDescription.SwiftSetting { .enableUpcomingFeature("MemberImportVisibility"), - // This setting is enabled in the package, but not in the toolchain build - // (via CMake). Enabling it is dependent on acceptance of the @section - // proposal via Swift Evolution. - .enableExperimentalFeature("SymbolLinkageMarkers"), - .enableUpcomingFeature("InferIsolatedConformances"), // When building as a package, the macro plugin always builds as an diff --git a/Sources/TestingMacros/ConditionMacro.swift b/Sources/TestingMacros/ConditionMacro.swift index 6ba8ff124..9c154b9fa 100644 --- a/Sources/TestingMacros/ConditionMacro.swift +++ b/Sources/TestingMacros/ConditionMacro.swift @@ -13,10 +13,6 @@ public import SwiftSyntax import SwiftSyntaxBuilder public import SwiftSyntaxMacros -#if !hasFeature(SymbolLinkageMarkers) && SWT_NO_LEGACY_TEST_DISCOVERY -#error("Platform-specific misconfiguration: either SymbolLinkageMarkers or legacy test discovery is required to expand #expect(processExitsWith:)") -#endif - /// A protocol containing the common implementation for the expansions of the /// `#expect()` and `#require()` macros. /// @@ -496,7 +492,7 @@ extension ExitTestConditionMacro { // Create another local type for legacy test discovery. var recordDecl: DeclSyntax? -#if !SWT_NO_LEGACY_TEST_DISCOVERY +#if compiler(<6.3) let legacyEnumName = context.makeUniqueName("__🟡$") recordDecl = """ enum \(legacyEnumName): Testing.__TestContentRecordContainer { diff --git a/Sources/TestingMacros/SuiteDeclarationMacro.swift b/Sources/TestingMacros/SuiteDeclarationMacro.swift index 4bee4c30f..cfe76ccfb 100644 --- a/Sources/TestingMacros/SuiteDeclarationMacro.swift +++ b/Sources/TestingMacros/SuiteDeclarationMacro.swift @@ -13,10 +13,6 @@ public import SwiftSyntax import SwiftSyntaxBuilder public import SwiftSyntaxMacros -#if !hasFeature(SymbolLinkageMarkers) && SWT_NO_LEGACY_TEST_DISCOVERY -#error("Platform-specific misconfiguration: either SymbolLinkageMarkers or legacy test discovery is required to expand @Suite") -#endif - /// A type describing the expansion of the `@Suite` attribute macro. /// /// This type is used to implement the `@Suite` attribute macro. Do not use it @@ -166,7 +162,7 @@ public struct SuiteDeclarationMacro: MemberMacro, PeerMacro, Sendable { ) ) -#if !SWT_NO_LEGACY_TEST_DISCOVERY +#if compiler(<6.3) // Emit a type that contains a reference to the test content record. let enumName = context.makeUniqueName("__🟡$") result.append( diff --git a/Sources/TestingMacros/Support/TestContentGeneration.swift b/Sources/TestingMacros/Support/TestContentGeneration.swift index 05214d1b8..314802005 100644 --- a/Sources/TestingMacros/Support/TestContentGeneration.swift +++ b/Sources/TestingMacros/Support/TestContentGeneration.swift @@ -74,20 +74,18 @@ func makeTestContentRecordDecl(named name: TokenSyntax, in typeName: TypeSyntax? ) """ -#if hasFeature(SymbolLinkageMarkers) +#if compiler(>=6.3) result = """ - #if hasFeature(SymbolLinkageMarkers) #if os(macOS) || os(iOS) || os(watchOS) || os(tvOS) || os(visionOS) - @_section("__DATA_CONST,__swift5_tests") + @section("__DATA_CONST,__swift5_tests") #elseif os(Linux) || os(FreeBSD) || os(OpenBSD) || os(Android) || os(WASI) - @_section("swift5_tests") + @section("swift5_tests") #elseif os(Windows) - @_section(".sw5test$B") + @section(".sw5test$B") #else @Testing.__testing(warning: "Platform-specific implementation missing: test content section name unavailable") #endif - @_used - #endif + @used \(result) """ #endif diff --git a/Sources/TestingMacros/TestDeclarationMacro.swift b/Sources/TestingMacros/TestDeclarationMacro.swift index 8007c3aaf..6aebbc729 100644 --- a/Sources/TestingMacros/TestDeclarationMacro.swift +++ b/Sources/TestingMacros/TestDeclarationMacro.swift @@ -13,10 +13,6 @@ public import SwiftSyntax import SwiftSyntaxBuilder public import SwiftSyntaxMacros -#if !hasFeature(SymbolLinkageMarkers) && SWT_NO_LEGACY_TEST_DISCOVERY -#error("Platform-specific misconfiguration: either SymbolLinkageMarkers or legacy test discovery is required to expand @Test") -#endif - /// A type describing the expansion of the `@Test` attribute macro. /// /// This type is used to implement the `@Test` attribute macro. Do not use it @@ -493,7 +489,7 @@ public struct TestDeclarationMacro: PeerMacro, Sendable { ) ) -#if !SWT_NO_LEGACY_TEST_DISCOVERY +#if compiler(<6.3) // Emit a type that contains a reference to the test content record. let enumName = context.makeUniqueName(thunking: functionDecl, withPrefix: "__🟡$") result.append( diff --git a/Sources/_TestDiscovery/TestContentRecord.swift b/Sources/_TestDiscovery/TestContentRecord.swift index b830026e2..1f1bd9c15 100644 --- a/Sources/_TestDiscovery/TestContentRecord.swift +++ b/Sources/_TestDiscovery/TestContentRecord.swift @@ -244,7 +244,7 @@ extension DiscoverableAsTestContent { return SectionBounds.all(.testContent).lazy.flatMap { sb in sb.buffer.withMemoryRebound(to: _TestContentRecord.self) { records in (0 ..< records.count).lazy - .map { (records.baseAddress! + $0) as UnsafePointer<_TestContentRecord> } + .map { records.baseAddress! + $0 } .filter { $0.pointee.kind == kind } .map { TestContentRecord(imageAddress: sb.imageAddress, recordAddress: $0) } } diff --git a/Tests/TestingMacrosTests/TestDeclarationMacroTests.swift b/Tests/TestingMacrosTests/TestDeclarationMacroTests.swift index aec6d2c10..c78fac75d 100644 --- a/Tests/TestingMacrosTests/TestDeclarationMacroTests.swift +++ b/Tests/TestingMacrosTests/TestDeclarationMacroTests.swift @@ -472,10 +472,11 @@ struct TestDeclarationMacroTests { func differentFunctionTypes(input: String, expectedTypeName: String?, otherCode: String?) throws { let (output, _) = try parse(input) -#if hasFeature(SymbolLinkageMarkers) - #expect(output.contains("@_section")) -#endif -#if !SWT_NO_LEGACY_TEST_DISCOVERY +#if compiler(>=6.3) + #expect(output.contains("@section")) + #expect(!output.contains("__TestContentRecordContainer")) +#else + #expect(!output.contains("@section")) #expect(output.contains("__TestContentRecordContainer")) #endif if let expectedTypeName { diff --git a/Tests/TestingTests/DiscoveryTests.swift b/Tests/TestingTests/DiscoveryTests.swift index 24d2eecfa..2c415e001 100644 --- a/Tests/TestingTests/DiscoveryTests.swift +++ b/Tests/TestingTests/DiscoveryTests.swift @@ -58,7 +58,7 @@ struct DiscoveryTests { } #endif -#if !SWT_NO_DYNAMIC_LINKING && hasFeature(SymbolLinkageMarkers) +#if compiler(>=6.3) && !SWT_NO_DYNAMIC_LINKING struct MyTestContent: DiscoverableAsTestContent { typealias TestContentAccessorHint = UInt32 @@ -81,15 +81,15 @@ struct DiscoveryTests { } #if os(macOS) || os(iOS) || os(watchOS) || os(tvOS) || os(visionOS) - @_section("__DATA_CONST,__swift5_tests") + @section("__DATA_CONST,__swift5_tests") #elseif os(Linux) || os(FreeBSD) || os(OpenBSD) || os(Android) || os(WASI) - @_section("swift5_tests") + @section("swift5_tests") #elseif os(Windows) - @_section(".sw5test$B") + @section(".sw5test$B") #else @__testing(warning: "Platform-specific implementation missing: test content section name unavailable") #endif - @_used + @used private static let record: __TestContentRecord = ( 0xABCD1234, 0, @@ -143,21 +143,4 @@ struct DiscoveryTests { }) } #endif - -#if !SWT_NO_LEGACY_TEST_DISCOVERY && hasFeature(SymbolLinkageMarkers) - @Test("Legacy test discovery finds the same number of tests") func discoveredTestCount() async { - let oldFlag = Environment.variable(named: "SWT_USE_LEGACY_TEST_DISCOVERY") - defer { - Environment.setVariable(oldFlag, named: "SWT_USE_LEGACY_TEST_DISCOVERY") - } - - Environment.setVariable("1", named: "SWT_USE_LEGACY_TEST_DISCOVERY") - let testsWithOldCode = await Array(Test.all).count - - Environment.setVariable("0", named: "SWT_USE_LEGACY_TEST_DISCOVERY") - let testsWithNewCode = await Array(Test.all).count - - #expect(testsWithOldCode == testsWithNewCode) - } -#endif } From c3efd8778c14e1d8373d43352d9045da2cc60381 Mon Sep 17 00:00:00 2001 From: Jonathan Grynspan Date: Mon, 10 Nov 2025 11:18:03 -0500 Subject: [PATCH 02/12] Mark accessors @safe --- Sources/TestingMacros/ConditionMacro.swift | 2 +- Sources/TestingMacros/SuiteDeclarationMacro.swift | 2 +- Sources/TestingMacros/Support/TestContentGeneration.swift | 2 +- Sources/TestingMacros/TestDeclarationMacro.swift | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Sources/TestingMacros/ConditionMacro.swift b/Sources/TestingMacros/ConditionMacro.swift index 9c154b9fa..d19cc62dd 100644 --- a/Sources/TestingMacros/ConditionMacro.swift +++ b/Sources/TestingMacros/ConditionMacro.swift @@ -507,7 +507,7 @@ extension ExitTestConditionMacro { """ @available(*, deprecated, message: "This type is an implementation detail of the testing library. Do not use it directly.") enum \(enumName) { - private nonisolated static let accessor: Testing.__TestContentRecordAccessor = { outValue, type, hint, _ in + @safe private nonisolated static let accessor: Testing.__TestContentRecordAccessor = { outValue, type, hint, _ in Testing.ExitTest.__store( \(idExpr), \(bodyThunkName), diff --git a/Sources/TestingMacros/SuiteDeclarationMacro.swift b/Sources/TestingMacros/SuiteDeclarationMacro.swift index cfe76ccfb..f0ff9c253 100644 --- a/Sources/TestingMacros/SuiteDeclarationMacro.swift +++ b/Sources/TestingMacros/SuiteDeclarationMacro.swift @@ -145,7 +145,7 @@ public struct SuiteDeclarationMacro: MemberMacro, PeerMacro, Sendable { result.append( """ @available(*, deprecated, message: "This property is an implementation detail of the testing library. Do not use it directly.") - private nonisolated static let \(accessorName): Testing.__TestContentRecordAccessor = { outValue, type, _, _ in + @safe private nonisolated static let \(accessorName): Testing.__TestContentRecordAccessor = { outValue, type, _, _ in Testing.Test.__store(\(generatorName), into: outValue, asTypeAt: type) } """ diff --git a/Sources/TestingMacros/Support/TestContentGeneration.swift b/Sources/TestingMacros/Support/TestContentGeneration.swift index 314802005..f40b1e46b 100644 --- a/Sources/TestingMacros/Support/TestContentGeneration.swift +++ b/Sources/TestingMacros/Support/TestContentGeneration.swift @@ -68,7 +68,7 @@ func makeTestContentRecordDecl(named name: TokenSyntax, in typeName: TypeSyntax? private nonisolated \(staticKeyword(for: typeName)) let \(name): Testing.__TestContentRecord = ( \(kindExpr), \(kind.commentRepresentation) 0, - unsafe \(accessorName), + \(accessorName), \(contextExpr), 0 ) diff --git a/Sources/TestingMacros/TestDeclarationMacro.swift b/Sources/TestingMacros/TestDeclarationMacro.swift index 6aebbc729..27df1f5f9 100644 --- a/Sources/TestingMacros/TestDeclarationMacro.swift +++ b/Sources/TestingMacros/TestDeclarationMacro.swift @@ -472,7 +472,7 @@ public struct TestDeclarationMacro: PeerMacro, Sendable { result.append( """ @available(*, deprecated, message: "This property is an implementation detail of the testing library. Do not use it directly.") - private \(staticKeyword(for: typeName)) nonisolated let \(accessorName): Testing.__TestContentRecordAccessor = { outValue, type, _, _ in + @safe private \(staticKeyword(for: typeName)) nonisolated let \(accessorName): Testing.__TestContentRecordAccessor = { outValue, type, _, _ in Testing.Test.__store(\(generatorName), into: outValue, asTypeAt: type) } """ From 79498f46cbe913e769316a63b37815b46cba2ddc Mon Sep 17 00:00:00 2001 From: Jonathan Grynspan Date: Mon, 10 Nov 2025 11:24:45 -0500 Subject: [PATCH 03/12] Revert "Mark accessors @safe" This reverts commit c3efd8778c14e1d8373d43352d9045da2cc60381. --- Sources/TestingMacros/ConditionMacro.swift | 2 +- Sources/TestingMacros/SuiteDeclarationMacro.swift | 2 +- Sources/TestingMacros/Support/TestContentGeneration.swift | 2 +- Sources/TestingMacros/TestDeclarationMacro.swift | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Sources/TestingMacros/ConditionMacro.swift b/Sources/TestingMacros/ConditionMacro.swift index d19cc62dd..9c154b9fa 100644 --- a/Sources/TestingMacros/ConditionMacro.swift +++ b/Sources/TestingMacros/ConditionMacro.swift @@ -507,7 +507,7 @@ extension ExitTestConditionMacro { """ @available(*, deprecated, message: "This type is an implementation detail of the testing library. Do not use it directly.") enum \(enumName) { - @safe private nonisolated static let accessor: Testing.__TestContentRecordAccessor = { outValue, type, hint, _ in + private nonisolated static let accessor: Testing.__TestContentRecordAccessor = { outValue, type, hint, _ in Testing.ExitTest.__store( \(idExpr), \(bodyThunkName), diff --git a/Sources/TestingMacros/SuiteDeclarationMacro.swift b/Sources/TestingMacros/SuiteDeclarationMacro.swift index f0ff9c253..cfe76ccfb 100644 --- a/Sources/TestingMacros/SuiteDeclarationMacro.swift +++ b/Sources/TestingMacros/SuiteDeclarationMacro.swift @@ -145,7 +145,7 @@ public struct SuiteDeclarationMacro: MemberMacro, PeerMacro, Sendable { result.append( """ @available(*, deprecated, message: "This property is an implementation detail of the testing library. Do not use it directly.") - @safe private nonisolated static let \(accessorName): Testing.__TestContentRecordAccessor = { outValue, type, _, _ in + private nonisolated static let \(accessorName): Testing.__TestContentRecordAccessor = { outValue, type, _, _ in Testing.Test.__store(\(generatorName), into: outValue, asTypeAt: type) } """ diff --git a/Sources/TestingMacros/Support/TestContentGeneration.swift b/Sources/TestingMacros/Support/TestContentGeneration.swift index f40b1e46b..314802005 100644 --- a/Sources/TestingMacros/Support/TestContentGeneration.swift +++ b/Sources/TestingMacros/Support/TestContentGeneration.swift @@ -68,7 +68,7 @@ func makeTestContentRecordDecl(named name: TokenSyntax, in typeName: TypeSyntax? private nonisolated \(staticKeyword(for: typeName)) let \(name): Testing.__TestContentRecord = ( \(kindExpr), \(kind.commentRepresentation) 0, - \(accessorName), + unsafe \(accessorName), \(contextExpr), 0 ) diff --git a/Sources/TestingMacros/TestDeclarationMacro.swift b/Sources/TestingMacros/TestDeclarationMacro.swift index 27df1f5f9..6aebbc729 100644 --- a/Sources/TestingMacros/TestDeclarationMacro.swift +++ b/Sources/TestingMacros/TestDeclarationMacro.swift @@ -472,7 +472,7 @@ public struct TestDeclarationMacro: PeerMacro, Sendable { result.append( """ @available(*, deprecated, message: "This property is an implementation detail of the testing library. Do not use it directly.") - @safe private \(staticKeyword(for: typeName)) nonisolated let \(accessorName): Testing.__TestContentRecordAccessor = { outValue, type, _, _ in + private \(staticKeyword(for: typeName)) nonisolated let \(accessorName): Testing.__TestContentRecordAccessor = { outValue, type, _, _ in Testing.Test.__store(\(generatorName), into: outValue, asTypeAt: type) } """ From c0f67e2c0ada137a83bdb5990af4ade68b07930f Mon Sep 17 00:00:00 2001 From: Jonathan Grynspan Date: Fri, 14 Nov 2025 12:55:34 -0500 Subject: [PATCH 04/12] Use @c on accessor functions --- Sources/TestingMacros/ConditionMacro.swift | 2 +- Sources/TestingMacros/SuiteDeclarationMacro.swift | 2 +- Sources/TestingMacros/TestDeclarationMacro.swift | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Sources/TestingMacros/ConditionMacro.swift b/Sources/TestingMacros/ConditionMacro.swift index 9c154b9fa..6bb088629 100644 --- a/Sources/TestingMacros/ConditionMacro.swift +++ b/Sources/TestingMacros/ConditionMacro.swift @@ -507,7 +507,7 @@ extension ExitTestConditionMacro { """ @available(*, deprecated, message: "This type is an implementation detail of the testing library. Do not use it directly.") enum \(enumName) { - private nonisolated static let accessor: Testing.__TestContentRecordAccessor = { outValue, type, hint, _ in + @c private nonisolated static func accessor(_ outValue: UnsafeMutableRawPointer, _ type: UnsafeRawPointer, _ hint: UnsafeRawPointer?, _ reserved: UInt) { Testing.ExitTest.__store( \(idExpr), \(bodyThunkName), diff --git a/Sources/TestingMacros/SuiteDeclarationMacro.swift b/Sources/TestingMacros/SuiteDeclarationMacro.swift index cfe76ccfb..4459099fd 100644 --- a/Sources/TestingMacros/SuiteDeclarationMacro.swift +++ b/Sources/TestingMacros/SuiteDeclarationMacro.swift @@ -145,7 +145,7 @@ public struct SuiteDeclarationMacro: MemberMacro, PeerMacro, Sendable { result.append( """ @available(*, deprecated, message: "This property is an implementation detail of the testing library. Do not use it directly.") - private nonisolated static let \(accessorName): Testing.__TestContentRecordAccessor = { outValue, type, _, _ in + @c private nonisolated static func \(accessorName)(_ outValue: UnsafeMutableRawPointer, _ type: UnsafeRawPointer, _ hint: UnsafeRawPointer?, _ reserved: UInt) { Testing.Test.__store(\(generatorName), into: outValue, asTypeAt: type) } """ diff --git a/Sources/TestingMacros/TestDeclarationMacro.swift b/Sources/TestingMacros/TestDeclarationMacro.swift index 6aebbc729..bd357f3cd 100644 --- a/Sources/TestingMacros/TestDeclarationMacro.swift +++ b/Sources/TestingMacros/TestDeclarationMacro.swift @@ -472,7 +472,7 @@ public struct TestDeclarationMacro: PeerMacro, Sendable { result.append( """ @available(*, deprecated, message: "This property is an implementation detail of the testing library. Do not use it directly.") - private \(staticKeyword(for: typeName)) nonisolated let \(accessorName): Testing.__TestContentRecordAccessor = { outValue, type, _, _ in + @c private nonisolated \(staticKeyword(for: typeName)) func \(accessorName)(_ outValue: UnsafeMutableRawPointer, _ type: UnsafeRawPointer, _: UnsafeRawPointer?, _ reserved: UInt) { Testing.Test.__store(\(generatorName), into: outValue, asTypeAt: type) } """ From 10006e968124e0579bc37279155b67448a6bc0b6 Mon Sep 17 00:00:00 2001 From: Jonathan Grynspan Date: Fri, 14 Nov 2025 12:59:25 -0500 Subject: [PATCH 05/12] Oh yeah return types --- Sources/TestingMacros/ConditionMacro.swift | 2 +- Sources/TestingMacros/SuiteDeclarationMacro.swift | 2 +- Sources/TestingMacros/TestDeclarationMacro.swift | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Sources/TestingMacros/ConditionMacro.swift b/Sources/TestingMacros/ConditionMacro.swift index 6bb088629..a7697c97b 100644 --- a/Sources/TestingMacros/ConditionMacro.swift +++ b/Sources/TestingMacros/ConditionMacro.swift @@ -507,7 +507,7 @@ extension ExitTestConditionMacro { """ @available(*, deprecated, message: "This type is an implementation detail of the testing library. Do not use it directly.") enum \(enumName) { - @c private nonisolated static func accessor(_ outValue: UnsafeMutableRawPointer, _ type: UnsafeRawPointer, _ hint: UnsafeRawPointer?, _ reserved: UInt) { + @c private nonisolated static func accessor(_ outValue: UnsafeMutableRawPointer, _ type: UnsafeRawPointer, _ hint: UnsafeRawPointer?, _: UInt) -> CBool { Testing.ExitTest.__store( \(idExpr), \(bodyThunkName), diff --git a/Sources/TestingMacros/SuiteDeclarationMacro.swift b/Sources/TestingMacros/SuiteDeclarationMacro.swift index 4459099fd..73eb09a76 100644 --- a/Sources/TestingMacros/SuiteDeclarationMacro.swift +++ b/Sources/TestingMacros/SuiteDeclarationMacro.swift @@ -145,7 +145,7 @@ public struct SuiteDeclarationMacro: MemberMacro, PeerMacro, Sendable { result.append( """ @available(*, deprecated, message: "This property is an implementation detail of the testing library. Do not use it directly.") - @c private nonisolated static func \(accessorName)(_ outValue: UnsafeMutableRawPointer, _ type: UnsafeRawPointer, _ hint: UnsafeRawPointer?, _ reserved: UInt) { + @c private nonisolated static func \(accessorName)(_ outValue: UnsafeMutableRawPointer, _ type: UnsafeRawPointer, _ hint: UnsafeRawPointer?, _: UInt) -> CBool { Testing.Test.__store(\(generatorName), into: outValue, asTypeAt: type) } """ diff --git a/Sources/TestingMacros/TestDeclarationMacro.swift b/Sources/TestingMacros/TestDeclarationMacro.swift index bd357f3cd..435f74b02 100644 --- a/Sources/TestingMacros/TestDeclarationMacro.swift +++ b/Sources/TestingMacros/TestDeclarationMacro.swift @@ -472,7 +472,7 @@ public struct TestDeclarationMacro: PeerMacro, Sendable { result.append( """ @available(*, deprecated, message: "This property is an implementation detail of the testing library. Do not use it directly.") - @c private nonisolated \(staticKeyword(for: typeName)) func \(accessorName)(_ outValue: UnsafeMutableRawPointer, _ type: UnsafeRawPointer, _: UnsafeRawPointer?, _ reserved: UInt) { + @c private nonisolated \(staticKeyword(for: typeName)) func \(accessorName)(_ outValue: UnsafeMutableRawPointer, _ type: UnsafeRawPointer, _: UnsafeRawPointer?, _: UInt) -> CBool { Testing.Test.__store(\(generatorName), into: outValue, asTypeAt: type) } """ From 15cb951b3c9e659c44c986b5ac54a835a7a8382a Mon Sep 17 00:00:00 2001 From: Jonathan Grynspan Date: Sat, 13 Dec 2025 12:06:13 -0500 Subject: [PATCH 06/12] Use a closure to call the accessor --- Sources/TestingMacros/Support/TestContentGeneration.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Sources/TestingMacros/Support/TestContentGeneration.swift b/Sources/TestingMacros/Support/TestContentGeneration.swift index 314802005..613628ef2 100644 --- a/Sources/TestingMacros/Support/TestContentGeneration.swift +++ b/Sources/TestingMacros/Support/TestContentGeneration.swift @@ -68,7 +68,7 @@ func makeTestContentRecordDecl(named name: TokenSyntax, in typeName: TypeSyntax? private nonisolated \(staticKeyword(for: typeName)) let \(name): Testing.__TestContentRecord = ( \(kindExpr), \(kind.commentRepresentation) 0, - unsafe \(accessorName), + unsafe { unsafe \(accessorName)($0, $1, $2, $3) }, \(contextExpr), 0 ) From 2fd8f1f771c5e41af928f2873db2ecac9431b774 Mon Sep 17 00:00:00 2001 From: Jonathan Grynspan Date: Sat, 13 Dec 2025 12:13:21 -0500 Subject: [PATCH 07/12] Don't use @c --- Sources/TestingMacros/ConditionMacro.swift | 2 +- Sources/TestingMacros/SuiteDeclarationMacro.swift | 2 +- Sources/TestingMacros/TestDeclarationMacro.swift | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Sources/TestingMacros/ConditionMacro.swift b/Sources/TestingMacros/ConditionMacro.swift index a7697c97b..9c154b9fa 100644 --- a/Sources/TestingMacros/ConditionMacro.swift +++ b/Sources/TestingMacros/ConditionMacro.swift @@ -507,7 +507,7 @@ extension ExitTestConditionMacro { """ @available(*, deprecated, message: "This type is an implementation detail of the testing library. Do not use it directly.") enum \(enumName) { - @c private nonisolated static func accessor(_ outValue: UnsafeMutableRawPointer, _ type: UnsafeRawPointer, _ hint: UnsafeRawPointer?, _: UInt) -> CBool { + private nonisolated static let accessor: Testing.__TestContentRecordAccessor = { outValue, type, hint, _ in Testing.ExitTest.__store( \(idExpr), \(bodyThunkName), diff --git a/Sources/TestingMacros/SuiteDeclarationMacro.swift b/Sources/TestingMacros/SuiteDeclarationMacro.swift index 73eb09a76..cfe76ccfb 100644 --- a/Sources/TestingMacros/SuiteDeclarationMacro.swift +++ b/Sources/TestingMacros/SuiteDeclarationMacro.swift @@ -145,7 +145,7 @@ public struct SuiteDeclarationMacro: MemberMacro, PeerMacro, Sendable { result.append( """ @available(*, deprecated, message: "This property is an implementation detail of the testing library. Do not use it directly.") - @c private nonisolated static func \(accessorName)(_ outValue: UnsafeMutableRawPointer, _ type: UnsafeRawPointer, _ hint: UnsafeRawPointer?, _: UInt) -> CBool { + private nonisolated static let \(accessorName): Testing.__TestContentRecordAccessor = { outValue, type, _, _ in Testing.Test.__store(\(generatorName), into: outValue, asTypeAt: type) } """ diff --git a/Sources/TestingMacros/TestDeclarationMacro.swift b/Sources/TestingMacros/TestDeclarationMacro.swift index 435f74b02..6aebbc729 100644 --- a/Sources/TestingMacros/TestDeclarationMacro.swift +++ b/Sources/TestingMacros/TestDeclarationMacro.swift @@ -472,7 +472,7 @@ public struct TestDeclarationMacro: PeerMacro, Sendable { result.append( """ @available(*, deprecated, message: "This property is an implementation detail of the testing library. Do not use it directly.") - @c private nonisolated \(staticKeyword(for: typeName)) func \(accessorName)(_ outValue: UnsafeMutableRawPointer, _ type: UnsafeRawPointer, _: UnsafeRawPointer?, _: UInt) -> CBool { + private \(staticKeyword(for: typeName)) nonisolated let \(accessorName): Testing.__TestContentRecordAccessor = { outValue, type, _, _ in Testing.Test.__store(\(generatorName), into: outValue, asTypeAt: type) } """ From 17d3b8710467b6ee6f13a4cccbb672b74c5467f2 Mon Sep 17 00:00:00 2001 From: Jonathan Grynspan Date: Sat, 13 Dec 2025 12:17:36 -0500 Subject: [PATCH 08/12] Explicitly specify the type on which the accessor is declared --- .../TestingMacros/Support/TestContentGeneration.swift | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/Sources/TestingMacros/Support/TestContentGeneration.swift b/Sources/TestingMacros/Support/TestContentGeneration.swift index 613628ef2..870b5e34b 100644 --- a/Sources/TestingMacros/Support/TestContentGeneration.swift +++ b/Sources/TestingMacros/Support/TestContentGeneration.swift @@ -63,12 +63,19 @@ func makeTestContentRecordDecl(named name: TokenSyntax, in typeName: TypeSyntax? IntegerLiteralExprSyntax(context, radix: .binary) } + var accessorExpr: ExprSyntax = if let typeName { + "\(typeName.trimmed).\(accessorName)" + } else { + "\(accessorName)" + } + accessorExpr = "{ unsafe \(accessorExpr)($0, $1, $2, $3) }" + var result: DeclSyntax = """ @available(*, deprecated, message: "This property is an implementation detail of the testing library. Do not use it directly.") private nonisolated \(staticKeyword(for: typeName)) let \(name): Testing.__TestContentRecord = ( \(kindExpr), \(kind.commentRepresentation) 0, - unsafe { unsafe \(accessorName)($0, $1, $2, $3) }, + \(accessorExpr), \(contextExpr), 0 ) From 119d10fd148b20b7e0e5aed78e35d441498b829e Mon Sep 17 00:00:00 2001 From: Jonathan Grynspan Date: Mon, 15 Dec 2025 11:55:05 -0500 Subject: [PATCH 09/12] Make __TestContentRecord.accessor non-optional to work around https://github.com/swiftlang/swift/issues/86051 --- Sources/Testing/Discovery+Macro.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Sources/Testing/Discovery+Macro.swift b/Sources/Testing/Discovery+Macro.swift index 35b276efe..f3e598e13 100644 --- a/Sources/Testing/Discovery+Macro.swift +++ b/Sources/Testing/Discovery+Macro.swift @@ -34,7 +34,7 @@ public typealias __TestContentRecordAccessor = @convention(c) ( public typealias __TestContentRecord = ( kind: UInt32, reserved1: UInt32, - accessor: __TestContentRecordAccessor?, + accessor: __TestContentRecordAccessor, context: UInt, reserved2: UInt ) From d1997b25372a9ec04e57a4dafc3968be5e3bbcd7 Mon Sep 17 00:00:00 2001 From: Jonathan Grynspan Date: Mon, 15 Dec 2025 12:18:37 -0500 Subject: [PATCH 10/12] Avoid 'capturing' accessor function references --- Sources/TestingMacros/ConditionMacro.swift | 20 +++++++++---------- .../TestingMacros/SuiteDeclarationMacro.swift | 14 +++---------- .../Support/TestContentGeneration.swift | 12 ++--------- .../TestingMacros/TestDeclarationMacro.swift | 14 +++---------- 4 files changed, 17 insertions(+), 43 deletions(-) diff --git a/Sources/TestingMacros/ConditionMacro.swift b/Sources/TestingMacros/ConditionMacro.swift index 9c154b9fa..9eb42f583 100644 --- a/Sources/TestingMacros/ConditionMacro.swift +++ b/Sources/TestingMacros/ConditionMacro.swift @@ -487,7 +487,15 @@ extension ExitTestConditionMacro { named: .identifier("testContentRecord"), in: TypeSyntax(IdentifierTypeSyntax(name: enumName)), ofKind: .exitTest, - accessingWith: .identifier("accessor") + accessingWith: """ + unsafe Testing.ExitTest.__store( + \(idExpr), + \(bodyThunkName), + into: outValue, + asTypeAt: type, + withHintAt: hint + ) + """ ) // Create another local type for legacy test discovery. @@ -507,16 +515,6 @@ extension ExitTestConditionMacro { """ @available(*, deprecated, message: "This type is an implementation detail of the testing library. Do not use it directly.") enum \(enumName) { - private nonisolated static let accessor: Testing.__TestContentRecordAccessor = { outValue, type, hint, _ in - Testing.ExitTest.__store( - \(idExpr), - \(bodyThunkName), - into: outValue, - asTypeAt: type, - withHintAt: hint - ) - } - \(testContentRecordDecl) \(recordDecl) diff --git a/Sources/TestingMacros/SuiteDeclarationMacro.swift b/Sources/TestingMacros/SuiteDeclarationMacro.swift index cfe76ccfb..d11ac32db 100644 --- a/Sources/TestingMacros/SuiteDeclarationMacro.swift +++ b/Sources/TestingMacros/SuiteDeclarationMacro.swift @@ -141,23 +141,15 @@ public struct SuiteDeclarationMacro: MemberMacro, PeerMacro, Sendable { """ ) - let accessorName = context.makeUniqueName("accessor") - result.append( - """ - @available(*, deprecated, message: "This property is an implementation detail of the testing library. Do not use it directly.") - private nonisolated static let \(accessorName): Testing.__TestContentRecordAccessor = { outValue, type, _, _ in - Testing.Test.__store(\(generatorName), into: outValue, asTypeAt: type) - } - """ - ) - let testContentRecordName = context.makeUniqueName("testContentRecord") result.append( makeTestContentRecordDecl( named: testContentRecordName, in: declaration.type, ofKind: .testDeclaration, - accessingWith: accessorName, + accessingWith: """ + unsafe Testing.Test.__store(\(generatorName), into: outValue, asTypeAt: type) + """, context: attributeInfo.testContentRecordFlags ) ) diff --git a/Sources/TestingMacros/Support/TestContentGeneration.swift b/Sources/TestingMacros/Support/TestContentGeneration.swift index 870b5e34b..c38d5e016 100644 --- a/Sources/TestingMacros/Support/TestContentGeneration.swift +++ b/Sources/TestingMacros/Support/TestContentGeneration.swift @@ -47,15 +47,14 @@ enum TestContentKind: UInt32 { /// - typeName: The name of the type enclosing the resulting declaration, or /// `nil` if it will not be emitted into a type's scope. /// - kind: The kind of test content record being emitted. -/// - accessorName: The Swift name of an `@convention(c)` function to emit -/// into the resulting record. +/// - accessorExpr: The accessor function call expression. /// - context: A value to emit as the `context` field of the test content /// record. /// /// - Returns: A variable declaration that, when emitted into Swift source, will /// cause the linker to emit data in a location that is discoverable at /// runtime. -func makeTestContentRecordDecl(named name: TokenSyntax, in typeName: TypeSyntax? = nil, ofKind kind: TestContentKind, accessingWith accessorName: TokenSyntax, context: UInt32 = 0) -> DeclSyntax { +func makeTestContentRecordDecl(named name: TokenSyntax, in typeName: TypeSyntax? = nil, ofKind kind: TestContentKind, accessingWith accessorExpr: ExprSyntax, context: UInt32 = 0) -> DeclSyntax { let kindExpr = IntegerLiteralExprSyntax(kind.rawValue, radix: .hex) let contextExpr = if context == 0 { IntegerLiteralExprSyntax(0) @@ -63,13 +62,6 @@ func makeTestContentRecordDecl(named name: TokenSyntax, in typeName: TypeSyntax? IntegerLiteralExprSyntax(context, radix: .binary) } - var accessorExpr: ExprSyntax = if let typeName { - "\(typeName.trimmed).\(accessorName)" - } else { - "\(accessorName)" - } - accessorExpr = "{ unsafe \(accessorExpr)($0, $1, $2, $3) }" - var result: DeclSyntax = """ @available(*, deprecated, message: "This property is an implementation detail of the testing library. Do not use it directly.") private nonisolated \(staticKeyword(for: typeName)) let \(name): Testing.__TestContentRecord = ( diff --git a/Sources/TestingMacros/TestDeclarationMacro.swift b/Sources/TestingMacros/TestDeclarationMacro.swift index 6aebbc729..78864b1da 100644 --- a/Sources/TestingMacros/TestDeclarationMacro.swift +++ b/Sources/TestingMacros/TestDeclarationMacro.swift @@ -468,23 +468,15 @@ public struct TestDeclarationMacro: PeerMacro, Sendable { """ ) - let accessorName = context.makeUniqueName(thunking: functionDecl, withPrefix: "accessor") - result.append( - """ - @available(*, deprecated, message: "This property is an implementation detail of the testing library. Do not use it directly.") - private \(staticKeyword(for: typeName)) nonisolated let \(accessorName): Testing.__TestContentRecordAccessor = { outValue, type, _, _ in - Testing.Test.__store(\(generatorName), into: outValue, asTypeAt: type) - } - """ - ) - let testContentRecordName = context.makeUniqueName(thunking: functionDecl, withPrefix: "testContentRecord") result.append( makeTestContentRecordDecl( named: testContentRecordName, in: typeName, ofKind: .testDeclaration, - accessingWith: accessorName, + accessingWith: """ + unsafe Testing.Test.__store(\(generatorName), into: outValue, asTypeAt: type) + """, context: attributeInfo.testContentRecordFlags ) ) From fcc3d12ab4c64d5c2dbffd27d66a4ef3e17aca89 Mon Sep 17 00:00:00 2001 From: Jonathan Grynspan Date: Mon, 15 Dec 2025 12:27:57 -0500 Subject: [PATCH 11/12] sigh --- Sources/TestingMacros/Support/TestContentGeneration.swift | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Sources/TestingMacros/Support/TestContentGeneration.swift b/Sources/TestingMacros/Support/TestContentGeneration.swift index c38d5e016..152998f17 100644 --- a/Sources/TestingMacros/Support/TestContentGeneration.swift +++ b/Sources/TestingMacros/Support/TestContentGeneration.swift @@ -67,7 +67,9 @@ func makeTestContentRecordDecl(named name: TokenSyntax, in typeName: TypeSyntax? private nonisolated \(staticKeyword(for: typeName)) let \(name): Testing.__TestContentRecord = ( \(kindExpr), \(kind.commentRepresentation) 0, - \(accessorExpr), + { outValue, type, hint, _ in + \(accessorExpr) + }, \(contextExpr), 0 ) From 5eb8f8df7d24c32641d79ecd50e5c042c07ec5d1 Mon Sep 17 00:00:00 2001 From: Jonathan Grynspan Date: Mon, 15 Dec 2025 12:41:11 -0500 Subject: [PATCH 12/12] Remove unsafe keywords where we don't need them --- Sources/TestingMacros/ConditionMacro.swift | 2 +- Sources/TestingMacros/SuiteDeclarationMacro.swift | 2 +- Sources/TestingMacros/TestDeclarationMacro.swift | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Sources/TestingMacros/ConditionMacro.swift b/Sources/TestingMacros/ConditionMacro.swift index 9eb42f583..48080f085 100644 --- a/Sources/TestingMacros/ConditionMacro.swift +++ b/Sources/TestingMacros/ConditionMacro.swift @@ -488,7 +488,7 @@ extension ExitTestConditionMacro { in: TypeSyntax(IdentifierTypeSyntax(name: enumName)), ofKind: .exitTest, accessingWith: """ - unsafe Testing.ExitTest.__store( + Testing.ExitTest.__store( \(idExpr), \(bodyThunkName), into: outValue, diff --git a/Sources/TestingMacros/SuiteDeclarationMacro.swift b/Sources/TestingMacros/SuiteDeclarationMacro.swift index d11ac32db..d462f5ea7 100644 --- a/Sources/TestingMacros/SuiteDeclarationMacro.swift +++ b/Sources/TestingMacros/SuiteDeclarationMacro.swift @@ -148,7 +148,7 @@ public struct SuiteDeclarationMacro: MemberMacro, PeerMacro, Sendable { in: declaration.type, ofKind: .testDeclaration, accessingWith: """ - unsafe Testing.Test.__store(\(generatorName), into: outValue, asTypeAt: type) + Testing.Test.__store(\(generatorName), into: outValue, asTypeAt: type) """, context: attributeInfo.testContentRecordFlags ) diff --git a/Sources/TestingMacros/TestDeclarationMacro.swift b/Sources/TestingMacros/TestDeclarationMacro.swift index 78864b1da..d4ba7f7d0 100644 --- a/Sources/TestingMacros/TestDeclarationMacro.swift +++ b/Sources/TestingMacros/TestDeclarationMacro.swift @@ -475,7 +475,7 @@ public struct TestDeclarationMacro: PeerMacro, Sendable { in: typeName, ofKind: .testDeclaration, accessingWith: """ - unsafe Testing.Test.__store(\(generatorName), into: outValue, asTypeAt: type) + Testing.Test.__store(\(generatorName), into: outValue, asTypeAt: type) """, context: attributeInfo.testContentRecordFlags )