From aa6cc4a489176e22c564fa8420646ab557407557 Mon Sep 17 00:00:00 2001 From: Holly Borla Date: Tue, 27 Jun 2023 16:43:11 -0700 Subject: [PATCH 1/9] [Macros] Add attached extension macros. --- Sources/SwiftParser/Attributes.swift | 32 ++++++++++++++- Sources/SwiftParser/Types.swift | 4 ++ .../MacroExpansion.swift | 40 ++++++++++++++++++- Sources/SwiftSyntaxMacros/CMakeLists.txt | 1 + .../MacroProtocols/ExtensionMacro.swift | 34 ++++++++++++++++ Tests/SwiftParserTest/AttributeTests.swift | 16 ++++++++ 6 files changed, 125 insertions(+), 2 deletions(-) create mode 100644 Sources/SwiftSyntaxMacros/MacroProtocols/ExtensionMacro.swift diff --git a/Sources/SwiftParser/Attributes.swift b/Sources/SwiftParser/Attributes.swift index 8fb100b8849..e4d793797a9 100644 --- a/Sources/SwiftParser/Attributes.swift +++ b/Sources/SwiftParser/Attributes.swift @@ -332,14 +332,44 @@ extension Parser { ) ) case nil: + let isAttached = self.peek().isAttachedKeyword return parseAttribute(argumentMode: .customAttribute) { parser in - let arguments = parser.parseArgumentListElements(pattern: .none) + let arguments: [RawTupleExprElementSyntax] + if isAttached { + arguments = parser.parseAttachedArguments() + } else { + arguments = parser.parseArgumentListElements(pattern: .none) + } + return .argumentList(RawTupleExprElementListSyntax(elements: arguments, arena: parser.arena)) } } } } +extension Parser { + mutating func parseAttachedArguments() -> [RawTupleExprElementSyntax] { + let (unexpectedBeforeRole, role) = self.expect(.identifier, TokenSpec(.extension, remapping: .identifier), default: .identifier) + let roleTrailingComma = self.consume(if: .comma) + let roleElement = RawTupleExprElementSyntax( + label: nil, + colon: nil, + expression: RawExprSyntax( + RawIdentifierExprSyntax( + unexpectedBeforeRole, + identifier: role, + declNameArguments: nil, + arena: self.arena + ) + ), + trailingComma: roleTrailingComma, + arena: self.arena + ) + let additionalArgs = self.parseArgumentListElements(pattern: .none) + return [roleElement] + additionalArgs + } +} + extension Parser { mutating func parseDifferentiableAttribute() -> RawAttributeSyntax { let (unexpectedBeforeAtSign, atSign) = self.expect(.atSign) diff --git a/Sources/SwiftParser/Types.swift b/Sources/SwiftParser/Types.swift index e3453b61f22..346f1fa2723 100644 --- a/Sources/SwiftParser/Types.swift +++ b/Sources/SwiftParser/Types.swift @@ -1076,6 +1076,10 @@ extension Lexer.Lexeme { || self.rawTokenKind == .prefixOperator } + var isAttachedKeyword: Bool { + return self.rawTokenKind == .identifier && self.tokenText == "attached" + } + var isEllipsis: Bool { return self.isAnyOperator && self.tokenText == "..." } diff --git a/Sources/SwiftSyntaxMacroExpansion/MacroExpansion.swift b/Sources/SwiftSyntaxMacroExpansion/MacroExpansion.swift index 08a4671eace..668060475ce 100644 --- a/Sources/SwiftSyntaxMacroExpansion/MacroExpansion.swift +++ b/Sources/SwiftSyntaxMacroExpansion/MacroExpansion.swift @@ -22,6 +22,7 @@ public enum MacroRole { case peer case conformance case codeItem + case `extension` } extension MacroRole { @@ -35,6 +36,7 @@ extension MacroRole { case .peer: return "PeerMacro" case .conformance: return "ConformanceMacro" case .codeItem: return "CodeItemMacro" + case .extension: return "ExtensionMacro" } } } @@ -113,7 +115,7 @@ public func expandFreestandingMacro( let rewritten = try codeItemMacroDef.expansion(of: node, in: context) expandedSyntax = Syntax(CodeBlockItemListSyntax(rewritten)) - case (.accessor, _), (.memberAttribute, _), (.member, _), (.peer, _), (.conformance, _), (.expression, _), (.declaration, _), + case (.accessor, _), (.memberAttribute, _), (.member, _), (.peer, _), (.conformance, _), (.extension, _), (.expression, _), (.declaration, _), (.codeItem, _): throw MacroExpansionError.unmatchedMacroRole(definition, macroRole) } @@ -295,6 +297,42 @@ public func expandAttachedMacroWithoutCollapsing return "extension \(typeName) : \(protocolName) \(whereClause) {}" } + case (let attachedMacro as ExtensionMacro.Type, .extension): + guard let declGroup = declarationNode.asProtocol(DeclGroupSyntax.self) else { + // Compiler error: type mismatch. + throw MacroExpansionError.declarationNotDeclGroup + } + guard let identified = declarationNode.asProtocol(IdentifiedDeclSyntax.self) + else { + // Compiler error: type mismatch. + throw MacroExpansionError.declarationNotIdentified + } + + let type: TypeSyntax = "\(identified.identifier)" + + // Local function to expand a extension macro once we've opened up + // the existential. + func expandExtensionMacro( + _ node: some DeclGroupSyntax + ) throws -> [ExtensionDeclSyntax] { + return try attachedMacro.expansion( + of: attributeNode, + attachedTo: node, + providingExtensionsOf: type, + in: context + ) + } + + let extensions = try _openExistential( + declGroup, + do: expandExtensionMacro + ) + + // Form a buffer of peer declarations to return to the caller. + return extensions.map { + $0.formattedExpansion(definition.formatMode) + } + default: throw MacroExpansionError.unmatchedMacroRole(definition, macroRole) } diff --git a/Sources/SwiftSyntaxMacros/CMakeLists.txt b/Sources/SwiftSyntaxMacros/CMakeLists.txt index e3380d556ec..97cdca91ae4 100644 --- a/Sources/SwiftSyntaxMacros/CMakeLists.txt +++ b/Sources/SwiftSyntaxMacros/CMakeLists.txt @@ -13,6 +13,7 @@ add_swift_host_library(SwiftSyntaxMacros MacroProtocols/ConformanceMacro.swift MacroProtocols/DeclarationMacro.swift MacroProtocols/ExpressionMacro.swift + MacroProtocols/ExtensionMacro.swift MacroProtocols/FreestandingMacro.swift MacroProtocols/Macro.swift MacroProtocols/Macro+Format.swift diff --git a/Sources/SwiftSyntaxMacros/MacroProtocols/ExtensionMacro.swift b/Sources/SwiftSyntaxMacros/MacroProtocols/ExtensionMacro.swift new file mode 100644 index 00000000000..f2e985d17ac --- /dev/null +++ b/Sources/SwiftSyntaxMacros/MacroProtocols/ExtensionMacro.swift @@ -0,0 +1,34 @@ +//===----------------------------------------------------------------------===// +// +// 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 SwiftSyntax + +/// Describes a macro that can add conformances to the declaration it's +/// attached to. +public protocol ExtensionMacro: AttachedMacro { + /// Expand an attached conformance macro to produce a set of conformances. + /// + /// - Parameters: + /// - node: The custom attribute describing the attached macro. + /// - declaration: The declaration the macro attribute is attached to. + /// - context: The context in which to perform the macro expansion. + /// + /// - Returns: the set of `(type, where-clause?)` pairs that each provide the + /// protocol type to which the declared type conforms, along with + /// an optional where clause. + static func expansion( + of node: AttributeSyntax, + attachedTo decl: some DeclGroupSyntax, + providingExtensionsOf type: some TypeSyntaxProtocol, + in context: some MacroExpansionContext + ) throws -> [ExtensionDeclSyntax] +} diff --git a/Tests/SwiftParserTest/AttributeTests.swift b/Tests/SwiftParserTest/AttributeTests.swift index 6295daa112c..8e09c1c5464 100644 --- a/Tests/SwiftParserTest/AttributeTests.swift +++ b/Tests/SwiftParserTest/AttributeTests.swift @@ -629,4 +629,20 @@ final class AttributeTests: XCTestCase { """ ) } + + func testAttachedExtensionAttribute() { + assertParse( + """ + @attached(extension) + macro m() + """ + ) + + assertParse( + """ + @attached(extension, names: named(test)) + macro m() + """ + ) + } } From 5d77ef40295d03756f46f9f9eb38c48723fd410d Mon Sep 17 00:00:00 2001 From: Holly Borla Date: Tue, 27 Jun 2023 17:12:26 -0700 Subject: [PATCH 2/9] [MacroSystem] Expand extension macros. --- Sources/SwiftSyntaxMacros/MacroSystem.swift | 17 +++++++- .../MacroSystemTests.swift | 40 +++++++++++++++++++ 2 files changed, 56 insertions(+), 1 deletion(-) diff --git a/Sources/SwiftSyntaxMacros/MacroSystem.swift b/Sources/SwiftSyntaxMacros/MacroSystem.swift index 5d88efc2fb8..f1a01575faa 100644 --- a/Sources/SwiftSyntaxMacros/MacroSystem.swift +++ b/Sources/SwiftSyntaxMacros/MacroSystem.swift @@ -115,7 +115,8 @@ class MacroApplication: SyntaxRewriter { || macro is MemberMacro.Type || macro is AccessorMacro.Type || macro is MemberAttributeMacro.Type - || macro is ConformanceMacro.Type) + || macro is ConformanceMacro.Type + || macro is ExtensionMacro.Type) } if newAttributes.isEmpty { @@ -438,6 +439,20 @@ extension MacroApplication { } } + let extensionMacroAttrs = getMacroAttributes(attachedTo: decl.as(DeclSyntax.self)!, ofType: ExtensionMacro.Type.self) + let extendedTypeSyntax = TypeSyntax("\(extendedType.trimmed)") + for (attribute, extensionMacro) in extensionMacroAttrs { + do { + let newExtensions = try extensionMacro.expansion(of: attribute, + attachedTo: decl, + providingExtensionsOf: extendedTypeSyntax, + in: context) + extensions.append(contentsOf: newExtensions.map(DeclSyntax.init)) + } catch { + context.addDiagnostics(from: error, node: attribute) + } + } + return extensions } diff --git a/Tests/SwiftSyntaxMacrosTest/MacroSystemTests.swift b/Tests/SwiftSyntaxMacrosTest/MacroSystemTests.swift index 0a467e8f534..dda1cf940ca 100644 --- a/Tests/SwiftSyntaxMacrosTest/MacroSystemTests.swift +++ b/Tests/SwiftSyntaxMacrosTest/MacroSystemTests.swift @@ -682,6 +682,26 @@ public struct SendableConformanceMacro: ConformanceMacro { } } +public struct SendableExtensionMacro: ExtensionMacro { + public static func expansion( + of node: AttributeSyntax, + attachedTo: some DeclGroupSyntax, + providingExtensionsOf type: some TypeSyntaxProtocol, + in context: some MacroExpansionContext + ) throws -> [ExtensionDeclSyntax] { + let sendableExtension: DeclSyntax = + """ + extension \(type.trimmed): Sendable {} + """ + + guard let extensionDecl = sendableExtension.as(ExtensionDeclSyntax.self) else { + return [] + } + + return [extensionDecl] + } +} + public struct DeclsFromStringsMacroNoAttrs: DeclarationMacro { public static var propagateFreestandingMacroAttributes: Bool { false } public static var propagateFreestandingMacroModifiers: Bool { false } @@ -726,6 +746,7 @@ public let testMacros: [String: Macro.Type] = [ "customTypeWrapper": CustomTypeWrapperMacro.self, "unwrap": UnwrapMacro.self, "AddSendable": SendableConformanceMacro.self, + "AddSendableExtension": SendableExtensionMacro.self, ] final class MacroSystemTests: XCTestCase { @@ -1196,4 +1217,23 @@ final class MacroSystemTests: XCTestCase { indentationWidth: indentationWidth ) } + + func testExtensionExpansion() { + assertMacroExpansion( + """ + @AddSendableExtension + struct MyType { + } + """, + expandedSource: """ + + struct MyType { + } + extension MyType: Sendable { + } + """, + macros: testMacros, + indentationWidth: indentationWidth + ) + } } From 50bacd8838bcf192d49ec87834e846736606b86f Mon Sep 17 00:00:00 2001 From: Holly Borla Date: Tue, 27 Jun 2023 20:24:01 -0700 Subject: [PATCH 3/9] [Macros] Pass the extended type syntax for extension macros to `expandAttachedMacro`. --- .../CompilerPluginMessageHandler.swift | 5 +++-- .../Macros.swift | 7 ++++++- .../PluginMessages.swift | 3 ++- .../MacroExpansion.swift | 18 +++++++++++------- 4 files changed, 22 insertions(+), 11 deletions(-) diff --git a/Sources/SwiftCompilerPluginMessageHandling/CompilerPluginMessageHandler.swift b/Sources/SwiftCompilerPluginMessageHandling/CompilerPluginMessageHandler.swift index a59bf726fca..2b9e718b4f7 100644 --- a/Sources/SwiftCompilerPluginMessageHandling/CompilerPluginMessageHandler.swift +++ b/Sources/SwiftCompilerPluginMessageHandling/CompilerPluginMessageHandler.swift @@ -122,14 +122,15 @@ extension CompilerPluginMessageHandler { expandingSyntax: expandingSyntax ) - case .expandAttachedMacro(let macro, let macroRole, let discriminator, let attributeSyntax, let declSyntax, let parentDeclSyntax): + case .expandAttachedMacro(let macro, let macroRole, let discriminator, let attributeSyntax, let declSyntax, let parentDeclSyntax, let extendedTypeSyntax): try expandAttachedMacro( macro: macro, macroRole: macroRole, discriminator: discriminator, attributeSyntax: attributeSyntax, declSyntax: declSyntax, - parentDeclSyntax: parentDeclSyntax + parentDeclSyntax: parentDeclSyntax, + extendedTypeSyntax: extendedTypeSyntax ) case .loadPluginLibrary(let libraryPath, let moduleName): diff --git a/Sources/SwiftCompilerPluginMessageHandling/Macros.swift b/Sources/SwiftCompilerPluginMessageHandling/Macros.swift index 5923744bc96..0b6567f5942 100644 --- a/Sources/SwiftCompilerPluginMessageHandling/Macros.swift +++ b/Sources/SwiftCompilerPluginMessageHandling/Macros.swift @@ -86,7 +86,8 @@ extension CompilerPluginMessageHandler { discriminator: String, attributeSyntax: PluginMessage.Syntax, declSyntax: PluginMessage.Syntax, - parentDeclSyntax: PluginMessage.Syntax? + parentDeclSyntax: PluginMessage.Syntax?, + extendedTypeSyntax: PluginMessage.Syntax? ) throws { let sourceManager = SourceManager() let context = PluginMacroExpansionContext( @@ -100,6 +101,9 @@ extension CompilerPluginMessageHandler { ).cast(AttributeSyntax.self) let declarationNode = sourceManager.add(declSyntax).cast(DeclSyntax.self) let parentDeclNode = parentDeclSyntax.map { sourceManager.add($0).cast(DeclSyntax.self) } + let extendedType = extendedTypeSyntax.map { + sourceManager.add($0).cast(TypeSyntax.self) + } // TODO: Make this a 'String?' and remove non-'hasExpandMacroResult' branches. let expandedSources: [String]? @@ -115,6 +119,7 @@ extension CompilerPluginMessageHandler { attributeNode: attributeNode, declarationNode: declarationNode, parentDeclNode: parentDeclNode, + extendedType: extendedType, in: context ) if let expansions, hostCapability.hasExpandMacroResult { diff --git a/Sources/SwiftCompilerPluginMessageHandling/PluginMessages.swift b/Sources/SwiftCompilerPluginMessageHandling/PluginMessages.swift index 2075240488a..794f7cff37a 100644 --- a/Sources/SwiftCompilerPluginMessageHandling/PluginMessages.swift +++ b/Sources/SwiftCompilerPluginMessageHandling/PluginMessages.swift @@ -34,7 +34,8 @@ internal enum HostToPluginMessage: Codable { discriminator: String, attributeSyntax: PluginMessage.Syntax, declSyntax: PluginMessage.Syntax, - parentDeclSyntax: PluginMessage.Syntax? + parentDeclSyntax: PluginMessage.Syntax?, + extendedTypeSyntax: PluginMessage.Syntax? ) /// Optionally implemented message to load a dynamic link library. diff --git a/Sources/SwiftSyntaxMacroExpansion/MacroExpansion.swift b/Sources/SwiftSyntaxMacroExpansion/MacroExpansion.swift index 668060475ce..462a53d8db9 100644 --- a/Sources/SwiftSyntaxMacroExpansion/MacroExpansion.swift +++ b/Sources/SwiftSyntaxMacroExpansion/MacroExpansion.swift @@ -47,6 +47,7 @@ private enum MacroExpansionError: Error, CustomStringConvertible { case parentDeclGroupNil case declarationNotDeclGroup case declarationNotIdentified + case noExtendedTypeSyntax case noFreestandingMacroRoles(Macro.Type) var description: String { @@ -63,6 +64,9 @@ private enum MacroExpansionError: Error, CustomStringConvertible { case .declarationNotIdentified: return "declaration is not a 'Identified' syntax" + case .noExtendedTypeSyntax: + return "no extended type for extension macro" + case .noFreestandingMacroRoles(let type): return "macro implementation type '\(type)' does not conform to any freestanding macro protocol" @@ -180,6 +184,7 @@ public func expandAttachedMacroWithoutCollapsing attributeNode: AttributeSyntax, declarationNode: DeclSyntax, parentDeclNode: DeclSyntax?, + extendedType: TypeSyntax?, in context: Context ) -> [String]? { do { @@ -302,13 +307,10 @@ public func expandAttachedMacroWithoutCollapsing // Compiler error: type mismatch. throw MacroExpansionError.declarationNotDeclGroup } - guard let identified = declarationNode.asProtocol(IdentifiedDeclSyntax.self) - else { - // Compiler error: type mismatch. - throw MacroExpansionError.declarationNotIdentified - } - let type: TypeSyntax = "\(identified.identifier)" + guard let extendedType = extendedType else { + throw MacroExpansionError.noExtendedTypeSyntax + } // Local function to expand a extension macro once we've opened up // the existential. @@ -318,7 +320,7 @@ public func expandAttachedMacroWithoutCollapsing return try attachedMacro.expansion( of: attributeNode, attachedTo: node, - providingExtensionsOf: type, + providingExtensionsOf: extendedType, in: context ) } @@ -361,6 +363,7 @@ public func expandAttachedMacro( attributeNode: AttributeSyntax, declarationNode: DeclSyntax, parentDeclNode: DeclSyntax?, + extendedType: TypeSyntax?, in context: Context ) -> String? { let expandedSources = expandAttachedMacroWithoutCollapsing( @@ -369,6 +372,7 @@ public func expandAttachedMacro( attributeNode: attributeNode, declarationNode: declarationNode, parentDeclNode: parentDeclNode, + extendedType: extendedType, in: context ) return expandedSources.map { From d11f4c77966fd694251cea10bc1576eccef19863 Mon Sep 17 00:00:00 2001 From: Holly Borla Date: Wed, 28 Jun 2023 09:04:18 -0700 Subject: [PATCH 4/9] Fix formatting --- Sources/SwiftSyntaxMacros/MacroSystem.swift | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/Sources/SwiftSyntaxMacros/MacroSystem.swift b/Sources/SwiftSyntaxMacros/MacroSystem.swift index f1a01575faa..3f7ba87f8c7 100644 --- a/Sources/SwiftSyntaxMacros/MacroSystem.swift +++ b/Sources/SwiftSyntaxMacros/MacroSystem.swift @@ -443,10 +443,13 @@ extension MacroApplication { let extendedTypeSyntax = TypeSyntax("\(extendedType.trimmed)") for (attribute, extensionMacro) in extensionMacroAttrs { do { - let newExtensions = try extensionMacro.expansion(of: attribute, - attachedTo: decl, - providingExtensionsOf: extendedTypeSyntax, - in: context) + let newExtensions = try extensionMacro.expansion( + of: attribute, + attachedTo: decl, + providingExtensionsOf: extendedTypeSyntax, + in: context + ) + extensions.append(contentsOf: newExtensions.map(DeclSyntax.init)) } catch { context.addDiagnostics(from: error, node: attribute) From 1286637e16165ade7ed8d650453fb68fefa04e86 Mon Sep 17 00:00:00 2001 From: Holly Borla Date: Wed, 28 Jun 2023 13:55:43 -0700 Subject: [PATCH 5/9] [Macros] Bump the PluginMessage protocol version number for extension macros and add a macro plugin test for extension macros. --- .../Sources/ExamplePlugin/ExamplePlugin.swift | 1 + Examples/Sources/ExamplePlugin/Macros.swift | 20 +++++++++++++++++++ .../PluginMessages.swift | 2 +- lit_tests/compiler_plugin_basic.swift | 9 +++++++++ 4 files changed, 31 insertions(+), 1 deletion(-) diff --git a/Examples/Sources/ExamplePlugin/ExamplePlugin.swift b/Examples/Sources/ExamplePlugin/ExamplePlugin.swift index c3953f95930..9eab0b9b94b 100644 --- a/Examples/Sources/ExamplePlugin/ExamplePlugin.swift +++ b/Examples/Sources/ExamplePlugin/ExamplePlugin.swift @@ -10,6 +10,7 @@ struct ThePlugin: CompilerPlugin { PeerValueWithSuffixNameMacro.self, MemberDeprecatedMacro.self, EquatableConformanceMacro.self, + SendableExtensionMacro.self, DidSetPrintMacro.self, PrintAnyMacro.self, ] diff --git a/Examples/Sources/ExamplePlugin/Macros.swift b/Examples/Sources/ExamplePlugin/Macros.swift index 234d533d9e3..51f2179338b 100644 --- a/Examples/Sources/ExamplePlugin/Macros.swift +++ b/Examples/Sources/ExamplePlugin/Macros.swift @@ -86,6 +86,26 @@ struct EquatableConformanceMacro: ConformanceMacro { } } +public struct SendableExtensionMacro: ExtensionMacro { + public static func expansion( + of node: AttributeSyntax, + attachedTo: some DeclGroupSyntax, + providingExtensionsOf type: some TypeSyntaxProtocol, + in context: some MacroExpansionContext + ) throws -> [ExtensionDeclSyntax] { + let sendableExtension: DeclSyntax = + """ + extension \(type.trimmed): Sendable {} + """ + + guard let extensionDecl = sendableExtension.as(ExtensionDeclSyntax.self) else { + return [] + } + + return [extensionDecl] + } +} + /// Add 'didSet' printing the new value. struct DidSetPrintMacro: AccessorMacro { static func expansion( diff --git a/Sources/SwiftCompilerPluginMessageHandling/PluginMessages.swift b/Sources/SwiftCompilerPluginMessageHandling/PluginMessages.swift index 794f7cff37a..9595c04b923 100644 --- a/Sources/SwiftCompilerPluginMessageHandling/PluginMessages.swift +++ b/Sources/SwiftCompilerPluginMessageHandling/PluginMessages.swift @@ -77,7 +77,7 @@ internal enum PluginToHostMessage: Codable { } /*namespace*/ internal enum PluginMessage { - static var PROTOCOL_VERSION_NUMBER: Int { 5 } // Added 'expandMacroResult'. + static var PROTOCOL_VERSION_NUMBER: Int { 6 } // Added 'expandMacroResult'. struct HostCapability: Codable { var protocolVersion: Int diff --git a/lit_tests/compiler_plugin_basic.swift b/lit_tests/compiler_plugin_basic.swift index bb7c4200793..97f6239f3fe 100644 --- a/lit_tests/compiler_plugin_basic.swift +++ b/lit_tests/compiler_plugin_basic.swift @@ -33,12 +33,16 @@ macro MemberDeprecated() = #externalMacro(module: "ExamplePlugin", type: "Member @attached(conformance) macro Equatable() = #externalMacro(module: "ExamplePlugin", type: "EquatableConformanceMacro") +@attached(extension, conformances: Sendable) +macro AddSendbale() = #externalMacro(module: "ExamplePlugin", type: "SendableExtensionMacro") + @attached(accessor) macro DidSetPrint() = #externalMacro(module: "ExamplePlugin", type: "DidSetPrintMacro") @Metadata @MemberDeprecated @Equatable +@AddSendable @PeerWithSuffix struct MyStruct { @DidSetPrint @@ -81,6 +85,11 @@ struct MyStruct { // CHECK-NEXT: extension MyStruct : Equatable {} // CHECK-NEXT: ------------------------------ +// CHECK: @__swiftmacro_7TestApp8MyStruct9EquatablefMe_.swift +// CHECK-NEXT: ------------------------------ +// CHECK-NEXT: extension MyStruct: Sendable {} +// CHECK-NEXT: ------------------------------ + // CHECK: @__swiftmacro_7TestApp8MyStructV5value11DidSetPrintfMa_.swift // CHECK-NEXT: ------------------------------ // CHECK-NEXT: { From 3e2c782f0ef59dc28497932d4de0fb69d0413f23 Mon Sep 17 00:00:00 2001 From: Holly Borla Date: Wed, 28 Jun 2023 14:59:46 -0700 Subject: [PATCH 6/9] [Macros] Fix typos in comments and documentation for extension macros. --- .../SwiftSyntaxMacroExpansion/MacroExpansion.swift | 2 +- .../MacroProtocols/ExtensionMacro.swift | 13 +++++++------ 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/Sources/SwiftSyntaxMacroExpansion/MacroExpansion.swift b/Sources/SwiftSyntaxMacroExpansion/MacroExpansion.swift index 462a53d8db9..a0226454289 100644 --- a/Sources/SwiftSyntaxMacroExpansion/MacroExpansion.swift +++ b/Sources/SwiftSyntaxMacroExpansion/MacroExpansion.swift @@ -312,7 +312,7 @@ public func expandAttachedMacroWithoutCollapsing throw MacroExpansionError.noExtendedTypeSyntax } - // Local function to expand a extension macro once we've opened up + // Local function to expand an extension macro once we've opened up // the existential. func expandExtensionMacro( _ node: some DeclGroupSyntax diff --git a/Sources/SwiftSyntaxMacros/MacroProtocols/ExtensionMacro.swift b/Sources/SwiftSyntaxMacros/MacroProtocols/ExtensionMacro.swift index f2e985d17ac..8b3c8bd9b39 100644 --- a/Sources/SwiftSyntaxMacros/MacroProtocols/ExtensionMacro.swift +++ b/Sources/SwiftSyntaxMacros/MacroProtocols/ExtensionMacro.swift @@ -12,22 +12,23 @@ import SwiftSyntax -/// Describes a macro that can add conformances to the declaration it's +/// Describes a macro that can add extensions to the declaration it's /// attached to. public protocol ExtensionMacro: AttachedMacro { - /// Expand an attached conformance macro to produce a set of conformances. + /// Expand an attached extension macro to produce a set of extensions. /// /// - Parameters: /// - node: The custom attribute describing the attached macro. /// - declaration: The declaration the macro attribute is attached to. + /// - type: The type to provide extensions of. /// - context: The context in which to perform the macro expansion. /// - /// - Returns: the set of `(type, where-clause?)` pairs that each provide the - /// protocol type to which the declared type conforms, along with - /// an optional where clause. + /// - Returns: the set of extension declarations introduced by the macro, + /// which are always inserted at top-level scope. Each extension must extend + /// the `type` parameter. static func expansion( of node: AttributeSyntax, - attachedTo decl: some DeclGroupSyntax, + attachedTo declaration: some DeclGroupSyntax, providingExtensionsOf type: some TypeSyntaxProtocol, in context: some MacroExpansionContext ) throws -> [ExtensionDeclSyntax] From 9d1a869e89ecceb2bc468434cca2fc6ed2b20c13 Mon Sep 17 00:00:00 2001 From: Holly Borla Date: Wed, 28 Jun 2023 17:51:56 -0700 Subject: [PATCH 7/9] [NFC] Pass missing ExtensionMacros flag to compiler_plugin_basic.swift --- lit_tests/compiler_plugin_basic.swift | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lit_tests/compiler_plugin_basic.swift b/lit_tests/compiler_plugin_basic.swift index 97f6239f3fe..99a9834c0c7 100644 --- a/lit_tests/compiler_plugin_basic.swift +++ b/lit_tests/compiler_plugin_basic.swift @@ -4,6 +4,7 @@ // // RUN: %swift-frontend -typecheck -verify -swift-version 5 \ // RUN: -enable-experimental-feature CodeItemMacros \ +// RUN: -enable-experimental-feature ExtensionMacros \ // RUN: -dump-macro-expansions \ // RUN: -load-plugin-executable %examples_bin_path/ExamplePlugin#ExamplePlugin \ // RUN: -parse-as-library \ @@ -34,7 +35,7 @@ macro MemberDeprecated() = #externalMacro(module: "ExamplePlugin", type: "Member macro Equatable() = #externalMacro(module: "ExamplePlugin", type: "EquatableConformanceMacro") @attached(extension, conformances: Sendable) -macro AddSendbale() = #externalMacro(module: "ExamplePlugin", type: "SendableExtensionMacro") +macro AddSendable() = #externalMacro(module: "ExamplePlugin", type: "SendableExtensionMacro") @attached(accessor) macro DidSetPrint() = #externalMacro(module: "ExamplePlugin", type: "DidSetPrintMacro") From 0c1634b2e7d35195dce6c5007bc6fb3489ef3483 Mon Sep 17 00:00:00 2001 From: Holly Borla Date: Wed, 28 Jun 2023 22:55:46 -0700 Subject: [PATCH 8/9] [Macros] Add missing MacroRole case to PluginMessages.swift to align with PluginMessages.swift in the Swift repository. --- Sources/SwiftCompilerPluginMessageHandling/Macros.swift | 1 + Sources/SwiftCompilerPluginMessageHandling/PluginMessages.swift | 1 + 2 files changed, 2 insertions(+) diff --git a/Sources/SwiftCompilerPluginMessageHandling/Macros.swift b/Sources/SwiftCompilerPluginMessageHandling/Macros.swift index 0b6567f5942..f181d846c58 100644 --- a/Sources/SwiftCompilerPluginMessageHandling/Macros.swift +++ b/Sources/SwiftCompilerPluginMessageHandling/Macros.swift @@ -164,6 +164,7 @@ private extension MacroRole { case .peer: self = .peer case .conformance: self = .conformance case .codeItem: self = .codeItem + case .extension: self = .extension } } } diff --git a/Sources/SwiftCompilerPluginMessageHandling/PluginMessages.swift b/Sources/SwiftCompilerPluginMessageHandling/PluginMessages.swift index 9595c04b923..0b01d495604 100644 --- a/Sources/SwiftCompilerPluginMessageHandling/PluginMessages.swift +++ b/Sources/SwiftCompilerPluginMessageHandling/PluginMessages.swift @@ -108,6 +108,7 @@ internal enum PluginToHostMessage: Codable { case peer case conformance case codeItem + case `extension` } struct SourceLocation: Codable { From 0ac000dd15d03cad7e868e4ea35e0e9103bbac17 Mon Sep 17 00:00:00 2001 From: Holly Borla Date: Wed, 28 Jun 2023 23:14:46 -0700 Subject: [PATCH 9/9] [NFC] Remove extension macro test from compiler_plugin_basic.swift --- lit_tests/compiler_plugin_basic.swift | 9 --------- 1 file changed, 9 deletions(-) diff --git a/lit_tests/compiler_plugin_basic.swift b/lit_tests/compiler_plugin_basic.swift index 99a9834c0c7..4799c4c7dba 100644 --- a/lit_tests/compiler_plugin_basic.swift +++ b/lit_tests/compiler_plugin_basic.swift @@ -34,16 +34,12 @@ macro MemberDeprecated() = #externalMacro(module: "ExamplePlugin", type: "Member @attached(conformance) macro Equatable() = #externalMacro(module: "ExamplePlugin", type: "EquatableConformanceMacro") -@attached(extension, conformances: Sendable) -macro AddSendable() = #externalMacro(module: "ExamplePlugin", type: "SendableExtensionMacro") - @attached(accessor) macro DidSetPrint() = #externalMacro(module: "ExamplePlugin", type: "DidSetPrintMacro") @Metadata @MemberDeprecated @Equatable -@AddSendable @PeerWithSuffix struct MyStruct { @DidSetPrint @@ -86,11 +82,6 @@ struct MyStruct { // CHECK-NEXT: extension MyStruct : Equatable {} // CHECK-NEXT: ------------------------------ -// CHECK: @__swiftmacro_7TestApp8MyStruct9EquatablefMe_.swift -// CHECK-NEXT: ------------------------------ -// CHECK-NEXT: extension MyStruct: Sendable {} -// CHECK-NEXT: ------------------------------ - // CHECK: @__swiftmacro_7TestApp8MyStructV5value11DidSetPrintfMa_.swift // CHECK-NEXT: ------------------------------ // CHECK-NEXT: {