diff options
Diffstat (limited to 'lib/Frontend/TextDiagnostic.cpp')
-rw-r--r-- | lib/Frontend/TextDiagnostic.cpp | 381 |
1 files changed, 204 insertions, 177 deletions
diff --git a/lib/Frontend/TextDiagnostic.cpp b/lib/Frontend/TextDiagnostic.cpp index 35dabad..ca4ad60 100644 --- a/lib/Frontend/TextDiagnostic.cpp +++ b/lib/Frontend/TextDiagnostic.cpp @@ -8,19 +8,19 @@ //===----------------------------------------------------------------------===// #include "clang/Frontend/TextDiagnostic.h" +#include "clang/Basic/CharInfo.h" +#include "clang/Basic/DiagnosticOptions.h" #include "clang/Basic/FileManager.h" #include "clang/Basic/SourceManager.h" -#include "clang/Basic/ConvertUTF.h" -#include "clang/Basic/DiagnosticOptions.h" #include "clang/Lex/Lexer.h" -#include "llvm/Support/MemoryBuffer.h" -#include "llvm/Support/raw_ostream.h" -#include "llvm/Support/ErrorHandling.h" -#include "llvm/Support/Locale.h" #include "llvm/ADT/SmallString.h" #include "llvm/ADT/StringExtras.h" +#include "llvm/Support/ConvertUTF.h" +#include "llvm/Support/ErrorHandling.h" +#include "llvm/Support/Locale.h" +#include "llvm/Support/MemoryBuffer.h" +#include "llvm/Support/raw_ostream.h" #include <algorithm> -#include <cctype> using namespace clang; @@ -248,6 +248,7 @@ static void columnToByte(StringRef SourceLine, unsigned TabStop, out.back() = i; } +namespace { struct SourceColumnMap { SourceColumnMap(StringRef SourceLine, unsigned TabStop) : m_SourceLine(SourceLine) { @@ -313,14 +314,13 @@ private: }; // used in assert in selectInterestingSourceRegion() -namespace { struct char_out_of_range { const char lower,upper; char_out_of_range(char lower, char upper) : lower(lower), upper(upper) {} bool operator()(char c) { return c < lower || upper < c; } }; -} +} // end anonymous namespace /// \brief When the source code line we want to print is too long for /// the terminal, select the "interesting" region. @@ -348,11 +348,11 @@ static void selectInterestingSourceRegion(std::string &SourceLine, // correctly. unsigned CaretStart = 0, CaretEnd = CaretLine.size(); for (; CaretStart != CaretEnd; ++CaretStart) - if (!isspace(static_cast<unsigned char>(CaretLine[CaretStart]))) + if (!isWhitespace(CaretLine[CaretStart])) break; for (; CaretEnd != CaretStart; --CaretEnd) - if (!isspace(static_cast<unsigned char>(CaretLine[CaretEnd - 1]))) + if (!isWhitespace(CaretLine[CaretEnd - 1])) break; // caret has already been inserted into CaretLine so the above whitespace @@ -363,11 +363,11 @@ static void selectInterestingSourceRegion(std::string &SourceLine, if (!FixItInsertionLine.empty()) { unsigned FixItStart = 0, FixItEnd = FixItInsertionLine.size(); for (; FixItStart != FixItEnd; ++FixItStart) - if (!isspace(static_cast<unsigned char>(FixItInsertionLine[FixItStart]))) + if (!isWhitespace(FixItInsertionLine[FixItStart])) break; for (; FixItEnd != FixItStart; --FixItEnd) - if (!isspace(static_cast<unsigned char>(FixItInsertionLine[FixItEnd - 1]))) + if (!isWhitespace(FixItInsertionLine[FixItEnd - 1])) break; CaretStart = std::min(FixItStart, CaretStart); @@ -423,14 +423,13 @@ static void selectInterestingSourceRegion(std::string &SourceLine, // Skip over any whitespace we see here; we're looking for // another bit of interesting text. // FIXME: Detect non-ASCII whitespace characters too. - while (NewStart && - isspace(static_cast<unsigned char>(SourceLine[NewStart]))) + while (NewStart && isWhitespace(SourceLine[NewStart])) NewStart = map.startOfPreviousColumn(NewStart); // Skip over this bit of "interesting" text. while (NewStart) { unsigned Prev = map.startOfPreviousColumn(NewStart); - if (isspace(static_cast<unsigned char>(SourceLine[Prev]))) + if (isWhitespace(SourceLine[Prev])) break; NewStart = Prev; } @@ -450,13 +449,11 @@ static void selectInterestingSourceRegion(std::string &SourceLine, // Skip over any whitespace we see here; we're looking for // another bit of interesting text. // FIXME: Detect non-ASCII whitespace characters too. - while (NewEnd < SourceLine.size() && - isspace(static_cast<unsigned char>(SourceLine[NewEnd]))) + while (NewEnd < SourceLine.size() && isWhitespace(SourceLine[NewEnd])) NewEnd = map.startOfNextColumn(NewEnd); // Skip over this bit of "interesting" text. - while (NewEnd < SourceLine.size() && - !isspace(static_cast<unsigned char>(SourceLine[NewEnd]))) + while (NewEnd < SourceLine.size() && isWhitespace(SourceLine[NewEnd])) NewEnd = map.startOfNextColumn(NewEnd); assert(map.byteToColumn(NewEnd) != -1); @@ -517,7 +514,7 @@ static void selectInterestingSourceRegion(std::string &SourceLine, /// greater than or equal to Idx or, if no such character exists, /// returns the end of the string. static unsigned skipWhitespace(unsigned Idx, StringRef Str, unsigned Length) { - while (Idx < Length && isspace(Str[Idx])) + while (Idx < Length && isWhitespace(Str[Idx])) ++Idx; return Idx; } @@ -562,7 +559,7 @@ static unsigned findEndOfWord(unsigned Start, StringRef Str, char EndPunct = findMatchingPunctuation(Str[Start]); if (!EndPunct) { // This is a normal word. Just find the first space character. - while (End < Length && !isspace(Str[End])) + while (End < Length && !isWhitespace(Str[End])) ++End; return End; } @@ -581,7 +578,7 @@ static unsigned findEndOfWord(unsigned Start, StringRef Str, } // Find the first space character after the punctuation ended. - while (End < Length && !isspace(Str[End])) + while (End < Length && !isWhitespace(Str[End])) ++End; unsigned PunctWordLength = End - Start; @@ -884,6 +881,178 @@ void TextDiagnostic::emitIncludeLocation(SourceLocation Loc, OS << "In included file:\n"; } +void TextDiagnostic::emitImportLocation(SourceLocation Loc, PresumedLoc PLoc, + StringRef ModuleName, + const SourceManager &SM) { + if (DiagOpts->ShowLocation) + OS << "In module '" << ModuleName << "' imported from " + << PLoc.getFilename() << ':' << PLoc.getLine() << ":\n"; + else + OS << "In module " << ModuleName << "':\n"; +} + +void TextDiagnostic::emitBuildingModuleLocation(SourceLocation Loc, + PresumedLoc PLoc, + StringRef ModuleName, + const SourceManager &SM) { + if (DiagOpts->ShowLocation && PLoc.getFilename()) + OS << "While building module '" << ModuleName << "' imported from " + << PLoc.getFilename() << ':' << PLoc.getLine() << ":\n"; + else + OS << "While building module '" << ModuleName << "':\n"; +} + +/// \brief Highlight a SourceRange (with ~'s) for any characters on LineNo. +static void highlightRange(const CharSourceRange &R, + unsigned LineNo, FileID FID, + const SourceColumnMap &map, + std::string &CaretLine, + const SourceManager &SM, + const LangOptions &LangOpts) { + if (!R.isValid()) return; + + SourceLocation Begin = R.getBegin(); + SourceLocation End = R.getEnd(); + + unsigned StartLineNo = SM.getExpansionLineNumber(Begin); + if (StartLineNo > LineNo || SM.getFileID(Begin) != FID) + return; // No intersection. + + unsigned EndLineNo = SM.getExpansionLineNumber(End); + if (EndLineNo < LineNo || SM.getFileID(End) != FID) + return; // No intersection. + + // Compute the column number of the start. + unsigned StartColNo = 0; + if (StartLineNo == LineNo) { + StartColNo = SM.getExpansionColumnNumber(Begin); + if (StartColNo) --StartColNo; // Zero base the col #. + } + + // Compute the column number of the end. + unsigned EndColNo = map.getSourceLine().size(); + if (EndLineNo == LineNo) { + EndColNo = SM.getExpansionColumnNumber(End); + if (EndColNo) { + --EndColNo; // Zero base the col #. + + // Add in the length of the token, so that we cover multi-char tokens if + // this is a token range. + if (R.isTokenRange()) + EndColNo += Lexer::MeasureTokenLength(End, SM, LangOpts); + } else { + EndColNo = CaretLine.size(); + } + } + + assert(StartColNo <= EndColNo && "Invalid range!"); + + // Check that a token range does not highlight only whitespace. + if (R.isTokenRange()) { + // Pick the first non-whitespace column. + while (StartColNo < map.getSourceLine().size() && + (map.getSourceLine()[StartColNo] == ' ' || + map.getSourceLine()[StartColNo] == '\t')) + StartColNo = map.startOfNextColumn(StartColNo); + + // Pick the last non-whitespace column. + if (EndColNo > map.getSourceLine().size()) + EndColNo = map.getSourceLine().size(); + while (EndColNo && + (map.getSourceLine()[EndColNo-1] == ' ' || + map.getSourceLine()[EndColNo-1] == '\t')) + EndColNo = map.startOfPreviousColumn(EndColNo); + + // If the start/end passed each other, then we are trying to highlight a + // range that just exists in whitespace, which must be some sort of other + // bug. + assert(StartColNo <= EndColNo && "Trying to highlight whitespace??"); + } + + assert(StartColNo <= map.getSourceLine().size() && "Invalid range!"); + assert(EndColNo <= map.getSourceLine().size() && "Invalid range!"); + + // Fill the range with ~'s. + StartColNo = map.byteToContainingColumn(StartColNo); + EndColNo = map.byteToContainingColumn(EndColNo); + + assert(StartColNo <= EndColNo && "Invalid range!"); + if (CaretLine.size() < EndColNo) + CaretLine.resize(EndColNo,' '); + std::fill(CaretLine.begin()+StartColNo,CaretLine.begin()+EndColNo,'~'); +} + +static std::string buildFixItInsertionLine(unsigned LineNo, + const SourceColumnMap &map, + ArrayRef<FixItHint> Hints, + const SourceManager &SM, + const DiagnosticOptions *DiagOpts) { + 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 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) && + 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. + // 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; + + // The hint must start inside the source or right at the end + assert(HintByteOffset < static_cast<unsigned>(map.bytes())+1); + unsigned HintCol = map.byteToContainingColumn(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, ' '); + + std::copy(I->CodeToInsert.begin(), I->CodeToInsert.end(), + FixItInsertionLine.begin() + HintCol); + + PrevHintEndCol = LastColumnModified; + } else { + FixItInsertionLine.clear(); + break; + } + } + } + + expandTabs(FixItInsertionLine, DiagOpts->TabStop); + + return FixItInsertionLine; +} + /// \brief Emit a code snippet and caret line. /// /// This routine emits a single line's code snippet and caret line.. @@ -924,18 +1093,26 @@ void TextDiagnostic::emitSnippetAndCaret( unsigned LineNo = SM.getLineNumber(FID, FileOffset); unsigned ColNo = SM.getColumnNumber(FID, FileOffset); + + // Arbitrarily stop showing snippets when the line is too long. + static const ptrdiff_t MaxLineLengthToPrint = 4096; + if (ColNo > MaxLineLengthToPrint) + return; // Rewind from the current position to the start of the line. const char *TokPtr = BufStart+FileOffset; const char *LineStart = TokPtr-ColNo+1; // Column # is 1-based. - // 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 != '\0') ++LineEnd; + // Arbitrarily stop showing snippets when the line is too long. + if (LineEnd - LineStart > MaxLineLengthToPrint) + return; + // Copy the line of code into an std::string for ease of manipulation. std::string SourceLine(LineStart, LineEnd); @@ -949,7 +1126,7 @@ void TextDiagnostic::emitSnippetAndCaret( for (SmallVectorImpl<CharSourceRange>::iterator I = Ranges.begin(), E = Ranges.end(); I != E; ++I) - highlightRange(*I, LineNo, FID, sourceColMap, CaretLine, SM); + highlightRange(*I, LineNo, FID, sourceColMap, CaretLine, SM, LangOpts); // Next, insert the caret itself. ColNo = sourceColMap.byteToContainingColumn(ColNo-1); @@ -959,7 +1136,8 @@ void TextDiagnostic::emitSnippetAndCaret( std::string FixItInsertionLine = buildFixItInsertionLine(LineNo, sourceColMap, - Hints, SM); + Hints, SM, + DiagOpts.getPtr()); // If the source line is too long for our terminal, select only the // "interesting" source region within that line. @@ -1041,157 +1219,6 @@ void TextDiagnostic::emitSnippet(StringRef line) { OS << '\n'; } -/// \brief Highlight a SourceRange (with ~'s) for any characters on LineNo. -void TextDiagnostic::highlightRange(const CharSourceRange &R, - unsigned LineNo, FileID FID, - const SourceColumnMap &map, - std::string &CaretLine, - const SourceManager &SM) { - if (!R.isValid()) return; - - SourceLocation Begin = R.getBegin(); - SourceLocation End = R.getEnd(); - - unsigned StartLineNo = SM.getExpansionLineNumber(Begin); - if (StartLineNo > LineNo || SM.getFileID(Begin) != FID) - return; // No intersection. - - unsigned EndLineNo = SM.getExpansionLineNumber(End); - if (EndLineNo < LineNo || SM.getFileID(End) != FID) - return; // No intersection. - - // Compute the column number of the start. - unsigned StartColNo = 0; - if (StartLineNo == LineNo) { - StartColNo = SM.getExpansionColumnNumber(Begin); - if (StartColNo) --StartColNo; // Zero base the col #. - } - - // Compute the column number of the end. - unsigned EndColNo = map.getSourceLine().size(); - if (EndLineNo == LineNo) { - EndColNo = SM.getExpansionColumnNumber(End); - if (EndColNo) { - --EndColNo; // Zero base the col #. - - // Add in the length of the token, so that we cover multi-char tokens if - // this is a token range. - if (R.isTokenRange()) - EndColNo += Lexer::MeasureTokenLength(End, SM, LangOpts); - } else { - EndColNo = CaretLine.size(); - } - } - - assert(StartColNo <= EndColNo && "Invalid range!"); - - // Check that a token range does not highlight only whitespace. - if (R.isTokenRange()) { - // Pick the first non-whitespace column. - while (StartColNo < map.getSourceLine().size() && - (map.getSourceLine()[StartColNo] == ' ' || - map.getSourceLine()[StartColNo] == '\t')) - StartColNo = map.startOfNextColumn(StartColNo); - - // Pick the last non-whitespace column. - if (EndColNo > map.getSourceLine().size()) - EndColNo = map.getSourceLine().size(); - while (EndColNo-1 && - (map.getSourceLine()[EndColNo-1] == ' ' || - map.getSourceLine()[EndColNo-1] == '\t')) - EndColNo = map.startOfPreviousColumn(EndColNo); - - // If the start/end passed each other, then we are trying to highlight a - // range that just exists in whitespace, which must be some sort of other - // bug. - assert(StartColNo <= EndColNo && "Trying to highlight whitespace??"); - } - - assert(StartColNo <= map.getSourceLine().size() && "Invalid range!"); - assert(EndColNo <= map.getSourceLine().size() && "Invalid range!"); - - // Fill the range with ~'s. - StartColNo = map.byteToContainingColumn(StartColNo); - EndColNo = map.byteToContainingColumn(EndColNo); - - assert(StartColNo <= EndColNo && "Invalid range!"); - if (CaretLine.size() < EndColNo) - CaretLine.resize(EndColNo,' '); - std::fill(CaretLine.begin()+StartColNo,CaretLine.begin()+EndColNo,'~'); -} - -std::string TextDiagnostic::buildFixItInsertionLine( - unsigned LineNo, - const SourceColumnMap &map, - 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 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) && - 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. - // 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; - - // The hint must start inside the source or right at the end - assert(HintByteOffset < static_cast<unsigned>(map.bytes())+1); - unsigned HintCol = map.byteToContainingColumn(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, ' '); - - std::copy(I->CodeToInsert.begin(), I->CodeToInsert.end(), - FixItInsertionLine.begin() + HintCol); - - PrevHintEndCol = LastColumnModified; - } else { - FixItInsertionLine.clear(); - break; - } - } - } - - expandTabs(FixItInsertionLine, DiagOpts->TabStop); - - return FixItInsertionLine; -} - void TextDiagnostic::emitParseableFixits(ArrayRef<FixItHint> Hints, const SourceManager &SM) { if (!DiagOpts->ShowParseableFixits) |