diff --git a/lib/ASTGen/CMakeLists.txt b/lib/ASTGen/CMakeLists.txt index 63654fc3b102b..b42be7a88d7e1 100644 --- a/lib/ASTGen/CMakeLists.txt +++ b/lib/ASTGen/CMakeLists.txt @@ -32,5 +32,6 @@ add_pure_swift_host_library(swiftASTGen STATIC SwiftSyntax::SwiftParserDiagnostics SwiftSyntax::SwiftSyntax SwiftSyntax::SwiftSyntaxMacros + SwiftSyntax::SwiftSyntaxMacroExpansion swiftLLVMJSON ) diff --git a/lib/ASTGen/Package.swift b/lib/ASTGen/Package.swift index 683feef602934..f982dd84d26f6 100644 --- a/lib/ASTGen/Package.swift +++ b/lib/ASTGen/Package.swift @@ -37,10 +37,10 @@ let package = Package( .product(name: "SwiftOperators", package: "swift-syntax"), .product(name: "SwiftParser", package: "swift-syntax"), .product(name: "SwiftSyntaxMacros", package: "swift-syntax"), + .product(name: "SwiftSyntaxMacroExpansion", package: "swift-syntax"), "swiftLLVMJSON" ], path: "Sources/ASTGen", - exclude: ["CMakeLists.txt"], swiftSettings: swiftSetttings ), .target( diff --git a/lib/ASTGen/Sources/ASTGen/Macros.swift b/lib/ASTGen/Sources/ASTGen/Macros.swift index 125821804e35b..12fbaaf20c420 100644 --- a/lib/ASTGen/Sources/ASTGen/Macros.swift +++ b/lib/ASTGen/Sources/ASTGen/Macros.swift @@ -16,6 +16,7 @@ import SwiftOperators import SwiftParser import SwiftSyntax import SwiftSyntaxMacros +import SwiftSyntaxMacroExpansion extension SyntaxProtocol { func token(at position: AbsolutePosition) -> TokenSyntax? { @@ -56,14 +57,19 @@ enum MacroPluginKind: UInt8 { case Executable = 1 } -enum MacroRole: UInt8 { - case Expression = 0x01 - case FreestandingDeclaration = 0x02 - case Accessor = 0x04 - case MemberAttribute = 0x08 - case Member = 0x10 - case Peer = 0x20 - case Conformance = 0x40 +extension MacroRole { + init(rawMacroRole: UInt8) { + switch rawMacroRole { + case 0x01: self = .expression + case 0x02: self = .declaration + case 0x04: self = .accessor + case 0x08: self = .memberAttribute + case 0x10: self = .member + case 0x20: self = .peer + case 0x40: self = .conformance + default: fatalError("unknown macro role") + } + } } extension String { @@ -428,6 +434,12 @@ func expandFreestandingMacro( sourceFilePtr: sourceFilePtr, sourceLocationPtr: sourceLocationPtr, type: Syntax.self) else { return 1 } + guard let expansion = macroSyntax.asProtocol( + FreestandingMacroExpansionSyntax.self + ) else { + print("not on a macro expansion node: \(macroSyntax.debugDescription)") + return 1 + } let discriminatorBuffer = UnsafeBufferPointer( start: discriminatorText, count: discriminatorTextLength @@ -440,14 +452,14 @@ func expandFreestandingMacro( expandedSource = expandFreestandingMacroInProcess( macroPtr: macroPtr, diagEnginePtr: diagEnginePtr, - macroSyntax: macroSyntax, + expansionSyntax: expansion, sourceFilePtr: sourceFilePtr, discriminator: discriminator) case .Executable: expandedSource = expandFreestandingMacroIPC( macroPtr: macroPtr, diagEnginePtr: diagEnginePtr, - macroSyntax: macroSyntax, + expansionSyntax: expansion, sourceFilePtr: sourceFilePtr, discriminator: discriminator) } @@ -474,15 +486,15 @@ func expandFreestandingMacro( func expandFreestandingMacroIPC( macroPtr: UnsafeRawPointer, diagEnginePtr: UnsafeMutablePointer, - macroSyntax: Syntax, + expansionSyntax: FreestandingMacroExpansionSyntax, sourceFilePtr: UnsafePointer, discriminator: String ) -> String? { let macroName: String - if let exprSyntax = macroSyntax.as(MacroExpansionExprSyntax.self) { + if let exprSyntax = expansionSyntax.as(MacroExpansionExprSyntax.self) { macroName = exprSyntax.macro.text - } else if let declSyntax = macroSyntax.as(MacroExpansionDeclSyntax.self) { + } else if let declSyntax = expansionSyntax.as(MacroExpansionDeclSyntax.self) { macroName = declSyntax.macro.text } else { fatalError("unknown syntax") @@ -494,7 +506,7 @@ func expandFreestandingMacroIPC( let message = HostToPluginMessage.expandFreestandingMacro( macro: .init(moduleName: macro.moduleName, typeName: macro.typeName, name: macroName), discriminator: discriminator, - syntax: PluginMessage.Syntax(syntax: macroSyntax, in: sourceFilePtr)!) + syntax: PluginMessage.Syntax(syntax: Syntax(expansionSyntax), in: sourceFilePtr)!) do { let result = try macro.plugin.sendMessageAndWait(message) guard @@ -516,7 +528,7 @@ func expandFreestandingMacroIPC( srcMgr.insert(sourceFilePtr) srcMgr.diagnose( diagnostic: .init( - node: macroSyntax, + node: Syntax(expansionSyntax), // FIXME: This is probably a plugin communication error. // The error might not be relevant as the diagnostic message. message: ASTGenMacroDiagnostic.thrownError(error) @@ -530,10 +542,13 @@ func expandFreestandingMacroIPC( func expandFreestandingMacroInProcess( macroPtr: UnsafeRawPointer, diagEnginePtr: UnsafeMutablePointer, - macroSyntax: Syntax, + expansionSyntax: FreestandingMacroExpansionSyntax, sourceFilePtr: UnsafePointer, discriminator: String ) -> String? { + // Get the macro. + let macroPtr = macroPtr.bindMemory(to: ExportedMacro.self, capacity: 1) + let macro = macroPtr.pointee.macro // Create a source manager. This should probably persist and be given to us. let sourceManager = SourceManager(cxxDiagnosticEngine: diagEnginePtr) @@ -543,14 +558,7 @@ func expandFreestandingMacroInProcess( discriminator: discriminator ) - guard let parentExpansion = macroSyntax.asProtocol( - FreestandingMacroExpansionSyntax.self - ) else { - print("not on a macro expansion node: \(macroSyntax.debugDescription)") - return nil - } - - let macroName = parentExpansion.macro.text + let macroName = expansionSyntax.macro.text // Make sure we emit all of the diagnostics from the context. defer { @@ -565,73 +573,16 @@ func expandFreestandingMacroInProcess( context.diagnostics = [] } - let macroPtr = macroPtr.bindMemory(to: ExportedMacro.self, capacity: 1) - let macro = macroPtr.pointee.macro - - let evaluatedSyntax: Syntax - do { - switch macro { - // Handle expression macro. - case let exprMacro as ExpressionMacro.Type: - func expandExpressionMacro( - _ node: Node - ) throws -> ExprSyntax { - return try exprMacro.expansion( - of: sourceManager.detach( - node, - foldingWith: OperatorTable.standardOperators - ), - in: context - ) - } - - evaluatedSyntax = Syntax( - try _openExistential(parentExpansion, do: expandExpressionMacro) - ) - - // Handle declaration macro. The resulting decls are wrapped in a - // `CodeBlockItemListSyntax`. - case let declMacro as DeclarationMacro.Type: - func expandDeclarationMacro( - _ node: Node - ) throws -> [DeclSyntax] { - return try declMacro.expansion( - of: sourceManager.detach( - node, - foldingWith: OperatorTable.standardOperators - ), - in: context - ) - } - let decls = try _openExistential(parentExpansion, do: expandDeclarationMacro) - evaluatedSyntax = Syntax(CodeBlockItemListSyntax( - decls.map { CodeBlockItemSyntax(item: .decl($0)) })) - - case let codeItemMacro as CodeItemMacro.Type: - func expandCodeItemMacro( - _ node: Node - ) throws -> [CodeBlockItemSyntax] { - return try codeItemMacro.expansion( - of: sourceManager.detach( - node, - foldingWith: OperatorTable.standardOperators - ), - in: context - ) - } - let items = try _openExistential(parentExpansion, do: expandCodeItemMacro) - evaluatedSyntax = Syntax(CodeBlockItemListSyntax(items)) - - default: - print("not an expression macro or a declaration macro") - return nil - } - } catch { - context.addDiagnostics(from: error, node: macroSyntax) - return nil - } + let node = sourceManager.detach( + expansionSyntax, + foldingWith: OperatorTable.standardOperators + ) - return evaluatedSyntax.formattedExpansion(macro.formatMode) + return SwiftSyntaxMacroExpansion.expandFreestandingMacro( + definition: macro, + node: node, + in: context + ) } /// Retrieve a syntax node in the given source file, with the given type. @@ -768,7 +719,7 @@ func expandAttachedMacro( } // Fixup the source. - var expandedSource: String = collapse(expansions: expandedSources, for: MacroRole(rawValue: rawMacroRole)!, attachedTo: declarationNode) + var expandedSource: String = collapse(expansions: expandedSources, for: MacroRole(rawMacroRole: rawMacroRole), attachedTo: declarationNode) // Form the result buffer for our caller. expandedSource.withUTF8 { utf8 in @@ -802,15 +753,16 @@ func expandAttachedMacroIPC( // Map the macro role. let macroRole: PluginMessage.MacroRole - switch MacroRole(rawValue: rawMacroRole)! { - case .Accessor: macroRole = .accessor - case .Member: macroRole = .member - case .MemberAttribute: macroRole = .memberAttribute - case .Peer: macroRole = .peer - case .Conformance: macroRole = .conformance + switch MacroRole(rawMacroRole: rawMacroRole) { + case .accessor: macroRole = .accessor + case .member: macroRole = .member + case .memberAttribute: macroRole = .memberAttribute + case .peer: macroRole = .peer + case .conformance: macroRole = .conformance case - .Expression, - .FreestandingDeclaration: + .expression, + .declaration, + .codeItem: preconditionFailure("unhandled macro role for attached macro") } @@ -892,7 +844,6 @@ func expandAttachedMacroInProcess( // Get the macro. let macroPtr = macroPtr.bindMemory(to: ExportedMacro.self, capacity: 1) let macro = macroPtr.pointee.macro - let macroRole = MacroRole(rawValue: rawMacroRole) // Create a source manager covering the files we know about. let sourceManager = SourceManager(cxxDiagnosticEngine: diagEnginePtr) @@ -922,147 +873,21 @@ func expandAttachedMacroInProcess( context.diagnostics = [] } - var expandedSources: [String] - do { - switch (macro, macroRole) { - case (let attachedMacro as AccessorMacro.Type, .Accessor): - let accessors = try attachedMacro.expansion( - of: sourceManager.detach( - customAttrNode, - foldingWith: OperatorTable.standardOperators - ), - providingAccessorsOf: sourceManager.detach(declarationNode), - in: context - ) - - // Form a buffer of accessor declarations to return to the caller. - expandedSources = accessors.map { - $0.formattedExpansion(macro.formatMode) - } - - case (let attachedMacro as MemberAttributeMacro.Type, .MemberAttribute): - // Dig out the node for the parent declaration of the to-expand - // declaration. Only member attribute macros need this. - guard let parentDeclNode = parentDeclNode, - let parentDeclGroup = parentDeclNode.asProtocol(DeclGroupSyntax.self) - else { - return nil - } - - // Local function to expand a member atribute macro once we've opened up - // the existential. - func expandMemberAttributeMacro( - _ node: Node - ) throws -> [AttributeSyntax] { - return try attachedMacro.expansion( - of: sourceManager.detach( - customAttrNode, - foldingWith: OperatorTable.standardOperators - ), - attachedTo: sourceManager.detach(node), - providingAttributesFor: sourceManager.detach(declarationNode), - in: context - ) - } - - let attributes = try _openExistential( - parentDeclGroup, do: expandMemberAttributeMacro - ) - - // Form a buffer containing an attribute list to return to the caller. - expandedSources = attributes.map { - $0.formattedExpansion(macro.formatMode) - } - - case (let attachedMacro as MemberMacro.Type, .Member): - guard let declGroup = declarationNode.asProtocol(DeclGroupSyntax.self) - else { - return nil - } - - // Local function to expand a member macro once we've opened up - // the existential. - func expandMemberMacro( - _ node: Node - ) throws -> [DeclSyntax] { - return try attachedMacro.expansion( - of: sourceManager.detach( - customAttrNode, - foldingWith: OperatorTable.standardOperators - ), - providingMembersOf: sourceManager.detach(node), - in: context - ) - } - - let members = try _openExistential(declGroup, do: expandMemberMacro) - - // Form a buffer of member declarations to return to the caller. - expandedSources = members.map { - $0.formattedExpansion(macro.formatMode) - } - - case (let attachedMacro as PeerMacro.Type, .Peer): - let peers = try attachedMacro.expansion( - of: sourceManager.detach( - customAttrNode, - foldingWith: OperatorTable.standardOperators - ), - providingPeersOf: sourceManager.detach( - declarationNode, - foldingWith: OperatorTable.standardOperators - ), - in: context - ) - - // Form a buffer of peer declarations to return to the caller. - expandedSources = peers.map { - $0.formattedExpansion(macro.formatMode) - } - - case (let attachedMacro as ConformanceMacro.Type, .Conformance): - guard let declGroup = declarationNode.asProtocol(DeclGroupSyntax.self), - let identified = declarationNode.asProtocol(IdentifiedDeclSyntax.self) else { - return nil - } - - // Local function to expand a conformance macro once we've opened up - // the existential. - func expandConformanceMacro( - _ node: Node - ) throws -> [(TypeSyntax, GenericWhereClauseSyntax?)] { - return try attachedMacro.expansion( - of: sourceManager.detach( - customAttrNode, - foldingWith: OperatorTable.standardOperators - ), - providingConformancesOf: sourceManager.detach(node), - in: context - ) - } - - let conformances = try _openExistential( - declGroup, do: expandConformanceMacro - ) - - // Form a buffer of extension declarations to return to the caller. - expandedSources = conformances.map { typeSyntax, whereClause in - let typeName = identified.identifier.trimmedDescription - let protocolName = typeSyntax.trimmedDescription - let whereClause = whereClause?.trimmedDescription ?? "" - return "extension \(typeName) : \(protocolName) \(whereClause) {}" - } - - default: - print("\(macroPtr) does not conform to any known attached macro protocol") - return nil - } - } catch { - context.addDiagnostics(from: error, node: declarationNode) - return nil - } - - return expandedSources + let attributeNode = sourceManager.detach( + customAttrNode, + foldingWith: OperatorTable.standardOperators + ) + let declarationNode = sourceManager.detach(declarationNode) + let parentDeclNode = parentDeclNode.map { sourceManager.detach($0) } + + return SwiftSyntaxMacroExpansion.expandAttachedMacro( + definition: macro, + macroRole: MacroRole(rawMacroRole: rawMacroRole), + attributeNode: attributeNode, + declarationNode: declarationNode, + parentDeclNode: parentDeclNode, + in: context + ) } fileprivate extension SyntaxProtocol { @@ -1092,7 +917,7 @@ fileprivate func collapse( var expansions = expansions var separator: String = "\n\n" - if role == .Accessor, + if role == .accessor, let varDecl = declarationNode.as(VariableDeclSyntax.self), let binding = varDecl.bindings.first, binding.accessor == nil { @@ -1101,7 +926,7 @@ fileprivate func collapse( expansions = expansions.map({ indent($0, with: indentation) }) expansions[0] = "{\n" + expansions[0] expansions[expansions.count - 1] += "\n}" - } else if role == .MemberAttribute { + } else if role == .memberAttribute { separator = " " } diff --git a/lib/CMakeLists.txt b/lib/CMakeLists.txt index 612c03aed535d..55e8d7dc73ae2 100644 --- a/lib/CMakeLists.txt +++ b/lib/CMakeLists.txt @@ -28,6 +28,7 @@ if (SWIFT_SWIFT_PARSER) SwiftOperators SwiftSyntaxBuilder SwiftSyntaxMacros + SwiftSyntaxMacroExpansion SwiftCompilerPluginMessageHandling ) diff --git a/tools/swift-plugin-server/CMakeLists.txt b/tools/swift-plugin-server/CMakeLists.txt index aed4400e50e0c..09332309b10c9 100644 --- a/tools/swift-plugin-server/CMakeLists.txt +++ b/tools/swift-plugin-server/CMakeLists.txt @@ -18,6 +18,7 @@ if (SWIFT_SWIFT_PARSER) $ SWIFT_DEPENDENCIES SwiftSyntax::SwiftSyntaxMacros + SwiftSyntax::SwiftSyntaxMacroExpansion SwiftSyntax::SwiftCompilerPluginMessageHandling swiftLLVMJSON )