[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