Skip to content

Commit a866f5d

Browse files
authored
Merge pull request #79874 from DougGregor/category-footnotes
[Diagnostics] Print category footnotes at the end of translation
2 parents 737b5ec + 29b4f3d commit a866f5d

File tree

6 files changed

+111
-14
lines changed

6 files changed

+111
-14
lines changed

include/swift/AST/DiagnosticBridge.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,9 @@ class DiagnosticBridge {
3131
/// A queued up source file known to the queued diagnostics.
3232
using QueuedBuffer = void *;
3333

34+
/// Per-frontend state maintained on the Swift side.
35+
void *perFrontendState = nullptr;
36+
3437
/// The queued diagnostics structure.
3538
void *queuedDiagnostics = nullptr;
3639
llvm::DenseMap<unsigned, QueuedBuffer> queuedBuffers;
@@ -56,6 +59,9 @@ class DiagnosticBridge {
5659
static SmallVector<unsigned, 1> getSourceBufferStack(SourceManager &sourceMgr,
5760
SourceLoc loc);
5861

62+
/// Print the category footnotes as part of teardown.
63+
void printCategoryFootnotes(llvm::raw_ostream &os, bool forceColors);
64+
5965
DiagnosticBridge() = default;
6066
~DiagnosticBridge();
6167

include/swift/Bridging/ASTGen.h

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,8 @@ void swift_ASTGen_addQueuedSourceFile(
2626
void *_Nonnull sourceFile, const uint8_t *_Nonnull displayNamePtr,
2727
intptr_t displayNameLength, ssize_t parentID, ssize_t positionInParent);
2828
void swift_ASTGen_addQueuedDiagnostic(
29-
void *_Nonnull queued, const char *_Nonnull text, ptrdiff_t textLength,
29+
void *_Nonnull queued, void *_Nonnull state,
30+
const char *_Nonnull text, ptrdiff_t textLength,
3031
BridgedDiagnosticSeverity severity, const void *_Nullable sourceLoc,
3132
const char *_Nullable categoryName, ptrdiff_t categoryNameLength,
3233
const char *_Nullable documentationPath,
@@ -37,6 +38,12 @@ void swift_ASTGen_renderQueuedDiagnostics(
3738
void *_Nonnull queued, ssize_t contextSize, ssize_t colorize,
3839
BridgedStringRef *_Nonnull renderedString);
3940

41+
void *_Nonnull swift_ASTGen_createPerFrontendDiagnosticState();
42+
void swift_ASTGen_destroyPerFrontendDiagnosticState(void * _Nonnull state);
43+
void swift_ASTGen_renderCategoryFootnotes(
44+
void * _Nonnull state, ssize_t colorize,
45+
BridgedStringRef *_Nonnull renderedString);
46+
4047
// FIXME: Hack because we cannot easily get to the already-parsed source
4148
// file from here. Fix this egregious oversight!
4249
void *_Nullable swift_ASTGen_parseSourceFile(BridgedStringRef buffer,

lib/AST/DiagnosticBridge.cpp

Lines changed: 30 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ using namespace swift;
2727
#if SWIFT_BUILD_SWIFT_SYNTAX
2828
/// Enqueue a diagnostic with ASTGen's diagnostic rendering.
2929
static void addQueueDiagnostic(void *queuedDiagnostics,
30+
void *perFrontendState,
3031
const DiagnosticInfo &info, SourceManager &SM) {
3132
llvm::SmallString<256> text;
3233
{
@@ -69,7 +70,8 @@ static void addQueueDiagnostic(void *queuedDiagnostics,
6970
documentationPath = info.EducationalNotePaths[0];
7071

7172
// FIXME: Translate Fix-Its.
72-
swift_ASTGen_addQueuedDiagnostic(queuedDiagnostics, text.data(), text.size(),
73+
swift_ASTGen_addQueuedDiagnostic(queuedDiagnostics, perFrontendState,
74+
text.data(), text.size(),
7375
severity, info.Loc.getOpaquePointerValue(),
7476
info.Category.data(),
7577
info.Category.size(),
@@ -82,20 +84,25 @@ static void addQueueDiagnostic(void *queuedDiagnostics,
8284
// argument to `swift_ASTGen_addQueuedDiagnostic` but that requires
8385
// bridging of `Note` structure and new serialization.
8486
for (auto *childNote : info.ChildDiagnosticInfo) {
85-
addQueueDiagnostic(queuedDiagnostics, *childNote, SM);
87+
addQueueDiagnostic(queuedDiagnostics, perFrontendState, *childNote, SM);
8688
}
8789
}
8890

8991
void DiagnosticBridge::enqueueDiagnostic(SourceManager &SM,
9092
const DiagnosticInfo &Info,
9193
unsigned innermostBufferID) {
94+
// If we didn't have per-frontend state before, create it now.
95+
if (!perFrontendState) {
96+
perFrontendState = swift_ASTGen_createPerFrontendDiagnosticState();
97+
}
98+
9299
// If there are no enqueued diagnostics, or we have hit a non-note
93100
// diagnostic, flush any enqueued diagnostics and start fresh.
94101
if (!queuedDiagnostics)
95102
queuedDiagnostics = swift_ASTGen_createQueuedDiagnostics();
96103

97104
queueBuffer(SM, innermostBufferID);
98-
addQueueDiagnostic(queuedDiagnostics, Info, SM);
105+
addQueueDiagnostic(queuedDiagnostics, perFrontendState, Info, SM);
99106
}
100107

101108
void DiagnosticBridge::flush(llvm::raw_ostream &OS, bool includeTrailingBreak,
@@ -120,6 +127,22 @@ void DiagnosticBridge::flush(llvm::raw_ostream &OS, bool includeTrailingBreak,
120127
OS << "\n";
121128
}
122129

130+
void DiagnosticBridge::printCategoryFootnotes(llvm::raw_ostream &os,
131+
bool forceColors) {
132+
if (!perFrontendState)
133+
return;
134+
135+
BridgedStringRef bridgedRenderedString{nullptr, 0};
136+
swift_ASTGen_renderCategoryFootnotes(
137+
perFrontendState, forceColors ? 1 : 0, &bridgedRenderedString);
138+
139+
auto renderedString = bridgedRenderedString.unbridged();
140+
if (auto renderedData = renderedString.data()) {
141+
os.write(renderedData, renderedString.size());
142+
swift_ASTGen_freeBridgedString(renderedString);
143+
}
144+
}
145+
123146
void *DiagnosticBridge::getSourceFileSyntax(SourceManager &sourceMgr,
124147
unsigned bufferID,
125148
StringRef displayName) {
@@ -199,6 +222,10 @@ DiagnosticBridge::getSourceBufferStack(SourceManager &sourceMgr,
199222
}
200223

201224
DiagnosticBridge::~DiagnosticBridge() {
225+
if (perFrontendState) {
226+
swift_ASTGen_destroyPerFrontendDiagnosticState(perFrontendState);
227+
}
228+
202229
assert(!queuedDiagnostics && "unflushed diagnostics");
203230
for (const auto &sourceFileSyntax : sourceFileSyntax) {
204231
swift_ASTGen_destroySourceFile(sourceFileSyntax.second);

lib/ASTGen/Sources/ASTGen/DiagnosticsBridge.swift

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,11 @@ import BasicBridging
1515
import SwiftDiagnostics
1616
import SwiftSyntax
1717

18+
fileprivate struct PerFrontendDiagnosticState {
19+
/// The set of categories that were referenced by a diagnostic.
20+
var referencedCategories: Set<DiagnosticCategory> = []
21+
}
22+
1823
fileprivate func emitDiagnosticParts(
1924
diagnosticEngine: BridgedDiagnosticEngine,
2025
sourceFileBuffer: UnsafeBufferPointer<UInt8>,
@@ -235,6 +240,7 @@ public func addQueuedSourceFile(
235240
@_cdecl("swift_ASTGen_addQueuedDiagnostic")
236241
public func addQueuedDiagnostic(
237242
queuedDiagnosticsPtr: UnsafeMutableRawPointer,
243+
perFrontendDiagnosticStatePtr: UnsafeMutableRawPointer,
238244
text: UnsafePointer<UInt8>,
239245
textLength: Int,
240246
severity: BridgedDiagnosticSeverity,
@@ -250,6 +256,10 @@ public func addQueuedDiagnostic(
250256
to: QueuedDiagnostics.self
251257
)
252258

259+
let diagnosticState = perFrontendDiagnosticStatePtr.assumingMemoryBound(
260+
to: PerFrontendDiagnosticState.self
261+
)
262+
253263
guard let rawPosition = cLoc.getOpaquePointerValue() else {
254264
return
255265
}
@@ -375,6 +385,11 @@ public func addQueuedDiagnostic(
375385
)
376386
}
377387

388+
// Note that we referenced this category.
389+
if let category {
390+
diagnosticState.pointee.referencedCategories.insert(category)
391+
}
392+
378393
let textBuffer = UnsafeBufferPointer(start: text, count: textLength)
379394
let diagnostic = Diagnostic(
380395
node: node,
@@ -431,3 +446,42 @@ extension String {
431446
return false
432447
}
433448
}
449+
450+
@_cdecl("swift_ASTGen_createPerFrontendDiagnosticState")
451+
public func createPerFrontendDiagnosticState() -> UnsafeMutableRawPointer {
452+
let ptr = UnsafeMutablePointer<PerFrontendDiagnosticState>.allocate(capacity: 1)
453+
ptr.initialize(to: .init())
454+
return UnsafeMutableRawPointer(ptr)
455+
}
456+
457+
@_cdecl("swift_ASTGen_destroyPerFrontendDiagnosticState")
458+
public func destroyPerFrontendDiagnosticState(
459+
statePtr: UnsafeMutableRawPointer
460+
) {
461+
let state = statePtr.assumingMemoryBound(to: PerFrontendDiagnosticState.self)
462+
state.deinitialize(count: 1)
463+
state.deallocate()
464+
}
465+
466+
@_cdecl("swift_ASTGen_renderCategoryFootnotes")
467+
public func renderCategoryFootnotes(
468+
statePtr: UnsafeMutableRawPointer,
469+
colorize: Int,
470+
renderedStringOutPtr: UnsafeMutablePointer<BridgedStringRef>
471+
) {
472+
let state = statePtr.assumingMemoryBound(to: PerFrontendDiagnosticState.self)
473+
let formatter = DiagnosticsFormatter(contextSize: 0, colorize: colorize != 0)
474+
var renderedStr = formatter.categoryFootnotes(
475+
Array(state.pointee.referencedCategories),
476+
leadingText: "\n"
477+
)
478+
479+
if !renderedStr.isEmpty {
480+
renderedStr += "\n"
481+
}
482+
483+
renderedStringOutPtr.pointee = allocateBridgedString(renderedStr)
484+
485+
// Clear out categories so we start fresh.
486+
state.pointee.referencedCategories = []
487+
}

lib/Frontend/PrintingDiagnosticConsumer.cpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -295,6 +295,12 @@ void PrintingDiagnosticConsumer::flush(bool includeTrailingBreak) {
295295
bool PrintingDiagnosticConsumer::finishProcessing() {
296296
// If there's an in-flight snippet, flush it.
297297
flush(false);
298+
299+
#if SWIFT_BUILD_SWIFT_SYNTAX
300+
// Print out footnotes for any category that was referenced.
301+
DiagBridge.printCategoryFootnotes(Stream, ForceColors);
302+
#endif
303+
298304
return false;
299305
}
300306

Lines changed: 7 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,16 @@
1-
// RUN: %target-swift-frontend -typecheck -diagnostic-style llvm -print-diagnostic-groups %s 2>&1 | %FileCheck %s --check-prefix=CHECK
1+
// RUN: %target-swift-frontend -typecheck %s 2>&1 | %FileCheck %s --check-prefix=CHECK
2+
// REQUIRES: swift_swift_parser
23

4+
// CHECK: warning: file 'print-diagnostic-groups.swift' is part of module 'main'; ignoring import{{$}}
5+
import main
36

4-
// This test checks that "-print-diagnostic-groups" prints the diagnostic group
5-
// if it exists, and prints nothing if it does not.
6-
7+
// This test checks that we get diagnostic groups as part of the printed output.
78

89
@available(*, deprecated, renamed: "bar2")
910
func bar() {
1011
}
1112

12-
// CHECK: warning: 'bar()' is deprecated: renamed to 'bar2' [DeprecatedDeclaration]{{$}}
13+
// CHECK: warning: 'bar()' is deprecated: renamed to 'bar2' [#DeprecatedDeclaration]{{$}}
1314
bar()
1415

15-
16-
func foo() {
17-
// CHECK: warning: initialization of immutable value 'x' was never used; consider replacing with assignment to '_' or removing it{{$}}
18-
let x = 42
19-
}
16+
// CHECK: [#DeprecatedDeclarations]: <{{.*}}deprecated-declaration.md>

0 commit comments

Comments
 (0)