[clang] [clang] Adjust TextDiagnostic style ranges for interesting source region (PR #164941)
Timm Baeder via cfe-commits
cfe-commits at lists.llvm.org
Fri Oct 24 01:01:56 PDT 2025
Timm =?utf-8?q?Bäder?= <tbaeder at redhat.com>
Message-ID: <llvm.org/llvm/llvm-project/pull/164941 at github.com>
In-Reply-To:
https://github.com/tbaederr created https://github.com/llvm/llvm-project/pull/164941
Need to merge https://github.com/llvm/llvm-project/pull/164935 first.
After:
<img width="1904" height="186" alt="Screenshot From 2025-10-24 09-59-40" src="https://github.com/user-attachments/assets/c860227f-50c5-4afe-a959-83e3452fc72d" />
<img width="1366" height="204" alt="Screenshot From 2025-10-24 09-59-12" src="https://github.com/user-attachments/assets/450bffec-b4b2-465c-b435-bddf8ebdbd32" />
<img width="1310" height="204" alt="Screenshot From 2025-10-24 09-58-53" src="https://github.com/user-attachments/assets/8015ec6f-e032-4f0b-b55c-b2c718d14f6b" />
>From 9a1d196f43eceeab32e18e15cf3a381fc7333556 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Timm=20B=C3=A4der?= <tbaeder at redhat.com>
Date: Fri, 24 Oct 2025 08:32:28 +0200
Subject: [PATCH 1/2] [clang] Use a formatted_raw_ostream in TextDiagnostic
Let's see about the CI.
---
clang/include/clang/Frontend/TextDiagnostic.h | 28 +++++-----
.../clang/Frontend/TextDiagnosticPrinter.h | 3 +-
clang/lib/Frontend/TextDiagnostic.cpp | 54 +++++++++++--------
clang/lib/Frontend/TextDiagnosticPrinter.cpp | 1 +
4 files changed, 51 insertions(+), 35 deletions(-)
diff --git a/clang/include/clang/Frontend/TextDiagnostic.h b/clang/include/clang/Frontend/TextDiagnostic.h
index e2e88d4d648a2..bf25f7660eca2 100644
--- a/clang/include/clang/Frontend/TextDiagnostic.h
+++ b/clang/include/clang/Frontend/TextDiagnostic.h
@@ -16,10 +16,12 @@
#define LLVM_CLANG_FRONTEND_TEXTDIAGNOSTIC_H
#include "clang/Frontend/DiagnosticRenderer.h"
-#include "llvm/Support/raw_ostream.h"
+#include "llvm/Support/FormattedStream.h"
namespace clang {
+using formatted_raw_ostream = llvm::formatted_raw_ostream;
+
/// Class to encapsulate the logic for formatting and printing a textual
/// diagnostic message.
///
@@ -33,11 +35,11 @@ namespace clang {
/// DiagnosticClient is implemented through this class as is diagnostic
/// printing coming out of libclang.
class TextDiagnostic : public DiagnosticRenderer {
- raw_ostream &OS;
+ formatted_raw_ostream &OS;
const Preprocessor *PP;
public:
- TextDiagnostic(raw_ostream &OS, const LangOptions &LangOpts,
+ TextDiagnostic(formatted_raw_ostream &OS, const LangOptions &LangOpts,
DiagnosticOptions &DiagOpts, const Preprocessor *PP = nullptr);
~TextDiagnostic() override;
@@ -45,23 +47,24 @@ class TextDiagnostic : public DiagnosticRenderer {
struct StyleRange {
unsigned Start;
unsigned End;
- enum llvm::raw_ostream::Colors Color;
- StyleRange(unsigned S, unsigned E, enum llvm::raw_ostream::Colors C)
- : Start(S), End(E), Color(C){};
+ enum llvm::formatted_raw_ostream::Colors Color;
+ StyleRange(unsigned S, unsigned E,
+ enum llvm::formatted_raw_ostream::Colors C)
+ : Start(S), End(E), Color(C) {};
};
- /// Print the diagonstic level to a raw_ostream.
+ /// Print the diagonstic level to a formatted_raw_ostream.
///
/// This is a static helper that handles colorizing the level and formatting
/// it into an arbitrary output stream. This is used internally by the
/// TextDiagnostic emission code, but it can also be used directly by
/// consumers that don't have a source manager or other state that the full
/// TextDiagnostic logic requires.
- static void printDiagnosticLevel(raw_ostream &OS,
+ static void printDiagnosticLevel(formatted_raw_ostream &OS,
DiagnosticsEngine::Level Level,
bool ShowColors);
- /// Pretty-print a diagnostic message to a raw_ostream.
+ /// Pretty-print a diagnostic message to a formatted_raw_ostream.
///
/// This is a static helper to handle the line wrapping, colorizing, and
/// rendering of a diagnostic message to a particular ostream. It is
@@ -77,9 +80,10 @@ class TextDiagnostic : public DiagnosticRenderer {
/// \param Columns The number of columns to use in line-wrapping, 0 disables
/// all line-wrapping.
/// \param ShowColors Enable colorizing of the message.
- static void printDiagnosticMessage(raw_ostream &OS, bool IsSupplemental,
- StringRef Message, unsigned CurrentColumn,
- unsigned Columns, bool ShowColors);
+ static void printDiagnosticMessage(formatted_raw_ostream &OS,
+ bool IsSupplemental, StringRef Message,
+ unsigned CurrentColumn, unsigned Columns,
+ bool ShowColors);
protected:
void emitDiagnosticMessage(FullSourceLoc Loc, PresumedLoc PLoc,
diff --git a/clang/include/clang/Frontend/TextDiagnosticPrinter.h b/clang/include/clang/Frontend/TextDiagnosticPrinter.h
index dd1ca6b2248b7..d7d892f2b80e7 100644
--- a/clang/include/clang/Frontend/TextDiagnosticPrinter.h
+++ b/clang/include/clang/Frontend/TextDiagnosticPrinter.h
@@ -17,6 +17,7 @@
#include "clang/Basic/Diagnostic.h"
#include "clang/Basic/LLVM.h"
#include "llvm/ADT/IntrusiveRefCntPtr.h"
+#include "llvm/Support/FormattedStream.h"
#include <memory>
namespace clang {
@@ -25,7 +26,7 @@ class LangOptions;
class TextDiagnostic;
class TextDiagnosticPrinter : public DiagnosticConsumer {
- raw_ostream &OS;
+ llvm::formatted_raw_ostream OS;
DiagnosticOptions &DiagOpts;
/// Handle to the currently active text diagnostic emitter.
diff --git a/clang/lib/Frontend/TextDiagnostic.cpp b/clang/lib/Frontend/TextDiagnostic.cpp
index 58885712fbdcc..ad642f39aee17 100644
--- a/clang/lib/Frontend/TextDiagnostic.cpp
+++ b/clang/lib/Frontend/TextDiagnostic.cpp
@@ -17,7 +17,6 @@
#include "llvm/Support/ConvertUTF.h"
#include "llvm/Support/ErrorHandling.h"
#include "llvm/Support/Locale.h"
-#include "llvm/Support/raw_ostream.h"
#include <algorithm>
#include <optional>
@@ -49,7 +48,7 @@ static constexpr raw_ostream::Colors LiteralColor = raw_ostream::GREEN;
static constexpr raw_ostream::Colors KeywordColor = raw_ostream::BLUE;
/// Add highlights to differences in template strings.
-static void applyTemplateHighlighting(raw_ostream &OS, StringRef Str,
+static void applyTemplateHighlighting(formatted_raw_ostream &OS, StringRef Str,
bool &Normal, bool Bold) {
while (true) {
size_t Pos = Str.find(ToggleHighlight);
@@ -59,11 +58,11 @@ static void applyTemplateHighlighting(raw_ostream &OS, StringRef Str,
Str = Str.substr(Pos + 1);
if (Normal)
- OS.changeColor(templateColor, true);
+ OS.changeColor(templateColor, true, false);
else {
OS.resetColor();
if (Bold)
- OS.changeColor(savedColor, true);
+ OS.changeColor(savedColor, true, false);
}
Normal = !Normal;
}
@@ -603,8 +602,8 @@ static unsigned findEndOfWord(unsigned Start, StringRef Str,
/// \param Bold if the current text should be bold
/// \returns true if word-wrapping was required, or false if the
/// string fit on the first line.
-static bool printWordWrapped(raw_ostream &OS, StringRef Str, unsigned Columns,
- unsigned Column, bool Bold) {
+static bool printWordWrapped(formatted_raw_ostream &OS, StringRef Str,
+ unsigned Columns, unsigned Column, bool Bold) {
const unsigned Length = std::min(Str.find('\n'), Str.size());
bool TextNormal = true;
@@ -651,7 +650,8 @@ static bool printWordWrapped(raw_ostream &OS, StringRef Str, unsigned Columns,
return Wrapped;
}
-TextDiagnostic::TextDiagnostic(raw_ostream &OS, const LangOptions &LangOpts,
+TextDiagnostic::TextDiagnostic(formatted_raw_ostream &OS,
+ const LangOptions &LangOpts,
DiagnosticOptions &DiagOpts,
const Preprocessor *PP)
: DiagnosticRenderer(LangOpts, DiagOpts), OS(OS), PP(PP) {}
@@ -662,7 +662,7 @@ void TextDiagnostic::emitDiagnosticMessage(
FullSourceLoc Loc, PresumedLoc PLoc, DiagnosticsEngine::Level Level,
StringRef Message, ArrayRef<clang::CharSourceRange> Ranges,
DiagOrStoredDiag D) {
- uint64_t StartOfLocationInfo = OS.tell();
+ uint64_t StartOfLocationInfo = OS.getColumn();
// Emit the location of this particular diagnostic.
if (Loc.isValid())
@@ -675,12 +675,12 @@ void TextDiagnostic::emitDiagnosticMessage(
printDiagnosticLevel(OS, Level, DiagOpts.ShowColors);
printDiagnosticMessage(OS,
/*IsSupplemental*/ Level == DiagnosticsEngine::Note,
- Message, OS.tell() - StartOfLocationInfo,
+ Message, OS.getColumn() - StartOfLocationInfo,
DiagOpts.MessageLength, DiagOpts.ShowColors);
}
/*static*/ void
-TextDiagnostic::printDiagnosticLevel(raw_ostream &OS,
+TextDiagnostic::printDiagnosticLevel(formatted_raw_ostream &OS,
DiagnosticsEngine::Level Level,
bool ShowColors) {
if (ShowColors) {
@@ -688,11 +688,21 @@ TextDiagnostic::printDiagnosticLevel(raw_ostream &OS,
switch (Level) {
case DiagnosticsEngine::Ignored:
llvm_unreachable("Invalid diagnostic type");
- case DiagnosticsEngine::Note: OS.changeColor(noteColor, true); break;
- case DiagnosticsEngine::Remark: OS.changeColor(remarkColor, true); break;
- case DiagnosticsEngine::Warning: OS.changeColor(warningColor, true); break;
- case DiagnosticsEngine::Error: OS.changeColor(errorColor, true); break;
- case DiagnosticsEngine::Fatal: OS.changeColor(fatalColor, true); break;
+ case DiagnosticsEngine::Note:
+ OS.changeColor(noteColor, true, false);
+ break;
+ case DiagnosticsEngine::Remark:
+ OS.changeColor(remarkColor, true, false);
+ break;
+ case DiagnosticsEngine::Warning:
+ OS.changeColor(warningColor, true, false);
+ break;
+ case DiagnosticsEngine::Error:
+ OS.changeColor(errorColor, true, false);
+ break;
+ case DiagnosticsEngine::Fatal:
+ OS.changeColor(fatalColor, true, false);
+ break;
}
}
@@ -711,7 +721,7 @@ TextDiagnostic::printDiagnosticLevel(raw_ostream &OS,
}
/*static*/
-void TextDiagnostic::printDiagnosticMessage(raw_ostream &OS,
+void TextDiagnostic::printDiagnosticMessage(formatted_raw_ostream &OS,
bool IsSupplemental,
StringRef Message,
unsigned CurrentColumn,
@@ -720,7 +730,7 @@ void TextDiagnostic::printDiagnosticMessage(raw_ostream &OS,
if (ShowColors && !IsSupplemental) {
// Print primary diagnostic messages in bold and without color, to visually
// indicate the transition from continuation notes and other output.
- OS.changeColor(savedColor, true);
+ OS.changeColor(savedColor, true, false);
Bold = true;
}
@@ -798,7 +808,7 @@ void TextDiagnostic::emitDiagnosticLoc(FullSourceLoc Loc, PresumedLoc PLoc,
return;
if (DiagOpts.ShowColors)
- OS.changeColor(savedColor, true);
+ OS.changeColor(savedColor, true, false);
emitFilename(PLoc.getFilename(), Loc.getManager());
switch (DiagOpts.getFormat()) {
@@ -1423,7 +1433,7 @@ void TextDiagnostic::emitSnippetAndCaret(
if (!CaretLine.empty()) {
indentForLineNumbers();
if (DiagOpts.ShowColors)
- OS.changeColor(caretColor, true);
+ OS.changeColor(caretColor, true, false);
OS << CaretLine << '\n';
if (DiagOpts.ShowColors)
OS.resetColor();
@@ -1433,7 +1443,7 @@ void TextDiagnostic::emitSnippetAndCaret(
indentForLineNumbers();
if (DiagOpts.ShowColors)
// Print fixit line in color
- OS.changeColor(fixitColor, false);
+ OS.changeColor(fixitColor, false, false);
if (DiagOpts.ShowSourceRanges)
OS << ' ';
OS << FixItInsertionLine << '\n';
@@ -1459,7 +1469,7 @@ void TextDiagnostic::emitSnippet(StringRef SourceLine,
// Print the source line one character at a time.
bool PrintReversed = false;
- std::optional<llvm::raw_ostream::Colors> CurrentColor;
+ std::optional<llvm::formatted_raw_ostream::Colors> CurrentColor;
size_t I = 0;
while (I < SourceLine.size()) {
auto [Str, WasPrintable] =
@@ -1485,7 +1495,7 @@ void TextDiagnostic::emitSnippet(StringRef SourceLine,
if (CharStyle != Styles.end()) {
if (!CurrentColor ||
(CurrentColor && *CurrentColor != CharStyle->Color)) {
- OS.changeColor(CharStyle->Color, false);
+ OS.changeColor(CharStyle->Color, false, false);
CurrentColor = CharStyle->Color;
}
} else if (CurrentColor) {
diff --git a/clang/lib/Frontend/TextDiagnosticPrinter.cpp b/clang/lib/Frontend/TextDiagnosticPrinter.cpp
index 83fd70e5f99f9..a4165b2593ccc 100644
--- a/clang/lib/Frontend/TextDiagnosticPrinter.cpp
+++ b/clang/lib/Frontend/TextDiagnosticPrinter.cpp
@@ -16,6 +16,7 @@
#include "clang/Frontend/TextDiagnostic.h"
#include "clang/Lex/Lexer.h"
#include "llvm/Support/ErrorHandling.h"
+#include "llvm/Support/FormattedStream.h"
#include "llvm/Support/raw_ostream.h"
using namespace clang;
>From 4f04fac8285ded5752606869ab6b84c15554cee4 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Timm=20B=C3=A4der?= <tbaeder at redhat.com>
Date: Fri, 24 Oct 2025 09:59:45 +0200
Subject: [PATCH 2/2] [clang] Adjust TextDiagnostic style ranges for
interesting source region
---
clang/include/clang/Frontend/TextDiagnostic.h | 2 +-
clang/lib/Frontend/TextDiagnostic.cpp | 59 +++++++++++++++----
2 files changed, 50 insertions(+), 11 deletions(-)
diff --git a/clang/include/clang/Frontend/TextDiagnostic.h b/clang/include/clang/Frontend/TextDiagnostic.h
index bf25f7660eca2..5ea55641e0720 100644
--- a/clang/include/clang/Frontend/TextDiagnostic.h
+++ b/clang/include/clang/Frontend/TextDiagnostic.h
@@ -47,7 +47,7 @@ class TextDiagnostic : public DiagnosticRenderer {
struct StyleRange {
unsigned Start;
unsigned End;
- enum llvm::formatted_raw_ostream::Colors Color;
+ enum llvm::raw_ostream::Colors Color;
StyleRange(unsigned S, unsigned E,
enum llvm::formatted_raw_ostream::Colors C)
: Start(S), End(E), Color(C) {};
diff --git a/clang/lib/Frontend/TextDiagnostic.cpp b/clang/lib/Frontend/TextDiagnostic.cpp
index ad642f39aee17..94abaabc8868f 100644
--- a/clang/lib/Frontend/TextDiagnostic.cpp
+++ b/clang/lib/Frontend/TextDiagnostic.cpp
@@ -316,11 +316,11 @@ struct SourceColumnMap {
/// When the source code line we want to print is too long for
/// the terminal, select the "interesting" region.
-static void selectInterestingSourceRegion(std::string &SourceLine,
- std::string &CaretLine,
- std::string &FixItInsertionLine,
- unsigned Columns,
- const SourceColumnMap &map) {
+static void selectInterestingSourceRegion(
+ std::string &SourceLine, std::string &CaretLine,
+ std::string &FixItInsertionLine, unsigned Columns,
+ const SourceColumnMap &map,
+ SmallVector<clang::TextDiagnostic::StyleRange> &Styles) {
unsigned CaretColumns = CaretLine.size();
unsigned FixItColumns = llvm::sys::locale::columnWidth(FixItInsertionLine);
unsigned MaxColumns = std::max(static_cast<unsigned>(map.columns()),
@@ -409,10 +409,10 @@ static void selectInterestingSourceRegion(std::string &SourceLine,
if (TargetColumns > ellipses_space+CaretColumnsOutsideSource)
TargetColumns -= ellipses_space+CaretColumnsOutsideSource;
- while (SourceStart>0 || SourceEnd<SourceLine.size()) {
+ while (SourceStart > 0 || SourceEnd < SourceLine.size()) {
bool ExpandedRegion = false;
- if (SourceStart>0) {
+ if (SourceStart > 0) {
unsigned NewStart = map.startOfPreviousColumn(SourceStart);
// Skip over any whitespace we see here; we're looking for
@@ -487,8 +487,41 @@ static void selectInterestingSourceRegion(std::string &SourceLine,
if (BackColumnsRemoved > strlen(back_ellipse))
SourceLine.replace(SourceEnd, std::string::npos, back_ellipse);
+ // Since we've modified the SourceLine, we also need to adjust the line's
+ // highlighting information. In particular, if we've removed
+ // from the front of the line, we need to move the style ranges to the
+ // left and remove unneeded ranges.
+ unsigned FrontDiff = FrontColumnsRemoved > strlen(front_ellipse)
+ ? FrontColumnsRemoved - strlen(front_ellipse)
+ : 0;
+ unsigned CodeStart =
+ FrontColumnsRemoved > strlen(front_ellipse) ? strlen(front_ellipse) : 0;
+ unsigned CodeEnd = CodeStart + ColumnsKept;
+ for (auto &R : Styles) {
+ // Style ranges too far left. Just move them where they don't bother.
+ if (R.End < FrontDiff) {
+ R.Start = R.End = std::numeric_limits<unsigned>::max();
+ continue;
+ }
+ // Move them left. (Note that this can wrap R.Start, but that doesn't
+ // matter).
+ R.Start -= FrontDiff;
+ R.End -= FrontDiff;
+
+ // Style ranges too far to the right.
+ if (R.Start >= (ColumnsKept + strlen(front_ellipse))) {
+ R.Start = R.End = std::numeric_limits<unsigned>::max();
+ continue;
+ }
+
+ // If the range overlaps the end of the code, don't leak into the back
+ // ellipse.
+ if (R.Start < CodeEnd && R.End > CodeEnd)
+ R.End = CodeEnd;
+ }
+
// If that's enough then we're done
- if (FrontColumnsRemoved+ColumnsKept <= Columns)
+ if (FrontColumnsRemoved + ColumnsKept <= Columns)
return;
// Otherwise remove the front as well
@@ -1355,6 +1388,11 @@ void TextDiagnostic::emitSnippetAndCaret(
OS.indent(MaxLineNoDisplayWidth + 2) << "| ";
};
+ unsigned Columns = DiagOpts.MessageLength;
+ // If we don't have enough columns available, just abort now.
+ if (Columns && Columns <= (MaxLineNoDisplayWidth + 4))
+ return;
+
// Prepare source highlighting information for the lines we're about to
// emit, starting from the first line.
std::unique_ptr<SmallVector<StyleRange>[]> SourceStyles =
@@ -1412,10 +1450,11 @@ void TextDiagnostic::emitSnippetAndCaret(
// If the source line is too long for our terminal, select only the
// "interesting" source region within that line.
- unsigned Columns = DiagOpts.MessageLength;
if (Columns)
selectInterestingSourceRegion(SourceLine, CaretLine, FixItInsertionLine,
- Columns, sourceColMap);
+ (Columns - (MaxLineNoDisplayWidth + 4)),
+ sourceColMap,
+ SourceStyles[LineNo - Lines.first]);
// If we are in -fdiagnostics-print-source-range-info mode, we are trying
// to produce easily machine parsable output. Add a space before the
More information about the cfe-commits
mailing list