[Diagnostics] Use the swift-syntax formatting for invalid source locations, too

The diagnostics formatter from swift-syntax previously only handled
fully-formed diagnostics anchored at a particular syntax node.
Therefore, the compiler would fall back to the existing LLVM-based
diagnostic formatter for diagnostics that had no source location.

Adopt new API in the swift-syntax diagnostics formatter that renders a
diagnostic message without requiring source location information, so
that we consistently use the swift-syntax formatter when it is
selected (which is the default).
This commit is contained in:
Doug Gregor
2025-04-16 17:21:43 -07:00
parent 0ec146a687
commit 235242e8b3
5 changed files with 128 additions and 28 deletions

View File

@@ -46,6 +46,10 @@ public:
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);

View File

@@ -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);

View File

@@ -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<BridgedCharSourceRange, 2> 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 << "<unknown>:0: ";
out.write(renderedString.data(), renderedString.size());
swift_ASTGen_freeBridgedString(renderedString);
out << "\n";
}
}
void DiagnosticBridge::enqueueDiagnostic(SourceManager &SM,
const DiagnosticInfo &Info,
unsigned innermostBufferID) {

View File

@@ -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<BridgedStringRef>
) {
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(

View File

@@ -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();
}