Skip to content

Commit b13d8f6

Browse files
authored
Merge pull request #66935 from rintaro/5.9-macros-plugins-collappsed-attached-result
[5.9][Macros] Attached macro expansions return single string
2 parents ee52608 + 5efe57d commit b13d8f6

File tree

8 files changed

+136
-123
lines changed

8 files changed

+136
-123
lines changed

include/swift/AST/CASTBridging.h

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

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

313313
/// Get a capability data set by \c Plugin_setCapability .
314314
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

@@ -707,10 +724,10 @@ func expandAttachedMacro(
707724
)
708725
let discriminator = String(decoding: discriminatorBuffer, as: UTF8.self)
709726

710-
let expandedSources: [String]?
727+
let expandedSource: String?
711728
switch MacroPluginKind(rawValue: macroKind)! {
712729
case .Executable:
713-
expandedSources = expandAttachedMacroIPC(
730+
expandedSource = expandAttachedMacroIPC(
714731
diagEnginePtr: diagEnginePtr,
715732
macroPtr: macroPtr,
716733
rawMacroRole: rawMacroRole,
@@ -722,7 +739,7 @@ func expandAttachedMacro(
722739
parentDeclSourceFilePtr: parentDeclSourceFilePtr,
723740
parentDeclNode: parentDeclNode)
724741
case .InProcess:
725-
expandedSources = expandAttachedMacroInProcess(
742+
expandedSource = expandAttachedMacroInProcess(
726743
diagEnginePtr: diagEnginePtr,
727744
macroPtr: macroPtr,
728745
rawMacroRole: rawMacroRole,
@@ -735,26 +752,11 @@ func expandAttachedMacro(
735752
parentDeclNode: parentDeclNode)
736753
}
737754

738-
guard let expandedSources = expandedSources else {
739-
return -1
740-
}
741-
742-
// Fixup the source.
743-
var expandedSource: String = collapse(expansions: expandedSources, for: MacroRole(rawMacroRole: rawMacroRole), attachedTo: declarationNode)
744-
745-
// Form the result buffer for our caller.
746-
expandedSource.withUTF8 { utf8 in
747-
let evaluatedResultPtr = UnsafeMutablePointer<UInt8>.allocate(capacity: utf8.count + 1)
748-
if let baseAddress = utf8.baseAddress {
749-
evaluatedResultPtr.initialize(from: baseAddress, count: utf8.count)
750-
}
751-
evaluatedResultPtr[utf8.count] = 0
752-
753-
expandedSourcePointer.pointee = UnsafePointer(evaluatedResultPtr)
754-
expandedSourceLength.pointee = utf8.count
755-
}
756-
757-
return 0
755+
return makeExpansionOutputResult(
756+
expandedSource: expandedSource,
757+
outputPointer: expandedSourcePointer,
758+
outputLength: expandedSourceLength
759+
)
758760
}
759761

760762
func expandAttachedMacroIPC(
@@ -768,7 +770,7 @@ func expandAttachedMacroIPC(
768770
attachedTo declarationNode: DeclSyntax,
769771
parentDeclSourceFilePtr: UnsafePointer<ExportedSourceFile>?,
770772
parentDeclNode: DeclSyntax?
771-
) -> [String]? {
773+
) -> String? {
772774
let macroName: String = customAttrNode.attributeName.description
773775
let macro = macroPtr.assumingMemoryBound(to: ExportedExecutableMacro.self).pointee
774776

@@ -810,10 +812,27 @@ func expandAttachedMacroIPC(
810812
declSyntax: declSyntax,
811813
parentDeclSyntax: parentDeclSyntax)
812814
do {
813-
let result = try macro.plugin.sendMessageAndWait(message)
814-
guard
815-
case .expandAttachedMacroResult(let expandedSources, let diagnostics) = result
816-
else {
815+
let expandedSource: String?
816+
let diagnostics: [PluginMessage.Diagnostic]
817+
switch try macro.plugin.sendMessageAndWait(message) {
818+
case .expandMacroResult(let _expandedSource, let _diagnostics):
819+
expandedSource = _expandedSource
820+
diagnostics = _diagnostics
821+
822+
// Handle legacy result message.
823+
case .expandAttachedMacroResult(let _expandedSources, let _diagnostics):
824+
if let _expandedSources = _expandedSources {
825+
expandedSource = SwiftSyntaxMacroExpansion.collapse(
826+
expansions: _expandedSources,
827+
for: MacroRole(rawMacroRole: rawMacroRole),
828+
attachedTo: declarationNode
829+
)
830+
} else {
831+
expandedSource = nil
832+
}
833+
diagnostics = _diagnostics
834+
break
835+
default:
817836
throw PluginError.invalidReponseKind
818837
}
819838

@@ -827,7 +846,7 @@ func expandAttachedMacroIPC(
827846
}
828847
diagEngine.emit(diagnostics, messageSuffix: " (from macro '\(macroName)')")
829848
}
830-
return expandedSources
849+
return expandedSource
831850

832851
} catch let error {
833852
let srcMgr = SourceManager(cxxDiagnosticEngine: diagEnginePtr)
@@ -861,7 +880,7 @@ func expandAttachedMacroInProcess(
861880
attachedTo declarationNode: DeclSyntax,
862881
parentDeclSourceFilePtr: UnsafePointer<ExportedSourceFile>?,
863882
parentDeclNode: DeclSyntax?
864-
) -> [String]? {
883+
) -> String? {
865884
// Get the macro.
866885
let macroPtr = macroPtr.bindMemory(to: ExportedMacro.self, capacity: 1)
867886
let macro = macroPtr.pointee.macro
@@ -925,54 +944,3 @@ fileprivate extension SyntaxProtocol {
925944
return formatted.trimmedDescription(matching: { $0.isWhitespace })
926945
}
927946
}
928-
929-
fileprivate func collapse<Node: SyntaxProtocol>(
930-
expansions: [String],
931-
for role: MacroRole,
932-
attachedTo declarationNode: Node
933-
) -> String {
934-
if expansions.isEmpty {
935-
return ""
936-
}
937-
938-
var expansions = expansions
939-
var separator: String = "\n\n"
940-
941-
if role == .accessor,
942-
let varDecl = declarationNode.as(VariableDeclSyntax.self),
943-
let binding = varDecl.bindings.first,
944-
binding.accessor == nil {
945-
let indentation = String(repeating: " ", count: 4)
946-
947-
expansions = expansions.map({ indent($0, with: indentation) })
948-
expansions[0] = "{\n" + expansions[0]
949-
expansions[expansions.count - 1] += "\n}"
950-
} else if role == .memberAttribute {
951-
separator = " "
952-
}
953-
954-
return expansions.joined(separator: separator)
955-
}
956-
957-
fileprivate func indent(_ source: String, with indentation: String) -> String {
958-
if source.isEmpty || indentation.isEmpty {
959-
return source
960-
}
961-
962-
var indented = ""
963-
var remaining = source[...]
964-
while let nextNewline = remaining.firstIndex(where: { $0.isNewline }) {
965-
if nextNewline != remaining.startIndex {
966-
indented += indentation
967-
}
968-
indented += remaining[...nextNewline]
969-
remaining = remaining[remaining.index(after: nextNewline)...]
970-
}
971-
972-
if !remaining.isEmpty {
973-
indented += indentation
974-
indented += remaining
975-
}
976-
977-
return indented
978-
}

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 the plugin capability stored in C++
175+
/// 'LoadedExecutablePlugin'. 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: 18 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,19 @@ internal enum PluginToHostMessage: Codable {
4951
capability: PluginMessage.PluginCapability
5052
)
5153

54+
/// Unified response for freestanding/attached macro expansion.
55+
case expandMacroResult(
56+
expandedSource: String?,
57+
diagnostics: [PluginMessage.Diagnostic]
58+
)
59+
60+
// @available(*, deprecated: "use expandMacroResult() instead")
5261
case expandFreestandingMacroResult(
5362
expandedSource: String?,
5463
diagnostics: [PluginMessage.Diagnostic]
5564
)
5665

66+
// @available(*, deprecated: "use expandMacroResult() instead")
5767
case expandAttachedMacroResult(
5868
expandedSources: [String]?,
5969
diagnostics: [PluginMessage.Diagnostic]
@@ -66,7 +76,11 @@ internal enum PluginToHostMessage: Codable {
6676
}
6777

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

7185
struct PluginCapability: Codable {
7286
var protocolVersion: Int
@@ -86,7 +100,7 @@ internal enum PluginToHostMessage: Codable {
86100

87101
enum MacroRole: String, Codable {
88102
case expression
89-
case freeStandingDeclaration
103+
case declaration
90104
case accessor
91105
case memberAttribute
92106
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)