swiftswift-macro

How to emit errors from a Swift Macro?


I am trying to emit error messages from my Swift macro.

I saw that every kind of macro's expansion function has a MacroExpansionContext parameter, and it has a diagnose method, so presumably that is the intended way to emit errors.

diagnose takes a Diagnostic, so I tried to create an instance of that. I saw that its initialiser takes a syntax node and a DiagnosticMessage. The syntax node I can easily pass, but DiagnosticMessage is a protocol. Using Xcode's fix-its, I managed to write such a type that conforms to DiagnosticMessage,

struct MyMessage: DiagnosticMessage {
    let message: String
    
    var diagnosticID: MessageID {
        .init(domain: "some domain", id: "some id")
    }
    
    var severity: DiagnosticSeverity {
        .error
    }
}

And passing this worked as expected,

context.diagnose(Diagnostic(
    node: declaration, message: MyMessage(message: "You must attach this macro to a class!")
))

But surely I am not supposed to write something like this every time I write a macro, right? Is there some type in SwiftSyntax that conforms to DiagnosticMessage that I can directly use?

I have tried searching in the documentation for SwiftSyntax on Swift Package Index and found nothing there. Interestingly, I couldn't even find DiagnosticMessage there.


Solution

  • MacroExpansionErrorMessage is a built-in type that conforms to DiagnosticMessage, so you can just write:

    context.diagnose(Diagnostic(
        node: declaration, message: MacroExpansionErrorMessage("You must attach this macro to a class!")
    ))
    

    There is also MacroExpansionWarningMessage for warnings.

    These are in the SwiftSyntaxMacros module, which is why you couldn't find it on the linked page, which only shows the documentation for the SwiftSyntax module.