[cfe-commits] r161087 - in /cfe/trunk: include/clang-c/ include/clang/AST/ include/clang/Basic/ lib/AST/ lib/Sema/ test/Index/ test/Sema/ tools/c-index-test/ tools/libclang/ unittests/AST/

Dmitri Gribenko gribozavr at gmail.com
Tue Jul 31 15:37:06 PDT 2012


Author: gribozavr
Date: Tue Jul 31 17:37:06 2012
New Revision: 161087

URL: http://llvm.org/viewvc/llvm-project?rev=161087&view=rev
Log:
Comment parsing: add support for \tparam command on all levels.

The only caveat is renumbering CXCommentKind enum for aesthetic reasons -- this
breaks libclang binary compatibility, but should not be a problem since API is
so new.

This also fixes PR13372 as a side-effect.

Modified:
    cfe/trunk/include/clang-c/Index.h
    cfe/trunk/include/clang/AST/Comment.h
    cfe/trunk/include/clang/AST/CommentParser.h
    cfe/trunk/include/clang/AST/CommentSema.h
    cfe/trunk/include/clang/Basic/CommentNodes.td
    cfe/trunk/include/clang/Basic/DiagnosticCommentKinds.td
    cfe/trunk/lib/AST/ASTContext.cpp
    cfe/trunk/lib/AST/CommentBriefParser.cpp
    cfe/trunk/lib/AST/CommentDumper.cpp
    cfe/trunk/lib/AST/CommentParser.cpp
    cfe/trunk/lib/AST/CommentSema.cpp
    cfe/trunk/lib/Sema/SemaDecl.cpp
    cfe/trunk/lib/Sema/SemaTemplate.cpp
    cfe/trunk/test/Index/annotate-comments.cpp
    cfe/trunk/test/Sema/warn-documentation.cpp
    cfe/trunk/tools/c-index-test/c-index-test.c
    cfe/trunk/tools/libclang/CXComment.cpp
    cfe/trunk/tools/libclang/libclang.exports
    cfe/trunk/unittests/AST/CommentParser.cpp

Modified: cfe/trunk/include/clang-c/Index.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang-c/Index.h?rev=161087&r1=161086&r2=161087&view=diff
==============================================================================
--- cfe/trunk/include/clang-c/Index.h (original)
+++ cfe/trunk/include/clang-c/Index.h Tue Jul 31 17:37:06 2012
@@ -3277,6 +3277,14 @@
   CXComment_ParamCommand = 7,
 
   /**
+   * \brief A \\tparam command that describes a template parameter (name and
+   * description).
+   *
+   * \brief For example: \\tparam T description.
+   */
+  CXComment_TParamCommand = 8,
+
+  /**
    * \brief A verbatim block command (e. g., preformatted code).  Verbatim
    * block has an opening and a closing command and contains multiple lines of
    * text (\c CXComment_VerbatimBlockLine child nodes).
@@ -3286,25 +3294,25 @@
    * aaa
    * \\endverbatim
    */
-  CXComment_VerbatimBlockCommand = 8,
+  CXComment_VerbatimBlockCommand = 9,
 
   /**
    * \brief A line of text that is contained within a
    * CXComment_VerbatimBlockCommand node.
    */
-  CXComment_VerbatimBlockLine = 9,
+  CXComment_VerbatimBlockLine = 10,
 
   /**
    * \brief A verbatim line command.  Verbatim line has an opening command,
    * a single line of text (up to the newline after the opening command) and
    * has no closing command.
    */
-  CXComment_VerbatimLine = 10,
+  CXComment_VerbatimLine = 11,
 
   /**
    * \brief A full comment attached to a declaration, contains block content.
    */
-  CXComment_FullComment = 11
+  CXComment_FullComment = 12
 };
 
 /**
@@ -3564,6 +3572,63 @@
                                                             CXComment Comment);
 
 /**
+ * \param Comment a \c CXComment_TParamCommand AST node.
+ *
+ * \returns template parameter name.
+ */
+CINDEX_LINKAGE
+CXString clang_TParamCommandComment_getParamName(CXComment Comment);
+
+/**
+ * \param Comment a \c CXComment_TParamCommand AST node.
+ *
+ * \returns non-zero if the parameter that this AST node represents was found
+ * in the template parameter list and
+ * \c clang_TParamCommandComment_getDepth and
+ * \c clang_TParamCommandComment_getIndex functions will return a meaningful
+ * value.
+ */
+CINDEX_LINKAGE
+unsigned clang_TParamCommandComment_isParamPositionValid(CXComment Comment);
+
+/**
+ * \param Comment a \c CXComment_TParamCommand AST node.
+ *
+ * \returns zero-based nesting depth of this parameter in the template parameter list.
+ *
+ * For example,
+ * \verbatim
+ *     template<typename C, template<typename T> class TT>
+ *     void test(TT<int> aaa);
+ * \endverbatim
+ * for C and TT nesting depth is 0,
+ * for T nesting depth is 1.
+ */
+CINDEX_LINKAGE
+unsigned clang_TParamCommandComment_getDepth(CXComment Comment);
+
+/**
+ * \param Comment a \c CXComment_TParamCommand AST node.
+ *
+ * \returns zero-based parameter index in the template parameter list at a
+ * given nesting depth.
+ *
+ * For example,
+ * \verbatim
+ *     template<typename C, template<typename T> class TT>
+ *     void test(TT<int> aaa);
+ * \endverbatim
+ * for C and TT nesting depth is 0, so we can ask for index at depth 0:
+ * at depth 0 C's index is 0, TT's index is 1.
+ *
+ * For T nesting depth is 1, so we can ask for index at depth 0 and 1:
+ * at depth 0 T's index is 1 (same as TT's),
+ * at depth 1 T's index is 0.
+ */
+CINDEX_LINKAGE
+unsigned clang_TParamCommandComment_getIndex(CXComment Comment, unsigned Depth);
+
+/**
  * \param Comment a \c CXComment_VerbatimBlockLine AST node.
  *
  * \returns text contained in the AST node.
@@ -3606,6 +3671,15 @@
  * \li "param-name-index-invalid" and "param-descr-index-invalid" are used if
  * parameter index is invalid.
  *
+ * Template parameter documentation is rendered as a \<dl\> list with
+ * parameters sorted in template parameter list order.  CSS classes used:
+ * \li "tparam-name-index-NUMBER" for parameter name (\<dt\>);
+ * \li "tparam-descr-index-NUMBER" for parameter description (\<dd\>);
+ * \li "taram-name-index-other" and "tparam-descr-index-other" are used for
+ * names inside template template parameters;
+ * \li "tparam-name-index-invalid" and "tparam-descr-index-invalid" are used if
+ * parameter position is invalid.
+ *
  * \param Comment a \c CXComment_FullComment AST node.
  *
  * \returns string containing an HTML fragment.

Modified: cfe/trunk/include/clang/AST/Comment.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/AST/Comment.h?rev=161087&r1=161086&r2=161087&view=diff
==============================================================================
--- cfe/trunk/include/clang/AST/Comment.h (original)
+++ cfe/trunk/include/clang/AST/Comment.h Tue Jul 31 17:37:06 2012
@@ -723,6 +723,68 @@
   }
 };
 
+/// Doxygen \\tparam command, describes a template parameter.
+class TParamCommandComment : public BlockCommandComment {
+private:
+  /// If this template parameter name was resolved (found in template parameter
+  /// list), then this stores a list of position indexes in all template
+  /// parameter lists.
+  ///
+  /// For example:
+  /// \verbatim
+  ///     template<typename C, template<typename T> class TT>
+  ///     void test(TT<int> aaa);
+  /// \endverbatim
+  /// For C:  Position = { 0 }
+  /// For TT: Position = { 1 }
+  /// For T:  Position = { 1, 0 }
+  llvm::ArrayRef<unsigned> Position;
+
+public:
+  TParamCommandComment(SourceLocation LocBegin,
+                       SourceLocation LocEnd,
+                       StringRef Name) :
+      BlockCommandComment(TParamCommandCommentKind, LocBegin, LocEnd, Name)
+  { }
+
+  static bool classof(const Comment *C) {
+    return C->getCommentKind() == TParamCommandCommentKind;
+  }
+
+  static bool classof(const TParamCommandComment *) { return true; }
+
+  bool hasParamName() const {
+    return getNumArgs() > 0;
+  }
+
+  StringRef getParamName() const {
+    return Args[0].Text;
+  }
+
+  SourceRange getParamNameRange() const {
+    return Args[0].Range;
+  }
+
+  bool isPositionValid() const LLVM_READONLY {
+    return !Position.empty();
+  }
+
+  unsigned getDepth() const {
+    assert(isPositionValid());
+    return Position.size();
+  }
+
+  unsigned getIndex(unsigned Depth) const {
+    assert(isPositionValid());
+    return Position[Depth];
+  }
+
+  void setPosition(ArrayRef<unsigned> NewPosition) {
+    Position = NewPosition;
+    assert(isPositionValid());
+  }
+};
+
 /// A line of text contained in a verbatim block.
 class VerbatimBlockLineComment : public Comment {
   StringRef Text;

Modified: cfe/trunk/include/clang/AST/CommentParser.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/AST/CommentParser.h?rev=161087&r1=161086&r2=161087&view=diff
==============================================================================
--- cfe/trunk/include/clang/AST/CommentParser.h (original)
+++ cfe/trunk/include/clang/AST/CommentParser.h Tue Jul 31 17:37:06 2012
@@ -42,17 +42,6 @@
   /// Source manager for the comment being parsed.
   const SourceManager &SourceMgr;
 
-  template<typename T>
-  ArrayRef<T> copyArray(ArrayRef<T> Source) {
-    size_t Size = Source.size();
-    if (Size != 0) {
-      T *Mem = Allocator.Allocate<T>(Size);
-      std::uninitialized_copy(Source.begin(), Source.end(), Mem);
-      return llvm::makeArrayRef(Mem, Size);
-    } else
-      return llvm::makeArrayRef(static_cast<T *>(NULL), 0);
-  }
-
   DiagnosticsEngine &Diags;
 
   DiagnosticBuilder Diag(SourceLocation Loc, unsigned DiagID) {
@@ -105,6 +94,11 @@
                                     ParamCommandComment *PC,
                                     TextTokenRetokenizer &Retokenizer);
 
+  /// Parse arguments for \\tparam command.
+  TParamCommandComment *parseTParamCommandArgs(
+                                    TParamCommandComment *TPC,
+                                    TextTokenRetokenizer &Retokenizer);
+
   BlockCommandComment *parseBlockCommandArgs(
                                     BlockCommandComment *BC,
                                     TextTokenRetokenizer &Retokenizer,

Modified: cfe/trunk/include/clang/AST/CommentSema.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/AST/CommentSema.h?rev=161087&r1=161086&r2=161087&view=diff
==============================================================================
--- cfe/trunk/include/clang/AST/CommentSema.h (original)
+++ cfe/trunk/include/clang/AST/CommentSema.h Tue Jul 31 17:37:06 2012
@@ -19,12 +19,14 @@
 #include "clang/AST/Comment.h"
 #include "llvm/ADT/ArrayRef.h"
 #include "llvm/ADT/StringRef.h"
+#include "llvm/ADT/StringMap.h"
 #include "llvm/Support/Allocator.h"
 
 namespace clang {
 class Decl;
 class FunctionDecl;
 class ParmVarDecl;
+class TemplateParameterList;
 class SourceMgr;
 
 namespace comments {
@@ -56,6 +58,18 @@
   /// true.
   llvm::SmallVector<ParamCommandComment *, 8> ParamVarDocs;
 
+  /// Template parameters that can be referenced by \\tparam if \c ThisDecl is
+  /// a template.
+  ///
+  /// Contains a valid value if \c IsThisDeclInspected is true.
+  const TemplateParameterList *TemplateParameters;
+
+  /// Comment AST nodes that correspond to parameter names in
+  /// \c TemplateParameters.
+  ///
+  /// Contains a valid value if \c IsThisDeclInspected is true.
+  llvm::StringMap<TParamCommandComment *> TemplateParameterDocs;
+
   /// True if we extracted all important information from \c ThisDecl into
   /// \c Sema members.
   unsigned IsThisDeclInspected : 1;
@@ -64,6 +78,10 @@
   /// Contains a valid value if \c IsThisDeclInspected is true.
   unsigned IsFunctionDecl : 1;
 
+  /// Is \c ThisDecl a template declaration.
+  /// Contains a valid value if \c IsThisDeclInspected is true.
+  unsigned IsTemplateDecl : 1;
+
   DiagnosticBuilder Diag(SourceLocation Loc, unsigned DiagID) {
     return Diags.Report(Loc, DiagID);
   }
@@ -78,6 +96,18 @@
 
   void setDecl(const Decl *D);
 
+  /// Returns a copy of array, owned by Sema's allocator.
+  template<typename T>
+  ArrayRef<T> copyArray(ArrayRef<T> Source) {
+    size_t Size = Source.size();
+    if (Size != 0) {
+      T *Mem = Allocator.Allocate<T>(Size);
+      std::uninitialized_copy(Source.begin(), Source.end(), Mem);
+      return llvm::makeArrayRef(Mem, Size);
+    } else
+      return llvm::makeArrayRef(static_cast<T *>(NULL), 0);
+  }
+
   ParagraphComment *actOnParagraphComment(
       ArrayRef<InlineContentComment *> Content);
 
@@ -111,6 +141,19 @@
   ParamCommandComment *actOnParamCommandFinish(ParamCommandComment *Command,
                                                ParagraphComment *Paragraph);
 
+  TParamCommandComment *actOnTParamCommandStart(SourceLocation LocBegin,
+                                                SourceLocation LocEnd,
+                                                StringRef Name);
+
+  TParamCommandComment *actOnTParamCommandParamNameArg(
+                                            TParamCommandComment *Command,
+                                            SourceLocation ArgLocBegin,
+                                            SourceLocation ArgLocEnd,
+                                            StringRef Arg);
+
+  TParamCommandComment *actOnTParamCommandFinish(TParamCommandComment *Command,
+                                                 ParagraphComment *Paragraph);
+
   InlineCommandComment *actOnInlineCommand(SourceLocation CommandLocBegin,
                                            SourceLocation CommandLocEnd,
                                            StringRef CommandName);
@@ -165,6 +208,8 @@
   void checkBlockCommandEmptyParagraph(BlockCommandComment *Command);
 
   bool isFunctionDecl();
+  bool isTemplateDecl();
+
   ArrayRef<const ParmVarDecl *> getParamVars();
 
   /// Extract all important semantic information from \c ThisDecl into
@@ -180,8 +225,17 @@
   unsigned correctTypoInParmVarReference(StringRef Typo,
                                          ArrayRef<const ParmVarDecl *> ParamVars);
 
+  bool resolveTParamReference(StringRef Name,
+                              const TemplateParameterList *TemplateParameters,
+                              SmallVectorImpl<unsigned> *Position);
+
+  StringRef correctTypoInTParamReference(
+                              StringRef Typo,
+                              const TemplateParameterList *TemplateParameters);
+
   bool isBlockCommand(StringRef Name);
   bool isParamCommand(StringRef Name);
+  bool isTParamCommand(StringRef Name);
   unsigned getBlockCommandNumArgs(StringRef Name);
 
   bool isInlineCommand(StringRef Name) const;

Modified: cfe/trunk/include/clang/Basic/CommentNodes.td
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Basic/CommentNodes.td?rev=161087&r1=161086&r2=161087&view=diff
==============================================================================
--- cfe/trunk/include/clang/Basic/CommentNodes.td (original)
+++ cfe/trunk/include/clang/Basic/CommentNodes.td Tue Jul 31 17:37:06 2012
@@ -17,6 +17,7 @@
   def ParagraphComment : DComment<BlockContentComment>;
   def BlockCommandComment : DComment<BlockContentComment>;
     def ParamCommandComment : DComment<BlockCommandComment>;
+    def TParamCommandComment : DComment<BlockCommandComment>;
     def VerbatimBlockComment : DComment<BlockCommandComment>;
     def VerbatimLineComment : DComment<BlockCommandComment>;
 

Modified: cfe/trunk/include/clang/Basic/DiagnosticCommentKinds.td
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Basic/DiagnosticCommentKinds.td?rev=161087&r1=161086&r2=161087&view=diff
==============================================================================
--- cfe/trunk/include/clang/Basic/DiagnosticCommentKinds.td (original)
+++ cfe/trunk/include/clang/Basic/DiagnosticCommentKinds.td Tue Jul 31 17:37:06 2012
@@ -77,5 +77,26 @@
 def note_doc_param_name_suggestion : Note<
   "did you mean '%0'?">;
 
+// \tparam command
+
+def warn_doc_tparam_not_attached_to_a_template_decl : Warning<
+  "'\\tparam' command used in a comment that is not attached to "
+  "a template declaration">,
+  InGroup<Documentation>, DefaultIgnore;
+
+def warn_doc_tparam_duplicate : Warning<
+  "template parameter '%0' is already documented">,
+  InGroup<Documentation>, DefaultIgnore;
+
+def note_doc_tparam_previous : Note<
+  "previous documentation">;
+
+def warn_doc_tparam_not_found : Warning<
+  "template parameter '%0' not found in the template declaration">,
+  InGroup<Documentation>, DefaultIgnore;
+
+def note_doc_tparam_name_suggestion : Note<
+  "did you mean '%0'?">;
+
 } // end of documentation issue category
 } // end of AST component

Modified: cfe/trunk/lib/AST/ASTContext.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/AST/ASTContext.cpp?rev=161087&r1=161086&r2=161087&view=diff
==============================================================================
--- cfe/trunk/lib/AST/ASTContext.cpp (original)
+++ cfe/trunk/lib/AST/ASTContext.cpp Tue Jul 31 17:37:06 2012
@@ -72,6 +72,13 @@
   if (isa<ParmVarDecl>(D))
     return NULL;
 
+  // TODO: we could look up template parameter documentation in the template
+  // documentation.
+  if (isa<TemplateTypeParmDecl>(D) ||
+      isa<NonTypeTemplateParmDecl>(D) ||
+      isa<TemplateTemplateParmDecl>(D))
+    return NULL;
+
   ArrayRef<RawComment *> RawComments = Comments.getComments();
 
   // If there are no comments anywhere, we won't find anything.
@@ -86,7 +93,9 @@
   // so we use the location of the identifier as the "declaration location".
   SourceLocation DeclLoc;
   if (isa<ObjCMethodDecl>(D) || isa<ObjCContainerDecl>(D) ||
-      isa<ObjCPropertyDecl>(D))
+      isa<ObjCPropertyDecl>(D) ||
+      isa<FunctionTemplateDecl>(D) ||
+      isa<ClassTemplateDecl>(D) || isa<ClassTemplateSpecializationDecl>(D))
     DeclLoc = D->getLocStart();
   else
     DeclLoc = D->getLocation();

Modified: cfe/trunk/lib/AST/CommentBriefParser.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/AST/CommentBriefParser.cpp?rev=161087&r1=161086&r2=161087&view=diff
==============================================================================
--- cfe/trunk/lib/AST/CommentBriefParser.cpp (original)
+++ cfe/trunk/lib/AST/CommentBriefParser.cpp Tue Jul 31 17:37:06 2012
@@ -48,6 +48,7 @@
       .Case("pre", true)
       .Case("post", true)
       .Cases("param", "arg", true)
+      .Case("tparam", true)
       .Default(false);
 }
 } // unnamed namespace

Modified: cfe/trunk/lib/AST/CommentDumper.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/AST/CommentDumper.cpp?rev=161087&r1=161086&r2=161087&view=diff
==============================================================================
--- cfe/trunk/lib/AST/CommentDumper.cpp (original)
+++ cfe/trunk/lib/AST/CommentDumper.cpp Tue Jul 31 17:37:06 2012
@@ -50,6 +50,7 @@
   void visitParagraphComment(const ParagraphComment *C);
   void visitBlockCommandComment(const BlockCommandComment *C);
   void visitParamCommandComment(const ParamCommandComment *C);
+  void visitTParamCommandComment(const TParamCommandComment *C);
   void visitVerbatimBlockComment(const VerbatimBlockComment *C);
   void visitVerbatimBlockLineComment(const VerbatimBlockLineComment *C);
   void visitVerbatimLineComment(const VerbatimLineComment *C);
@@ -176,6 +177,24 @@
     OS << " ParamIndex=" << C->getParamIndex();
 }
 
+void CommentDumper::visitTParamCommandComment(const TParamCommandComment *C) {
+  dumpComment(C);
+
+  if (C->hasParamName()) {
+    OS << " Param=\"" << C->getParamName() << "\"";
+  }
+
+  if (C->isPositionValid()) {
+    OS << " Position=<";
+    for (unsigned i = 0, e = C->getDepth(); i != e; ++i) {
+      OS << C->getIndex(i);
+      if (i != e - 1)
+        OS << ", ";
+    }
+    OS << ">";
+  }
+}
+
 void CommentDumper::visitVerbatimBlockComment(const VerbatimBlockComment *C) {
   dumpComment(C);
 

Modified: cfe/trunk/lib/AST/CommentParser.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/AST/CommentParser.cpp?rev=161087&r1=161086&r2=161087&view=diff
==============================================================================
--- cfe/trunk/lib/AST/CommentParser.cpp (original)
+++ cfe/trunk/lib/AST/CommentParser.cpp Tue Jul 31 17:37:06 2012
@@ -276,6 +276,19 @@
   return PC;
 }
 
+TParamCommandComment *Parser::parseTParamCommandArgs(
+                                  TParamCommandComment *TPC,
+                                  TextTokenRetokenizer &Retokenizer) {
+  Token Arg;
+  if (Retokenizer.lexWord(Arg))
+    TPC = S.actOnTParamCommandParamNameArg(TPC,
+                                           Arg.getLocation(),
+                                           Arg.getEndLocation(),
+                                           Arg.getText());
+
+  return TPC;
+}
+
 BlockCommandComment *Parser::parseBlockCommandArgs(
     BlockCommandComment *BC,
     TextTokenRetokenizer &Retokenizer,
@@ -299,14 +312,21 @@
   assert(Tok.is(tok::command));
 
   ParamCommandComment *PC;
+  TParamCommandComment *TPC;
   BlockCommandComment *BC;
   bool IsParam = false;
+  bool IsTParam = false;
   unsigned NumArgs = 0;
   if (S.isParamCommand(Tok.getCommandName())) {
     IsParam = true;
     PC = S.actOnParamCommandStart(Tok.getLocation(),
                                   Tok.getEndLocation(),
                                   Tok.getCommandName());
+  } if (S.isTParamCommand(Tok.getCommandName())) {
+    IsTParam = true;
+    TPC = S.actOnTParamCommandStart(Tok.getLocation(),
+                                    Tok.getEndLocation(),
+                                    Tok.getCommandName());
   } else {
     NumArgs = S.getBlockCommandNumArgs(Tok.getCommandName());
     BC = S.actOnBlockCommandStart(Tok.getLocation(),
@@ -323,13 +343,15 @@
     return S.actOnBlockCommandFinish(IsParam ? PC : BC, Paragraph);
   }
 
-  if (IsParam || NumArgs > 0) {
+  if (IsParam || IsTParam || NumArgs > 0) {
     // In order to parse command arguments we need to retokenize a few
     // following text tokens.
     TextTokenRetokenizer Retokenizer(Allocator, *this);
 
     if (IsParam)
       PC = parseParamCommandArgs(PC, Retokenizer);
+    else if (IsTParam)
+      TPC = parseTParamCommandArgs(TPC, Retokenizer);
     else
       BC = parseBlockCommandArgs(BC, Retokenizer, NumArgs);
 
@@ -341,6 +363,8 @@
   // paragraph.
   if (IsParam)
     return S.actOnParamCommandFinish(PC, cast<ParagraphComment>(Block));
+  else if (IsTParam)
+    return S.actOnTParamCommandFinish(TPC, cast<ParagraphComment>(Block));
   else
     return S.actOnBlockCommandFinish(BC, cast<ParagraphComment>(Block));
 }
@@ -419,7 +443,7 @@
 
     case tok::html_greater:
       HST = S.actOnHTMLStartTagFinish(HST,
-                                      copyArray(llvm::makeArrayRef(Attrs)),
+                                      S.copyArray(llvm::makeArrayRef(Attrs)),
                                       Tok.getLocation(),
                                       /* IsSelfClosing = */ false);
       consumeToken();
@@ -427,7 +451,7 @@
 
     case tok::html_slash_greater:
       HST = S.actOnHTMLStartTagFinish(HST,
-                                      copyArray(llvm::makeArrayRef(Attrs)),
+                                      S.copyArray(llvm::makeArrayRef(Attrs)),
                                       Tok.getLocation(),
                                       /* IsSelfClosing = */ true);
       consumeToken();
@@ -446,14 +470,14 @@
         continue;
 
       return S.actOnHTMLStartTagFinish(HST,
-                                       copyArray(llvm::makeArrayRef(Attrs)),
+                                       S.copyArray(llvm::makeArrayRef(Attrs)),
                                        SourceLocation(),
                                        /* IsSelfClosing = */ false);
 
     default:
       // Not a token from an HTML start tag.  Thus HTML tag prematurely ended.
       HST = S.actOnHTMLStartTagFinish(HST,
-                                      copyArray(llvm::makeArrayRef(Attrs)),
+                                      S.copyArray(llvm::makeArrayRef(Attrs)),
                                       SourceLocation(),
                                       /* IsSelfClosing = */ false);
       bool StartLineInvalid;
@@ -563,7 +587,7 @@
     break;
   }
 
-  return S.actOnParagraphComment(copyArray(llvm::makeArrayRef(Content)));
+  return S.actOnParagraphComment(S.copyArray(llvm::makeArrayRef(Content)));
 }
 
 VerbatimBlockComment *Parser::parseVerbatimBlock() {
@@ -601,12 +625,12 @@
   if (Tok.is(tok::verbatim_block_end)) {
     VB = S.actOnVerbatimBlockFinish(VB, Tok.getLocation(),
                                     Tok.getVerbatimBlockName(),
-                                    copyArray(llvm::makeArrayRef(Lines)));
+                                    S.copyArray(llvm::makeArrayRef(Lines)));
     consumeToken();
   } else {
     // Unterminated \\verbatim block
     VB = S.actOnVerbatimBlockFinish(VB, SourceLocation(), "",
-                                    copyArray(llvm::makeArrayRef(Lines)));
+                                    S.copyArray(llvm::makeArrayRef(Lines)));
   }
 
   return VB;
@@ -680,7 +704,7 @@
     while (Tok.is(tok::newline))
       consumeToken();
   }
-  return S.actOnFullComment(copyArray(llvm::makeArrayRef(Blocks)));
+  return S.actOnFullComment(S.copyArray(llvm::makeArrayRef(Blocks)));
 }
 
 } // end namespace comments

Modified: cfe/trunk/lib/AST/CommentSema.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/AST/CommentSema.cpp?rev=161087&r1=161086&r2=161087&view=diff
==============================================================================
--- cfe/trunk/lib/AST/CommentSema.cpp (original)
+++ cfe/trunk/lib/AST/CommentSema.cpp Tue Jul 31 17:37:06 2012
@@ -11,6 +11,7 @@
 #include "clang/AST/CommentDiagnostic.h"
 #include "clang/AST/Decl.h"
 #include "clang/AST/DeclObjC.h"
+#include "clang/AST/DeclTemplate.h"
 #include "clang/Basic/SourceManager.h"
 #include "llvm/ADT/StringSwitch.h"
 
@@ -200,6 +201,90 @@
   return Command;
 }
 
+TParamCommandComment *Sema::actOnTParamCommandStart(SourceLocation LocBegin,
+                                                    SourceLocation LocEnd,
+                                                    StringRef Name) {
+  TParamCommandComment *Command =
+      new (Allocator) TParamCommandComment(LocBegin, LocEnd, Name);
+
+  if (!isTemplateDecl())
+    Diag(Command->getLocation(),
+         diag::warn_doc_tparam_not_attached_to_a_template_decl)
+      << Command->getCommandNameRange();
+
+  return Command;
+}
+
+TParamCommandComment *Sema::actOnTParamCommandParamNameArg(
+                                            TParamCommandComment *Command,
+                                            SourceLocation ArgLocBegin,
+                                            SourceLocation ArgLocEnd,
+                                            StringRef Arg) {
+  // Parser will not feed us more arguments than needed.
+  assert(Command->getNumArgs() == 0);
+
+  typedef BlockCommandComment::Argument Argument;
+  Argument *A = new (Allocator) Argument(SourceRange(ArgLocBegin,
+                                                     ArgLocEnd),
+                                         Arg);
+  Command->setArgs(llvm::makeArrayRef(A, 1));
+
+  if (!isTemplateDecl()) {
+    // We already warned that this \\tparam is not attached to a template decl.
+    return Command;
+  }
+
+  SmallVector<unsigned, 2> Position;
+  if (resolveTParamReference(Arg, TemplateParameters, &Position)) {
+    Command->setPosition(copyArray(llvm::makeArrayRef(Position)));
+    llvm::StringMap<TParamCommandComment *>::iterator PrevCommandIt =
+        TemplateParameterDocs.find(Arg);
+    if (PrevCommandIt != TemplateParameterDocs.end()) {
+      SourceRange ArgRange(ArgLocBegin, ArgLocEnd);
+      Diag(ArgLocBegin, diag::warn_doc_tparam_duplicate)
+        << Arg << ArgRange;
+      TParamCommandComment *PrevCommand = PrevCommandIt->second;
+      Diag(PrevCommand->getLocation(), diag::note_doc_tparam_previous)
+        << PrevCommand->getParamNameRange();
+    }
+    TemplateParameterDocs[Arg] = Command;
+    return Command;
+  }
+
+  SourceRange ArgRange(ArgLocBegin, ArgLocEnd);
+  Diag(ArgLocBegin, diag::warn_doc_tparam_not_found)
+    << Arg << ArgRange;
+
+  if (!TemplateParameters || TemplateParameters->size() == 0)
+    return Command;
+
+  StringRef CorrectedName;
+  if (TemplateParameters->size() == 1) {
+    const NamedDecl *Param = TemplateParameters->getParam(0);
+    const IdentifierInfo *II = Param->getIdentifier();
+    if (II)
+      CorrectedName = II->getName();
+  } else {
+    CorrectedName = correctTypoInTParamReference(Arg, TemplateParameters);
+  }
+
+  if (!CorrectedName.empty()) {
+    Diag(ArgLocBegin, diag::note_doc_tparam_name_suggestion)
+      << CorrectedName
+      << FixItHint::CreateReplacement(ArgRange, CorrectedName);
+  }
+
+  return Command;
+}
+
+TParamCommandComment *Sema::actOnTParamCommandFinish(
+                                            TParamCommandComment *Command,
+                                            ParagraphComment *Paragraph) {
+  Command->setParagraph(Paragraph);
+  checkBlockCommandEmptyParagraph(Command);
+  return Command;
+}
+
 InlineCommandComment *Sema::actOnInlineCommand(SourceLocation CommandLocBegin,
                                                SourceLocation CommandLocEnd,
                                                StringRef CommandName) {
@@ -387,6 +472,12 @@
   return IsFunctionDecl;
 }
 
+bool Sema::isTemplateDecl() {
+  if (!IsThisDeclInspected)
+    inspectThisDecl();
+  return IsTemplateDecl;
+}
+
 ArrayRef<const ParmVarDecl *> Sema::getParamVars() {
   if (!IsThisDeclInspected)
     inspectThisDecl();
@@ -397,18 +488,56 @@
   assert(!IsThisDeclInspected);
   if (!ThisDecl) {
     IsFunctionDecl = false;
+    IsTemplateDecl = false;
     ParamVars = ArrayRef<const ParmVarDecl *>();
+    TemplateParameters = NULL;
   } else if (const FunctionDecl *FD = dyn_cast<FunctionDecl>(ThisDecl)) {
     IsFunctionDecl = true;
+    IsTemplateDecl = false;
     ParamVars = ArrayRef<const ParmVarDecl *>(FD->param_begin(),
                                               FD->getNumParams());
+    TemplateParameters = NULL;
+    unsigned NumLists = FD->getNumTemplateParameterLists();
+    if (NumLists != 0) {
+      IsTemplateDecl = true;
+      TemplateParameters = FD->getTemplateParameterList(NumLists - 1);
+    }
   } else if (const ObjCMethodDecl *MD = dyn_cast<ObjCMethodDecl>(ThisDecl)) {
     IsFunctionDecl = true;
+    IsTemplateDecl = false;
     ParamVars = ArrayRef<const ParmVarDecl *>(MD->param_begin(),
                                               MD->param_size());
+    TemplateParameters = NULL;
+  } else if (const FunctionTemplateDecl *FTD =
+                 dyn_cast<FunctionTemplateDecl>(ThisDecl)) {
+    IsFunctionDecl = true;
+    IsTemplateDecl = true;
+    const FunctionDecl *FD = FTD->getTemplatedDecl();
+    ParamVars = ArrayRef<const ParmVarDecl *>(FD->param_begin(),
+                                              FD->getNumParams());
+    TemplateParameters = FTD->getTemplateParameters();
+  } else if (const ClassTemplateDecl *CTD =
+                 dyn_cast<ClassTemplateDecl>(ThisDecl)) {
+    IsFunctionDecl = false;
+    IsTemplateDecl = true;
+    ParamVars = ArrayRef<const ParmVarDecl *>();
+    TemplateParameters = CTD->getTemplateParameters();
+  } else if (const ClassTemplatePartialSpecializationDecl *CTPSD =
+                 dyn_cast<ClassTemplatePartialSpecializationDecl>(ThisDecl)) {
+    IsFunctionDecl = false;
+    IsTemplateDecl = true;
+    ParamVars = ArrayRef<const ParmVarDecl *>();
+    TemplateParameters = CTPSD->getTemplateParameters();
+  } else if (isa<ClassTemplateSpecializationDecl>(ThisDecl)) {
+    IsFunctionDecl = false;
+    IsTemplateDecl = true;
+    ParamVars = ArrayRef<const ParmVarDecl *>();
+    TemplateParameters = NULL;
   } else {
     IsFunctionDecl = false;
+    IsTemplateDecl = false;
     ParamVars = ArrayRef<const ParmVarDecl *>();
+    TemplateParameters = NULL;
   }
   ParamVarDocs.resize(ParamVars.size(), NULL);
   IsThisDeclInspected = true;
@@ -424,34 +553,136 @@
   return ParamCommandComment::InvalidParamIndex;
 }
 
+namespace {
+class SimpleTypoCorrector {
+  StringRef Typo;
+  const unsigned MaxEditDistance;
+
+  const NamedDecl *BestDecl;
+  unsigned BestEditDistance;
+  unsigned BestIndex;
+  unsigned NextIndex;
+
+public:
+  SimpleTypoCorrector(StringRef Typo) :
+      Typo(Typo), MaxEditDistance((Typo.size() + 2) / 3),
+      BestDecl(NULL), BestEditDistance(MaxEditDistance + 1),
+      BestIndex(0), NextIndex(0)
+  { }
+
+  void addDecl(const NamedDecl *ND);
+
+  const NamedDecl *getBestDecl() const {
+    if (BestEditDistance > MaxEditDistance)
+      return NULL;
+
+    return BestDecl;
+  }
+
+  unsigned getBestDeclIndex() const {
+    assert(getBestDecl());
+    return BestIndex;
+  }
+};
+
+void SimpleTypoCorrector::addDecl(const NamedDecl *ND) {
+  unsigned CurrIndex = NextIndex++;
+
+  const IdentifierInfo *II = ND->getIdentifier();
+  if (!II)
+    return;
+
+  StringRef Name = II->getName();
+  unsigned MinPossibleEditDistance = abs((int)Name.size() - (int)Typo.size());
+  if (MinPossibleEditDistance > 0 &&
+      Typo.size() / MinPossibleEditDistance < 3)
+    return;
+
+  unsigned EditDistance = Typo.edit_distance(Name, true, MaxEditDistance);
+  if (EditDistance < BestEditDistance) {
+    BestEditDistance = EditDistance;
+    BestDecl = ND;
+    BestIndex = CurrIndex;
+  }
+}
+} // unnamed namespace
+
 unsigned Sema::correctTypoInParmVarReference(
                                     StringRef Typo,
                                     ArrayRef<const ParmVarDecl *> ParamVars) {
-  const unsigned MaxEditDistance = (Typo.size() + 2) / 3;
-  unsigned BestPVDIndex = 0;
-  unsigned BestEditDistance = MaxEditDistance + 1;
-  for (unsigned i = 0, e = ParamVars.size(); i != e; ++i) {
-    const IdentifierInfo *II = ParamVars[i]->getIdentifier();
-    if (II) {
-      StringRef Name = II->getName();
-      unsigned MinPossibleEditDistance =
-        abs((int)Name.size() - (int)Typo.size());
-      if (MinPossibleEditDistance > 0 &&
-          Typo.size() / MinPossibleEditDistance < 3)
-        continue;
-
-      unsigned EditDistance = Typo.edit_distance(Name, true, MaxEditDistance);
-      if (EditDistance < BestEditDistance) {
-        BestEditDistance = EditDistance;
-        BestPVDIndex = i;
-      }
+  SimpleTypoCorrector Corrector(Typo);
+  for (unsigned i = 0, e = ParamVars.size(); i != e; ++i)
+    Corrector.addDecl(ParamVars[i]);
+  if (Corrector.getBestDecl())
+    return Corrector.getBestDeclIndex();
+  else
+    return ParamCommandComment::InvalidParamIndex;;
+}
+
+namespace {
+bool ResolveTParamReferenceHelper(
+                            StringRef Name,
+                            const TemplateParameterList *TemplateParameters,
+                            SmallVectorImpl<unsigned> *Position) {
+  for (unsigned i = 0, e = TemplateParameters->size(); i != e; ++i) {
+    const NamedDecl *Param = TemplateParameters->getParam(i);
+    const IdentifierInfo *II = Param->getIdentifier();
+    if (II && II->getName() == Name) {
+      Position->push_back(i);
+      return true;
+    }
+
+    if (const TemplateTemplateParmDecl *TTP =
+            dyn_cast<TemplateTemplateParmDecl>(Param)) {
+      Position->push_back(i);
+      if (ResolveTParamReferenceHelper(Name, TTP->getTemplateParameters(),
+                                       Position))
+        return true;
+      Position->pop_back();
     }
   }
+  return false;
+}
+} // unnamed namespace
 
-  if (BestEditDistance <= MaxEditDistance)
-    return BestPVDIndex;
-  else
-    return ParamCommandComment::InvalidParamIndex;
+bool Sema::resolveTParamReference(
+                            StringRef Name,
+                            const TemplateParameterList *TemplateParameters,
+                            SmallVectorImpl<unsigned> *Position) {
+  Position->clear();
+  if (!TemplateParameters)
+    return false;
+
+  return ResolveTParamReferenceHelper(Name, TemplateParameters, Position);
+}
+
+namespace {
+void CorrectTypoInTParamReferenceHelper(
+                            const TemplateParameterList *TemplateParameters,
+                            SimpleTypoCorrector &Corrector) {
+  for (unsigned i = 0, e = TemplateParameters->size(); i != e; ++i) {
+    const NamedDecl *Param = TemplateParameters->getParam(i);
+    Corrector.addDecl(Param);
+
+    if (const TemplateTemplateParmDecl *TTP =
+            dyn_cast<TemplateTemplateParmDecl>(Param))
+      CorrectTypoInTParamReferenceHelper(TTP->getTemplateParameters(),
+                                         Corrector);
+  }
+}
+} // unnamed namespace
+
+StringRef Sema::correctTypoInTParamReference(
+                            StringRef Typo,
+                            const TemplateParameterList *TemplateParameters) {
+  SimpleTypoCorrector Corrector(Typo);
+  CorrectTypoInTParamReferenceHelper(TemplateParameters, Corrector);
+  if (const NamedDecl *ND = Corrector.getBestDecl()) {
+    const IdentifierInfo *II = ND->getIdentifier();
+    assert(II && "SimpleTypoCorrector should not return this decl");
+    return II->getName();
+  }
+  return StringRef();
 }
 
 // TODO: tablegen
@@ -465,7 +696,7 @@
       .Case("authors", true)
       .Case("pre", true)
       .Case("post", true)
-      .Default(false) || isParamCommand(Name);
+      .Default(false) || isParamCommand(Name) || isTParamCommand(Name);
 }
 
 bool Sema::isParamCommand(StringRef Name) {
@@ -475,6 +706,10 @@
       .Default(false);
 }
 
+bool Sema::isTParamCommand(StringRef Name) {
+  return Name == "tparam";
+}
+
 unsigned Sema::getBlockCommandNumArgs(StringRef Name) {
   return llvm::StringSwitch<unsigned>(Name)
       .Cases("brief", "short", 0)

Modified: cfe/trunk/lib/Sema/SemaDecl.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaDecl.cpp?rev=161087&r1=161086&r2=161087&view=diff
==============================================================================
--- cfe/trunk/lib/Sema/SemaDecl.cpp (original)
+++ cfe/trunk/lib/Sema/SemaDecl.cpp Tue Jul 31 17:37:06 2012
@@ -7618,6 +7618,7 @@
         << FD->getName() << "dllimport";
     }
   }
+  ActOnDocumentableDecl(FD);
   return FD;
 }
 

Modified: cfe/trunk/lib/Sema/SemaTemplate.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaTemplate.cpp?rev=161087&r1=161086&r2=161087&view=diff
==============================================================================
--- cfe/trunk/lib/Sema/SemaTemplate.cpp (original)
+++ cfe/trunk/lib/Sema/SemaTemplate.cpp Tue Jul 31 17:37:06 2012
@@ -1139,6 +1139,8 @@
   if (PrevClassTemplate)
     mergeDeclAttributes(NewClass, PrevClassTemplate->getTemplatedDecl());
 
+  ActOnDocumentableDecl(NewTemplate);
+
   return NewTemplate;
 }
 
@@ -5568,7 +5570,9 @@
 Decl *Sema::ActOnTemplateDeclarator(Scope *S,
                               MultiTemplateParamsArg TemplateParameterLists,
                                     Declarator &D) {
-  return HandleDeclarator(S, D, move(TemplateParameterLists));
+  Decl *NewDecl = HandleDeclarator(S, D, move(TemplateParameterLists));
+  ActOnDocumentableDecl(NewDecl);
+  return NewDecl;
 }
 
 Decl *Sema::ActOnStartOfFunctionTemplateDef(Scope *FnBodyScope,

Modified: cfe/trunk/test/Index/annotate-comments.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/Index/annotate-comments.cpp?rev=161087&r1=161086&r2=161087&view=diff
==============================================================================
--- cfe/trunk/test/Index/annotate-comments.cpp (original)
+++ cfe/trunk/test/Index/annotate-comments.cpp Tue Jul 31 17:37:06 2012
@@ -290,6 +290,30 @@
 /// \param x1 Aaa.
 void comment_to_html_conversion_16(int x1, int x2);
 
+/// \tparam
+/// \param aaa Blah blah
+template<typename T>
+void comment_to_html_conversion_17(T aaa);
+
+/// \tparam T2 Bbb
+/// \tparam T1 Aaa
+template<typename T1, typename T2>
+void comment_to_html_conversion_18(T1 aaa, T2 bbb);
+
+/// \tparam T2 Bbb
+/// \tparam U Zzz
+/// \tparam V Ccc
+/// \tparam T1 Aaa
+template<typename T1, typename T2, int V>
+void comment_to_html_conversion_19(T1 aaa, T2 bbb);
+
+/// \tparam TTT Ddd
+/// \tparam C Ccc
+/// \tparam T Aaa
+/// \tparam TT Bbb
+template<template<template<typename T> class TT, class C> class TTT>
+void comment_to_html_conversion_20();
+
 /// \brief Aaa.
 ///
 /// Bbb.
@@ -297,34 +321,34 @@
 /// \param x2 Ddd.
 /// \param x1 Ccc.
 /// \returns Eee.
-void comment_to_html_conversion_17(int x1, int x2);
+void comment_to_html_conversion_21(int x1, int x2);
 
 /// <br><a href="http://example.com/">Aaa</a>
-void comment_to_html_conversion_18();
+void comment_to_html_conversion_22();
 
 /// \verbatim
 /// <a href="http://example.com/">Aaa</a>
 /// <a href='http://example.com/'>Aaa</a>
 /// \endverbatim
-void comment_to_html_conversion_19();
+void comment_to_html_conversion_23();
 
 /// \b Aaa
-void comment_to_html_conversion_20();
+void comment_to_html_conversion_24();
 
 /// \c Aaa \p Bbb
-void comment_to_html_conversion_21();
+void comment_to_html_conversion_25();
 
 /// \a Aaa \e Bbb \em Ccc
-void comment_to_html_conversion_22();
+void comment_to_html_conversion_26();
 
 /// \\ \@ \& \$ \# \< \> \% \" \. \::
-void comment_to_html_conversion_23();
+void comment_to_html_conversion_27();
 
 /// & < > "
-void comment_to_html_conversion_24();
+void comment_to_html_conversion_28();
 
 /// <em>0<i</em>
-void comment_to_html_conversion_25();
+void comment_to_html_conversion_29();
 
 #endif
 
@@ -555,7 +579,70 @@
 // CHECK-NEXT:       (CXComment_ParamCommand in implicitly ParamName=[x1] ParamIndex=0
 // CHECK-NEXT:         (CXComment_Paragraph
 // CHECK-NEXT:           (CXComment_Text Text=[ Aaa.]))))]
-// CHECK: annotate-comments.cpp:300:6: FunctionDecl=comment_to_html_conversion_17:{{.*}} FullCommentAsHTML=[<p class="para-brief"> Aaa.</p><p> Bbb.</p><dl><dt class="param-name-index-0">x1</dt><dd class="param-descr-index-0"> Ccc. </dd><dt class="param-name-index-1">x2</dt><dd class="param-descr-index-1"> Ddd. </dd></dl><p class="para-returns"><span class="word-returns">Returns</span>  Eee.</p>]
+// CHECK: annotate-comments.cpp:296:6: FunctionTemplate=comment_to_html_conversion_17:{{.*}} FullCommentAsHTML=[<dl><dt class="param-name-index-0">aaa</dt><dd class="param-descr-index-0"> Blah blah</dd></dl>]
+// CHECK-NEXT:  CommentAST=[
+// CHECK-NEXT:    (CXComment_FullComment
+// CHECK-NEXT:       (CXComment_Paragraph IsWhitespace
+// CHECK-NEXT:         (CXComment_Text Text=[ ] IsWhitespace))
+// CHECK-NEXT:       (CXComment_TParamCommand ParamName=[] ParamPosition=Invalid
+// CHECK-NEXT:         (CXComment_Paragraph IsWhitespace
+// CHECK-NEXT:           (CXComment_Text Text=[ ] IsWhitespace)))
+// CHECK-NEXT:       (CXComment_ParamCommand in implicitly ParamName=[aaa] ParamIndex=0
+// CHECK-NEXT:         (CXComment_Paragraph
+// CHECK-NEXT:           (CXComment_Text Text=[ Blah blah]))))]
+// CHECK: annotate-comments.cpp:301:6: FunctionTemplate=comment_to_html_conversion_18:{{.*}} FullCommentAsHTML=[<dl><dt class="taram-name-index-0">T1</dt><dd class="tparam-descr-index-0"> Aaa</dd><dt class="taram-name-index-1">T2</dt><dd class="tparam-descr-index-1"> Bbb </dd></dl>]
+// CHECK-NEXT:  CommentAST=[
+// CHECK-NEXT:    (CXComment_FullComment
+// CHECK-NEXT:       (CXComment_Paragraph IsWhitespace
+// CHECK-NEXT:         (CXComment_Text Text=[ ] IsWhitespace))
+// CHECK-NEXT:       (CXComment_TParamCommand ParamName=[T2] ParamPosition={1}
+// CHECK-NEXT:         (CXComment_Paragraph
+// CHECK-NEXT:           (CXComment_Text Text=[ Bbb] HasTrailingNewline)
+// CHECK-NEXT:           (CXComment_Text Text=[ ] IsWhitespace)))
+// CHECK-NEXT:       (CXComment_TParamCommand ParamName=[T1] ParamPosition={0}
+// CHECK-NEXT:         (CXComment_Paragraph
+// CHECK-NEXT:           (CXComment_Text Text=[ Aaa]))))]
+// CHECK: annotate-comments.cpp:308:6: FunctionTemplate=comment_to_html_conversion_19:{{.*}} FullCommentAsHTML=[<dl><dt class="taram-name-index-0">T1</dt><dd class="tparam-descr-index-0"> Aaa</dd><dt class="taram-name-index-1">T2</dt><dd class="tparam-descr-index-1"> Bbb </dd><dt class="taram-name-index-2">V</dt><dd class="tparam-descr-index-2"> Ccc </dd><dt class="tparam-name-index-invalid">U</dt><dd class="tparam-descr-index-invalid"> Zzz </dd></dl>]
+// CHECK-NEXT:  CommentAST=[
+// CHECK-NEXT:    (CXComment_FullComment
+// CHECK-NEXT:       (CXComment_Paragraph IsWhitespace
+// CHECK-NEXT:         (CXComment_Text Text=[ ] IsWhitespace))
+// CHECK-NEXT:       (CXComment_TParamCommand ParamName=[T2] ParamPosition={1}
+// CHECK-NEXT:         (CXComment_Paragraph
+// CHECK-NEXT:           (CXComment_Text Text=[ Bbb] HasTrailingNewline)
+// CHECK-NEXT:           (CXComment_Text Text=[ ] IsWhitespace)))
+// CHECK-NEXT:       (CXComment_TParamCommand ParamName=[U] ParamPosition=Invalid
+// CHECK-NEXT:         (CXComment_Paragraph
+// CHECK-NEXT:           (CXComment_Text Text=[ Zzz] HasTrailingNewline)
+// CHECK-NEXT:           (CXComment_Text Text=[ ] IsWhitespace)))
+// CHECK-NEXT:       (CXComment_TParamCommand ParamName=[V] ParamPosition={2}
+// CHECK-NEXT:         (CXComment_Paragraph
+// CHECK-NEXT:           (CXComment_Text Text=[ Ccc] HasTrailingNewline)
+// CHECK-NEXT:           (CXComment_Text Text=[ ] IsWhitespace)))
+// CHECK-NEXT:       (CXComment_TParamCommand ParamName=[T1] ParamPosition={0}
+// CHECK-NEXT:         (CXComment_Paragraph
+// CHECK-NEXT:           (CXComment_Text Text=[ Aaa]))))]
+// CHECK: annotate-comments.cpp:315:6: FunctionTemplate=comment_to_html_conversion_20:{{.*}} FullCommentAsHTML=[<dl><dt class="taram-name-index-0">TTT</dt><dd class="tparam-descr-index-0"> Ddd </dd><dt class="taram-name-index-other">C</dt><dd class="tparam-descr-index-other"> Ccc </dd><dt class="taram-name-index-other">T</dt><dd class="tparam-descr-index-other"> Aaa </dd><dt class="taram-name-index-other">TT</dt><dd class="tparam-descr-index-other"> Bbb</dd></dl>]
+// CHECK-NEXT:  CommentAST=[
+// CHECK-NEXT:    (CXComment_FullComment
+// CHECK-NEXT:       (CXComment_Paragraph IsWhitespace
+// CHECK-NEXT:         (CXComment_Text Text=[ ] IsWhitespace))
+// CHECK-NEXT:       (CXComment_TParamCommand ParamName=[TTT] ParamPosition={0}
+// CHECK-NEXT:         (CXComment_Paragraph
+// CHECK-NEXT:           (CXComment_Text Text=[ Ddd] HasTrailingNewline)
+// CHECK-NEXT:           (CXComment_Text Text=[ ] IsWhitespace)))
+// CHECK-NEXT:       (CXComment_TParamCommand ParamName=[C] ParamPosition={0, 1}
+// CHECK-NEXT:         (CXComment_Paragraph
+// CHECK-NEXT:           (CXComment_Text Text=[ Ccc] HasTrailingNewline)
+// CHECK-NEXT:           (CXComment_Text Text=[ ] IsWhitespace)))
+// CHECK-NEXT:       (CXComment_TParamCommand ParamName=[T] ParamPosition={0, 0, 0}
+// CHECK-NEXT:         (CXComment_Paragraph
+// CHECK-NEXT:           (CXComment_Text Text=[ Aaa] HasTrailingNewline)
+// CHECK-NEXT:           (CXComment_Text Text=[ ] IsWhitespace)))
+// CHECK-NEXT:       (CXComment_TParamCommand ParamName=[TT] ParamPosition={0, 0}
+// CHECK-NEXT:         (CXComment_Paragraph
+// CHECK-NEXT:           (CXComment_Text Text=[ Bbb]))))]
+// CHECK: annotate-comments.cpp:324:6: FunctionDecl=comment_to_html_conversion_21:{{.*}} FullCommentAsHTML=[<p class="para-brief"> Aaa.</p><p> Bbb.</p><dl><dt class="param-name-index-0">x1</dt><dd class="param-descr-index-0"> Ccc. </dd><dt class="param-name-index-1">x2</dt><dd class="param-descr-index-1"> Ddd. </dd></dl><p class="para-returns"><span class="word-returns">Returns</span>  Eee.</p>]
 // CHECK-NEXT:  CommentAST=[
 // CHECK-NEXT:    (CXComment_FullComment
 // CHECK-NEXT:       (CXComment_Paragraph IsWhitespace
@@ -578,7 +665,7 @@
 // CHECK-NEXT:       (CXComment_BlockCommand CommandName=[returns]
 // CHECK-NEXT:         (CXComment_Paragraph
 // CHECK-NEXT:           (CXComment_Text Text=[ Eee.]))))]
-// CHECK: annotate-comments.cpp:303:6: FunctionDecl=comment_to_html_conversion_18:{{.*}} FullCommentAsHTML=[<p class="para-brief"> <br><a href="http://example.com/">Aaa</a></p>]
+// CHECK: annotate-comments.cpp:327:6: FunctionDecl=comment_to_html_conversion_22:{{.*}} FullCommentAsHTML=[<p class="para-brief"> <br><a href="http://example.com/">Aaa</a></p>]
 // CHECK-NEXT:  CommentAST=[
 // CHECK-NEXT:    (CXComment_FullComment
 // CHECK-NEXT:       (CXComment_Paragraph
@@ -587,7 +674,7 @@
 // CHECK-NEXT:         (CXComment_HTMLStartTag Name=[a] Attrs: href=http://example.com/)
 // CHECK-NEXT:         (CXComment_Text Text=[Aaa])
 // CHECK-NEXT:         (CXComment_HTMLEndTag Name=[a])))]
-// CHECK: annotate-comments.cpp:309:6: FunctionDecl=comment_to_html_conversion_19:{{.*}} FullCommentAsHTML=[<pre> <a href="http://example.com/">Aaa</a>\n <a href='http://example.com/'>Aaa</a></pre>]
+// CHECK: annotate-comments.cpp:333:6: FunctionDecl=comment_to_html_conversion_23:{{.*}} FullCommentAsHTML=[<pre> <a href="http://example.com/">Aaa</a>\n <a href='http://example.com/'>Aaa</a></pre>]
 // CHECK-NEXT:  CommentAST=[
 // CHECK-NEXT:    (CXComment_FullComment
 // CHECK-NEXT:       (CXComment_Paragraph IsWhitespace
@@ -595,13 +682,13 @@
 // CHECK-NEXT:       (CXComment_VerbatimBlockCommand CommandName=[verbatim]
 // CHECK-NEXT:         (CXComment_VerbatimBlockLine Text=[ <a href="http://example.com/">Aaa</a>])
 // CHECK-NEXT:         (CXComment_VerbatimBlockLine Text=[ <a href='http://example.com/'>Aaa</a>])))]
-// CHECK: annotate-comments.cpp:312:6: FunctionDecl=comment_to_html_conversion_20:{{.*}} FullCommentAsHTML=[<p class="para-brief"> <b>Aaa</b></p>]
+// CHECK: annotate-comments.cpp:336:6: FunctionDecl=comment_to_html_conversion_24:{{.*}} FullCommentAsHTML=[<p class="para-brief"> <b>Aaa</b></p>]
 // CHECK-NEXT:  CommentAST=[
 // CHECK-NEXT:    (CXComment_FullComment
 // CHECK-NEXT:       (CXComment_Paragraph
 // CHECK-NEXT:         (CXComment_Text Text=[ ] IsWhitespace)
 // CHECK-NEXT:         (CXComment_InlineCommand CommandName=[b] RenderBold Arg[0]=Aaa)))]
-// CHECK: annotate-comments.cpp:315:6: FunctionDecl=comment_to_html_conversion_21:{{.*}} FullCommentAsHTML=[<p class="para-brief"> <tt>Aaa</tt> <tt>Bbb</tt></p>]
+// CHECK: annotate-comments.cpp:339:6: FunctionDecl=comment_to_html_conversion_25:{{.*}} FullCommentAsHTML=[<p class="para-brief"> <tt>Aaa</tt> <tt>Bbb</tt></p>]
 // CHECK-NEXT:  CommentAST=[
 // CHECK-NEXT:    (CXComment_FullComment
 // CHECK-NEXT:       (CXComment_Paragraph
@@ -609,7 +696,7 @@
 // CHECK-NEXT:         (CXComment_InlineCommand CommandName=[c] RenderMonospaced Arg[0]=Aaa)
 // CHECK-NEXT:         (CXComment_Text Text=[ ] IsWhitespace)
 // CHECK-NEXT:         (CXComment_InlineCommand CommandName=[p] RenderMonospaced Arg[0]=Bbb)))]
-// CHECK: annotate-comments.cpp:318:6: FunctionDecl=comment_to_html_conversion_22:{{.*}} FullCommentAsHTML=[<p class="para-brief"> <em>Aaa</em> <em>Bbb</em> <em>Ccc</em></p>]
+// CHECK: annotate-comments.cpp:342:6: FunctionDecl=comment_to_html_conversion_26:{{.*}} FullCommentAsHTML=[<p class="para-brief"> <em>Aaa</em> <em>Bbb</em> <em>Ccc</em></p>]
 // CHECK-NEXT:  CommentAST=[
 // CHECK-NEXT:    (CXComment_FullComment
 // CHECK-NEXT:       (CXComment_Paragraph
@@ -619,7 +706,7 @@
 // CHECK-NEXT:         (CXComment_InlineCommand CommandName=[e] RenderEmphasized Arg[0]=Bbb)
 // CHECK-NEXT:         (CXComment_Text Text=[ ] IsWhitespace)
 // CHECK-NEXT:         (CXComment_InlineCommand CommandName=[em] RenderEmphasized Arg[0]=Ccc)))]
-// CHECK: annotate-comments.cpp:321:6: FunctionDecl=comment_to_html_conversion_23:{{.*}} FullCommentAsHTML=[<p class="para-brief"> \ @ & $ # < > % " . ::</p>]
+// CHECK: annotate-comments.cpp:345:6: FunctionDecl=comment_to_html_conversion_27:{{.*}} FullCommentAsHTML=[<p class="para-brief"> \ @ & $ # < > % " . ::</p>]
 // CHECK-NEXT:  CommentAST=[
 // CHECK-NEXT:    (CXComment_FullComment
 // CHECK-NEXT:       (CXComment_Paragraph
@@ -645,7 +732,7 @@
 // CHECK-NEXT:         (CXComment_Text Text=[.])
 // CHECK-NEXT:         (CXComment_Text Text=[ ] IsWhitespace)
 // CHECK-NEXT:         (CXComment_Text Text=[::])))]
-// CHECK: annotate-comments.cpp:324:6: FunctionDecl=comment_to_html_conversion_24:{{.*}} FullCommentAsHTML=[<p class="para-brief"> & < > "</p>]
+// CHECK: annotate-comments.cpp:348:6: FunctionDecl=comment_to_html_conversion_28:{{.*}} FullCommentAsHTML=[<p class="para-brief"> & < > "</p>]
 // CHECK-NEXT:  CommentAST=[
 // CHECK-NEXT:    (CXComment_FullComment
 // CHECK-NEXT:       (CXComment_Paragraph
@@ -657,7 +744,7 @@
 // CHECK-NEXT:         (CXComment_Text Text=[>])
 // CHECK-NEXT:         (CXComment_Text Text=[ ] IsWhitespace)
 // CHECK-NEXT:         (CXComment_Text Text=["])))]
-// CHECK: annotate-comments.cpp:327:6: FunctionDecl=comment_to_html_conversion_25:{{.*}} FullCommentAsHTML=[<p class="para-brief"> <em>0<i</em></p>]
+// CHECK: annotate-comments.cpp:351:6: FunctionDecl=comment_to_html_conversion_29:{{.*}} FullCommentAsHTML=[<p class="para-brief"> <em>0<i</em></p>]
 // CHECK-NEXT:  CommentAST=[
 // CHECK-NEXT:    (CXComment_FullComment
 // CHECK-NEXT:       (CXComment_Paragraph

Modified: cfe/trunk/test/Sema/warn-documentation.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/Sema/warn-documentation.cpp?rev=161087&r1=161086&r2=161087&view=diff
==============================================================================
--- cfe/trunk/test/Sema/warn-documentation.cpp (original)
+++ cfe/trunk/test/Sema/warn-documentation.cpp Tue Jul 31 17:37:06 2012
@@ -170,18 +170,86 @@
  int test_param15(int bbb, int ccc);
 };
 
+// expected-warning at +1 {{parameter 'aab' not found in the function declaration}}
+/// \param aab Blah blah.
+template<typename T>
+void test_param16(int bbb, int ccc);
+
 // expected-warning at +3 {{parameter 'a' is already documented}}
 // expected-note at +1 {{previous documentation}}
 /// \param a Aaa.
 /// \param a Aaa.
-int test_param16(int a);
+int test_param17(int a);
 
 // expected-warning at +4 {{parameter 'x2' is already documented}}
 // expected-note at +2 {{previous documentation}}
 /// \param x1 Aaa.
 /// \param x2 Bbb.
 /// \param x2 Ccc.
-int test_param17(int x1, int x2, int x3);
+int test_param18(int x1, int x2, int x3);
+
+
+// expected-warning at +1 {{'\tparam' command used in a comment that is not attached to a template declaration}}
+/// \tparam T Aaa
+int test_tparam1;
+
+// expected-warning at +1 {{'\tparam' command used in a comment that is not attached to a template declaration}}
+/// \tparam T Aaa
+void test_tparam2(int aaa);
+
+// expected-warning at +1 {{empty paragraph passed to '\tparam' command}}
+/// \tparam
+/// \param aaa Blah blah
+template<typename T>
+void test_tparam3(T aaa);
+
+// expected-warning at +1 {{template parameter 'T' not found in the template declaration}} expected-note at +1 {{did you mean 'TT'?}}
+/// \tparam T Aaa
+template<typename TT>
+void test_tparam4(TT aaa);
+
+// expected-warning at +1 {{template parameter 'T' not found in the template declaration}} expected-note at +1 {{did you mean 'TT'?}}
+/// \tparam T Aaa
+template<typename TT>
+class test_tparam5 {
+  // expected-warning at +1 {{template parameter 'T' not found in the template declaration}} expected-note at +1 {{did you mean 'TTT'?}}
+  /// \tparam T Aaa
+  template<typename TTT>
+  void test_tparam6(TTT aaa);
+};
+
+/// \tparam T1 Aaa
+/// \tparam T2 Bbb
+template<typename T1, typename T2>
+void test_tparam7(T1 aaa, T2 bbb);
+
+// expected-warning at +1 {{template parameter 'SomTy' not found in the template declaration}} expected-note at +1 {{did you mean 'SomeTy'?}}
+/// \tparam SomTy Aaa
+/// \tparam OtherTy Bbb
+template<typename SomeTy, typename OtherTy>
+void test_tparam8(SomeTy aaa, OtherTy bbb);
+
+// expected-warning at +2 {{template parameter 'T1' is already documented}} expected-note at +1 {{previous documentation}}
+/// \tparam T1 Aaa
+/// \tparam T1 Bbb
+template<typename T1, typename T2>
+void test_tparam9(T1 aaa, T2 bbb);
+
+/// \tparam T Aaa
+/// \tparam TT Bbb
+template<template<typename T> class TT>
+void test_tparam10(TT<int> aaa);
+
+/// \tparam T Aaa
+/// \tparam TT Bbb
+/// \tparam TTT Ccc
+template<template<template<typename T> class TT, class C> class TTT>
+void test_tparam11();
+
+/// \tparam I Aaa
+template<int I>
+void test_tparam12();
+
 
 // expected-warning at +1 {{empty paragraph passed to '\brief' command}}
 int test1; ///< \brief\brief Aaa
@@ -292,6 +360,125 @@
   }
 }
 
+// expected-warning at +1 {{empty paragraph passed to '\brief' command}}
+/// \brief\brief Aaa
+/// \tparam T Aaa
+template<typename T>
+void test_attach26(T aaa);
+
+// expected-warning at +1 {{empty paragraph passed to '\brief' command}}
+/// \brief\brief Aaa
+/// \tparam T Aaa
+template<typename T, typename U>
+void test_attach27(T aaa, U bbb);
+
+// expected-warning at +2 {{empty paragraph passed to '\brief' command}}
+// expected-warning at +2 {{template parameter 'T' not found in the template declaration}}
+/// \brief\brief Aaa
+/// \tparam T Aaa
+template<>
+void test_attach27(int aaa, int bbb);
+
+// expected-warning at +1 {{empty paragraph passed to '\brief' command}}
+/// \brief\brief Aaa
+/// \tparam T Aaa
+template<typename T>
+class test_attach28 {
+  T aaa;
+};
+
+// expected-warning at +1 {{empty paragraph passed to '\brief' command}}
+/// \brief\brief Aaa
+/// \tparam T Aaa
+template<typename T, typename U>
+class test_attach29 { };
+
+// expected-warning at +1 {{empty paragraph passed to '\brief' command}}
+/// \brief\brief Aaa
+/// \tparam T Aaa
+template<typename T>
+class test_attach29<T, int> { };
+
+// expected-warning at +1 {{empty paragraph passed to '\brief' command}}
+/// \brief\brief Aaa
+template<>
+class test_attach29<int, int> { };
+
+// expected-warning at +1 {{empty paragraph passed to '\brief' command}}
+/// \brief\brief Aaa
+/// \tparam T Aaa
+template<typename T, typename U, typename V>
+class test_attach30 { };
+
+// expected-warning at +1 {{empty paragraph passed to '\brief' command}}
+/// \brief\brief Aaa
+/// \tparam T Aaa
+template<typename T, typename U>
+class test_attach30<T, U, int> { };
+
+// expected-warning at +1 {{empty paragraph passed to '\brief' command}}
+/// \brief\brief Aaa
+/// \tparam T Aaa
+template<typename T>
+class test_attach30<T, int, int> { };
+
+// expected-warning at +2 {{empty paragraph passed to '\brief' command}}
+// expected-warning at +2 {{template parameter 'T' not found in the template declaration}}
+/// \brief\brief Aaa
+/// \tparam T Aaa
+template<>
+class test_attach30<int, int, int> { };
+
+// expected-warning at +1 {{empty paragraph passed to '\brief' command}}
+/// \brief\brief Aaa
+class test_attach31 {
+  // expected-warning at +1 {{empty paragraph passed to '\brief' command}}
+  /// \brief\brief Aaa
+  /// \tparam T Aaa
+  template<typename T, typename U>
+  void test_attach32(T aaa, U bbb);
+};
+
+template<typename T>
+class test_attach33 {
+  // expected-warning at +2 {{empty paragraph passed to '\brief' command}}
+  // expected-warning at +2 {{template parameter 'T' not found in the template declaration}}
+  /// \brief\brief Aaa
+  /// \tparam T Aaa
+  template<typename TT, typename UU>
+  void test_attach34(TT aaa, UU bbb);
+};
+
+// expected-warning at +2 {{empty paragraph passed to '\brief' command}}
+// expected-warning at +2 {{template parameter 'T' not found in the template declaration}}
+/// \brief\brief Aaa
+/// \tparam T Aaa
+template<> template<>
+void test_attach33<int>::test_attach34(int aaa, int bbb) {}
+
+template<typename T>
+class test_attach35 {
+  // expected-warning at +2 {{empty paragraph passed to '\brief' command}}
+  // expected-warning at +2 {{'\tparam' command used in a comment that is not attached to a template declaration}}
+  /// \brief\brief Aaa
+  /// \tparam T Aaa
+  void test_attach36(int aaa, int bbb);
+};
+
+// expected-warning at +1 {{empty paragraph passed to '\brief' command}}
+/// \brief\brief Aaa
+/// \tparam T Aaa
+template<typename T>
+void test_attach35<T>::test_attach36(int aaa, int bbb) {}
+
+// expected-warning at +2 {{empty paragraph passed to '\brief' command}}
+// expected-warning at +2 {{template parameter 'T' not found in the template declaration}}
+/// \brief\brief Aaa
+/// \tparam T Aaa
+template<>
+void test_attach35<int>::test_attach36(int aaa, int bbb) {}
+
+
 // PR13411, reduced.  We used to crash on this.
 /**
  * @code Aaa.

Modified: cfe/trunk/tools/c-index-test/c-index-test.c
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/tools/c-index-test/c-index-test.c?rev=161087&r1=161086&r2=161087&view=diff
==============================================================================
--- cfe/trunk/tools/c-index-test/c-index-test.c (original)
+++ cfe/trunk/tools/c-index-test/c-index-test.c Tue Jul 31 17:37:06 2012
@@ -378,6 +378,23 @@
     else
       printf(" ParamIndex=Invalid");
     break;
+  case CXComment_TParamCommand:
+    printf("CXComment_TParamCommand");
+    PrintCXStringWithPrefixAndDispose(
+        "ParamName",
+        clang_TParamCommandComment_getParamName(Comment));
+    if (clang_TParamCommandComment_isParamPositionValid(Comment)) {
+      printf(" ParamPosition={");
+      for (i = 0, e = clang_TParamCommandComment_getDepth(Comment);
+           i != e; ++i) {
+        printf("%u", clang_TParamCommandComment_getIndex(Comment, i));
+        if (i != e - 1)
+          printf(", ");
+      }
+      printf("}");
+    } else
+      printf(" ParamPosition=Invalid");
+    break;
   case CXComment_VerbatimBlockCommand:
     printf("CXComment_VerbatimBlockCommand");
     PrintCXStringWithPrefixAndDispose(

Modified: cfe/trunk/tools/libclang/CXComment.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/tools/libclang/CXComment.cpp?rev=161087&r1=161086&r2=161087&view=diff
==============================================================================
--- cfe/trunk/tools/libclang/CXComment.cpp (original)
+++ cfe/trunk/tools/libclang/CXComment.cpp Tue Jul 31 17:37:06 2012
@@ -59,6 +59,9 @@
   case Comment::ParamCommandCommentKind:
     return CXComment_ParamCommand;
 
+  case Comment::TParamCommandCommentKind:
+    return CXComment_TParamCommand;
+
   case Comment::VerbatimBlockCommentKind:
     return CXComment_VerbatimBlockCommand;
 
@@ -291,6 +294,38 @@
   llvm_unreachable("unknown ParamCommandComment::PassDirection");
 }
 
+CXString clang_TParamCommandComment_getParamName(CXComment CXC) {
+  const TParamCommandComment *TPCC = getASTNodeAs<TParamCommandComment>(CXC);
+  if (!TPCC || !TPCC->hasParamName())
+    return createCXString((const char *) 0);
+
+  return createCXString(TPCC->getParamName(), /*DupString=*/ false);
+}
+
+unsigned clang_TParamCommandComment_isParamPositionValid(CXComment CXC) {
+  const TParamCommandComment *TPCC = getASTNodeAs<TParamCommandComment>(CXC);
+  if (!TPCC)
+    return false;
+
+  return TPCC->isPositionValid();
+}
+
+unsigned clang_TParamCommandComment_getDepth(CXComment CXC) {
+  const TParamCommandComment *TPCC = getASTNodeAs<TParamCommandComment>(CXC);
+  if (!TPCC || !TPCC->isPositionValid())
+    return 0;
+
+  return TPCC->getDepth();
+}
+
+unsigned clang_TParamCommandComment_getIndex(CXComment CXC, unsigned Depth) {
+  const TParamCommandComment *TPCC = getASTNodeAs<TParamCommandComment>(CXC);
+  if (!TPCC || !TPCC->isPositionValid() || Depth >= TPCC->getDepth())
+    return 0;
+
+  return TPCC->getIndex(Depth);
+}
+
 CXString clang_VerbatimBlockLineComment_getText(CXComment CXC) {
   const VerbatimBlockLineComment *VBL =
       getASTNodeAs<VerbatimBlockLineComment>(CXC);
@@ -333,6 +368,34 @@
   }
 };
 
+/// This comparison will sort template parameters in the following order:
+/// \li real template parameters (depth = 1) in index order;
+/// \li all other names (depth > 1);
+/// \li unresolved names.
+class TParamCommandCommentComparePosition {
+public:
+  bool operator()(const TParamCommandComment *LHS,
+                  const TParamCommandComment *RHS) const {
+    // Sort unresolved names last.
+    if (!LHS->isPositionValid())
+      return false;
+    if (!RHS->isPositionValid())
+      return true;
+
+    if (LHS->getDepth() > 1)
+      return false;
+    if (RHS->getDepth() > 1)
+      return true;
+
+    // Sort template parameters in index order.
+    if (LHS->getDepth() == 1 && RHS->getDepth() == 1)
+      return LHS->getIndex(0) < RHS->getIndex(0);
+
+    // Leave all other names in source order.
+    return true;
+  }
+};
+
 class CommentASTToHTMLConverter :
     public ConstCommentVisitor<CommentASTToHTMLConverter> {
 public:
@@ -349,6 +412,7 @@
   void visitParagraphComment(const ParagraphComment *C);
   void visitBlockCommandComment(const BlockCommandComment *C);
   void visitParamCommandComment(const ParamCommandComment *C);
+  void visitTParamCommandComment(const TParamCommandComment *C);
   void visitVerbatimBlockComment(const VerbatimBlockComment *C);
   void visitVerbatimBlockLineComment(const VerbatimBlockLineComment *C);
   void visitVerbatimLineComment(const VerbatimLineComment *C);
@@ -486,6 +550,34 @@
   Result << "</dd>";
 }
 
+void CommentASTToHTMLConverter::visitTParamCommandComment(
+                                  const TParamCommandComment *C) {
+  if (C->isPositionValid()) {
+    if (C->getDepth() == 1)
+      Result << "<dt class=\"taram-name-index-"
+             << C->getIndex(0)
+             << "\">";
+    else
+      Result << "<dt class=\"taram-name-index-other\">";
+  } else
+    Result << "<dt class=\"tparam-name-index-invalid\">";
+
+  Result << C->getParamName() << "</dt>";
+
+  if (C->isPositionValid()) {
+    if (C->getDepth() == 1)
+      Result << "<dd class=\"tparam-descr-index-"
+             << C->getIndex(0)
+             << "\">";
+    else
+      Result << "<dd class=\"tparam-descr-index-other\">";
+  } else
+    Result << "<dd class=\"tparam-descr-index-invalid\">";
+
+  visitNonStandaloneParagraphComment(C->getParagraph());
+  Result << "</dd>";
+}
+
 void CommentASTToHTMLConverter::visitVerbatimBlockComment(
                                   const VerbatimBlockComment *C) {
   unsigned NumLines = C->getNumLines();
@@ -518,6 +610,7 @@
   const ParagraphComment *FirstParagraph = NULL;
   const BlockCommandComment *Returns = NULL;
   SmallVector<const ParamCommandComment *, 8> Params;
+  SmallVector<const TParamCommandComment *, 4> TParams;
   SmallVector<const BlockContentComment *, 8> MiscBlocks;
 
   // Extract various blocks into separate variables and vectors above.
@@ -568,6 +661,15 @@
       break;
     }
 
+    case Comment::TParamCommandCommentKind: {
+      const TParamCommandComment *TPCC = cast<TParamCommandComment>(Child);
+      if (!TPCC->hasParamName())
+        break;
+
+      TParams.push_back(TPCC);
+      break;
+    }
+
     case Comment::VerbatimBlockCommentKind:
     case Comment::VerbatimLineCommentKind:
       MiscBlocks.push_back(cast<BlockCommandComment>(Child));
@@ -590,6 +692,9 @@
   std::stable_sort(Params.begin(), Params.end(),
                    ParamCommandCommentCompareIndex());
 
+  std::stable_sort(TParams.begin(), TParams.end(),
+                   TParamCommandCommentComparePosition());
+
   bool FirstParagraphIsBrief = false;
   if (Brief)
     visit(Brief);
@@ -607,6 +712,13 @@
     visit(C);
   }
 
+  if (TParams.size() != 0) {
+    Result << "<dl>";
+    for (unsigned i = 0, e = TParams.size(); i != e; ++i)
+      visit(TParams[i]);
+    Result << "</dl>";
+  }
+
   if (Params.size() != 0) {
     Result << "<dl>";
     for (unsigned i = 0, e = Params.size(); i != e; ++i)

Modified: cfe/trunk/tools/libclang/libclang.exports
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/tools/libclang/libclang.exports?rev=161087&r1=161086&r2=161087&view=diff
==============================================================================
--- cfe/trunk/tools/libclang/libclang.exports (original)
+++ cfe/trunk/tools/libclang/libclang.exports Tue Jul 31 17:37:06 2012
@@ -42,6 +42,10 @@
 clang_ParamCommandComment_getParamIndex
 clang_ParamCommandComment_isDirectionExplicit
 clang_ParamCommandComment_getDirection
+clang_TParamCommandComment_getParamName
+clang_TParamCommandComment_isParamPositionValid
+clang_TParamCommandComment_getDepth
+clang_TParamCommandComment_getIndex
 clang_VerbatimBlockLineComment_getText
 clang_VerbatimLineComment_getText
 clang_HTMLTagComment_getAsString

Modified: cfe/trunk/unittests/AST/CommentParser.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/unittests/AST/CommentParser.cpp?rev=161087&r1=161086&r2=161087&view=diff
==============================================================================
--- cfe/trunk/unittests/AST/CommentParser.cpp (original)
+++ cfe/trunk/unittests/AST/CommentParser.cpp Tue Jul 31 17:37:06 2012
@@ -221,6 +221,39 @@
   return ::testing::AssertionSuccess();
 }
 
+::testing::AssertionResult HasTParamCommandAt(
+                              const Comment *C,
+                              size_t Idx,
+                              TParamCommandComment *&TPCC,
+                              StringRef CommandName,
+                              StringRef ParamName,
+                              ParagraphComment *&Paragraph) {
+  ::testing::AssertionResult AR = GetChildAt(C, Idx, TPCC);
+  if (!AR)
+    return AR;
+
+  StringRef ActualCommandName = TPCC->getCommandName();
+  if (ActualCommandName != CommandName)
+    return ::testing::AssertionFailure()
+        << "TParamCommandComment has name \"" << ActualCommandName.str() << "\", "
+           "expected \"" << CommandName.str() << "\"";
+
+  if (!TPCC->hasParamName())
+    return ::testing::AssertionFailure()
+        << "TParamCommandComment has no parameter name";
+
+  StringRef ActualParamName = TPCC->getParamName();
+  if (ActualParamName != ParamName)
+    return ::testing::AssertionFailure()
+        << "TParamCommandComment has parameter name \"" << ActualParamName.str()
+        << "\", "
+           "expected \"" << ParamName.str() << "\"";
+
+  Paragraph = TPCC->getParagraph();
+
+  return ::testing::AssertionSuccess();
+}
+
 ::testing::AssertionResult HasInlineCommandAt(const Comment *C,
                                               size_t Idx,
                                               InlineCommandComment *&ICC,
@@ -838,6 +871,33 @@
   }
 }
 
+TEST_F(CommentParserTest, TParamCommand1) {
+  const char *Sources[] = {
+    "// \\tparam aaa Bbb\n",
+    "// \\tparam\n"
+    "//     aaa Bbb\n",
+    "// \\tparam \n"
+    "//     aaa Bbb\n",
+    "// \\tparam aaa\n"
+    "// Bbb\n"
+  };
+
+  for (size_t i = 0, e = array_lengthof(Sources); i != e; i++) {
+    FullComment *FC = parseString(Sources[i]);
+    ASSERT_TRUE(HasChildCount(FC, 2));
+
+    ASSERT_TRUE(HasParagraphCommentAt(FC, 0, " "));
+    {
+      TParamCommandComment *TPCC;
+      ParagraphComment *PC;
+      ASSERT_TRUE(HasTParamCommandAt(FC, 1, TPCC, "tparam",
+                                     "aaa", PC));
+      ASSERT_TRUE(HasChildCount(TPCC, 1));
+      ASSERT_TRUE(HasParagraphCommentAt(TPCC, 0, " Bbb"));
+    }
+  }
+}
+
 TEST_F(CommentParserTest, InlineCommand1) {
   const char *Source = "// \\c";
 





More information about the cfe-commits mailing list