Skip to content

Update macro template to support Swift Testing tests #8890 #8897

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: main
Choose a base branch
from

Conversation

MaelRB
Copy link
Contributor

@MaelRB MaelRB commented Jul 5, 2025

Add support for macro project using Swift Testing in InitPackage.

Motivation:

Since the introduction of SwiftSyntaxMacrosGenericTestSupport, Swift Testing should be supported to test macro

Modifications:

  • Update writeMacroTestsFile to support Swift Testing
  • Use MacroSpec for both XCTest and Swift Testing
  • Remove swift-testing dependency from the Package manifest when building macro template for Swift Testing
  • Remove unsupportedTestingLibraryForPackageType error as it's unused
  • Add unit tests for macro template generation

Result:

Generated test file when using Swift Testing:

import SwiftSyntax
import SwiftSyntaxBuilder
import SwiftSyntaxMacros
import SwiftSyntaxMacroExpansion
import Testing
import SwiftSyntaxMacrosGenericTestSupport

// Macro implementations build for the host, so the corresponding module is not available when cross-compiling. Cross-compiled tests may still make use of the macro itself in end-to-end tests.
#if canImport(fooMacros)
import fooMacros

let testMacros: [String: MacroSpec] = [
    "stringify": MacroSpec(type: StringifyMacro.self),
]
#endif

struct fooTests {
    
    @Test
    func macro() {
#if canImport(fooMacros)
        assertMacroExpansion(
            """
            #stringify(a + b)
            """,
            expandedSource: """
            (a + b, "a + b")
            """,
            macroSpecs: testMacros
        ) {
            Issue.record(
                "\($0.message)",
                sourceLocation:
                    SourceLocation(
                        fileID: $0.location.fileID,
                        filePath: $0.location.filePath,
                        line: $0.location.line,
                        column: $0.location.column
                    )
            )
        }
#else
        Issue.record("macros are only supported when running tests for the host platform")
#endif
    }
    
    @Test
    func macroWithStringLiteral() {
#if canImport(fooMacros)
        assertMacroExpansion(
            #"""
            #stringify("Hello, \(name)")
            """#,
            expandedSource: #"""
            ("Hello, \(name)", #""Hello, \(name)""#)
            """#,
            macroSpecs: testMacros
        ) {
            Issue.record(
                "\($0.message)",
                sourceLocation:
                    SourceLocation(
                        fileID: $0.location.fileID,
                        filePath: $0.location.filePath,
                        line: $0.location.line,
                        column: $0.location.column
                    )
            )
        }
#else
        Issue.record("macros are only supported when running tests for the host platform")
#endif
    }
}

Fixes: #8890

Add support for macro project using Swift Testing.
@bkhouri
Copy link
Contributor

bkhouri commented Jul 5, 2025

This resolves #8890

@vanvoorden
Copy link

Ahh… just found this one! I hope we can ship a solution to this soon.

FWIW it might be worth highlighting something to engineers reminding them this support would require swift-syntax 600:

#if canImport(SwiftSyntax600)
...
#else
// error? or warning?
#endif

It might be confusing to engineers if they create a macro target with this template and then try to back support swift-syntax 510.

@@ -726,15 +720,22 @@ public final class InitPackage {
import SwiftSyntax
import SwiftSyntaxBuilder
import SwiftSyntaxMacros
import SwiftSyntaxMacrosTestSupport
import SwiftSyntaxMacroExpansion
Copy link

@vanvoorden vanvoorden Jul 28, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe we should also import SwiftSyntaxMacrosGenericTestSupport?

Ahh… nevermind… I see it below!

Comment on lines +774 to +783
Issue.record(
"\($0.message)",
sourceLocation:
SourceLocation(
fileID: $0.location.fileID,
filePath: $0.location.filePath,
line: $0.location.line,
column: $0.location.column
)
)

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Style nit: Could this be refactored to an extension on Issue? Then we can put this work in one place instead of duplicated it across N different tests?

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

extension Issue {
  @discardableResult static func record(_ failure: TestFailureSpec) -> Self {
    Self.record(
      Comment(rawValue: failure.message),
      sourceLocation: SourceLocation(
        fileID: failure.location.fileID,
        filePath: failure.location.filePath,
        line: failure.location.line,
        column: failure.location.column
      )
    )
  }
}

cc @grynspan Was there anything blocking us from shipping that extension directly in swift-testing? Or is there another canonical way to bridge between SwiftSyntaxMacrosGenericTestSupport.TestFailureSpec and Testing.Issue?

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We could also choose to factor this to a free function:

func failureHander(_ failure: TestFailureSpec) {
  Issue.record(
    Comment(rawValue: failure.message),
    sourceLocation:
      SourceLocation(
        fileID: failure.location.fileID,
        filePath: failure.location.filePath,
        line: failure.location.line,
        column: failure.location.column
      )
  )
}

And then pass that failureHander to every test similar to how we pass testMacros.

Copy link
Contributor

@grynspan grynspan Jul 28, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Swift Testing cannot directly link to swift-syntax since it doesn't exist in the toolchain at runtime. That means we cannot export any symbols that reference types from swift-syntax (except in our macro target which is only loaded/run at compile time.)

@bkhouri
Copy link
Contributor

bkhouri commented Jul 28, 2025

we should work the the swift-syntax owners and the Testing Workgroup to ensure this aligns with the overall vision.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Update macro template to support Swift Testing tests
4 participants