diff --git a/include/swift/AST/DiagnosticBridge.h b/include/swift/AST/DiagnosticBridge.h index b110f530e9b6b..81a161aa3a549 100644 --- a/include/swift/AST/DiagnosticBridge.h +++ b/include/swift/AST/DiagnosticBridge.h @@ -46,6 +46,10 @@ class DiagnosticBridge { void enqueueDiagnostic(SourceManager &SM, const DiagnosticInfo &Info, unsigned innermostBufferID); + /// Emit a single diagnostic without location information. + void emitDiagnosticWithoutLocation( + const DiagnosticInfo &Info, llvm::raw_ostream &out, bool forceColors); + /// Flush all enqueued diagnostics. void flush(llvm::raw_ostream &OS, bool includeTrailingBreak, bool forceColors); diff --git a/include/swift/Bridging/ASTGen.h b/include/swift/Bridging/ASTGen.h index afef5603a99c7..b72b9a52d4814 100644 --- a/include/swift/Bridging/ASTGen.h +++ b/include/swift/Bridging/ASTGen.h @@ -35,6 +35,14 @@ void swift_ASTGen_addQueuedDiagnostic( const BridgedCharSourceRange *_Nullable highlightRanges, ptrdiff_t numHighlightRanges, BridgedArrayRef /*BridgedFixIt*/ fixIts); +void swift_ASTGen_renderSingleDiagnostic( + void *_Nonnull state, + BridgedStringRef text, + BridgedDiagnosticSeverity severity, + BridgedStringRef categoryName, + BridgedStringRef documentationPath, + ssize_t colorize, + BridgedStringRef *_Nonnull renderedString); void swift_ASTGen_renderQueuedDiagnostics( void *_Nonnull queued, ssize_t contextSize, ssize_t colorize, BridgedStringRef *_Nonnull renderedString); diff --git a/lib/AST/DiagnosticBridge.cpp b/lib/AST/DiagnosticBridge.cpp index 859acbb22c2d6..e79cb386771ea 100644 --- a/lib/AST/DiagnosticBridge.cpp +++ b/lib/AST/DiagnosticBridge.cpp @@ -26,6 +26,22 @@ using namespace swift; #if SWIFT_BUILD_SWIFT_SYNTAX +static BridgedDiagnosticSeverity bridgeDiagnosticSeverity(DiagnosticKind kind) { + switch (kind) { + case DiagnosticKind::Error: + return BridgedDiagnosticSeverity::BridgedError; + + case DiagnosticKind::Warning: + return BridgedDiagnosticSeverity::BridgedWarning; + + case DiagnosticKind::Remark: + return BridgedDiagnosticSeverity::BridgedRemark; + + case DiagnosticKind::Note: + return BridgedDiagnosticSeverity::BridgedNote; + } +} + /// Enqueue a diagnostic with ASTGen's diagnostic rendering. static void addQueueDiagnostic(void *queuedDiagnostics, void *perFrontendState, @@ -37,24 +53,7 @@ static void addQueueDiagnostic(void *queuedDiagnostics, info.FormatArgs); } - BridgedDiagnosticSeverity severity; - switch (info.Kind) { - case DiagnosticKind::Error: - severity = BridgedDiagnosticSeverity::BridgedError; - break; - - case DiagnosticKind::Warning: - severity = BridgedDiagnosticSeverity::BridgedWarning; - break; - - case DiagnosticKind::Remark: - severity = BridgedDiagnosticSeverity::BridgedRemark; - break; - - case DiagnosticKind::Note: - severity = BridgedDiagnosticSeverity::BridgedNote; - break; - } + BridgedDiagnosticSeverity severity = bridgeDiagnosticSeverity(info.Kind); // Map the highlight ranges. SmallVector highlightRanges; @@ -87,6 +86,39 @@ static void addQueueDiagnostic(void *queuedDiagnostics, } } +void DiagnosticBridge::emitDiagnosticWithoutLocation( + const DiagnosticInfo &info, llvm::raw_ostream &out, bool forceColors) { + ASSERT(queuedDiagnostics == nullptr); + + // If we didn't have per-frontend state before, create it now. + if (!perFrontendState) { + perFrontendState = swift_ASTGen_createPerFrontendDiagnosticState(); + } + + llvm::SmallString<256> text; + { + llvm::raw_svector_ostream out(text); + DiagnosticEngine::formatDiagnosticText(out, info.FormatString, + info.FormatArgs); + } + + BridgedDiagnosticSeverity severity = bridgeDiagnosticSeverity(info.Kind); + + BridgedStringRef bridgedRenderedString{nullptr, 0}; + swift_ASTGen_renderSingleDiagnostic( + perFrontendState, text.str(), severity, info.Category, + llvm::StringRef(info.CategoryDocumentationURL), forceColors ? 1 : 0, + &bridgedRenderedString); + + auto renderedString = bridgedRenderedString.unbridged(); + if (renderedString.data()) { + out << ":0: "; + out.write(renderedString.data(), renderedString.size()); + swift_ASTGen_freeBridgedString(renderedString); + out << "\n"; + } +} + void DiagnosticBridge::enqueueDiagnostic(SourceManager &SM, const DiagnosticInfo &Info, unsigned innermostBufferID) { diff --git a/lib/ASTGen/Sources/ASTGen/DiagnosticsBridge.swift b/lib/ASTGen/Sources/ASTGen/DiagnosticsBridge.swift index 6d733229c2e1a..5f0eab41ed26c 100644 --- a/lib/ASTGen/Sources/ASTGen/DiagnosticsBridge.swift +++ b/lib/ASTGen/Sources/ASTGen/DiagnosticsBridge.swift @@ -425,6 +425,60 @@ public func addQueuedDiagnostic( queuedDiagnostics.pointee.grouped.addDiagnostic(diagnostic) } +/// Render a single diagnostic that has no source location information. +@_cdecl("swift_ASTGen_renderSingleDiagnostic") +public func renderSingleDiagnostic( + perFrontendDiagnosticStatePtr: UnsafeMutableRawPointer, + text: BridgedStringRef, + severity: BridgedDiagnosticSeverity, + categoryName: BridgedStringRef, + documentationPath: BridgedStringRef, + colorize: Int, + renderedStringOutPtr: UnsafeMutablePointer +) { + let diagnosticState = perFrontendDiagnosticStatePtr.assumingMemoryBound( + to: PerFrontendDiagnosticState.self + ) + + let documentationPath = String(bridged: documentationPath) + let documentationURL: String? = if !documentationPath.isEmpty { + // If this looks doesn't look like a URL, prepend file://. + documentationPath.looksLikeURL ? documentationPath : "file://\(documentationPath)" + } else { + nil + } + + let categoryName = String(bridged: categoryName) + // If the data comes from serialized diagnostics, it's possible that + // the category name is empty because StringRef() is serialized into + // an empty string. + let category: DiagnosticCategory? = if !categoryName.isEmpty { + DiagnosticCategory( + name: categoryName, + documentationURL: documentationURL + ) + } else { + nil + } + + // Note that we referenced this category. + if let category { + diagnosticState.pointee.referencedCategories.insert(category) + } + + let formatter = DiagnosticsFormatter(colorize: colorize != 0) + + let renderedStr = formatter.formattedMessage( + SimpleDiagnostic( + message: String(bridged: text), + severity: severity.asSeverity, + category: category + ) + ) + + renderedStringOutPtr.pointee = allocateBridgedString(renderedStr) +} + /// Render the queued diagnostics into a UTF-8 string. @_cdecl("swift_ASTGen_renderQueuedDiagnostics") public func renderQueuedDiagnostics( diff --git a/lib/Frontend/PrintingDiagnosticConsumer.cpp b/lib/Frontend/PrintingDiagnosticConsumer.cpp index 00806723d8c34..79120207045e8 100644 --- a/lib/Frontend/PrintingDiagnosticConsumer.cpp +++ b/lib/Frontend/PrintingDiagnosticConsumer.cpp @@ -48,20 +48,20 @@ void PrintingDiagnosticConsumer::handleDiagnostic(SourceManager &SM, #if SWIFT_BUILD_SWIFT_SYNTAX // Use the swift-syntax formatter. auto bufferStack = DiagnosticBridge::getSourceBufferStack(SM, Info.Loc); - if (!bufferStack.empty()) { - if (Info.Kind != DiagnosticKind::Note) - DiagBridge.flush(Stream, /*includeTrailingBreak=*/true, - /*forceColors=*/ForceColors); + if (Info.Kind != DiagnosticKind::Note || bufferStack.empty()) + DiagBridge.flush(Stream, /*includeTrailingBreak=*/true, + /*forceColors=*/ForceColors); + if (bufferStack.empty()) { + DiagBridge.emitDiagnosticWithoutLocation(Info, Stream, ForceColors); + } else { DiagBridge.enqueueDiagnostic(SM, Info, bufferStack.front()); - break; } -#endif - + return; +#else // Fall through when we don't have the new diagnostics renderer available. - // This also happens if the location of the diagnostic is invalid, because - // the new rendered cannot cope with that. LLVM_FALLTHROUGH; +#endif } case DiagnosticOptions::FormattingStyle::LLVM: @@ -215,4 +215,6 @@ PrintingDiagnosticConsumer::PrintingDiagnosticConsumer( llvm::raw_ostream &stream) : Stream(stream) {} -PrintingDiagnosticConsumer::~PrintingDiagnosticConsumer() {} +PrintingDiagnosticConsumer::~PrintingDiagnosticConsumer() { + flush(); +}