]> granicus.if.org Git - clang/commitdiff
When printing a source line as part of a diagnostic, the source line
authorDouglas Gregor <dgregor@apple.com>
Fri, 1 May 2009 23:32:58 +0000 (23:32 +0000)
committerDouglas Gregor <dgregor@apple.com>
Fri, 1 May 2009 23:32:58 +0000 (23:32 +0000)
might be wider than we're supposed to print. In this case, we try to
select the "important" subregion of the source line, which contains
everything that we want to show (e.g., with underlining and the caret
itself) and tries to also contain some of the context.

From the fantastically long line in the test case, we get an error
message that slices down to this:

message-length.c:18:120: warning: comparison of distinct pointer types
      ('int *' and 'float *')
  a_func_to_call(ip == FloatPointer, ip[ALongIndexName],
                 ~~ ^  ~~~~~~~~~~~~

There are a bunch of gee-it-sounds-good heuristics in here, which seem
to do well on the various simple tests I've thrown at it. However,
we're going to need to look at a bunch more diagnostics to tweak these
heuristics.

This is the second part of <rdar://problem/6711348>. Almost there!

git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@70597 91177308-0d34-0410-b5e6-96231b3b80d8

include/clang/Frontend/TextDiagnosticPrinter.h
lib/Frontend/TextDiagnosticPrinter.cpp
test/Misc/message-length.c

index b41356d788819fa4cca1c957fabf710f7b1177d3..8b3d0a916bef410fff0a241ba56b82967fa834c2 100644 (file)
@@ -74,7 +74,8 @@ public:
                            SourceManager &SM,
                            const CodeModificationHint *Hints,
                            unsigned NumHints,
-                           unsigned AvoidColumn);
+                           unsigned AvoidColumn,
+                           unsigned Columns);
   
   virtual void HandleDiagnostic(Diagnostic::Level DiagLevel,
                                 const DiagnosticInfo &Info);
index 007dfae5a7b587636e1a61ab011e49f5c338debc..017ead43e4562daf62e1828d6fca509c68c848b1 100644 (file)
@@ -108,20 +108,158 @@ void TextDiagnosticPrinter::HighlightRange(const SourceRange &R,
     CaretLine[i] = '~';
 }
 
+/// \brief Whether this is a closing delimiter such as ')' or ']'.
+static inline bool isClosingDelimiter(char c) {
+  return c == ')' || c == ']' || c == '}';
+}
+
+/// \brief When the source code line we want to print is too long for
+/// the terminal, select the "interesting" region.
+static void SelectInterestingSourceRegion(std::string &SourceLine,
+                                          std::string &CaretLine,
+                                          std::string &FixItInsertionLine,
+                                          unsigned Columns) {
+  if (CaretLine.size() > SourceLine.size())
+    SourceLine.resize(CaretLine.size(), ' ');
+
+  // Find the slice that we need to display the full caret line
+  // correctly.
+  unsigned CaretStart = 0, CaretEnd = CaretLine.size();
+  for (; CaretStart != CaretEnd; ++CaretStart)
+    if (!isspace(CaretLine[CaretStart]))
+      break;
+
+  for (; CaretEnd != CaretStart; --CaretEnd)
+    if (!isspace(CaretLine[CaretEnd - 1]))
+      break;
+    
+  // 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
+  // more context.
+
+  // If the end of the interesting region comes before we run out of
+  // space in the terminal, start at the beginning of the line.
+  if (CaretEnd < Columns)
+    CaretStart = 0;
+
+  unsigned TargetColumns = Columns - 4; // Give us a little extra room.
+  unsigned SourceLength = SourceLine.size();
+  bool StartIsFixed = false;
+  while (CaretEnd - CaretStart < TargetColumns) {
+    bool ExpandedRegion = false;
+    // Move the start of the interesting region left until we've
+    // pulled in something else interesting.
+    if (CaretStart && !StartIsFixed && 
+        CaretEnd - CaretStart < TargetColumns) {
+      unsigned NewStart = CaretStart;
+        
+      bool BadStart = false;
+      do {
+        // Skip over any whitespace we see here; we're looking for
+        // another bit of interesting text.
+        if (NewStart)
+          --NewStart;
+        while (NewStart && isspace(SourceLine[NewStart]))
+          --NewStart;
+          
+        // Skip over this bit of "interesting" text.
+        while (NewStart && !isspace(SourceLine[NewStart])) {
+          if (isClosingDelimiter(SourceLine[NewStart]))
+            StartIsFixed = true;
+          --NewStart;
+        }
+
+        // Move up to the non-whitespace character we just saw.
+        if (!StartIsFixed && 
+            isspace(SourceLine[NewStart]) &&
+            !isspace(SourceLine[NewStart + 1]))
+          ++NewStart;
+
+        // Never go back past closing delimeters, because
+        // they're unlikely to be important (and they result in
+        // weird slices). Instead, move forward to the next
+        // non-whitespace character.
+        BadStart = false;
+        if (StartIsFixed) {
+          ++NewStart;
+          while (NewStart != CaretEnd && isspace(SourceLine[NewStart]))
+            ++NewStart;
+        } else if (NewStart) {
+          // There are some characters that always signal that we've
+          // found a bad stopping place, because they always occur in
+          // the middle of or at the end of an expression. In these
+          // cases, we either keep bringing in more "interesting" text
+          // to try to get to a somewhat-complete slice of the code.
+          BadStart = ispunct(SourceLine[NewStart]);
+        }
+      } while (BadStart);
+
+      // If we're still within our limit, update the starting
+      // position within the source/caret line.
+      if (CaretEnd - NewStart <= TargetColumns && !StartIsFixed) {
+        CaretStart = NewStart;
+        ExpandedRegion = true;
+      }
+    }
+
+    // Move the end of the interesting region right until we've
+    // pulled in something else interesting.
+    if (CaretEnd != SourceLength && 
+        CaretEnd - CaretStart < TargetColumns) {
+      unsigned NewEnd = CaretEnd;
+
+      // Skip over any whitespace we see here; we're looking for
+      // another bit of interesting text.
+      while (CaretEnd != SourceLength && isspace(SourceLine[NewEnd - 1]))
+        ++NewEnd;
+        
+      // Skip over this bit of "interesting" text.
+      while (CaretEnd != SourceLength && !isspace(SourceLine[NewEnd - 1]))
+        ++NewEnd;
+
+      if (NewEnd - CaretStart <= TargetColumns) {
+        CaretEnd = NewEnd;
+        ExpandedRegion = true;
+      }
+
+      if (!ExpandedRegion)
+        break;
+    }
+  }
+
+  // [CaretStart, CaretEnd) is the slice we want. Update the various
+  // output lines to show only this slice, with two-space padding
+  // before the lines so that it looks nicer.
+  SourceLine.erase(CaretEnd, std::string::npos);
+  CaretLine.erase(CaretEnd, std::string::npos);
+  if (FixItInsertionLine.size() > CaretEnd)
+    FixItInsertionLine.erase(CaretEnd, std::string::npos);
+  
+  if (CaretStart > 2) {
+    SourceLine.replace(0, CaretStart, "  ");
+    CaretLine.replace(0, CaretStart, "  ");
+    if (FixItInsertionLine.size() >= CaretStart)
+      FixItInsertionLine.replace(0, CaretStart, "  ");
+  }
+}
+
 void TextDiagnosticPrinter::EmitCaretDiagnostic(SourceLocation Loc,
                                                 SourceRange *Ranges,
                                                 unsigned NumRanges,
                                                 SourceManager &SM,
                                           const CodeModificationHint *Hints,
                                                 unsigned NumHints,
-                                                unsigned AvoidColumn) {
+                                                unsigned AvoidColumn,
+                                                unsigned Columns) {
   assert(!Loc.isInvalid() && "must have a valid source location here");
   
   // We always emit diagnostics about the instantiation points, not the spelling
   // points.  This more closely correlates to what the user writes.
   if (!Loc.isFileID()) {
     SourceLocation OneLevelUp = SM.getImmediateInstantiationRange(Loc).first;
-    EmitCaretDiagnostic(OneLevelUp, Ranges, NumRanges, SM, 0, 0, AvoidColumn);
+    EmitCaretDiagnostic(OneLevelUp, Ranges, NumRanges, SM, 0, 0, AvoidColumn,
+                        Columns);
     
     // Map the location through the macro.
     Loc = SM.getInstantiationLoc(SM.getImmediateSpellingLoc(Loc));
@@ -210,10 +348,6 @@ void TextDiagnosticPrinter::EmitCaretDiagnostic(SourceLocation Loc,
     CaretLine.insert(i+1, NumSpaces, CaretLine[i] == '~' ? '~' : ' ');
   }
   
-  // Finally, remove any blank spaces from the end of CaretLine.
-  while (CaretLine[CaretLine.size()-1] == ' ')
-    CaretLine.erase(CaretLine.end()-1);
-  
   // 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 line from what
@@ -222,27 +356,9 @@ void TextDiagnosticPrinter::EmitCaretDiagnostic(SourceLocation Loc,
     SourceLine = ' ' + SourceLine;
     CaretLine = ' ' + CaretLine;
   }
-  
-  // AvoidColumn tells us which column we should avoid when printing
-  // the source line. If the source line would start at or near that
-  // column, add another line of whitespace before printing the source
-  // line. Otherwise, the source line and the diagnostic text can get
-  // jumbled together.
-  unsigned StartCol = 0;
-  for (unsigned N = SourceLine.size(); StartCol != N; ++StartCol)
-    if (!isspace(SourceLine[StartCol]))
-      break;
-  
-  if (StartCol != SourceLine.size() &&
-      abs((int)StartCol - (int)AvoidColumn) <= 2)
-    OS << '\n';
-  
-  // Emit what we have computed.
-  OS << SourceLine << '\n';
-  OS << CaretLine << '\n';
-
+    
+  std::string FixItInsertionLine;
   if (NumHints && PrintFixItInfo) {
-    std::string InsertionLine;
     for (const CodeModificationHint *Hint = Hints, *LastHint = Hints + NumHints;
          Hint != LastHint; ++Hint) {
       if (Hint->InsertionLoc.isValid()) {
@@ -258,19 +374,47 @@ void TextDiagnosticPrinter::EmitCaretDiagnostic(SourceLocation Loc,
             = SM.getColumnNumber(HintLocInfo.first, HintLocInfo.second);
           unsigned LastColumnModified 
             = HintColNo - 1 + Hint->CodeToInsert.size();
-          if (LastColumnModified > InsertionLine.size())
-            InsertionLine.resize(LastColumnModified, ' ');
+          if (LastColumnModified > FixItInsertionLine.size())
+            FixItInsertionLine.resize(LastColumnModified, ' ');
           std::copy(Hint->CodeToInsert.begin(), Hint->CodeToInsert.end(),
-                    InsertionLine.begin() + HintColNo - 1);
+                    FixItInsertionLine.begin() + HintColNo - 1);
         }
       }
     }
+  }
 
-    if (!InsertionLine.empty()) {
-      if (PrintRangeInfo) 
-        OS << ' ';
-      OS << InsertionLine << '\n';
-    }
+  // If the source line is too long for our terminal, select only the
+  // "interesting" source region within that line.
+  if (Columns && SourceLine.size() > Columns)
+    SelectInterestingSourceRegion(SourceLine, CaretLine, FixItInsertionLine,
+                                  Columns);    
+
+  // AvoidColumn tells us which column we should avoid when printing
+  // the source line. If the source line would start at or near that
+  // column, add another line of whitespace before printing the source
+  // line. Otherwise, the source line and the diagnostic text can get
+  // jumbled together.
+  unsigned StartCol = 0;
+  for (unsigned N = SourceLine.size(); StartCol != N; ++StartCol)
+    if (!isspace(SourceLine[StartCol]))
+      break;
+  
+  if (StartCol != SourceLine.size() &&
+      abs((int)StartCol - (int)AvoidColumn) <= 2)
+    OS << '\n';
+
+  // Finally, remove any blank spaces from the end of CaretLine.
+  while (CaretLine[CaretLine.size()-1] == ' ')
+    CaretLine.erase(CaretLine.end()-1);
+  
+  // Emit what we have computed.
+  OS << SourceLine << '\n';
+  OS << CaretLine << '\n';
+
+  if (!FixItInsertionLine.empty()) {
+    if (PrintRangeInfo) 
+      OS << ' ';
+    OS << FixItInsertionLine << '\n';
   }
 }
 
@@ -572,7 +716,8 @@ void TextDiagnosticPrinter::HandleDiagnostic(Diagnostic::Level Level,
     EmitCaretDiagnostic(LastLoc, Ranges, NumRanges, LastLoc.getManager(),
                         Info.getCodeModificationHints(),
                         Info.getNumCodeModificationHints(),
-                        WordWrapped? WordWrapIndentation : 0);
+                        WordWrapped? WordWrapIndentation : 0,
+                        MessageLength);
   }
   
   OS.flush();
index a99889237c24f955751a5f16959f271f82917c54..8e75776e2a6326bdd2b0f0577abbc69069f4197d 100644 (file)
@@ -11,3 +11,12 @@ void g() {
 
   int (*fp2)(int, float, short, float) = f;
 }
+
+void a_func_to_call(int, int, int);
+
+void a_very_long_line(int *ip, float *FloatPointer) {
+  for (int ALongIndexName = 0; ALongIndexName < 100; ALongIndexName++) if (ip[ALongIndexName] == 17) a_func_to_call(ip == FloatPointer, ip[ALongIndexName], FloatPointer[ALongIndexName]);
+
+
+  int array0[] = { [3] 3, 5, 7, 4, 2, 7, 6, 3, 4, 5, 6, 7, 8, 9, 12, 345, 14, 345, 789, 234, 678, 345, 123, 765, 234 };
+}