Skip to content

[Macros] Support function body macros on closures. #3016

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Mar 17, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions Sources/SwiftCompilerPluginMessageHandling/Macros.swift
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,7 @@ extension PluginProviderMessageHandler {
attributeSyntax,
foldingWith: .standardOperators
).cast(AttributeSyntax.self)
let declarationNode = sourceManager.add(declSyntax).cast(DeclSyntax.self)
let declarationNode = sourceManager.add(declSyntax)
let parentDeclNode = parentDeclSyntax.map { sourceManager.add($0).cast(DeclSyntax.self) }
let extendedType = extendedTypeSyntax.map {
sourceManager.add($0).cast(TypeSyntax.self)
Expand Down Expand Up @@ -156,7 +156,7 @@ extension PluginProviderMessageHandler {
definition: macroDefinition,
macroRole: role,
attributeNode: attributeNode,
declarationNode: declarationNode,
node: declarationNode,
parentDeclNode: parentDeclNode,
extendedType: extendedType,
conformanceList: conformanceList,
Expand Down
122 changes: 107 additions & 15 deletions Sources/SwiftSyntaxMacroExpansion/MacroExpansion.swift
Original file line number Diff line number Diff line change
Expand Up @@ -211,6 +211,7 @@ public func expandFreestandingMacro(
/// - Returns: A list of expanded source text. Upon failure (i.e.
/// `definition.expansion()` throws) returns `nil`, and the diagnostics
/// representing the `Error` are guaranteed to be added to context.
@available(*, deprecated, message: "Change the 'declarationNode' argument label to 'node'")
public func expandAttachedMacroWithoutCollapsing<Context: MacroExpansionContext>(
definition: Macro.Type,
macroRole: MacroRole,
Expand All @@ -221,10 +222,51 @@ public func expandAttachedMacroWithoutCollapsing<Context: MacroExpansionContext>
conformanceList: InheritedTypeListSyntax?,
in context: Context,
indentationWidth: Trivia? = nil
) -> [String]? {
expandAttachedMacroWithoutCollapsing(
definition: definition,
macroRole: macroRole,
attributeNode: attributeNode,
node: declarationNode,
parentDeclNode: parentDeclNode,
extendedType: extendedType,
conformanceList: conformanceList,
in: context,
indentationWidth: indentationWidth
)
}

/// Expand `@attached(XXX)` macros.
///
/// - Parameters:
/// - definition: a type that conforms to one or more attached `Macro` protocols.
/// - macroRole: indicates which `Macro` protocol expansion should be performed
/// - attributeNode: attribute syntax node (e.g. `@macroName(argument)`).
/// - node: target syntax node to apply the expansion. This is either a declaration
/// or a closure syntax node.
/// - parentDeclNode: Only used for `MacroRole.memberAttribute`. The parent
/// context node of `declarationNode`.
/// - context: context of the expansion.
/// - indentationWidth: The indentation that should be added for each additional
/// nesting level
/// - Returns: A list of expanded source text. Upon failure (i.e.
/// `definition.expansion()` throws) returns `nil`, and the diagnostics
/// representing the `Error` are guaranteed to be added to context.
public func expandAttachedMacroWithoutCollapsing<Context: MacroExpansionContext>(
definition: Macro.Type,
macroRole: MacroRole,
attributeNode: AttributeSyntax,
node: some SyntaxProtocol,
parentDeclNode: DeclSyntax?,
extendedType: TypeSyntax?,
conformanceList: InheritedTypeListSyntax?,
in context: Context,
indentationWidth: Trivia? = nil
) -> [String]? {
do {
switch (definition, macroRole) {
case (let attachedMacro as AccessorMacro.Type, .accessor):
let declarationNode = node.cast(DeclSyntax.self)
let accessors = try attachedMacro.expansion(
of: attributeNode,
providingAccessorsOf: declarationNode,
Expand All @@ -235,6 +277,7 @@ public func expandAttachedMacroWithoutCollapsing<Context: MacroExpansionContext>
}

case (let attachedMacro as MemberAttributeMacro.Type, .memberAttribute):
let declarationNode = node.cast(DeclSyntax.self)
guard
let parentDeclGroup = parentDeclNode?.asProtocol(DeclGroupSyntax.self)
else {
Expand All @@ -255,7 +298,7 @@ public func expandAttachedMacroWithoutCollapsing<Context: MacroExpansionContext>
}

case (let attachedMacro as MemberMacro.Type, .member):
guard let declGroup = declarationNode.asProtocol(DeclGroupSyntax.self)
guard let declGroup = node.asProtocol(DeclGroupSyntax.self)
else {
// Compiler error: declNode for member macro must be DeclGroupSyntax.
throw MacroExpansionError.declarationNotDeclGroup
Expand All @@ -274,6 +317,7 @@ public func expandAttachedMacroWithoutCollapsing<Context: MacroExpansionContext>
}

case (let attachedMacro as PeerMacro.Type, .peer):
let declarationNode = node.cast(DeclSyntax.self)
let peers = try attachedMacro.expansion(
of: attributeNode,
providingPeersOf: declarationNode,
Expand All @@ -286,15 +330,15 @@ public func expandAttachedMacroWithoutCollapsing<Context: MacroExpansionContext>
}

case (let attachedMacro as ExtensionMacro.Type, .extension):
guard let declGroup = declarationNode.asProtocol(DeclGroupSyntax.self) else {
guard let declGroup = node.asProtocol(DeclGroupSyntax.self) else {
// Compiler error: type mismatch.
throw MacroExpansionError.declarationNotDeclGroup
}

let extensionOf: TypeSyntax
if let extendedType {
extensionOf = extendedType
} else if let identified = declarationNode.asProtocol(NamedDeclSyntax.self) {
} else if let identified = node.asProtocol(NamedDeclSyntax.self) {
// Fallback for old compilers with a new plugin, where
extensionOf = TypeSyntax(IdentifierTypeSyntax(name: identified.name))
} else {
Expand All @@ -318,7 +362,7 @@ public func expandAttachedMacroWithoutCollapsing<Context: MacroExpansionContext>

case (let attachedMacro as PreambleMacro.Type, .preamble):
guard
let declToPass = Syntax(declarationNode).asProtocol(SyntaxProtocol.self)
let declToPass = Syntax(node).asProtocol(SyntaxProtocol.self)
as? (DeclSyntaxProtocol & WithOptionalCodeBlockSyntax)
else {
// Compiler error: declaration must have a body.
Expand All @@ -335,19 +379,26 @@ public func expandAttachedMacroWithoutCollapsing<Context: MacroExpansionContext>
}

case (let attachedMacro as BodyMacro.Type, .body):
guard
let declToPass = Syntax(declarationNode).asProtocol(SyntaxProtocol.self)
as? (DeclSyntaxProtocol & WithOptionalCodeBlockSyntax)
else {
let body: [CodeBlockItemSyntax]
if let closureSyntax = node.as(ClosureExprSyntax.self) {
body = try attachedMacro.expansion(
of: attributeNode,
providingBodyFor: closureSyntax,
in: context
)
} else if let declToPass = Syntax(node).asProtocol(SyntaxProtocol.self)
as? (DeclSyntaxProtocol & WithOptionalCodeBlockSyntax)
{
body = try attachedMacro.expansion(
of: attributeNode,
providingBodyFor: declToPass,
in: context
)
} else {
// Compiler error: declaration must have a body.
throw MacroExpansionError.declarationHasNoBody
}

let body = try attachedMacro.expansion(
of: attributeNode,
providingBodyFor: declToPass,
in: context
)
return body.map {
$0.formattedExpansion(definition.formatMode, indentationWidth: indentationWidth)
}
Expand Down Expand Up @@ -376,6 +427,7 @@ public func expandAttachedMacroWithoutCollapsing<Context: MacroExpansionContext>
/// - Returns: expanded source text. Upon failure (i.e. `defintion.expansion()`
/// throws) returns `nil`, and the diagnostics representing the `Error` are
/// guaranteed to be added to context.
@available(*, deprecated, message: "Change the 'declarationNode' argument label to 'node'")
public func expandAttachedMacro<Context: MacroExpansionContext>(
definition: Macro.Type,
macroRole: MacroRole,
Expand All @@ -386,12 +438,52 @@ public func expandAttachedMacro<Context: MacroExpansionContext>(
conformanceList: InheritedTypeListSyntax?,
in context: Context,
indentationWidth: Trivia? = nil
) -> String? {
expandAttachedMacro(
definition: definition,
macroRole: macroRole,
attributeNode: attributeNode,
node: declarationNode,
parentDeclNode: parentDeclNode,
extendedType: extendedType,
conformanceList: conformanceList,
in: context,
indentationWidth: indentationWidth
)
}

/// Expand `@attached(XXX)` macros.
///
/// - Parameters:
/// - definition: a type that conforms to one or more attached `Macro` protocols.
/// - macroRole: indicates which `Macro` protocol expansion should be performed
/// - attributeNode: attribute syntax node (e.g. `@macroName(argument)`).
/// - node: target declaration syntax node to apply the expansion. This is either
/// a declaration or a closure syntax node.
/// - parentDeclNode: Only used for `MacroRole.memberAttribute`. The parent
/// context node of `declarationNode`.
/// - context: context of the expansion.
/// - indentationWidth: The indentation that should be added for each additional
/// nesting level
/// - Returns: expanded source text. Upon failure (i.e. `defintion.expansion()`
/// throws) returns `nil`, and the diagnostics representing the `Error` are
/// guaranteed to be added to context.
public func expandAttachedMacro<Context: MacroExpansionContext>(
definition: Macro.Type,
macroRole: MacroRole,
attributeNode: AttributeSyntax,
node: some SyntaxProtocol,
parentDeclNode: DeclSyntax?,
extendedType: TypeSyntax?,
conformanceList: InheritedTypeListSyntax?,
in context: Context,
indentationWidth: Trivia? = nil
) -> String? {
let expandedSources = expandAttachedMacroWithoutCollapsing(
definition: definition,
macroRole: macroRole,
attributeNode: attributeNode,
declarationNode: declarationNode,
node: node,
parentDeclNode: parentDeclNode,
extendedType: extendedType,
conformanceList: conformanceList,
Expand All @@ -411,7 +503,7 @@ public func expandAttachedMacro<Context: MacroExpansionContext>(
return collapse(
expansions: expandedSources,
for: macroRole,
attachedTo: declarationNode,
attachedTo: node,
indentationWidth: collapseIndentationWidth
)
}
Expand Down
Loading