Index: test/Sema/warn-documentation.cpp
===================================================================
--- test/Sema/warn-documentation.cpp	(revision 161244)
+++ test/Sema/warn-documentation.cpp	(working copy)
@@ -79,34 +79,92 @@
 
 
 // expected-warning@+1 {{empty paragraph passed to '\brief' command}}
-/// \brief\brief Aaa
+/// \brief\returns Aaa
 int test_block_command1(int);
 
 // expected-warning@+1 {{empty paragraph passed to '\brief' command}}
-/// \brief \brief Aaa
+/// \brief \returns Aaa
 int test_block_command2(int);
 
 // expected-warning@+1 {{empty paragraph passed to '\brief' command}}
 /// \brief
-/// \brief Aaa
+/// \returns Aaa
 int test_block_command3(int);
 
 // expected-warning@+1 {{empty paragraph passed to '\brief' command}}
 /// \brief
 ///
-/// \brief Aaa
+/// \returns Aaa
 int test_block_command4(int);
 
 // There is trailing whitespace on one of the following lines, don't remove it!
 // expected-warning@+1 {{empty paragraph passed to '\brief' command}}
 /// \brief
 /// 
-/// \brief Aaa
+/// \returns Aaa
 int test_block_command5(int);
 
 /// \brief \c Aaa
 int test_block_command6(int);
 
+// expected-warning@+5 {{command '\brief' is duplicate}} expected-note@+1 {{previous command here}}
+/// \brief Aaa
+///
+/// Bbb
+///
+/// \brief Ccc
+int test_duplicate_brief1(int);
+
+// expected-warning@+5 {{command '\short' is duplicate}} expected-note@+1 {{previous command here}}
+/// \short Aaa
+///
+/// Bbb
+///
+/// \short Ccc
+int test_duplicate_brief2(int);
+
+// expected-warning@+5 {{command '\brief' is duplicate}} expected-note@+1 {{previous command here}} expected-note@+1 {{commands '\brief' and '\short' are aliases}}
+/// \short Aaa
+///
+/// Bbb
+///
+/// \brief Ccc
+int test_duplicate_brief3(int);
+
+
+// expected-warning@+5 {{command '\return' is duplicate}} expected-note@+1 {{previous command here}}
+/// \return Aaa
+///
+/// Bbb
+///
+/// \return Ccc
+int test_duplicate_returns1(int);
+
+// expected-warning@+5 {{command '\returns' is duplicate}} expected-note@+1 {{previous command here}}
+/// \returns Aaa
+///
+/// Bbb
+///
+/// \returns Ccc
+int test_duplicate_returns2(int);
+
+// expected-warning@+5 {{command '\result' is duplicate}} expected-note@+1 {{previous command here}}
+/// \result Aaa
+///
+/// Bbb
+///
+/// \result Ccc
+int test_duplicate_returns3(int);
+
+// expected-warning@+5 {{command '\return' is duplicate}} expected-note@+1 {{previous command here}} expected-note@+1 {{commands '\return' and '\returns' are aliases}}
+/// \returns Aaa
+///
+/// Bbb
+///
+/// \return Ccc
+int test_duplicate_returns4(int);
+
+
 // expected-warning@+1 {{'\param' command used in a comment that is not attached to a function declaration}}
 /// \param a Blah blah.
 int test_param1;
@@ -264,16 +322,16 @@
 
 
 // expected-warning@+1 {{empty paragraph passed to '\brief' command}}
-int test1; ///< \brief\brief Aaa
+int test1; ///< \brief\author Aaa
 
 // expected-warning@+2 {{empty paragraph passed to '\brief' command}}
 // expected-warning@+2 {{empty paragraph passed to '\brief' command}}
-int test2, ///< \brief\brief Aaa
-    test3; ///< \brief\brief Aaa
+int test2, ///< \brief\author Aaa
+    test3; ///< \brief\author Aaa
 
 // expected-warning@+1 {{empty paragraph passed to '\brief' command}}
 int test4; ///< \brief
-           ///< \brief Aaa
+           ///< \author Aaa
 
 
 // Check that we attach the comment to the declaration during parsing in the
@@ -281,118 +339,118 @@
 // documentation comments that are not attached to anything.
 
 // expected-warning@+1 {{empty paragraph passed to '\brief' command}}
-/// \brief\brief Aaa
+/// \brief\author Aaa
 int test_attach1;
 
 // expected-warning@+1 {{empty paragraph passed to '\brief' command}}
-/// \brief\brief Aaa
+/// \brief\author Aaa
 int test_attach2(int);
 
 // expected-warning@+1 {{empty paragraph passed to '\brief' command}}
-/// \brief\brief Aaa
+/// \brief\author Aaa
 struct test_attach3 {
   // expected-warning@+1 {{empty paragraph passed to '\brief' command}}
-  /// \brief\brief Aaa
+  /// \brief\author Aaa
   int test_attach4;
 
   // expected-warning@+1 {{empty paragraph passed to '\brief' command}}
-  int test_attach5; ///< \brief\brief Aaa
+  int test_attach5; ///< \brief\author Aaa
 
   // expected-warning@+1 {{empty paragraph passed to '\brief' command}}
-  /// \brief\brief Aaa
+  /// \brief\author Aaa
   int test_attach6(int);
 };
 
 // expected-warning@+1 {{empty paragraph passed to '\brief' command}}
-/// \brief\brief Aaa
+/// \brief\author Aaa
 class test_attach7 {
   // expected-warning@+1 {{empty paragraph passed to '\brief' command}}
-  /// \brief\brief Aaa
+  /// \brief\author Aaa
   int test_attach8;
 
   // expected-warning@+1 {{empty paragraph passed to '\brief' command}}
-  int test_attach9; ///< \brief\brief Aaa
+  int test_attach9; ///< \brief\author Aaa
 
   // expected-warning@+1 {{empty paragraph passed to '\brief' command}}
-  /// \brief\brief Aaa
+  /// \brief\author Aaa
   int test_attach10(int);
 };
 
 // expected-warning@+1 {{empty paragraph passed to '\brief' command}}
-/// \brief\brief Aaa
+/// \brief\author Aaa
 enum test_attach9 {
   // expected-warning@+1 {{empty paragraph passed to '\brief' command}}
-  /// \brief\brief Aaa
+  /// \brief\author Aaa
   test_attach10,
 
   // expected-warning@+1 {{empty paragraph passed to '\brief' command}}
-  test_attach11 ///< \brief\brief Aaa
+  test_attach11 ///< \brief\author Aaa
 };
 
 // expected-warning@+1 {{empty paragraph passed to '\brief' command}}
-/// \brief\brief Aaa
+/// \brief\author Aaa
 struct test_noattach12 *test_attach13;
 
 // expected-warning@+1 {{empty paragraph passed to '\brief' command}}
-/// \brief\brief Aaa
+/// \brief\author Aaa
 typedef struct test_noattach14 *test_attach15;
 
 // expected-warning@+1 {{empty paragraph passed to '\brief' command}}
-/// \brief\brief Aaa
+/// \brief\author Aaa
 typedef struct test_attach16 { int a; } test_attach17;
 
 struct S { int a; };
 
 // expected-warning@+1 {{empty paragraph passed to '\brief' command}}
-/// \brief\brief Aaa
+/// \brief\author Aaa
 struct S *test_attach18;
 
 // expected-warning@+1 {{empty paragraph passed to '\brief' command}}
-/// \brief\brief Aaa
+/// \brief\author Aaa
 typedef struct S *test_attach19;
 
 // expected-warning@+1 {{empty paragraph passed to '\brief' command}}
-/// \brief\brief Aaa
+/// \brief\author Aaa
 struct test_attach20;
 
 // expected-warning@+1 {{empty paragraph passed to '\brief' command}}
-/// \brief\brief Aaa
+/// \brief\author Aaa
 typedef struct test_attach21 {
   // expected-warning@+1 {{empty paragraph passed to '\brief' command}}
-  /// \brief\brief Aaa
+  /// \brief\author Aaa
   int test_attach22;
 } test_attach23;
 
 // expected-warning@+1 {{empty paragraph passed to '\brief' command}}
-/// \brief\brief Aaa
+/// \brief\author Aaa
 namespace test_attach24 {
   // expected-warning@+1 {{empty paragraph passed to '\brief' command}}
-  /// \brief\brief Aaa
+  /// \brief\author Aaa
   namespace test_attach25 {
   }
 }
 
 // expected-warning@+1 {{empty paragraph passed to '\brief' command}}
-/// \brief\brief Aaa
+/// \brief\author Aaa
 /// \tparam T Aaa
 template<typename T>
 void test_attach26(T aaa);
 
 // expected-warning@+1 {{empty paragraph passed to '\brief' command}}
-/// \brief\brief Aaa
+/// \brief\author Aaa
 /// \tparam T Aaa
 template<typename T, typename U>
 void test_attach27(T aaa, U bbb);
 
 // expected-warning@+2 {{empty paragraph passed to '\brief' command}}
 // expected-warning@+2 {{template parameter 'T' not found in the template declaration}}
-/// \brief\brief Aaa
+/// \brief\author Aaa
 /// \tparam T Aaa
 template<>
 void test_attach27(int aaa, int bbb);
 
 // expected-warning@+1 {{empty paragraph passed to '\brief' command}}
-/// \brief\brief Aaa
+/// \brief\author Aaa
 /// \tparam T Aaa
 template<typename T>
 class test_attach28 {
@@ -400,61 +458,61 @@
 };
 
 // expected-warning@+1 {{empty paragraph passed to '\brief' command}}
-/// \brief\brief Aaa
+/// \brief\author Aaa
 using test_attach29 = test_attach28<int>;
 
 // expected-warning@+1 {{empty paragraph passed to '\brief' command}}
-/// \brief\brief Aaa
+/// \brief\author Aaa
 /// \tparam T Aaa
 template<typename T, typename U>
 class test_attach30 { };
 
 // expected-warning@+1 {{empty paragraph passed to '\brief' command}}
-/// \brief\brief Aaa
+/// \brief\author Aaa
 /// \tparam T Aaa
 template<typename T>
 class test_attach30<T, int> { };
 
 // expected-warning@+1 {{empty paragraph passed to '\brief' command}}
-/// \brief\brief Aaa
+/// \brief\author Aaa
 template<>
 class test_attach30<int, int> { };
 
 // expected-warning@+1 {{empty paragraph passed to '\brief' command}}
-/// \brief\brief Aaa
+/// \brief\author Aaa
 template<typename T>
 using test_attach31 = test_attach30<T, int>;
 
 // expected-warning@+1 {{empty paragraph passed to '\brief' command}}
-/// \brief\brief Aaa
+/// \brief\author Aaa
 /// \tparam T Aaa
 template<typename T, typename U, typename V>
 class test_attach32 { };
 
 // expected-warning@+1 {{empty paragraph passed to '\brief' command}}
-/// \brief\brief Aaa
+/// \brief\author Aaa
 /// \tparam T Aaa
 template<typename T, typename U>
 class test_attach32<T, U, int> { };
 
 // expected-warning@+1 {{empty paragraph passed to '\brief' command}}
-/// \brief\brief Aaa
+/// \brief\author Aaa
 /// \tparam T Aaa
 template<typename T>
 class test_attach32<T, int, int> { };
 
 // expected-warning@+2 {{empty paragraph passed to '\brief' command}}
 // expected-warning@+2 {{template parameter 'T' not found in the template declaration}}
-/// \brief\brief Aaa
+/// \brief\author Aaa
 /// \tparam T Aaa
 template<>
 class test_attach32<int, int, int> { };
 
 // expected-warning@+1 {{empty paragraph passed to '\brief' command}}
-/// \brief\brief Aaa
+/// \brief\author Aaa
 class test_attach33 {
   // expected-warning@+1 {{empty paragraph passed to '\brief' command}}
-  /// \brief\brief Aaa
+  /// \brief\author Aaa
   /// \tparam T Aaa
   template<typename T, typename U>
   void test_attach34(T aaa, U bbb);
@@ -464,7 +522,7 @@
 class test_attach35 {
   // expected-warning@+2 {{empty paragraph passed to '\brief' command}}
   // expected-warning@+2 {{template parameter 'T' not found in the template declaration}}
-  /// \brief\brief Aaa
+  /// \brief\author Aaa
   /// \tparam T Aaa
   template<typename TT, typename UU>
   void test_attach36(TT aaa, UU bbb);
@@ -472,7 +530,7 @@
 
 // expected-warning@+2 {{empty paragraph passed to '\brief' command}}
 // expected-warning@+2 {{template parameter 'T' not found in the template declaration}}
-/// \brief\brief Aaa
+/// \brief\author Aaa
 /// \tparam T Aaa
 template<> template<>
 void test_attach35<int>::test_attach36(int aaa, int bbb) {}
@@ -481,20 +539,20 @@
 class test_attach37 {
   // expected-warning@+2 {{empty paragraph passed to '\brief' command}}
   // expected-warning@+2 {{'\tparam' command used in a comment that is not attached to a template declaration}}
-  /// \brief\brief Aaa
+  /// \brief\author Aaa
   /// \tparam T Aaa
   void test_attach38(int aaa, int bbb);
 };
 
 // expected-warning@+1 {{empty paragraph passed to '\brief' command}}
-/// \brief\brief Aaa
+/// \brief\author Aaa
 /// \tparam T Aaa
 template<typename T>
 void test_attach37<T>::test_attach38(int aaa, int bbb) {}
 
 // expected-warning@+2 {{empty paragraph passed to '\brief' command}}
 // expected-warning@+2 {{template parameter 'T' not found in the template declaration}}
-/// \brief\brief Aaa
+/// \brief\author Aaa
 /// \tparam T Aaa
 template<>
 void test_attach37<int>::test_attach38(int aaa, int bbb) {}
Index: include/clang/Basic/DiagnosticCommentKinds.td
===================================================================
--- include/clang/Basic/DiagnosticCommentKinds.td	(revision 161244)
+++ include/clang/Basic/DiagnosticCommentKinds.td	(working copy)
@@ -47,6 +47,16 @@
   "empty paragraph passed to '\\%0' command">,
   InGroup<Documentation>, DefaultIgnore;
 
+def warn_doc_block_command_duplicate : Warning<
+  "command '\\%0' is duplicate">,
+  InGroup<Documentation>, DefaultIgnore;
+
+def note_doc_block_command_previous : Note<
+  "previous command here">;
+
+def note_doc_block_command_aliases : Note<
+  "commands '\\%0' and '\\%1' are aliases">;
+
 // \param command
 
 def warn_doc_param_invalid_direction : Warning<
Index: include/clang/AST/CommentSema.h
===================================================================
--- include/clang/AST/CommentSema.h	(revision 161244)
+++ include/clang/AST/CommentSema.h	(working copy)
@@ -56,6 +56,12 @@
   /// Contains a valid value if \c DeclInfo->IsFilled is true.
   llvm::StringMap<TParamCommandComment *> TemplateParameterDocs;
 
+  /// AST node for the \\brief command and its aliases.
+  const BlockCommandComment *BriefCommand;
+
+  /// AST node for the \\returns command and its aliases.
+  const BlockCommandComment *ReturnsCommand;
+
   DiagnosticBuilder Diag(SourceLocation Loc, unsigned DiagID) {
     return Diags.Report(Loc, DiagID);
   }
@@ -181,6 +187,10 @@
 
   void checkBlockCommandEmptyParagraph(BlockCommandComment *Command);
 
+  /// Emit diagnostics about duplicate block commands that should be
+  /// used only once per comment, e.g., \\brief and \\returns.
+  void checkBlockCommandDuplicate(const BlockCommandComment *Command);
+
   bool isFunctionDecl();
   bool isTemplateDecl();
 
@@ -210,6 +220,8 @@
   bool isBlockCommand(StringRef Name);
   bool isParamCommand(StringRef Name);
   bool isTParamCommand(StringRef Name);
+  bool isBriefCommand(StringRef Name);
+  bool isReturnsCommand(StringRef Name);
   unsigned getBlockCommandNumArgs(StringRef Name);
 
   bool isInlineCommand(StringRef Name) const;
Index: lib/AST/CommentSema.cpp
===================================================================
--- lib/AST/CommentSema.cpp	(revision 161244)
+++ lib/AST/CommentSema.cpp	(working copy)
@@ -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);
   return Command;
 }
 
@@ -472,6 +473,36 @@
   }
 }
 
+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();
+  Diag(PrevCommand->getLocation(), diag::note_doc_block_command_previous)
+      << Command->getSourceRange();
+  if (Name != PrevCommand->getCommandName())
+    Diag(PrevCommand->getLocation(), diag::note_doc_block_command_aliases)
+        << Name
+        << PrevCommand->getCommandName();
+}
+
 bool Sema::isFunctionDecl() {
   if (!ThisDeclInfo)
     return false;
@@ -643,16 +674,14 @@
 
 // TODO: tablegen
 bool Sema::isBlockCommand(StringRef Name) {
-  return llvm::StringSwitch<bool>(Name)
-      .Cases("brief", "short", true)
-      .Case("result", true)
-      .Case("return", true)
-      .Case("returns", true)
+  return isBriefCommand(Name) || isReturnsCommand(Name) ||
+      isParamCommand(Name) || isTParamCommand(Name) ||
+      llvm::StringSwitch<bool>(Name)
       .Case("author", true)
       .Case("authors", true)
       .Case("pre", true)
       .Case("post", true)
-      .Default(false) || isParamCommand(Name) || isTParamCommand(Name);
+      .Default(false);
 }
 
 bool Sema::isParamCommand(StringRef Name) {
@@ -666,6 +695,14 @@
   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";
+}
+
 unsigned Sema::getBlockCommandNumArgs(StringRef Name) {
   return llvm::StringSwitch<unsigned>(Name)
       .Cases("brief", "short", 0)
