Files
swift-mirror/test/Macros/Inputs/syntax_macro_definitions.swift
Nate Cook 85208293c9 Allow inout parameters in expression macros (#82370)
Sema seems to be flagging inout expressions (e.g. &foo) in expression
macros as invalid, setting up a catch 22 where Sema emits an error when
a parameter has the '&' sigil and type checking fails when it doesn't.

This resolves the issue by allowing inout expressions inside macro
expansion expressions.

Resolves https://github.com/swiftlang/swift/issues/82369.
2025-07-03 23:52:16 -07:00

2983 lines
81 KiB
Swift

import SwiftDiagnostics
import SwiftOperators
@_spi(ExperimentalLanguageFeatures) import SwiftSyntax
import SwiftSyntaxBuilder
@_spi(ExperimentalLanguageFeature) import SwiftSyntaxMacros
/// Replace the label of the first element in the tuple with the given
/// new label.
private func replaceFirstLabel(
of tuple: LabeledExprListSyntax, with newLabel: String
) -> LabeledExprListSyntax{
if tuple.isEmpty {
return tuple
}
return tuple.with(\.[tuple.startIndex].label, .identifier(newLabel))
}
public struct ColorLiteralMacro: ExpressionMacro {
public static func expansion(
of macro: some FreestandingMacroExpansionSyntax,
in context: some MacroExpansionContext
) -> ExprSyntax {
let argList = replaceFirstLabel(
of: macro.arguments, with: "_colorLiteralRed"
)
let initSyntax: ExprSyntax = ".init(\(argList))"
return initSyntax
}
}
public struct FileIDMacro: ExpressionMacro {
public static func expansion<
Node: FreestandingMacroExpansionSyntax,
Context: MacroExpansionContext
>(
of macro: Node,
in context: Context
) throws -> ExprSyntax {
guard let sourceLoc = context.location(of: macro) else {
throw CustomError.message("can't find location for macro")
}
let fileLiteral: ExprSyntax = "\(sourceLoc.file)"
return fileLiteral
}
}
public struct AssertMacro: ExpressionMacro {
public static func expansion(
of macro: some FreestandingMacroExpansionSyntax,
in context: some MacroExpansionContext
) -> ExprSyntax {
guard let argument = macro.arguments.first?.expression else {
fatalError("boom")
}
return "assert(\(argument))"
}
}
public struct StringifyMacro: ExpressionMacro {
public static func expansion(
of macro: some FreestandingMacroExpansionSyntax,
in context: some MacroExpansionContext
) -> ExprSyntax {
guard let argument = macro.arguments.first?.expression else {
fatalError("boom")
}
return "(\(argument), \(StringLiteralExprSyntax(content: argument.description)))"
}
}
public struct ExprAndDeclMacro: ExpressionMacro, DeclarationMacro {
public static func expansion(
of macro: some FreestandingMacroExpansionSyntax,
in context: some MacroExpansionContext
) -> ExprSyntax {
guard let argument = macro.arguments.first?.expression else {
fatalError("boom")
}
return "(\(argument), \(StringLiteralExprSyntax(content: argument.description)))"
}
public static func expansion(
of macro: some FreestandingMacroExpansionSyntax,
in context: some MacroExpansionContext
) -> [DeclSyntax] {
return []
}
}
public struct StringifyAndTryMacro: ExpressionMacro {
public static func expansion(
of macro: some FreestandingMacroExpansionSyntax,
in context: some MacroExpansionContext
) -> ExprSyntax {
guard let argument = macro.arguments.first?.expression else {
fatalError("boom")
}
return "(try \(argument), \(StringLiteralExprSyntax(content: argument.description)))"
}
}
public struct TryCallThrowingFuncMacro: ExpressionMacro {
public static func expansion(
of macro: some FreestandingMacroExpansionSyntax,
in context: some MacroExpansionContext
) -> ExprSyntax {
return """
try await {
print("let's throw")
return try await throwingFunc()
}()
"""
}
}
struct SimpleDiagnosticMessage: DiagnosticMessage {
let message: String
let diagnosticID: MessageID
let severity: DiagnosticSeverity
}
extension SimpleDiagnosticMessage: FixItMessage {
var fixItID: MessageID { diagnosticID }
}
public enum AddBlocker: ExpressionMacro {
class AddVisitor: SyntaxRewriter {
var diagnostics: [Diagnostic] = []
override func visit(
_ node: InfixOperatorExprSyntax
) -> ExprSyntax {
if let binOp = node.operator.as(BinaryOperatorExprSyntax.self) {
if binOp.operator.text == "+" {
let messageID = MessageID(domain: "silly", id: "addblock")
diagnostics.append(
Diagnostic(
node: Syntax(node.operator),
message: SimpleDiagnosticMessage(
message: "blocked an add; did you mean to subtract?",
diagnosticID: messageID,
severity: .error
),
highlights: [
Syntax(node.leftOperand),
Syntax(node.rightOperand)
],
fixIts: [
FixIt(
message: SimpleDiagnosticMessage(
message: "use '-'",
diagnosticID: messageID,
severity: .error
),
changes: [
FixIt.Change.replace(
oldNode: Syntax(binOp.operator),
newNode: Syntax(binOp.operator.detached.with(\.tokenKind, .binaryOperator("-"))),
)
]
),
]
)
)
let minusOperator = binOp.with(\.operator.tokenKind, .binaryOperator("-"))
return ExprSyntax(node.with(\.operator, ExprSyntax(minusOperator)))
}
}
return ExprSyntax(node)
}
}
public static func expansion(
of node: some FreestandingMacroExpansionSyntax,
in context: some MacroExpansionContext
) -> ExprSyntax {
let visitor = AddVisitor()
let result = visitor.rewrite(Syntax(node))
for diag in visitor.diagnostics {
context.diagnose(diag)
}
return result.asProtocol(FreestandingMacroExpansionSyntax.self)!.arguments.first!.expression
}
}
public class RecursiveMacro: ExpressionMacro {
public static func expansion(
of macro: some FreestandingMacroExpansionSyntax,
in context: some MacroExpansionContext
) -> ExprSyntax {
guard let argument = macro.arguments.first?.expression,
argument.description == "false" else {
return "\(macro)"
}
return "()"
}
}
public class NestedDeclInExprMacro: ExpressionMacro {
public static func expansion(
of macro: some FreestandingMacroExpansionSyntax,
in context: some MacroExpansionContext
) -> ExprSyntax {
return """
{ () -> Void in
struct Foo { }
return ()
}
"""
}
}
public class NullaryFunctionCallMacro: ExpressionMacro {
public static func expansion(
of macro: some FreestandingMacroExpansionSyntax,
in context: some MacroExpansionContext
) -> ExprSyntax {
let calls = macro.arguments.compactMap(\.expression).map { "\($0)()" }
return "(\(raw: calls.joined(separator: ", ")))"
}
}
public class TupleMacro: ExpressionMacro {
public static func expansion(
of macro: some FreestandingMacroExpansionSyntax,
in context: some MacroExpansionContext
) -> ExprSyntax {
return "(\(raw: macro.arguments.map { "\($0)" }.joined()))"
}
}
public class VoidExpressionMacro: ExpressionMacro {
public static func expansion(
of macro: some FreestandingMacroExpansionSyntax,
in context: some MacroExpansionContext
) -> ExprSyntax {
return "()"
}
}
enum CustomError: Error, CustomStringConvertible {
case message(String)
var description: String {
switch self {
case .message(let text):
return text
}
}
}
public struct EmptyDeclarationMacro: DeclarationMacro {
public static func expansion(
of node: some FreestandingMacroExpansionSyntax,
in context: some MacroExpansionContext
) throws -> [DeclSyntax] {
return []
}
}
public struct DefineBitwidthNumberedStructsMacro: DeclarationMacro {
public static func expansion(
of node: some FreestandingMacroExpansionSyntax,
in context: some MacroExpansionContext
) throws -> [DeclSyntax] {
guard let firstElement = node.arguments.first,
let stringLiteral = firstElement.expression.as(StringLiteralExprSyntax.self),
stringLiteral.segments.count == 1,
case let .stringSegment(prefix) = stringLiteral.segments.first else {
throw CustomError.message("#bitwidthNumberedStructs macro requires a string literal")
}
if prefix.content.text == "BUG" {
return [
"""
struct \(raw: prefix) {
func \(context.makeUniqueName("method"))() { return 0 }
func \(context.makeUniqueName("method"))() { return 1 }
}
"""
]
}
return [8, 16, 32, 64].map { bitwidth in
"""
struct \(raw: prefix)\(raw: String(bitwidth)) {
func \(context.makeUniqueName("method"))() { }
func \(context.makeUniqueName("method"))() { }
}
"""
}
}
}
public struct DefineDeclsWithKnownNamesMacro: DeclarationMacro {
public static func expansion(
of node: some FreestandingMacroExpansionSyntax,
in context: some MacroExpansionContext
) throws -> [DeclSyntax] {
return [
"""
struct A {
func \(context.makeUniqueName("method"))() { }
func \(context.makeUniqueName("method"))() { }
}
""",
"""
struct B {
func \(context.makeUniqueName("method"))() { }
func \(context.makeUniqueName("method"))() { }
}
""",
"""
var foo: Int {
1
}
""",
"""
var addOne: (Int) -> Int { { $0 + 1 } }
"""
// FIXME:
// 1. Stored properties are not visited in IRGen
// let addTwo: (Int) -> Int = { $0 + 2 }
// 2. Curry thunk at call sites
// func foo()
// Foo2.foo // AutoClosureExpr with invalid discriminator
]
}
}
public struct VarDeclMacro: CodeItemMacro {
public static func expansion(
of node: some FreestandingMacroExpansionSyntax,
in context: some MacroExpansionContext
) throws -> [CodeBlockItemSyntax] {
let name = context.makeUniqueName("fromMacro")
return [
"let \(name) = 23",
"use(\(name))",
"""
if true {
let \(name) = "string"
use(\(name))
}
"""
]
}
}
public struct WarningMacro: ExpressionMacro {
public static func expansion(
of macro: some FreestandingMacroExpansionSyntax,
in context: some MacroExpansionContext
) throws -> ExprSyntax {
guard let firstElement = macro.arguments.first,
let stringLiteral = firstElement.expression
.as(StringLiteralExprSyntax.self),
stringLiteral.segments.count == 1,
case let .stringSegment(messageString)? = stringLiteral.segments.first
else {
throw CustomError.message("#myWarning macro requires a string literal")
}
context.diagnose(
Diagnostic(
node: Syntax(macro),
message: SimpleDiagnosticMessage(
message: messageString.content.description,
diagnosticID: MessageID(domain: "test", id: "error"),
severity: .warning
)
)
)
return "()"
}
}
public struct ErrorMacro: ExpressionMacro {
public static func expansion(
of macro: some FreestandingMacroExpansionSyntax,
in context: some MacroExpansionContext
) throws -> ExprSyntax {
guard let firstElement = macro.arguments.first,
let stringLiteral = firstElement.expression.as(StringLiteralExprSyntax.self),
stringLiteral.segments.count == 1,
case let .stringSegment(messageString)? = stringLiteral.segments.first
else {
let errorNode: Syntax
if let firstElement = macro.arguments.first {
errorNode = Syntax(firstElement)
} else {
errorNode = Syntax(macro)
}
let messageID = MessageID(domain: "silly", id: "error")
let diag = Diagnostic(
node: errorNode,
message: SimpleDiagnosticMessage(
message: "#myError macro requires a string literal",
diagnosticID: messageID,
severity: .error
)
)
throw DiagnosticsError(diagnostics: [diag])
}
context.diagnose(
Diagnostic(
node: Syntax(macro),
message: SimpleDiagnosticMessage(
message: messageString.content.description,
diagnosticID: MessageID(domain: "test", id: "error"),
severity: .error
)
)
)
return "()"
}
}
public struct PropertyWrapperMacro {}
extension PropertyWrapperMacro: AccessorMacro, Macro {
public static func expansion(
of node: AttributeSyntax,
providingAccessorsOf declaration: some DeclSyntaxProtocol,
in context: some MacroExpansionContext
) throws -> [AccessorDeclSyntax] {
guard let varDecl = declaration.as(VariableDeclSyntax.self),
let binding = varDecl.bindings.first,
let identifier = binding.pattern.as(IdentifierPatternSyntax.self)?.identifier
else {
return []
}
return [
"""
get {
_\(identifier).wrappedValue
}
""",
"""
set {
_\(identifier).wrappedValue = newValue
}
""",
]
}
}
extension PropertyWrapperMacro: PeerMacro {
public static func expansion(
of node: AttributeSyntax,
providingPeersOf declaration: some DeclSyntaxProtocol,
in context: some MacroExpansionContext
) throws -> [DeclSyntax] {
guard let varDecl = declaration.as(VariableDeclSyntax.self),
let binding = varDecl.bindings.first,
let identifier = binding.pattern.as(IdentifierPatternSyntax.self)?.identifier,
binding.accessorBlock == nil,
let type = binding.typeAnnotation?.type
else {
return []
}
return [
"""
var _\(raw: identifier.trimmedDescription): MyWrapperThingy<\(type)>
"""
]
}
}
extension AccessorBlockSyntax {
var hasGetter: Bool {
switch self.accessors {
case .accessors(let accessors):
for accessor in accessors {
if accessor.accessorSpecifier.text == "get" {
return true
}
}
return false
case .getter:
return true
@unknown default:
return false
}
}
}
public struct PropertyWrapperSkipsComputedMacro {}
extension PropertyWrapperSkipsComputedMacro: AccessorMacro, Macro {
public static func expansion(
of node: AttributeSyntax,
providingAccessorsOf declaration: some DeclSyntaxProtocol,
in context: some MacroExpansionContext
) throws -> [AccessorDeclSyntax] {
guard let varDecl = declaration.as(VariableDeclSyntax.self),
let binding = varDecl.bindings.first,
let identifier = binding.pattern.as(IdentifierPatternSyntax.self)?.identifier, !(binding.accessorBlock?.hasGetter ?? false)
else {
return []
}
return [
"""
get {
_\(identifier).wrappedValue
}
""",
"""
set {
_\(identifier).wrappedValue = newValue
}
""",
]
}
}
public struct WillSetMacro: AccessorMacro {
public static func expansion(
of node: AttributeSyntax,
providingAccessorsOf declaration: some DeclSyntaxProtocol,
in context: some MacroExpansionContext
) throws -> [AccessorDeclSyntax] {
return [
"""
willSet { }
"""
]
}
}
public struct WrapAllProperties: MemberAttributeMacro {
public static func expansion(
of node: AttributeSyntax,
attachedTo parent: some DeclGroupSyntax,
providingAttributesFor member: some DeclSyntaxProtocol,
in context: some MacroExpansionContext
) throws -> [AttributeSyntax] {
guard member.is(VariableDeclSyntax.self) else {
return []
}
let wrapperTypeName: String
if parent.is(ClassDeclSyntax.self) {
wrapperTypeName = "EnclosingSelfWrapper"
} else {
wrapperTypeName = "Wrapper"
}
let propertyWrapperAttr = AttributeSyntax(
attributeName: IdentifierTypeSyntax(
name: .identifier(wrapperTypeName)
)
)
return [propertyWrapperAttr]
}
}
public struct TypeWrapperMacro {}
extension TypeWrapperMacro: MemberAttributeMacro {
public static func expansion(
of node: AttributeSyntax,
attachedTo decl: some DeclGroupSyntax,
providingAttributesFor member: some DeclSyntaxProtocol,
in context: some MacroExpansionContext
) throws -> [AttributeSyntax] {
guard let varDecl = member.as(VariableDeclSyntax.self),
let binding = varDecl.bindings.first,
let identifier = binding.pattern.as(IdentifierPatternSyntax.self)?.identifier,
binding.accessorBlock == nil
else {
return []
}
if identifier.text == "_storage" {
return []
}
let customAttr = AttributeSyntax(
attributeName: IdentifierTypeSyntax(
name: .identifier("accessViaStorage")
)
)
return [customAttr]
}
}
extension TypeWrapperMacro: MemberMacro {
public static func expansion(
of node: AttributeSyntax,
providingMembersOf decl: some DeclGroupSyntax,
in context: some MacroExpansionContext
) throws -> [DeclSyntax] {
let storageVariable: DeclSyntax =
"""
private var _storage = _Storage()
"""
return [
storageVariable,
]
}
}
public struct AccessViaStorageMacro: AccessorMacro {
public static func expansion(
of node: AttributeSyntax,
providingAccessorsOf declaration: some DeclSyntaxProtocol,
in context: some MacroExpansionContext
) throws -> [AccessorDeclSyntax] {
guard let varDecl = declaration.as(VariableDeclSyntax.self),
let binding = varDecl.bindings.first,
let identifier = binding.pattern.as(IdentifierPatternSyntax.self)?.identifier,
binding.accessorBlock == nil
else {
return []
}
if identifier.text == "_storage" {
return []
}
return [
"get { _storage.\(identifier) }",
"set { _storage.\(identifier) = newValue }",
]
}
}
public struct AddMembers: MemberMacro {
public static func expansion(
of node: AttributeSyntax,
providingMembersOf decl: some DeclGroupSyntax,
in context: some MacroExpansionContext
) throws -> [DeclSyntax] {
let uniqueClassName = context.makeUniqueName("uniqueClass")
let storageStruct: DeclSyntax =
"""
struct Storage {}
"""
let storageVariable: DeclSyntax =
"""
private var storage = Storage()
"""
let instanceMethod: DeclSyntax =
"""
func getStorage() -> Storage {
print("synthesized method")
return storage
}
"""
let staticMethod: DeclSyntax =
"""
static func method() {}
"""
let initDecl: DeclSyntax =
"""
init() { _ = \(uniqueClassName)() }
"""
let classDecl: DeclSyntax =
"""
class \(uniqueClassName) { }
"""
return [
storageStruct,
storageVariable,
instanceMethod,
staticMethod,
initDecl,
classDecl,
]
}
}
public struct AddExtMembers: MemberMacro {
public static func expansion(
of node: AttributeSyntax,
providingMembersOf decl: some DeclGroupSyntax,
in context: some MacroExpansionContext
) throws -> [DeclSyntax] {
let uniqueClassName = context.makeUniqueName("uniqueClass")
let instanceMethod: DeclSyntax =
"""
func extInstanceMethod() {}
"""
let staticMethod: DeclSyntax =
"""
static func extStaticMethod() {}
"""
let classDecl: DeclSyntax =
"""
class \(uniqueClassName) { }
"""
return [
instanceMethod,
staticMethod,
classDecl,
]
}
}
public struct AddArbitraryMembers: MemberMacro {
public static func expansion(
of node: AttributeSyntax,
providingMembersOf decl: some DeclGroupSyntax,
in context: some MacroExpansionContext
) throws -> [DeclSyntax] {
guard let identified = decl.asProtocol(NamedDeclSyntax.self) else {
return []
}
let parentName = identified.name.trimmed
return [
"struct \(parentName)1 {}",
"struct \(parentName)2 {}",
"struct \(parentName)3 {}",
]
}
}
public struct MemberThatCallsCodeMacro: MemberMacro {
public static func expansion(
of node: AttributeSyntax,
providingMembersOf decl: some DeclGroupSyntax,
in context: some MacroExpansionContext
) throws -> [DeclSyntax] {
guard case let .argumentList(arguments) = node.arguments,
let firstElement = arguments.first,
let stringLiteral = firstElement.expression.as(StringLiteralExprSyntax.self) else {
throw CustomError.message("Macro requires a string literal")
}
let codeString = try stringLiteral.segments.map { segment in
switch segment {
case .stringSegment(let string):
return string.content.text
case .expressionSegment(_):
throw CustomError.message("Macro cannot handle string interpolation")
}
}.joined(separator: "")
return [
"""
static var synthesizedMember: String {
\(raw: codeString)
return "hello"
}
""",
]
}
}
/// Implementation of the `wrapStoredProperties` macro, which can be
/// used to apply an attribute to all of the stored properties of a type.
///
/// This macro demonstrates member-attribute macros.
public struct WrapStoredPropertiesMacro: MemberAttributeMacro {
public static func expansion<
Declaration: DeclGroupSyntax,
Context: MacroExpansionContext
>(
of node: AttributeSyntax,
attachedTo decl: Declaration,
providingAttributesFor member: some DeclSyntaxProtocol,
in context: Context
) throws -> [AttributeSyntax] {
guard let property = member.as(VariableDeclSyntax.self),
property.isStoredProperty
else {
return []
}
guard case let .argumentList(arguments) = node.arguments,
let firstElement = arguments.first,
let stringLiteral = firstElement.expression
.as(StringLiteralExprSyntax.self),
stringLiteral.segments.count == 1,
case let .stringSegment(wrapperName)? = stringLiteral.segments.first else {
throw CustomError.message("macro requires a string literal containing the name of an attribute")
}
return [
AttributeSyntax(
attributeName: IdentifierTypeSyntax(
name: .identifier(wrapperName.content.text)
)
)
]
}
}
extension VariableDeclSyntax {
/// Determine whether this variable has the syntax of a stored property.
///
/// This syntactic check cannot account for semantic adjustments due to,
/// e.g., accessor macros or property wrappers.
var isStoredProperty: Bool {
if bindings.count != 1 {
return false
}
let binding = bindings.first!
switch binding.accessorBlock?.accessors {
case .none:
return true
case .accessors(let node):
for accessor in node {
switch accessor.accessorSpecifier.tokenKind {
case .keyword(.willSet), .keyword(.didSet):
// Observers can occur on a stored property.
break
default:
// Other accessors make it a computed property.
return false
}
}
return true
case .getter:
return false
@unknown default:
return false
}
}
}
extension DeclGroupSyntax {
/// Enumerate the stored properties that syntactically occur in this
/// declaration.
func storedProperties() -> [VariableDeclSyntax] {
return memberBlock.members.compactMap { member in
guard let variable = member.decl.as(VariableDeclSyntax.self),
variable.isStoredProperty else {
return nil
}
return variable
}
}
}
public enum LeftHandOperandFinderMacro: ExpressionMacro {
class Visitor<Context: MacroExpansionContext>: SyntaxVisitor {
let context: Context
init(context: Context) {
self.context = context
super.init(viewMode: .sourceAccurate)
}
override func visit(
_ node: InfixOperatorExprSyntax
) -> SyntaxVisitorContinueKind {
guard let lhsStartLoc = context.location(of: node.leftOperand),
let lhsEndLoc = context.location(
of: node.leftOperand, at: .beforeTrailingTrivia, filePathMode: .fileID
) else {
fatalError("missing source location information")
}
print("Source range for LHS is \(lhsStartLoc.file): \(lhsStartLoc.line):\(lhsStartLoc.column)-\(lhsEndLoc.line):\(lhsEndLoc.column)")
return .visitChildren
}
}
public static func expansion(
of node: some FreestandingMacroExpansionSyntax,
in context: some MacroExpansionContext
) -> ExprSyntax {
let visitor = Visitor(context: context)
visitor.walk(node)
return node.arguments.first!.expression
}
}
extension SyntaxCollection {
mutating func removeLast() {
self.remove(at: self.index(before: self.endIndex))
}
}
public struct AddAsyncMacro: PeerMacro {
public static func expansion<
Context: MacroExpansionContext,
Declaration: DeclSyntaxProtocol
>(
of node: AttributeSyntax,
providingPeersOf declaration: Declaration,
in context: Context
) throws -> [DeclSyntax] {
// Only on functions at the moment.
guard var funcDecl = declaration.as(FunctionDeclSyntax.self) else {
throw CustomError.message("@addAsync only works on functions")
}
// This only makes sense for non async functions.
if funcDecl.signature.effectSpecifiers?.asyncSpecifier != nil {
throw CustomError.message(
"@addAsync requires an non async function"
)
}
// This only makes sense void functions
if let resultType = funcDecl.signature.returnClause?.type,
resultType.as(IdentifierTypeSyntax.self)?.name.text != "Void" {
throw CustomError.message(
"@addAsync requires an function that returns void"
)
}
// Requires a completion handler block as last parameter
let completionHandlerParameter = funcDecl
.signature
.parameterClause
.parameters.last?
.type.as(AttributedTypeSyntax.self)?
.baseType.as(FunctionTypeSyntax.self)
guard let completionHandlerParameter else {
throw CustomError.message(
"@AddAsync requires an function that has a completion handler as last parameter"
)
}
// Completion handler needs to return Void
if completionHandlerParameter.returnClause.type.as(IdentifierTypeSyntax.self)?.name.text != "Void" {
throw CustomError.message(
"@AddAsync requires an function that has a completion handler that returns Void"
)
}
let returnType = completionHandlerParameter.parameters.first?.type
let isResultReturn = returnType?.children(viewMode: .all).first?.description == "Result"
let successReturnType: TypeSyntax?
if isResultReturn {
let argument = returnType!.as(IdentifierTypeSyntax.self)!.genericArgumentClause?.arguments.first!.argument
switch argument {
case .some(.type(let type)):
successReturnType = type
case .some(.expr(_)):
fatalError("expression not available here")
case .none:
successReturnType = nil
}
} else {
successReturnType = returnType
}
// Remove completionHandler and comma from the previous parameter
var newParameterList = funcDecl.signature.parameterClause.parameters
newParameterList.removeLast()
var newParameterListLastParameter = newParameterList.last!
newParameterList.removeLast()
newParameterListLastParameter.trailingTrivia = []
newParameterListLastParameter.trailingComma = nil
newParameterList.append(newParameterListLastParameter)
// Drop the @AddAsync attribute from the new declaration.
let newAttributeList = funcDecl.attributes.filter {
guard case let .attribute(attribute) = $0,
let attributeType = attribute.attributeName.as(IdentifierTypeSyntax.self),
let nodeType = node.attributeName.as(IdentifierTypeSyntax.self)
else {
return true
}
return attributeType.name.text != nodeType.name.text
}
let callArguments: [String] = newParameterList.map { param in
let argName = param.secondName ?? param.firstName
let paramName = param.firstName
if paramName.text != "_" {
return "\(paramName.text): \(argName.text)"
}
return "\(argName.text)"
}
let switchBody: ExprSyntax =
"""
switch returnValue {
case .success(let value):
continuation.resume(returning: value)
case .failure(let error):
continuation.resume(throwing: error)
}
"""
let newBody: ExprSyntax =
"""
\(raw: isResultReturn ? "try await withCheckedThrowingContinuation { continuation in" : "await withCheckedContinuation { continuation in")
\(raw: funcDecl.name)(\(raw: callArguments.joined(separator: ", "))) { \(raw: returnType != nil ? "returnValue in" : "")
\(raw: isResultReturn ? switchBody : "continuation.resume(returning: \(raw: returnType != nil ? "returnValue" : "()"))")
}
}
"""
// add async
funcDecl.signature.effectSpecifiers = FunctionEffectSpecifiersSyntax(
leadingTrivia: .space,
asyncSpecifier: .keyword(.async),
throwsClause: isResultReturn ? ThrowsClauseSyntax(throwsSpecifier: .keyword(.throws)) : nil
)
// add result type
if let successReturnType {
funcDecl.signature.returnClause = ReturnClauseSyntax(
leadingTrivia: .space,
type: successReturnType.with(\.leadingTrivia, .space)
)
} else {
funcDecl.signature.returnClause = nil
}
// drop completion handler
funcDecl.signature.parameterClause.parameters = newParameterList
funcDecl.signature.parameterClause.trailingTrivia = []
funcDecl.body = CodeBlockSyntax(
leftBrace: .leftBraceToken(leadingTrivia: .space),
statements: CodeBlockItemListSyntax(
[CodeBlockItemSyntax(item: .expr(newBody))]
),
rightBrace: .rightBraceToken(leadingTrivia: .newline)
)
funcDecl.attributes = newAttributeList
// If this declaration needs to be final, make it so.
let isFinal = node.attributeName.trimmedDescription.contains("AddAsyncFinal")
if isFinal {
var allModifiers = Array(funcDecl.modifiers)
if let openIndex = allModifiers.firstIndex(where: { $0.name.text == "open" }) {
allModifiers[openIndex].name = .keyword(.public)
} else {
allModifiers.append(
DeclModifierSyntax(
name: .keyword(.public),
trailingTrivia: .space
)
)
}
allModifiers.append(
DeclModifierSyntax(
name: .keyword(.final),
trailingTrivia: .space
)
)
funcDecl.modifiers = DeclModifierListSyntax(allModifiers)
var allAttributes = Array(funcDecl.attributes)
allAttributes.append(
.attribute("@_alwaysEmitIntoClient ")
)
funcDecl.attributes = AttributeListSyntax(allAttributes)
}
funcDecl.leadingTrivia = .newlines(2)
return [DeclSyntax(funcDecl)]
}
}
public struct AddCompletionHandler: PeerMacro {
public static func expansion(
of node: AttributeSyntax,
providingPeersOf declaration: some DeclSyntaxProtocol,
in context: some MacroExpansionContext
) throws -> [DeclSyntax] {
// Only on functions at the moment. We could handle initializers as well
// with a bit of work.
guard let funcDecl = declaration.as(FunctionDeclSyntax.self) else {
throw CustomError.message("@addCompletionHandler only works on functions")
}
// This only makes sense for async functions.
if funcDecl.signature.effectSpecifiers?.asyncSpecifier == nil {
throw CustomError.message(
"@addCompletionHandler requires an async function"
)
}
// Form the completion handler parameter.
let resultType: TypeSyntax? = funcDecl.signature.returnClause?.type.with(\.leadingTrivia, []).with(\.trailingTrivia, [])
let completionHandlerParam =
FunctionParameterSyntax(
firstName: .identifier("completionHandler"),
colon: .colonToken(),
type: "@escaping (\(resultType ?? "")) -> Void" as TypeSyntax
)
// Add the completion handler parameter to the parameter list.
let parameterList = funcDecl.signature.parameterClause.parameters
var newParameterList = parameterList
if !newParameterList.isEmpty {
// We need to add a trailing comma to the preceding list.
let lastIndex = newParameterList.index(before: newParameterList.endIndex)
newParameterList[lastIndex].trailingComma = .commaToken()
}
newParameterList.append(completionHandlerParam)
let callArguments: [String] = parameterList.map { param in
let argName = param.secondName ?? param.firstName
if param.firstName.text != "_" {
return "\(param.firstName.text): \(argName.text)"
}
return "\(argName.text)"
}
let call: ExprSyntax =
"\(funcDecl.name)(\(raw: callArguments.joined(separator: ", ")))"
// FIXME: We should make CodeBlockSyntax ExpressibleByStringInterpolation,
// so that the full body could go here.
let newBody: ExprSyntax =
"""
Task {
completionHandler(await \(call))
}
"""
// Drop the @addCompletionHandler attribute from the new declaration.
let newAttributeList = funcDecl.attributes.filter {
guard case let .attribute(attribute) = $0 else {
return true
}
if let attributeType = attribute.attributeName.as(IdentifierTypeSyntax.self),
let nodeType = node.attributeName.as(IdentifierTypeSyntax.self) {
return attributeType.name.text != nodeType.name.text
}
if let attributeMemberType = attribute.attributeName.as(MemberTypeSyntax.self),
let attributeModuleName = attributeMemberType.baseType.as(IdentifierTypeSyntax.self),
let nodeMemberType = node.attributeName.as(MemberTypeSyntax.self),
let moduleName = nodeMemberType.baseType.as(IdentifierTypeSyntax.self) {
return attributeModuleName.name.text != moduleName.name.text ||
nodeMemberType.name.text != attributeMemberType.name.text
}
return true
}
var newFunc = funcDecl
newFunc.signature.effectSpecifiers?.asyncSpecifier = nil // drop async
newFunc.signature.returnClause = nil // drop result type
newFunc.signature.parameterClause.parameters = newParameterList
newFunc.signature.parameterClause.trailingTrivia = []
newFunc.body = CodeBlockSyntax(
leftBrace: .leftBraceToken(),
statements: CodeBlockItemListSyntax(
[CodeBlockItemSyntax(item: .expr(newBody))]
),
rightBrace: .rightBraceToken()
)
newFunc.attributes = newAttributeList
return [DeclSyntax(newFunc)]
}
}
public struct ExpandTypeErrorMacro: PeerMacro {
public static func expansion<
Context: MacroExpansionContext,
Declaration: DeclSyntaxProtocol
>(
of node: AttributeSyntax,
providingPeersOf declaration: Declaration,
in context: Context
) throws -> [DeclSyntax] {
guard let funcDecl = declaration.as(FunctionDeclSyntax.self) else {
throw CustomError.message("@ExpandTypeError only works on functions")
}
return [
"""
public func \(funcDecl.name)(_ bar: Int) {
callToMissingFunction(foo)
}
"""
]
}
}
public struct InvalidMacro: PeerMacro, DeclarationMacro {
public static func expansion(
of node: AttributeSyntax,
providingPeersOf declaration: some DeclSyntaxProtocol,
in context: some MacroExpansionContext
) throws -> [DeclSyntax] {
return [
"import Swift",
"precedencegroup MyPrecedence {}",
"@attached(member) macro myMacro()",
"extension Int {}",
"""
@main
struct MyMain {
static func main() {}
}
""",
"typealias Array = Void",
"typealias Dictionary = Void",
"typealias BooleanLiteralType = Void",
"typealias ExtendedGraphemeClusterType = Void",
"typealias FloatLiteralType = Void",
"typealias IntegerLiteralType = Void",
"typealias StringLiteralType = Void",
"typealias UnicodeScalarType = Void",
"typealias _ColorLiteralType = Void",
"typealias _ImageLiteralType = Void",
"typealias _FileReferenceLiteralType = Void",
]
}
public static func expansion(
of node: some FreestandingMacroExpansionSyntax,
in context: some MacroExpansionContext
) throws -> [DeclSyntax] {
return [
"var value: Int"
]
}
}
public struct CoerceToIntMacro: ExpressionMacro {
public static func expansion(
of node: some FreestandingMacroExpansionSyntax,
in context: some MacroExpansionContext
) -> ExprSyntax {
"\(node.arguments.first!.expression) as Int"
}
}
public struct WrapInType: PeerMacro {
public static func expansion(
of node: AttributeSyntax,
providingPeersOf declaration: some DeclSyntaxProtocol,
in context: some MacroExpansionContext
) throws -> [DeclSyntax] {
guard let funcDecl = declaration.as(FunctionDeclSyntax.self) else {
throw CustomError.message("@wrapInType only applies to functions")
}
// Build a new function with the same signature that forwards arguments
// to the original function.
let parameterList = funcDecl.signature.parameterClause.parameters
let callArguments: [String] = parameterList.map { param in
let argName = param.secondName ?? param.firstName
if param.firstName.text != "_" {
return "\(param.firstName.text): \(argName.text)"
}
return "\(argName.text)"
}
let call: ExprSyntax =
"""
\(funcDecl.name)(\(raw: callArguments.joined(separator: ", ")))
"""
// Drop the peer macro attribute from the new declaration.
let newAttributeList = funcDecl.attributes.filter {
guard case let .attribute(attribute) = $0,
let attributeType = attribute.attributeName.as(IdentifierTypeSyntax.self),
let nodeType = node.attributeName.as(IdentifierTypeSyntax.self)
else {
return true
}
return attributeType.name.text != nodeType.name.text
}
var method = funcDecl
method.name = "\(context.makeUniqueName(funcDecl.name.text))"
method.signature = funcDecl.signature
method.body = CodeBlockSyntax(
leftBrace: .leftBraceToken(),
statements: CodeBlockItemListSyntax(
[CodeBlockItemSyntax(item: .expr(call))]
),
rightBrace: .rightBraceToken()
)
method.attributes = newAttributeList
let structType: DeclSyntax =
"""
struct \(context.makeUniqueName(funcDecl.name.text)) {
\(method)
}
"""
return [structType]
}
}
private extension DeclSyntaxProtocol {
var isObservableStoredProperty: Bool {
if let property = self.as(VariableDeclSyntax.self),
let binding = property.bindings.first,
let identifier = binding.pattern.as(IdentifierPatternSyntax.self)?.identifier,
identifier.text != "_registrar", identifier.text != "_storage",
binding.accessorBlock == nil {
return true
}
return false
}
}
public struct ObservableMacro: MemberMacro, MemberAttributeMacro {
// MARK: - MemberMacro
public static func expansion(
of node: AttributeSyntax,
providingMembersOf declaration: some DeclGroupSyntax,
in context: some MacroExpansionContext
) throws -> [DeclSyntax] {
guard let identified = declaration.asProtocol(NamedDeclSyntax.self) else {
return []
}
let parentName = identified.name
let registrar: DeclSyntax =
"""
let _registrar = ObservationRegistrar<\(parentName)>()
"""
let addObserver: DeclSyntax =
"""
public nonisolated func addObserver(_ observer: some Observer<\(parentName)>) {
_registrar.addObserver(observer)
}
"""
let removeObserver: DeclSyntax =
"""
public nonisolated func removeObserver(_ observer: some Observer<\(parentName)>) {
_registrar.removeObserver(observer)
}
"""
let withTransaction: DeclSyntax =
"""
private func withTransaction<T>(_ apply: () throws -> T) rethrows -> T {
_registrar.beginAccess()
defer { _registrar.endAccess() }
return try apply()
}
"""
let memberList = declaration.memberBlock.members.filter {
$0.decl.isObservableStoredProperty
}
let storageStruct: DeclSyntax =
"""
private struct Storage {
\(memberList)
}
"""
let storage: DeclSyntax =
"""
private var _storage = Storage()
"""
return [
registrar,
addObserver,
removeObserver,
withTransaction,
storageStruct,
storage,
]
}
// MARK: - MemberAttributeMacro
public static func expansion(
of node: AttributeSyntax,
attachedTo declaration: some DeclGroupSyntax,
providingAttributesFor member: some DeclSyntaxProtocol,
in context: some MacroExpansionContext
) throws -> [SwiftSyntax.AttributeSyntax] {
guard member.isObservableStoredProperty else {
return []
}
return [
AttributeSyntax(
attributeName: IdentifierTypeSyntax(
name: .identifier("ObservableProperty")
)
)
]
}
}
extension ObservableMacro: ExtensionMacro {
public static func expansion(
of node: AttributeSyntax,
attachedTo decl: some DeclGroupSyntax,
providingExtensionsOf type: some TypeSyntaxProtocol,
conformingTo protocols: [TypeSyntax],
in context: some MacroExpansionContext
) throws -> [ExtensionDeclSyntax] {
if (protocols.isEmpty) {
return []
}
let decl: DeclSyntax =
"""
extension \(raw: type.trimmedDescription): Observable {
}
"""
return [
decl.cast(ExtensionDeclSyntax.self)
]
}
}
public struct ObservablePropertyMacro: AccessorMacro {
public static func expansion(
of node: AttributeSyntax,
providingAccessorsOf declaration: some DeclSyntaxProtocol,
in context: some MacroExpansionContext
) throws -> [AccessorDeclSyntax] {
guard let property = declaration.as(VariableDeclSyntax.self),
let binding = property.bindings.first,
let identifier = binding.pattern.as(IdentifierPatternSyntax.self)?.identifier,
binding.accessorBlock == nil
else {
return []
}
let getAccessor: AccessorDeclSyntax =
"""
get {
_registrar.beginAccess(\\.\(identifier))
defer { _registrar.endAccess() }
return _storage.\(identifier)
}
"""
let setAccessor: AccessorDeclSyntax =
"""
set {
_registrar.beginAccess(\\.\(identifier))
_registrar.register(observable: self, willSet: \\.\(identifier), to: newValue)
defer {
_registrar.register(observable: self, didSet: \\.\(identifier))
_registrar.endAccess()
}
_storage.\(identifier) = newValue
}
"""
return [getAccessor, setAccessor]
}
}
extension DeclModifierSyntax {
fileprivate var isNeededAccessLevelModifier: Bool {
switch self.name.tokenKind {
case .keyword(.public): return true
default: return false
}
}
}
extension SyntaxStringInterpolation {
fileprivate mutating func appendInterpolation<Node: SyntaxProtocol>(_ node: Node?) {
if let node {
appendInterpolation(node)
}
}
}
public struct NewTypeMacro: MemberMacro {
public static func expansion(
of node: AttributeSyntax,
providingMembersOf declaration: some DeclGroupSyntax,
in context: some MacroExpansionContext
) throws -> [DeclSyntax] {
guard let type = node.attributeName.as(IdentifierTypeSyntax.self),
let genericArguments = type.genericArgumentClause?.arguments,
genericArguments.count == 1,
let rawType = genericArguments.first
else {
throw CustomError.message(#"@NewType requires the raw type as an argument, in the form "<RawType>"."#)
}
guard let declaration = declaration.as(StructDeclSyntax.self) else {
throw CustomError.message("@NewType can only be applied to a struct declarations.")
}
let access = declaration.modifiers.first(where: \.isNeededAccessLevelModifier)
return [
"\(access)typealias RawValue = \(rawType)",
"\(access)var rawValue: RawValue",
"\(access)init(_ rawValue: RawValue) { self.rawValue = rawValue }",
]
}
}
public struct EmptyMacro: MemberMacro {
public static func expansion(
of node: AttributeSyntax,
providingMembersOf decl: some DeclGroupSyntax,
in context: some MacroExpansionContext
) throws -> [DeclSyntax] {
return []
}
}
public struct EmptyPeerMacro: PeerMacro {
public static func expansion(
of node: AttributeSyntax,
providingPeersOf declaration: some DeclSyntaxProtocol,
in context: some MacroExpansionContext
) throws -> [DeclSyntax] {
return []
}
}
public struct EquatableMacro: ExtensionMacro {
public static func expansion(
of node: AttributeSyntax,
attachedTo: some DeclGroupSyntax,
providingExtensionsOf type: some TypeSyntaxProtocol,
conformingTo protocols: [TypeSyntax],
in context: some MacroExpansionContext
) throws -> [ExtensionDeclSyntax] {
let ext: DeclSyntax = "extension \(type.trimmed): Equatable {}"
return [ext.cast(ExtensionDeclSyntax.self)]
}
}
public struct EquatableViaMembersMacro: ExtensionMacro {
public static func expansion(
of node: AttributeSyntax,
attachedTo decl: some DeclGroupSyntax,
providingExtensionsOf type: some TypeSyntaxProtocol,
conformingTo protocols: [TypeSyntax],
in context: some MacroExpansionContext
) throws -> [ExtensionDeclSyntax] {
let comparisons: [String] = decl.storedProperties().map { property in
guard let binding = property.bindings.first,
let identifier = binding.pattern.as(IdentifierPatternSyntax.self)?.identifier else {
return "true"
}
return "lhs.\(identifier) == rhs.\(identifier)"
}
let condition = comparisons.joined(separator: " && ")
let equalOperator: DeclSyntax = """
static func ==(lhs: \(type.trimmed), rhs: \(type.trimmed)) -> Bool {
return \(raw: condition)
}
"""
let ext: DeclSyntax = """
extension \(type.trimmed): Equatable {
\(equalOperator)
}
"""
return [ext.cast(ExtensionDeclSyntax.self)]
}
}
public struct FooExtensionMacro: ExtensionMacro {
public static func expansion(of node: AttributeSyntax, attachedTo declaration: some DeclGroupSyntax, providingExtensionsOf type: some TypeSyntaxProtocol, conformingTo protocols: [TypeSyntax], in context: some MacroExpansionContext) throws -> [ExtensionDeclSyntax] {
let decl: DeclSyntax =
"""
extension Foo {
var foo: String { "foo" }
func printFoo() {
print(foo)
}
}
"""
guard let extensionDecl = decl.as(ExtensionDeclSyntax.self) else {
return []
}
return [extensionDecl]
}
}
public struct BadExtensionMacro: ExtensionMacro {
public static func expansion(
of node: AttributeSyntax,
attachedTo declaration: some DeclGroupSyntax,
providingExtensionsOf type: some TypeSyntaxProtocol,
conformingTo protocols: [TypeSyntax],
in context: some MacroExpansionContext
) throws -> [ExtensionDeclSyntax] {
// Note this is purposefully not using `providingExtensionsOf`.
let unqualifiedName = declaration.as(StructDeclSyntax.self)!.name.trimmed
return [try ExtensionDeclSyntax("extension \(unqualifiedName) {}")]
}
}
public struct ConformanceViaExtensionMacro: ExtensionMacro {
public static func expansion(
of node: AttributeSyntax,
attachedTo decl: some DeclGroupSyntax,
providingExtensionsOf type: some TypeSyntaxProtocol,
conformingTo protocols: [TypeSyntax],
in context: some MacroExpansionContext
) throws -> [ExtensionDeclSyntax] {
if (protocols.isEmpty) {
return []
}
let decl: DeclSyntax =
"""
extension \(raw: type.trimmedDescription): MyProtocol {
}
"""
return [
decl.cast(ExtensionDeclSyntax.self)
]
}
}
public struct HashableMacro: ExtensionMacro {
public static func expansion(
of node: AttributeSyntax,
attachedTo: some DeclGroupSyntax,
providingExtensionsOf type: some TypeSyntaxProtocol,
conformingTo protocols: [TypeSyntax],
in context: some MacroExpansionContext
) throws -> [ExtensionDeclSyntax] {
let ext: DeclSyntax = "extension \(type.trimmed): Hashable {}"
return [ext.cast(ExtensionDeclSyntax.self)]
}
}
public struct ImpliesHashableMacro: ExtensionMacro {
public static func expansion(
of node: AttributeSyntax,
attachedTo: some DeclGroupSyntax,
providingExtensionsOf type: some TypeSyntaxProtocol,
conformingTo protocols: [TypeSyntax],
in context: some MacroExpansionContext
) throws -> [ExtensionDeclSyntax] {
let ext: DeclSyntax = "extension \(type.trimmed): ImpliesHashable {}"
return [ext.cast(ExtensionDeclSyntax.self)]
}
}
public struct DelegatedConformanceMacro: ExtensionMacro, MemberMacro {
public static func expansion(
of node: AttributeSyntax,
attachedTo: some DeclGroupSyntax,
providingExtensionsOf type: some TypeSyntaxProtocol,
conformingTo protocols: [TypeSyntax],
in context: some MacroExpansionContext
) throws -> [ExtensionDeclSyntax] {
let conformance: DeclSyntax =
"""
extension \(type.trimmed): P where Element: P {}
"""
guard let extensionDecl = conformance.as(ExtensionDeclSyntax.self) else {
return []
}
return [extensionDecl]
}
public static func expansion(
of node: AttributeSyntax,
providingMembersOf decl: some DeclGroupSyntax,
in context: some MacroExpansionContext
) throws -> [DeclSyntax] {
let requirement: DeclSyntax =
"""
static func requirement() where Element : P {
Element.requirement()
}
"""
return [requirement]
}
}
public struct DelegatedConformanceViaExtensionMacro: ExtensionMacro {
public static func expansion(
of node: AttributeSyntax,
attachedTo decl: some DeclGroupSyntax,
providingExtensionsOf type: some TypeSyntaxProtocol,
conformingTo protocols: [TypeSyntax],
in context: some MacroExpansionContext
) throws -> [ExtensionDeclSyntax] {
if (protocols.isEmpty) {
return []
}
let decl: DeclSyntax =
"""
extension \(raw: type.trimmedDescription): P where Element: P {
static func requirement() {
Element.requirement()
}
}
"""
guard let extensionDecl = decl.as(ExtensionDeclSyntax.self) else {
return []
}
return [
extensionDecl
]
}
}
public struct AlwaysAddConformance: ExtensionMacro {
public static func expansion(
of node: AttributeSyntax,
attachedTo decl: some DeclGroupSyntax,
providingExtensionsOf type: some TypeSyntaxProtocol,
conformingTo protocols: [TypeSyntax],
in context: some MacroExpansionContext
) throws -> [ExtensionDeclSyntax] {
let decl: DeclSyntax =
"""
extension \(raw: type.trimmedDescription): P where Element: P {
static func requirement() {
Element.requirement()
}
}
"""
return [
decl.cast(ExtensionDeclSyntax.self)
]
}
}
public struct ConditionallyAvailableConformance: ExtensionMacro {
public static func expansion(
of node: AttributeSyntax,
attachedTo decl: some DeclGroupSyntax,
providingExtensionsOf type: some TypeSyntaxProtocol,
conformingTo protocols: [TypeSyntax],
in context: some MacroExpansionContext
) throws -> [ExtensionDeclSyntax] {
let decl: DeclSyntax =
"""
@available(macOS 99, *)
extension \(raw: type.trimmedDescription): Equatable {}
"""
return [
decl.cast(ExtensionDeclSyntax.self)
]
}
}
public struct AddAllConformancesMacro: ExtensionMacro {
public static func expansion(
of node: AttributeSyntax,
attachedTo decl: some DeclGroupSyntax,
providingExtensionsOf type: some TypeSyntaxProtocol,
conformingTo protocols: [TypeSyntax],
in context: some MacroExpansionContext
) throws -> [ExtensionDeclSyntax] {
protocols.map { proto in
let decl: DeclSyntax =
"""
extension \(type): \(proto) {}
"""
return decl.cast(ExtensionDeclSyntax.self)
}
}
}
public struct ListConformancesMacro { }
extension ListConformancesMacro: ExtensionMacro {
public static func expansion(
of node: AttributeSyntax,
attachedTo decl: some DeclGroupSyntax,
providingExtensionsOf type: some TypeSyntaxProtocol,
conformingTo protocols: [TypeSyntax],
in context: some MacroExpansionContext
) throws -> [ExtensionDeclSyntax] {
protocols.map { proto in
let decl: DeclSyntax =
"""
extension \(type): \(proto) {}
"""
return decl.cast(ExtensionDeclSyntax.self)
}
}
}
extension ListConformancesMacro: MemberMacro {
public static func expansion(
of node: AttributeSyntax,
providingMembersOf declaration: some DeclGroupSyntax,
conformingTo protocols: [TypeSyntax],
in context: some MacroExpansionContext
) throws -> [DeclSyntax] {
let typeName = declaration.asProtocol(NamedDeclSyntax.self)!.name.text
let protocolNames: [ExprSyntax] = protocols.map { "\(literal: $0.trimmedDescription)" }
let protocolsArray: ExprSyntax =
"[ \(raw: protocolNames.map { $0.description }.joined(separator: ", ")) ]"
let unknownDecl: DeclSyntax =
"""
@_nonoverride static func conformances() -> [String: [String]] { [ \(literal: typeName): \(protocolsArray) ] }
"""
return [unknownDecl]
}
}
public struct AlwaysAddCodable: ExtensionMacro {
public static func expansion(
of node: AttributeSyntax,
attachedTo decl: some DeclGroupSyntax,
providingExtensionsOf type: some TypeSyntaxProtocol,
conformingTo protocols: [TypeSyntax],
in context: some MacroExpansionContext
) throws -> [ExtensionDeclSyntax] {
let decl: DeclSyntax =
"""
extension \(raw: type.trimmedDescription): Codable {
}
"""
return [
decl.cast(ExtensionDeclSyntax.self)
]
}
}
public struct ExtendableEnum: MemberMacro {
public static func expansion(
of node: AttributeSyntax,
providingMembersOf declaration: some DeclGroupSyntax,
in context: some MacroExpansionContext
) throws -> [DeclSyntax] {
let unknownDecl: DeclSyntax =
"""
func unknown() -> Int { 34 } // or something like: `case unknown`
"""
return [unknownDecl]
}
}
public struct DefineStructWithUnqualifiedLookupMacro: DeclarationMacro {
public static func expansion(
of node: some FreestandingMacroExpansionSyntax,
in context: some MacroExpansionContext
) throws -> [DeclSyntax] {
return ["""
struct StructWithUnqualifiedLookup {
let hello = 1
func foo() -> Int {
hello + world // looks up "world" in the parent scope
}
}
"""]
}
}
public struct DefineComparableTypeMacro: DeclarationMacro {
public static func expansion(
of node: some FreestandingMacroExpansionSyntax,
in context: some MacroExpansionContext
) throws -> [DeclSyntax] {
return ["""
struct ComparableType: Comparable {
static func <(lhs: ComparableType, rhs: ComparableType) -> Bool {
return false
}
enum Inner: String, Comparable {
case hello = "hello"
static func <(lhs: ComparableType.Inner, rhs: ComparableType.Inner) -> Bool {
return lhs.rawValue < rhs.rawValue
}
}
}
"""]
}
}
public struct AddMemberWithFixIt: MemberMacro {
public static func expansion<
Declaration: DeclGroupSyntax, Context: MacroExpansionContext
>(
of node: AttributeSyntax,
providingMembersOf declaration: Declaration,
in context: Context
) throws -> [DeclSyntax] {
[
"""
func foo() {
var x = 0
_ = x
}
"""
]
}
}
extension LabeledExprListSyntax {
/// Retrieve the first element with the given label.
func first(labeled name: String) -> Element? {
return first { element in
if let label = element.label, label.text == name {
return true
}
return false
}
}
}
public struct DefineAnonymousTypesMacro: DeclarationMacro {
public static func expansion(
of node: some FreestandingMacroExpansionSyntax,
in context: some MacroExpansionContext
) throws -> [DeclSyntax] {
guard let body = node.trailingClosure else {
throw CustomError.message("#anonymousTypes macro requires a trailing closure")
}
let accessSpecifier: String
if let _ = node.arguments.first(labeled: "public") {
accessSpecifier = "public "
} else {
accessSpecifier = ""
}
var results: [DeclSyntax] = [
"""
\(raw:accessSpecifier)class \(context.makeUniqueName("name")) {
\(raw:accessSpecifier)func hello() -> String {
\(body.statements)
}
\(raw:accessSpecifier)func getSelf() -> Any.Type { return Self.self }
}
""",
"""
enum \(context.makeUniqueName("name")) {
case apple
case banana
func hello() -> String {
\(body.statements)
}
}
""",
"""
struct \(context.makeUniqueName("name")): Equatable {
static func == (lhs: Self, rhs: Self) -> Bool { false }
}
"""
]
if let _ = node.arguments.first(labeled: "causeErrors") {
results += ["""
struct \(context.makeUniqueName("name"))<T> where T == Equatable { // expect error: need 'any'
#introduceTypeCheckingErrors // make sure we get nested errors
}
"""]
}
return results
}
}
public struct IntroduceTypeCheckingErrorsMacro: DeclarationMacro {
public static func expansion(
of node: some FreestandingMacroExpansionSyntax,
in context: some MacroExpansionContext
) -> [DeclSyntax] {
return [
"""
struct \(context.makeUniqueName("name")) {
struct \(context.makeUniqueName("name"))<T> where T == Hashable { // expect error: need 'any'
}
}
"""
]
}
}
public struct AddClassReferencingSelfMacro: PeerMacro {
public static func expansion(
of node: AttributeSyntax,
providingPeersOf declaration: some DeclSyntaxProtocol,
in context: some MacroExpansionContext
) throws -> [DeclSyntax] {
guard let protocolDecl = declaration.as(ProtocolDeclSyntax.self) else {
throw CustomError.message("Macro can only be applied to a protocol declarations.")
}
let className = "\(protocolDecl.name.text)Builder"
return [
"""
struct \(raw: className) {
init(_ build: (_ builder: Self) -> Self) {
_ = build(self)
}
}
"""
]
}
}
public struct SimpleCodeItemMacro: CodeItemMacro {
public static func expansion(
of node: some FreestandingMacroExpansionSyntax,
in context: some MacroExpansionContext
) throws -> [CodeBlockItemSyntax] {
[
.init(item: .decl("""
struct \(context.makeUniqueName("foo")) {
var x: Int
}
""")),
.init(item: .stmt("""
if true {
print("from stmt")
usedInExpandedStmt()
}
""")),
.init(item: .stmt("""
if 1 == 0 {
print("impossible")
}
""")),
.init(item: .expr("""
print("from expr")
""")),
]
}
}
public struct MultiStatementClosure: ExpressionMacro {
public static func expansion(
of node: some FreestandingMacroExpansionSyntax,
in context: some MacroExpansionContext
) throws -> ExprSyntax {
return """
{
let temp = 10
let result = temp
return result
}()
"""
}
}
public struct VarValueMacro: DeclarationMacro, PeerMacro {
public static func expansion(
of macro: some FreestandingMacroExpansionSyntax,
in context: some MacroExpansionContext
) -> [DeclSyntax] {
return [
"var value: Int { 1 }"
]
}
public static func expansion(
of node: AttributeSyntax,
providingPeersOf declaration: some DeclSyntaxProtocol,
in context: some MacroExpansionContext
) throws -> [DeclSyntax] {
return [
"var value: Int { 1 }"
]
}
}
struct StoredPropertyMacro: DeclarationMacro {
public static func expansion(
of node: some FreestandingMacroExpansionSyntax,
in context: some MacroExpansionContext
) throws -> [DeclSyntax] {
guard let argument = node.arguments.first?.expression else {
fatalError("boom")
}
return [
"var storedProperty = \(argument)"
]
}
}
public struct GenericToVoidMacro: ExpressionMacro {
public static func expansion(
of node: some FreestandingMacroExpansionSyntax,
in context: some MacroExpansionContext
) throws -> ExprSyntax {
return """
()
"""
}
}
public struct SingleMemberMacro: MemberMacro {
public static func expansion(
of node: AttributeSyntax,
providingMembersOf decl: some DeclGroupSyntax,
in context: some MacroExpansionContext
) throws -> [DeclSyntax] {
let member: DeclSyntax =
"""
var expandedMember: Int = 10
"""
return [
member,
]
}
}
public struct UseIdentifierMacro: DeclarationMacro {
public static func expansion(
of node: some FreestandingMacroExpansionSyntax,
in context: some MacroExpansionContext
) throws -> [DeclSyntax] {
guard let argument = node.arguments.first?.expression.as(StringLiteralExprSyntax.self) else {
fatalError("boom")
}
return [
"""
func \(context.makeUniqueName("name"))() {
_ = \(raw: argument.segments.first!)
}
""",
"""
struct Foo {
var \(context.makeUniqueName("name")): Int {
_ = \(raw: argument.segments.first!)
return 0
}
}
"""
]
}
}
public struct StaticFooFuncMacro: DeclarationMacro {
public static func expansion(
of node: some FreestandingMacroExpansionSyntax,
in context: some MacroExpansionContext
) throws -> [DeclSyntax] {
return [
"static func foo() {}",
]
}
}
public struct SelfAlwaysEqualOperator: DeclarationMacro {
public static func expansion(
of node: some FreestandingMacroExpansionSyntax,
in context: some MacroExpansionContext
) throws -> [DeclSyntax] {
return [
"static func ==(lhs: Self, rhs: Bool) -> Bool { true }",
]
}
}
extension SelfAlwaysEqualOperator: MemberMacro {
public static func expansion(
of node: AttributeSyntax,
providingMembersOf decl: some DeclGroupSyntax,
in context: some MacroExpansionContext
) throws -> [DeclSyntax] {
return [
"static func ==(lhs: Self, rhs: Bool) -> Bool { true }",
]
}
}
public struct AddPeerStoredPropertyMacro: PeerMacro, Sendable {
public static func expansion(
of attribute: AttributeSyntax,
providingPeersOf declaration: some DeclSyntaxProtocol,
in context: some MacroExpansionContext
) throws -> [DeclSyntax] {
return [
"""
public var _foo: Int = 100
"""
]
}
}
public struct InitializableMacro: ExtensionMacro {
public static func expansion(
of node: AttributeSyntax,
attachedTo: some DeclGroupSyntax,
providingExtensionsOf type: some TypeSyntaxProtocol,
conformingTo protocols: [TypeSyntax],
in context: some MacroExpansionContext
) throws -> [ExtensionDeclSyntax] {
let ext: DeclSyntax =
"""
extension \(type.trimmed): Initializable {
init(value: Int) {}
}
"""
return [ext.cast(ExtensionDeclSyntax.self)]
}
}
public struct PeerValueWithSuffixNameMacro: PeerMacro {
public static func expansion(
of node: AttributeSyntax,
providingPeersOf declaration: some DeclSyntaxProtocol,
in context: some MacroExpansionContext
) throws -> [DeclSyntax] {
guard let identified = declaration.asProtocol(NamedDeclSyntax.self) else {
throw CustomError.message("Macro can only be applied to an identified declarations.")
}
return ["var \(raw: identified.name.text)_peer: Int { 1 }"]
}
}
public struct MagicFileMacro: ExpressionMacro {
public static func expansion(
of node: some FreestandingMacroExpansionSyntax,
in context: some MacroExpansionContext
) -> ExprSyntax {
return "#file"
}
}
public struct MagicLineMacro: ExpressionMacro {
public static func expansion(
of node: some FreestandingMacroExpansionSyntax,
in context: some MacroExpansionContext
) -> ExprSyntax {
return "(#line)"
}
}
public struct NestedMagicLiteralMacro: ExpressionMacro {
public static func expansion(
of node: some FreestandingMacroExpansionSyntax,
in context: some MacroExpansionContext
) -> ExprSyntax {
return
"""
{
print(#MagicFile)
print(#MagicLine)
}()
"""
}
}
public struct NativeFileIDMacro: ExpressionMacro {
public static func expansion(
of node: some FreestandingMacroExpansionSyntax,
in context: some MacroExpansionContext
) -> ExprSyntax {
return context.location(
of: node, at: .afterLeadingTrivia, filePathMode: .fileID
)!.file
}
}
public struct NativeFilePathMacro: ExpressionMacro {
public static func expansion(
of node: some FreestandingMacroExpansionSyntax,
in context: some MacroExpansionContext
) -> ExprSyntax {
return context.location(
of: node, at: .afterLeadingTrivia, filePathMode: .filePath
)!.file
}
}
public struct NativeLineMacro: ExpressionMacro {
public static func expansion(
of node: some FreestandingMacroExpansionSyntax,
in context: some MacroExpansionContext
) -> ExprSyntax {
return context.location(of: node)!.line
}
}
public struct NativeColumnMacro: ExpressionMacro {
public static func expansion(
of node: some FreestandingMacroExpansionSyntax,
in context: some MacroExpansionContext
) -> ExprSyntax {
return context.location(of: node)!.column
}
}
public struct ClosureCallerMacro: ExpressionMacro {
public static func expansion(
of node: some FreestandingMacroExpansionSyntax,
in context: some MacroExpansionContext
) -> ExprSyntax {
let location = context.location(of: node)!
return #"""
ClosureCaller({ (value, then) in
#sourceLocation(file: \#(location.file), line: \#(location.line))
print("\(value)@\(\#(location.file))#\(\#(location.line))")
then()
#sourceLocation()
})
"""#
}
}
public struct PrependHelloToShadowedMacro: ExpressionMacro {
public static func expansion(
of node: some FreestandingMacroExpansionSyntax,
in context: some MacroExpansionContext
) -> ExprSyntax {
#"""
"hello \(shadowed)"
"""#
}
}
public struct InvalidIfExprMacro: MemberMacro {
public static func expansion(
of node: AttributeSyntax,
providingMembersOf decl: some DeclGroupSyntax,
in context: some MacroExpansionContext
) throws -> [DeclSyntax] {
return ["""
func bar() {
let _ = (if .random() { 0 } else { 1 })
}
"""]
}
}
public struct InitWithProjectedValueWrapperMacro: PeerMacro {
public static func expansion(
of node: AttributeSyntax,
providingPeersOf declaration: some DeclSyntaxProtocol,
in context: some MacroExpansionContext
) throws -> [DeclSyntax] {
return [
"""
private var _value: Wrapper
var _$value: Wrapper {
@storageRestrictions(initializes: _value)
init {
self._value = newValue
}
get { _value }
}
"""
]
}
}
public struct RequiredDefaultInitMacro: ExtensionMacro {
public static func expansion(
of node: AttributeSyntax,
attachedTo decl: some DeclGroupSyntax,
providingExtensionsOf type: some TypeSyntaxProtocol,
conformingTo protocols: [TypeSyntax],
in context: some MacroExpansionContext
) throws -> [ExtensionDeclSyntax] {
if protocols.isEmpty {
return []
}
let decl: DeclSyntax =
"""
extension \(type.trimmed): DefaultInit {
}
"""
return [
decl.cast(ExtensionDeclSyntax.self)
]
}
}
extension RequiredDefaultInitMacro: MemberMacro {
public static func expansion(
of node: AttributeSyntax,
providingMembersOf declaration: some DeclGroupSyntax,
in context: some MacroExpansionContext
) throws -> [DeclSyntax] {
fatalError("old swift-syntax")
}
public static func expansion(
of node: AttributeSyntax,
providingMembersOf declaration: some DeclGroupSyntax,
conformingTo protocols: [TypeSyntax],
in context: some MacroExpansionContext
) throws -> [DeclSyntax] {
let initDecl: DeclSyntax
let funcDecl: DeclSyntax
if !declaration.is(ClassDeclSyntax.self) {
initDecl = "init() { }"
funcDecl = "func f() { }"
} else if !protocols.isEmpty {
initDecl = "required init() { }"
funcDecl = "func f() { }"
} else {
initDecl = "required init() { }"
funcDecl = "override func f() { }"
}
return [ initDecl, funcDecl ]
}
}
public struct SendableMacro: ExtensionMacro {
public static func expansion(
of node: AttributeSyntax,
attachedTo decl: some DeclGroupSyntax,
providingExtensionsOf type: some TypeSyntaxProtocol,
conformingTo protocols: [TypeSyntax],
in context: some MacroExpansionContext
) throws -> [ExtensionDeclSyntax] {
if protocols.isEmpty {
return []
}
let decl: DeclSyntax =
"""
extension \(type.trimmed): Sendable {
}
"""
return [
decl.cast(ExtensionDeclSyntax.self)
]
}
}
public struct GenerateStubMemberMacro: MemberMacro {
public static func expansion(
of node: AttributeSyntax,
providingMembersOf declaration: some DeclGroupSyntax,
conformingTo protocols: [TypeSyntax],
in context: some MacroExpansionContext
) throws -> [DeclSyntax] {
return ["#generateMemberStubs"]
}
}
public struct GenerateStubsFreestandingMacro: DeclarationMacro {
public static func expansion(
of node: some FreestandingMacroExpansionSyntax,
in context: some MacroExpansionContext
) throws -> [DeclSyntax] {
return ["#generateMember"]
}
}
public struct SingleMemberStubMacro: DeclarationMacro {
public static func expansion(
of node: some FreestandingMacroExpansionSyntax,
in context: some MacroExpansionContext
) throws -> [DeclSyntax] {
return ["static func member() {}"]
}
}
public struct DeclMacroWithControlFlow: DeclarationMacro {
public static func expansion(
of node: some FreestandingMacroExpansionSyntax,
in context: some MacroExpansionContext
) throws -> [DeclSyntax] {
return ["let _ = .random() ? try throwingFn() : 0"]
}
}
public struct GenerateStubsForProtocolRequirementsMacro: PeerMacro, ExtensionMacro {
public static func expansion(
of node: AttributeSyntax,
attachedTo declaration: some DeclGroupSyntax,
providingExtensionsOf type: some TypeSyntaxProtocol,
conformingTo protocols: [TypeSyntax],
in context: some MacroExpansionContext
) throws -> [ExtensionDeclSyntax] {
guard let proto = declaration.as(ProtocolDeclSyntax.self) else {
return []
}
let requirements =
proto.memberBlock.members.map { member in member.trimmed }
let requirementStubs = requirements
.map { req in
"\(req) { fatalError() }"
}
.joined(separator: "\n ")
let extensionDecl: DeclSyntax =
"""
extension \(proto.name) where Self: _TestStub {
\(raw: requirementStubs)
}
"""
return [extensionDecl.cast(ExtensionDeclSyntax.self)]
}
public static func expansion(
of node: AttributeSyntax,
providingPeersOf declaration: some DeclSyntaxProtocol,
in context: some MacroExpansionContext
) throws -> [DeclSyntax] {
guard let proto = declaration.as(ProtocolDeclSyntax.self) else {
return []
}
return [
"""
struct __\(proto.name): \(proto.name), _TestStub {
init() {}
}
"""
]
}
}
public struct FakeCodeItemMacro: DeclarationMacro, PeerMacro {
public static func expansion(
of node: some FreestandingMacroExpansionSyntax,
in context: some MacroExpansionContext
) throws -> [DeclSyntax] {
return ["guard true else { return }"]
}
public static func expansion(
of node: AttributeSyntax,
providingPeersOf declaration: some DeclSyntaxProtocol,
in context: some MacroExpansionContext
) throws -> [DeclSyntax] {
return ["if true { return }"]
}
}
public struct NotMacroStruct {
public static func expansion(
of macro: some FreestandingMacroExpansionSyntax,
in context: some MacroExpansionContext
) -> ExprSyntax {
fatalError()
}
}
extension FunctionParameterSyntax {
var parameterName: TokenSyntax? {
// If there were two names, the second is the parameter name.
if let secondName {
if secondName.text == "_" {
return nil
}
return secondName
}
if firstName.text == "_" {
return nil
}
return firstName
}
}
@_spi(ExperimentalLanguageFeature)
public struct RemoteBodyMacro: BodyMacro {
public static func expansion(
of node: AttributeSyntax,
providingBodyFor declaration: some DeclSyntaxProtocol & WithOptionalCodeBlockSyntax,
in context: some MacroExpansionContext
) throws -> [CodeBlockItemSyntax] {
// FIXME: Should be able to support (de-)initializers and accessors as
// well, but this is a lazy implementation.
guard let funcDecl = declaration.as(FunctionDeclSyntax.self) else {
return []
}
let funcBaseName = funcDecl.name.text
let paramNames = funcDecl.signature.parameterClause.parameters.map { param in
param.parameterName ?? TokenSyntax(.wildcard, presence: .present)
}
let passedArgs = DictionaryExprSyntax(
content: .elements(
DictionaryElementListSyntax {
for paramName in paramNames {
DictionaryElementSyntax(
key: ExprSyntax("\(literal: paramName.text)"),
value: DeclReferenceExprSyntax(baseName: paramName)
)
}
}
)
)
return [
"""
return try await remoteCall(function: \(literal: funcBaseName), arguments: \(passedArgs))
"""
]
}
}
@_spi(ExperimentalLanguageFeature)
public struct BodyMacroWithControlFlow: BodyMacro {
public static func expansion(
of node: AttributeSyntax,
providingBodyFor declaration: some DeclSyntaxProtocol & WithOptionalCodeBlockSyntax,
in context: some MacroExpansionContext
) throws -> [CodeBlockItemSyntax] {
[
"guard .random() else { return }",
"_ = try throwingFn()"
]
}
}
struct ThrowCancellationMacro: BodyMacro {
static func expansion(
of node: AttributeSyntax,
providingBodyFor declaration: some DeclSyntaxProtocol & WithOptionalCodeBlockSyntax,
in context: some MacroExpansionContext
) throws -> [CodeBlockItemSyntax] {
["throw CancellationError()"]
}
}
@_spi(ExperimentalLanguageFeature)
public struct TracedPreambleMacro: PreambleMacro {
public static func expansion(
of node: AttributeSyntax,
providingPreambleFor declaration: some DeclSyntaxProtocol & WithOptionalCodeBlockSyntax,
in context: some MacroExpansionContext
) throws -> [CodeBlockItemSyntax] {
// FIXME: Should be able to support (de-)initializers and accessors as
// well, but this is a lazy implementation.
guard let funcDecl = declaration.as(FunctionDeclSyntax.self) else {
return []
}
let funcBaseName = funcDecl.name
let paramNames = funcDecl.signature.parameterClause.parameters.map { param in
param.parameterName?.text ?? "_"
}
let passedArgs = paramNames.map { "\($0): \\(\($0))" }.joined(separator: ", ")
let entry: CodeBlockItemSyntax = """
log("Entering \(funcBaseName)(\(raw: passedArgs))")
"""
let argLabels = paramNames.map { "\($0):" }.joined()
let exit: CodeBlockItemSyntax = """
log("Exiting \(funcBaseName)(\(raw: argLabels))")
"""
return [
entry,
"""
defer {
\(exit)
}
""",
]
}
}
@_spi(ExperimentalLanguageFeature)
public struct LoggerMacro: PreambleMacro {
public static func expansion(
of node: AttributeSyntax,
providingPreambleFor declaration: some DeclSyntaxProtocol & WithOptionalCodeBlockSyntax,
in context: some MacroExpansionContext
) throws -> [CodeBlockItemSyntax] {
// FIXME: Should be able to support (de-)initializers and accessors as
// well, but this is a lazy implementation.
guard let funcDecl = declaration.as(FunctionDeclSyntax.self) else {
return []
}
let funcBaseName = funcDecl.name
let paramNames = funcDecl.signature.parameterClause.parameters.map { param in
param.parameterName?.text ?? "_"
}
let passedArgs = paramNames.map { "\($0): \\(\($0))" }.joined(separator: ", ")
let entry: CodeBlockItemSyntax = """
logger.log(entering: "\(funcBaseName)(\(raw: passedArgs))")
"""
let argLabels = paramNames.map { "\($0):" }.joined()
let exit: CodeBlockItemSyntax = """
logger.log(exiting: "\(funcBaseName)(\(raw: argLabels))")
"""
return [
"""
let logger = Logger()
""",
entry,
"""
defer {
\(exit)
}
""",
]
}
}
public struct AddMemberPeersMacro: MemberAttributeMacro {
public static func expansion(of node: AttributeSyntax, attachedTo declaration: some DeclGroupSyntax, providingAttributesFor member: some DeclSyntaxProtocol, in context: some MacroExpansionContext) throws -> [AttributeSyntax] {
["@_AddPeer"]
}
}
public struct _AddPeerMacro: PeerMacro {
public static func expansion<Decl: DeclSyntaxProtocol>(of node: AttributeSyntax, providingPeersOf declaration: Decl, in context: some MacroExpansionContext) throws -> [DeclSyntax] {
guard let name = declaration.as(VariableDeclSyntax.self)?.bindings.first?.pattern.as(IdentifierPatternSyntax.self)?.identifier.text else {
return []
}
return ["static let \(raw: name)_special = 41"]
}
}
public struct WrapperMacro: PeerMacro {
public static func expansion(
of node: SwiftSyntax.AttributeSyntax,
providingPeersOf declaration: some SwiftSyntax.DeclSyntaxProtocol,
in context: some SwiftSyntaxMacros.MacroExpansionContext
) throws -> [SwiftSyntax.DeclSyntax] {
[]
}
}
public struct AddDeinit: MemberMacro {
public static func expansion(
of node: AttributeSyntax,
providingMembersOf decl: some DeclGroupSyntax,
in context: some MacroExpansionContext
) throws -> [DeclSyntax] {
return [
"""
deinit {
print("deinit was called")
}
"""
]
}
}
public struct AddSubscript: MemberMacro {
public static func expansion(
of node: AttributeSyntax,
providingMembersOf decl: some DeclGroupSyntax,
in context: some MacroExpansionContext
) throws -> [DeclSyntax] {
return [
"""
subscript(unchecked index: Int) -> String {
return "\\(index)"
}
"""
]
}
}
public struct AllLexicalContextsMacro: DeclarationMacro {
public static func expansion(
of node: some FreestandingMacroExpansionSyntax,
in context: some MacroExpansionContext
) throws -> [DeclSyntax] {
context.lexicalContext.compactMap { $0.as(DeclSyntax.self)?.trimmed }
}
}
public struct AddGetterMacro: AccessorMacro {
public static func expansion(
of node: AttributeSyntax,
providingAccessorsOf declaration: some DeclSyntaxProtocol,
in context: some MacroExpansionContext
) throws -> [AccessorDeclSyntax] {
return ["get { 0 }"]
}
}
public struct HangingMacro: PeerMacro {
public static func expansion(
of node: AttributeSyntax,
providingPeersOf declaration: some DeclSyntaxProtocol,
in context: some MacroExpansionContext
) throws -> [DeclSyntax] {
guard let variableDecl = declaration.as(VariableDeclSyntax.self) else {
return []
}
guard let binding: PatternBindingSyntax = variableDecl.bindings.first else {
return []
}
guard let typeAnnotation = binding.typeAnnotation else {
return []
}
let mockProperty = try VariableDeclSyntax("var BadThing: \(typeAnnotation.type)") {
}
return [
DeclSyntax(mockProperty)
]
}
}
public struct PWithNonisolatedFuncMacro: ExtensionMacro {
public static var inferNonisolatedConformances: Bool { false }
public static func expansion(
of node: AttributeSyntax,
attachedTo decl: some DeclGroupSyntax,
providingExtensionsOf type: some TypeSyntaxProtocol,
conformingTo protocols: [TypeSyntax],
in context: some MacroExpansionContext
) throws -> [ExtensionDeclSyntax] {
if (protocols.isEmpty) {
return []
}
let decl: DeclSyntax =
"""
extension \(raw: type.trimmedDescription): P {
nonisolated static func requirement() { }
}
"""
return [
decl.cast(ExtensionDeclSyntax.self)
]
}
}
public struct NonisolatedPWithNonisolatedFuncMacro: ExtensionMacro {
public static func expansion(
of node: AttributeSyntax,
attachedTo decl: some DeclGroupSyntax,
providingExtensionsOf type: some TypeSyntaxProtocol,
conformingTo protocols: [TypeSyntax],
in context: some MacroExpansionContext
) throws -> [ExtensionDeclSyntax] {
if (protocols.isEmpty) {
return []
}
let decl: DeclSyntax =
"""
extension \(raw: type.trimmedDescription): P {
nonisolated static func requirement() { }
}
"""
return [
decl.cast(ExtensionDeclSyntax.self)
]
}
}
public struct BigEndianAccessorMacro: AccessorMacro {
public static func expansion(
of node: AttributeSyntax,
providingAccessorsOf declaration: some DeclSyntaxProtocol,
in context: some MacroExpansionContext
) throws -> [AccessorDeclSyntax] {
[
"""
get {
__value.bigEndian
}
"""
]
}
}