From 16a798cc3917b0f0420f48578ab83b3bb2b0eeaf Mon Sep 17 00:00:00 2001 From: Will Hawkins Date: Fri, 22 May 2026 00:04:11 -0400 Subject: [PATCH] testing: Update to Latest ABI Protocol for Test Discovery Signed-off-by: Will Hawkins --- .swift-version | 2 +- Sources/Macros/Macros.swift | 117 +++++++++++++++++++++++++++--------- 2 files changed, 90 insertions(+), 29 deletions(-) diff --git a/.swift-version b/.swift-version index 42cc526..97b1c32 100644 --- a/.swift-version +++ b/.swift-version @@ -1 +1 @@ -6.2.4 +main-snapshot diff --git a/Sources/Macros/Macros.swift b/Sources/Macros/Macros.swift index 0abba87..c8889db 100644 --- a/Sources/Macros/Macros.swift +++ b/Sources/Macros/Macros.swift @@ -291,6 +291,77 @@ public struct MustOr: CodeItemMacro { public struct CliTestDeclarationMacro: PeerMacro, Sendable { + // NOTE: Taken from swift-testing. + + /// Get an expression initializing an instance of ``SourceLocation`` from an + /// arbitrary syntax node. + /// + /// - Parameters: + /// - node: The syntax node for which an instance of ``SourceLocation`` is + /// needed. + /// - context: The macro context in which the expression is being parsed. + /// + /// - Returns: An expression value that initializes an instance of + /// ``SourceLocation`` for `node`. + static func createSourceLocationExpr( + of node: some SyntaxProtocol, context: some MacroExpansionContext + ) -> ExprSyntax { + if node.isProtocol((any FreestandingMacroExpansionSyntax).self) { + // Freestanding macro expressions can just use __here() + // directly and do not need to talk to the macro context to get source + // location info. + return "Testing.SourceLocation.__here()" + } + + // Get the equivalent source location in both `#fileID` and `#filePath` modes. + guard let fileIDSourceLoc: AbstractSourceLocation = context.location(of: node), + let filePathSourceLoc: AbstractSourceLocation = context.location( + of: node, at: .afterLeadingTrivia, filePathMode: .filePath) + else { + return "Testing.SourceLocation.__here()" + } + + return + "Testing.SourceLocation(__uncheckedFileID: \(fileIDSourceLoc.file), filePath: \(filePathSourceLoc.file), line: \(fileIDSourceLoc.line), column: \(fileIDSourceLoc.column))" + } + + /// Get an expression initializing an instance of `__SourceBounds` from two + /// arbitrary syntax nodesvalues. + /// + /// - Parameters: + /// - lowerBoundNode: The syntax node representing the lower bound. The start + /// of this node (after leading trivia) is used. + /// - upperBoundNode: The syntax node representing the upper bound. The end of + /// this node (before trailing trivia) is used. + /// - context: The macro context in which the expression is being parsed. + /// + /// - Returns: An expression value that initializes an instance of + /// `__SourceBounds`. + /// + /// The resulting source bounds instance represents (approximately): + /// + /// ```swift + /// lowerBoundNode.positionAfterSkippingLeadingTrivia ..< upperBoundNode.endPositionBeforeTrailingTrivia + /// ``` + static func createSourceBoundsExpr( + from lowerBoundNode: some SyntaxProtocol, to upperBoundNode: some SyntaxProtocol, + in context: some MacroExpansionContext + ) -> ExprSyntax { + let lowerBoundExpr = createSourceLocationExpr(of: lowerBoundNode, context: context) + let upperBoundExpr: ExprSyntax = + if let upperBoundSourceLoc = context.location( + of: upperBoundNode, at: .beforeTrailingTrivia, filePathMode: .fileID) + { + "(\(upperBoundSourceLoc.line), \(upperBoundSourceLoc.column))" + } else { + "(.max, .max)" + } + return + "Testing.__SourceBounds(__uncheckedLowerBound: \(lowerBoundExpr), upperBound: \(upperBoundExpr))" + } + + // NOTE: End of what was taken from swift-testing + private static func doc_shrink(_ from: String) -> String { return from.replacing(Regex(#/^.*\/\/\/[\s]+/#), with: "") } @@ -307,8 +378,7 @@ public struct CliTestDeclarationMacro: PeerMacro, Sendable { doc_shrink("\($0)") }).joined(separator: "\\n") - let cli_test_driver_thunk_name = context.makeUniqueName( - declaration.cast(FunctionDeclSyntax.self).name.text + "_thunk_") + let cli_test_driver_thunk_name = context.makeUniqueName("_thunk_") let (expected_decl, expected_label) = if cli_test_expected_output.isEmpty { @@ -327,53 +397,44 @@ public struct CliTestDeclarationMacro: PeerMacro, Sendable { } """ - let cli_test_driver_generator_name = context.makeUniqueName(test_name.text + "_generator_") + let source_bounds = createSourceBoundsExpr(from: node, to: declaration, in: context) + let cli_test_driver_generator_name = context.makeUniqueName("_generator_") let cli_test_driver_generator: DeclSyntax = """ @Sendable private func \(cli_test_driver_generator_name)() async -> Testing.Test { return .__function( named: "\(test_name)", - in: nil, + in: nil as Swift.Never.Type?, xcTestCompatibleSelector: Testing.__xcTestCompatibleSelector("\(test_name)"), traits: [], - sourceLocation: Testing.SourceLocation.__here(), + sourceBounds: \(source_bounds), parameters: [], testFunction: \(cli_test_driver_thunk_name) ) } """ - let cli_test_driver_accessor_name = context.makeUniqueName(test_name.text + "_accessor_") - let cli_test_driver_accessor: DeclSyntax = """ - private nonisolated let \(cli_test_driver_accessor_name): Accessor = { outValue, type, _, _ in + #if os(macOS) + let section = "__DATA_CONST,__swift5_tests" + #else + let section = "swift5_tests" + #endif - Testing.Test.__store(\(cli_test_driver_generator_name), into: outValue, asTypeAt: type) - } - """ - - let cli_test_driver_content_record_name = context.makeUniqueName( - declaration.cast(FunctionDeclSyntax.self).name.text + "_cr_") + let cli_test_driver_content_record_name = context.makeUniqueName("testContentRecord") let cli_test_driver_cr: DeclSyntax = """ - private nonisolated let \(cli_test_driver_content_record_name): TestContentRecord = ( + @section("\(raw: section)") + @used + private nonisolated let \(cli_test_driver_content_record_name): Testing.__TestContentRecord = ( 0x7465_7374, /* indicate a test */ 0, - unsafe \(cli_test_driver_accessor_name), + { outValue, type, _, _ in + Testing.Test.__store(\(cli_test_driver_generator_name), into: outValue, asTypeAt: type) + }, 0, 0 ) """ - - let cli_test_driver_content_container_name = context.makeUniqueName( - test_name.text + "__🟡$_container_") - let cli_test_driver_container: DeclSyntax = """ - struct \(cli_test_driver_content_container_name): Testing.__TestContentRecordContainer { - nonisolated static var __testContentRecord: TestContentRecord { - unsafe \(cli_test_driver_content_record_name) - } - } - """ return [ - cli_test_driver_thunk, cli_test_driver_generator, cli_test_driver_accessor, - cli_test_driver_cr, cli_test_driver_container, + cli_test_driver_thunk, cli_test_driver_generator, cli_test_driver_cr, ] } }