Skip to content

Commit 2c857ae

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 992a9da commit 2c857ae

File tree

6 files changed

+149
-13
lines changed

6 files changed

+149
-13
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: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import SwiftSyntax
2-
import SwiftSyntaxMacros
2+
@_spi(MacroExpansion) import SwiftSyntaxMacros
33

44
public enum MacroRole {
55
case expression
@@ -31,7 +31,13 @@ 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+
$0.applying(attributes: expansionDecl.attributes, modifiers: expansionDecl.modifiers)
39+
}
40+
}
3541
expandedSyntax = Syntax(
3642
CodeBlockItemListSyntax(
3743
rewritten.map {

Sources/SwiftSyntaxMacros/MacroSystem.swift

Lines changed: 57 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: some 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+
$0.applying(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+
$0.applying(attributes: declExpansion.attributes, modifiers: declExpansion.modifiers)
203+
}
196204

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

479+
extension DeclSyntax {
480+
/// Returns this node with `attributes` and `modifiers` prepended to the
481+
/// node’s attributes and modifiers, respectively. If the node doesn’t contain
482+
/// attributes or modifiers, `attributes` or `modifiers` are ignored and not
483+
/// applied.
484+
@_spi(MacroExpansion)
485+
public func applying(
486+
attributes: AttributeListSyntax?,
487+
modifiers: ModifierListSyntax?
488+
) -> DeclSyntax {
489+
func _combine<C: SyntaxCollection>(_ left: C, _ right: C?) -> C? {
490+
guard let right = right else { return left }
491+
var elems: [C.Element] = []
492+
elems.append(contentsOf: left)
493+
elems.append(contentsOf: right)
494+
return C(elems)
495+
}
496+
var node = self
497+
if let attributes = attributes,
498+
let withAttrs = node.asProtocol(WithAttributesSyntax.self)
499+
{
500+
node = withAttrs.with(
501+
\.attributes,
502+
_combine(attributes, withAttrs.attributes)
503+
).cast(DeclSyntax.self)
504+
}
505+
if let modifiers = modifiers,
506+
let withModifiers = node.asProtocol(WithModifiersSyntax.self)
507+
{
508+
node = withModifiers.with(
509+
\.modifiers,
510+
_combine(modifiers, withModifiers.modifiers)
511+
).cast(DeclSyntax.self)
512+
}
513+
return node
514+
}
515+
}
516+
471517
extension SyntaxProtocol {
472518
/// Expand all uses of the given set of macros within this syntax
473519
/// node.

Tests/SwiftSyntaxMacrosTest/MacroSystemTests.swift

Lines changed: 78 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,61 @@ 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+
}
1074+
"""#,
1075+
expandedSource: #"""
1076+
struct S {
1077+
public static func foo() {
1078+
print("value")
1079+
}
1080+
public struct Inner {
1081+
1082+
}
1083+
}
1084+
"""#,
1085+
macros: ["decls": DeclsFromStringsMacro.self],
1086+
indentationWidth: indentationWidth
1087+
)
1088+
1089+
assertMacroExpansion(
1090+
#"""
1091+
struct S {
1092+
@attr static #decls("var value1 = 1", "typealias A = B")
1093+
}
1094+
"""#,
1095+
expandedSource: #"""
1096+
struct S {
1097+
@attr static var value1 = 1
1098+
@attr typealias A = B
1099+
}
1100+
"""#,
1101+
macros: ["decls": DeclsFromStringsMacro.self],
1102+
indentationWidth: indentationWidth
1103+
)
1104+
1105+
assertMacroExpansion(
1106+
#"""
1107+
@attribute
1108+
@otherAttribute(x: 1) #decls("@moreAttibute var global = 42")
1109+
"""#,
1110+
expandedSource: #"""
1111+
@attribute
1112+
@otherAttribute(x: 1) @moreAttibute var global = 42
1113+
"""#,
1114+
macros: ["decls": DeclsFromStringsMacro.self],
1115+
indentationWidth: indentationWidth
1116+
)
1117+
}
10401118
}

0 commit comments

Comments
 (0)