Skip to content

Commit 1930f86

Browse files
committed
[Macros] Emit diagnostics from plugins
Make `_rewrite` return an buffer containing diagnostics and emit them.
1 parent f63de71 commit 1930f86

File tree

10 files changed

+191
-30
lines changed

10 files changed

+191
-30
lines changed

include/swift/AST/CompilerPlugin.h

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,18 @@ class CompilerPlugin {
3737
ExpressionMacro,
3838
};
3939

40+
enum class DiagnosticSeverity: uint8_t {
41+
Note = 0,
42+
Warning = 1,
43+
Error = 2,
44+
};
45+
46+
struct Diagnostic {
47+
StringRef message;
48+
unsigned position;
49+
DiagnosticSeverity severity;
50+
};
51+
4052
private:
4153
// Must be modified together with `_CompilerPlugin` in
4254
// stdlib/toolchain/CompilerPluginSupport.swift.
@@ -92,10 +104,11 @@ class CompilerPlugin {
92104
CompilerPlugin(CompilerPlugin &&) = default;
93105

94106
/// Invoke the `_rewrite` method. The caller assumes ownership of the result
95-
/// string buffer.
107+
/// string buffer and diagnostic buffers.
96108
Optional<NullTerminatedStringRef> invokeRewrite(
97109
StringRef targetModuleName, StringRef filePath, StringRef sourceFileText,
98-
CharSourceRange range, ASTContext &ctx) const;
110+
CharSourceRange range, ASTContext &ctx,
111+
SmallVectorImpl<Diagnostic> &diagnostics) const;
99112

100113
/// Invoke the `_genericSignature` method. The caller assumes ownership of the
101114
/// result string buffer.

include/swift/AST/DiagnosticsSema.def

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6739,6 +6739,12 @@ ERROR(expected_macro_expansion_expr,PointsToFirstBadToken,
67396739
ERROR(macro_undefined,PointsToFirstBadToken,
67406740
"macro %0 is undefined; use `-load-plugin-library` to specify dynamic "
67416741
"libraries that contain this macro", (Identifier))
6742+
NOTE(macro_note, none,
6743+
"macro %0: %1", (Identifier, StringRef))
6744+
WARNING(macro_warning, none,
6745+
"macro %0: %1", (Identifier, StringRef))
6746+
ERROR(macro_error, none,
6747+
"macro %0: %1", (Identifier, StringRef))
67426748

67436749
//------------------------------------------------------------------------------
67446750
// MARK: Move Only Errors

lib/AST/CompilerPlugin.cpp

Lines changed: 46 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
#include "swift/AST/ASTMangler.h"
2020
#include "swift/AST/ASTContext.h"
2121
#include "swift/AST/DiagnosticsFrontend.h"
22+
#include "swift/Basic/Defer.h"
2223
#include "swift/Demangling/Demangle.h"
2324
#include "swift/Demangling/Demangler.h"
2425
#include "swift/Parse/Lexer.h"
@@ -231,18 +232,38 @@ CompilerPlugin::~CompilerPlugin() {
231232
}
232233

233234
namespace {
235+
// Corresponds to Swift type `(UnsafePointer<UInt8>, Int)`.
234236
struct CharBuffer {
235237
const char *data;
236-
ptrdiff_t size;
238+
ptrdiff_t length;
237239

238240
StringRef str() const {
239-
return StringRef(data, (size_t)size);
241+
return StringRef(data, (size_t)length);
240242
}
241243

242244
NullTerminatedStringRef cstr() const {
243-
return NullTerminatedStringRef(data, (size_t)size);
245+
return NullTerminatedStringRef(data, (size_t)length);
246+
}
247+
};
248+
249+
// Corresponds to Swift type `(UnsafePointer<UInt8>, Int, Int, UInt8)`.
250+
struct DiagnosticBuffer {
251+
const char *message;
252+
ptrdiff_t length;
253+
ptrdiff_t position;
254+
uint8_t severity;
255+
256+
StringRef str() const {
257+
return StringRef(message, (size_t)length);
244258
}
245259
};
260+
261+
// Corresponds to Swift type
262+
// `(UnsafePointer<(UnsafePointer<UInt8>, Int, Int)>, Int)`.
263+
struct DiagnosticArrayBuffer {
264+
const DiagnosticBuffer *data;
265+
ptrdiff_t count;
266+
};
246267
}
247268

248269
StringRef CompilerPlugin::invokeName() const {
@@ -268,13 +289,16 @@ CompilerPlugin::Kind CompilerPlugin::invokeKind() const {
268289
}
269290

270291
Optional<NullTerminatedStringRef>
271-
CompilerPlugin::invokeRewrite(StringRef targetModuleName,
272-
StringRef filePath,
273-
StringRef sourceFileText,
274-
CharSourceRange range,
275-
ASTContext &ctx) const {
292+
CompilerPlugin::invokeRewrite(
293+
StringRef targetModuleName, StringRef filePath, StringRef sourceFileText,
294+
CharSourceRange range, ASTContext &ctx,
295+
SmallVectorImpl<Diagnostic> &diagnostics) const {
296+
struct RewriteResult {
297+
CharBuffer code;
298+
DiagnosticArrayBuffer diagnostics;
299+
};
276300
#if __clang__
277-
using Method = SWIFT_CC CharBuffer(
301+
using Method = SWIFT_CC RewriteResult(
278302
const char *, ptrdiff_t,
279303
const char *, ptrdiff_t,
280304
const char *, ptrdiff_t,
@@ -287,9 +311,20 @@ CompilerPlugin::invokeRewrite(StringRef targetModuleName,
287311
sourceFileText.data(), (ptrdiff_t)sourceFileText.size(),
288312
range.str().data(), (ptrdiff_t)range.getByteLength(),
289313
metadata, metadata, witnessTable);
290-
if (!result.data)
314+
SWIFT_DEFER {
315+
free((void*)result.diagnostics.data);
316+
};
317+
if (!result.code.data)
291318
return None;
292-
return result.cstr();
319+
// Collect diagnostics.
320+
for (unsigned i = 0, n = result.diagnostics.count; i < n; ++i) {
321+
auto diag = result.diagnostics.data[i];
322+
StringRef message(diag.message, diag.length);
323+
diagnostics.push_back(
324+
{message, (unsigned)diag.position,
325+
(CompilerPlugin::DiagnosticSeverity)diag.severity});
326+
}
327+
return result.code.cstr();
293328
#else
294329
llvm_unreachable("Incompatible host compiler");
295330
#endif

lib/ASTGen/Package.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ let package = Package(
2424
.target(
2525
name: "swiftASTGen",
2626
dependencies: [
27+
.product(name: "SwiftDiagnostics", package: "swift-syntax"),
2728
.product(name: "SwiftSyntax", package: "swift-syntax"),
2829
.product(name: "SwiftParser", package: "swift-syntax"),
2930
.product(name: "_SwiftSyntaxMacros", package: "swift-syntax"),

lib/CompilerPluginSupport/CompilerPluginSupport.swift

Lines changed: 42 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,45 @@ public struct _CompilerPluginKind {
2525
}
2626
}
2727

28+
// Do not modify without modifying swift::CompilerPlugin::DiagnosticSeverity in
29+
// include/swift/AST/CompilerPlugin.h.
30+
@frozen
31+
public struct _DiagnosticSeverity {
32+
@usableFromInline
33+
enum RawValue: UInt8 {
34+
case note = 0
35+
case warning = 1
36+
case error = 2
37+
}
38+
@usableFromInline var rawValue: RawValue
39+
40+
public static var note: Self { Self(rawValue: .note) }
41+
public static var warning: Self { Self(rawValue: .warning) }
42+
public static var error: Self { Self(rawValue: .error) }
43+
}
44+
45+
public typealias _Diagnostic = (
46+
message: UnsafePointer<UInt8>,
47+
count: Int,
48+
position: Int,
49+
severity: _DiagnosticSeverity
50+
)
51+
52+
public func _makeDiagnostic(
53+
message: String, position: Int, severity: _DiagnosticSeverity
54+
) -> _Diagnostic {
55+
var message = message
56+
return message.withUTF8 { buffer in
57+
let resultBuffer = UnsafeMutableBufferPointer<UInt8>.allocate(
58+
capacity: buffer.count)
59+
_ = resultBuffer.initialize(from: buffer)
60+
return (
61+
message: UnsafePointer(resultBuffer.baseAddress!),
62+
count: resultBuffer.count, position: position, severity: severity
63+
)
64+
}
65+
}
66+
2867
/// A compiler plugin that can be loaded by the Swift compiler for code rewrite
2968
/// and custom compilation.
3069
public protocol _CompilerPlugin {
@@ -56,7 +95,9 @@ public protocol _CompilerPlugin {
5695
sourceFileTextCount: Int,
5796
localSourceText: UnsafePointer<UInt8>,
5897
localSourceTextCount: Int
59-
) -> (UnsafePointer<UInt8>?, count: Int)
98+
) -> (code: UnsafePointer<UInt8>?, codeLength: Int,
99+
diagnostics: UnsafePointer<_Diagnostic>?,
100+
diagnosticCount: Int)
60101

61102
/// Returns the generic signature of the plugin.
62103
///

lib/Sema/TypeCheckMacros.cpp

Lines changed: 26 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -394,13 +394,37 @@ Expr *swift::expandMacroExpr(
394394
auto *plugin = (CompilerPlugin *)macro->opaqueHandle;
395395
auto bufferID = sourceFile->getBufferID();
396396
auto sourceFileText = sourceMgr.getEntireTextForBuffer(*bufferID);
397+
SmallVector<CompilerPlugin::Diagnostic, 8> pluginDiags;
398+
SWIFT_DEFER {
399+
for (auto &diag : pluginDiags)
400+
free((void*)diag.message.data());
401+
};
397402
auto evaluated = plugin->invokeRewrite(
398403
/*targetModuleName*/ dc->getParentModule()->getName().str(),
399404
/*filePath*/ sourceFile->getFilename(),
400405
/*sourceFileText*/ sourceFileText,
401406
/*range*/ Lexer::getCharSourceRangeFromSourceRange(
402-
sourceMgr, expr->getSourceRange()),
403-
ctx);
407+
sourceMgr, expr->getSourceRange()), ctx, pluginDiags);
408+
for (auto &diag : pluginDiags) {
409+
auto loc = sourceMgr.getLocForOffset(*bufferID, diag.position);
410+
switch (diag.severity) {
411+
case CompilerPlugin::DiagnosticSeverity::Note:
412+
ctx.Diags.diagnose(loc, diag::macro_note,
413+
ctx.getIdentifier(plugin->getName()),
414+
diag.message);
415+
break;
416+
case CompilerPlugin::DiagnosticSeverity::Warning:
417+
ctx.Diags.diagnose(loc, diag::macro_warning,
418+
ctx.getIdentifier(plugin->getName()),
419+
diag.message);
420+
break;
421+
case CompilerPlugin::DiagnosticSeverity::Error:
422+
ctx.Diags.diagnose(loc, diag::macro_error,
423+
ctx.getIdentifier(plugin->getName()),
424+
diag.message);
425+
break;
426+
}
427+
}
404428
if (evaluated)
405429
evaluatedSource = *evaluated;
406430
else

test/Macros/Inputs/macro_definition.swift

Lines changed: 37 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -59,13 +59,15 @@ struct StringifyMacro: _CompilerPlugin {
5959
sourceFileTextCount: Int,
6060
localSourceText: UnsafePointer<UInt8>,
6161
localSourceTextCount: Int
62-
) -> (UnsafePointer<UInt8>?, count: Int) {
62+
) -> (code: UnsafePointer<UInt8>?, codeLength: Int,
63+
diagnostics: UnsafePointer<_Diagnostic>?,
64+
diagnosticCount: Int) {
6365
let meeTextBuffer = UnsafeBufferPointer(
6466
start: localSourceText, count: localSourceTextCount)
6567
let meeText = String(decoding: meeTextBuffer, as: UTF8.self)
6668
let prefix = "#customStringify("
6769
guard meeText.starts(with: prefix), meeText.last == ")" else {
68-
return (nil, 0)
70+
return (nil, 0, nil, 0)
6971
}
7072
let expr = meeText.dropFirst(prefix.count).dropLast()
7173
// Or use regex...
@@ -85,14 +87,34 @@ struct StringifyMacro: _CompilerPlugin {
8587
// return (nil, count: 0)
8688
// }
8789

88-
var resultString = "(\(expr), #\"\(expr)\"#)"
89-
return resultString.withUTF8 { buffer in
90+
let exprText = "\(expr)"
91+
var resultString = """
92+
(\(exprText),
93+
#\"\"\"
94+
\(exprText)
95+
\"\"\"#)
96+
"""
97+
let (resultBuffer, resultLength) = resultString.withUTF8 { buffer in
9098
let result = UnsafeMutableBufferPointer<UInt8>.allocate(
91-
capacity: buffer.count + 1)
99+
capacity: buffer.count + 1)
92100
_ = result.initialize(from: buffer)
93101
result[buffer.count] = 0
94102
return (UnsafePointer(result.baseAddress), buffer.count)
95103
}
104+
105+
let localSourceOffset = sourceFileText.distance(to: localSourceText)
106+
let diags = UnsafeMutablePointer<_Diagnostic>.allocate(capacity: 2)
107+
diags[0] = _makeDiagnostic(
108+
message: "test note", position: localSourceOffset, severity: .note)
109+
diags[1] = _makeDiagnostic(
110+
message: "test warning",
111+
position: localSourceOffset + localSourceTextCount - 1,
112+
severity: .warning)
113+
114+
return (
115+
code: resultBuffer, codeLength: resultLength,
116+
diagnostics: UnsafePointer?(diags), diagnosticCount: 2
117+
)
96118
}
97119
}
98120

@@ -160,13 +182,15 @@ struct ColorLiteralMacro: _CompilerPlugin {
160182
sourceFileTextCount: Int,
161183
localSourceText: UnsafePointer<UInt8>,
162184
localSourceTextCount: Int
163-
) -> (UnsafePointer<UInt8>?, count: Int) {
185+
) -> (code: UnsafePointer<UInt8>?, codeLength: Int,
186+
diagnostics: UnsafePointer<_Diagnostic>?,
187+
diagnosticCount: Int) {
164188
let meeTextBuffer = UnsafeBufferPointer(
165189
start: localSourceText, count: localSourceTextCount)
166190
let meeText = String(decoding: meeTextBuffer, as: UTF8.self)
167191
let prefix = "#customColorLiteral(red:"
168192
guard meeText.starts(with: prefix), meeText.last == ")" else {
169-
return (nil, 0)
193+
return (nil, 0, nil, 0)
170194
}
171195
let expr = meeText.dropFirst(prefix.count).dropLast()
172196
var resultString = ".init(_colorLiteralRed:\(expr))"
@@ -175,7 +199,7 @@ struct ColorLiteralMacro: _CompilerPlugin {
175199
capacity: buffer.count + 1)
176200
_ = result.initialize(from: buffer)
177201
result[buffer.count] = 0
178-
return (UnsafePointer(result.baseAddress), buffer.count)
202+
return (UnsafePointer(result.baseAddress), buffer.count, nil, 0)
179203
}
180204
}
181205
}
@@ -239,13 +263,15 @@ struct HSVColorLiteralMacro: _CompilerPlugin {
239263
sourceFileTextCount: Int,
240264
localSourceText: UnsafePointer<UInt8>,
241265
localSourceTextCount: Int
242-
) -> (UnsafePointer<UInt8>?, count: Int) {
266+
) -> (code: UnsafePointer<UInt8>?, codeLength: Int,
267+
diagnostics: UnsafePointer<_Diagnostic>?,
268+
diagnosticCount: Int) {
243269
let meeTextBuffer = UnsafeBufferPointer(
244270
start: localSourceText, count: localSourceTextCount)
245271
let meeText = String(decoding: meeTextBuffer, as: UTF8.self)
246272
let prefix = "#customColorLiteral(hue:"
247273
guard meeText.starts(with: prefix), meeText.last == ")" else {
248-
return (nil, 0)
274+
return (nil, 0, nil, 0)
249275
}
250276
let expr = meeText.dropFirst(prefix.count).dropLast()
251277
var resultString = ".init(_colorLiteralHue:\(expr))"
@@ -254,7 +280,7 @@ struct HSVColorLiteralMacro: _CompilerPlugin {
254280
capacity: buffer.count + 1)
255281
_ = result.initialize(from: buffer)
256282
result[buffer.count] = 0
257-
return (UnsafePointer(result.baseAddress), buffer.count)
283+
return (UnsafePointer(result.baseAddress), buffer.count, nil, 0)
258284
}
259285
}
260286
}

test/Macros/Inputs/macro_definition_missing_allmacros.swift

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -60,8 +60,9 @@ struct DummyMacro: _CompilerPlugin {
6060
sourceFileTextCount: Int,
6161
localSourceText: UnsafePointer<UInt8>,
6262
localSourceTextCount: Int
63-
) -> (UnsafePointer<UInt8>?, count: Int) {
64-
(nil, 0)
63+
) -> (code: UnsafePointer<UInt8>?, codeLength: Int,
64+
diagnostics: UnsafePointer<_Diagnostic>?, diagnosticCount: Int) {
65+
(nil, 0, nil, 0)
6566
}
6667
}
6768

test/Macros/macro_plugin.swift

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,9 @@ let _ = #customStringify(1.0.truncatingRemainder(dividingBy: 1.0) + 3.0)
2828
// CHECK: (binary_expr type='Double'
2929
// CHECK: (string_literal_expr type='String'
3030

31-
let _ = #customStringify(["a", "b", "c"] + ["d", "e", "f"])
31+
let _ = #customStringify(
32+
["a", "b", "c"] +
33+
["d", "e", "f"])
3234

3335
// CHECK: (macro_expansion_expr type='([String], String)' {{.*}} name=customStringify
3436
// CHECK: (argument_list

0 commit comments

Comments
 (0)