Introduce code modification hints into the diagnostics system. When we
know how to recover from an error, we can attach a hint to the
diagnostic that states how to modify the code, which can be one of:

  - Insert some new code (a text string) at a particular source
    location
  - Remove the code within a given range
  - Replace the code within a given range with some new code (a text
    string)

Right now, we use these hints to annotate diagnostic information. For
example, if one uses the '>>' in a template argument in C++98, as in
this code:

  template<int I> class B { };
  B<1000 >> 2> *b1;

we'll warn that the behavior will change in C++0x. The fix is to
insert parenthese, so we use code insertion annotations to illustrate
where the parentheses go:

test.cpp:10:10: warning: use of right-shift operator ('>>') in template
argument will require parentheses in C++0x
  B<1000 >> 2> *b1;
         ^
    (        )


Use of these annotations is partially implemented for HTML
diagnostics, but it's not (yet) producing valid HTML, which may be
related to PR2386, so it has been #if 0'd out.

In this future, we could consider hooking this mechanism up to the
rewriter to actually try to fix these problems during compilation (or,
after a compilation whose only errors have fixes). For now, however, I
suggest that we use these code modification hints whenever we can, so
that we get better diagnostics now and will have better coverage when
we find better ways to use this information.

This also fixes PR3410 by placing the complaint about missing tokens
just after the previous token (rather than at the location of the next
token).




git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@65570 91177308-0d34-0410-b5e6-96231b3b80d8
diff --git a/lib/Driver/HTMLDiagnostics.cpp b/lib/Driver/HTMLDiagnostics.cpp
index 6a4bf23..8ab9b18 100644
--- a/lib/Driver/HTMLDiagnostics.cpp
+++ b/lib/Driver/HTMLDiagnostics.cpp
@@ -51,7 +51,9 @@
   void HandlePiece(Rewriter& R, FileID BugFileID,
                    const PathDiagnosticPiece& P, unsigned num, unsigned max);
   
-  void HighlightRange(Rewriter& R, FileID BugFileID, SourceRange Range);
+  void HighlightRange(Rewriter& R, FileID BugFileID, SourceRange Range,
+                      const char *HighlightStart = "<span class=\"mrange\">",
+                      const char *HighlightEnd = "</span>");
 
   void ReportDiag(const PathDiagnostic& D);
 };
@@ -438,10 +440,33 @@
   for (const SourceRange *I = P.ranges_begin(), *E = P.ranges_end();
         I != E; ++I)
     HighlightRange(R, LPosInfo.first, *I);
+
+#if 0
+  // If there is a code insertion hint, insert that code.
+  // FIXME: This code is disabled because it seems to mangle the HTML
+  // output. I'm leaving it here because it's generally the right idea,
+  // but needs some help from someone more familiar with the rewriter.
+  for (const CodeModificationHint *Hint = P.code_modifications_begin(),
+                               *HintEnd = P.code_modifications_end();
+       Hint != HintEnd; ++Hint) {
+    if (Hint->RemoveRange.isValid()) {
+      HighlightRange(R, LPosInfo.first, Hint->RemoveRange,
+                     "<span class=\"CodeRemovalHint\">", "</span>");
+    }
+    if (Hint->InsertionLoc.isValid()) {
+      std::string EscapedCode = html::EscapeText(Hint->CodeToInsert, true);
+      EscapedCode = "<span class=\"CodeInsertionHint\">" + EscapedCode
+        + "</span>";
+      R.InsertStrBefore(Hint->InsertionLoc, EscapedCode);
+    }
+  }
+#endif
 }
 
 void HTMLDiagnostics::HighlightRange(Rewriter& R, FileID BugFileID,
-                                     SourceRange Range) {
+                                     SourceRange Range,
+                                     const char *HighlightStart,
+                                     const char *HighlightEnd) {
   
   SourceManager& SM = R.getSourceMgr();
   
@@ -473,6 +498,5 @@
   SourceLocation E =
     InstantiationEnd.getFileLocWithOffset(EndColNo - OldEndColNo);
   
-  html::HighlightRange(R, InstantiationStart, E,
-                       "<span class=\"mrange\">", "</span>");
+  html::HighlightRange(R, InstantiationStart, E, HighlightStart, HighlightEnd);
 }
diff --git a/lib/Driver/TextDiagnosticPrinter.cpp b/lib/Driver/TextDiagnosticPrinter.cpp
index b5226f5..8a7e40f 100644
--- a/lib/Driver/TextDiagnosticPrinter.cpp
+++ b/lib/Driver/TextDiagnosticPrinter.cpp
@@ -16,6 +16,7 @@
 #include "clang/Lex/Lexer.h"
 #include "llvm/Support/raw_ostream.h"
 #include "llvm/ADT/SmallString.h"
+#include <algorithm>
 using namespace clang;
 
 void TextDiagnosticPrinter::
@@ -104,7 +105,9 @@
 void TextDiagnosticPrinter::EmitCaretDiagnostic(SourceLocation Loc,
                                                 SourceRange *Ranges,
                                                 unsigned NumRanges,
-                                                SourceManager &SM) {
+                                                SourceManager &SM,
+                                          const CodeModificationHint *Hints,
+                                                unsigned NumHints) {
   assert(!Loc.isInvalid() && "must have a valid source location here");
   
   // We always emit diagnostics about the instantiation points, not the spelling
@@ -205,6 +208,36 @@
   // Emit what we have computed.
   OS << SourceLine << '\n';
   OS << CaretLine << '\n';
+
+  if (NumHints) {
+    std::string InsertionLine;
+    for (const CodeModificationHint *Hint = Hints, 
+                                *LastHint = Hints + NumHints;
+         Hint != LastHint; ++Hint) {
+      if (Hint->InsertionLoc.isValid()) {
+        // We have an insertion hint. Determine whether the inserted
+        // code is on the same line as the caret.
+        std::pair<FileID, unsigned> HintLocInfo 
+          = SM.getDecomposedLoc(Hint->InsertionLoc);
+        if (SM.getLineNumber(HintLocInfo.first, HintLocInfo.second) ==
+              SM.getLineNumber(FID, FileOffset)) {
+          // 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 + Hint->CodeToInsert.size();
+          if (LastColumnModified > InsertionLine.size())
+            InsertionLine.resize(LastColumnModified, ' ');
+          std::copy(Hint->CodeToInsert.begin(), Hint->CodeToInsert.end(),
+                    InsertionLine.begin() + HintColNo - 1);
+        }
+      }
+    }
+
+    if (!InsertionLine.empty())
+      OS << InsertionLine << '\n';
+  }
 }
 
 
@@ -252,18 +285,30 @@
   // diagnostic, or if the diagnostic has ranges.  We don't want to emit the
   // same caret multiple times if one loc has multiple diagnostics.
   if (CaretDiagnostics && Info.getLocation().isValid() &&
-      ((LastLoc != Info.getLocation()) || Info.getNumRanges())) {
+      ((LastLoc != Info.getLocation()) || Info.getNumRanges() || 
+       Info.getNumCodeModificationHints())) {
     // Cache the LastLoc, it allows us to omit duplicate source/caret spewage.
     LastLoc = Info.getLocation();
 
     // Get the ranges into a local array we can hack on.
-    SourceRange Ranges[10];
+    SourceRange Ranges[20];
     unsigned NumRanges = Info.getNumRanges();
-    assert(NumRanges < 10 && "Out of space");
+    assert(NumRanges < 20 && "Out of space");
     for (unsigned i = 0; i != NumRanges; ++i)
       Ranges[i] = Info.getRange(i);
     
-    EmitCaretDiagnostic(LastLoc, Ranges, NumRanges, LastLoc.getManager());
+    unsigned NumHints = Info.getNumCodeModificationHints();
+    for (unsigned idx = 0; idx < NumHints; ++idx) {
+      const CodeModificationHint &Hint = Info.getCodeModificationHint(idx);
+      if (Hint.RemoveRange.isValid()) {
+        assert(NumRanges < 20 && "Out of space");
+        Ranges[NumRanges++] = Hint.RemoveRange;
+      }
+    }
+
+    EmitCaretDiagnostic(LastLoc, Ranges, NumRanges, LastLoc.getManager(),
+                        Info.getCodeModificationHints(),
+                        Info.getNumCodeModificationHints());
   }
   
   OS.flush();