[cfe-commits] r148389 - in /cfe/trunk: include/clang/Basic/DiagnosticLexKinds.td lib/Lex/LiteralSupport.cpp
Eli Friedman
eli.friedman at gmail.com
Fri Mar 23 13:42:49 PDT 2012
On Fri, Mar 23, 2012 at 1:49 AM, Eli Friedman <eli.friedman at gmail.com> wrote:
> On Thu, Mar 22, 2012 at 11:57 PM, Seth Cantrell <seth.cantrell at gmail.com> wrote:
>> Here are some patches for review. One enables setting reverse colors with raw_ostreams in LLVM. The other is to print unprintable characters in clang diagnostics in hex with reversed colors.
>
> The patch for color reversal seems fine, although maybe someone more
> familiar with that code should take a look. The patch for unprintable
> characters is a step in the right direction, but completely ignores
> the caret/ranges and fixits (so things won't line up).
>
> I was actually hacking on this code from that angle recently; I'll
> send you what I have soon.
Attaching my work-in-progress, which lines up the caret correctly (but
doesn't have your nice color-formatting, and there are still a couple
other issues with making everything line up).
-Eli
-------------- next part --------------
Index: include/clang/Frontend/TextDiagnostic.h
===================================================================
--- include/clang/Frontend/TextDiagnostic.h (revision 153278)
+++ include/clang/Frontend/TextDiagnostic.h (working copy)
@@ -104,14 +104,17 @@
ArrayRef<FixItHint> Hints);
void highlightRange(const CharSourceRange &R,
- unsigned LineNo, FileID FID,
+ const char *BufStart, FileID FID,
+ const char *LineStart, const char *LineEnd,
const std::string &SourceLine,
+ std::vector<unsigned> &SourceLineOffsets,
std::string &CaretLine);
- std::string buildFixItInsertionLine(unsigned LineNo,
+ std::string buildFixItInsertionLine(const char *BufStart, FileID FID,
const char *LineStart,
const char *LineEnd,
ArrayRef<FixItHint> Hints);
- void expandTabs(std::string &SourceLine, std::string &CaretLine);
+ void expandSourceLine(std::string &SourceLine,
+ std::vector<unsigned>&SourceLineOffsets);
void emitParseableFixits(ArrayRef<FixItHint> Hints);
};
Index: lib/Frontend/TextDiagnostic.cpp
===================================================================
--- lib/Frontend/TextDiagnostic.cpp (revision 153278)
+++ lib/Frontend/TextDiagnostic.cpp (working copy)
@@ -16,6 +16,7 @@
#include "llvm/Support/raw_ostream.h"
#include "llvm/Support/ErrorHandling.h"
#include "llvm/ADT/SmallString.h"
+#include "llvm/ADT/StringExtras.h"
#include <algorithm>
using namespace clang;
@@ -29,6 +30,7 @@
raw_ostream::MAGENTA;
static const enum raw_ostream::Colors errorColor = raw_ostream::RED;
static const enum raw_ostream::Colors fatalColor = raw_ostream::RED;
+static const enum raw_ostream::Colors badEncodingColor = raw_ostream::BLUE;
// Used for changing only the bold attribute.
static const enum raw_ostream::Colors savedColor =
raw_ostream::SAVEDCOLOR;
@@ -41,7 +43,7 @@
static void selectInterestingSourceRegion(std::string &SourceLine,
std::string &CaretLine,
std::string &FixItInsertionLine,
- unsigned EndOfCaretToken,
+ unsigned CaretTokLength,
unsigned Columns) {
unsigned MaxSize = std::max(SourceLine.size(),
std::max(CaretLine.size(),
@@ -66,8 +68,7 @@
// Make sure we don't chop the string shorter than the caret token
// itself.
- if (CaretEnd < EndOfCaretToken)
- CaretEnd = EndOfCaretToken;
+ CaretEnd = std::max(CaretEnd, CaretStart + CaretTokLength);
// If we have a fix-it line, make sure the slice includes all of the
// fix-it information.
@@ -568,48 +569,44 @@
if (Invalid)
return;
- unsigned LineNo = SM.getLineNumber(FID, FileOffset);
- unsigned ColNo = SM.getColumnNumber(FID, FileOffset);
- unsigned CaretEndColNo
- = ColNo + Lexer::MeasureTokenLength(Loc, SM, LangOpts);
+ const char *TokPtr = BufStart+FileOffset;
+ // FIXME: TokLength is currently getting misused.
+ unsigned TokLength = Lexer::MeasureTokenLength(Loc, SM, LangOpts);
// 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.
+ const char *LineStart = TokPtr;
+ while (LineStart != BufStart && *(LineStart-1) != '\n' &&
+ *(LineStart-1) != '\r')
+ --LineStart;
-
// 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;
- // 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);
+ std::vector<unsigned> LineOffsetMap(SourceLine.size() + 1);
+ expandSourceLine(SourceLine, LineOffsetMap);
// Create a line for the caret that is filled with spaces that is the same
// length as the line of source code.
- std::string CaretLine(LineEnd-LineStart, ' ');
+ std::string CaretLine(SourceLine.size(), ' ');
// Highlight all of the characters covered by Ranges with ~ characters.
for (SmallVectorImpl<CharSourceRange>::iterator I = Ranges.begin(),
E = Ranges.end();
I != E; ++I)
- highlightRange(*I, LineNo, FID, SourceLine, CaretLine);
+ highlightRange(*I, BufStart, FID, LineStart, LineEnd, SourceLine,
+ LineOffsetMap, CaretLine);
// Next, insert the caret itself.
- if (ColNo-1 < CaretLine.size())
- CaretLine[ColNo-1] = '^';
+ if (TokPtr == LineEnd)
+ CaretLine.push_back('^');
else
- CaretLine.push_back('^');
+ CaretLine[LineOffsetMap[TokPtr - LineStart]] = '^';
- expandTabs(SourceLine, CaretLine);
-
// If we are in -fdiagnostics-print-source-range-info mode, we are trying
// to produce easily machine parsable output. Add a space before the
// source line and the caret to make it trivial to tell the main diagnostic
@@ -619,20 +616,20 @@
CaretLine = ' ' + CaretLine;
}
- std::string FixItInsertionLine = buildFixItInsertionLine(LineNo,
+ std::string FixItInsertionLine = buildFixItInsertionLine(BufStart, FID,
LineStart, LineEnd,
Hints);
// If the source line is too long for our terminal, select only the
// "interesting" source region within that line.
unsigned Columns = DiagOpts.MessageLength;
- if (Columns && SourceLine.size() > Columns)
+ if (Columns && LineOffsetMap.back() > Columns)
selectInterestingSourceRegion(SourceLine, CaretLine, FixItInsertionLine,
- CaretEndColNo, Columns);
+ TokLength, Columns);
// Finally, remove any blank spaces from the end of CaretLine.
- while (CaretLine[CaretLine.size()-1] == ' ')
- CaretLine.erase(CaretLine.end()-1);
+ while (CaretLine.back() == ' ')
+ CaretLine.pop_back();
// Emit what we have computed.
OS << SourceLine << '\n';
@@ -660,57 +657,61 @@
/// \brief Highlight a SourceRange (with ~'s) for any characters on LineNo.
void TextDiagnostic::highlightRange(const CharSourceRange &R,
- unsigned LineNo, FileID FID,
+ const char *BufStart, FileID FID,
+ const char *LineStart, const char *LineEnd,
const std::string &SourceLine,
+ std::vector<unsigned> &LineOffsetMap,
std::string &CaretLine) {
assert(CaretLine.size() == SourceLine.size() &&
"Expect a correspondence between source and caret line!");
if (!R.isValid()) return;
- SourceLocation Begin = SM.getExpansionLoc(R.getBegin());
- SourceLocation End = SM.getExpansionLoc(R.getEnd());
+ SourceLocation BeginLoc = SM.getExpansionLoc(R.getBegin());
+ SourceLocation EndLoc = SM.getExpansionLoc(R.getEnd());
// If the End location and the start location are the same and are a macro
// location, then the range was something that came from a macro expansion
// or _Pragma. If this is an object-like macro, the best we can do is to
// highlight the range. If this is a function-like macro, we'd also like to
// highlight the arguments.
- if (Begin == End && R.getEnd().isMacroID())
- End = SM.getExpansionRange(R.getEnd()).second;
+ if (BeginLoc == EndLoc && R.getEnd().isMacroID())
+ EndLoc = SM.getExpansionRange(R.getEnd()).second;
- unsigned StartLineNo = SM.getExpansionLineNumber(Begin);
- if (StartLineNo > LineNo || SM.getFileID(Begin) != FID)
- return; // No intersection.
+ std::pair<FileID, unsigned> BeginInfo = SM.getDecomposedLoc(BeginLoc);
+ std::pair<FileID, unsigned> EndInfo = SM.getDecomposedLoc(EndLoc);
- unsigned EndLineNo = SM.getExpansionLineNumber(End);
- if (EndLineNo < LineNo || SM.getFileID(End) != FID)
- return; // No intersection.
+ // Don't attempt to highlight ranges in different files. (This should
+ // be very rare.)
+ if (BeginInfo.first != FID || EndInfo.first != FID)
+ return;
+ const char* Begin = BufStart + BeginInfo.second;
+ const char* End = BufStart + EndInfo.second;
+
+ if (Begin > LineEnd || End < LineStart)
+ 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 #.
- }
+ if (Begin > LineStart)
+ StartColNo = Begin - LineStart;
// Compute the column number of the end.
- unsigned EndColNo = CaretLine.size();
- if (EndLineNo == LineNo) {
- EndColNo = SM.getExpansionColumnNumber(End);
- if (EndColNo) {
- --EndColNo; // Zero base the col #.
+ unsigned EndColTokLen = 0;
+ if (R.isTokenRange())
+ EndColTokLen += Lexer::MeasureTokenLength(EndLoc, SM, LangOpts);
+ unsigned EndColNo = LineEnd - LineStart;
+ if (End + EndColTokLen < LineEnd)
+ EndColNo = End + EndColTokLen - LineStart;
- // 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!");
+ StartColNo = LineOffsetMap[StartColNo];
+ EndColNo = LineOffsetMap[EndColNo];
+
+ assert(StartColNo <= EndColNo && "Bad range conversion!");
+ assert(EndColNo <= CaretLine.size() && "Highlight past end of line");
+
// Check that a token range does not highlight only whitespace.
if (R.isTokenRange()) {
// Pick the first non-whitespace column.
@@ -736,7 +737,8 @@
CaretLine[i] = '~';
}
-std::string TextDiagnostic::buildFixItInsertionLine(unsigned LineNo,
+std::string TextDiagnostic::buildFixItInsertionLine(const char *BufStart,
+ FileID FID,
const char *LineStart,
const char *LineEnd,
ArrayRef<FixItHint> Hints) {
@@ -747,25 +749,28 @@
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.
std::pair<FileID, unsigned> HintLocInfo
= SM.getDecomposedExpansionLoc(I->RemoveRange.getBegin());
- if (LineNo == SM.getLineNumber(HintLocInfo.first, HintLocInfo.second)) {
- // Insert the new code into the line just below the code
- // that the user wrote.
- unsigned HintColNo
- = SM.getColumnNumber(HintLocInfo.first, HintLocInfo.second);
- unsigned LastColumnModified
- = HintColNo - 1 + I->CodeToInsert.size();
- if (LastColumnModified > FixItInsertionLine.size())
- FixItInsertionLine.resize(LastColumnModified, ' ');
- std::copy(I->CodeToInsert.begin(), I->CodeToInsert.end(),
- FixItInsertionLine.begin() + HintColNo - 1);
- } else {
+ if (HintLocInfo.first != FID) {
FixItInsertionLine.clear();
break;
}
+ const char* Hint = BufStart + HintLocInfo.second;
+ if (Hint < LineStart || Hint > LineEnd) {
+ FixItInsertionLine.clear();
+ break;
+ }
+
+ // Insert hint.
+ // FIXME: When there are multiple fixits, this will overwrite previous
+ // fixits.
+ unsigned HintColNo = Hint - LineStart;
+ unsigned LastColumnModified
+ = HintColNo + I->CodeToInsert.size();
+ if (LastColumnModified > FixItInsertionLine.size())
+ FixItInsertionLine.resize(LastColumnModified, ' ');
+ std::copy(I->CodeToInsert.begin(), I->CodeToInsert.end(),
+ FixItInsertionLine.begin() + HintColNo);
}
}
@@ -810,29 +815,43 @@
return FixItInsertionLine;
}
-void TextDiagnostic::expandTabs(std::string &SourceLine,
- std::string &CaretLine) {
- // Scan the source line, looking for tabs. If we find any, manually expand
- // them to spaces and update the CaretLine to match.
- for (unsigned i = 0; i != SourceLine.size(); ++i) {
- if (SourceLine[i] != '\t') continue;
+void TextDiagnostic::expandSourceLine(std::string &SourceLine,
+ std::vector<unsigned>& LineOffsetMap/*,
+ std::vector<unsigned>& EscapeOffsets*/) {
+ unsigned TabStop = DiagOpts.TabStop;
+ assert(0 < TabStop && TabStop <= DiagnosticOptions::MaxTabStop &&
+ "Invalid -ftabstop value");
- // Replace this tab with at least one space.
- SourceLine[i] = ' ';
+ // FIXME: CurColNo is overloaded for both bytes in the destination and
+ // columns in the destination; they're identical for the moment, but that
+ // will change when we actually let UTF-8 chars through.
+ // http://www.mail-archive.com/cfe-commits@cs.uiuc.edu/msg44487.html
+ unsigned CurColNo = 0;
+ unsigned NumSourceBytes = SourceLine.size();
+ for (unsigned i = 0; i != NumSourceBytes; ++i) {
+ unsigned CharSize = 1;
+ if (SourceLine[CurColNo] == '\t') {
+ // Compute the number of spaces we need to insert.
+ CharSize = ((CurColNo+TabStop)/TabStop * TabStop) - CurColNo;
+ assert(CharSize && CharSize <= TabStop &&
+ "Invalid computation of space amt");
- // Compute the number of spaces we need to insert.
- unsigned TabStop = DiagOpts.TabStop;
- assert(0 < TabStop && TabStop <= DiagnosticOptions::MaxTabStop &&
- "Invalid -ftabstop value");
- unsigned NumSpaces = ((i+TabStop)/TabStop * TabStop) - (i+1);
- assert(NumSpaces < TabStop && "Invalid computation of space amt");
-
- // Insert spaces into the SourceLine.
- SourceLine.insert(i+1, NumSpaces, ' ');
-
- // Insert spaces or ~'s into CaretLine.
- CaretLine.insert(i+1, NumSpaces, CaretLine[i] == '~' ? '~' : ' ');
+ // Insert spaces into the SourceLine.
+ SourceLine.replace(CurColNo, 1, CharSize, ' ');
+ } else if (SourceLine[CurColNo] < 32 || SourceLine[CurColNo] > 127) {
+ char NewStr[4];
+ NewStr[0] = '<';
+ NewStr[1] = llvm::hexdigit((SourceLine[CurColNo] & 0xFF) / 16, true);
+ NewStr[2] = llvm::hexdigit((SourceLine[CurColNo] & 0xFF) % 16, true);
+ NewStr[3] = '>';
+ SourceLine.replace(CurColNo, 1, NewStr, sizeof(NewStr));
+ CharSize = sizeof(NewStr);
+ //EscapeOffsets.push_back()
+ }
+ LineOffsetMap[i] = CurColNo;
+ CurColNo += CharSize;
}
+ LineOffsetMap[NumSourceBytes] = CurColNo;
}
void TextDiagnostic::emitParseableFixits(ArrayRef<FixItHint> Hints) {
More information about the cfe-commits
mailing list