Skip to content

Commit 14aebca

Browse files
committed
Bridge swift-syntax diagnostics to the C++ diagnostic engine's diagnostics.
Implement an ASTGen operation to bridge swift-syntax diagnostics, as produced by the parser, operator folding, and macros, over to the C++ diagnostic engine infrastructure. Use this to wire up macro expansion diagnostics.
1 parent 728ba4e commit 14aebca

File tree

9 files changed

+369
-2
lines changed

9 files changed

+369
-2
lines changed

include/swift/AST/CASTBridging.h

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,13 +86,49 @@ typedef struct {
8686
// FIXME: Handle Layout Requirements
8787
} BridgedRequirementRepr;
8888

89+
/// Diagnostic severity when reporting diagnostics.
90+
typedef enum ENUM_EXTENSIBILITY_ATTR(open) BridgedDiagnosticSeverity : long {
91+
BridgedFatalError,
92+
BridgedError,
93+
BridgedWarning,
94+
BridgedRemark,
95+
BridgedNote,
96+
} BridgedDiagnosticSeverity;
97+
98+
typedef void* BridgedDiagnostic;
99+
89100
#ifdef __cplusplus
90101
extern "C" {
91102

92103
#define _Bool bool
93104

94105
#endif
95106

107+
// Diagnostics
108+
109+
/// Create a new diagnostic with the given severity, location, and diagnostic
110+
/// text.
111+
///
112+
/// \returns a diagnostic instance that can be extended with additional
113+
/// information and then must be finished via \c SwiftDiagnostic_finish.
114+
BridgedDiagnostic SwiftDiagnostic_create(
115+
void *diagnosticEngine, BridgedDiagnosticSeverity severity,
116+
void *_Nullable sourceLoc,
117+
const uint8_t *_Nullable text, long textLen);
118+
119+
/// Highlight a source range as part of the diagnostic.
120+
void SwiftDiagnostic_highlight(
121+
BridgedDiagnostic diag, void *_Nullable startLoc, void *_Nullable endLoc);
122+
123+
/// Add a Fix-It to replace a source range as part of the diagnostic.
124+
void SwiftDiagnostic_fixItReplace(
125+
BridgedDiagnostic diag,
126+
void *_Nullable replaceStartLoc, void *_Nullable replaceEndLoc,
127+
const uint8_t *_Nullable newText, long newTextLen);
128+
129+
/// Finish the given diagnostic and emit it.
130+
void SwiftDiagnostic_finish(BridgedDiagnostic diag);
131+
96132
BridgedIdentifier SwiftASTContext_getIdentifier(void *ctx,
97133
const uint8_t *_Nullable str,
98134
long len);

include/swift/AST/DiagnosticsCommon.def

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -214,5 +214,15 @@ REMARK(remark_save_cache, none,
214214
ERROR(unknown_attribute,none,
215215
"unknown attribute '%0'", (StringRef))
216216

217+
//------------------------------------------------------------------------------
218+
// MARK: bridged diagnostics
219+
//------------------------------------------------------------------------------
220+
221+
NOTE(bridged_note, none, "%0", (StringRef))
222+
WARNING(bridged_warning, none,"%0", (StringRef))
223+
ERROR(bridged_error, none, "%0", (StringRef))
224+
ERROR(bridged_fatal_error, Fatal, "%0", (StringRef))
225+
REMARK(bridged_remark, none, "%0", (StringRef))
226+
217227
#define UNDEFINE_DIAGNOSTIC_MACROS
218228
#include "DefineDiagnosticMacros.h"

lib/AST/CASTBridging.cpp

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
#include "swift/AST/ASTContext.h"
44
#include "swift/AST/ASTNode.h"
55
#include "swift/AST/Decl.h"
6+
#include "swift/AST/DiagnosticsCommon.h"
67
#include "swift/AST/Expr.h"
78
#include "swift/AST/GenericParamList.h"
89
#include "swift/AST/Identifier.h"
@@ -23,6 +24,95 @@ static SourceLoc getSourceLocFromPointer(void *loc) {
2324
return SourceLoc(smLoc);
2425
}
2526

27+
namespace {
28+
struct BridgedDiagnosticImpl {
29+
InFlightDiagnostic inFlight;
30+
std::vector<StringRef> textBlobs;
31+
32+
BridgedDiagnosticImpl(const BridgedDiagnosticImpl&) = delete;
33+
BridgedDiagnosticImpl(BridgedDiagnosticImpl &&) = delete;
34+
BridgedDiagnosticImpl &operator=(const BridgedDiagnosticImpl&) = delete;
35+
BridgedDiagnosticImpl &operator=(BridgedDiagnosticImpl &&) = delete;
36+
37+
~BridgedDiagnosticImpl() {
38+
inFlight.flush();
39+
for (auto text: textBlobs) {
40+
free((void*)text.data());
41+
}
42+
}
43+
};
44+
}
45+
46+
BridgedDiagnostic SwiftDiagnostic_create(
47+
void *diagnosticEngine, BridgedDiagnosticSeverity severity,
48+
void *sourceLocPtr,
49+
const uint8_t *textPtr, long textLen
50+
) {
51+
StringRef origText{
52+
reinterpret_cast<const char *>(textPtr), size_t(textLen)};
53+
llvm::MallocAllocator mallocAlloc;
54+
StringRef text = origText.copy(mallocAlloc);
55+
56+
SourceLoc loc = getSourceLocFromPointer(sourceLocPtr);
57+
58+
Diag<StringRef> diagID;
59+
switch (severity) {
60+
case BridgedDiagnosticSeverity::BridgedError:
61+
diagID = diag::bridged_error;
62+
break;
63+
case BridgedDiagnosticSeverity::BridgedFatalError:
64+
diagID = diag::bridged_fatal_error;
65+
break;
66+
case BridgedDiagnosticSeverity::BridgedNote:
67+
diagID = diag::bridged_note;
68+
break;
69+
case BridgedDiagnosticSeverity::BridgedRemark:
70+
diagID = diag::bridged_remark;
71+
break;
72+
case BridgedDiagnosticSeverity::BridgedWarning:
73+
diagID = diag::bridged_warning;
74+
break;
75+
}
76+
77+
DiagnosticEngine &diags = *static_cast<DiagnosticEngine *>(diagnosticEngine);
78+
return new BridgedDiagnosticImpl{diags.diagnose(loc, diagID, text), {text}};
79+
}
80+
81+
/// Highlight a source range as part of the diagnostic.
82+
void SwiftDiagnostic_highlight(
83+
BridgedDiagnostic diagPtr, void *startLocPtr, void *endLocPtr
84+
) {
85+
SourceLoc startLoc = getSourceLocFromPointer(startLocPtr);
86+
SourceLoc endLoc = getSourceLocFromPointer(endLocPtr);
87+
88+
BridgedDiagnosticImpl *impl = static_cast<BridgedDiagnosticImpl *>(diagPtr);
89+
impl->inFlight.highlightChars(startLoc, endLoc);
90+
}
91+
92+
/// Add a Fix-It to replace a source range as part of the diagnostic.
93+
void SwiftDiagnostic_fixItReplace(
94+
BridgedDiagnostic diagPtr, void *replaceStartLocPtr, void *replaceEndLocPtr,
95+
const uint8_t *newTextPtr, long newTextLen) {
96+
97+
SourceLoc startLoc = getSourceLocFromPointer(replaceStartLocPtr);
98+
SourceLoc endLoc = getSourceLocFromPointer(replaceEndLocPtr);
99+
100+
StringRef origReplaceText{
101+
reinterpret_cast<const char *>(newTextPtr), size_t(newTextLen)};
102+
llvm::MallocAllocator mallocAlloc;
103+
StringRef replaceText = origReplaceText.copy(mallocAlloc);
104+
105+
BridgedDiagnosticImpl *impl = static_cast<BridgedDiagnosticImpl *>(diagPtr);
106+
impl->textBlobs.push_back(replaceText);
107+
impl->inFlight.fixItReplaceChars(startLoc, endLoc, replaceText);
108+
}
109+
110+
/// Finish the given diagnostic and emit it.
111+
void SwiftDiagnostic_finish(BridgedDiagnostic diagPtr) {
112+
BridgedDiagnosticImpl *impl = static_cast<BridgedDiagnosticImpl *>(diagPtr);
113+
delete impl;
114+
}
115+
26116
BridgedIdentifier SwiftASTContext_getIdentifier(void *ctx,
27117
const uint8_t *_Nullable str,
28118
long len) {

lib/ASTGen/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ if (SWIFT_SWIFT_PARSER)
2121
add_library(swiftASTGen STATIC
2222
Sources/ASTGen/ASTGen.swift
2323
Sources/ASTGen/Decls.swift
24+
Sources/ASTGen/Diagnostics.swift
2425
Sources/ASTGen/Exprs.swift
2526
Sources/ASTGen/Generics.swift
2627
Sources/ASTGen/Literals.swift
Lines changed: 135 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,135 @@
1+
import CASTBridging
2+
import SwiftDiagnostics
3+
import SwiftSyntax
4+
5+
fileprivate func emitDiagnosticParts(
6+
diagEnginePtr: UnsafeMutablePointer<UInt8>,
7+
sourceFileBuffer: UnsafeMutableBufferPointer<UInt8>,
8+
nodeStartOffset: Int?,
9+
message: String,
10+
severity: DiagnosticSeverity,
11+
position: AbsolutePosition,
12+
highlights: [Syntax] = [],
13+
fixItChanges: [FixIt.Change] = []
14+
) {
15+
// Map severity
16+
let bridgedSeverity: BridgedDiagnosticSeverity
17+
switch severity {
18+
case .error: bridgedSeverity = .error
19+
case .note: bridgedSeverity = .note
20+
case .warning: bridgedSeverity = .warning
21+
}
22+
23+
// Form a source location for the given absolute position
24+
func sourceLoc(
25+
at origPosition: AbsolutePosition
26+
) -> UnsafeMutablePointer<UInt8>? {
27+
// FIXME: Our tree is very confused about absolute offsets. Work around
28+
// the issue in a very hacky way.
29+
let position: AbsolutePosition
30+
if let nodeStartOffset = nodeStartOffset,
31+
origPosition.utf8Offset < nodeStartOffset {
32+
position = origPosition + SourceLength(utf8Length: nodeStartOffset)
33+
} else {
34+
position = origPosition
35+
}
36+
37+
if let sourceFileBase = sourceFileBuffer.baseAddress,
38+
position.utf8Offset >= 0 &&
39+
position.utf8Offset < sourceFileBuffer.count {
40+
return sourceFileBase + position.utf8Offset
41+
}
42+
43+
return nil
44+
}
45+
46+
// Emit the diagnostic
47+
var mutableMessage = message
48+
let diag = mutableMessage.withUTF8 { messageBuffer in
49+
SwiftDiagnostic_create(
50+
diagEnginePtr, bridgedSeverity, sourceLoc(at: position),
51+
messageBuffer.baseAddress, messageBuffer.count
52+
)
53+
}
54+
55+
// Emit highlights
56+
for highlight in highlights {
57+
SwiftDiagnostic_highlight(
58+
diag, sourceLoc(at: highlight.position),
59+
sourceLoc(at: highlight.endPosition)
60+
)
61+
}
62+
63+
// Emit changes for a Fix-It.
64+
for change in fixItChanges {
65+
let replaceStartLoc: UnsafeMutablePointer<UInt8>?
66+
let replaceEndLoc: UnsafeMutablePointer<UInt8>?
67+
var newText: String
68+
69+
switch change {
70+
case .replace(let oldNode, let newNode):
71+
replaceStartLoc = sourceLoc(at: oldNode.position)
72+
replaceEndLoc = sourceLoc(at: oldNode.endPosition)
73+
newText = newNode.description
74+
75+
case .replaceLeadingTrivia(let oldToken, let newTrivia):
76+
replaceStartLoc = sourceLoc(at: oldToken.position)
77+
replaceEndLoc = sourceLoc(
78+
at: oldToken.positionAfterSkippingLeadingTrivia)
79+
newText = newTrivia.description
80+
81+
case .replaceTrailingTrivia(let oldToken, let newTrivia):
82+
replaceStartLoc = sourceLoc(at: oldToken.endPositionBeforeTrailingTrivia)
83+
replaceEndLoc = sourceLoc(at: oldToken.endPosition)
84+
newText = newTrivia.description
85+
}
86+
87+
newText.withUTF8 { textBuffer in
88+
SwiftDiagnostic_fixItReplace(
89+
diag, replaceStartLoc, replaceEndLoc,
90+
textBuffer.baseAddress, textBuffer.count
91+
)
92+
}
93+
}
94+
95+
SwiftDiagnostic_finish(diag);
96+
}
97+
98+
/// Emit the given diagnostic via the diagnostic engine.
99+
func emitDiagnostic(
100+
diagEnginePtr: UnsafeMutablePointer<UInt8>,
101+
sourceFileBuffer: UnsafeMutableBufferPointer<UInt8>,
102+
nodeStartOffset: Int? = nil,
103+
diagnostic: Diagnostic
104+
) {
105+
// Collect all of the Fix-It changes based on their Fix-It ID.
106+
var fixItChangesByID: [MessageID : [FixIt.Change]] = [:]
107+
for fixIt in diagnostic.fixIts {
108+
fixItChangesByID[fixIt.message.fixItID, default: []]
109+
.append(contentsOf: fixIt.changes.changes)
110+
}
111+
112+
// Emit the main diagnostic
113+
emitDiagnosticParts(
114+
diagEnginePtr: diagEnginePtr,
115+
sourceFileBuffer: sourceFileBuffer,
116+
nodeStartOffset: nodeStartOffset,
117+
message: diagnostic.diagMessage.message,
118+
severity: diagnostic.diagMessage.severity,
119+
position: diagnostic.position,
120+
highlights: diagnostic.highlights,
121+
fixItChanges: fixItChangesByID[diagnostic.diagnosticID] ?? []
122+
)
123+
124+
// Emit any notes as follow-ons.
125+
for note in diagnostic.notes {
126+
emitDiagnosticParts(
127+
diagEnginePtr: diagEnginePtr,
128+
sourceFileBuffer: sourceFileBuffer,
129+
nodeStartOffset: nodeStartOffset,
130+
message: note.message,
131+
severity: .note, position: note.position,
132+
fixItChanges: fixItChangesByID[note.noteMessage.fixItID] ?? []
133+
)
134+
}
135+
}

lib/ASTGen/Sources/ASTGen/Macros.swift

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,7 @@ extension String {
9797
@_cdecl("swift_ASTGen_evaluateMacro")
9898
@usableFromInline
9999
func evaluateMacro(
100+
diagEnginePtr: UnsafeMutablePointer<UInt8>,
100101
macroPtr: UnsafeMutablePointer<UInt8>,
101102
sourceFilePtr: UnsafePointer<UInt8>,
102103
sourceLocationPtr: UnsafePointer<UInt8>?,
@@ -150,7 +151,17 @@ func evaluateMacro(
150151
return exprMacro.expansion(of: parentExpansion, in: &context)
151152
}
152153

153-
// FIXME: Emit diagnostics accumulated in the
154+
// Emit diagnostics accumulated in the context.
155+
for diag in context.diagnostics {
156+
// FIXME: Consider tacking on a note that says that this diagnostic
157+
// came from a macro expansion.
158+
emitDiagnostic(
159+
diagEnginePtr: diagEnginePtr,
160+
sourceFileBuffer: .init(mutating: sourceFile.pointee.buffer),
161+
nodeStartOffset: parentSyntax.position.utf8Offset,
162+
diagnostic: diag
163+
)
164+
}
154165

155166
var evaluatedSyntaxStr = evaluatedSyntax.withoutTrivia().description
156167
evaluatedSyntaxStr.withUTF8 { utf8 in

lib/Sema/TypeCheckMacros.cpp

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,8 @@ extern "C" void *swift_ASTGen_resolveMacroType(const void *macroType);
4040
extern "C" void swift_ASTGen_destroyMacro(void *macro);
4141

4242
extern "C" ptrdiff_t swift_ASTGen_evaluateMacro(
43-
void *macro, void *sourceFile, const void *sourceLocation,
43+
void *diagEngine, void *macro, void *sourceFile,
44+
const void *sourceLocation,
4445
const char **evaluatedSource, ptrdiff_t *evaluatedSourceLength);
4546

4647
/// Produce the mangled name for the nominal type descriptor of a type
@@ -175,6 +176,7 @@ Expr *swift::expandMacroExpr(
175176
const char *evaluatedSourceAddress;
176177
ptrdiff_t evaluatedSourceLength;
177178
swift_ASTGen_evaluateMacro(
179+
&ctx.Diags,
178180
macroDef.getAsBuiltin(),
179181
astGenSourceFile, expr->getStartLoc().getOpaquePointerValue(),
180182
&evaluatedSourceAddress, &evaluatedSourceLength);

0 commit comments

Comments
 (0)