Learning SwiftSyntax: Is this a good approach to identifying protocols

Hello everyone, I'm developing a macro which needs to identify if a protocol is already implemented on a type. The macro looks like this:

public static func expansion(
        of _: AttributeSyntax,
        attachedTo _: some DeclGroupSyntax,
        providingExtensionsOf type: some TypeSyntaxProtocol,
        conformingTo protocols: [TypeSyntax],
        in _: some MacroExpansionContext
    ) throws -> [ExtensionDeclSyntax] {
        var newFunctions: [ExtensionDeclSyntax] = []
        let identifiers = protocols.compactMap { $0.as(IdentifierTypeSyntax.self) }
        if identifiers.contains(where: { $0.description == "Hashable" }) {
            try newFunctions.append(
                ExtensionDeclSyntax("""
                    extension \(type.trimmed): Hashable {
                    }
                    """
                )
            )
        }
        return newFunctions
    }

my question is whether my approach of mapping to IdentifierTypeSyntax and then using description is a good one for identifying whether Hashable is in the protocols list? Or is there a simpler way to do this?

Here is my approach from Swift-CowBox. I'm not completely sure how the description property maps to name… you might want to test how description handles any trivia:

struct Person : Hashable /* trivia */ { }

Something else to keep in mind is whether or not this sees adoptions in extensions:

@macro struct Person { }

extension Person : Hashable { }

AFAIK a macro attached to the main declaration will not see (or know about) adoptions that might be lurking in extensions (but there could be a workaround for this I did not know about).

Another edge case is if a type adopts a protocol that also adopts Hashable:

protocol P : Hashable { }

struct Person : P { }

AFAIK the only option at that point is some kind of runtime-check in the macro expansion that tests each adoption does (or does not) adopt Hashable somewhere.

1 Like