[cfe-commits] r65570 - in /cfe/trunk: include/clang/Analysis/ include/clang/Basic/ include/clang/Driver/ include/clang/Parse/ lib/Analysis/ lib/Basic/ lib/Driver/ lib/Parse/ lib/Rewrite/ lib/Sema/ test/Parser/ test/SemaTemplate/

Douglas Gregor dgregor at apple.com
Thu Feb 26 13:00:50 PST 2009


Author: dgregor
Date: Thu Feb 26 15:00:50 2009
New Revision: 65570

URL: http://llvm.org/viewvc/llvm-project?rev=65570&view=rev
Log:
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).



Modified:
    cfe/trunk/include/clang/Analysis/PathDiagnostic.h
    cfe/trunk/include/clang/Basic/Diagnostic.h
    cfe/trunk/include/clang/Basic/DiagnosticParseKinds.def
    cfe/trunk/include/clang/Basic/TokenKinds.h
    cfe/trunk/include/clang/Driver/TextDiagnosticPrinter.h
    cfe/trunk/include/clang/Parse/Action.h
    cfe/trunk/include/clang/Parse/Parser.h
    cfe/trunk/lib/Analysis/PathDiagnostic.cpp
    cfe/trunk/lib/Basic/TokenKinds.cpp
    cfe/trunk/lib/Driver/HTMLDiagnostics.cpp
    cfe/trunk/lib/Driver/TextDiagnosticPrinter.cpp
    cfe/trunk/lib/Parse/ParseExpr.cpp
    cfe/trunk/lib/Parse/ParseTemplate.cpp
    cfe/trunk/lib/Parse/Parser.cpp
    cfe/trunk/lib/Rewrite/HTMLRewrite.cpp
    cfe/trunk/lib/Sema/Sema.h
    cfe/trunk/lib/Sema/SemaExpr.cpp
    cfe/trunk/lib/Sema/SemaTemplate.cpp
    cfe/trunk/test/Parser/cxx-using-directive.cpp
    cfe/trunk/test/Parser/recovery.c
    cfe/trunk/test/SemaTemplate/right-angle-brackets-98.cpp
    cfe/trunk/test/SemaTemplate/temp_arg_nontype.cpp

Modified: cfe/trunk/include/clang/Analysis/PathDiagnostic.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Analysis/PathDiagnostic.h?rev=65570&r1=65569&r2=65570&view=diff

==============================================================================
--- cfe/trunk/include/clang/Analysis/PathDiagnostic.h (original)
+++ cfe/trunk/include/clang/Analysis/PathDiagnostic.h Thu Feb 26 15:00:50 2009
@@ -31,6 +31,7 @@
 private:
   FullSourceLoc Pos;
   std::string str;
+  std::vector<CodeModificationHint> CodeModificationHints;
   DisplayHint Hint;
   std::vector<SourceRange> ranges;
   
@@ -56,6 +57,10 @@
     ranges.push_back(SourceRange(B,E));
   }
   
+  void addCodeModificationHint(const CodeModificationHint& Hint) {
+    CodeModificationHints.push_back(Hint);
+  }
+
   typedef const SourceRange* range_iterator;
   
   range_iterator ranges_begin() const {
@@ -65,7 +70,18 @@
   range_iterator ranges_end() const { 
     return ranges_begin() + ranges.size();
   }
-    
+
+  typedef const CodeModificationHint *code_modifications_iterator;
+
+  code_modifications_iterator code_modifications_begin() const {
+    return CodeModificationHints.empty()? 0 : &CodeModificationHints[0];
+  }
+
+  code_modifications_iterator code_modifications_end() const {
+    return CodeModificationHints.empty()? 0 
+                   : &CodeModificationHints[0] + CodeModificationHints.size();
+  }
+
   const SourceManager& getSourceManager() const {
     return Pos.getManager();
   }

Modified: cfe/trunk/include/clang/Basic/Diagnostic.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Basic/Diagnostic.h?rev=65570&r1=65569&r2=65570&view=diff

==============================================================================
--- cfe/trunk/include/clang/Basic/Diagnostic.h (original)
+++ cfe/trunk/include/clang/Basic/Diagnostic.h Thu Feb 26 15:00:50 2009
@@ -68,6 +68,69 @@
     };
   }
   
+/// \brief Annotates a diagnostic with some code that should be
+/// inserted, removed, or replaced to fix the problem.
+///
+/// This kind of hint should be used when we are certain that the
+/// introduction, removal, or modification of a particular (small!)
+/// amount of code will correct a compilation error. The compiler
+/// should also provide full recovery from such errors, such that
+/// suppressing the diagnostic output can still result successful
+/// compilation.
+class CodeModificationHint {
+public:
+  /// \brief Tokens that should be removed to correct the error.
+  SourceRange RemoveRange;
+
+  /// \brief The location at which we should insert code to correct
+  /// the error.
+  SourceLocation InsertionLoc;
+
+  /// \brief The actual code to insert at the insertion location, as a
+  /// string.
+  std::string CodeToInsert;
+
+  /// \brief Empty code modification hint, indicating that no code
+  /// modification is known.
+  CodeModificationHint() : RemoveRange(), InsertionLoc() { }
+
+  /// \brief Create a code modification hint that inserts the given
+  /// code string at a specific location.
+  CodeModificationHint(SourceLocation InsertionLoc, const std::string &Code)
+    : RemoveRange(), InsertionLoc(InsertionLoc), CodeToInsert(Code) { }
+
+  /// \brief Create a code modification hint that removes the given
+  /// source range.
+  CodeModificationHint(SourceRange RemoveRange)
+    : RemoveRange(RemoveRange), InsertionLoc(), CodeToInsert() { }
+
+  /// \brief Create a code modification hint that replaces the given
+  /// source range with the given code string.
+  CodeModificationHint(SourceRange RemoveRange, const std::string &Code)
+    : RemoveRange(RemoveRange), InsertionLoc(RemoveRange.getBegin()),
+      CodeToInsert(Code) { }
+};
+
+/// \brief Creates a code modification hint that inserts the given
+/// string at a particular location in the source code.
+inline CodeModificationHint 
+CodeInsertionHint(SourceLocation InsertionLoc, const std::string &Code) {
+  return CodeModificationHint(InsertionLoc, Code);
+}
+
+/// \brief Creates a code modification hint that removes the given
+/// source range.
+inline CodeModificationHint CodeRemovalHint(SourceRange RemoveRange) {
+  return CodeModificationHint(RemoveRange);
+}
+
+/// \brief Creates a code modification hint that replaces the given
+/// source range with the given code string.
+inline CodeModificationHint 
+CodeReplacementHint(SourceRange RemoveRange, const std::string &Code) {
+  return CodeModificationHint(RemoveRange, Code);
+}
+
 /// Diagnostic - This concrete class is used by the front-end to report
 /// problems and issues.  It massages the diagnostics (e.g. handling things like
 /// "report warnings as errors" and passes them off to the DiagnosticClient for
@@ -272,6 +335,9 @@
   signed char NumDiagArgs;
   /// NumRanges - This is the number of ranges in the DiagRanges array.
   unsigned char NumDiagRanges;
+  /// \brief The number of code modifications hints in the
+  /// CodeModificationHints array.
+  unsigned char NumCodeModificationHints;
   
   /// DiagArgumentsKind - This is an array of ArgumentKind::ArgumentKind enum
   /// values, with one for each argument.  This specifies whether the argument
@@ -293,6 +359,12 @@
   /// only support 10 ranges, could easily be extended if needed.
   const SourceRange *DiagRanges[10];
   
+  enum { MaxCodeModificationHints = 3 };
+
+  /// CodeModificationHints - If valid, provides a hint with some code
+  /// to insert, remove, or modify at a particular position.
+  CodeModificationHint CodeModificationHints[MaxCodeModificationHints];
+
   /// ProcessDiag - This is the method used to report a diagnostic that is
   /// finally fully formed.
   void ProcessDiag();
@@ -315,12 +387,13 @@
 /// for example.
 class DiagnosticBuilder {
   mutable Diagnostic *DiagObj;
-  mutable unsigned NumArgs, NumRanges;
+  mutable unsigned NumArgs, NumRanges, NumCodeModificationHints;
   
   void operator=(const DiagnosticBuilder&); // DO NOT IMPLEMENT
   friend class Diagnostic;
   explicit DiagnosticBuilder(Diagnostic *diagObj)
-    : DiagObj(diagObj), NumArgs(0), NumRanges(0) {}
+    : DiagObj(diagObj), NumArgs(0), NumRanges(0), 
+      NumCodeModificationHints(0) {}
 public:
   
   /// Copy constructor.  When copied, this "takes" the diagnostic info from the
@@ -339,6 +412,7 @@
     // the Diagnostic object.
     DiagObj->NumDiagArgs = NumArgs;
     DiagObj->NumDiagRanges = NumRanges;
+    DiagObj->NumCodeModificationHints = NumCodeModificationHints;
 
     // Process the diagnostic, sending the accumulated information to the
     // DiagnosticClient.
@@ -373,6 +447,12 @@
            "Too many arguments to diagnostic!");
     DiagObj->DiagRanges[NumRanges++] = &R;
   }    
+
+  void AddCodeModificationHint(const CodeModificationHint &Hint) const {
+    assert(NumCodeModificationHints < Diagnostic::MaxCodeModificationHints &&
+           "Too many code modification hints!");
+    DiagObj->CodeModificationHints[NumCodeModificationHints++] = Hint;
+  }
 };
 
 inline const DiagnosticBuilder &operator<<(const DiagnosticBuilder &DB,
@@ -416,6 +496,12 @@
   DB.AddSourceRange(R);
   return DB;
 }
+
+inline const DiagnosticBuilder &operator<<(const DiagnosticBuilder &DB,
+                                           const CodeModificationHint &Hint) {
+  DB.AddCodeModificationHint(Hint);
+  return DB;
+}
   
 
 /// Report - Issue the message to the client.  DiagID is a member of the
@@ -433,7 +519,7 @@
 //===----------------------------------------------------------------------===//
   
 /// DiagnosticInfo - This is a little helper class (which is basically a smart
-/// pointer that forward info from Diagnostic) that allows clients ot enquire
+/// pointer that forward info from Diagnostic) that allows clients to enquire
 /// about the currently in-flight diagnostic.
 class DiagnosticInfo {
   const Diagnostic *DiagObj;
@@ -507,14 +593,25 @@
     return *DiagObj->DiagRanges[Idx];
   }
   
-  
+  unsigned getNumCodeModificationHints() const {
+    return DiagObj->NumCodeModificationHints;
+  }
+
+  const CodeModificationHint &getCodeModificationHint(unsigned Idx) const {
+    return DiagObj->CodeModificationHints[Idx];
+  }
+
+  const CodeModificationHint *getCodeModificationHints() const {
+    return DiagObj->NumCodeModificationHints? 
+             &DiagObj->CodeModificationHints[0] : 0;
+  }
+
   /// FormatDiagnostic - Format this diagnostic into a string, substituting the
   /// formal arguments into the %0 slots.  The result is appended onto the Str
   /// array.
   void FormatDiagnostic(llvm::SmallVectorImpl<char> &OutStr) const;
 };
   
-
 /// DiagnosticClient - This is an abstract interface implemented by clients of
 /// the front-end, which formats and prints fully processed diagnostics.
 class DiagnosticClient {

Modified: cfe/trunk/include/clang/Basic/DiagnosticParseKinds.def
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Basic/DiagnosticParseKinds.def?rev=65570&r1=65569&r2=65570&view=diff

==============================================================================
--- cfe/trunk/include/clang/Basic/DiagnosticParseKinds.def (original)
+++ cfe/trunk/include/clang/Basic/DiagnosticParseKinds.def Thu Feb 26 15:00:50 2009
@@ -280,6 +280,9 @@
      "expected '<' after 'template %0' in nested name specifier")
 DIAG(err_two_right_angle_brackets_need_space, ERROR,
      "a space is required between consecutive right angle brackets (use '> >')")
+DIAG(warn_cxx0x_right_shift_in_template_arg, WARNING,
+     "use of right-shift operator ('>>') in template argument will require "
+     "parentheses in C++0x")
 
 // Language specific pragmas
 

Modified: cfe/trunk/include/clang/Basic/TokenKinds.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Basic/TokenKinds.h?rev=65570&r1=65569&r2=65570&view=diff

==============================================================================
--- cfe/trunk/include/clang/Basic/TokenKinds.h (original)
+++ cfe/trunk/include/clang/Basic/TokenKinds.h Thu Feb 26 15:00:50 2009
@@ -44,6 +44,7 @@
 };
 
 const char *getTokenName(enum TokenKind Kind);
+const char *getTokenSpelling(enum TokenKind Kind);
 
 }  // end namespace tok
 }  // end namespace clang

Modified: cfe/trunk/include/clang/Driver/TextDiagnosticPrinter.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Driver/TextDiagnosticPrinter.h?rev=65570&r1=65569&r2=65570&view=diff

==============================================================================
--- cfe/trunk/include/clang/Driver/TextDiagnosticPrinter.h (original)
+++ cfe/trunk/include/clang/Driver/TextDiagnosticPrinter.h Thu Feb 26 15:00:50 2009
@@ -48,7 +48,9 @@
 
   void EmitCaretDiagnostic(SourceLocation Loc, 
                            SourceRange *Ranges, unsigned NumRanges,
-                           SourceManager &SM);
+                           SourceManager &SM,
+                           const CodeModificationHint *Hints = 0,
+                           unsigned NumHints = 0);
   
   virtual void HandleDiagnostic(Diagnostic::Level DiagLevel,
                                 const DiagnosticInfo &Info);

Modified: cfe/trunk/include/clang/Parse/Action.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Parse/Action.h?rev=65570&r1=65569&r2=65570&view=diff

==============================================================================
--- cfe/trunk/include/clang/Parse/Action.h (original)
+++ cfe/trunk/include/clang/Parse/Action.h Thu Feb 26 15:00:50 2009
@@ -551,6 +551,12 @@
 
   // Primary Expressions.
 
+  /// \brief Retrieve the source range that corresponds to the given
+  /// expression.
+  virtual SourceRange getExprRange(ExprTy *E) const {
+    return SourceRange();
+  }
+
   /// ActOnIdentifierExpr - Parse an identifier in expression context.
   /// 'HasTrailingLParen' indicates whether or not the identifier has a '('
   /// token immediately after it.

Modified: cfe/trunk/include/clang/Parse/Parser.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Parse/Parser.h?rev=65570&r1=65569&r2=65570&view=diff

==============================================================================
--- cfe/trunk/include/clang/Parse/Parser.h (original)
+++ cfe/trunk/include/clang/Parse/Parser.h Thu Feb 26 15:00:50 2009
@@ -37,6 +37,12 @@
   /// that this is valid.
   Token Tok;
   
+  // PrevTokLocation - The location of the token we previously
+  // consumed. This token is used for diagnostics where we expected to
+  // see a token following another token (e.g., the ';' at the end of
+  // a statement).
+  SourceLocation PrevTokLocation;
+
   unsigned short ParenCount, BracketCount, BraceCount;
 
   /// Actions - These are the callbacks we invoke as we parse various constructs
@@ -175,9 +181,9 @@
     assert(!isTokenStringLiteral() && !isTokenParen() && !isTokenBracket() &&
            !isTokenBrace() &&
            "Should consume special tokens with Consume*Token");
-    SourceLocation L = Tok.getLocation();
+    PrevTokLocation = Tok.getLocation();
     PP.Lex(Tok);
-    return L;
+    return PrevTokLocation;
   }
   
   /// ConsumeAnyToken - Dispatch to the right Consume* method based on the
@@ -204,9 +210,9 @@
       ++ParenCount;
     else if (ParenCount)
       --ParenCount;       // Don't let unbalanced )'s drive the count negative.
-    SourceLocation L = Tok.getLocation();
+    PrevTokLocation = Tok.getLocation();
     PP.Lex(Tok);
-    return L;
+    return PrevTokLocation;
   }
   
   /// ConsumeBracket - This consume method keeps the bracket count up-to-date.
@@ -218,9 +224,9 @@
     else if (BracketCount)
       --BracketCount;     // Don't let unbalanced ]'s drive the count negative.
     
-    SourceLocation L = Tok.getLocation();
+    PrevTokLocation = Tok.getLocation();
     PP.Lex(Tok);
-    return L;
+    return PrevTokLocation;
   }
       
   /// ConsumeBrace - This consume method keeps the brace count up-to-date.
@@ -232,9 +238,9 @@
     else if (BraceCount)
       --BraceCount;     // Don't let unbalanced }'s drive the count negative.
     
-    SourceLocation L = Tok.getLocation();
+    PrevTokLocation = Tok.getLocation();
     PP.Lex(Tok);
-    return L;
+    return PrevTokLocation;
   }
   
   /// ConsumeStringToken - Consume the current 'peek token', lexing a new one
@@ -244,9 +250,9 @@
   SourceLocation ConsumeStringToken() {
     assert(isTokenStringLiteral() &&
            "Should only consume string literals with this method");
-    SourceLocation L = Tok.getLocation();
+    PrevTokLocation = Tok.getLocation();
     PP.Lex(Tok);
-    return L;
+    return PrevTokLocation;
   }
   
   /// GetLookAheadToken - This peeks ahead N tokens and returns that token
@@ -399,6 +405,9 @@
   DiagnosticBuilder Diag(SourceLocation Loc, unsigned DiagID);
   DiagnosticBuilder Diag(const Token &Tok, unsigned DiagID);
 
+  void SuggestParentheses(SourceLocation Loc, unsigned DK, 
+                          SourceRange ParenRange);
+
   /// SkipUntil - Read tokens until we get to the specified token, then consume
   /// it (unless DontConsume is true).  Because we cannot guarantee that the
   /// token will ever occur, this skips to the next token, or to some likely

Modified: cfe/trunk/lib/Analysis/PathDiagnostic.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Analysis/PathDiagnostic.cpp?rev=65570&r1=65569&r2=65570&view=diff

==============================================================================
--- cfe/trunk/lib/Analysis/PathDiagnostic.cpp (original)
+++ cfe/trunk/lib/Analysis/PathDiagnostic.cpp Thu Feb 26 15:00:50 2009
@@ -47,6 +47,8 @@
   
   for (unsigned i = 0, e = Info.getNumRanges(); i != e; ++i)
     P->addRange(Info.getRange(i));
+  for (unsigned i = 0, e = Info.getNumCodeModificationHints(); i != e; ++i)
+    P->addCodeModificationHint(Info.getCodeModificationHint(i));
   D->push_front(P);
 
   HandlePathDiagnostic(D);  

Modified: cfe/trunk/lib/Basic/TokenKinds.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Basic/TokenKinds.cpp?rev=65570&r1=65569&r2=65570&view=diff

==============================================================================
--- cfe/trunk/lib/Basic/TokenKinds.cpp (original)
+++ cfe/trunk/lib/Basic/TokenKinds.cpp Thu Feb 26 15:00:50 2009
@@ -27,3 +27,66 @@
   assert(Kind < tok::NUM_TOKENS);
   return TokNames[Kind];
 }
+
+/// \brief Determines the spelling of simple punctuation tokens like
+/// '!' or '%', and returns NULL for literal and annotation tokens.
+const char *tok::getTokenSpelling(enum TokenKind Kind) {
+  switch (Kind) {
+  case tok::l_square:            return "[";
+  case tok::r_square:            return "]";
+  case tok::l_paren:             return "(";
+  case tok::r_paren:             return ")";
+  case tok::l_brace:             return "{";
+  case tok::r_brace:             return "}";
+  case tok::period:              return ".";
+  case tok::ellipsis:            return "...";
+  case tok::amp:                 return "&";
+  case tok::ampamp:              return "&&";
+  case tok::ampequal:            return "&=";
+  case tok::star:                return "*";
+  case tok::starequal:           return "*=";
+  case tok::plus:                return "+";
+  case tok::plusplus:            return "++";
+  case tok::plusequal:           return "+=";
+  case tok::minus:               return "-";
+  case tok::arrow:               return "->";
+  case tok::minusminus:          return "--";
+  case tok::minusequal:          return "-=";
+  case tok::tilde:               return "~";
+  case tok::exclaim:             return "!";
+  case tok::exclaimequal:        return "!=";
+  case tok::slash:               return "/";
+  case tok::slashequal:          return "/=";
+  case tok::percent:             return "%";
+  case tok::percentequal:        return "%=";
+  case tok::less:                return "<";
+  case tok::lessless:            return "<<";
+  case tok::lessequal:           return "<=";
+  case tok::lesslessequal:       return "<<=";
+  case tok::greater:             return ">";
+  case tok::greatergreater:      return ">>";
+  case tok::greaterequal:        return ">=";
+  case tok::greatergreaterequal: return ">>=";
+  case tok::caret:               return "^";
+  case tok::caretequal:          return "^=";
+  case tok::pipe:                return "|";
+  case tok::pipepipe:            return "||";
+  case tok::pipeequal:           return "|=";
+  case tok::question:            return "?";
+  case tok::colon:               return ":";
+  case tok::semi:                return ";";
+  case tok::equal:               return "=";
+  case tok::equalequal:          return "==";
+  case tok::comma:               return ",";
+  case tok::hash:                return "#";
+  case tok::hashhash:            return "##";
+  case tok::hashat:              return "#@";
+  case tok::periodstar:          return ".*";
+  case tok::arrowstar:           return "->*";
+  case tok::coloncolon:          return "::";
+  case tok::at:                  return "@";
+  default: break;
+  }
+
+  return 0;
+}

Modified: cfe/trunk/lib/Driver/HTMLDiagnostics.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Driver/HTMLDiagnostics.cpp?rev=65570&r1=65569&r2=65570&view=diff

==============================================================================
--- cfe/trunk/lib/Driver/HTMLDiagnostics.cpp (original)
+++ cfe/trunk/lib/Driver/HTMLDiagnostics.cpp Thu Feb 26 15:00:50 2009
@@ -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);
 }

Modified: cfe/trunk/lib/Driver/TextDiagnosticPrinter.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Driver/TextDiagnosticPrinter.cpp?rev=65570&r1=65569&r2=65570&view=diff

==============================================================================
--- cfe/trunk/lib/Driver/TextDiagnosticPrinter.cpp (original)
+++ cfe/trunk/lib/Driver/TextDiagnosticPrinter.cpp Thu Feb 26 15:00:50 2009
@@ -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();

Modified: cfe/trunk/lib/Parse/ParseExpr.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Parse/ParseExpr.cpp?rev=65570&r1=65569&r2=65570&view=diff

==============================================================================
--- cfe/trunk/lib/Parse/ParseExpr.cpp (original)
+++ cfe/trunk/lib/Parse/ParseExpr.cpp Thu Feb 26 15:00:50 2009
@@ -365,10 +365,19 @@
 
     if (!LHS.isInvalid()) {
       // Combine the LHS and RHS into the LHS (e.g. build AST).
-      if (TernaryMiddle.isInvalid())
+      if (TernaryMiddle.isInvalid()) {
+        // If we're using '>>' as an operator within a template
+        // argument list (in C++98), suggest the addition of
+        // parentheses so that the code remains well-formed in C++0x.
+        if (!GreaterThanIsOperator && OpToken.is(tok::greatergreater))
+          SuggestParentheses(OpToken.getLocation(),
+                             diag::warn_cxx0x_right_shift_in_template_arg,
+                         SourceRange(Actions.getExprRange(LHS.get()).getBegin(),
+                                     Actions.getExprRange(RHS.get()).getEnd()));
+
         LHS = Actions.ActOnBinOp(CurScope, OpToken.getLocation(),
                                  OpToken.getKind(), move(LHS), move(RHS));
-      else
+      } else
         LHS = Actions.ActOnConditionalOp(OpToken.getLocation(), ColonLoc,
                                          move(LHS), move(TernaryMiddle),
                                          move(RHS));

Modified: cfe/trunk/lib/Parse/ParseTemplate.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Parse/ParseTemplate.cpp?rev=65570&r1=65569&r2=65570&view=diff

==============================================================================
--- cfe/trunk/lib/Parse/ParseTemplate.cpp (original)
+++ cfe/trunk/lib/Parse/ParseTemplate.cpp Thu Feb 26 15:00:50 2009
@@ -439,8 +439,14 @@
   RAngleLoc = Tok.getLocation();
 
   if (Tok.is(tok::greatergreater)) {
-    if (!getLang().CPlusPlus0x)
-      Diag(Tok.getLocation(), diag::err_two_right_angle_brackets_need_space);
+    if (!getLang().CPlusPlus0x) {
+      const char *ReplaceStr = "> >";
+      if (NextToken().is(tok::greater) || NextToken().is(tok::greatergreater))
+        ReplaceStr = "> > ";
+
+      Diag(Tok.getLocation(), diag::err_two_right_angle_brackets_need_space)
+        << CodeReplacementHint(SourceRange(Tok.getLocation()), ReplaceStr);
+    }
 
     Tok.setKind(tok::greater);
     if (!ConsumeLastToken) {

Modified: cfe/trunk/lib/Parse/Parser.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Parse/Parser.cpp?rev=65570&r1=65569&r2=65570&view=diff

==============================================================================
--- cfe/trunk/lib/Parse/Parser.cpp (original)
+++ cfe/trunk/lib/Parse/Parser.cpp Thu Feb 26 15:00:50 2009
@@ -68,6 +68,28 @@
   return Diag(Tok.getLocation(), DiagID);
 }
 
+/// \brief Emits a diagnostic suggesting parentheses surrounding a
+/// given range.
+///
+/// \param Loc The location where we'll emit the diagnostic.
+/// \param Loc The kind of diagnostic to emit.
+/// \param ParenRange Source range enclosing code that should be parenthesized.
+void Parser::SuggestParentheses(SourceLocation Loc, unsigned DK,
+                                SourceRange ParenRange) {
+  if (!ParenRange.getEnd().isFileID()) {
+    // We can't display the parentheses, so just dig the
+    // warning/error and return.
+    Diag(Loc, DK);
+    return;
+  }
+    
+  unsigned Len = Lexer::MeasureTokenLength(ParenRange.getEnd(), 
+                                           PP.getSourceManager());
+  Diag(Loc, DK) 
+    << CodeInsertionHint(ParenRange.getBegin(), "(")
+    << CodeInsertionHint(ParenRange.getEnd().getFileLocWithOffset(Len), ")");
+}
+
 /// MatchRHSPunctuation - For punctuation with a LHS and RHS (e.g. '['/']'),
 /// this helper function matches and consumes the specified RHS token if
 /// present.  If not present, it emits the specified diagnostic indicating
@@ -108,7 +130,18 @@
     return false;
   }
 
-  Diag(Tok, DiagID) << Msg;
+  const char *Spelling = 0;
+  if (PrevTokLocation.isValid() && PrevTokLocation.isFileID() &&
+      (Spelling = tok::getTokenSpelling(ExpectedTok))) {
+    // Show what code to insert to fix this problem.
+    SourceLocation DiagLoc 
+      = PrevTokLocation.getFileLocWithOffset(strlen(Spelling));
+    Diag(DiagLoc, DiagID) 
+      << Msg
+      << CodeInsertionHint(DiagLoc, Spelling);
+  } else
+    Diag(Tok, DiagID) << Msg;
+
   if (SkipToTok != tok::unknown)
     SkipUntil(SkipToTok);
   return true;

Modified: cfe/trunk/lib/Rewrite/HTMLRewrite.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Rewrite/HTMLRewrite.cpp?rev=65570&r1=65569&r2=65570&view=diff

==============================================================================
--- cfe/trunk/lib/Rewrite/HTMLRewrite.cpp (original)
+++ cfe/trunk/lib/Rewrite/HTMLRewrite.cpp Thu Feb 26 15:00:50 2009
@@ -319,6 +319,9 @@
       " .mrange { background-color:#dfddf3 }\n"
       " .mrange { border-bottom:1px solid #6F9DBE }\n"
       " .PathIndex { font-weight: bold }\n"
+      " .CodeInsertionHint { font-weight: bold; background-color: #10dd10 }\n"
+      " .CodeRemovalHint { background-color:#de1010 }\n"
+      " .CodeRemovalHint { border-bottom:1px solid #6F9DBE }\n"
       " table.simpletable {\n"
       "   padding: 5px;\n"
       "   font-size:12pt;\n"

Modified: cfe/trunk/lib/Sema/Sema.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/Sema.h?rev=65570&r1=65569&r2=65570&view=diff

==============================================================================
--- cfe/trunk/lib/Sema/Sema.h (original)
+++ cfe/trunk/lib/Sema/Sema.h Thu Feb 26 15:00:50 2009
@@ -1036,6 +1036,8 @@
   bool DiagnoseUseOfDecl(NamedDecl *D, SourceLocation Loc);
 
   // Primary Expressions.
+  virtual SourceRange getExprRange(ExprTy *E) const;
+
   virtual OwningExprResult ActOnIdentifierExpr(Scope *S, SourceLocation Loc,
                                                IdentifierInfo &II,
                                                bool HasTrailingLParen,

Modified: cfe/trunk/lib/Sema/SemaExpr.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaExpr.cpp?rev=65570&r1=65569&r2=65570&view=diff

==============================================================================
--- cfe/trunk/lib/Sema/SemaExpr.cpp (original)
+++ cfe/trunk/lib/Sema/SemaExpr.cpp Thu Feb 26 15:00:50 2009
@@ -110,6 +110,11 @@
   return false;
 }
 
+SourceRange Sema::getExprRange(ExprTy *E) const {
+  Expr *Ex = (Expr *)E;
+  return Ex? Ex->getSourceRange() : SourceRange();
+}
+
 //===----------------------------------------------------------------------===//
 //  Standard Promotions and Conversions
 //===----------------------------------------------------------------------===//

Modified: cfe/trunk/lib/Sema/SemaTemplate.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaTemplate.cpp?rev=65570&r1=65569&r2=65570&view=diff

==============================================================================
--- cfe/trunk/lib/Sema/SemaTemplate.cpp (original)
+++ cfe/trunk/lib/Sema/SemaTemplate.cpp Thu Feb 26 15:00:50 2009
@@ -1615,12 +1615,10 @@
   // FIXME: Once we have member templates, we'll need to check
   // C++ [temp.expl.spec]p17-18, where we could have multiple levels of
   // template<> headers.
-  if (TemplateParameterLists.size() == 0) {
-    // FIXME: It would be very nifty if we could introduce some kind
-    // of "code insertion hint" that could show the code that needs to
-    // be added.
-    Diag(KWLoc, diag::err_template_spec_needs_header);
-  } else {
+  if (TemplateParameterLists.size() == 0)
+    Diag(KWLoc, diag::err_template_spec_needs_header)
+      << CodeInsertionHint(KWLoc, "template<> ");
+  else {
     TemplateParameterList *TemplateParams 
       = static_cast<TemplateParameterList*>(*TemplateParameterLists.get());
     if (TemplateParameterLists.size() > 1) {

Modified: cfe/trunk/test/Parser/cxx-using-directive.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/Parser/cxx-using-directive.cpp?rev=65570&r1=65569&r2=65570&view=diff

==============================================================================
--- cfe/trunk/test/Parser/cxx-using-directive.cpp (original)
+++ cfe/trunk/test/Parser/cxx-using-directive.cpp Thu Feb 26 15:00:50 2009
@@ -28,8 +28,9 @@
 
 using namespace ! ; // expected-error{{expected namespace name}}
 using namespace A ; // expected-error{{expected namespace name}}
-using namespace ::A // expected-error{{expected namespace name}}
-                    B ; // expected-error{{expected ';' after namespace name}}
+using namespace ::A // expected-error{{expected namespace name}} \
+                    // expected-error{{expected ';' after namespace name}}
+                    B ; 
 
 void test_nslookup() {
   int B;

Modified: cfe/trunk/test/Parser/recovery.c
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/Parser/recovery.c?rev=65570&r1=65569&r2=65570&view=diff

==============================================================================
--- cfe/trunk/test/Parser/recovery.c (original)
+++ cfe/trunk/test/Parser/recovery.c Thu Feb 26 15:00:50 2009
@@ -68,3 +68,8 @@
 struct forward; // expected-note{{forward declaration of 'struct forward'}}
 void x(struct forward* x) {switch(x->a) {}} // expected-error {{incomplete definition of type}}
 
+// PR3410
+void foo() {
+  int X;
+  X = 4 // expected-error{{expected ';' after expression}}
+}

Modified: cfe/trunk/test/SemaTemplate/right-angle-brackets-98.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/SemaTemplate/right-angle-brackets-98.cpp?rev=65570&r1=65569&r2=65570&view=diff

==============================================================================
--- cfe/trunk/test/SemaTemplate/right-angle-brackets-98.cpp (original)
+++ cfe/trunk/test/SemaTemplate/right-angle-brackets-98.cpp Thu Feb 26 15:00:50 2009
@@ -9,5 +9,4 @@
     >> *x3;   // expected-error{{a space is required between consecutive right angle brackets (use '> >')}}
 
 Y<(1 >> 2)> *y1;
-Y<1 >> 2> *y2;
-// FIXME: when we get a -Wc++0x mode, warn about the use above
+Y<1 >> 2> *y2; // expected-warning{{use of right-shift operator ('>>') in template argument will require parentheses in C++0x}}

Modified: cfe/trunk/test/SemaTemplate/temp_arg_nontype.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/SemaTemplate/temp_arg_nontype.cpp?rev=65570&r1=65569&r2=65570&view=diff

==============================================================================
--- cfe/trunk/test/SemaTemplate/temp_arg_nontype.cpp (original)
+++ cfe/trunk/test/SemaTemplate/temp_arg_nontype.cpp Thu Feb 26 15:00:50 2009
@@ -7,7 +7,7 @@
 
 A<int> *a2; // expected-error{{template argument for non-type template parameter must be an expression}}
 
-A<1 >> 2> *a3;
+A<1 >> 2> *a3; // expected-warning{{use of right-shift operator ('>>') in template argument will require parentheses in C++0x}}
 
 // C++ [temp.arg.nontype]p5:
 A<A> *a4; // expected-error{{must have an integral or enumeration type}} \





More information about the cfe-commits mailing list