From 3bf79d6941c47fa00b79e6c9c6cf21138d74ce48 Mon Sep 17 00:00:00 2001 From: Rintaro Ishizaki Date: Wed, 5 Jul 2023 16:34:33 -0700 Subject: [PATCH] [Macros] Remove MacroSystem and related files from 'SwiftSyntaxMacros' All the users of "MacroSystem" in `SwiftSyntaxMacros` have migrated to `SwiftSyntaxMacroExpansion`. Remove `MacroSystem.swift` and related files from `SwiftSyntaxMacros` module. rdar://111588860 (cherry picked from commit 9d33ca7c69afd840d6187a44a0cf57da27141b32) --- Package.swift | 10 +- .../BasicMacroExpansionContext.swift | 199 ------ Sources/SwiftSyntaxMacros/CMakeLists.txt | 4 - .../MacroExpansionContext.swift | 1 - Sources/SwiftSyntaxMacros/MacroSystem.swift | 596 ------------------ .../Syntax+MacroEvaluation.swift | 112 ---- .../MacroSystemTests.swift | 1 + 7 files changed, 5 insertions(+), 918 deletions(-) delete mode 100644 Sources/SwiftSyntaxMacros/BasicMacroExpansionContext.swift delete mode 100644 Sources/SwiftSyntaxMacros/MacroSystem.swift delete mode 100644 Sources/SwiftSyntaxMacros/Syntax+MacroEvaluation.swift rename Tests/{SwiftSyntaxMacrosTest => SwiftSyntaxMacroExpansionTest}/MacroSystemTests.swift (99%) diff --git a/Package.swift b/Package.swift index 2919836bfc1..ec665ed7a0d 100644 --- a/Package.swift +++ b/Package.swift @@ -189,11 +189,6 @@ let package = Package( exclude: ["CMakeLists.txt"] ), - .testTarget( - name: "SwiftSyntaxMacrosTest", - dependencies: ["_SwiftSyntaxTestSupport", "SwiftDiagnostics", "SwiftOperators", "SwiftParser", "SwiftSyntaxBuilder", "SwiftSyntaxMacros", "SwiftSyntaxMacrosTestSupport"] - ), - // MARK: SwiftSyntaxMacroExpansion .target( @@ -204,7 +199,10 @@ let package = Package( .testTarget( name: "SwiftSyntaxMacroExpansionTest", - dependencies: ["SwiftSyntax", "_SwiftSyntaxTestSupport", "SwiftSyntaxMacroExpansion", "SwiftSyntaxBuilder"] + dependencies: [ + "SwiftSyntax", "_SwiftSyntaxTestSupport", "SwiftDiagnostics", "SwiftOperators", "SwiftParser", "SwiftSyntaxBuilder", "SwiftSyntaxMacros", + "SwiftSyntaxMacroExpansion", "SwiftSyntaxMacrosTestSupport", + ] ), // MARK: SwiftSyntaxMacrosTestSupport diff --git a/Sources/SwiftSyntaxMacros/BasicMacroExpansionContext.swift b/Sources/SwiftSyntaxMacros/BasicMacroExpansionContext.swift deleted file mode 100644 index a0b44a1672a..00000000000 --- a/Sources/SwiftSyntaxMacros/BasicMacroExpansionContext.swift +++ /dev/null @@ -1,199 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// This source file is part of the Swift.org open source project -// -// Copyright (c) 2014 - 2023 Apple Inc. and the Swift project authors -// Licensed under Apache License v2.0 with Runtime Library Exception -// -// See https://swift.org/LICENSE.txt for license information -// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors -// -//===----------------------------------------------------------------------===// - -import SwiftDiagnostics -import SwiftSyntax - -/// An implementation of the `MacroExpansionContext` protocol that is -/// suitable for testing purposes. -@available(*, deprecated, message: "use SwiftSyntaxMacroExpansion.BasicMacroExpansionContext instead") -public class BasicMacroExpansionContext { - /// A single source file that is known to the macro expansion context. - @available(*, deprecated, message: "use SwiftSyntaxMacroExpansion.BasicMacroExpansionContext.KnownSourceFile instead") - public struct KnownSourceFile { - /// The name of the module in which this source file resides. - let moduleName: String - - /// The full path to the file. - let fullFilePath: String - - @available(*, deprecated, message: "use SwiftSyntaxMacroExpansion.BasicMacroExpansionContext.KnownSourceFile instead") - @_disfavoredOverload // deprecated. - public init(moduleName: String, fullFilePath: String) { - self.moduleName = moduleName - self.fullFilePath = fullFilePath - } - } - - /// Create a new macro evaluation context. - @available(*, deprecated, message: "use SwiftSyntaxMacroExpansion.BasicMacroExpansionContext instead") - @_disfavoredOverload // deprecated. - public init( - expansionDiscriminator: String = "__macro_local_", - sourceFiles: [SourceFileSyntax: KnownSourceFile] = [:] - ) { - self.expansionDiscriminator = expansionDiscriminator - self.sourceFiles = sourceFiles - } - - /// The set of diagnostics that were emitted as part of expanding the - /// macro. - public private(set) var diagnostics: [Diagnostic] = [] - - /// Mapping from the root source file syntax nodes to the known source-file - /// information about that source file. - private var sourceFiles: [SourceFileSyntax: KnownSourceFile] = [:] - - /// Mapping from intentionally-disconnected syntax node roots to the - /// absolute offsets that have within a given source file, which is used - /// to establish the link between a node that been intentionally disconnected - /// from a source file to hide information from the macro implementation. - private var disconnectedNodes: [Syntax: (SourceFileSyntax, Int)] = [:] - - /// The macro expansion discriminator, which is used to form unique names - /// when requested. - /// - /// The expansion discriminator is combined with the `uniqueNames` counters - /// to produce unique names. - private var expansionDiscriminator: String = "" - - /// Counter for each of the uniqued names. - /// - /// Used in conjunction with `expansionDiscriminator`. - private var uniqueNames: [String: Int] = [:] - -} - -extension BasicMacroExpansionContext { - /// Note that the given node that was at the given position in the provided - /// source file has been disconnected and is now a new root. - private func addDisconnected( - _ node: Node, - at offset: AbsolutePosition, - in sourceFile: SourceFileSyntax - ) { - disconnectedNodes[Syntax(node)] = (sourceFile, offset.utf8Offset) - } - - /// Detach the given node, and record where it came from. - public func detach(_ node: Node) -> Node { - let detached = node.detach() - - if let rootSourceFile = node.root.as(SourceFileSyntax.self) { - addDisconnected(detached, at: node.position, in: rootSourceFile) - } - - return detached - } -} - -extension String { - /// Retrieve the base name of a string that represents a path, removing the - /// directory. - fileprivate var basename: String { - guard let lastSlash = lastIndex(of: "/") else { - return self - } - - return String(self[index(after: lastSlash)...]) - } - -} -extension BasicMacroExpansionContext: MacroExpansionContext { - /// Generate a unique name for use in the macro. - public func makeUniqueName(_ providedName: String) -> TokenSyntax { - // If provided with an empty name, substitute in something. - let name = providedName.isEmpty ? "__local" : providedName - - // Grab a unique index value for this name. - let uniqueIndex = uniqueNames[name, default: 0] - uniqueNames[name] = uniqueIndex + 1 - - // Start with the expansion discriminator. - var resultString = expansionDiscriminator - - // Mangle the name - resultString += "\(name.count)\(name)" - - // Mangle the operator for unique macro names. - resultString += "fMu" - - // Mangle the index. - if uniqueIndex > 0 { - resultString += "\(uniqueIndex - 1)" - } - resultString += "_" - - return TokenSyntax(.identifier(resultString), presence: .present) - } - - /// Produce a diagnostic while expanding the macro. - public func diagnose(_ diagnostic: Diagnostic) { - diagnostics.append(diagnostic) - } - - public func location( - of node: Node, - at position: PositionInSyntaxNode, - filePathMode: SourceLocationFilePathMode - ) -> AbstractSourceLocation? { - // Dig out the root source file and figure out how we need to adjust the - // offset of the given syntax node to adjust for it. - let rootSourceFile: SourceFileSyntax - let offsetAdjustment: Int - if let directRootSourceFile = node.root.as(SourceFileSyntax.self) { - // The syntax node came from the source file itself. - rootSourceFile = directRootSourceFile - offsetAdjustment = 0 - } else if let (adjustedSourceFile, offset) = disconnectedNodes[Syntax(node)] { - // The syntax node came from a disconnected root, so adjust for that. - rootSourceFile = adjustedSourceFile - offsetAdjustment = offset - } else { - return nil - } - - guard let knownRoot = sourceFiles[rootSourceFile] else { - return nil - } - - // Determine the filename to use in the resulting location. - let fileName: String - switch filePathMode { - case .fileID: - fileName = "\(knownRoot.moduleName)/\(knownRoot.fullFilePath.basename)" - - case .filePath: - fileName = knownRoot.fullFilePath - } - - // Find the node's offset relative to its root. - let rawPosition: AbsolutePosition - switch position { - case .beforeLeadingTrivia: - rawPosition = node.position - - case .afterLeadingTrivia: - rawPosition = node.positionAfterSkippingLeadingTrivia - - case .beforeTrailingTrivia: - rawPosition = node.endPositionBeforeTrailingTrivia - - case .afterTrailingTrivia: - rawPosition = node.endPosition - } - - // Do the location lookup. - let converter = SourceLocationConverter(file: fileName, tree: rootSourceFile) - return AbstractSourceLocation(converter.location(for: rawPosition.advanced(by: offsetAdjustment))) - } -} diff --git a/Sources/SwiftSyntaxMacros/CMakeLists.txt b/Sources/SwiftSyntaxMacros/CMakeLists.txt index 97cdca91ae4..511cd7774ce 100644 --- a/Sources/SwiftSyntaxMacros/CMakeLists.txt +++ b/Sources/SwiftSyntaxMacros/CMakeLists.txt @@ -22,13 +22,9 @@ add_swift_host_library(SwiftSyntaxMacros MacroProtocols/PeerMacro.swift AbstractSourceLocation.swift - BasicMacroExpansionContext.swift MacroExpansionContext.swift - MacroSystem.swift - Syntax+MacroEvaluation.swift ) target_link_libraries(SwiftSyntaxMacros PUBLIC - SwiftParser SwiftSyntaxBuilder ) diff --git a/Sources/SwiftSyntaxMacros/MacroExpansionContext.swift b/Sources/SwiftSyntaxMacros/MacroExpansionContext.swift index fc60bae917c..6c061adc8f4 100644 --- a/Sources/SwiftSyntaxMacros/MacroExpansionContext.swift +++ b/Sources/SwiftSyntaxMacros/MacroExpansionContext.swift @@ -12,7 +12,6 @@ import SwiftDiagnostics import SwiftSyntax -import SwiftSyntaxBuilder /// Interface to extract information about the context in which a given /// macro is expanded. diff --git a/Sources/SwiftSyntaxMacros/MacroSystem.swift b/Sources/SwiftSyntaxMacros/MacroSystem.swift deleted file mode 100644 index 69d56c0bf9c..00000000000 --- a/Sources/SwiftSyntaxMacros/MacroSystem.swift +++ /dev/null @@ -1,596 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// This source file is part of the Swift.org open source project -// -// Copyright (c) 2014 - 2023 Apple Inc. and the Swift project authors -// Licensed under Apache License v2.0 with Runtime Library Exception -// -// See https://swift.org/LICENSE.txt for license information -// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors -// -//===----------------------------------------------------------------------===// - -import SwiftDiagnostics -import SwiftSyntax - -/// Describes the kinds of errors that can occur within a macro system. -enum MacroSystemError: Error { - /// Indicates that a macro with the given name has already been defined. - case alreadyDefined(new: Macro.Type, existing: Macro.Type) - - /// Indicates that an unknown macro was encountered during expansion. - case unknownMacro(name: String, node: Syntax) - - /// Indicates that a macro evaluated as an expression by the given node - /// is not an expression macro. - case requiresExpressionMacro(macro: Macro.Type, node: Syntax) - - /// Indicates that a macro evaluated as a code item by the given node - /// is not suitable for code items. - case requiresCodeItemMacro(macro: Macro.Type, node: Syntax) - - /// Indicates that a macro produced diagnostics during evaluation. The - /// diagnostics might not specifically include errors, but will be reported - /// nonetheless. - case evaluationDiagnostics(node: Syntax, diagnostics: [Diagnostic]) -} - -/// A system of known macros that can be expanded syntactically -struct MacroSystem { - var macros: [String: Macro.Type] = [:] - - /// Create an empty macro system. - init() {} - - /// Add a macro to the system. - /// - /// Throws an error if there is already a macro with this name. - mutating func add(_ macro: Macro.Type, name: String) throws { - if let knownMacro = macros[name] { - throw MacroSystemError.alreadyDefined(new: macro, existing: knownMacro) - } - - macros[name] = macro - } - - /// Look for a macro with the given name. - func lookup(_ macroName: String) -> Macro.Type? { - return macros[macroName] - } -} - -/// Syntax rewriter that evaluates any macros encountered along the way. -class MacroApplication: SyntaxRewriter { - let macroSystem: MacroSystem - var context: Context - var skipNodes: Set = [] - - /// A stack of member attribute macos to expand when iterating over a `MemberDeclListSyntax`. - var memberAttributeMacros: [([(AttributeSyntax, MemberAttributeMacro.Type)], DeclSyntax)] = [] - - init( - macroSystem: MacroSystem, - context: Context - ) { - self.macroSystem = macroSystem - self.context = context - super.init(viewMode: .sourceAccurate) - } - - override func visitAny(_ node: Syntax) -> Syntax? { - if skipNodes.contains(node) { - return nil - } - - if node.evaluatedMacroName != nil { - return node.evaluateMacro( - with: macroSystem, - context: context - ) - } - - if let declSyntax = node.as(DeclSyntax.self), - let attributedNode = node.asProtocol(WithAttributesSyntax.self), - let attributes = attributedNode.attributes - { - // Visit the node. - skipNodes.insert(node) - let visitedNode = self.visit(declSyntax).asProtocol(WithAttributesSyntax.self)! - skipNodes.remove(node) - - // Remove any attached attributes. - let newAttributes = attributes.filter { - guard case let .attribute(attribute) = $0 else { - return true - } - - guard let attributeName = attribute.attributeName.as(SimpleTypeIdentifierSyntax.self)?.name.text, - let macro = macroSystem.macros[attributeName] - else { - return true - } - - return - !(macro is PeerMacro.Type - || macro is MemberMacro.Type - || macro is AccessorMacro.Type - || macro is MemberAttributeMacro.Type - || macro is ExtensionMacro.Type) - } - - if newAttributes.isEmpty { - return Syntax(fromProtocol: visitedNode.with(\.attributes, nil)) - } - - return Syntax(fromProtocol: visitedNode.with(\.attributes, AttributeListSyntax(newAttributes))) - } - - return nil - } - - override func visit(_ node: CodeBlockItemListSyntax) -> CodeBlockItemListSyntax { - var newItems: [CodeBlockItemSyntax] = [] - for item in node { - if let expansion = item.item.asProtocol(FreestandingMacroExpansionSyntax.self), - let macro = macroSystem.macros[expansion.macro.text] - { - func _expand(expansion: some FreestandingMacroExpansionSyntax) throws { - if let macro = macro as? CodeItemMacro.Type { - let expandedItemList = try macro.expansion( - of: expansion, - in: context - ) - newItems.append(contentsOf: expandedItemList) - } else if let macro = macro as? DeclarationMacro.Type { - var expandedItemList = try macro.expansion( - of: expansion, - in: context - ) - if let declExpansion = expansion.as(MacroExpansionDeclSyntax.self) { - let attributes = macro.propagateFreestandingMacroAttributes ? declExpansion.attributes : nil - let modifiers = macro.propagateFreestandingMacroModifiers ? declExpansion.modifiers : nil - expandedItemList = expandedItemList.map { - $0.applying(attributes: attributes, modifiers: modifiers) - } - } - newItems.append( - contentsOf: expandedItemList.map { - CodeBlockItemSyntax(item: .decl($0)) - } - ) - } else if let macro = macro as? ExpressionMacro.Type { - let expandedExpr = try macro.expansion( - of: expansion, - in: context - ) - newItems.append(CodeBlockItemSyntax(item: .init(expandedExpr))) - } - } - do { - try _openExistential(expansion, do: _expand) - } catch { - context.addDiagnostics(from: error, node: node) - } - - continue - } - - // Recurse on the child node. - let newItem = visit(item.item) - newItems.append(item.with(\.item, newItem)) - - // Expand any peer declarations or conformances triggered by macros used - // as attributes. - if case let .decl(decl) = item.item { - let peers = expandPeers(of: decl) - newItems.append( - contentsOf: peers.map { - newDecl in CodeBlockItemSyntax(item: .decl(newDecl)) - } - ) - - if let declGroup = decl.asProtocol(DeclGroupSyntax.self) { - newItems.append( - contentsOf: expandExtensions(of: declGroup).map { - newDecl in CodeBlockItemSyntax(item: .decl(newDecl)) - } - ) - } - } - } - - return CodeBlockItemListSyntax(newItems) - } - - override func visit(_ node: MemberDeclListSyntax) -> MemberDeclListSyntax { - var newItems: [MemberDeclListItemSyntax] = [] - for item in node { - // Expand declaration macros, which produce zero or more declarations. - if let declExpansion = item.decl.as(MacroExpansionDeclSyntax.self), - let macro = macroSystem.macros[declExpansion.macro.text], - let freestandingMacro = macro as? DeclarationMacro.Type - { - do { - var expandedList = try freestandingMacro.expansion( - of: declExpansion, - in: context - ) - let attributes = freestandingMacro.propagateFreestandingMacroAttributes ? declExpansion.attributes : nil - let modifiers = freestandingMacro.propagateFreestandingMacroModifiers ? declExpansion.modifiers : nil - expandedList = expandedList.map { - $0.applying(attributes: attributes, modifiers: modifiers) - } - - newItems.append( - contentsOf: expandedList.map { decl in - return MemberDeclListItemSyntax(decl: decl) - } - ) - } catch { - context.addDiagnostics(from: error, node: declExpansion) - } - - continue - } - - // Expand member attribute members attached to the declaration context. - let attributedMember: MemberDeclListSyntax.Element - if let (macroAttributes, decl) = memberAttributeMacros.last { - attributedMember = expandAttributes( - for: macroAttributes, - attachedTo: decl, - annotating: item - ) - } else { - attributedMember = item - } - - // Recurse on the child node. - let newDecl = visit(attributedMember.decl) - newItems.append(attributedMember.with(\.decl, newDecl)) - - // Expand any peer declarations triggered by macros used as attributes. - let peers = expandPeers(of: item.decl) - newItems.append( - contentsOf: peers.map { - newDecl in MemberDeclListItemSyntax(decl: newDecl) - } - ) - } - - return .init(newItems) - } - - func visit( - declGroup: DeclType - ) -> DeclSyntax { - memberAttributeMacros.append( - ( - getMacroAttributes(attachedTo: DeclSyntax(declGroup), ofType: MemberAttributeMacro.Type.self), - DeclSyntax(declGroup) - ) - ) - defer { memberAttributeMacros.removeLast() } - - // Expand any attached member macros. - let expandedDeclGroup = expandMembers(of: declGroup) - - // Recurse into member decls. - let newMembers = visit(expandedDeclGroup.memberBlock) - - return DeclSyntax(expandedDeclGroup.with(\.memberBlock, newMembers)) - } - - override func visit(_ node: ActorDeclSyntax) -> DeclSyntax { - return visit(declGroup: node) - } - - override func visit(_ node: StructDeclSyntax) -> DeclSyntax { - return visit(declGroup: node) - } - - override func visit(_ node: EnumDeclSyntax) -> DeclSyntax { - return visit(declGroup: node) - } - - override func visit(_ node: ClassDeclSyntax) -> DeclSyntax { - return visit(declGroup: node) - } - - override func visit(_ node: ProtocolDeclSyntax) -> DeclSyntax { - return visit(declGroup: node) - } - - override func visit(_ node: ExtensionDeclSyntax) -> DeclSyntax { - return visit(declGroup: node) - } - - // Properties - override func visit(_ node: VariableDeclSyntax) -> DeclSyntax { - let visitedNode = super.visit(node) - guard let visitedVarDecl = visitedNode.as(VariableDeclSyntax.self) else { - return visitedNode - } - - guard let binding = visitedVarDecl.bindings.first, - visitedVarDecl.bindings.count == 1 - else { - return DeclSyntax(node) - } - - var accessors: [AccessorDeclSyntax] = [] - - let accessorMacroAttributes = getMacroAttributes(attachedTo: DeclSyntax(node), ofType: AccessorMacro.Type.self) - for (accessorAttr, accessorMacro) in accessorMacroAttributes { - do { - let newAccessors = try accessorMacro.expansion( - of: accessorAttr, - providingAccessorsOf: visitedNode, - in: context - ) - - accessors.append(contentsOf: newAccessors) - } catch { - // FIXME: record the error - } - } - - if accessors.isEmpty { - return visitedNode - } - - return DeclSyntax( - visitedVarDecl.with( - \.bindings, - visitedVarDecl.bindings.replacing( - childAt: 0, - with: binding.with( - \.accessor, - .accessors( - .init( - leftBrace: .leftBraceToken(leadingTrivia: .space), - accessors: .init(accessors), - rightBrace: .rightBraceToken(leadingTrivia: .newline) - ) - ) - ) - ) - ) - ) - } - - // Subscripts -} - -extension MacroApplication { - private func getMacroAttributes( - attachedTo decl: DeclSyntax, - ofType: MacroType.Type - ) -> [(AttributeSyntax, MacroType)] { - guard let attributedNode = decl.asProtocol(WithAttributesSyntax.self), - let attributes = attributedNode.attributes - else { - return [] - } - - return attributes.compactMap { - guard case let .attribute(attribute) = $0, - let attributeName = attribute.attributeName.as(SimpleTypeIdentifierSyntax.self)?.name.text, - let macro = macroSystem.macros[attributeName], - let macroType = macro as? MacroType - else { - return nil - } - - return (attribute, macroType) - } - } - - // If any of the custom attributes associated with the given declaration - // refer to "peer" declaration macros, expand them and return the resulting - // set of peer declarations. - private func expandPeers(of decl: DeclSyntax) -> [DeclSyntax] { - var peers: [DeclSyntax] = [] - let macroAttributes = getMacroAttributes(attachedTo: decl, ofType: PeerMacro.Type.self) - for (attribute, peerMacro) in macroAttributes { - do { - let newPeers = try peerMacro.expansion(of: attribute, providingPeersOf: decl, in: context) - peers.append(contentsOf: newPeers) - } catch { - context.addDiagnostics(from: error, node: attribute) - } - } - - return peers - } - - // If any of the custom attributes associated with the given declaration - // refer to conformance macros, expand them and return the resulting - // set of extension declarations. - private func expandExtensions(of decl: DeclGroupSyntax) -> [DeclSyntax] { - let extendedType: TypeSyntax - if let identified = decl.asProtocol(IdentifiedDeclSyntax.self) { - extendedType = "\(identified.identifier.trimmed)" - } else if let ext = decl.as(ExtensionDeclSyntax.self) { - extendedType = "\(ext.extendedType.trimmed)" - } else { - return [] - } - - var extensions: [DeclSyntax] = [] - - let extensionMacroAttrs = getMacroAttributes(attachedTo: decl.as(DeclSyntax.self)!, ofType: ExtensionMacro.Type.self) - for (attribute, extensionMacro) in extensionMacroAttrs { - do { - // FIXME: We need a way for unit tests of extension macros to - // specify protocols already stated in source (e.g. as arguments - // to `assertMacroExpansion`). - let newExtensions = try extensionMacro.expansion( - of: attribute, - attachedTo: decl, - providingExtensionsOf: extendedType, - conformingTo: [], - in: context - ) - - extensions.append(contentsOf: newExtensions.map(DeclSyntax.init)) - } catch { - context.addDiagnostics(from: error, node: attribute) - } - } - - return extensions - } - - /// Expands any attached custom attributes that refer to member declaration macros, - /// and returns result of adding those members to the given declaration. - private func expandMembers( - of decl: Decl - ) -> Decl { - var newMembers: [DeclSyntax] = [] - let macroAttributes = getMacroAttributes(attachedTo: DeclSyntax(decl), ofType: MemberMacro.Type.self) - for (attribute, memberMacro) in macroAttributes { - do { - try newMembers.append( - contentsOf: memberMacro.expansion( - of: attribute, - providingMembersOf: decl, - in: context - ) - ) - } catch { - context.addDiagnostics(from: error, node: attribute) - } - } - - // FIXME: Is there a better way to add N members to a decl? - return decl.with( - \.memberBlock, - newMembers.reduce(decl.memberBlock) { partialMembers, newMember in - partialMembers.addMember(.init(decl: newMember)) - } - ) - } - - private func expandMemberAttribute( - attribute: AttributeSyntax, - macro: MemberAttributeMacro.Type, - decl: DeclGroupSyntax, - member: DeclSyntax, - in context: MacroExpansionContext - ) throws -> [AttributeSyntax] { - #if false - _openExistential(decl) { d in - return try! macro.expansion( - of: attribute, - attachedTo: d, - annotating: member, - in: context - ) - } - #else - return [] - #endif - } - - private func expandAttributes( - for macroAttributes: [(AttributeSyntax, MemberAttributeMacro.Type)], - attachedTo decl: DeclSyntax, - annotating member: MemberDeclListSyntax.Element - ) -> MemberDeclListSyntax.Element { - guard let attributedDecl = member.decl.asProtocol(WithAttributesSyntax.self) else { - return member - } - - var attributes: [AttributeSyntax] = [] - for (attribute, attributeMacro) in macroAttributes { - do { - let typedDecl = decl.asProtocol(DeclGroupSyntax.self)! - - func expand(_ decl: Decl) throws -> [AttributeSyntax] { - return try attributeMacro.expansion( - of: attribute, - attachedTo: decl, - providingAttributesFor: member.decl, - in: context - ) - } - - attributes.append( - contentsOf: try _openExistential(typedDecl, do: expand) - ) - } catch { - context.addDiagnostics(from: error, node: attribute) - } - } - - let newAttributes = attributes.reduce(attributedDecl.attributes ?? .init([])) { - $0.appending(AttributeListSyntax.Element($1)) - } - - let newDecl = attributedDecl.with(\.attributes, newAttributes).as(DeclSyntax.self)! - return member.with(\.decl, newDecl) - } -} - -extension DeclSyntax { - /// Returns this node with `attributes` and `modifiers` prepended to the - /// node’s attributes and modifiers, respectively. If the node doesn’t contain - /// attributes or modifiers, `attributes` or `modifiers` are ignored and not - /// applied. - @_spi(MacroExpansion) - public func applying( - attributes: AttributeListSyntax?, - modifiers: ModifierListSyntax? - ) -> DeclSyntax { - func _combine(_ left: C, _ right: C?) -> C? { - guard let right = right else { return left } - var elems: [C.Element] = [] - elems.append(contentsOf: left) - elems.append(contentsOf: right) - return C(elems) - } - var node = self - if let attributes = attributes, - let withAttrs = node.asProtocol(WithAttributesSyntax.self) - { - node = withAttrs.with( - \.attributes, - _combine(attributes, withAttrs.attributes) - ).cast(DeclSyntax.self) - } - if let modifiers = modifiers, - let withModifiers = node.asProtocol(WithModifiersSyntax.self) - { - node = withModifiers.with( - \.modifiers, - _combine(modifiers, withModifiers.modifiers) - ).cast(DeclSyntax.self) - } - return node - } -} - -extension SyntaxProtocol { - /// Expand all uses of the given set of macros within this syntax - /// node. - @available(*, deprecated, message: "Use SwiftSyntaxMacroExpansion instead") - @_disfavoredOverload // deprecated. - public func expand( - macros: [String: Macro.Type], - in context: some MacroExpansionContext - ) -> Syntax { - // Build the macro system. - var system = MacroSystem() - for (macroName, macroType) in macros { - try! system.add(macroType, name: macroName) - } - - let applier = MacroApplication( - macroSystem: system, - context: context - ) - - return applier.rewrite(self) - } -} diff --git a/Sources/SwiftSyntaxMacros/Syntax+MacroEvaluation.swift b/Sources/SwiftSyntaxMacros/Syntax+MacroEvaluation.swift deleted file mode 100644 index 701405b92f3..00000000000 --- a/Sources/SwiftSyntaxMacros/Syntax+MacroEvaluation.swift +++ /dev/null @@ -1,112 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// This source file is part of the Swift.org open source project -// -// Copyright (c) 2014 - 2023 Apple Inc. and the Swift project authors -// Licensed under Apache License v2.0 with Runtime Library Exception -// -// See https://swift.org/LICENSE.txt for license information -// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors -// -//===----------------------------------------------------------------------===// - -import SwiftDiagnostics -import SwiftSyntax - -extension SyntaxProtocol { - /// Detach the current node and inform the macro expansion context, - /// if it needs to know. - fileprivate func detach(in context: MacroExpansionContext) -> Self { - if let basicContext = context as? BasicMacroExpansionContext { - return basicContext.detach(self) - } - - return self.detach() - } -} - -extension MacroExpansionExprSyntax { - /// Evaluate the given macro for this syntax node, producing the expanded - /// result and (possibly) some diagnostics. - func evaluateMacro( - _ macro: Macro.Type, - in context: Context - ) -> ExprSyntax { - guard let exprMacro = macro as? ExpressionMacro.Type else { - return ExprSyntax(self) - } - - // Handle the rewrite. - do { - return try exprMacro.expansion(of: detach(in: context), in: context) - } catch { - context.addDiagnostics(from: error, node: self) - return ExprSyntax(self) - } - } -} - -extension MacroExpansionDeclSyntax { - /// Evaluate the given macro for this syntax node, producing the expanded - /// result and (possibly) some diagnostics. - func evaluateMacro( - _ macro: Macro.Type, - in context: Context - ) -> Syntax { - // TODO: declaration/statement macros - - return Syntax(self) - } -} - -extension Syntax { - /// Determine the name of the macro that is evaluated by this syntax node, - /// if indeed it is a macro evaluation. For example, "#stringify(x)" has the - /// name "stringify". - var evaluatedMacroName: String? { - switch self.as(SyntaxEnum.self) { - case .macroExpansionDecl(let expansion): - return expansion.macro.text - - case .macroExpansionExpr(let expansion): - return expansion.macro.text - - default: - return nil - } - } - - /// Evaluate the given macro and return the resulting syntax tree along with - /// any errors along the way. - /// - /// This operation only makes sense when `evaluatedMacroName` produces a - /// non-nil value, indicating that this syntax node is a macro evaluation of - /// some kind. - func evaluateMacro( - with macroSystem: MacroSystem, - context: Context - ) -> Syntax { - // If this isn't a macro evaluation node, do nothing. - guard let macroName = evaluatedMacroName else { - return self - } - - // Look for a macro with the given name. Otherwise, fail. - guard let macro = macroSystem.macros[macroName] else { - return self - } - - switch self.as(SyntaxEnum.self) { - case .macroExpansionDecl(let expansion): - return expansion.evaluateMacro(macro, in: context) - - case .macroExpansionExpr(let expansion): - return Syntax( - expansion.evaluateMacro(macro, in: context) - ) - - default: - fatalError("switch is out-of-sync with evaluatedMacroName") - } - } -} diff --git a/Tests/SwiftSyntaxMacrosTest/MacroSystemTests.swift b/Tests/SwiftSyntaxMacroExpansionTest/MacroSystemTests.swift similarity index 99% rename from Tests/SwiftSyntaxMacrosTest/MacroSystemTests.swift rename to Tests/SwiftSyntaxMacroExpansionTest/MacroSystemTests.swift index 1f8613ea377..1622b472b7c 100644 --- a/Tests/SwiftSyntaxMacrosTest/MacroSystemTests.swift +++ b/Tests/SwiftSyntaxMacroExpansionTest/MacroSystemTests.swift @@ -16,6 +16,7 @@ import SwiftParser import SwiftSyntax import SwiftSyntaxBuilder import SwiftSyntaxMacros +import SwiftSyntaxMacroExpansion import SwiftSyntaxMacrosTestSupport import XCTest