@@ -24,15 +24,34 @@ public struct FirebaseGenerableMacro: MemberMacro, ExtensionMacro {
2424 -> [ SwiftSyntax . DeclSyntax ]
2525 where Declaration: SwiftSyntax . DeclGroupSyntax ,
2626 Context: SwiftSyntaxMacros . MacroExpansionContext {
27- let properties = declaration . memberBlock . members . compactMap {
28- $0 . decl . as ( VariableDeclSyntax . self )
29- }
27+ // 1. Get all variable declarations from the member block.
28+ let varDecls = declaration . memberBlock . members
29+ . compactMap { $0 . decl . as ( VariableDeclSyntax . self ) }
3030
31- let schemaProperties : [ String ] = try properties. map { property in
32- let ( name, type) = try property. toNameAndType ( )
33- return try """
34- " \( name) " : \( schema ( for: type) )
35- """
31+ // 2. Process each declaration to extract schema properties from its bindings.
32+ let schemaProperties = try varDecls. flatMap { varDecl -> [ String ] in
33+ // For declarations like `let a, b: String`, the type annotation is on the last binding.
34+ let typeAnnotationFromDecl = varDecl. bindings. last? . typeAnnotation
35+
36+ return try varDecl. bindings. compactMap { binding -> String ? in
37+ // 3. Filter out computed properties. Stored properties do not have a getter/setter block.
38+ guard binding. accessorBlock == nil else {
39+ return nil
40+ }
41+
42+ // 4. Get the property's name. Skip complex patterns like tuples.
43+ guard let name = binding. pattern. as ( IdentifierPatternSyntax . self) ? . identifier. text else {
44+ return nil
45+ }
46+
47+ // 5. Determine the property's type. It can be on the binding itself or on the declaration.
48+ guard let type = binding. typeAnnotation? . type ?? typeAnnotationFromDecl? . type else {
49+ throw MacroError . missingExplicitType ( for: name)
50+ }
51+
52+ // 6. Generate the schema string for this property.
53+ return try " \" \( name) \" : \( schema ( for: type) ) "
54+ }
3655 }
3756
3857 return [
@@ -106,29 +125,16 @@ public struct FirebaseGenerableMacro: MemberMacro, ExtensionMacro {
106125 }
107126}
108127
109- private extension VariableDeclSyntax {
110- func toNameAndType( ) throws -> ( String , TypeSyntax ) {
111- guard let binding = bindings. first else {
112- throw MacroError . unsupportedType ( Syntax ( self ) )
113- }
114- guard let name = binding. pattern. as ( IdentifierPatternSyntax . self) ? . identifier. text else {
115- throw MacroError . unsupportedType ( Syntax ( self ) )
116- }
117- guard let type = binding. typeAnnotation? . type else {
118- throw MacroError . unsupportedType ( Syntax ( self ) )
119- }
120-
121- return ( name, type)
122- }
123- }
124-
125128private enum MacroError : Error , CustomStringConvertible {
126129 case unsupportedType( Syntax )
130+ case missingExplicitType( for: String )
127131
128132 var description : String {
129133 switch self {
130- case let . unsupportedType( type) :
131- return " Unsupported type: \( type) "
134+ case let . unsupportedType( syntax) :
135+ return " Unsupported type syntax: \( syntax) "
136+ case let . missingExplicitType( name) :
137+ return " Property ' \( name) ' must have an explicit type annotation to be used with @FirebaseGenerable. "
132138 }
133139 }
134140}
0 commit comments