@@ -267,7 +267,8 @@ private func expandAccessorMacroWithoutExistingAccessors(
267
267
conformanceList: nil ,
268
268
in: context,
269
269
indentationWidth: indentationWidth
270
- )
270
+ ) ,
271
+ !expanded. isEmpty
271
272
else {
272
273
return nil
273
274
}
@@ -306,7 +307,7 @@ private func expandAccessorMacroWithExistingAccessors(
306
307
return nil
307
308
}
308
309
309
- // Separate the accessor from any existing accessors by two spaces
310
+ // Separate the accessor from any existing accessors by an empty line
310
311
let indentedSource = " \n " + expanded. indented ( by: attachedTo. indentationOfFirstLine + indentationWidth)
311
312
return " \( raw: indentedSource) "
312
313
}
@@ -635,13 +636,26 @@ private class MacroApplication<Context: MacroExpansionContext>: SyntaxRewriter {
635
636
context. addDiagnostics ( from: MacroApplicationError . accessorMacroOnVariableWithMultipleBindings, node: node)
636
637
return DeclSyntax ( node)
637
638
}
638
- node. bindings [ node. bindings. startIndex] . accessorBlock = expandAccessors ( of: node, existingAccessors: binding. accessorBlock)
639
+
640
+ let expansion = expandAccessors ( of: node, existingAccessors: binding. accessorBlock)
641
+ if expansion. accessors != binding. accessorBlock {
642
+ if binding. initializer != nil , expansion. expandsGetSet {
643
+ // The accessor block will have a leading space, but there will already be a
644
+ // space between the variable and the to-be-removed initializer. Remove the
645
+ // leading trivia on the accessor block so we don't double up.
646
+ node. bindings [ node. bindings. startIndex] . accessorBlock = expansion. accessors? . with ( \. leadingTrivia, [ ] )
647
+ node. bindings [ node. bindings. startIndex] . initializer = nil
648
+ } else {
649
+ node. bindings [ node. bindings. startIndex] . accessorBlock = expansion. accessors
650
+ }
651
+ }
652
+
639
653
return DeclSyntax ( node)
640
654
}
641
655
642
656
override func visit( _ node: SubscriptDeclSyntax ) -> DeclSyntax {
643
657
var node = super. visit ( node) . cast ( SubscriptDeclSyntax . self)
644
- node. accessorBlock = expandAccessors ( of: node, existingAccessors: node. accessorBlock)
658
+ node. accessorBlock = expandAccessors ( of: node, existingAccessors: node. accessorBlock) . accessors
645
659
return DeclSyntax ( node)
646
660
}
647
661
}
@@ -802,14 +816,23 @@ extension MacroApplication {
802
816
}
803
817
}
804
818
805
- /// Expand all 'accessor' macros attached to `storage` and return the `storage`
806
- /// node.
819
+ /// Expand all 'accessor' macros attached to `storage`.
807
820
///
808
- /// - Returns: The storage node with all macro-synthesized accessors applied.
809
- private func expandAccessors( of storage: some DeclSyntaxProtocol , existingAccessors: AccessorBlockSyntax ? ) -> AccessorBlockSyntax ? {
821
+ /// - Returns: The final accessors block that includes both the existing
822
+ /// and expanded accessors, as well as whether any `get`/`set` were
823
+ /// expanded (in which case any initializer on `storage` should be
824
+ /// removed).
825
+ private func expandAccessors( of storage: some DeclSyntaxProtocol , existingAccessors: AccessorBlockSyntax ? ) -> (
826
+ accessors: AccessorBlockSyntax ? , expandsGetSet: Bool
827
+ ) {
810
828
let accessorMacros = macroAttributes ( attachedTo: DeclSyntax ( storage) , ofType: AccessorMacro . Type. self)
811
829
812
830
var newAccessorsBlock = existingAccessors
831
+ var expandsGetSet = false
832
+ func checkExpansions( _ accessors: AccessorDeclListSyntax ? ) {
833
+ guard let accessors else { return }
834
+ expandsGetSet = expandsGetSet || accessors. contains ( where: \. isGetOrSet)
835
+ }
813
836
814
837
for macro in accessorMacros {
815
838
do {
@@ -827,6 +850,8 @@ extension MacroApplication {
827
850
in: context,
828
851
indentationWidth: indentationWidth
829
852
) {
853
+ checkExpansions ( newAccessors)
854
+
830
855
// If existingAccessors is not `nil`, then we also set
831
856
// `newAccessorBlock` above to a a non-nil value, so
832
857
// `newAccessorsBlock` also isn’t `nil`.
@@ -835,31 +860,33 @@ extension MacroApplication {
835
860
indentationWidth: self . indentationWidth
836
861
)
837
862
}
838
- } else {
839
- let newAccessors = try expandAccessorMacroWithoutExistingAccessors (
840
- definition : macro. definition ,
841
- attributeNode : macro . attributeNode ,
842
- attachedTo : DeclSyntax ( storage ) ,
843
- in : context ,
844
- indentationWidth : indentationWidth
845
- )
846
- if newAccessorsBlock == nil {
847
- newAccessorsBlock = newAccessors
848
- } else if let newAccessors = newAccessors {
849
- guard case . accessors ( let accessorList) = newAccessors . accessors else {
850
- throw MacroApplicationError . malformedAccessor
851
- }
852
- newAccessorsBlock = newAccessorsBlock! . addingAccessors (
863
+ } else if let newAccessors = try expandAccessorMacroWithoutExistingAccessors (
864
+ definition : macro . definition ,
865
+ attributeNode : macro. attributeNode ,
866
+ attachedTo : DeclSyntax ( storage ) ,
867
+ in : context ,
868
+ indentationWidth : indentationWidth
869
+ ) {
870
+ guard case . accessors ( let accessorList ) = newAccessors . accessors else {
871
+ throw MacroApplicationError . malformedAccessor
872
+ }
873
+
874
+ checkExpansions ( accessorList)
875
+
876
+ if let oldBlock = newAccessorsBlock {
877
+ newAccessorsBlock = oldBlock . addingAccessors (
853
878
from: accessorList,
854
879
indentationWidth: self . indentationWidth
855
880
)
881
+ } else {
882
+ newAccessorsBlock = newAccessors
856
883
}
857
884
}
858
885
} catch {
859
886
context. addDiagnostics ( from: error, node: macro. attributeNode)
860
887
}
861
888
}
862
- return newAccessorsBlock
889
+ return ( newAccessorsBlock, expandsGetSet )
863
890
}
864
891
}
865
892
@@ -1063,3 +1090,9 @@ private extension AttributeSyntax {
1063
1090
return ( detach ( in: context, foldingWith: operatorTable) as Syntax ) . cast ( Self . self)
1064
1091
}
1065
1092
}
1093
+
1094
+ private extension AccessorDeclSyntax {
1095
+ var isGetOrSet : Bool {
1096
+ return accessorSpecifier. tokenKind == . keyword( . get) || accessorSpecifier. tokenKind == . keyword( . set)
1097
+ }
1098
+ }
0 commit comments