15 #include "llvm/ADT/SmallString.h"
16 #include "llvm/ADT/StringExtras.h"
17 #include "llvm/Support/ConvertUTF.h"
18 #include "llvm/Support/ErrorHandling.h"
19 #include "llvm/Support/Locale.h"
20 #include "llvm/Support/Path.h"
21 #include "llvm/Support/raw_ostream.h"
24 using namespace clang;
38 static const enum raw_ostream::Colors
errorColor = raw_ostream::RED;
39 static const enum raw_ostream::Colors
fatalColor = raw_ostream::RED;
42 raw_ostream::SAVEDCOLOR;
46 bool &Normal,
bool Bold) {
49 OS << Str.slice(0, Pos);
50 if (Pos == StringRef::npos)
53 Str = Str.substr(Pos + 1);
71 if (SourceLine[--i]==
'\t')
97 static std::pair<SmallString<16>,
bool>
100 assert(i &&
"i must not be null");
101 assert(*i<SourceLine.size() &&
"must point to a valid index");
103 if (SourceLine[*i]==
'\t') {
105 "Invalid -ftabstop value");
107 unsigned NumSpaces = TabStop - col%TabStop;
108 assert(0 < NumSpaces && NumSpaces <= TabStop
109 &&
"Invalid computation of space amt");
113 expandedTab.assign(NumSpaces,
' ');
114 return std::make_pair(expandedTab,
true);
117 unsigned char const *begin, *end;
118 begin =
reinterpret_cast<unsigned char const *
>(&*(SourceLine.begin() + *i));
119 end = begin + (SourceLine.size() - *i);
121 if (llvm::isLegalUTF8Sequence(begin, end)) {
123 llvm::UTF32 *cptr = &
c;
124 unsigned char const *original_begin = begin;
125 unsigned char const *cp_end =
126 begin + llvm::getNumBytesForUTF8(SourceLine[*i]);
128 llvm::ConversionResult res = llvm::ConvertUTF8toUTF32(
129 &begin, cp_end, &cptr, cptr + 1, llvm::strictConversion);
131 assert(llvm::conversionOK == res);
132 assert(0 < begin-original_begin
133 &&
"we must be further along in the string now");
134 *i += begin-original_begin;
136 if (!llvm::sys::locale::isPrint(
c)) {
140 expandedCP.insert(expandedCP.begin()+3, llvm::hexdigit(
c%16));
143 while (expandedCP.size() < 8)
144 expandedCP.insert(expandedCP.begin()+3, llvm::hexdigit(0));
145 return std::make_pair(expandedCP,
false);
155 unsigned char byte = SourceLine[*i];
156 expandedByte[1] = llvm::hexdigit(
byte / 16);
157 expandedByte[2] = llvm::hexdigit(
byte % 16);
159 return std::make_pair(expandedByte,
false);
163 size_t i = SourceLine.size();
166 if (SourceLine[i]!=
'\t')
169 std::pair<SmallString<16>,
bool> res
171 SourceLine.replace(i, 1, res.first.c_str());
201 if (SourceLine.empty()) {
206 out.resize(SourceLine.size()+1, -1);
210 while (i<SourceLine.size()) {
212 std::pair<SmallString<16>,
bool> res
216 out.back() = columns;
235 if (SourceLine.empty()) {
242 while (i<SourceLine.size()) {
243 out.resize(columns+1, -1);
245 std::pair<SmallString<16>,
bool> res
249 out.resize(columns+1, -1);
254 struct SourceColumnMap {
255 SourceColumnMap(StringRef SourceLine,
unsigned TabStop)
256 : m_SourceLine(SourceLine) {
261 assert(m_byteToColumn.size()==SourceLine.size()+1);
262 assert(0 < m_byteToColumn.size() && 0 < m_columnToByte.size());
263 assert(m_byteToColumn.size()
264 ==
static_cast<unsigned>(m_columnToByte.back()+1));
265 assert(
static_cast<unsigned>(m_byteToColumn.back()+1)
266 == m_columnToByte.size());
268 int columns()
const {
return m_byteToColumn.back(); }
269 int bytes()
const {
return m_columnToByte.back(); }
274 assert(0<=n && n<
static_cast<int>(m_byteToColumn.size()));
275 return m_byteToColumn[n];
279 int byteToContainingColumn(
int N)
const {
280 assert(0 <= N && N <
static_cast<int>(m_byteToColumn.size()));
281 while (m_byteToColumn[N] == -1)
283 return m_byteToColumn[N];
290 assert(0<=n && n<
static_cast<int>(m_columnToByte.size()));
291 return m_columnToByte[n];
295 int startOfNextColumn(
int N)
const {
296 assert(0 <= N && N <
static_cast<int>(m_byteToColumn.size() - 1));
302 int startOfPreviousColumn(
int N)
const {
303 assert(0 < N && N <
static_cast<int>(m_byteToColumn.size()));
308 StringRef getSourceLine()
const {
325 const SourceColumnMap &map) {
326 unsigned CaretColumns = CaretLine.size();
328 unsigned MaxColumns =
std::max(
static_cast<unsigned>(map.columns()),
329 std::max(CaretColumns, FixItColumns));
331 if (MaxColumns <= Columns)
335 assert(CaretLine.end() ==
336 llvm::find_if(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;
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;
477 assert(CaretStart!=(
unsigned)-1 && CaretEnd!=(
unsigned)-1 &&
478 SourceStart!=(
unsigned)-1 && SourceEnd!=(
unsigned)-1);
479 assert(SourceStart <= SourceEnd);
480 assert(CaretStart <= CaretEnd);
482 unsigned BackColumnsRemoved
483 = map.byteToColumn(SourceLine.size())-map.byteToColumn(SourceEnd);
484 unsigned FrontColumnsRemoved = CaretStart;
485 unsigned ColumnsKept = CaretEnd-CaretStart;
488 assert(FrontColumnsRemoved+ColumnsKept+BackColumnsRemoved > Columns);
492 if (BackColumnsRemoved > strlen(back_ellipse))
493 SourceLine.replace(SourceEnd, std::string::npos, back_ellipse);
496 if (FrontColumnsRemoved+ColumnsKept <= Columns)
500 if (FrontColumnsRemoved > strlen(front_ellipse)) {
501 SourceLine.replace(0, SourceStart, front_ellipse);
502 CaretLine.replace(0, CaretStart, front_space);
503 if (!FixItInsertionLine.empty())
504 FixItInsertionLine.replace(0, CaretStart, front_space);
528 case '\'':
return '\'';
529 case '`':
return '\'';
530 case '"':
return '"';
531 case '(':
return ')';
532 case '[':
return ']';
533 case '{':
return '}';
546 unsigned Length,
unsigned Column,
548 assert(Start < Str.size() &&
"Invalid start position!");
549 unsigned End = Start + 1;
552 if (
End == Str.size())
568 PunctuationEndStack.push_back(EndPunct);
569 while (
End < Length && !PunctuationEndStack.empty()) {
570 if (Str[
End] == PunctuationEndStack.back())
571 PunctuationEndStack.pop_back();
573 PunctuationEndStack.push_back(SubEndPunct);
582 unsigned PunctWordLength =
End - Start;
584 Column + PunctWordLength <= Columns ||
587 PunctWordLength < Columns/3)
594 return findEndOfWord(Start + 1, Str, Length, Column + 1, Columns);
617 const unsigned Length =
std::min(Str.find(
'\n'), Str.size());
618 bool TextNormal =
true;
622 IndentStr.assign(Indentation,
' ');
623 bool Wrapped =
false;
624 for (
unsigned WordStart = 0, WordEnd; WordStart < Length;
625 WordStart = WordEnd) {
628 if (WordStart == Length)
632 WordEnd =
findEndOfWord(WordStart, Str, Length, Column, Columns);
635 unsigned WordLength = WordEnd - WordStart;
636 if (Column + WordLength < Columns) {
644 Column += WordLength;
651 OS.write(&IndentStr[0], Indentation);
654 Column = Indentation + WordLength;
661 assert(TextNormal &&
"Text highlighted at end of diagnostic message.");
677 uint64_t StartOfLocationInfo = OS.tell();
690 Message, OS.tell() - StartOfLocationInfo,
702 llvm_unreachable(
"Invalid diagnostic type");
713 llvm_unreachable(
"Invalid diagnostic type");
729 unsigned CurrentColumn,
730 unsigned Columns,
bool ShowColors) {
732 if (ShowColors && !IsSupplemental) {
744 assert(
Normal &&
"Formatting should have returned to normal");
774 TmpFilename = (*File)->getName();
775 llvm::sys::fs::make_absolute(TmpFilename);
776 llvm::sys::path::native(TmpFilename);
777 llvm::sys::path::remove_dots(TmpFilename,
true);
778 Filename = StringRef(TmpFilename.data(), TmpFilename.size());
780 Filename =
SM.getFileManager().getCanonicalName(*File);
802 emitFilename(FE->getName(), Loc.
getManager());
808 unsigned LineNo = PLoc.
getLine();
832 if (
LangOpts.MSCompatibilityVersion &&
846 if (
LangOpts.MSCompatibilityVersion &&
853 if (
DiagOpts->ShowSourceRanges && !Ranges.empty()) {
855 bool PrintedRange =
false;
861 if (!RI->isValid())
continue;
869 std::pair<FileID, unsigned> BInfo =
SM.getDecomposedLoc(B);
870 std::pair<FileID, unsigned> EInfo =
SM.getDecomposedLoc(E);
874 if (BInfo.first != CaretFileID || EInfo.first != CaretFileID)
879 unsigned TokSize = 0;
885 << BF.getLineNumber() <<
':' << BF.getColumnNumber() <<
'-'
899 OS <<
"In file included from " << PLoc.
getFilename() <<
':'
902 OS <<
"In included file:\n";
906 StringRef ModuleName) {
908 OS <<
"In module '" << ModuleName <<
"' imported from "
911 OS <<
"In module '" << ModuleName <<
"':\n";
916 StringRef ModuleName) {
918 OS <<
"While building module '" << ModuleName <<
"' imported from "
921 OS <<
"While building module '" << ModuleName <<
"':\n";
932 if (
SM.getFileID(
Begin) != FID ||
SM.getFileID(
End) != FID)
935 return std::make_pair(
SM.getExpansionLineNumber(
Begin),
936 SM.getExpansionLineNumber(
End));
941 static std::pair<unsigned, unsigned>
942 maybeAddRange(std::pair<unsigned, unsigned> A, std::pair<unsigned, unsigned> B,
945 unsigned Slack = MaxRange - (A.second - A.first + 1);
950 unsigned Min =
std::min(A.first, B.first);
951 unsigned Max =
std::max(A.second, B.second);
952 if (Max - Min + 1 <= MaxRange)
957 if ((B.first > A.first && B.first - A.first + 1 > MaxRange) ||
958 (B.second < A.second && A.second - B.second + 1 > MaxRange))
967 A.second =
std::min(A.second + (Slack + 1) / 2, Max);
968 Slack = MaxRange - (A.second - A.first + 1);
969 A.first =
std::max(Min + Slack, A.first) - Slack;
970 A.second =
std::min(A.first + MaxRange - 1, Max);
976 unsigned LineNo,
FileID FID,
977 const SourceColumnMap &map,
986 unsigned StartLineNo =
SM.getExpansionLineNumber(
Begin);
987 if (StartLineNo > LineNo ||
SM.getFileID(
Begin) != FID)
990 unsigned EndLineNo =
SM.getExpansionLineNumber(
End);
991 if (EndLineNo < LineNo ||
SM.getFileID(
End) != FID)
995 unsigned StartColNo = 0;
996 if (StartLineNo == LineNo) {
997 StartColNo =
SM.getExpansionColumnNumber(
Begin);
998 if (StartColNo) --StartColNo;
1002 unsigned EndColNo = map.getSourceLine().size();
1003 if (EndLineNo == LineNo) {
1004 EndColNo =
SM.getExpansionColumnNumber(
End);
1013 EndColNo = CaretLine.size();
1017 assert(StartColNo <= EndColNo &&
"Invalid range!");
1022 while (StartColNo < map.getSourceLine().size() &&
1023 (map.getSourceLine()[StartColNo] ==
' ' ||
1024 map.getSourceLine()[StartColNo] ==
'\t'))
1025 StartColNo = map.startOfNextColumn(StartColNo);
1028 if (EndColNo > map.getSourceLine().size())
1029 EndColNo = map.getSourceLine().size();
1031 (map.getSourceLine()[EndColNo-1] ==
' ' ||
1032 map.getSourceLine()[EndColNo-1] ==
'\t'))
1033 EndColNo = map.startOfPreviousColumn(EndColNo);
1038 if (StartColNo > EndColNo) {
1039 assert(StartLineNo != EndLineNo &&
"trying to highlight whitespace");
1040 StartColNo = EndColNo;
1044 assert(StartColNo <= map.getSourceLine().size() &&
"Invalid range!");
1045 assert(EndColNo <= map.getSourceLine().size() &&
"Invalid range!");
1048 StartColNo = map.byteToContainingColumn(StartColNo);
1049 EndColNo = map.byteToContainingColumn(EndColNo);
1051 assert(StartColNo <= EndColNo &&
"Invalid range!");
1052 if (CaretLine.size() < EndColNo)
1053 CaretLine.resize(EndColNo,
' ');
1054 std::fill(CaretLine.begin()+StartColNo,CaretLine.begin()+EndColNo,
'~');
1059 const SourceColumnMap &map,
1064 if (Hints.empty() || !DiagOpts->ShowFixits)
1065 return FixItInsertionLine;
1066 unsigned PrevHintEndCol = 0;
1070 if (!I->CodeToInsert.empty()) {
1073 std::pair<FileID, unsigned> HintLocInfo
1074 =
SM.getDecomposedExpansionLoc(I->RemoveRange.getBegin());
1075 if (FID == HintLocInfo.first &&
1076 LineNo ==
SM.getLineNumber(HintLocInfo.first, HintLocInfo.second) &&
1077 StringRef(I->CodeToInsert).find_first_of(
"\n\r") == StringRef::npos) {
1083 unsigned HintByteOffset
1084 =
SM.getColumnNumber(HintLocInfo.first, HintLocInfo.second) - 1;
1087 assert(HintByteOffset <
static_cast<unsigned>(map.bytes())+1);
1088 unsigned HintCol = map.byteToContainingColumn(HintByteOffset);
1097 if (HintCol < PrevHintEndCol)
1098 HintCol = PrevHintEndCol + 1;
1102 unsigned NewFixItLineSize = FixItInsertionLine.size() +
1103 (HintCol - PrevHintEndCol) + I->CodeToInsert.size();
1104 if (NewFixItLineSize > FixItInsertionLine.size())
1105 FixItInsertionLine.resize(NewFixItLineSize,
' ');
1107 std::copy(I->CodeToInsert.begin(), I->CodeToInsert.end(),
1108 FixItInsertionLine.end() - I->CodeToInsert.size());
1116 expandTabs(FixItInsertionLine, DiagOpts->TabStop);
1118 return FixItInsertionLine;
1128 void TextDiagnostic::emitSnippetAndCaret(
1131 assert(Loc.
isValid() &&
"must have a valid source location here");
1132 assert(Loc.
isFileID() &&
"must have a file location here");
1142 if (Loc ==
LastLoc && Ranges.empty() && Hints.empty() &&
1148 FileID FID = LocInfo.first;
1161 static const size_t MaxLineLengthToPrint = 4096;
1162 if (CaretColNo > MaxLineLengthToPrint)
1166 const unsigned MaxLines =
DiagOpts->SnippetLineLimit;
1167 std::pair<unsigned, unsigned> Lines = {CaretLineNo, CaretLineNo};
1174 for (
unsigned LineNo = Lines.first; LineNo != Lines.second + 1; ++LineNo) {
1175 const char *BufStart = BufData.data();
1176 const char *BufEnd = BufStart + BufData.size();
1179 const char *LineStart =
1181 SM.getDecomposedLoc(
SM.translateLineCol(FID, LineNo, 1)).second;
1182 if (LineStart == BufEnd)
1186 const char *LineEnd = LineStart;
1187 while (*LineEnd !=
'\n' && *LineEnd !=
'\r' && LineEnd != BufEnd)
1192 if (
size_t(LineEnd - LineStart) > MaxLineLengthToPrint)
1196 StringRef
Line(LineStart, LineEnd - LineStart);
1197 while (!
Line.empty() &&
Line.back() ==
'\0' &&
1198 (LineNo != CaretLineNo ||
Line.size() > CaretColNo))
1205 const SourceColumnMap sourceColMap(SourceLine,
DiagOpts->TabStop);
1209 std::string CaretLine(sourceColMap.columns(),
' ');
1218 if (CaretLineNo == LineNo) {
1219 CaretColNo = sourceColMap.byteToContainingColumn(CaretColNo - 1);
1220 if (CaretLine.size() < CaretColNo + 1)
1221 CaretLine.resize(CaretColNo + 1,
' ');
1222 CaretLine[CaretColNo] =
'^';
1226 FID, LineNo, sourceColMap, Hints,
SM,
DiagOpts.get());
1230 unsigned Columns =
DiagOpts->MessageLength;
1233 Columns, sourceColMap);
1240 SourceLine =
' ' + SourceLine;
1241 CaretLine =
' ' + CaretLine;
1245 while (!CaretLine.empty() && CaretLine[CaretLine.size() - 1] ==
' ')
1246 CaretLine.erase(CaretLine.end() - 1);
1249 emitSnippet(SourceLine);
1251 if (!CaretLine.empty()) {
1254 OS << CaretLine <<
'\n';
1259 if (!FixItInsertionLine.empty()) {
1265 OS << FixItInsertionLine <<
'\n';
1272 emitParseableFixits(Hints,
SM);
1275 void TextDiagnostic::emitSnippet(StringRef line) {
1282 bool print_reversed =
false;
1284 while (i<line.size()) {
1285 std::pair<SmallString<16>,
bool> res
1287 bool was_printable = res.second;
1289 if (
DiagOpts->ShowColors && was_printable == print_reversed) {
1298 print_reversed = !was_printable;
1299 to_print += res.first.str();
1302 if (print_reversed &&
DiagOpts->ShowColors)
1305 if (print_reversed &&
DiagOpts->ShowColors)
1313 if (!
DiagOpts->ShowParseableFixits)
1320 if (I->RemoveRange.isInvalid() ||
1321 I->RemoveRange.getBegin().isMacroID() ||
1322 I->RemoveRange.getEnd().isMacroID())
1331 std::pair<FileID, unsigned> BInfo =
SM.getDecomposedLoc(BLoc);
1332 std::pair<FileID, unsigned> EInfo =
SM.getDecomposedLoc(ELoc);
1335 if (I->RemoveRange.isTokenRange())
1346 OS <<
"\":{" <<
SM.getLineNumber(BInfo.first, BInfo.second)
1347 <<
':' <<
SM.getColumnNumber(BInfo.first, BInfo.second)
1348 <<
'-' <<
SM.getLineNumber(EInfo.first, EInfo.second)
1349 <<
':' <<
SM.getColumnNumber(EInfo.first, EInfo.second)
1351 OS.write_escaped(I->CodeToInsert);