Skip to content

Commit 8fd9ccc

Browse files
committed
[Macros] Attached macro expasnions return single string
* Move collapse(expansions:for:attachedTo:) to SwiftSyntaxMacroExpansion * SwiftSyntaxMacroExpansion.expandAttachedMacro() now perform collapsing * SwiftSyntaxMacroExpansion.expandAttachedMacroWithoutCollapsing() to keep old behavior * IPC request 'getCapability' now sends the host protocol version * Unified IPC response 'macroExpansionResult' that returns single string for both 'expandFreestandingMacro' and 'expandAttachedMacro' * Compiler accepts old 'expandFreestandingMacroResult' and 'expandAttachedMacroResult' to keep compatibility * Plugins check the compiler's protcol version to see if it suppports 'macroExpansionResult', and fall back to old behavior if necessary
1 parent 253d8fb commit 8fd9ccc

File tree

8 files changed

+135
-123
lines changed

8 files changed

+135
-123
lines changed

include/swift/AST/CASTBridging.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -456,7 +456,7 @@ typedef const void *PluginCapabilityPtr;
456456

457457
/// Set a capability data to the plugin object. Since the data is just a opaque
458458
/// pointer, it's not used in AST at all.
459-
void Plugin_setCapability(PluginHandle handle, PluginCapabilityPtr data);
459+
void Plugin_setCapability(PluginHandle handle, PluginCapabilityPtr _Nullable data);
460460

461461
/// Get a capability data set by \c Plugin_setCapability .
462462
PluginCapabilityPtr _Nullable Plugin_getCapability(PluginHandle handle);

lib/ASTGen/Sources/ASTGen/Macros.swift

Lines changed: 70 additions & 102 deletions
Original file line numberDiff line numberDiff line change
@@ -407,6 +407,29 @@ func checkMacroDefinition(
407407
}
408408
}
409409

410+
// Make an expansion result for '@_cdecl' function caller.
411+
func makeExpansionOutputResult(
412+
expandedSource: String?,
413+
outputPointer: UnsafeMutablePointer<UnsafePointer<UInt8>?>,
414+
outputLength: UnsafeMutablePointer<Int>
415+
) -> Int {
416+
guard var expandedSource = expandedSource else {
417+
return -1
418+
}
419+
420+
// Form the result buffer for our caller.
421+
expandedSource.withUTF8 { utf8 in
422+
let evaluatedResultPtr = UnsafeMutablePointer<UInt8>.allocate(capacity: utf8.count + 1)
423+
if let baseAddress = utf8.baseAddress {
424+
evaluatedResultPtr.initialize(from: baseAddress, count: utf8.count)
425+
}
426+
evaluatedResultPtr[utf8.count] = 0
427+
428+
outputPointer.pointee = UnsafePointer(evaluatedResultPtr)
429+
outputLength.pointee = utf8.count
430+
}
431+
return 0
432+
}
410433

411434
@_cdecl("swift_ASTGen_expandFreestandingMacro")
412435
@usableFromInline
@@ -470,23 +493,11 @@ func expandFreestandingMacro(
470493
discriminator: discriminator)
471494
}
472495

473-
guard var expandedSource = expandedSource else {
474-
return -1
475-
}
476-
477-
// Form the result buffer for our caller.
478-
expandedSource.withUTF8 { utf8 in
479-
let evaluatedResultPtr = UnsafeMutablePointer<UInt8>.allocate(capacity: utf8.count + 1)
480-
if let baseAddress = utf8.baseAddress {
481-
evaluatedResultPtr.initialize(from: baseAddress, count: utf8.count)
482-
}
483-
evaluatedResultPtr[utf8.count] = 0
484-
485-
expandedSourcePointer.pointee = UnsafePointer(evaluatedResultPtr)
486-
expandedSourceLength.pointee = utf8.count
487-
}
488-
489-
return 0
496+
return makeExpansionOutputResult(
497+
expandedSource: expandedSource,
498+
outputPointer: expandedSourcePointer,
499+
outputLength: expandedSourceLength
500+
)
490501
}
491502

492503
func expandFreestandingMacroIPC(
@@ -516,7 +527,7 @@ func expandFreestandingMacroIPC(
516527
preconditionFailure("unhandled macro role for freestanding macro")
517528

518529
case .expression: pluginMacroRole = .expression
519-
case .declaration: pluginMacroRole = .freeStandingDeclaration
530+
case .declaration: pluginMacroRole = .declaration
520531
case .codeItem: pluginMacroRole = .codeItem
521532
}
522533

@@ -528,9 +539,15 @@ func expandFreestandingMacroIPC(
528539
syntax: PluginMessage.Syntax(syntax: Syntax(expansionSyntax), in: sourceFilePtr)!)
529540
do {
530541
let result = try macro.plugin.sendMessageAndWait(message)
531-
guard
532-
case .expandFreestandingMacroResult(let expandedSource, let diagnostics) = result
533-
else {
542+
let expandedSource: String?
543+
let diagnostics: [PluginMessage.Diagnostic]
544+
switch result {
545+
case
546+
.expandMacroResult(let _expandedSource, let _diagnostics),
547+
.expandFreestandingMacroResult(let _expandedSource, let _diagnostics):
548+
expandedSource = _expandedSource
549+
diagnostics = _diagnostics
550+
default:
534551
throw PluginError.invalidReponseKind
535552
}
536553

@@ -737,10 +754,10 @@ func expandAttachedMacro(
737754
)
738755
let discriminator = String(decoding: discriminatorBuffer, as: UTF8.self)
739756

740-
let expandedSources: [String]?
757+
let expandedSource: String?
741758
switch MacroPluginKind(rawValue: macroKind)! {
742759
case .Executable:
743-
expandedSources = expandAttachedMacroIPC(
760+
expandedSource = expandAttachedMacroIPC(
744761
diagEnginePtr: diagEnginePtr,
745762
macroPtr: macroPtr,
746763
rawMacroRole: rawMacroRole,
@@ -752,7 +769,7 @@ func expandAttachedMacro(
752769
parentDeclSourceFilePtr: parentDeclSourceFilePtr,
753770
parentDeclNode: parentDeclNode)
754771
case .InProcess:
755-
expandedSources = expandAttachedMacroInProcess(
772+
expandedSource = expandAttachedMacroInProcess(
756773
diagEnginePtr: diagEnginePtr,
757774
macroPtr: macroPtr,
758775
rawMacroRole: rawMacroRole,
@@ -765,26 +782,11 @@ func expandAttachedMacro(
765782
parentDeclNode: parentDeclNode)
766783
}
767784

768-
guard let expandedSources = expandedSources else {
769-
return -1
770-
}
771-
772-
// Fixup the source.
773-
var expandedSource: String = collapse(expansions: expandedSources, for: MacroRole(rawMacroRole: rawMacroRole), attachedTo: declarationNode)
774-
775-
// Form the result buffer for our caller.
776-
expandedSource.withUTF8 { utf8 in
777-
let evaluatedResultPtr = UnsafeMutablePointer<UInt8>.allocate(capacity: utf8.count + 1)
778-
if let baseAddress = utf8.baseAddress {
779-
evaluatedResultPtr.initialize(from: baseAddress, count: utf8.count)
780-
}
781-
evaluatedResultPtr[utf8.count] = 0
782-
783-
expandedSourcePointer.pointee = UnsafePointer(evaluatedResultPtr)
784-
expandedSourceLength.pointee = utf8.count
785-
}
786-
787-
return 0
785+
return makeExpansionOutputResult(
786+
expandedSource: expandedSource,
787+
outputPointer: expandedSourcePointer,
788+
outputLength: expandedSourceLength
789+
)
788790
}
789791

790792
func expandAttachedMacroIPC(
@@ -798,7 +800,7 @@ func expandAttachedMacroIPC(
798800
attachedTo declarationNode: DeclSyntax,
799801
parentDeclSourceFilePtr: UnsafePointer<ExportedSourceFile>?,
800802
parentDeclNode: DeclSyntax?
801-
) -> [String]? {
803+
) -> String? {
802804
let macroName: String = customAttrNode.attributeName.description
803805
let macro = macroPtr.assumingMemoryBound(to: ExportedExecutableMacro.self).pointee
804806

@@ -840,10 +842,27 @@ func expandAttachedMacroIPC(
840842
declSyntax: declSyntax,
841843
parentDeclSyntax: parentDeclSyntax)
842844
do {
843-
let result = try macro.plugin.sendMessageAndWait(message)
844-
guard
845-
case .expandAttachedMacroResult(let expandedSources, let diagnostics) = result
846-
else {
845+
let expandedSource: String?
846+
let diagnostics: [PluginMessage.Diagnostic]
847+
switch try macro.plugin.sendMessageAndWait(message) {
848+
case .expandMacroResult(let _expandedSource, let _diagnostics):
849+
expandedSource = _expandedSource
850+
diagnostics = _diagnostics
851+
852+
// Handle legacy result message.
853+
case .expandAttachedMacroResult(let _expandedSources, let _diagnostics):
854+
if let _expandedSources = _expandedSources {
855+
expandedSource = SwiftSyntaxMacroExpansion.collapse(
856+
expansions: _expandedSources,
857+
for: MacroRole(rawMacroRole: rawMacroRole),
858+
attachedTo: declarationNode
859+
)
860+
} else {
861+
expandedSource = nil
862+
}
863+
diagnostics = _diagnostics
864+
break
865+
default:
847866
throw PluginError.invalidReponseKind
848867
}
849868

@@ -857,7 +876,7 @@ func expandAttachedMacroIPC(
857876
}
858877
diagEngine.emit(diagnostics, messageSuffix: " (from macro '\(macroName)')")
859878
}
860-
return expandedSources
879+
return expandedSource
861880

862881
} catch let error {
863882
let srcMgr = SourceManager(cxxDiagnosticEngine: diagEnginePtr)
@@ -891,7 +910,7 @@ func expandAttachedMacroInProcess(
891910
attachedTo declarationNode: DeclSyntax,
892911
parentDeclSourceFilePtr: UnsafePointer<ExportedSourceFile>?,
893912
parentDeclNode: DeclSyntax?
894-
) -> [String]? {
913+
) -> String? {
895914
// Get the macro.
896915
let macroPtr = macroPtr.bindMemory(to: ExportedMacro.self, capacity: 1)
897916
let macro = macroPtr.pointee.macro
@@ -955,54 +974,3 @@ fileprivate extension SyntaxProtocol {
955974
return formatted.trimmedDescription(matching: { $0.isWhitespace })
956975
}
957976
}
958-
959-
fileprivate func collapse<Node: SyntaxProtocol>(
960-
expansions: [String],
961-
for role: MacroRole,
962-
attachedTo declarationNode: Node
963-
) -> String {
964-
if expansions.isEmpty {
965-
return ""
966-
}
967-
968-
var expansions = expansions
969-
var separator: String = "\n\n"
970-
971-
if role == .accessor,
972-
let varDecl = declarationNode.as(VariableDeclSyntax.self),
973-
let binding = varDecl.bindings.first,
974-
binding.accessor == nil {
975-
let indentation = String(repeating: " ", count: 4)
976-
977-
expansions = expansions.map({ indent($0, with: indentation) })
978-
expansions[0] = "{\n" + expansions[0]
979-
expansions[expansions.count - 1] += "\n}"
980-
} else if role == .memberAttribute {
981-
separator = " "
982-
}
983-
984-
return expansions.joined(separator: separator)
985-
}
986-
987-
fileprivate func indent(_ source: String, with indentation: String) -> String {
988-
if source.isEmpty || indentation.isEmpty {
989-
return source
990-
}
991-
992-
var indented = ""
993-
var remaining = source[...]
994-
while let nextNewline = remaining.firstIndex(where: { $0.isNewline }) {
995-
if nextNewline != remaining.startIndex {
996-
indented += indentation
997-
}
998-
indented += remaining[...nextNewline]
999-
remaining = remaining[remaining.index(after: nextNewline)...]
1000-
}
1001-
1002-
if !remaining.isEmpty {
1003-
indented += indentation
1004-
indented += remaining
1005-
}
1006-
1007-
return indented
1008-
}

lib/ASTGen/Sources/ASTGen/PluginHost.swift

Lines changed: 22 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -154,24 +154,38 @@ struct CompilerPlugin {
154154

155155
/// Initialize the plugin. This should be called inside lock.
156156
func initialize() throws {
157-
// Get capability.
158-
let response = try self.sendMessageAndWaitWithoutLock(.getCapability)
157+
// Send host capability and get plugin capability.
158+
let hostCapability = PluginMessage.HostCapability(
159+
protocolVersion: PluginMessage.PROTOCOL_VERSION_NUMBER
160+
)
161+
let request = HostToPluginMessage.getCapability(capability: hostCapability)
162+
let response = try self.sendMessageAndWaitWithoutLock(request)
159163
guard case .getCapabilityResult(let capability) = response else {
160164
throw PluginError.invalidReponseKind
161165
}
166+
167+
deinitializePluginCapabilityIfExist()
168+
162169
let ptr = UnsafeMutablePointer<Capability>.allocate(capacity: 1)
163170
ptr.initialize(to: .init(capability))
164171
Plugin_setCapability(opaqueHandle, UnsafeRawPointer(ptr))
165172
}
166173

174+
/// Deinitialize and unset athe plugin capability stored in C++
175+
/// 'LoadexExeucutablePlugin'. This should be called inside lock.
176+
func deinitializePluginCapabilityIfExist() {
177+
if let ptr = Plugin_getCapability(opaqueHandle) {
178+
let capabilityPtr = UnsafeMutableRawPointer(mutating: ptr)
179+
.assumingMemoryBound(to: PluginMessage.PluginCapability.self)
180+
capabilityPtr.deinitialize(count: 1)
181+
capabilityPtr.deallocate()
182+
Plugin_setCapability(opaqueHandle, nil)
183+
}
184+
}
185+
167186
func deinitialize() {
168187
self.withLock {
169-
if let ptr = Plugin_getCapability(opaqueHandle) {
170-
let capabilityPtr = UnsafeMutableRawPointer(mutating: ptr)
171-
.assumingMemoryBound(to: PluginMessage.PluginCapability.self)
172-
capabilityPtr.deinitialize(count: 1)
173-
capabilityPtr.deallocate()
174-
}
188+
deinitializePluginCapabilityIfExist()
175189
}
176190
}
177191

lib/ASTGen/Sources/ASTGen/PluginMessages.swift

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,10 @@
1414
// NOTE: Types in this file should be self-contained and should not depend on any non-stdlib types.
1515

1616
internal enum HostToPluginMessage: Codable {
17-
/// Get capability of this plugin.
18-
case getCapability
17+
/// Send capability of the host, and get capability of the plugin.
18+
case getCapability(
19+
capability: PluginMessage.HostCapability?
20+
)
1921

2022
/// Expand a '@freestanding' macro.
2123
case expandFreestandingMacro(
@@ -49,11 +51,18 @@ internal enum PluginToHostMessage: Codable {
4951
capability: PluginMessage.PluginCapability
5052
)
5153

54+
case expandMacroResult(
55+
expandedSource: String?,
56+
diagnostics: [PluginMessage.Diagnostic]
57+
)
58+
59+
// @available(*, deprecated: "use expandMacroResult() instead")
5260
case expandFreestandingMacroResult(
5361
expandedSource: String?,
5462
diagnostics: [PluginMessage.Diagnostic]
5563
)
5664

65+
// @available(*, deprecated: "use expandMacroResult() instead")
5766
case expandAttachedMacroResult(
5867
expandedSources: [String]?,
5968
diagnostics: [PluginMessage.Diagnostic]
@@ -66,7 +75,11 @@ internal enum PluginToHostMessage: Codable {
6675
}
6776

6877
/*namespace*/ internal enum PluginMessage {
69-
static var PROTOCOL_VERSION_NUMBER: Int { 4 } // Added 'loadPluginLibrary'.
78+
static var PROTOCOL_VERSION_NUMBER: Int { 5 } // Added 'expandMacroResult'.
79+
80+
struct HostCapability: Codable {
81+
var protocolVersion: Int
82+
}
7083

7184
struct PluginCapability: Codable {
7285
var protocolVersion: Int
@@ -86,7 +99,7 @@ internal enum PluginToHostMessage: Codable {
8699

87100
enum MacroRole: String, Codable {
88101
case expression
89-
case freeStandingDeclaration
102+
case declaration
90103
case accessor
91104
case memberAttribute
92105
case member

lib/Sema/TypeCheckMacros.cpp

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -292,6 +292,15 @@ initializeExecutablePlugin(ASTContext &ctx,
292292
if (!swift_ASTGen_initializePlugin(executablePlugin, &ctx.Diags)) {
293293
return nullptr;
294294
}
295+
296+
// Resend the compiler capability on reconnect.
297+
auto *callback = new std::function<void(void)>(
298+
[executablePlugin]() {
299+
(void)swift_ASTGen_initializePlugin(
300+
executablePlugin, /*diags=*/nullptr);
301+
});
302+
executablePlugin->addOnReconnect(callback);
303+
295304
executablePlugin->setCleanup([executablePlugin] {
296305
swift_ASTGen_deinitializePlugin(executablePlugin);
297306
});

test/Macros/macro_plugin_basic.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@
2121

2222
// RUN: %FileCheck -strict-whitespace %s < %t/macro-expansions.txt
2323

24-
// CHECK: ->(plugin:[[#PID:]]) {"getCapability":{}}
24+
// CHECK: ->(plugin:[[#PID:]]) {"getCapability":{"capability":{"protocolVersion":[[#PROTOCOL_VERSION:]]}}}
2525
// CHECK: <-(plugin:[[#PID]]) {"getCapabilityResult":{"capability":{"protocolVersion":1}}}
2626
// CHECK: ->(plugin:[[#PID]]) {"expandFreestandingMacro":{"discriminator":"$s{{.+}}","macro":{"moduleName":"TestPlugin","name":"testString","typeName":"TestStringMacro"},"macroRole":"expression","syntax":{"kind":"expression","location":{"column":19,"fileID":"MyApp/test.swift","fileName":"BUILD_DIR{{.+}}test.swift","line":5,"offset":301},"source":"#testString(123)"}}}
2727
// CHECK: <-(plugin:[[#PID]]) {"expandFreestandingMacroResult":{"diagnostics":[],"expandedSource":"\"123\"\n + \"foo \""}}

0 commit comments

Comments
 (0)