summaryrefslogtreecommitdiffstats
path: root/lib/Frontend/TextDiagnostic.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'lib/Frontend/TextDiagnostic.cpp')
-rw-r--r--lib/Frontend/TextDiagnostic.cpp217
1 files changed, 140 insertions, 77 deletions
diff --git a/lib/Frontend/TextDiagnostic.cpp b/lib/Frontend/TextDiagnostic.cpp
index 65fb1ae..9bb3e1d 100644
--- a/lib/Frontend/TextDiagnostic.cpp
+++ b/lib/Frontend/TextDiagnostic.cpp
@@ -20,6 +20,7 @@
#include "llvm/ADT/SmallString.h"
#include "llvm/ADT/StringExtras.h"
#include <algorithm>
+#include <cctype>
using namespace clang;
@@ -31,16 +32,36 @@ static const enum raw_ostream::Colors caretColor =
raw_ostream::GREEN;
static const enum raw_ostream::Colors warningColor =
raw_ostream::MAGENTA;
+static const enum raw_ostream::Colors templateColor =
+ raw_ostream::CYAN;
static const enum raw_ostream::Colors errorColor = raw_ostream::RED;
static const enum raw_ostream::Colors fatalColor = raw_ostream::RED;
// Used for changing only the bold attribute.
static const enum raw_ostream::Colors savedColor =
raw_ostream::SAVEDCOLOR;
+/// \brief Add highlights to differences in template strings.
+static void applyTemplateHighlighting(raw_ostream &OS, StringRef Str,
+ bool &Normal, bool Bold) {
+ for (unsigned i = 0, e = Str.size(); i < e; ++i)
+ if (Str[i] != ToggleHighlight) {
+ OS << Str[i];
+ } else {
+ if (Normal)
+ OS.changeColor(templateColor, true);
+ else {
+ OS.resetColor();
+ if (Bold)
+ OS.changeColor(savedColor, true);
+ }
+ Normal = !Normal;
+ }
+}
+
/// \brief Number of spaces to indent when word-wrapping.
const unsigned WordWrapIndentation = 6;
-int bytesSincePreviousTabOrLineBegin(StringRef SourceLine, size_t i) {
+static int bytesSincePreviousTabOrLineBegin(StringRef SourceLine, size_t i) {
int bytes = 0;
while (0<i) {
if (SourceLine[--i]=='\t')
@@ -69,7 +90,7 @@ int bytesSincePreviousTabOrLineBegin(StringRef SourceLine, size_t i) {
/// \param TabStop used to expand tabs
/// \return pair(printable text, 'true' iff original text was printable)
///
-std::pair<SmallString<16>,bool>
+static std::pair<SmallString<16>, bool>
printableTextForNextCharacter(StringRef SourceLine, size_t *i,
unsigned TabStop) {
assert(i && "i must not be null");
@@ -146,7 +167,7 @@ printableTextForNextCharacter(StringRef SourceLine, size_t *i,
return std::make_pair(expandedByte, false);
}
-void expandTabs(std::string &SourceLine, unsigned TabStop) {
+static void expandTabs(std::string &SourceLine, unsigned TabStop) {
size_t i = SourceLine.size();
while (i>0) {
i--;
@@ -164,7 +185,7 @@ void expandTabs(std::string &SourceLine, unsigned TabStop) {
/// characters will appear at (numbering the first column as 0).
///
/// If a byte 'i' corresponds to muliple columns (e.g. the byte contains a tab
-/// character) then the the array will map that byte to the first column the
+/// character) then the array will map that byte to the first column the
/// tab appears at and the next value in the map will have been incremented
/// more than once.
///
@@ -179,9 +200,9 @@ void expandTabs(std::string &SourceLine, unsigned TabStop) {
///
/// "a \t \u3042" -> {0,1,2,8,9,-1,-1,11}
///
-/// (\u3042 is represented in UTF-8 by three bytes and takes two columns to
+/// (\\u3042 is represented in UTF-8 by three bytes and takes two columns to
/// display)
-void byteToColumn(StringRef SourceLine, unsigned TabStop,
+static void byteToColumn(StringRef SourceLine, unsigned TabStop,
SmallVectorImpl<int> &out) {
out.clear();
@@ -213,9 +234,9 @@ void byteToColumn(StringRef SourceLine, unsigned TabStop,
///
/// "a \t \u3042" -> {0,1,2,-1,-1,-1,-1,-1,3,4,-1,7}
///
-/// (\u3042 is represented in UTF-8 by three bytes and takes two columns to
+/// (\\u3042 is represented in UTF-8 by three bytes and takes two columns to
/// display)
-void columnToByte(StringRef SourceLine, unsigned TabStop,
+static void columnToByte(StringRef SourceLine, unsigned TabStop,
SmallVectorImpl<int> &out) {
out.clear();
@@ -307,11 +328,11 @@ static void selectInterestingSourceRegion(std::string &SourceLine,
// correctly.
unsigned CaretStart = 0, CaretEnd = CaretLine.size();
for (; CaretStart != CaretEnd; ++CaretStart)
- if (!isspace(CaretLine[CaretStart]))
+ if (!isspace(static_cast<unsigned char>(CaretLine[CaretStart])))
break;
for (; CaretEnd != CaretStart; --CaretEnd)
- if (!isspace(CaretLine[CaretEnd - 1]))
+ if (!isspace(static_cast<unsigned char>(CaretLine[CaretEnd - 1])))
break;
// caret has already been inserted into CaretLine so the above whitespace
@@ -322,17 +343,33 @@ static void selectInterestingSourceRegion(std::string &SourceLine,
if (!FixItInsertionLine.empty()) {
unsigned FixItStart = 0, FixItEnd = FixItInsertionLine.size();
for (; FixItStart != FixItEnd; ++FixItStart)
- if (!isspace(FixItInsertionLine[FixItStart]))
+ if (!isspace(static_cast<unsigned char>(FixItInsertionLine[FixItStart])))
break;
for (; FixItEnd != FixItStart; --FixItEnd)
- if (!isspace(FixItInsertionLine[FixItEnd - 1]))
+ if (!isspace(static_cast<unsigned char>(FixItInsertionLine[FixItEnd - 1])))
break;
CaretStart = std::min(FixItStart, CaretStart);
CaretEnd = std::max(FixItEnd, CaretEnd);
}
+ // CaretEnd may have been set at the middle of a character
+ // If it's not at a character's first column then advance it past the current
+ // character.
+ while (static_cast<int>(CaretEnd) < map.columns() &&
+ -1 == map.columnToByte(CaretEnd))
+ ++CaretEnd;
+
+ assert((static_cast<int>(CaretStart) > map.columns() ||
+ -1!=map.columnToByte(CaretStart)) &&
+ "CaretStart must not point to a column in the middle of a source"
+ " line character");
+ assert((static_cast<int>(CaretEnd) > map.columns() ||
+ -1!=map.columnToByte(CaretEnd)) &&
+ "CaretEnd must not point to a column in the middle of a source line"
+ " character");
+
// CaretLine[CaretStart, CaretEnd) contains all of the interesting
// parts of the caret line. While this slice is smaller than the
// number of columns we have, try to grow the slice to encompass
@@ -366,12 +403,14 @@ static void selectInterestingSourceRegion(std::string &SourceLine,
// Skip over any whitespace we see here; we're looking for
// another bit of interesting text.
while (NewStart &&
- (map.byteToColumn(NewStart)==-1 || isspace(SourceLine[NewStart])))
+ (map.byteToColumn(NewStart)==-1 ||
+ isspace(static_cast<unsigned char>(SourceLine[NewStart]))))
--NewStart;
// Skip over this bit of "interesting" text.
while (NewStart &&
- (map.byteToColumn(NewStart)!=-1 && !isspace(SourceLine[NewStart])))
+ (map.byteToColumn(NewStart)!=-1 &&
+ !isspace(static_cast<unsigned char>(SourceLine[NewStart]))))
--NewStart;
// Move up to the non-whitespace character we just saw.
@@ -392,12 +431,14 @@ static void selectInterestingSourceRegion(std::string &SourceLine,
// Skip over any whitespace we see here; we're looking for
// another bit of interesting text.
while (NewEnd<SourceLine.size() &&
- (map.byteToColumn(NewEnd)==-1 || isspace(SourceLine[NewEnd])))
+ (map.byteToColumn(NewEnd)==-1 ||
+ isspace(static_cast<unsigned char>(SourceLine[NewEnd]))))
++NewEnd;
// Skip over this bit of "interesting" text.
while (NewEnd<SourceLine.size() &&
- (map.byteToColumn(NewEnd)!=-1 && !isspace(SourceLine[NewEnd])))
+ (map.byteToColumn(NewEnd)!=-1 &&
+ !isspace(static_cast<unsigned char>(SourceLine[NewEnd]))))
++NewEnd;
unsigned NewColumns = map.byteToColumn(NewEnd) -
@@ -549,6 +590,7 @@ static unsigned findEndOfWord(unsigned Start, StringRef Str,
/// \param Column the column number at which the first character of \p
/// Str will be printed. This will be non-zero when part of the first
/// line has already been printed.
+/// \param Bold if the current text should be bold
/// \param Indentation the number of spaces to indent any lines beyond
/// the first line.
/// \returns true if word-wrapping was required, or false if the
@@ -556,8 +598,10 @@ static unsigned findEndOfWord(unsigned Start, StringRef Str,
static bool printWordWrapped(raw_ostream &OS, StringRef Str,
unsigned Columns,
unsigned Column = 0,
+ bool Bold = false,
unsigned Indentation = WordWrapIndentation) {
const unsigned Length = std::min(Str.find('\n'), Str.size());
+ bool TextNormal = true;
// The string used to indent each line.
SmallString<16> IndentStr;
@@ -581,7 +625,8 @@ static bool printWordWrapped(raw_ostream &OS, StringRef Str,
OS << ' ';
Column += 1;
}
- OS << Str.substr(WordStart, WordLength);
+ applyTemplateHighlighting(OS, Str.substr(WordStart, WordLength),
+ TextNormal, Bold);
Column += WordLength;
continue;
}
@@ -590,22 +635,24 @@ static bool printWordWrapped(raw_ostream &OS, StringRef Str,
// line.
OS << '\n';
OS.write(&IndentStr[0], Indentation);
- OS << Str.substr(WordStart, WordLength);
+ applyTemplateHighlighting(OS, Str.substr(WordStart, WordLength),
+ TextNormal, Bold);
Column = Indentation + WordLength;
Wrapped = true;
}
// Append any remaning text from the message with its existing formatting.
- OS << Str.substr(Length);
+ applyTemplateHighlighting(OS, Str.substr(Length), TextNormal, Bold);
+
+ assert(TextNormal && "Text highlighted at end of diagnostic message.");
return Wrapped;
}
TextDiagnostic::TextDiagnostic(raw_ostream &OS,
- const SourceManager &SM,
const LangOptions &LangOpts,
const DiagnosticOptions &DiagOpts)
- : DiagnosticRenderer(SM, LangOpts, DiagOpts), OS(OS) {}
+ : DiagnosticRenderer(LangOpts, DiagOpts), OS(OS) {}
TextDiagnostic::~TextDiagnostic() {}
@@ -615,11 +662,13 @@ TextDiagnostic::emitDiagnosticMessage(SourceLocation Loc,
DiagnosticsEngine::Level Level,
StringRef Message,
ArrayRef<clang::CharSourceRange> Ranges,
+ const SourceManager *SM,
DiagOrStoredDiag D) {
uint64_t StartOfLocationInfo = OS.tell();
// Emit the location of this particular diagnostic.
- emitDiagnosticLoc(Loc, PLoc, Level, Ranges);
+ if (Loc.isValid())
+ emitDiagnosticLoc(Loc, PLoc, Level, Ranges, *SM);
if (DiagOpts.ShowColors)
OS.resetColor();
@@ -665,20 +714,27 @@ TextDiagnostic::printDiagnosticMessage(raw_ostream &OS,
StringRef Message,
unsigned CurrentColumn, unsigned Columns,
bool ShowColors) {
+ bool Bold = false;
if (ShowColors) {
// Print warnings, errors and fatal errors in bold, no color
switch (Level) {
- case DiagnosticsEngine::Warning: OS.changeColor(savedColor, true); break;
- case DiagnosticsEngine::Error: OS.changeColor(savedColor, true); break;
- case DiagnosticsEngine::Fatal: OS.changeColor(savedColor, true); break;
+ case DiagnosticsEngine::Warning:
+ case DiagnosticsEngine::Error:
+ case DiagnosticsEngine::Fatal:
+ OS.changeColor(savedColor, true);
+ Bold = true;
+ break;
default: break; //don't bold notes
}
}
if (Columns)
- printWordWrapped(OS, Message, Columns, CurrentColumn);
- else
- OS << Message;
+ printWordWrapped(OS, Message, Columns, CurrentColumn, Bold);
+ else {
+ bool Normal = true;
+ applyTemplateHighlighting(OS, Message, Normal, Bold);
+ assert(Normal && "Formatting should have returned to normal");
+ }
if (ShowColors)
OS.resetColor();
@@ -693,7 +749,8 @@ TextDiagnostic::printDiagnosticMessage(raw_ostream &OS,
/// ranges necessary.
void TextDiagnostic::emitDiagnosticLoc(SourceLocation Loc, PresumedLoc PLoc,
DiagnosticsEngine::Level Level,
- ArrayRef<CharSourceRange> Ranges) {
+ ArrayRef<CharSourceRange> Ranges,
+ const SourceManager &SM) {
if (PLoc.isInvalid()) {
// At least print the file name if available:
FileID FID = SM.getFileID(Loc);
@@ -799,7 +856,8 @@ void TextDiagnostic::emitBasicNote(StringRef Message) {
}
void TextDiagnostic::emitIncludeLocation(SourceLocation Loc,
- PresumedLoc PLoc) {
+ PresumedLoc PLoc,
+ const SourceManager &SM) {
if (DiagOpts.ShowLocation)
OS << "In file included from " << PLoc.getFilename() << ':'
<< PLoc.getLine() << ":\n";
@@ -817,7 +875,8 @@ void TextDiagnostic::emitIncludeLocation(SourceLocation Loc,
void TextDiagnostic::emitSnippetAndCaret(
SourceLocation Loc, DiagnosticsEngine::Level Level,
SmallVectorImpl<CharSourceRange>& Ranges,
- ArrayRef<FixItHint> Hints) {
+ ArrayRef<FixItHint> Hints,
+ const SourceManager &SM) {
assert(!Loc.isInvalid() && "must have a valid source location here");
assert(Loc.isFileID() && "must have a file location here");
@@ -840,17 +899,12 @@ void TextDiagnostic::emitSnippetAndCaret(
// Get information about the buffer it points into.
bool Invalid = false;
- StringRef BufData = SM.getBufferData(FID, &Invalid);
+ const char *BufStart = SM.getBufferData(FID, &Invalid).data();
if (Invalid)
return;
- const char *BufStart = BufData.data();
- const char *BufEnd = BufStart + BufData.size();
-
unsigned LineNo = SM.getLineNumber(FID, FileOffset);
unsigned ColNo = SM.getColumnNumber(FID, FileOffset);
- unsigned CaretEndColNo
- = ColNo + Lexer::MeasureTokenLength(Loc, SM, LangOpts);
// Rewind from the current position to the start of the line.
const char *TokPtr = BufStart+FileOffset;
@@ -860,14 +914,9 @@ void TextDiagnostic::emitSnippetAndCaret(
// Compute the line end. Scan forward from the error position to the end of
// the line.
const char *LineEnd = TokPtr;
- while (*LineEnd != '\n' && *LineEnd != '\r' && LineEnd!=BufEnd)
+ while (*LineEnd != '\n' && *LineEnd != '\r' && *LineEnd != '\0')
++LineEnd;
- // FIXME: This shouldn't be necessary, but the CaretEndColNo can extend past
- // the source line length as currently being computed. See
- // test/Misc/message-length.c.
- CaretEndColNo = std::min(CaretEndColNo, unsigned(LineEnd - LineStart));
-
// Copy the line of code into an std::string for ease of manipulation.
std::string SourceLine(LineStart, LineEnd);
@@ -881,7 +930,7 @@ void TextDiagnostic::emitSnippetAndCaret(
for (SmallVectorImpl<CharSourceRange>::iterator I = Ranges.begin(),
E = Ranges.end();
I != E; ++I)
- highlightRange(*I, LineNo, FID, sourceColMap, CaretLine);
+ highlightRange(*I, LineNo, FID, sourceColMap, CaretLine, SM);
// Next, insert the caret itself.
ColNo = sourceColMap.byteToColumn(ColNo-1);
@@ -891,7 +940,7 @@ void TextDiagnostic::emitSnippetAndCaret(
std::string FixItInsertionLine = buildFixItInsertionLine(LineNo,
sourceColMap,
- Hints);
+ Hints, SM);
// If the source line is too long for our terminal, select only the
// "interesting" source region within that line.
@@ -934,11 +983,10 @@ void TextDiagnostic::emitSnippetAndCaret(
}
// Print out any parseable fixit information requested by the options.
- emitParseableFixits(Hints);
+ emitParseableFixits(Hints, SM);
}
-void TextDiagnostic::emitSnippet(StringRef line)
-{
+void TextDiagnostic::emitSnippet(StringRef line) {
if (line.empty())
return;
@@ -952,8 +1000,7 @@ void TextDiagnostic::emitSnippet(StringRef line)
= printableTextForNextCharacter(line, &i, DiagOpts.TabStop);
bool was_printable = res.second;
- if (DiagOpts.ShowColors
- && was_printable==print_reversed) {
+ if (DiagOpts.ShowColors && was_printable == print_reversed) {
if (print_reversed)
OS.reverseColor();
OS << to_print;
@@ -979,7 +1026,8 @@ void TextDiagnostic::emitSnippet(StringRef line)
void TextDiagnostic::highlightRange(const CharSourceRange &R,
unsigned LineNo, FileID FID,
const SourceColumnMap &map,
- std::string &CaretLine) {
+ std::string &CaretLine,
+ const SourceManager &SM) {
if (!R.isValid()) return;
SourceLocation Begin = SM.getExpansionLoc(R.getBegin());
@@ -1064,49 +1112,63 @@ void TextDiagnostic::highlightRange(const CharSourceRange &R,
std::string TextDiagnostic::buildFixItInsertionLine(
unsigned LineNo,
const SourceColumnMap &map,
- ArrayRef<FixItHint> Hints) {
+ ArrayRef<FixItHint> Hints,
+ const SourceManager &SM) {
std::string FixItInsertionLine;
if (Hints.empty() || !DiagOpts.ShowFixits)
return FixItInsertionLine;
+ unsigned PrevHintEndCol = 0;
for (ArrayRef<FixItHint>::iterator I = Hints.begin(), E = Hints.end();
I != E; ++I) {
if (!I->CodeToInsert.empty()) {
// We have an insertion hint. Determine whether the inserted
- // code is on the same line as the caret.
+ // code contains no newlines and is on the same line as the caret.
std::pair<FileID, unsigned> HintLocInfo
= SM.getDecomposedExpansionLoc(I->RemoveRange.getBegin());
- if (LineNo == SM.getLineNumber(HintLocInfo.first, HintLocInfo.second)) {
+ if (LineNo == SM.getLineNumber(HintLocInfo.first, HintLocInfo.second) &&
+ StringRef(I->CodeToInsert).find_first_of("\n\r") == StringRef::npos) {
// Insert the new code into the line just below the code
// that the user wrote.
- unsigned HintColNo
+ // Note: When modifying this function, be very careful about what is a
+ // "column" (printed width, platform-dependent) and what is a
+ // "byte offset" (SourceManager "column").
+ unsigned HintByteOffset
= SM.getColumnNumber(HintLocInfo.first, HintLocInfo.second) - 1;
- // hint must start inside the source or right at the end
- assert(HintColNo<static_cast<unsigned>(map.bytes())+1);
- HintColNo = map.byteToColumn(HintColNo);
-
- // FIXME: if the fixit includes tabs or other characters that do not
- // take up a single column per byte when displayed then
- // I->CodeToInsert.size() is not a column number and we're mixing
- // units (columns + bytes). We should get printable versions
- // of each fixit before using them.
- unsigned LastColumnModified
- = HintColNo + I->CodeToInsert.size();
-
- if (LastColumnModified > static_cast<unsigned>(map.bytes())) {
- unsigned LastExistingColumn = map.byteToColumn(map.bytes());
- unsigned AddedColumns = LastColumnModified-LastExistingColumn;
- LastColumnModified = LastExistingColumn + AddedColumns;
- } else {
- LastColumnModified = map.byteToColumn(LastColumnModified);
- }
+ // The hint must start inside the source or right at the end
+ assert(HintByteOffset < static_cast<unsigned>(map.bytes())+1);
+ unsigned HintCol = map.byteToColumn(HintByteOffset);
+
+ // If we inserted a long previous hint, push this one forwards, and add
+ // an extra space to show that this is not part of the previous
+ // completion. This is sort of the best we can do when two hints appear
+ // to overlap.
+ //
+ // Note that if this hint is located immediately after the previous
+ // hint, no space will be added, since the location is more important.
+ if (HintCol < PrevHintEndCol)
+ HintCol = PrevHintEndCol + 1;
+
+ // FIXME: This function handles multibyte characters in the source, but
+ // not in the fixits. This assertion is intended to catch unintended
+ // use of multibyte characters in fixits. If we decide to do this, we'll
+ // have to track separate byte widths for the source and fixit lines.
+ assert((size_t)llvm::sys::locale::columnWidth(I->CodeToInsert) ==
+ I->CodeToInsert.size());
+
+ // This relies on one byte per column in our fixit hints.
+ // This should NOT use HintByteOffset, because the source might have
+ // Unicode characters in earlier columns.
+ unsigned LastColumnModified = HintCol + I->CodeToInsert.size();
if (LastColumnModified > FixItInsertionLine.size())
FixItInsertionLine.resize(LastColumnModified, ' ');
- assert(HintColNo+I->CodeToInsert.size() <= FixItInsertionLine.size());
+
std::copy(I->CodeToInsert.begin(), I->CodeToInsert.end(),
- FixItInsertionLine.begin() + HintColNo);
+ FixItInsertionLine.begin() + HintCol);
+
+ PrevHintEndCol = LastColumnModified;
} else {
FixItInsertionLine.clear();
break;
@@ -1119,7 +1181,8 @@ std::string TextDiagnostic::buildFixItInsertionLine(
return FixItInsertionLine;
}
-void TextDiagnostic::emitParseableFixits(ArrayRef<FixItHint> Hints) {
+void TextDiagnostic::emitParseableFixits(ArrayRef<FixItHint> Hints,
+ const SourceManager &SM) {
if (!DiagOpts.ShowParseableFixits)
return;
OpenPOWER on IntegriCloud