Skip to content

Commit fe05c66

Browse files
committed
[Macros] Pass macro role for freestanding macro expansion
Macros can only have a single freestanding macro role, so that their expansion is unambiguous. However, that restriction is at the level of a macro declaration, and is not reflected in a similar restriction on the set of macro protocols to which a macro implementation can conform. Therefore, have the compiler pass a freestanding macro role through to macro expansion in the same way that we do for attached macros, and use that to determine which macro protocol to use. Part of fixing rdar://110418969.
1 parent fadf844 commit fe05c66

File tree

5 files changed

+63
-6
lines changed

5 files changed

+63
-6
lines changed

Sources/SwiftCompilerPluginMessageHandling/CompilerPluginMessageHandler.swift

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -87,9 +87,10 @@ extension CompilerPluginMessageHandler {
8787
)
8888
try self.sendMessage(.getCapabilityResult(capability: capability))
8989

90-
case .expandFreestandingMacro(let macro, let discriminator, let expandingSyntax):
90+
case .expandFreestandingMacro(let macro, let macroRole, let discriminator, let expandingSyntax):
9191
try expandFreestandingMacro(
9292
macro: macro,
93+
macroRole: macroRole,
9394
discriminator: discriminator,
9495
expandingSyntax: expandingSyntax
9596
)

Sources/SwiftCompilerPluginMessageHandling/Diagnostics.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ enum MacroExpansionError: String {
1919
case unmathedMacroRole = "macro doesn't conform to required macro role"
2020
case freestandingMacroSyntaxIsNotMacro = "macro syntax couldn't be parsed"
2121
case invalidExpansionMessage = "internal message error; please file a bug report"
22+
case invalidMacroRole = "invalid macro role for expansion"
2223
}
2324

2425
extension MacroExpansionError: DiagnosticMessage {

Sources/SwiftCompilerPluginMessageHandling/Macros.swift

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ extension CompilerPluginMessageHandler {
2626
/// Expand `@freestainding(XXX)` macros.
2727
func expandFreestandingMacro(
2828
macro: PluginMessage.MacroReference,
29+
macroRole pluginMacroRole: PluginMessage.MacroRole?,
2930
discriminator: String,
3031
expandingSyntax: PluginMessage.Syntax
3132
) throws {
@@ -46,8 +47,23 @@ extension CompilerPluginMessageHandler {
4647
throw MacroExpansionError.macroTypeNotFound
4748
}
4849

50+
let macroRole: MacroRole
51+
if let pluginMacroRole {
52+
switch pluginMacroRole {
53+
case .expression: macroRole = .expression
54+
case .declaration: macroRole = .declaration
55+
case .codeItem: macroRole = .codeItem
56+
57+
case .accessor, .conformance, .member, .memberAttribute, .peer:
58+
throw MacroExpansionError.invalidMacroRole
59+
}
60+
} else {
61+
macroRole = try inferFreestandingMacroRole(definition: macroDefinition)
62+
}
63+
4964
expandedSource = SwiftSyntaxMacroExpansion.expandFreestandingMacro(
5065
definition: macroDefinition,
66+
macroRole: macroRole,
5167
node: macroSyntax,
5268
in: context
5369
)

Sources/SwiftCompilerPluginMessageHandling/PluginMessages.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ internal enum HostToPluginMessage: Codable {
2020
/// Expand a '@freestanding' macro.
2121
case expandFreestandingMacro(
2222
macro: PluginMessage.MacroReference,
23+
macroRole: PluginMessage.MacroRole? = nil,
2324
discriminator: String,
2425
syntax: PluginMessage.Syntax
2526
)

Sources/SwiftSyntaxMacroExpansion/MacroExpansion.swift

Lines changed: 43 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -25,24 +25,26 @@ private enum MacroExpansionError: String, Error, CustomStringConvertible {
2525
///
2626
/// - Parameters:
2727
/// - definition: a type conforms to one of freestanding `Macro` protocol.
28+
/// - macroRole: indicates which `Macro` protocol expansion should be performed
2829
/// - node: macro expansion syntax node (e.g. `#macroName(argument)`).
2930
/// - in: context of the expansion.
3031
/// - Returns: expanded source text. Upon failure (i.e. `defintion.expansion()`
3132
/// throws) returns `nil`, and the diagnostics representing the `Error` are
3233
/// guaranteed to be added to context.
3334
public func expandFreestandingMacro(
3435
definition: Macro.Type,
36+
macroRole: MacroRole,
3537
node: FreestandingMacroExpansionSyntax,
3638
in context: some MacroExpansionContext
3739
) -> String? {
3840
do {
3941
func _expand(node: some FreestandingMacroExpansionSyntax) throws -> String {
4042
let expandedSyntax: Syntax
41-
switch definition {
42-
case let exprMacroDef as ExpressionMacro.Type:
43+
switch (macroRole, definition) {
44+
case (.expression, let exprMacroDef as ExpressionMacro.Type):
4345
expandedSyntax = try Syntax(exprMacroDef.expansion(of: node, in: context))
4446

45-
case let declMacroDef as DeclarationMacro.Type:
47+
case (.declaration, let declMacroDef as DeclarationMacro.Type):
4648
var rewritten = try declMacroDef.expansion(of: node, in: context)
4749
// Copy attributes and modifiers to the generated decls.
4850
if let expansionDecl = node.as(MacroExpansionDeclSyntax.self) {
@@ -60,12 +62,15 @@ public func expandFreestandingMacro(
6062
)
6163
)
6264

63-
case let codeItemMacroDef as CodeItemMacro.Type:
65+
case (.codeItem, let codeItemMacroDef as CodeItemMacro.Type):
6466
let rewritten = try codeItemMacroDef.expansion(of: node, in: context)
6567
expandedSyntax = Syntax(CodeBlockItemListSyntax(rewritten))
6668

67-
default:
69+
case (.expression, _), (.declaration, _), (.codeItem, _):
6870
throw MacroExpansionError.unmathedMacroRole
71+
72+
case (.accessor, _), (.memberAttribute, _), (.member, _), (.peer, _), (.conformance, _), (.codeItem, _):
73+
fatalError("macro role \(macroRole) is not a freestanding macro")
6974
}
7075
return expandedSyntax.formattedExpansion(definition.formatMode)
7176
}
@@ -76,6 +81,39 @@ public func expandFreestandingMacro(
7681
}
7782
}
7883

84+
/// Try to infer the freestanding macro role from the type definition itself.
85+
///
86+
/// This is a workaround for older compilers with a newer plugin
87+
public func inferFreestandingMacroRole(definition: Macro.Type) throws -> MacroRole {
88+
switch definition {
89+
case is ExpressionMacro.Type: return .expression
90+
case is DeclarationMacro.Type: return .declaration
91+
case is CodeItemMacro.Type: return .codeItem
92+
93+
default:
94+
throw MacroExpansionError.unmathedMacroRole
95+
}
96+
}
97+
98+
@available(*, deprecated, message: "pass a macro role, please!")
99+
public func expandFreestandingMacro(
100+
definition: Macro.Type,
101+
node: FreestandingMacroExpansionSyntax,
102+
in context: some MacroExpansionContext
103+
) -> String? {
104+
do {
105+
return expandFreestandingMacro(
106+
definition: definition,
107+
macroRole: try inferFreestandingMacroRole(definition: definition),
108+
node: node,
109+
in: context
110+
)
111+
} catch {
112+
context.addDiagnostics(from: error, node: node)
113+
return nil
114+
}
115+
}
116+
79117
/// Expand `@attached(XXX)` macros.
80118
///
81119
/// - Parameters:

0 commit comments

Comments
 (0)