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: 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(_ 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: 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 ""."#) } 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")) 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")) 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(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 } """ ] } }