16#include "llvm/ADT/SmallString.h"
17#include "llvm/ADT/StringExtras.h"
18#include "llvm/Support/ConvertUTF.h"
19#include "llvm/Support/ErrorHandling.h"
20#include "llvm/Support/Locale.h"
21#include "llvm/Support/Path.h"
22#include "llvm/Support/raw_ostream.h"
28static const enum raw_ostream::Colors
noteColor = raw_ostream::CYAN;
39static const enum raw_ostream::Colors
errorColor = raw_ostream::RED;
40static const enum raw_ostream::Colors
fatalColor = raw_ostream::RED;
43 raw_ostream::SAVEDCOLOR;
49static constexpr raw_ostream::Colors
CommentColor = raw_ostream::YELLOW;
50static constexpr raw_ostream::Colors
LiteralColor = raw_ostream::GREEN;
51static constexpr raw_ostream::Colors
KeywordColor = raw_ostream::BLUE;
58 OS << Str.slice(0, Pos);
59 if (Pos == StringRef::npos)
62 Str = Str.substr(Pos + 1);
80 if (SourceLine[--i]==
'\t')
106static std::pair<SmallString<16>,
bool>
109 assert(I &&
"I must not be null");
110 assert(*I < SourceLine.size() &&
"must point to a valid index");
112 if (SourceLine[*I] ==
'\t') {
114 "Invalid -ftabstop value");
116 unsigned NumSpaces = TabStop - (Col % TabStop);
117 assert(0 < NumSpaces && NumSpaces <= TabStop
118 &&
"Invalid computation of space amt");
122 ExpandedTab.assign(NumSpaces,
' ');
123 return std::make_pair(ExpandedTab,
true);
126 const unsigned char *
Begin = SourceLine.bytes_begin() + *I;
129 if (*
Begin < 0x80 && llvm::sys::locale::isPrint(*
Begin)) {
133 unsigned CharSize = llvm::getNumBytesForUTF8(*
Begin);
134 const unsigned char *End =
Begin + CharSize;
137 if (End <= SourceLine.bytes_end() && llvm::isLegalUTF8Sequence(
Begin, End)) {
139 llvm::UTF32 *CPtr = &
C;
142 unsigned char const *OriginalBegin =
Begin;
143 llvm::ConversionResult Res = llvm::ConvertUTF8toUTF32(
144 &
Begin, End, &CPtr, CPtr + 1, llvm::strictConversion);
146 assert(Res == llvm::conversionOK);
147 assert(OriginalBegin <
Begin);
148 assert(
unsigned(
Begin - OriginalBegin) == CharSize);
150 (*I) += (
Begin - OriginalBegin);
153 if (llvm::sys::locale::isPrint(
C))
159 Str.insert(Str.begin() + 3, llvm::hexdigit(
C % 16));
162 while (Str.size() < 8)
163 Str.insert(Str.begin() + 3, llvm::hexdigit(0));
164 return std::make_pair(Str,
false);
169 unsigned char Byte = SourceLine[*I];
170 ExpandedByte[1] = llvm::hexdigit(Byte / 16);
171 ExpandedByte[2] = llvm::hexdigit(Byte % 16);
173 return std::make_pair(ExpandedByte,
false);
176static void expandTabs(std::string &SourceLine,
unsigned TabStop) {
177 size_t I = SourceLine.size();
180 if (SourceLine[I] !=
'\t')
183 auto [Str, Printable] =
185 SourceLine.replace(I, 1, Str.c_str());
228 assert(BytesOut.empty());
229 assert(ColumnsOut.empty());
231 if (SourceLine.empty()) {
232 BytesOut.resize(1u, 0);
233 ColumnsOut.resize(1u, 0);
237 ColumnsOut.resize(SourceLine.size() + 1, -1);
241 while (I < SourceLine.size()) {
242 ColumnsOut[I] = Columns;
243 BytesOut.resize(Columns + 1, -1);
245 auto [Str, Printable] =
247 Columns += llvm::sys::locale::columnWidth(Str);
250 ColumnsOut.back() = Columns;
251 BytesOut.resize(Columns + 1, -1);
256struct SourceColumnMap {
257 SourceColumnMap(StringRef SourceLine,
unsigned TabStop)
258 : m_SourceLine(SourceLine) {
262 assert(m_byteToColumn.size()==SourceLine.size()+1);
263 assert(0 < m_byteToColumn.size() && 0 < m_columnToByte.size());
264 assert(m_byteToColumn.size()
265 ==
static_cast<unsigned>(m_columnToByte.back()+1));
266 assert(
static_cast<unsigned>(m_byteToColumn.back()+1)
267 == m_columnToByte.size());
269 int columns()
const {
return m_byteToColumn.back(); }
270 int bytes()
const {
return m_columnToByte.back(); }
274 int byteToColumn(
int n)
const {
275 assert(0<=n && n<
static_cast<int>(m_byteToColumn.size()));
276 return m_byteToColumn[n];
280 int byteToContainingColumn(
int N)
const {
281 assert(0 <= N && N <
static_cast<int>(m_byteToColumn.size()));
282 while (m_byteToColumn[N] == -1)
284 return m_byteToColumn[N];
290 int columnToByte(
int n)
const {
291 assert(0<=n && n<
static_cast<int>(m_columnToByte.size()));
292 return m_columnToByte[n];
296 int startOfNextColumn(
int N)
const {
297 assert(0 <= N && N <
static_cast<int>(m_byteToColumn.size() - 1));
298 while (byteToColumn(++N) == -1) {}
303 int startOfPreviousColumn(
int N)
const {
304 assert(0 < N && N <
static_cast<int>(m_byteToColumn.size()));
305 while (byteToColumn(--N) == -1) {}
309 StringRef getSourceLine()
const {
314 const std::string m_SourceLine;
323 std::string &CaretLine,
324 std::string &FixItInsertionLine,
326 const SourceColumnMap &map) {
327 unsigned CaretColumns = CaretLine.size();
328 unsigned FixItColumns = llvm::sys::locale::columnWidth(FixItInsertionLine);
329 unsigned MaxColumns = std::max(
static_cast<unsigned>(map.columns()),
330 std::max(CaretColumns, FixItColumns));
332 if (MaxColumns <= Columns)
336 assert(llvm::none_of(CaretLine, [](
char c) {
return c <
' ' ||
'~' <
c; }));
340 unsigned CaretStart = 0, CaretEnd = CaretLine.size();
341 for (; CaretStart != CaretEnd; ++CaretStart)
345 for (; CaretEnd != CaretStart; --CaretEnd)
354 if (!FixItInsertionLine.empty()) {
355 unsigned FixItStart = 0, FixItEnd = FixItInsertionLine.size();
356 for (; FixItStart != FixItEnd; ++FixItStart)
360 for (; FixItEnd != FixItStart; --FixItEnd)
367 unsigned FixItStartCol = FixItStart;
369 = llvm::sys::locale::columnWidth(FixItInsertionLine.substr(0, FixItEnd));
371 CaretStart = std::min(FixItStartCol, CaretStart);
372 CaretEnd = std::max(FixItEndCol, CaretEnd);
378 while (
static_cast<int>(CaretEnd) < map.columns() &&
379 -1 == map.columnToByte(CaretEnd))
382 assert((
static_cast<int>(CaretStart) > map.columns() ||
383 -1!=map.columnToByte(CaretStart)) &&
384 "CaretStart must not point to a column in the middle of a source"
386 assert((
static_cast<int>(CaretEnd) > map.columns() ||
387 -1!=map.columnToByte(CaretEnd)) &&
388 "CaretEnd must not point to a column in the middle of a source line"
396 unsigned SourceStart = map.columnToByte(std::min<unsigned>(CaretStart,
398 unsigned SourceEnd = map.columnToByte(std::min<unsigned>(CaretEnd,
401 unsigned CaretColumnsOutsideSource = CaretEnd-CaretStart
402 - (map.byteToColumn(SourceEnd)-map.byteToColumn(SourceStart));
404 char const *front_ellipse =
" ...";
405 char const *front_space =
" ";
406 char const *back_ellipse =
"...";
407 unsigned ellipses_space = strlen(front_ellipse) + strlen(back_ellipse);
409 unsigned TargetColumns = Columns;
412 if (TargetColumns > ellipses_space+CaretColumnsOutsideSource)
413 TargetColumns -= ellipses_space+CaretColumnsOutsideSource;
415 while (SourceStart>0 || SourceEnd<SourceLine.size()) {
416 bool ExpandedRegion =
false;
419 unsigned NewStart = map.startOfPreviousColumn(SourceStart);
425 NewStart = map.startOfPreviousColumn(NewStart);
429 unsigned Prev = map.startOfPreviousColumn(NewStart);
435 assert(map.byteToColumn(NewStart) != -1);
436 unsigned NewColumns = map.byteToColumn(SourceEnd) -
437 map.byteToColumn(NewStart);
438 if (NewColumns <= TargetColumns) {
439 SourceStart = NewStart;
440 ExpandedRegion =
true;
444 if (SourceEnd<SourceLine.size()) {
445 unsigned NewEnd = map.startOfNextColumn(SourceEnd);
450 while (NewEnd < SourceLine.size() &&
isWhitespace(SourceLine[NewEnd]))
451 NewEnd = map.startOfNextColumn(NewEnd);
454 while (NewEnd < SourceLine.size() &&
isWhitespace(SourceLine[NewEnd]))
455 NewEnd = map.startOfNextColumn(NewEnd);
457 assert(map.byteToColumn(NewEnd) != -1);
458 unsigned NewColumns = map.byteToColumn(NewEnd) -
459 map.byteToColumn(SourceStart);
460 if (NewColumns <= TargetColumns) {
462 ExpandedRegion =
true;
470 CaretStart = map.byteToColumn(SourceStart);
471 CaretEnd = map.byteToColumn(SourceEnd) + CaretColumnsOutsideSource;
475 assert(CaretStart!=(
unsigned)-1 && CaretEnd!=(
unsigned)-1 &&
476 SourceStart!=(
unsigned)-1 && SourceEnd!=(
unsigned)-1);
477 assert(SourceStart <= SourceEnd);
478 assert(CaretStart <= CaretEnd);
480 unsigned BackColumnsRemoved
481 = map.byteToColumn(SourceLine.size())-map.byteToColumn(SourceEnd);
482 unsigned FrontColumnsRemoved = CaretStart;
483 unsigned ColumnsKept = CaretEnd-CaretStart;
486 assert(FrontColumnsRemoved+ColumnsKept+BackColumnsRemoved > Columns);
490 if (BackColumnsRemoved > strlen(back_ellipse))
491 SourceLine.replace(SourceEnd, std::string::npos, back_ellipse);
494 if (FrontColumnsRemoved+ColumnsKept <= Columns)
498 if (FrontColumnsRemoved > strlen(front_ellipse)) {
499 SourceLine.replace(0, SourceStart, front_ellipse);
500 CaretLine.replace(0, CaretStart, front_space);
501 if (!FixItInsertionLine.empty())
502 FixItInsertionLine.replace(0, CaretStart, front_space);
526 case '\'':
return '\'';
527 case '`':
return '\'';
528 case '"':
return '"';
529 case '(':
return ')';
530 case '[':
return ']';
531 case '{':
return '}';
544 unsigned Length,
unsigned Column,
546 assert(Start < Str.size() &&
"Invalid start position!");
547 unsigned End = Start + 1;
550 if (End == Str.size())
566 PunctuationEndStack.push_back(EndPunct);
567 while (End < Length && !PunctuationEndStack.empty()) {
568 if (Str[End] == PunctuationEndStack.back())
569 PunctuationEndStack.pop_back();
571 PunctuationEndStack.push_back(SubEndPunct);
580 unsigned PunctWordLength = End - Start;
582 Column + PunctWordLength <= Columns ||
585 PunctWordLength < Columns/3)
609 unsigned Column,
bool Bold) {
610 const unsigned Length = std::min(Str.find(
'\n'), Str.size());
611 bool TextNormal =
true;
613 bool Wrapped =
false;
614 for (
unsigned WordStart = 0, WordEnd; WordStart < Length;
615 WordStart = WordEnd) {
618 if (WordStart == Length)
625 unsigned WordLength = WordEnd - WordStart;
626 if (
Column + WordLength < Columns) {
651 assert(TextNormal &&
"Text highlighted at end of diagnostic message.");
667 uint64_t StartOfLocationInfo = OS.tell();
680 Message, OS.tell() - StartOfLocationInfo,
692 llvm_unreachable(
"Invalid diagnostic type");
703 llvm_unreachable(
"Invalid diagnostic type");
719 unsigned CurrentColumn,
734 assert(
Normal &&
"Formatting should have returned to normal");
764 TmpFilename =
File->getName();
765 llvm::sys::fs::make_absolute(TmpFilename);
766 llvm::sys::path::native(TmpFilename);
767 llvm::sys::path::remove_dots(TmpFilename,
true);
768 Filename = StringRef(TmpFilename.data(), TmpFilename.size());
791 emitFilename(FE->getName(),
Loc.getManager());
797 unsigned LineNo = PLoc.
getLine();