Skip to content

Commit 613c6de

Browse files
authored
[clang] Adjust TextDiagnostic style ranges for interesting source region (#164941)
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" />
1 parent 47c54d5 commit 613c6de

File tree

2 files changed

+87
-19
lines changed

2 files changed

+87
-19
lines changed

clang/lib/Frontend/TextDiagnostic.cpp

Lines changed: 57 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -349,14 +349,13 @@ struct SourceColumnMap {
349349

350350
/// When the source code line we want to print is too long for
351351
/// the terminal, select the "interesting" region.
352-
static void selectInterestingSourceRegion(std::string &SourceLine,
353-
std::string &CaretLine,
354-
std::string &FixItInsertionLine,
355-
Columns NonGutterColumns,
356-
const SourceColumnMap &Map) {
357-
Columns CaretColumns = Columns(CaretLine.size());
358-
Columns FixItColumns =
359-
Columns(llvm::sys::locale::columnWidth(FixItInsertionLine));
352+
static void selectInterestingSourceRegion(
353+
std::string &SourceLine, std::string &CaretLine,
354+
std::string &FixItInsertionLine, Columns NonGutterColumns,
355+
const SourceColumnMap &Map,
356+
SmallVectorImpl<clang::TextDiagnostic::StyleRange> &Styles) {
357+
Columns CaretColumns = CaretLine.size();
358+
Columns FixItColumns = llvm::sys::locale::columnWidth(FixItInsertionLine);
360359
Columns MaxColumns =
361360
std::max({Map.columns().V, CaretColumns.V, FixItColumns.V});
362361
// if the number of columns is less than the desired number we're done
@@ -369,13 +368,11 @@ static void selectInterestingSourceRegion(std::string &SourceLine,
369368
// Find the slice that we need to display the full caret line
370369
// correctly.
371370
Columns CaretStart = 0, CaretEnd = CaretLine.size();
372-
for (; CaretStart != CaretEnd; CaretStart = CaretStart.next())
373-
if (!isWhitespace(CaretLine[CaretStart.V]))
374-
break;
371+
while (CaretStart != CaretEnd && isWhitespace(CaretLine[CaretStart.V]))
372+
CaretStart = CaretStart.next();
375373

376-
for (; CaretEnd != CaretStart; CaretEnd = CaretEnd.prev())
377-
if (!isWhitespace(CaretLine[CaretEnd.V - 1]))
378-
break;
374+
while (CaretEnd != CaretStart && isWhitespace(CaretLine[CaretEnd.V]))
375+
CaretEnd = CaretEnd.prev();
379376

380377
// caret has already been inserted into CaretLine so the above whitespace
381378
// check is guaranteed to include the caret
@@ -516,13 +513,45 @@ static void selectInterestingSourceRegion(std::string &SourceLine,
516513
assert(FrontColumnsRemoved + ColumnsKept + BackColumnsRemoved >
517514
NonGutterColumns);
518515

516+
// Since we've modified the SourceLine, we also need to adjust the line's
517+
// highlighting information. In particular, if we've removed
518+
// from the front of the line, we need to move the style ranges to the
519+
// left and remove unneeded ranges.
520+
// Note in particular that variables like CaretEnd are defined in the
521+
// CaretLine, which only contains ASCII, while the style ranges are defined in
522+
// the source line, where we have to care for the byte-index != column-index
523+
// case.
524+
Bytes BytesRemoved =
525+
FrontColumnsRemoved > FrontEllipse.size()
526+
? (Map.columnToByte(FrontColumnsRemoved) - Bytes(FrontEllipse.size()))
527+
: 0;
528+
Bytes CodeEnd =
529+
CaretEnd < Map.columns() ? Map.columnToByte(CaretEnd.V) : CaretEnd.V;
530+
for (TextDiagnostic::StyleRange &R : Styles) {
531+
// Remove style ranges before and after the new truncated snippet.
532+
if (R.Start >= static_cast<unsigned>(CodeEnd.V) ||
533+
R.End < static_cast<unsigned>(BytesRemoved.V)) {
534+
R.Start = R.End = std::numeric_limits<int>::max();
535+
continue;
536+
}
537+
// Move them left. (Note that this can wrap R.Start, but that doesn't
538+
// matter).
539+
R.Start -= BytesRemoved.V;
540+
R.End -= BytesRemoved.V;
541+
542+
// Don't leak into the ellipse at the end.
543+
if (R.Start < static_cast<unsigned>(CodeEnd.V) &&
544+
R.End > static_cast<unsigned>(CodeEnd.V))
545+
R.End = CodeEnd.V + 1; // R.End is inclusive.
546+
}
547+
519548
// The line needs some truncation, and we'd prefer to keep the front
520549
// if possible, so remove the back
521550
if (BackColumnsRemoved > Columns(BackEllipse.size()))
522551
SourceLine.replace(SourceEnd.V, std::string::npos, BackEllipse);
523552

524553
// If that's enough then we're done
525-
if (FrontColumnsRemoved + ColumnsKept <= Columns(NonGutterColumns))
554+
if (FrontColumnsRemoved + ColumnsKept <= NonGutterColumns)
526555
return;
527556

528557
// Otherwise remove the front as well
@@ -1391,6 +1420,11 @@ void TextDiagnostic::emitSnippetAndCaret(
13911420
OS.indent(MaxLineNoDisplayWidth + 2) << "| ";
13921421
};
13931422

1423+
Columns MessageLength = DiagOpts.MessageLength;
1424+
// If we don't have enough columns available, just abort now.
1425+
if (MessageLength != 0 && MessageLength <= Columns(MaxLineNoDisplayWidth + 4))
1426+
return;
1427+
13941428
// Prepare source highlighting information for the lines we're about to
13951429
// emit, starting from the first line.
13961430
std::unique_ptr<SmallVector<StyleRange>[]> SourceStyles =
@@ -1450,10 +1484,14 @@ void TextDiagnostic::emitSnippetAndCaret(
14501484

14511485
// If the source line is too long for our terminal, select only the
14521486
// "interesting" source region within that line.
1453-
Columns MessageLength = DiagOpts.MessageLength;
1454-
if (MessageLength.V != 0)
1487+
if (MessageLength != 0) {
1488+
Columns NonGutterColumns = MessageLength;
1489+
if (MaxLineNoDisplayWidth != 0)
1490+
NonGutterColumns -= Columns(MaxLineNoDisplayWidth + 4);
14551491
selectInterestingSourceRegion(SourceLine, CaretLine, FixItInsertionLine,
1456-
MessageLength, SourceColMap);
1492+
NonGutterColumns, SourceColMap,
1493+
SourceStyles[LineNo - Lines.first]);
1494+
}
14571495

14581496
// If we are in -fdiagnostics-print-source-range-info mode, we are trying
14591497
// to produce easily machine parsable output. Add a space before the
@@ -1508,7 +1546,7 @@ void TextDiagnostic::emitSnippet(StringRef SourceLine,
15081546
// Print the source line one character at a time.
15091547
bool PrintReversed = false;
15101548
std::optional<llvm::raw_ostream::Colors> CurrentColor;
1511-
size_t I = 0;
1549+
size_t I = 0; // Bytes.
15121550
while (I < SourceLine.size()) {
15131551
auto [Str, WasPrintable] =
15141552
printableTextForNextCharacter(SourceLine, &I, DiagOpts.TabStop);
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
// RUN: not %clang_cc1 %s -fmessage-length=40 -fcolor-diagnostics -fno-show-source-location -Wunused-value -o - 2>&1 | FileCheck %s
2+
3+
// REQUIRES: ansi-escape-sequences
4+
5+
int main() {
6+
1 + + if;
7+
// CHECK: expected expression
8+
// CHECK-NEXT: ...+ [[MAGENTA:.\[0;34m]]if[[RESET:.\[0m]];
9+
10+
/*😂*/1 + + if;
11+
// CHECK: expected expression
12+
// CHECK-NEXT: ...+ [[MAGENTA:.\[0;34m]]if[[RESET:.\[0m]];
13+
14+
a + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1;
15+
// CHECK: use of undeclared identifier
16+
// CHECK-NEXT: a + [[GREEN:.\[0;32m]]1[[RESET]] + [[GREEN]]1[[RESET]] + [[GREEN]]1[[RESET]] + [[GREEN]]1[[RESET]] + [[GREEN]]1[[RESET]] ...
17+
18+
19+
/*😂😂😂*/ a + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1;
20+
// CHECK: use of undeclared identifier
21+
// CHECK-NEXT: [[YELLOW:.\[0;33m]]/*😂😂😂*/[[RESET]] a + [[GREEN:.\[0;32m]]1[[RESET]] + [[GREEN]]1[[RESET]] ...
22+
23+
"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx";
24+
// CHECK: [[GREEN:.\[0;32m]]"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"[[RESET]];
25+
26+
"😂xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx";
27+
// CHECK: [[GREEN:.\[0;32m]]"😂xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"[[RESET]];
28+
}
29+
30+

0 commit comments

Comments
 (0)