Skip to content

Commit c1c4edf

Browse files
committed
[Macros] Support user-defined macros as compiler plugins
Make `Macro` refine `_CompilerPlugin` protocol when `_CompilerPluginSupport` exists. (`_CompilerPluginSupport` may not exist when ASTGen or SwiftCompilerSources is built with host tools.) This enables macros to talk to the compiler without an ABI-stable SwiftSyntax.
1 parent 4a1ae7e commit c1c4edf

File tree

2 files changed

+75
-1
lines changed

2 files changed

+75
-1
lines changed

Sources/_SwiftSyntaxMacros/ExpressionMacro.swift

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,10 @@
1111
//===----------------------------------------------------------------------===//
1212

1313
import SwiftSyntax
14+
import SwiftParser
15+
#if canImport(_CompilerPluginSupport)
16+
import _CompilerPluginSupport
17+
#endif
1418

1519
/// Describes a macro that is explicitly expanded as an expression.
1620
public protocol ExpressionMacro: Macro {
@@ -20,3 +24,54 @@ public protocol ExpressionMacro: Macro {
2024
_ macro: MacroExpansionExprSyntax, in context: MacroEvaluationContext
2125
) -> MacroResult<ExprSyntax>
2226
}
27+
28+
#if canImport(_CompilerPluginSupport)
29+
extension ExpressionMacro {
30+
public static func _kind() -> _CompilerPluginKind {
31+
.expressionMacro
32+
}
33+
34+
public static func _rewrite(
35+
targetModuleName: UnsafePointer<UInt8>,
36+
targetModuleNameCount: Int,
37+
filePath: UnsafePointer<UInt8>,
38+
filePathCount: Int,
39+
sourceFileText: UnsafePointer<UInt8>,
40+
sourceFileTextCount: Int,
41+
localSourceText: UnsafePointer<UInt8>,
42+
localSourceTextCount: Int
43+
) -> (UnsafePointer<UInt8>?, count: Int) {
44+
let targetModuleNameBuffer = UnsafeBufferPointer(
45+
start: filePath, count: targetModuleNameCount)
46+
let targetModuleName = String(
47+
decoding: targetModuleNameBuffer, as: UTF8.self)
48+
let filePathBuffer = UnsafeBufferPointer(
49+
start: filePath, count: filePathCount)
50+
let filePath = String(decoding: filePathBuffer, as: UTF8.self)
51+
let sourceFileTextBuffer = UnsafeBufferPointer(
52+
start: sourceFileText, count: sourceFileTextCount)
53+
let sourceFileText = String(decoding: sourceFileTextBuffer, as: UTF8.self)
54+
// TODO: `SourceLocationConverter.init(file:source:)` should be able to
55+
// accept a buffer.
56+
let converter = SourceLocationConverter(
57+
file: filePath, source: sourceFileText)
58+
let context = MacroEvaluationContext(
59+
moduleName: targetModuleName, sourceLocationConverter: converter)
60+
let meeTextBuffer = UnsafeBufferPointer(
61+
start: localSourceText, count: localSourceTextCount)
62+
var parser = Parser(meeTextBuffer)
63+
let mee = try! MacroExpansionExprSyntax.parse(from: &parser)
64+
65+
// Evaluate the macro.
66+
let evalResult = apply(mee, in: context)
67+
68+
var resultString = "\(evalResult.rewritten)"
69+
return resultString.withUTF8 { buffer in
70+
let result = UnsafeMutableBufferPointer<UInt8>.allocate(
71+
capacity: buffer.count)
72+
_ = result.initialize(from: buffer)
73+
return (UnsafePointer(result.baseAddress), result.count)
74+
}
75+
}
76+
}
77+
#endif

Sources/_SwiftSyntaxMacros/Macro.swift

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,16 @@
99
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
1010
//
1111
//===----------------------------------------------------------------------===//
12+
1213
import SwiftSyntax
14+
#if canImport(_CompilerPluginSupport)
15+
import _CompilerPluginSupport
16+
#else
17+
public typealias _CompilerPlugin = Any
18+
#endif
1319

1420
/// Describes a macro.
15-
public protocol Macro {
21+
public protocol Macro: _CompilerPlugin {
1622
/// The name of this macro.
1723
static var name: String { get }
1824

@@ -39,3 +45,16 @@ extension Macro {
3945
/// Default, empty documentation string for macros.
4046
public static var documentation: String { "" }
4147
}
48+
49+
#if canImport(_CompilerPluginSupport)
50+
extension Macro {
51+
public static func _name() -> (UnsafePointer<UInt8>, count: Int) {
52+
var name = name
53+
return name.withUTF8 { buffer in
54+
let result = UnsafeMutablePointer<UInt8>.allocate(capacity: buffer.count)
55+
result.initialize(from: buffer.baseAddress!, count: buffer.count)
56+
return (UnsafePointer(result), count: buffer.count)
57+
}
58+
}
59+
}
60+
#endif

0 commit comments

Comments
 (0)