From abc1825b38fab3b281a75708c61778a66f7dd100 Mon Sep 17 00:00:00 2001 From: Jonathan Grynspan Date: Tue, 8 Oct 2024 18:46:00 -0400 Subject: [PATCH 1/6] Store test content in a custom metadata section. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This PR uses the experimental symbol linkage margers feature in the Swift compiler to emit metadata about tests (and exit tests) into a dedicated section of the test executable being built. At runtime, we discover that section and read out the tests from it. This has several benefits over our current model, which involves walking Swift's type metadata table looking for types that conform to a protocol: 1. We don't need to define that protocol as public API in Swift Testing, 1. We don't need to emit type metadata (much larger than what we really need) for every test function, 1. We don't need to duplicate a large chunk of the Swift ABI sources in order to walk the type metadata table correctly, and 1. Almost all the new code is written in Swift, whereas the code it is intended to replace could not be fully represented in Swift and needed to be written in C++. The change also opens up the possibility of supporting generic types in the future because we can emit metadata without needing to emit a nested type (which is not always valid in a generic context.) That's a "future direction" and not covered by this PR specifically. I've defined a layout for entries in the new `swift5_tests` section that should be flexible enough for us in the short-to-medium term and which lets us define additional arbitrary test content record types. The layout of this section is covered in depth in the new [TestContent.md](Documentation/ABI/TestContent.md) article. This functionality is only available if a test target enables the experimental `"SymbolLinkageMarkers"` feature. We continue to emit protocol-conforming types for nowβ€”that code will be removed if and when the experimental feature is properly supported (modulo us adopting relevant changes to the feature's API.) https://github.com/swiftlang/swift-testing/issues/735 https://github.com/swiftlang/swift/issues/76698 https://github.com/swiftlang/swift/pull/78411 --- Documentation/Porting.md | 6 ++++ Package.swift | 10 +++--- Sources/Testing/ExitTests/ExitTest.swift | 2 ++ Sources/Testing/Test+Discovery+Legacy.swift | 2 ++ Sources/Testing/Test+Discovery.swift | 6 ++++ Sources/TestingMacros/ConditionMacro.swift | 32 +++++++++++++++++-- .../TestingMacros/SuiteDeclarationMacro.swift | 6 ++++ .../Support/TestContentGeneration.swift | 12 +++++++ .../TestingMacros/TestDeclarationMacro.swift | 19 ++++------- Sources/_TestDiscovery/SectionBounds.swift | 8 +++++ .../_TestDiscovery/TestContentRecord.swift | 2 ++ Sources/_TestingInternals/Discovery.cpp | 12 +++++++ .../TestDeclarationMacroTests.swift | 5 +++ Tests/TestingTests/MiscellaneousTests.swift | 17 ++++++++++ 14 files changed, 120 insertions(+), 19 deletions(-) diff --git a/Documentation/Porting.md b/Documentation/Porting.md index ce179d53d..0cc6bf70b 100644 --- a/Documentation/Porting.md +++ b/Documentation/Porting.md @@ -145,8 +145,10 @@ to load that information: + let resourceName: Str255 = switch kind { + case .testContent: + "__swift5_tests" ++#if !SWT_NO_LEGACY_TEST_DISCOVERY + case .typeMetadata: + "__swift5_types" ++#endif + } + + let oldRefNum = CurResFile() @@ -219,15 +221,19 @@ diff --git a/Sources/_TestingInternals/Discovery.cpp b/Sources/_TestingInternals +#elif defined(macintosh) +extern "C" const char testContentSectionBegin __asm__("..."); +extern "C" const char testContentSectionEnd __asm__("..."); ++#if !defined(SWT_NO_LEGACY_TEST_DISCOVERY) +extern "C" const char typeMetadataSectionBegin __asm__("..."); +extern "C" const char typeMetadataSectionEnd __asm__("..."); ++#endif #else #warning Platform-specific implementation missing: Runtime test discovery unavailable (static) static const char testContentSectionBegin = 0; static const char& testContentSectionEnd = testContentSectionBegin; + #if !defined(SWT_NO_LEGACY_TEST_DISCOVERY) static const char typeMetadataSectionBegin = 0; static const char& typeMetadataSectionEnd = testContentSectionBegin; #endif + #endif ``` These symbols must have unique addresses corresponding to the first byte of the diff --git a/Package.swift b/Package.swift index 11adfc14e..e2257af1a 100644 --- a/Package.swift +++ b/Package.swift @@ -89,10 +89,7 @@ let package = Package( "_Testing_CoreGraphics", "_Testing_Foundation", ], - swiftSettings: .packageSettings + [ - // For testing test content section discovery only - .enableExperimentalFeature("SymbolLinkageMarkers"), - ] + swiftSettings: .packageSettings ), .macro( @@ -205,6 +202,11 @@ extension Array where Element == PackageDescription.SwiftSetting { .enableExperimentalFeature("AccessLevelOnImport"), .enableUpcomingFeature("InternalImportsByDefault"), + // 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"), + // When building as a package, the macro plugin always builds as an // executable rather than a library. .define("SWT_NO_LIBRARY_MACRO_PLUGINS"), diff --git a/Sources/Testing/ExitTests/ExitTest.swift b/Sources/Testing/ExitTests/ExitTest.swift index 5b800f0c0..61b35b9fd 100644 --- a/Sources/Testing/ExitTests/ExitTest.swift +++ b/Sources/Testing/ExitTests/ExitTest.swift @@ -296,12 +296,14 @@ extension ExitTest { } } +#if !SWT_NO_LEGACY_TEST_DISCOVERY // Call the legacy lookup function that discovers tests embedded in types. for record in Self.allTypeMetadataBasedTestContentRecords() { if let exitTest = record.load(withHint: id) { return exitTest } } +#endif return nil } diff --git a/Sources/Testing/Test+Discovery+Legacy.swift b/Sources/Testing/Test+Discovery+Legacy.swift index 0be944ad9..711f95e73 100644 --- a/Sources/Testing/Test+Discovery+Legacy.swift +++ b/Sources/Testing/Test+Discovery+Legacy.swift @@ -8,6 +8,7 @@ // See https://swift.org/CONTRIBUTORS.txt for Swift project authors // +#if !SWT_NO_LEGACY_TEST_DISCOVERY @_spi(Experimental) @_spi(ForToolsIntegrationOnly) internal import _TestDiscovery /// A shadow declaration of `_TestDiscovery.TestContentRecordContainer` that @@ -41,3 +42,4 @@ open class __TestContentRecordContainer: TestContentRecordContainer { @available(*, unavailable) extension __TestContentRecordContainer: Sendable {} +#endif diff --git a/Sources/Testing/Test+Discovery.swift b/Sources/Testing/Test+Discovery.swift index a8cc831c4..2568353f8 100644 --- a/Sources/Testing/Test+Discovery.swift +++ b/Sources/Testing/Test+Discovery.swift @@ -65,6 +65,7 @@ extension Test { // the legacy and new mechanisms, but we can set an environment variable // to explicitly select one or the other. When we remove legacy support, // we can also remove this enumeration and environment variable check. +#if !SWT_NO_LEGACY_TEST_DISCOVERY let (useNewMode, useLegacyMode) = switch Environment.flag(named: "SWT_USE_LEGACY_TEST_DISCOVERY") { case .none: (true, true) @@ -73,6 +74,9 @@ extension Test { case .some(false): (true, false) } +#else + let useNewMode = true +#endif // Walk all test content and gather generator functions, then call them in // a task group and collate their results. @@ -86,6 +90,7 @@ extension Test { } } +#if !SWT_NO_LEGACY_TEST_DISCOVERY // Perform legacy test discovery if needed. if useLegacyMode && result.isEmpty { let generators = Generator.allTypeMetadataBasedTestContentRecords().lazy.compactMap { $0.load() } @@ -96,6 +101,7 @@ extension Test { result = await taskGroup.reduce(into: result) { $0.insert($1) } } } +#endif return result } diff --git a/Sources/TestingMacros/ConditionMacro.swift b/Sources/TestingMacros/ConditionMacro.swift index 01cac9a3a..e6f99fa2f 100644 --- a/Sources/TestingMacros/ConditionMacro.swift +++ b/Sources/TestingMacros/ConditionMacro.swift @@ -11,6 +11,10 @@ public import SwiftSyntax 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(exitsWith:)") +#endif + /// A protocol containing the common implementation for the expansions of the /// `#expect()` and `#require()` macros. /// @@ -460,9 +464,9 @@ extension ExitTestConditionMacro { accessingWith: .identifier("accessor") ) +#if !SWT_NO_LEGACY_TEST_DISCOVERY decls.append( """ - @available(*, deprecated, message: "This type is an implementation detail of the testing library. Do not use it directly.") final class \(className): Testing.__TestContentRecordContainer { private nonisolated static let accessor: Testing.__TestContentRecordAccessor = { outValue, type, hint in Testing.ExitTest.__store( @@ -482,12 +486,34 @@ extension ExitTestConditionMacro { } """ ) +#else + decls.append( + """ + final class \(className) { + private nonisolated static let accessor: Testing.__TestContentRecordAccessor = { outValue, type, hint in + Testing.ExitTest.__store( + \(exitTestIDExpr), + \(bodyThunkName), + into: outValue, + asTypeAt: type, + withHintAt: hint + ) + } + + \(testContentRecordDecl) + } + """ + ) +#endif arguments[trailingClosureIndex].expression = ExprSyntax( ClosureExprSyntax { for decl in decls { - CodeBlockItemSyntax(item: .decl(decl)) - .with(\.trailingTrivia, .newline) + CodeBlockItemSyntax( + leadingTrivia: .newline, + item: .decl(decl), + trailingTrivia: .newline + ) } } ) diff --git a/Sources/TestingMacros/SuiteDeclarationMacro.swift b/Sources/TestingMacros/SuiteDeclarationMacro.swift index b47109291..c90606577 100644 --- a/Sources/TestingMacros/SuiteDeclarationMacro.swift +++ b/Sources/TestingMacros/SuiteDeclarationMacro.swift @@ -11,6 +11,10 @@ public import SwiftSyntax 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 @@ -160,6 +164,7 @@ public struct SuiteDeclarationMacro: MemberMacro, PeerMacro, Sendable { ) ) +#if !SWT_NO_LEGACY_TEST_DISCOVERY // Emit a type that contains a reference to the test content record. let className = context.makeUniqueName("__🟑$") result.append( @@ -172,6 +177,7 @@ public struct SuiteDeclarationMacro: MemberMacro, PeerMacro, Sendable { } """ ) +#endif return result } diff --git a/Sources/TestingMacros/Support/TestContentGeneration.swift b/Sources/TestingMacros/Support/TestContentGeneration.swift index 646bd97d4..391a468b1 100644 --- a/Sources/TestingMacros/Support/TestContentGeneration.swift +++ b/Sources/TestingMacros/Support/TestContentGeneration.swift @@ -62,6 +62,18 @@ func makeTestContentRecordDecl(named name: TokenSyntax, in typeName: TypeSyntax? } return """ + #if hasFeature(SymbolLinkageMarkers) + #if os(macOS) || os(iOS) || os(watchOS) || os(tvOS) || os(visionOS) + @_section("__DATA_CONST,__swift5_tests") + #elseif os(Linux) || os(FreeBSD) || os(OpenBSD) || os(Android) || os(WASI) + @_section("swift5_tests") + #elseif os(Windows) + @_section(".sw5test$B") + #else + @__testing(warning: "Platform-specific implementation missing: test content section name unavailable") + #endif + @_used + #endif @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) diff --git a/Sources/TestingMacros/TestDeclarationMacro.swift b/Sources/TestingMacros/TestDeclarationMacro.swift index 21faed78f..3c6b12fe0 100644 --- a/Sources/TestingMacros/TestDeclarationMacro.swift +++ b/Sources/TestingMacros/TestDeclarationMacro.swift @@ -11,6 +11,10 @@ public import SwiftSyntax 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 @@ -188,17 +192,6 @@ public struct TestDeclarationMacro: PeerMacro, Sendable { return FunctionParameterClauseSyntax(parameters: parameterList) } - /// The `static` keyword, if `typeName` is not `nil`. - /// - /// - Parameters: - /// - typeName: The name of the type containing the macro being expanded. - /// - /// - Returns: A token representing the `static` keyword, or one representing - /// nothing if `typeName` is `nil`. - private static func _staticKeyword(for typeName: TypeSyntax?) -> TokenSyntax { - (typeName != nil) ? .keyword(.static) : .unknown("") - } - /// Create a thunk function with a normalized signature that calls a /// developer-supplied test function. /// @@ -356,7 +349,7 @@ public struct TestDeclarationMacro: PeerMacro, Sendable { let thunkName = context.makeUniqueName(thunking: functionDecl) let thunkDecl: DeclSyntax = """ @available(*, deprecated, message: "This function is an implementation detail of the testing library. Do not use it directly.") - @Sendable private \(_staticKeyword(for: typeName)) func \(thunkName)\(thunkParamsExpr) async throws -> Void { + @Sendable private \(staticKeyword(for: typeName)) func \(thunkName)\(thunkParamsExpr) async throws -> Void { \(thunkBody) } """ @@ -496,6 +489,7 @@ public struct TestDeclarationMacro: PeerMacro, Sendable { ) ) +#if !SWT_NO_LEGACY_TEST_DISCOVERY // Emit a type that contains a reference to the test content record. let className = context.makeUniqueName(thunking: functionDecl, withPrefix: "__🟑$") result.append( @@ -508,6 +502,7 @@ public struct TestDeclarationMacro: PeerMacro, Sendable { } """ ) +#endif return result } diff --git a/Sources/_TestDiscovery/SectionBounds.swift b/Sources/_TestDiscovery/SectionBounds.swift index 1fc379258..212edbfbf 100644 --- a/Sources/_TestDiscovery/SectionBounds.swift +++ b/Sources/_TestDiscovery/SectionBounds.swift @@ -27,8 +27,10 @@ struct SectionBounds: Sendable { /// The test content metadata section. case testContent +#if !SWT_NO_LEGACY_TEST_DISCOVERY /// The type metadata section. case typeMetadata +#endif } /// All section bounds of the given kind found in the current process. @@ -60,8 +62,10 @@ extension SectionBounds.Kind { switch self { case .testContent: ("__DATA_CONST", "__swift5_tests") +#if !SWT_NO_LEGACY_TEST_DISCOVERY case .typeMetadata: ("__TEXT", "__swift5_types") +#endif } } } @@ -186,8 +190,10 @@ private func _sectionBounds(_ kind: SectionBounds.Kind) -> [SectionBounds] { let range = switch context.pointee.kind { case .testContent: sections.swift5_tests +#if !SWT_NO_LEGACY_TEST_DISCOVERY case .typeMetadata: sections.swift5_type_metadata +#endif } let start = UnsafeRawPointer(bitPattern: range.start) let size = Int(clamping: range.length) @@ -276,8 +282,10 @@ private func _sectionBounds(_ kind: SectionBounds.Kind) -> some Sequence
+#if !defined(SWT_NO_LEGACY_TEST_DISCOVERY) #include #include #include +#endif #if defined(SWT_NO_DYNAMIC_LINKING) #pragma mark - Statically-linked section bounds @@ -21,24 +23,32 @@ #if defined(__APPLE__) extern "C" const char testContentSectionBegin __asm("section$start$__DATA_CONST$__swift5_tests"); extern "C" const char testContentSectionEnd __asm("section$end$__DATA_CONST$__swift5_tests"); +#if !defined(SWT_NO_LEGACY_TEST_DISCOVERY) extern "C" const char typeMetadataSectionBegin __asm__("section$start$__TEXT$__swift5_types"); extern "C" const char typeMetadataSectionEnd __asm__("section$end$__TEXT$__swift5_types"); +#endif #elif defined(__wasi__) extern "C" const char testContentSectionBegin __asm__("__start_swift5_tests"); extern "C" const char testContentSectionEnd __asm__("__stop_swift5_tests"); +#if !defined(SWT_NO_LEGACY_TEST_DISCOVERY) extern "C" const char typeMetadataSectionBegin __asm__("__start_swift5_type_metadata"); extern "C" const char typeMetadataSectionEnd __asm__("__stop_swift5_type_metadata"); +#endif #else #warning Platform-specific implementation missing: Runtime test discovery unavailable (static) static const char testContentSectionBegin = 0; static const char& testContentSectionEnd = testContentSectionBegin; +#if !defined(SWT_NO_LEGACY_TEST_DISCOVERY) static const char typeMetadataSectionBegin = 0; static const char& typeMetadataSectionEnd = typeMetadataSectionBegin; #endif +#endif static constexpr const char *const staticallyLinkedSectionBounds[][2] = { { &testContentSectionBegin, &testContentSectionEnd }, +#if !defined(SWT_NO_LEGACY_TEST_DISCOVERY) { &typeMetadataSectionBegin, &typeMetadataSectionEnd }, +#endif }; void swt_getStaticallyLinkedSectionBounds(size_t kind, const void **outSectionBegin, size_t *outByteCount) { @@ -48,6 +58,7 @@ void swt_getStaticallyLinkedSectionBounds(size_t kind, const void **outSectionBe } #endif +#if !defined(SWT_NO_LEGACY_TEST_DISCOVERY) #pragma mark - Swift ABI #if defined(__PTRAUTH_INTRINSICS__) @@ -221,3 +232,4 @@ const void *swt_getTypeFromTypeMetadataRecord(const void *recordAddress, const c return nullptr; } +#endif diff --git a/Tests/TestingMacrosTests/TestDeclarationMacroTests.swift b/Tests/TestingMacrosTests/TestDeclarationMacroTests.swift index 6ac3542a9..b0028c438 100644 --- a/Tests/TestingMacrosTests/TestDeclarationMacroTests.swift +++ b/Tests/TestingMacrosTests/TestDeclarationMacroTests.swift @@ -408,7 +408,12 @@ 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 #expect(output.contains("__TestContentRecordContainer")) +#endif if let expectedTypeName { #expect(output.contains(expectedTypeName)) } diff --git a/Tests/TestingTests/MiscellaneousTests.swift b/Tests/TestingTests/MiscellaneousTests.swift index ec7fdd12d..a6c62fdbc 100644 --- a/Tests/TestingTests/MiscellaneousTests.swift +++ b/Tests/TestingTests/MiscellaneousTests.swift @@ -664,4 +664,21 @@ struct MiscellaneousTests { }) } #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 72b7db274f2a5df18d65b4282fafa1d58b8e0ac6 Mon Sep 17 00:00:00 2001 From: Jonathan Grynspan Date: Tue, 11 Mar 2025 10:37:01 -0400 Subject: [PATCH 2/6] Fix typo --- Documentation/Porting.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Documentation/Porting.md b/Documentation/Porting.md index 0cc6bf70b..8b230ff22 100644 --- a/Documentation/Porting.md +++ b/Documentation/Porting.md @@ -231,7 +231,7 @@ diff --git a/Sources/_TestingInternals/Discovery.cpp b/Sources/_TestingInternals static const char& testContentSectionEnd = testContentSectionBegin; #if !defined(SWT_NO_LEGACY_TEST_DISCOVERY) static const char typeMetadataSectionBegin = 0; - static const char& typeMetadataSectionEnd = testContentSectionBegin; + static const char& typeMetadataSectionEnd = typeMetadataSectionBegin; #endif #endif ``` From eda41a975a95fea510d8db3dc79d2944c5b8f91c Mon Sep 17 00:00:00 2001 From: Jonathan Grynspan Date: Tue, 11 Mar 2025 13:02:23 -0400 Subject: [PATCH 3/6] Reduce code duplication, fix build-time warning --- Sources/TestingMacros/ConditionMacro.swift | 35 +++++++++++----------- 1 file changed, 17 insertions(+), 18 deletions(-) diff --git a/Sources/TestingMacros/ConditionMacro.swift b/Sources/TestingMacros/ConditionMacro.swift index e6f99fa2f..f332e33e0 100644 --- a/Sources/TestingMacros/ConditionMacro.swift +++ b/Sources/TestingMacros/ConditionMacro.swift @@ -454,6 +454,19 @@ extension ExitTestConditionMacro { """ ) + // Create the accessor function for the test content record. + let accessorDecl: DeclSyntax = """ + private nonisolated static let accessor: Testing.__TestContentRecordAccessor = { outValue, type, hint in + Testing.ExitTest.__store( + \(exitTestIDExpr), + \(bodyThunkName), + into: outValue, + asTypeAt: type, + withHintAt: hint + ) + } + """ + // Create a local type that can be discovered at runtime and which contains // the exit test body. let className = context.makeUniqueName("__🟑$") @@ -467,16 +480,9 @@ extension ExitTestConditionMacro { #if !SWT_NO_LEGACY_TEST_DISCOVERY decls.append( """ + @available(*, deprecated, message: "This type is an implementation detail of the testing library. Do not use it directly.") final class \(className): Testing.__TestContentRecordContainer { - private nonisolated static let accessor: Testing.__TestContentRecordAccessor = { outValue, type, hint in - Testing.ExitTest.__store( - \(exitTestIDExpr), - \(bodyThunkName), - into: outValue, - asTypeAt: type, - withHintAt: hint - ) - } + \(accessorDecl) \(testContentRecordDecl) @@ -489,16 +495,9 @@ extension ExitTestConditionMacro { #else decls.append( """ + @available(*, deprecated, message: "This type is an implementation detail of the testing library. Do not use it directly.") final class \(className) { - private nonisolated static let accessor: Testing.__TestContentRecordAccessor = { outValue, type, hint in - Testing.ExitTest.__store( - \(exitTestIDExpr), - \(bodyThunkName), - into: outValue, - asTypeAt: type, - withHintAt: hint - ) - } + \(accessorDecl) \(testContentRecordDecl) } From 6a5fc6dba79fbbe0b14fdfe7edbe8a29605b9c55 Mon Sep 17 00:00:00 2001 From: Jonathan Grynspan Date: Tue, 11 Mar 2025 13:24:54 -0400 Subject: [PATCH 4/6] Bit more cheese --- Sources/TestingMacros/ConditionMacro.swift | 78 ++++++++++------------ 1 file changed, 37 insertions(+), 41 deletions(-) diff --git a/Sources/TestingMacros/ConditionMacro.swift b/Sources/TestingMacros/ConditionMacro.swift index f332e33e0..43d28aa53 100644 --- a/Sources/TestingMacros/ConditionMacro.swift +++ b/Sources/TestingMacros/ConditionMacro.swift @@ -454,57 +454,53 @@ extension ExitTestConditionMacro { """ ) - // Create the accessor function for the test content record. - let accessorDecl: DeclSyntax = """ - private nonisolated static let accessor: Testing.__TestContentRecordAccessor = { outValue, type, hint in - Testing.ExitTest.__store( - \(exitTestIDExpr), - \(bodyThunkName), - into: outValue, - asTypeAt: type, - withHintAt: hint - ) - } - """ - // Create a local type that can be discovered at runtime and which contains // the exit test body. - let className = context.makeUniqueName("__🟑$") - let testContentRecordDecl = makeTestContentRecordDecl( - named: .identifier("testContentRecord"), - in: TypeSyntax(IdentifierTypeSyntax(name: className)), - ofKind: .exitTest, - accessingWith: .identifier("accessor") - ) + let enumName = context.makeUniqueName("") + do { + // Create the test content record. + let testContentRecordDecl = makeTestContentRecordDecl( + named: .identifier("testContentRecord"), + in: TypeSyntax(IdentifierTypeSyntax(name: enumName)), + ofKind: .exitTest, + accessingWith: .identifier("accessor") + ) + // Create another local type for legacy test discovery. + var recordDecl: DeclSyntax? #if !SWT_NO_LEGACY_TEST_DISCOVERY - decls.append( - """ - @available(*, deprecated, message: "This type is an implementation detail of the testing library. Do not use it directly.") - final class \(className): Testing.__TestContentRecordContainer { - \(accessorDecl) - - \(testContentRecordDecl) - + let className = context.makeUniqueName("__🟑$") + recordDecl = """ + private final class \(className): Testing.__TestContentRecordContainer { override nonisolated class var __testContentRecord: Testing.__TestContentRecord { - testContentRecord + \(enumName).testContentRecord } } """ - ) -#else - decls.append( - """ - @available(*, deprecated, message: "This type is an implementation detail of the testing library. Do not use it directly.") - final class \(className) { - \(accessorDecl) - - \(testContentRecordDecl) - } - """ - ) #endif + decls.append( + """ + @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( + \(exitTestIDExpr), + \(bodyThunkName), + into: outValue, + asTypeAt: type, + withHintAt: hint + ) + } + + \(testContentRecordDecl) + + \(recordDecl) + } + """ + ) + } + arguments[trailingClosureIndex].expression = ExprSyntax( ClosureExprSyntax { for decl in decls { From 003f09f2a4cb9a4efd68bc08196b92d98676d555 Mon Sep 17 00:00:00 2001 From: Jonathan Grynspan Date: Tue, 11 Mar 2025 14:17:55 -0400 Subject: [PATCH 5/6] Remove private keyword from a type that was not previously private --- Sources/TestingMacros/ConditionMacro.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Sources/TestingMacros/ConditionMacro.swift b/Sources/TestingMacros/ConditionMacro.swift index 43d28aa53..715b2d235 100644 --- a/Sources/TestingMacros/ConditionMacro.swift +++ b/Sources/TestingMacros/ConditionMacro.swift @@ -471,7 +471,7 @@ extension ExitTestConditionMacro { #if !SWT_NO_LEGACY_TEST_DISCOVERY let className = context.makeUniqueName("__🟑$") recordDecl = """ - private final class \(className): Testing.__TestContentRecordContainer { + final class \(className): Testing.__TestContentRecordContainer { override nonisolated class var __testContentRecord: Testing.__TestContentRecord { \(enumName).testContentRecord } From 4d3e8b17036b8e3162a47b694d9aee2fb917c99d Mon Sep 17 00:00:00 2001 From: Jonathan Grynspan Date: Tue, 11 Mar 2025 14:57:17 -0400 Subject: [PATCH 6/6] Revert "Remove private keyword from a type that was not previously private" This reverts commit 003f09f2a4cb9a4efd68bc08196b92d98676d555. --- Sources/TestingMacros/ConditionMacro.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Sources/TestingMacros/ConditionMacro.swift b/Sources/TestingMacros/ConditionMacro.swift index 715b2d235..43d28aa53 100644 --- a/Sources/TestingMacros/ConditionMacro.swift +++ b/Sources/TestingMacros/ConditionMacro.swift @@ -471,7 +471,7 @@ extension ExitTestConditionMacro { #if !SWT_NO_LEGACY_TEST_DISCOVERY let className = context.makeUniqueName("__🟑$") recordDecl = """ - final class \(className): Testing.__TestContentRecordContainer { + private final class \(className): Testing.__TestContentRecordContainer { override nonisolated class var __testContentRecord: Testing.__TestContentRecord { \(enumName).testContentRecord }