Skip to content

Commit 06e6de2

Browse files
committed
[Macros] Copy attrs/modifiers on MacroExpansionDecl to expanded decls
As per the SE-0397 amendment, copy attributes and modifiers on MacroExpansionDecl to the expanded declarations.
1 parent d531f15 commit 06e6de2

File tree

6 files changed

+158
-12
lines changed

6 files changed

+158
-12
lines changed

CodeGeneration/Sources/generate-swiftsyntax/templates/swiftsyntax/SyntaxCollectionsFile.swift

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@ let syntaxCollectionsFile = SourceFileSyntax(leadingTrivia: copyrightHeader) {
1919
DeclSyntax(
2020
"""
2121
public protocol SyntaxCollection: SyntaxProtocol, Sequence where Element: SyntaxProtocol {
22+
/// Creates a new collection with the elements.
23+
init(_ children: [Element])
2224
/// The number of elements, `present` or `missing`, in this collection.
2325
var count: Int { get }
2426
}

Sources/SwiftParser/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ add_swift_host_library(SwiftParser
2727
Specifiers.swift
2828
Statements.swift
2929
StringLiterals.swift
30+
StringLiteralRepresentedLiteralValue.swift
3031
SyntaxUtils.swift
3132
TokenConsumer.swift
3233
TokenPrecedence.swift

Sources/SwiftSyntax/generated/SyntaxCollections.swift

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,9 @@
1313
//===----------------------------------------------------------------------===//
1414

1515
public protocol SyntaxCollection: SyntaxProtocol, Sequence where Element: SyntaxProtocol {
16+
/// Creates a new collection with the elements.
17+
init(_ children: [Element])
18+
1619
/// The number of elements, `present` or `missing`, in this collection.
1720
var count: Int {
1821
get

Sources/SwiftSyntaxMacroExpansion/MacroExpansion.swift

Lines changed: 43 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,17 @@ public func expandFreestandingMacro(
3131
expandedSyntax = try Syntax(exprMacroDef.expansion(of: node, in: context))
3232

3333
case let declMacroDef as DeclarationMacro.Type:
34-
let rewritten = try declMacroDef.expansion(of: node, in: context)
34+
var rewritten = try declMacroDef.expansion(of: node, in: context)
35+
// Copy attributes and modifiers to the generated decls.
36+
if let expansionDecl = node.as(MacroExpansionDeclSyntax.self) {
37+
rewritten = rewritten.map {
38+
applyingAttrsModifiers(
39+
to: $0,
40+
attributes: expansionDecl.attributes,
41+
modifiers: expansionDecl.modifiers
42+
)
43+
}
44+
}
3545
expandedSyntax = Syntax(
3646
CodeBlockItemListSyntax(
3747
rewritten.map {
@@ -201,3 +211,35 @@ fileprivate extension SyntaxProtocol {
201211
return formatted.trimmedDescription(matching: { $0.isWhitespace })
202212
}
203213
}
214+
215+
func applyingAttrsModifiers(
216+
to node: DeclSyntax,
217+
attributes: AttributeListSyntax?,
218+
modifiers: ModifierListSyntax?
219+
) -> DeclSyntax {
220+
func _combine<C: SyntaxCollection>(_ left: C, _ right: C?) -> C? {
221+
guard let right = right else { return left }
222+
var elems: [C.Element] = []
223+
elems.append(contentsOf: left)
224+
elems.append(contentsOf: right)
225+
return C(elems)
226+
}
227+
var node = node
228+
if let attributes = attributes,
229+
let withAttrs = node.asProtocol(WithAttributesSyntax.self)
230+
{
231+
node = withAttrs.with(
232+
\.attributes,
233+
_combine(attributes, withAttrs.attributes)
234+
).as(DeclSyntax.self)!
235+
}
236+
if let modifiers = modifiers,
237+
let withModifiers = node.asProtocol(WithModifiersSyntax.self)
238+
{
239+
node = withModifiers.with(
240+
\.modifiers,
241+
_combine(modifiers, withModifiers.modifiers)
242+
).as(DeclSyntax.self)!
243+
}
244+
return node
245+
}

Sources/SwiftSyntaxMacros/MacroSystem.swift

Lines changed: 53 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -125,36 +125,41 @@ class MacroApplication<Context: MacroExpansionContext>: SyntaxRewriter {
125125
override func visit(_ node: CodeBlockItemListSyntax) -> CodeBlockItemListSyntax {
126126
var newItems: [CodeBlockItemSyntax] = []
127127
for item in node {
128-
// Expand declaration macros that were parsed as macro expansion
129-
// expressions in this context.
130-
if case let .expr(exprItem) = item.item,
131-
let exprExpansion = exprItem.as(MacroExpansionExprSyntax.self),
132-
let macro = macroSystem.macros[exprExpansion.macro.text]
128+
if let expansion = item.item.asProtocol(FreestandingMacroExpansionSyntax.self),
129+
let macro = macroSystem.macros[expansion.macro.text]
133130
{
134-
do {
131+
func _expand(expansion: FreestandingMacroExpansionSyntax) throws {
135132
if let macro = macro as? CodeItemMacro.Type {
136133
let expandedItemList = try macro.expansion(
137-
of: exprExpansion,
134+
of: expansion,
138135
in: context
139136
)
140137
newItems.append(contentsOf: expandedItemList)
141138
} else if let macro = macro as? DeclarationMacro.Type {
142-
let expandedItemList = try macro.expansion(
143-
of: exprExpansion,
139+
var expandedItemList = try macro.expansion(
140+
of: expansion,
144141
in: context
145142
)
143+
if let declExpansion = expansion.as(MacroExpansionDeclSyntax.self) {
144+
expandedItemList = expandedItemList.map {
145+
applyingAttrsModifiers(to: $0, attributes: declExpansion.attributes, modifiers: declExpansion.modifiers)
146+
}
147+
}
146148
newItems.append(
147149
contentsOf: expandedItemList.map {
148150
CodeBlockItemSyntax(item: .decl($0))
149151
}
150152
)
151153
} else if let macro = macro as? ExpressionMacro.Type {
152154
let expandedExpr = try macro.expansion(
153-
of: exprExpansion,
155+
of: expansion,
154156
in: context
155157
)
156158
newItems.append(CodeBlockItemSyntax(item: .init(expandedExpr)))
157159
}
160+
}
161+
do {
162+
try _openExistential(expansion, do: _expand)
158163
} catch {
159164
context.addDiagnostics(from: error, node: node)
160165
}
@@ -189,10 +194,13 @@ class MacroApplication<Context: MacroExpansionContext>: SyntaxRewriter {
189194
let freestandingMacro = macro as? DeclarationMacro.Type
190195
{
191196
do {
192-
let expandedList = try freestandingMacro.expansion(
197+
var expandedList = try freestandingMacro.expansion(
193198
of: declExpansion,
194199
in: context
195200
)
201+
expandedList = expandedList.map {
202+
applyingAttrsModifiers(to: $0, attributes: declExpansion.attributes, modifiers: declExpansion.modifiers)
203+
}
196204

197205
newItems.append(
198206
contentsOf: expandedList.map { decl in
@@ -468,6 +476,40 @@ extension MacroApplication {
468476
}
469477
}
470478

479+
// FIXME: This was copied from 'SwiftSyntaxMacroExpansion' module.
480+
// All expansion logics should be unified.
481+
private func applyingAttrsModifiers(
482+
to node: DeclSyntax,
483+
attributes: AttributeListSyntax?,
484+
modifiers: ModifierListSyntax?
485+
) -> DeclSyntax {
486+
func _combine<C: SyntaxCollection>(_ left: C, _ right: C?) -> C? {
487+
guard let right = right else { return left }
488+
var elems: [C.Element] = []
489+
elems.append(contentsOf: left)
490+
elems.append(contentsOf: right)
491+
return C(elems)
492+
}
493+
var node = node
494+
if let attributes = attributes,
495+
let withAttrs = node.asProtocol(WithAttributesSyntax.self)
496+
{
497+
node = withAttrs.with(
498+
\.attributes,
499+
_combine(attributes, withAttrs.attributes)
500+
).as(DeclSyntax.self)!
501+
}
502+
if let modifiers = modifiers,
503+
let withModifiers = node.asProtocol(WithModifiersSyntax.self)
504+
{
505+
node = withModifiers.with(
506+
\.modifiers,
507+
_combine(modifiers, withModifiers.modifiers)
508+
).as(DeclSyntax.self)!
509+
}
510+
return node
511+
}
512+
471513
extension SyntaxProtocol {
472514
/// Expand all uses of the given set of macros within this syntax
473515
/// node.

Tests/SwiftSyntaxMacrosTest/MacroSystemTests.swift

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -651,6 +651,27 @@ public struct UnwrapMacro: CodeItemMacro {
651651
}
652652
}
653653

654+
public struct DeclsFromStringsMacro: DeclarationMacro {
655+
public static func expansion(
656+
of node: some FreestandingMacroExpansionSyntax,
657+
in context: some MacroExpansionContext
658+
) throws -> [DeclSyntax] {
659+
var strings: [String] = []
660+
for arg in node.argumentList {
661+
guard
662+
let value = arg.expression.as(StringLiteralExprSyntax.self)?.representedLiteralValue
663+
else {
664+
continue
665+
}
666+
strings.append(value)
667+
}
668+
669+
return strings.map {
670+
"\(raw: $0)"
671+
}
672+
}
673+
}
674+
654675
// MARK: Tests
655676

656677
/// The set of test macros we use here.
@@ -1037,4 +1058,39 @@ final class MacroSystemTests: XCTestCase {
10371058
indentationWidth: indentationWidth
10381059
)
10391060
}
1061+
1062+
func testDeclsFromStringLiterals() {
1063+
assertMacroExpansion(
1064+
#"""
1065+
struct S {
1066+
public #decls(
1067+
"""
1068+
static func foo() {
1069+
print("value") }
1070+
""",
1071+
"struct Inner {\n\n}"
1072+
)
1073+
@attr static #decls("var value1 = 1", "typealias A = B")
1074+
}
1075+
@attribute
1076+
@otherAttribute(x: 1) #decls("@moreAttibute var global = 42")
1077+
"""#,
1078+
expandedSource: """
1079+
struct S {
1080+
public static func foo() {
1081+
print("value")
1082+
}
1083+
public struct Inner {
1084+
1085+
}
1086+
@attr static var value1 = 1
1087+
@attr typealias A = B
1088+
}
1089+
@attribute
1090+
@otherAttribute(x: 1) @moreAttibute var global = 42
1091+
""",
1092+
macros: ["decls": DeclsFromStringsMacro.self],
1093+
indentationWidth: indentationWidth
1094+
)
1095+
}
10401096
}

0 commit comments

Comments
 (0)