Skip to content

Commit 4d34529

Browse files
committed
[Macros] Freestanding macros expand to CodeBlockItemList; parse top-level macro expansion decls
- Change the return type of `FreestandingDeclarationMacro.expanion(of:in:)` to ``CodeBlockItemListSyntax` to allow expressions, statements, and declarations. - Parse freestanding macro expansions as declarations rather than expressions when it's not part of a statement or expression (i.e. "top-level").
1 parent 7c8540b commit 4d34529

File tree

7 files changed

+51
-32
lines changed

7 files changed

+51
-32
lines changed

Sources/SwiftParser/Declarations.swift

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -192,14 +192,12 @@ extension Parser {
192192
return RawDeclSyntax(directive)
193193
case (.poundWarningKeyword, _)?, (.poundErrorKeyword, _)?:
194194
return self.parsePoundDiagnosticDeclaration()
195-
case nil:
196-
break
197-
}
198-
199-
if (self.at(.pound)) {
195+
case (.pound, _)?:
200196
// FIXME: If we can have attributes for macro expansions, handle this
201197
// via DeclarationStart.
202198
return RawDeclSyntax(self.parseMacroExpansionDeclaration())
199+
case nil:
200+
break
203201
}
204202

205203
let attrs = DeclAttributes(

Sources/SwiftParser/RawTokenKindSubset.swift

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -499,12 +499,14 @@ enum PoundDeclarationStart: RawTokenKindSubset {
499499
case poundIfKeyword
500500
case poundWarningKeyword
501501
case poundErrorKeyword
502+
case pound
502503

503504
init?(lexeme: Lexer.Lexeme) {
504505
switch lexeme.tokenKind {
505506
case .poundIfKeyword: self = .poundIfKeyword
506507
case .poundWarningKeyword: self = .poundWarningKeyword
507508
case .poundErrorKeyword: self = .poundErrorKeyword
509+
case .pound: self = .pound
508510
default: return nil
509511
}
510512
}
@@ -514,6 +516,7 @@ enum PoundDeclarationStart: RawTokenKindSubset {
514516
case .poundIfKeyword: return .poundIfKeyword
515517
case .poundWarningKeyword: return .poundWarningKeyword
516518
case .poundErrorKeyword: return .poundErrorKeyword
519+
case .pound: return .pound
517520
}
518521
}
519522
}

Sources/_SwiftSyntaxMacros/FreestandingDeclarationMacro.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,5 +17,5 @@ public protocol FreestandingDeclarationMacro: DeclarationMacro {
1717
static func expansion(
1818
of node: MacroExpansionDeclSyntax,
1919
in context: inout MacroExpansionContext
20-
) throws -> [DeclSyntax]
20+
) throws -> CodeBlockItemListSyntax
2121
}

Sources/_SwiftSyntaxMacros/MacroSystem.swift

Lines changed: 26 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -88,22 +88,22 @@ class MacroApplication: SyntaxRewriter {
8888
for item in node {
8989
// Expand declaration macros that were parsed as macro expansion
9090
// expressions in this context.
91-
if case let .expr(exprItem) = item.item,
92-
let exprExpansion = exprItem.as(MacroExpansionExprSyntax.self),
93-
let macro = macroSystem.macros[exprExpansion.macro.text],
94-
let freestandingMacro = macro as? FreestandingDeclarationMacro.Type
91+
if case let .decl(declItem) = item.item,
92+
let declExpansion = declItem.as(MacroExpansionDeclSyntax.self),
93+
let macro = macroSystem.macros[declExpansion.macro.text]
9594
{
9695
do {
97-
let expandedDecls = try freestandingMacro.expansion(
98-
of: exprExpansion.asMacroExpansionDecl(),
99-
in: &context
100-
)
101-
102-
newItems.append(
103-
contentsOf: expandedDecls.map { decl in
104-
CodeBlockItemSyntax(item: .decl(decl))
105-
}
106-
)
96+
if let macro = macro as? FreestandingDeclarationMacro.Type {
97+
let expandedItemList = try macro.expansion(
98+
of: declExpansion, in: &context
99+
)
100+
newItems.append(contentsOf: expandedItemList)
101+
} else if let macro = macro as? ExpressionMacro.Type {
102+
let expandedExpr = try macro.expansion(
103+
of: declExpansion.asMacroExpansionExpr(), in: &context
104+
)
105+
newItems.append(CodeBlockItemSyntax(item: .init(expandedExpr)))
106+
}
107107
} catch {
108108
// Record the error
109109
context.diagnose(
@@ -144,16 +144,22 @@ class MacroApplication: SyntaxRewriter {
144144
let freestandingMacro = macro as? FreestandingDeclarationMacro.Type
145145
{
146146
do {
147-
let expandedDecls = try freestandingMacro.expansion(
147+
let expandedList = try freestandingMacro.expansion(
148148
of: declExpansion,
149149
in: &context
150150
)
151151

152-
newItems.append(
153-
contentsOf: expandedDecls.map { decl in
154-
MemberDeclListItemSyntax(decl: decl)
155-
}
156-
)
152+
newItems.append(contentsOf: expandedList.compactMap { item in
153+
guard let decl = item.item.as(DeclSyntax.self) else { return nil }
154+
return MemberDeclListItemSyntax(
155+
leadingTrivia: item.leadingTrivia,
156+
item.unexpectedBeforeItem,
157+
decl: decl,
158+
item.unexpectedBetweenItemAndSemicolon,
159+
semicolon: item.semicolon,
160+
item.unexpectedBetweenSemicolonAndErrorTokens,
161+
trailingTrivia: item.trailingTrivia)
162+
})
157163
} catch {
158164
// Record the error
159165
context.diagnose(

Sources/_SwiftSyntaxMacros/Syntax+MacroEvaluation.swift

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -24,11 +24,11 @@ struct ThrownErrorDiagnostic: DiagnosticMessage {
2424
}
2525
}
2626

27-
extension MacroExpansionExprSyntax {
27+
extension MacroExpansionDeclSyntax {
2828
/// Macro expansion declarations are parsed in some positions where an
2929
/// expression is also warranted, so
30-
func asMacroExpansionDecl() -> MacroExpansionDeclSyntax {
31-
MacroExpansionDeclSyntax(
30+
public func asMacroExpansionExpr() -> MacroExpansionExprSyntax {
31+
MacroExpansionExprSyntax(
3232
unexpectedBeforePoundToken,
3333
poundToken: poundToken,
3434
unexpectedBetweenPoundTokenAndMacro,
@@ -47,7 +47,9 @@ extension MacroExpansionExprSyntax {
4747
unexpectedAfterAdditionalTrailingClosures
4848
)
4949
}
50+
}
5051

52+
extension MacroExpansionExprSyntax {
5153
/// Evaluate the given macro for this syntax node, producing the expanded
5254
/// result and (possibly) some diagnostics.
5355
func evaluateMacro(

Tests/SwiftParserTest/DeclarationTests.swift

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1162,6 +1162,13 @@ final class DeclarationTests: XCTestCase {
11621162
}
11631163
"""
11641164
)
1165+
AssertParse(
1166+
"""
1167+
#expand
1168+
""",
1169+
substructure: Syntax(SourceFileSyntax(CodeBlockItemList {
1170+
MacroExpansionDecl(macro: "expand")
1171+
})))
11651172
}
11661173

11671174
func testVariableDeclWithGetSetButNoBrace() {

Tests/SwiftSyntaxMacrosTest/MacroSystemTests.swift

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -158,7 +158,7 @@ public struct ErrorMacro: FreestandingDeclarationMacro {
158158
public static func expansion(
159159
of node: MacroExpansionDeclSyntax,
160160
in context: inout MacroExpansionContext
161-
) throws -> [DeclSyntax] {
161+
) throws -> CodeBlockItemListSyntax {
162162
guard let firstElement = node.argumentList.first,
163163
let stringLiteral = firstElement.expression
164164
.as(StringLiteralExprSyntax.self),
@@ -187,7 +187,7 @@ struct DefineBitwidthNumberedStructsMacro: FreestandingDeclarationMacro {
187187
static func expansion(
188188
of node: MacroExpansionDeclSyntax,
189189
in context: inout MacroExpansionContext
190-
) throws -> [DeclSyntax] {
190+
) throws -> CodeBlockItemListSyntax {
191191
guard let firstElement = node.argumentList.first,
192192
let stringLiteral = firstElement.expression
193193
.as(StringLiteralExprSyntax.self),
@@ -199,12 +199,15 @@ struct DefineBitwidthNumberedStructsMacro: FreestandingDeclarationMacro {
199199
)
200200
}
201201

202-
return [8, 16, 32, 64].map { bitwidth in
202+
let decls: [Decl] = [8, 16, 32, 64].map { bitwidth in
203203
"""
204204
205205
struct \(raw: prefix)\(raw: String(bitwidth)) { }
206206
"""
207207
}
208+
// TODO: Interpolate to `CodeBlockItemList` directly when it supports
209+
// string interpolation.
210+
return CodeBlockItemList(decls.map { CodeBlockItemSyntax(item: .init($0)) })
208211
}
209212
}
210213

0 commit comments

Comments
 (0)