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.


git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@161087 91177308-0d34-0410-b5e6-96231b3b80d8
diff --git a/lib/AST/ASTContext.cpp b/lib/AST/ASTContext.cpp
index 46a4d87..edcfe8e 100644
--- a/lib/AST/ASTContext.cpp
+++ b/lib/AST/ASTContext.cpp
@@ -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();
diff --git a/lib/AST/CommentBriefParser.cpp b/lib/AST/CommentBriefParser.cpp
index 47d52e1..22209c0 100644
--- a/lib/AST/CommentBriefParser.cpp
+++ b/lib/AST/CommentBriefParser.cpp
@@ -48,6 +48,7 @@
       .Case("pre", true)
       .Case("post", true)
       .Cases("param", "arg", true)
+      .Case("tparam", true)
       .Default(false);
 }
 } // unnamed namespace
diff --git a/lib/AST/CommentDumper.cpp b/lib/AST/CommentDumper.cpp
index f02ea33..dffc823 100644
--- a/lib/AST/CommentDumper.cpp
+++ b/lib/AST/CommentDumper.cpp
@@ -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);
 
diff --git a/lib/AST/CommentParser.cpp b/lib/AST/CommentParser.cpp
index 6d53567..63560e1 100644
--- a/lib/AST/CommentParser.cpp
+++ b/lib/AST/CommentParser.cpp
@@ -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
diff --git a/lib/AST/CommentSema.cpp b/lib/AST/CommentSema.cpp
index bb0d03e..7b42c86 100644
--- a/lib/AST/CommentSema.cpp
+++ b/lib/AST/CommentSema.cpp
@@ -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;
+  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;;
+}
 
-      unsigned EditDistance = Typo.edit_distance(Name, true, MaxEditDistance);
-      if (EditDistance < BestEditDistance) {
-        BestEditDistance = EditDistance;
-        BestPVDIndex = i;
-      }
+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)