diff options
Diffstat (limited to 'contrib/llvm/tools/clang/lib/Frontend/TextDiagnostic.cpp')
-rw-r--r-- | contrib/llvm/tools/clang/lib/Frontend/TextDiagnostic.cpp | 217 |
1 files changed, 140 insertions, 77 deletions
diff --git a/contrib/llvm/tools/clang/lib/Frontend/TextDiagnostic.cpp b/contrib/llvm/tools/clang/lib/Frontend/TextDiagnostic.cpp index 65fb1ae..9bb3e1d 100644 --- a/contrib/llvm/tools/clang/lib/Frontend/TextDiagnostic.cpp +++ b/contrib/llvm/tools/clang/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; |