Comment diagnostics: warn on duplicate \brief and \return commands.

Doxygen manual claims that multiple \brief or \returns commands will be merged
together, but actual behavior is different (second \brief command becomes a
part of a discussion, second \returns becomes a "Returns: blah" paragraph on
its own).  Anyway, it seems to be a bad idea to use multiple \brief or \returns
commands in a single command.


git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@161325 91177308-0d34-0410-b5e6-96231b3b80d8
diff --git a/lib/AST/CommentSema.cpp b/lib/AST/CommentSema.cpp
index 770d5bb..57f8fda 100644
--- a/lib/AST/CommentSema.cpp
+++ b/lib/AST/CommentSema.cpp
@@ -20,7 +20,7 @@
 Sema::Sema(llvm::BumpPtrAllocator &Allocator, const SourceManager &SourceMgr,
            DiagnosticsEngine &Diags) :
     Allocator(Allocator), SourceMgr(SourceMgr), Diags(Diags),
-    ThisDeclInfo(NULL) {
+    ThisDeclInfo(NULL), BriefCommand(NULL), ReturnsCommand(NULL) {
 }
 
 void Sema::setDecl(const Decl *D) {
@@ -55,6 +55,7 @@
                               ParagraphComment *Paragraph) {
   Command->setParagraph(Paragraph);
   checkBlockCommandEmptyParagraph(Command);
+  checkBlockCommandDuplicate(Command);
   checkReturnsCommand(Command);
   return Command;
 }
@@ -507,6 +508,39 @@
     << Command->getSourceRange();
 }
 
+void Sema::checkBlockCommandDuplicate(const BlockCommandComment *Command) {
+  StringRef Name = Command->getCommandName();
+  const BlockCommandComment *PrevCommand = NULL;
+  if (isBriefCommand(Name)) {
+    if (!BriefCommand) {
+      BriefCommand = Command;
+      return;
+    }
+    PrevCommand = BriefCommand;
+  } else if (isReturnsCommand(Name)) {
+    if (!ReturnsCommand) {
+      ReturnsCommand = Command;
+      return;
+    }
+    PrevCommand = ReturnsCommand;
+  } else {
+    // We don't want to check this command for duplicates.
+    return;
+  }
+  Diag(Command->getLocation(), diag::warn_doc_block_command_duplicate)
+      << Name
+      << Command->getSourceRange();
+  if (Name == PrevCommand->getCommandName())
+    Diag(PrevCommand->getLocation(), diag::note_doc_block_command_previous)
+        << PrevCommand->getCommandName()
+        << Command->getSourceRange();
+  else
+    Diag(PrevCommand->getLocation(),
+         diag::note_doc_block_command_previous_alias)
+        << PrevCommand->getCommandName()
+        << Name;
+}
+
 bool Sema::isFunctionDecl() {
   if (!ThisDeclInfo)
     return false;
@@ -678,10 +712,9 @@
 
 // TODO: tablegen
 bool Sema::isBlockCommand(StringRef Name) {
-  return isReturnsCommand(Name) ||
+  return isBriefCommand(Name) || isReturnsCommand(Name) ||
       isParamCommand(Name) || isTParamCommand(Name) ||
       llvm::StringSwitch<bool>(Name)
-      .Cases("brief", "short", true)
       .Case("author", true)
       .Case("authors", true)
       .Case("pre", true)
@@ -700,6 +733,10 @@
   return Name == "tparam";
 }
 
+bool Sema::isBriefCommand(StringRef Name) {
+  return Name == "brief" || Name == "short";
+}
+
 bool Sema::isReturnsCommand(StringRef Name) {
   return Name == "returns" || Name == "return" || Name == "result";
 }