Skip to content

Macro with arbitrary name makes Equatable custom implemented == not work #84691

@Asura19

Description

@Asura19

Description

When I use the attached macro to declare an arbitrary name, and the decorated type implements Equatable with my own implementation of the == method, sometimes my implemented == method is not called, and instead, the compiler-generated == is used. However, if I write it as extension TheType: Equatable, this issue does not occur. Please see the attached package(zip file) or the reproduction code.

TestArbitrary.zip

Reproduction

/// xxx
///
///     @AddArbitrary
///     struct Person {
///         let name: String
///         let age: Int
///     }
///
@attached(member, names: arbitrary)
public macro AddArbitrary() = #externalMacro(module: "TestArbitraryMacros", type: "AddArbitraryMacro")

// if I remove arbitrary, it will call custom `==`
//@attached(member, names: named(anyName))
//public macro AddArbitrary() = #externalMacro(module: "TestArbitraryMacros", type: "AddArbitraryMacro")
import SwiftCompilerPlugin
import SwiftSyntax
import SwiftSyntaxBuilder
import SwiftSyntaxMacros

public struct AddArbitraryMacro: MemberMacro {
    public static func expansion(
        of node: AttributeSyntax,
        providingMembersOf declaration: some DeclGroupSyntax,
        conformingTo protocols: [TypeSyntax],
        in context: some MacroExpansionContext
    ) throws -> [DeclSyntax] {
        guard declaration.is(StructDeclSyntax.self) || declaration.is(ClassDeclSyntax.self) else {
            throw MacroExpansionErrorMessage("@AddArbitrary 只能应用于 class 或 struct")
        }
        
        let arbitraryMethod: DeclSyntax = """
            static func anyName() {
                print(111)
            }
            """
        
        return [arbitraryMethod]
    }
}

struct MacroExpansionErrorMessage: Error, CustomStringConvertible {
    let description: String
    
    init(_ description: String) {
        self.description = description
    }
}

@main
struct TestArbitraryPlugin: CompilerPlugin {
    let providingMacros: [Macro.Type] = [
        AddArbitraryMacro.self,
    ]
}
import TestArbitrary

// If I conform to the Equatable protocol through an extension, there will be no issues.
// extension Person: Equatable {}
// called custom ==

@AddArbitrary
struct Person: Equatable {
    let name: String
    let age: Int
    
    static func == (lhs: Person, rhs: Person) -> Bool {
        print("called custom ==")
        return lhs.name == rhs.name
    }
}


Person.anyName()

let p1 = Person(name: "123", age: 12)
let p2 = Person(name: "123", age: 34)

print(p1 == p2)
// true
// called custom ==


var persons = [p1]
for element in [p2] {
    if !persons.contains(element) {
        persons.append(element)
    }
}
print(persons)
// [TestArbitraryClient.Person(name: "123", age: 12), TestArbitraryClient.Person(name: "123", age: 34)]
// NOT call custom ==

Expected behavior

When I implement == myself, the self - implemented == will always be called instead of the one generated by the compiler. Don't be affected by arbitrary.

Environment

Swift 6.2

Additional information

No response

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugA deviation from expected or documented behavior. Also: expected but undesirable behavior.triage neededThis issue needs more specific labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions