@@ -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 );
0 commit comments