From ab1a64bbfd0d04b89106b2ff93e585d94f029570 Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Sun, 9 Mar 2025 11:42:33 -0700 Subject: [PATCH 1/2] [ASTGen] Stub out support for method keypaths --- lib/ASTGen/Sources/ASTGen/Exprs.swift | 3 +++ 1 file changed, 3 insertions(+) diff --git a/lib/ASTGen/Sources/ASTGen/Exprs.swift b/lib/ASTGen/Sources/ASTGen/Exprs.swift index 5cddba907844a..97e799c0923b6 100644 --- a/lib/ASTGen/Sources/ASTGen/Exprs.swift +++ b/lib/ASTGen/Sources/ASTGen/Exprs.swift @@ -736,6 +736,9 @@ extension ASTGenVisitor { additionalTrailingClosures: nil ) ).asExpr + + case .method(_): + fatalError("unimplemented") } } From 29b4f3ddbd57741bef3831888c3111ebdecf1496 Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Sun, 9 Mar 2025 14:17:41 -0700 Subject: [PATCH 2/2] [Diagnostics] Print category footnotes at the end of translation When printing diagnostics, category names are printed as [#] at the end of a diagnostic. For all of the category names that are mentioned in this manner, print "footnotes" at the end of compilation providing documentation references to each category, e.g., [#deprecated]: [#StrictMemorySafety]: Right now, these point into the markdown files in the installed toolchain, same as the URLs behind references. That is subject to change in the future. --- include/swift/AST/DiagnosticBridge.h | 6 +++ include/swift/Bridging/ASTGen.h | 9 +++- lib/AST/DiagnosticBridge.cpp | 33 ++++++++++-- .../Sources/ASTGen/DiagnosticsBridge.swift | 54 +++++++++++++++++++ lib/Frontend/PrintingDiagnosticConsumer.cpp | 6 +++ .../diagnostics/print-diagnostic-groups.swift | 17 +++--- 6 files changed, 111 insertions(+), 14 deletions(-) diff --git a/include/swift/AST/DiagnosticBridge.h b/include/swift/AST/DiagnosticBridge.h index 5920853c9774a..b110f530e9b6b 100644 --- a/include/swift/AST/DiagnosticBridge.h +++ b/include/swift/AST/DiagnosticBridge.h @@ -31,6 +31,9 @@ class DiagnosticBridge { /// A queued up source file known to the queued diagnostics. using QueuedBuffer = void *; + /// Per-frontend state maintained on the Swift side. + void *perFrontendState = nullptr; + /// The queued diagnostics structure. void *queuedDiagnostics = nullptr; llvm::DenseMap queuedBuffers; @@ -56,6 +59,9 @@ class DiagnosticBridge { static SmallVector getSourceBufferStack(SourceManager &sourceMgr, SourceLoc loc); + /// Print the category footnotes as part of teardown. + void printCategoryFootnotes(llvm::raw_ostream &os, bool forceColors); + DiagnosticBridge() = default; ~DiagnosticBridge(); diff --git a/include/swift/Bridging/ASTGen.h b/include/swift/Bridging/ASTGen.h index 110cb4aba0b8b..e50bad4780563 100644 --- a/include/swift/Bridging/ASTGen.h +++ b/include/swift/Bridging/ASTGen.h @@ -26,7 +26,8 @@ void swift_ASTGen_addQueuedSourceFile( void *_Nonnull sourceFile, const uint8_t *_Nonnull displayNamePtr, intptr_t displayNameLength, ssize_t parentID, ssize_t positionInParent); void swift_ASTGen_addQueuedDiagnostic( - void *_Nonnull queued, const char *_Nonnull text, ptrdiff_t textLength, + void *_Nonnull queued, void *_Nonnull state, + const char *_Nonnull text, ptrdiff_t textLength, BridgedDiagnosticSeverity severity, const void *_Nullable sourceLoc, const char *_Nullable categoryName, ptrdiff_t categoryNameLength, const char *_Nullable documentationPath, @@ -37,6 +38,12 @@ void swift_ASTGen_renderQueuedDiagnostics( void *_Nonnull queued, ssize_t contextSize, ssize_t colorize, BridgedStringRef *_Nonnull renderedString); +void *_Nonnull swift_ASTGen_createPerFrontendDiagnosticState(); +void swift_ASTGen_destroyPerFrontendDiagnosticState(void * _Nonnull state); +void swift_ASTGen_renderCategoryFootnotes( + void * _Nonnull state, ssize_t colorize, + BridgedStringRef *_Nonnull renderedString); + // FIXME: Hack because we cannot easily get to the already-parsed source // file from here. Fix this egregious oversight! void *_Nullable swift_ASTGen_parseSourceFile(BridgedStringRef buffer, diff --git a/lib/AST/DiagnosticBridge.cpp b/lib/AST/DiagnosticBridge.cpp index 35f8b095e50ae..bf41c2a0e7da5 100644 --- a/lib/AST/DiagnosticBridge.cpp +++ b/lib/AST/DiagnosticBridge.cpp @@ -27,6 +27,7 @@ using namespace swift; #if SWIFT_BUILD_SWIFT_SYNTAX /// Enqueue a diagnostic with ASTGen's diagnostic rendering. static void addQueueDiagnostic(void *queuedDiagnostics, + void *perFrontendState, const DiagnosticInfo &info, SourceManager &SM) { llvm::SmallString<256> text; { @@ -69,7 +70,8 @@ static void addQueueDiagnostic(void *queuedDiagnostics, documentationPath = info.EducationalNotePaths[0]; // FIXME: Translate Fix-Its. - swift_ASTGen_addQueuedDiagnostic(queuedDiagnostics, text.data(), text.size(), + swift_ASTGen_addQueuedDiagnostic(queuedDiagnostics, perFrontendState, + text.data(), text.size(), severity, info.Loc.getOpaquePointerValue(), info.Category.data(), info.Category.size(), @@ -82,20 +84,25 @@ static void addQueueDiagnostic(void *queuedDiagnostics, // argument to `swift_ASTGen_addQueuedDiagnostic` but that requires // bridging of `Note` structure and new serialization. for (auto *childNote : info.ChildDiagnosticInfo) { - addQueueDiagnostic(queuedDiagnostics, *childNote, SM); + addQueueDiagnostic(queuedDiagnostics, perFrontendState, *childNote, SM); } } void DiagnosticBridge::enqueueDiagnostic(SourceManager &SM, const DiagnosticInfo &Info, unsigned innermostBufferID) { + // If we didn't have per-frontend state before, create it now. + if (!perFrontendState) { + perFrontendState = swift_ASTGen_createPerFrontendDiagnosticState(); + } + // If there are no enqueued diagnostics, or we have hit a non-note // diagnostic, flush any enqueued diagnostics and start fresh. if (!queuedDiagnostics) queuedDiagnostics = swift_ASTGen_createQueuedDiagnostics(); queueBuffer(SM, innermostBufferID); - addQueueDiagnostic(queuedDiagnostics, Info, SM); + addQueueDiagnostic(queuedDiagnostics, perFrontendState, Info, SM); } void DiagnosticBridge::flush(llvm::raw_ostream &OS, bool includeTrailingBreak, @@ -120,6 +127,22 @@ void DiagnosticBridge::flush(llvm::raw_ostream &OS, bool includeTrailingBreak, OS << "\n"; } +void DiagnosticBridge::printCategoryFootnotes(llvm::raw_ostream &os, + bool forceColors) { + if (!perFrontendState) + return; + + BridgedStringRef bridgedRenderedString{nullptr, 0}; + swift_ASTGen_renderCategoryFootnotes( + perFrontendState, forceColors ? 1 : 0, &bridgedRenderedString); + + auto renderedString = bridgedRenderedString.unbridged(); + if (auto renderedData = renderedString.data()) { + os.write(renderedData, renderedString.size()); + swift_ASTGen_freeBridgedString(renderedString); + } +} + void *DiagnosticBridge::getSourceFileSyntax(SourceManager &sourceMgr, unsigned bufferID, StringRef displayName) { @@ -199,6 +222,10 @@ DiagnosticBridge::getSourceBufferStack(SourceManager &sourceMgr, } DiagnosticBridge::~DiagnosticBridge() { + if (perFrontendState) { + swift_ASTGen_destroyPerFrontendDiagnosticState(perFrontendState); + } + assert(!queuedDiagnostics && "unflushed diagnostics"); for (const auto &sourceFileSyntax : sourceFileSyntax) { swift_ASTGen_destroySourceFile(sourceFileSyntax.second); diff --git a/lib/ASTGen/Sources/ASTGen/DiagnosticsBridge.swift b/lib/ASTGen/Sources/ASTGen/DiagnosticsBridge.swift index a689a3c24b0ed..f367292963c50 100644 --- a/lib/ASTGen/Sources/ASTGen/DiagnosticsBridge.swift +++ b/lib/ASTGen/Sources/ASTGen/DiagnosticsBridge.swift @@ -15,6 +15,11 @@ import BasicBridging import SwiftDiagnostics import SwiftSyntax +fileprivate struct PerFrontendDiagnosticState { + /// The set of categories that were referenced by a diagnostic. + var referencedCategories: Set = [] +} + fileprivate func emitDiagnosticParts( diagnosticEngine: BridgedDiagnosticEngine, sourceFileBuffer: UnsafeBufferPointer, @@ -235,6 +240,7 @@ public func addQueuedSourceFile( @_cdecl("swift_ASTGen_addQueuedDiagnostic") public func addQueuedDiagnostic( queuedDiagnosticsPtr: UnsafeMutableRawPointer, + perFrontendDiagnosticStatePtr: UnsafeMutableRawPointer, text: UnsafePointer, textLength: Int, severity: BridgedDiagnosticSeverity, @@ -250,6 +256,10 @@ public func addQueuedDiagnostic( to: QueuedDiagnostics.self ) + let diagnosticState = perFrontendDiagnosticStatePtr.assumingMemoryBound( + to: PerFrontendDiagnosticState.self + ) + guard let rawPosition = cLoc.getOpaquePointerValue() else { return } @@ -375,6 +385,11 @@ public func addQueuedDiagnostic( ) } + // Note that we referenced this category. + if let category { + diagnosticState.pointee.referencedCategories.insert(category) + } + let textBuffer = UnsafeBufferPointer(start: text, count: textLength) let diagnostic = Diagnostic( node: node, @@ -431,3 +446,42 @@ extension String { return false } } + +@_cdecl("swift_ASTGen_createPerFrontendDiagnosticState") +public func createPerFrontendDiagnosticState() -> UnsafeMutableRawPointer { + let ptr = UnsafeMutablePointer.allocate(capacity: 1) + ptr.initialize(to: .init()) + return UnsafeMutableRawPointer(ptr) +} + +@_cdecl("swift_ASTGen_destroyPerFrontendDiagnosticState") +public func destroyPerFrontendDiagnosticState( + statePtr: UnsafeMutableRawPointer +) { + let state = statePtr.assumingMemoryBound(to: PerFrontendDiagnosticState.self) + state.deinitialize(count: 1) + state.deallocate() +} + +@_cdecl("swift_ASTGen_renderCategoryFootnotes") +public func renderCategoryFootnotes( + statePtr: UnsafeMutableRawPointer, + colorize: Int, + renderedStringOutPtr: UnsafeMutablePointer +) { + let state = statePtr.assumingMemoryBound(to: PerFrontendDiagnosticState.self) + let formatter = DiagnosticsFormatter(contextSize: 0, colorize: colorize != 0) + var renderedStr = formatter.categoryFootnotes( + Array(state.pointee.referencedCategories), + leadingText: "\n" + ) + + if !renderedStr.isEmpty { + renderedStr += "\n" + } + + renderedStringOutPtr.pointee = allocateBridgedString(renderedStr) + + // Clear out categories so we start fresh. + state.pointee.referencedCategories = [] +} diff --git a/lib/Frontend/PrintingDiagnosticConsumer.cpp b/lib/Frontend/PrintingDiagnosticConsumer.cpp index e6c5a91257610..c77126b55acfb 100644 --- a/lib/Frontend/PrintingDiagnosticConsumer.cpp +++ b/lib/Frontend/PrintingDiagnosticConsumer.cpp @@ -295,6 +295,12 @@ void PrintingDiagnosticConsumer::flush(bool includeTrailingBreak) { bool PrintingDiagnosticConsumer::finishProcessing() { // If there's an in-flight snippet, flush it. flush(false); + +#if SWIFT_BUILD_SWIFT_SYNTAX + // Print out footnotes for any category that was referenced. + DiagBridge.printCategoryFootnotes(Stream, ForceColors); +#endif + return false; } diff --git a/test/diagnostics/print-diagnostic-groups.swift b/test/diagnostics/print-diagnostic-groups.swift index 63d6f2bdf7a41..931c5eb8c65b4 100644 --- a/test/diagnostics/print-diagnostic-groups.swift +++ b/test/diagnostics/print-diagnostic-groups.swift @@ -1,19 +1,16 @@ -// RUN: %target-swift-frontend -typecheck -diagnostic-style llvm -print-diagnostic-groups %s 2>&1 | %FileCheck %s --check-prefix=CHECK +// RUN: %target-swift-frontend -typecheck %s 2>&1 | %FileCheck %s --check-prefix=CHECK +// REQUIRES: swift_swift_parser +// CHECK: warning: file 'print-diagnostic-groups.swift' is part of module 'main'; ignoring import{{$}} +import main -// This test checks that "-print-diagnostic-groups" prints the diagnostic group -// if it exists, and prints nothing if it does not. - +// This test checks that we get diagnostic groups as part of the printed output. @available(*, deprecated, renamed: "bar2") func bar() { } -// CHECK: warning: 'bar()' is deprecated: renamed to 'bar2' [DeprecatedDeclaration]{{$}} +// CHECK: warning: 'bar()' is deprecated: renamed to 'bar2' [#DeprecatedDeclaration]{{$}} bar() - -func foo() { - // CHECK: warning: initialization of immutable value 'x' was never used; consider replacing with assignment to '_' or removing it{{$}} - let x = 42 -} \ No newline at end of file +// CHECK: [#DeprecatedDeclarations]: <{{.*}}deprecated-declaration.md>