diff --git a/include/clang-c/Index.h b/include/clang-c/Index.h
index c9742e2483299b1eed3d6261a4fe8e100cbbe8f6..b7bd8bb738fa7d90b24c2d6dfc2f9f2117096a53 100644
--- a/include/clang-c/Index.h
+++ b/include/clang-c/Index.h
@@ -3187,6 +3187,19 @@ CINDEX_LINKAGE CXCursor clang_getCanonicalCursor(CXCursor);
  */
 CINDEX_LINKAGE int clang_Cursor_getObjCSelectorIndex(CXCursor);
 
+/**
+ * \brief Given a cursor that represents a declaration, return the associated
+ * comment's source range.  The range may include multiple consecutive comments
+ * with whitespace in between.
+ */
+CINDEX_LINKAGE CXSourceRange clang_Cursor_getCommentRange(CXCursor C);
+
+/**
+ * \brief Given a cursor that represents a declaration, return the associated
+ * comment text, including comment markers.
+ */
+CINDEX_LINKAGE CXString clang_Cursor_getRawCommentText(CXCursor C);
+
 /**
  * @}
  */
diff --git a/include/clang/AST/ASTContext.h b/include/clang/AST/ASTContext.h
index 6ceed17eb8925662ad7c10485d05fd5dc8178202..172ac88edf459555abc6d71d78b0df8cf2b254ee 100644
--- a/include/clang/AST/ASTContext.h
+++ b/include/clang/AST/ASTContext.h
@@ -27,6 +27,7 @@
 #include "clang/AST/TemplateName.h"
 #include "clang/AST/Type.h"
 #include "clang/AST/CanonicalType.h"
+#include "clang/Comments/RawCommentList.h"
 #include "llvm/ADT/DenseMap.h"
 #include "llvm/ADT/FoldingSet.h"
 #include "llvm/ADT/IntrusiveRefCntPtr.h"
@@ -51,7 +52,6 @@ namespace clang {
   class ASTMutationListener;
   class IdentifierTable;
   class SelectorTable;
-  class SourceManager;
   class TargetInfo;
   class CXXABI;
   // Decls
@@ -418,6 +418,30 @@ public:
     return FullSourceLoc(Loc,SourceMgr);
   }
 
+  /// \brief All comments in this translation unit.
+  RawCommentList Comments;
+
+  /// \brief True if comments are already loaded from ExternalASTSource.
+  mutable bool CommentsLoaded;
+
+  /// \brief Mapping from declarations to their comments (stored within
+  /// Comments list), once we have already looked up the comment associated
+  /// with a given declaration.
+  mutable llvm::DenseMap<const Decl *, const RawComment *> DeclComments;
+
+  /// \brief Return the Doxygen-style comment attached to a given declaration,
+  /// without looking into cache.
+  const RawComment *getRawCommentForDeclNoCache(const Decl *D) const;
+
+public:
+  void addComment(const RawComment &RC) {
+    Comments.addComment(RC, *this);
+  }
+
+  /// \brief Return the Doxygen-style comment attached to a given declaration.
+  /// Returns NULL if no comment is attached.
+  const RawComment *getRawCommentForDecl(const Decl *D) const;
+
   /// \brief Retrieve the attributes for the given declaration.
   AttrVec& getDeclAttrs(const Decl *D);
 
diff --git a/include/clang/AST/ExternalASTSource.h b/include/clang/AST/ExternalASTSource.h
index e2a60d5cf0326e3249932716452963adccf58c2a..7aedfe2ef6004c5454ae03cf2496d1cbb654a7bd 100644
--- a/include/clang/AST/ExternalASTSource.h
+++ b/include/clang/AST/ExternalASTSource.h
@@ -179,6 +179,9 @@ public:
   /// \c ObjCInterfaceDecl::setExternallyCompleted().
   virtual void CompleteType(ObjCInterfaceDecl *Class) { }
 
+  /// \brief Loads comment ranges.
+  virtual void ReadComments() { }
+
   /// \brief Notify ExternalASTSource that we started deserialization of
   /// a decl or type so until FinishedDeserializing is called there may be
   /// decls that are initializing. Must be paired with FinishedDeserializing.
diff --git a/include/clang/Basic/SourceManager.h b/include/clang/Basic/SourceManager.h
index 603bfebfd36fae9518705476e006de54c0bab151..19fa52ee1dbfe424aa8c1b4ed3769ab1ade31da5 100644
--- a/include/clang/Basic/SourceManager.h
+++ b/include/clang/Basic/SourceManager.h
@@ -1244,19 +1244,6 @@ public:
   /// \returns true if LHS source location comes before RHS, false otherwise.
   bool isBeforeInTranslationUnit(SourceLocation LHS, SourceLocation RHS) const;
 
-  /// \brief Comparison function class.
-  class LocBeforeThanCompare : public std::binary_function<SourceLocation,
-                                                         SourceLocation, bool> {
-    SourceManager &SM;
-
-  public:
-    explicit LocBeforeThanCompare(SourceManager &SM) : SM(SM) { }
-
-    bool operator()(SourceLocation LHS, SourceLocation RHS) const {
-      return SM.isBeforeInTranslationUnit(LHS, RHS);
-    }
-  };
-
   /// \brief Determines the order of 2 source locations in the "source location
   /// address space".
   bool isBeforeInSLocAddrSpace(SourceLocation LHS, SourceLocation RHS) const {
@@ -1500,6 +1487,35 @@ private:
   friend class ASTWriter;
 };
 
+/// \brief Comparison function object.
+template<typename T>
+class BeforeThanCompare;
+
+/// \brief Compare two source locations.
+template<>
+class BeforeThanCompare<SourceLocation> {
+  SourceManager &SM;
+
+public:
+  explicit BeforeThanCompare(SourceManager &SM) : SM(SM) { }
+
+  bool operator()(SourceLocation LHS, SourceLocation RHS) const {
+    return SM.isBeforeInTranslationUnit(LHS, RHS);
+  }
+};
+
+/// \brief Compare two non-overlapping source ranges.
+template<>
+class BeforeThanCompare<SourceRange> {
+  SourceManager &SM;
+
+public:
+  explicit BeforeThanCompare(SourceManager &SM) : SM(SM) { }
+
+  bool operator()(SourceRange LHS, SourceRange RHS) {
+    return SM.isBeforeInTranslationUnit(LHS.getBegin(), RHS.getBegin());
+  }
+};
 
 }  // end namespace clang
 
diff --git a/include/clang/Comments/RawCommentList.h b/include/clang/Comments/RawCommentList.h
new file mode 100644
index 0000000000000000000000000000000000000000..bf0b616383e08f7c7579b4dd3d159cdad58bafe9
--- /dev/null
+++ b/include/clang/Comments/RawCommentList.h
@@ -0,0 +1,172 @@
+//===--- RawCommentList.h - Classes for processing raw comments -*- C++ -*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_COMMENTS_RAW_COMMENT_LIST_H
+#define LLVM_CLANG_COMMENTS_RAW_COMMENT_LIST_H
+
+#include "clang/Basic/SourceManager.h"
+#include "llvm/ADT/ArrayRef.h"
+
+namespace clang {
+
+class ASTContext;
+class ASTReader;
+
+class RawComment {
+public:
+  enum CommentKind {
+    CK_Invalid,      ///< Invalid comment
+    CK_OrdinaryBCPL, ///< Any normal BCPL comments
+    CK_OrdinaryC,    ///< Any normal C comment
+    CK_BCPLSlash,    ///< \code /// stuff \endcode
+    CK_BCPLExcl,     ///< \code //! stuff \endcode
+    CK_JavaDoc,      ///< \code /** stuff */ \endcode
+    CK_Qt,           ///< \code /*! stuff */ \endcode, also used by HeaderDoc
+    CK_Merged        ///< Two or more Doxygen comments merged together
+  };
+
+  RawComment() : Kind(CK_Invalid), IsAlmostTrailingComment(false) { }
+
+  RawComment(const SourceManager &SourceMgr, SourceRange SR,
+             bool Merged = false);
+
+  CommentKind getKind() const LLVM_READONLY {
+    return (CommentKind) Kind;
+  }
+
+  bool isInvalid() const LLVM_READONLY {
+    return Kind == CK_Invalid;
+  }
+
+  bool isMerged() const LLVM_READONLY {
+    return Kind == CK_Merged;
+  }
+
+  /// Returns true if it is a comment that should be put after a member:
+  /// \code ///< stuff \endcode
+  /// \code //!< stuff \endcode
+  /// \code /**< stuff */ \endcode
+  /// \code /*!< stuff */ \endcode
+  bool isTrailingComment() const LLVM_READONLY {
+    assert(isDoxygen());
+    return IsTrailingComment;
+  }
+
+  /// Returns true if it is a probable typo:
+  /// \code //< stuff \endcode
+  /// \code /*< stuff */ \endcode
+  bool isAlmostTrailingComment() const LLVM_READONLY {
+    return IsAlmostTrailingComment;
+  }
+
+  /// Returns true if this comment is not a Doxygen comment.
+  bool isOrdinary() const LLVM_READONLY {
+    return (Kind == CK_OrdinaryBCPL) || (Kind == CK_OrdinaryC);
+  }
+
+  /// Returns true if this comment any kind of a Doxygen comment.
+  bool isDoxygen() const LLVM_READONLY {
+    return !isInvalid() && !isOrdinary();
+  }
+
+  /// Returns raw comment text with comment markers.
+  StringRef getRawText(const SourceManager &SourceMgr) const {
+    if (RawTextValid)
+      return RawText;
+
+    RawText = getRawTextSlow(SourceMgr);
+    RawTextValid = true;
+    return RawText;
+  }
+
+  SourceRange getSourceRange() const LLVM_READONLY {
+    return Range;
+  }
+
+  unsigned getBeginLine(const SourceManager &SM) const;
+  unsigned getEndLine(const SourceManager &SM) const;
+
+private:
+  SourceRange Range;
+
+  mutable StringRef RawText;
+  mutable bool RawTextValid : 1; ///< True if RawText is valid
+
+  unsigned Kind : 3;
+
+  bool IsTrailingComment : 1;
+  bool IsAlmostTrailingComment : 1;
+
+  mutable bool BeginLineValid : 1; ///< True if BeginLine is valid
+  mutable bool EndLineValid : 1;   ///< True if EndLine is valid
+  mutable unsigned BeginLine;      ///< Cached line number
+  mutable unsigned EndLine;        ///< Cached line number
+
+  /// \brief Constructor for AST deserialization.
+  RawComment(SourceRange SR, CommentKind K, bool IsTrailingComment,
+             bool IsAlmostTrailingComment) :
+    Range(SR), RawTextValid(false), Kind(K),
+    IsTrailingComment(IsTrailingComment),
+    IsAlmostTrailingComment(IsAlmostTrailingComment),
+    BeginLineValid(false), EndLineValid(false)
+  { }
+
+  StringRef getRawTextSlow(const SourceManager &SourceMgr) const;
+
+  friend class ASTReader;
+};
+
+/// \brief Compare comments' source locations.
+template<>
+class BeforeThanCompare<RawComment> {
+  const SourceManager &SM;
+
+public:
+  explicit BeforeThanCompare(const SourceManager &SM) : SM(SM) { }
+
+  bool operator()(const RawComment &LHS, const SourceRange &RHS) {
+    return SM.isBeforeInTranslationUnit(LHS.getSourceRange().getBegin(),
+                                        RHS.getBegin());
+  }
+};
+
+/// \brief This class represents all comments included in the translation unit,
+/// sorted in order of appearance in the translation unit.
+class RawCommentList {
+public:
+  RawCommentList(SourceManager &SourceMgr) :
+    SourceMgr(SourceMgr), OnlyWhitespaceSeen(true) { }
+
+  void addComment(const RawComment &RC, ASTContext &Context);
+
+  ArrayRef<RawComment> getComments() const {
+    return Comments;
+  }
+
+private:
+  SourceManager &SourceMgr;
+  std::vector<RawComment> Comments;
+  RawComment LastComment;
+  bool OnlyWhitespaceSeen;
+
+  void addCommentsToFront(const std::vector<RawComment> &C) {
+    size_t OldSize = Comments.size();
+    Comments.resize(C.size() + OldSize);
+    std::copy_backward(Comments.begin(), Comments.begin() + OldSize,
+                       Comments.end());
+    std::copy(C.begin(), C.end(), Comments.begin());
+  }
+
+  friend class ASTReader;
+};
+
+} // end namespace clang
+
+#endif
+
diff --git a/include/clang/Lex/Preprocessor.h b/include/clang/Lex/Preprocessor.h
index 7d3d6e1080d32b9f3aab6559c8c96910ed704212..3b94a5725e46059ccc3cc120cf921728a70c8531 100644
--- a/include/clang/Lex/Preprocessor.h
+++ b/include/clang/Lex/Preprocessor.h
@@ -510,12 +510,12 @@ public:
   }
 
   /// \brief Add the specified comment handler to the preprocessor.
-  void AddCommentHandler(CommentHandler *Handler);
+  void addCommentHandler(CommentHandler *Handler);
 
   /// \brief Remove the specified comment handler.
   ///
   /// It is an error to remove a handler that has not been registered.
-  void RemoveCommentHandler(CommentHandler *Handler);
+  void removeCommentHandler(CommentHandler *Handler);
 
   /// \brief Set the code completion handler to the given object.
   void setCodeCompletionHandler(CodeCompletionHandler &Handler) {
diff --git a/include/clang/Parse/Parser.h b/include/clang/Parse/Parser.h
index cf308327f3d6fb4de67a4e3a1c28b7977577f51b..b451ed97e97c890ba6ec10859e607265971bd884 100644
--- a/include/clang/Parse/Parser.h
+++ b/include/clang/Parse/Parser.h
@@ -170,6 +170,7 @@ class Parser : public CodeCompletionHandler {
   OwningPtr<PragmaHandler> RedefineExtnameHandler;
   OwningPtr<PragmaHandler> FPContractHandler;
   OwningPtr<PragmaHandler> OpenCLExtensionHandler;
+  OwningPtr<CommentHandler> CommentHandler;
 
   /// Whether the '>' token acts as an operator or not. This will be
   /// true except when we are parsing an expression within a C++
diff --git a/include/clang/Sema/Sema.h b/include/clang/Sema/Sema.h
index 408763f5cff13244fc601e118b9978ff5265063e..4be823a8db2605140e28645c1c4f15d605f98628 100644
--- a/include/clang/Sema/Sema.h
+++ b/include/clang/Sema/Sema.h
@@ -849,6 +849,8 @@ public:
   /// WeakTopLevelDeclDecls - access to \#pragma weak-generated Decls
   SmallVector<Decl*,2> &WeakTopLevelDecls() { return WeakTopLevelDecl; }
 
+  void ActOnComment(SourceRange Comment);
+
   //===--------------------------------------------------------------------===//
   // Type Analysis / Processing: SemaType.cpp.
   //
diff --git a/include/clang/Serialization/ASTBitCodes.h b/include/clang/Serialization/ASTBitCodes.h
index 42d0ad24675996e437d828b0151dbcc8b35ba183..2049da97306afd2a1d9996b87f6a55c3930f57e8 100644
--- a/include/clang/Serialization/ASTBitCodes.h
+++ b/include/clang/Serialization/ASTBitCodes.h
@@ -207,7 +207,10 @@ namespace clang {
       PREPROCESSOR_DETAIL_BLOCK_ID,
       
       /// \brief The block containing the submodule structure.
-      SUBMODULE_BLOCK_ID
+      SUBMODULE_BLOCK_ID,
+
+      /// \brief The block containing comments.
+      COMMENTS_BLOCK_ID
     };
 
     /// \brief Record types that occur within the AST block itself.
@@ -545,7 +548,12 @@ namespace clang {
       /// \brief Specifies a required feature.
       SUBMODULE_REQUIRES = 7
     };
-    
+
+    /// \brief Record types used within a comments block.
+    enum CommentRecordTypes {
+      COMMENTS_RAW_COMMENT = 0
+    };
+
     /// \defgroup ASTAST AST file AST constants
     ///
     /// The constants in this group describe various components of the
diff --git a/include/clang/Serialization/ASTReader.h b/include/clang/Serialization/ASTReader.h
index 21fd674b2aba60b10f345fa4e9657cbfd8bfa583..d084254eadc7b057cad2cfdd0434bc1006a2a4e9 100644
--- a/include/clang/Serialization/ASTReader.h
+++ b/include/clang/Serialization/ASTReader.h
@@ -1501,6 +1501,13 @@ public:
   SwitchCase *getSwitchCaseWithID(unsigned ID);
 
   void ClearSwitchCaseIDs();
+
+  /// \brief Cursors for comments blocks.
+  SmallVector<std::pair<llvm::BitstreamCursor,
+                        serialization::ModuleFile *>, 8> CommentsCursors;
+
+  /// \brief Loads comments ranges.
+  void ReadComments();
 };
 
 /// \brief Helper class that saves the current stream position and
diff --git a/include/clang/Serialization/ASTWriter.h b/include/clang/Serialization/ASTWriter.h
index 16c3766f8fc228021c593db7396e37676ef290c1..472b407992084cb249e9918c7dfc560f1eb4c62d 100644
--- a/include/clang/Serialization/ASTWriter.h
+++ b/include/clang/Serialization/ASTWriter.h
@@ -414,6 +414,7 @@ private:
   uint64_t WriteDeclContextVisibleBlock(ASTContext &Context, DeclContext *DC);
   void WriteTypeDeclOffsets();
   void WriteFileDeclIDsMap();
+  void WriteComments();
   void WriteSelectors(Sema &SemaRef);
   void WriteReferencedSelectorsPool(Sema &SemaRef);
   void WriteIdentifierTable(Preprocessor &PP, IdentifierResolver &IdResolver,
diff --git a/lib/ARCMigrate/TransEmptyStatementsAndDealloc.cpp b/lib/ARCMigrate/TransEmptyStatementsAndDealloc.cpp
index 0f6c799374608f5a60f0d0dd0cdbe15f4b5895cd..d8fabcd94867ca4572998823f6b3d28a5ad5d82d 100644
--- a/lib/ARCMigrate/TransEmptyStatementsAndDealloc.cpp
+++ b/lib/ARCMigrate/TransEmptyStatementsAndDealloc.cpp
@@ -44,7 +44,7 @@ static bool isEmptyARCMTMacroStatement(NullStmt *S,
   SourceManager &SM = Ctx.getSourceManager();
   std::vector<SourceLocation>::iterator
     I = std::upper_bound(MacroLocs.begin(), MacroLocs.end(), SemiLoc,
-                         SourceManager::LocBeforeThanCompare(SM));
+                         BeforeThanCompare<SourceLocation>(SM));
   --I;
   SourceLocation
       AfterMacroLoc = I->getLocWithOffset(getARCMTMacroName().size());
diff --git a/lib/AST/ASTContext.cpp b/lib/AST/ASTContext.cpp
index 9ad3a7f2e72997511d7f03ced517d38fba503333..d8677c2ee1baf81507520a849e4e3cc06276e923 100644
--- a/lib/AST/ASTContext.cpp
+++ b/lib/AST/ASTContext.cpp
@@ -53,6 +53,107 @@ enum FloatingRank {
   HalfRank, FloatRank, DoubleRank, LongDoubleRank
 };
 
+const RawComment *ASTContext::getRawCommentForDeclNoCache(const Decl *D) const {
+  if (!CommentsLoaded && ExternalSource) {
+    ExternalSource->ReadComments();
+    CommentsLoaded = true;
+  }
+
+  assert(D);
+
+  // TODO: handle comments for function parameters properly.
+  if (isa<ParmVarDecl>(D))
+    return NULL;
+
+  ArrayRef<RawComment> RawComments = Comments.getComments();
+
+  // If there are no comments anywhere, we won't find anything.
+  if (RawComments.empty())
+    return NULL;
+
+  // If the declaration doesn't map directly to a location in a file, we
+  // can't find the comment.
+  SourceLocation DeclLoc = D->getLocation();
+  if (DeclLoc.isInvalid() || !DeclLoc.isFileID())
+    return NULL;
+
+  // Find the comment that occurs just after this declaration.
+  ArrayRef<RawComment>::iterator Comment
+      = std::lower_bound(RawComments.begin(),
+                         RawComments.end(),
+                         SourceRange(DeclLoc),
+                         BeforeThanCompare<RawComment>(SourceMgr));
+
+  // Decompose the location for the declaration and find the beginning of the
+  // file buffer.
+  std::pair<FileID, unsigned> DeclLocDecomp = SourceMgr.getDecomposedLoc(DeclLoc);
+
+  // First check whether we have a trailing comment.
+  if (Comment != RawComments.end() &&
+      Comment->isDoxygen() && Comment->isTrailingComment() &&
+      !isa<TagDecl>(D) && !isa<NamespaceDecl>(D)) {
+    std::pair<FileID, unsigned> CommentBeginDecomp
+      = SourceMgr.getDecomposedLoc(Comment->getSourceRange().getBegin());
+    // Check that Doxygen trailing comment comes after the declaration, starts
+    // on the same line and in the same file as the declaration.
+    if (DeclLocDecomp.first == CommentBeginDecomp.first &&
+        SourceMgr.getLineNumber(DeclLocDecomp.first, DeclLocDecomp.second)
+          == SourceMgr.getLineNumber(CommentBeginDecomp.first,
+                                     CommentBeginDecomp.second)) {
+      return &*Comment;
+    }
+  }
+
+  // The comment just after the declaration was not a trailing comment.
+  // Let's look at the previous comment.
+  if (Comment == RawComments.begin())
+    return NULL;
+  --Comment;
+
+  // Check that we actually have a non-member Doxygen comment.
+  if (!Comment->isDoxygen() || Comment->isTrailingComment())
+    return NULL;
+
+  // Decompose the end of the comment.
+  std::pair<FileID, unsigned> CommentEndDecomp
+    = SourceMgr.getDecomposedLoc(Comment->getSourceRange().getEnd());
+
+  // If the comment and the declaration aren't in the same file, then they
+  // aren't related.
+  if (DeclLocDecomp.first != CommentEndDecomp.first)
+    return NULL;
+
+  // Get the corresponding buffer.
+  bool Invalid = false;
+  const char *Buffer = SourceMgr.getBufferData(DeclLocDecomp.first,
+                                               &Invalid).data();
+  if (Invalid)
+    return NULL;
+
+  // Extract text between the comment and declaration.
+  StringRef Text(Buffer + CommentEndDecomp.second,
+                 DeclLocDecomp.second - CommentEndDecomp.second);
+
+  // There should be no other declarations between comment and declaration.
+  if (Text.find_first_of(",;{}") != StringRef::npos)
+    return NULL;
+
+  return &*Comment;
+}
+
+const RawComment *ASTContext::getRawCommentForDecl(const Decl *D) const {
+  // Check whether we have cached a comment string for this declaration
+  // already.
+  llvm::DenseMap<const Decl *, const RawComment *>::iterator Pos
+      = DeclComments.find(D);
+  if (Pos != DeclComments.end())
+      return Pos->second;
+
+  const RawComment *RC = getRawCommentForDeclNoCache(D);
+  DeclComments[D] = RC;
+  return RC;
+}
+
 void 
 ASTContext::CanonicalTemplateTemplateParm::Profile(llvm::FoldingSetNodeID &ID, 
                                                TemplateTemplateParmDecl *Parm) {
@@ -244,6 +345,7 @@ ASTContext::ASTContext(LangOptions& LOpts, SourceManager &SM,
     BuiltinInfo(builtins),
     DeclarationNames(*this),
     ExternalSource(0), Listener(0),
+    Comments(SM), CommentsLoaded(false),
     LastSDM(0, 0),
     UniqueBlockByRefTypeID(0) 
 {
diff --git a/lib/CMakeLists.txt b/lib/CMakeLists.txt
index dfb9d61ff5391160aa908ba5da162ea18ee2c2c3..228b43b23592ac9f9eea14afa42a713550a6f2bb 100644
--- a/lib/CMakeLists.txt
+++ b/lib/CMakeLists.txt
@@ -15,3 +15,4 @@ add_subdirectory(Frontend)
 add_subdirectory(FrontendTool)
 add_subdirectory(Tooling)
 add_subdirectory(StaticAnalyzer)
+add_subdirectory(Comments)
diff --git a/lib/Comments/CMakeLists.txt b/lib/Comments/CMakeLists.txt
new file mode 100644
index 0000000000000000000000000000000000000000..f9561c649cd85d6087ee16a36b857ccb3194b519
--- /dev/null
+++ b/lib/Comments/CMakeLists.txt
@@ -0,0 +1,7 @@
+set(LLVM_USED_LIBS clangBasic clangAST clangLex)
+
+add_clang_library(clangComments
+  CommentLexer.cpp
+  RawCommentList.cpp
+  )
+
diff --git a/lib/Comments/Makefile b/lib/Comments/Makefile
new file mode 100644
index 0000000000000000000000000000000000000000..0783f1f26c23a6bb17d56f73f0a46aa44864b383
--- /dev/null
+++ b/lib/Comments/Makefile
@@ -0,0 +1,14 @@
+##===- clang/lib/Comments/Makefile -------------------------*- Makefile -*-===##
+# 
+#                     The LLVM Compiler Infrastructure
+#
+# This file is distributed under the University of Illinois Open Source
+# License. See LICENSE.TXT for details.
+# 
+##===----------------------------------------------------------------------===##
+
+CLANG_LEVEL := ../..
+LIBRARYNAME := clangComments
+
+include $(CLANG_LEVEL)/Makefile
+
diff --git a/lib/Comments/RawCommentList.cpp b/lib/Comments/RawCommentList.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..7db9175c174a500331996e5de7bf2027e0903564
--- /dev/null
+++ b/lib/Comments/RawCommentList.cpp
@@ -0,0 +1,207 @@
+//===--- RawCommentList.cpp - Processing raw comments -----------*- C++ -*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/Comments/RawCommentList.h"
+#include "clang/AST/ASTContext.h"
+#include "llvm/ADT/STLExtras.h"
+
+using namespace clang;
+
+namespace {
+/// Get comment kind and bool describing if it is a trailing comment.
+std::pair<RawComment::CommentKind, bool> getCommentKind(StringRef Comment) {
+  if (Comment.size() < 3 || Comment[0] != '/')
+    return std::make_pair(RawComment::CK_Invalid, false);
+
+  RawComment::CommentKind K;
+  if (Comment[1] == '/') {
+    if (Comment.size() < 3)
+      return std::make_pair(RawComment::CK_OrdinaryBCPL, false);
+
+    if (Comment[2] == '/')
+      K = RawComment::CK_BCPLSlash;
+    else if (Comment[2] == '!')
+      K = RawComment::CK_BCPLExcl;
+    else
+      return std::make_pair(RawComment::CK_OrdinaryBCPL, false);
+  } else {
+    assert(Comment.size() >= 4);
+
+    // Comment lexer does not understand escapes in comment markers, so pretend
+    // that this is not a comment.
+    if (Comment[1] != '*' ||
+        Comment[Comment.size() - 2] != '*' ||
+        Comment[Comment.size() - 1] != '/')
+      return std::make_pair(RawComment::CK_Invalid, false);
+
+    if (Comment[2] == '*')
+      K = RawComment::CK_JavaDoc;
+    else if (Comment[2] == '!')
+      K = RawComment::CK_Qt;
+    else
+      return std::make_pair(RawComment::CK_OrdinaryC, false);
+  }
+  const bool TrailingComment = (Comment.size() > 3) && (Comment[3] == '<');
+  return std::make_pair(K, TrailingComment);
+}
+
+bool mergedCommentIsTrailingComment(StringRef Comment) {
+  return (Comment.size() > 3) && (Comment[3] == '<');
+}
+} // unnamed namespace
+
+RawComment::RawComment(const SourceManager &SourceMgr, SourceRange SR,
+                       bool Merged) :
+    Range(SR), RawTextValid(false), IsAlmostTrailingComment(false),
+    BeginLineValid(false), EndLineValid(false) {
+  // Extract raw comment text, if possible.
+  if (getRawText(SourceMgr).empty()) {
+    Kind = CK_Invalid;
+    return;
+  }
+
+  if (!Merged) {
+    // Guess comment kind.
+    std::pair<CommentKind, bool> K = getCommentKind(RawText);
+    Kind = K.first;
+    IsTrailingComment = K.second;
+
+    IsAlmostTrailingComment = RawText.startswith("//<") ||
+                                 RawText.startswith("/*<");
+  } else {
+    Kind = CK_Merged;
+    IsTrailingComment = mergedCommentIsTrailingComment(RawText);
+  }
+}
+
+unsigned RawComment::getBeginLine(const SourceManager &SM) const {
+  if (BeginLineValid)
+    return BeginLine;
+
+  std::pair<FileID, unsigned> LocInfo = SM.getDecomposedLoc(Range.getBegin());
+  BeginLine = SM.getLineNumber(LocInfo.first, LocInfo.second);
+  BeginLineValid = true;
+  return BeginLine;
+}
+
+unsigned RawComment::getEndLine(const SourceManager &SM) const {
+  if (EndLineValid)
+    return EndLine;
+
+  std::pair<FileID, unsigned> LocInfo = SM.getDecomposedLoc(Range.getEnd());
+  EndLine = SM.getLineNumber(LocInfo.first, LocInfo.second);
+  EndLineValid = true;
+  return EndLine;
+}
+
+StringRef RawComment::getRawTextSlow(const SourceManager &SourceMgr) const {
+  FileID BeginFileID;
+  FileID EndFileID;
+  unsigned BeginOffset;
+  unsigned EndOffset;
+
+  llvm::tie(BeginFileID, BeginOffset) =
+      SourceMgr.getDecomposedLoc(Range.getBegin());
+  llvm::tie(EndFileID, EndOffset) =
+      SourceMgr.getDecomposedLoc(Range.getEnd());
+
+  const unsigned Length = EndOffset - BeginOffset;
+  if (Length < 2)
+    return StringRef();
+
+  // The comment can't begin in one file and end in another.
+  assert(BeginFileID == EndFileID);
+
+  bool Invalid = false;
+  const char *BufferStart = SourceMgr.getBufferData(BeginFileID,
+                                                    &Invalid).data();
+  if (Invalid)
+    return StringRef();
+
+  return StringRef(BufferStart + BeginOffset, Length);
+}
+
+namespace {
+bool containsOnlyWhitespace(StringRef Str) {
+  return Str.find_first_not_of(" \t\f\v\r\n") == StringRef::npos;
+}
+
+bool onlyWhitespaceBetweenComments(SourceManager &SM,
+                                   const RawComment &C1, const RawComment &C2) {
+  std::pair<FileID, unsigned> C1EndLocInfo = SM.getDecomposedLoc(
+                                                C1.getSourceRange().getEnd());
+  std::pair<FileID, unsigned> C2BeginLocInfo = SM.getDecomposedLoc(
+                                              C2.getSourceRange().getBegin());
+
+  // Question does not make sense if comments are located in different files.
+  if (C1EndLocInfo.first != C2BeginLocInfo.first)
+    return false;
+
+  bool Invalid = false;
+  const char *Buffer = SM.getBufferData(C1EndLocInfo.first, &Invalid).data();
+  if (Invalid)
+    return false;
+
+  StringRef TextBetweenComments(Buffer + C1EndLocInfo.second,
+                                C2BeginLocInfo.second - C1EndLocInfo.second);
+
+  return containsOnlyWhitespace(TextBetweenComments);
+}
+} // unnamed namespace
+
+void RawCommentList::addComment(const RawComment &RC, ASTContext &Context) {
+  if (RC.isInvalid())
+    return;
+
+  assert((Comments.empty() ||
+          SourceMgr.isBeforeInTranslationUnit(
+              Comments[0].getSourceRange().getEnd(),
+              RC.getSourceRange().getBegin())) &&
+         "comments are not coming in source order");
+
+  if (OnlyWhitespaceSeen) {
+    if (!onlyWhitespaceBetweenComments(SourceMgr, LastComment, RC))
+      OnlyWhitespaceSeen = false;
+  }
+
+  LastComment = RC;
+
+  // Ordinary comments are not interesting for us.
+  if (RC.isOrdinary())
+    return;
+
+  // If this is the first Doxygen comment, save it (because there isn't
+  // anything to merge it with).
+  if (Comments.empty()) {
+    Comments.push_back(RC);
+    OnlyWhitespaceSeen = true;
+    return;
+  }
+
+  const RawComment &C1 = Comments.back();
+  const RawComment &C2 = RC;
+
+  // Merge comments only if there is only whitespace between them.
+  // Can't merge trailing and non-trailing comments.
+  // Merge trailing comments if they are on same or consecutive lines.
+  if (OnlyWhitespaceSeen &&
+      (C1.isTrailingComment() == C2.isTrailingComment()) &&
+      (!C1.isTrailingComment() ||
+       C1.getEndLine(SourceMgr) + 1 >= C2.getBeginLine(SourceMgr))) {
+    SourceRange MergedRange(C1.getSourceRange().getBegin(),
+                            C2.getSourceRange().getEnd());
+    RawComment Merged(SourceMgr, MergedRange, true);
+    Comments.pop_back();
+    Comments.push_back(Merged);
+  } else
+    Comments.push_back(RC);
+
+  OnlyWhitespaceSeen = true;
+}
+
diff --git a/lib/Lex/Preprocessor.cpp b/lib/Lex/Preprocessor.cpp
index 5509f5f4e810846783e8307871ad810ed7a550d7..70be2cf7a60c622121864ce93cc05ea4dc05a567 100644
--- a/lib/Lex/Preprocessor.cpp
+++ b/lib/Lex/Preprocessor.cpp
@@ -623,14 +623,14 @@ void Preprocessor::LexAfterModuleImport(Token &Result) {
                                      /*IsIncludeDirective=*/false);
 }
 
-void Preprocessor::AddCommentHandler(CommentHandler *Handler) {
+void Preprocessor::addCommentHandler(CommentHandler *Handler) {
   assert(Handler && "NULL comment handler");
   assert(std::find(CommentHandlers.begin(), CommentHandlers.end(), Handler) ==
          CommentHandlers.end() && "Comment handler already registered");
   CommentHandlers.push_back(Handler);
 }
 
-void Preprocessor::RemoveCommentHandler(CommentHandler *Handler) {
+void Preprocessor::removeCommentHandler(CommentHandler *Handler) {
   std::vector<CommentHandler *>::iterator Pos
   = std::find(CommentHandlers.begin(), CommentHandlers.end(), Handler);
   assert(Pos != CommentHandlers.end() && "Comment handler not registered");
diff --git a/lib/Makefile b/lib/Makefile
index 2eb72a91e68b9b32f0e5bef91e9da67309325562..0e81af19cee04269d570f07c47e33a465482957c 100755
--- a/lib/Makefile
+++ b/lib/Makefile
@@ -10,7 +10,7 @@ CLANG_LEVEL := ..
 
 PARALLEL_DIRS = Headers Basic Lex Parse AST Sema CodeGen Analysis \
                 StaticAnalyzer Edit Rewrite ARCMigrate Serialization Frontend \
-                FrontendTool Tooling Driver
+                FrontendTool Tooling Driver Comments
 
 include $(CLANG_LEVEL)/Makefile
 
diff --git a/lib/Parse/Parser.cpp b/lib/Parse/Parser.cpp
index 0d7d047e00ebf42918c443bfa81852ddf84b14b7..10fb8eb8e9c7b019d6ea1e74109bffee417fdc9e 100644
--- a/lib/Parse/Parser.cpp
+++ b/lib/Parse/Parser.cpp
@@ -23,6 +23,20 @@
 #include "clang/AST/ASTConsumer.h"
 using namespace clang;
 
+/// \brief A comment handler that passes comments found by the preprocessor
+/// to the parser action.
+class ActionCommentHandler : public CommentHandler {
+  Sema &S;
+
+public:
+  explicit ActionCommentHandler(Sema &S) : S(S) { }
+
+  virtual bool HandleComment(Preprocessor &PP, SourceRange Comment) {
+    S.ActOnComment(Comment);
+    return false;
+  }
+};
+
 IdentifierInfo *Parser::getSEHExceptKeyword() {
   // __except is accepted as a (contextual) keyword 
   if (!Ident__except && (getLangOpts().MicrosoftExt || getLangOpts().Borland))
@@ -77,7 +91,10 @@ Parser::Parser(Preprocessor &pp, Sema &actions, bool SkipFunctionBodies)
 
     PP.AddPragmaHandler("OPENCL", FPContractHandler.get());
   }
-      
+
+  CommentHandler.reset(new ActionCommentHandler(actions));
+  PP.addCommentHandler(CommentHandler.get());
+
   PP.setCodeCompletionHandler(*this);
 }
 
@@ -422,6 +439,9 @@ Parser::~Parser() {
 
   PP.RemovePragmaHandler("STDC", FPContractHandler.get());
   FPContractHandler.reset();
+
+  PP.removeCommentHandler(CommentHandler.get());
+
   PP.clearCodeCompletionHandler();
 
   assert(TemplateIds.empty() && "Still alive TemplateIdAnnotations around?");
diff --git a/lib/Sema/Sema.cpp b/lib/Sema/Sema.cpp
index 6323589bd839a1ce876b92a529c4e5f102bed436..9e4b29197138a193a7a110f2f83a74825d79cb14 100644
--- a/lib/Sema/Sema.cpp
+++ b/lib/Sema/Sema.cpp
@@ -1014,6 +1014,11 @@ LambdaScopeInfo *Sema::getCurLambda() {
   return dyn_cast<LambdaScopeInfo>(FunctionScopes.back());  
 }
 
+void Sema::ActOnComment(SourceRange Comment) {
+  RawComment RC(SourceMgr, Comment);
+  Context.addComment(RC);
+}
+
 // Pin this vtable to this file.
 ExternalSemaSource::~ExternalSemaSource() {}
 
diff --git a/lib/Sema/SemaType.cpp b/lib/Sema/SemaType.cpp
index d6c8d92ce879d94a08b09c97412b98b874ffe15d..4bd9b14bf5a9fa67be08110f0c4b6a016cae0bbd 100644
--- a/lib/Sema/SemaType.cpp
+++ b/lib/Sema/SemaType.cpp
@@ -2550,7 +2550,7 @@ static TypeSourceInfo *GetFullTypeForDeclarator(TypeProcessingState &state,
         //  RemovalLocs.push_back(Chunk.Fun.getRestrictQualifierLoc());
         if (!RemovalLocs.empty()) {
           std::sort(RemovalLocs.begin(), RemovalLocs.end(),
-                    SourceManager::LocBeforeThanCompare(S.getSourceManager()));
+                    BeforeThanCompare<SourceLocation>(S.getSourceManager()));
           RemovalRange = SourceRange(RemovalLocs.front(), RemovalLocs.back());
           Loc = RemovalLocs.front();
         }
diff --git a/lib/Serialization/ASTReader.cpp b/lib/Serialization/ASTReader.cpp
index eb74566011f1c4c62ad6613146fe1d145ade20ec..f5aa74ea98ebe2ed7219064808691067963128df 100644
--- a/lib/Serialization/ASTReader.cpp
+++ b/lib/Serialization/ASTReader.cpp
@@ -1737,6 +1737,17 @@ ASTReader::ReadASTBlock(ModuleFile &F) {
         }
         break;
 
+      case COMMENTS_BLOCK_ID: {
+        llvm::BitstreamCursor C = Stream;
+        if (Stream.SkipBlock() ||
+            ReadBlockAbbrevs(C, COMMENTS_BLOCK_ID)) {
+          Error("malformed comments block in AST file");
+          return Failure;
+        }
+        CommentsCursors.push_back(std::make_pair(C, &F));
+        break;
+      }
+
       default:
         if (!Stream.SkipBlock())
           break;
@@ -6258,6 +6269,61 @@ void ASTReader::ClearSwitchCaseIDs() {
   CurrSwitchCaseStmts->clear();
 }
 
+void ASTReader::ReadComments() {
+  std::vector<RawComment> Comments;
+  for (SmallVectorImpl<std::pair<llvm::BitstreamCursor,
+                                 serialization::ModuleFile *> >::iterator
+       I = CommentsCursors.begin(),
+       E = CommentsCursors.end();
+       I != E; ++I) {
+    llvm::BitstreamCursor &Cursor = I->first;
+    serialization::ModuleFile &F = *I->second;
+    SavedStreamPosition SavedPosition(Cursor);
+
+    RecordData Record;
+    while (true) {
+      unsigned Code = Cursor.ReadCode();
+      if (Code == llvm::bitc::END_BLOCK)
+        break;
+
+      if (Code == llvm::bitc::ENTER_SUBBLOCK) {
+        // No known subblocks, always skip them.
+        Cursor.ReadSubBlockID();
+        if (Cursor.SkipBlock()) {
+          Error("malformed block record in AST file");
+          return;
+        }
+        continue;
+      }
+
+      if (Code == llvm::bitc::DEFINE_ABBREV) {
+        Cursor.ReadAbbrevRecord();
+        continue;
+      }
+
+      // Read a record.
+      Record.clear();
+      switch ((CommentRecordTypes) Cursor.ReadRecord(Code, Record)) {
+        default:  // Default behavior: ignore.
+          break;
+
+        case COMMENTS_RAW_COMMENT: {
+          unsigned Idx = 0;
+          SourceRange SR = ReadSourceRange(F, Record, Idx);
+          RawComment::CommentKind Kind =
+              (RawComment::CommentKind) Record[Idx++];
+          bool IsTrailingComment = Record[Idx++];
+          bool IsAlmostTrailingComment = Record[Idx++];
+          Comments.push_back(RawComment(SR, Kind, IsTrailingComment,
+                                        IsAlmostTrailingComment));
+          break;
+      }
+      }
+    }
+  }
+  Context.Comments.addCommentsToFront(Comments);
+}
+
 void ASTReader::finishPendingActions() {
   while (!PendingIdentifierInfos.empty() || !PendingDeclChains.empty()) {
     // If any identifiers with corresponding top-level declarations have
diff --git a/lib/Serialization/ASTWriter.cpp b/lib/Serialization/ASTWriter.cpp
index de5816df9e33ad28b1ce3d345dc7f11a9209ed92..1f96180507321980b000360cdde50141cd6a32cd 100644
--- a/lib/Serialization/ASTWriter.cpp
+++ b/lib/Serialization/ASTWriter.cpp
@@ -2240,6 +2240,23 @@ void ASTWriter::WriteFileDeclIDsMap() {
   Stream.EmitRecordWithBlob(AbbrevCode, Record, data(FileSortedIDs));
 }
 
+void ASTWriter::WriteComments() {
+  Stream.EnterSubblock(COMMENTS_BLOCK_ID, 3);
+  ArrayRef<RawComment> RawComments = Context->Comments.getComments();
+  RecordData Record;
+  for (ArrayRef<RawComment>::iterator I = RawComments.begin(),
+                                      E = RawComments.end();
+       I != E; ++I) {
+    Record.clear();
+    AddSourceRange(I->getSourceRange(), Record);
+    Record.push_back(I->getKind());
+    Record.push_back(I->isTrailingComment());
+    Record.push_back(I->isAlmostTrailingComment());
+    Stream.EmitRecord(COMMENTS_RAW_COMMENT, Record);
+  }
+  Stream.ExitBlock();
+}
+
 //===----------------------------------------------------------------------===//
 // Global Method Pool and Selector Serialization
 //===----------------------------------------------------------------------===//
@@ -3415,6 +3432,7 @@ void ASTWriter::WriteASTCore(Sema &SemaRef, MemorizeStatCalls *StatCalls,
 
   WriteFileDeclIDsMap();
   WriteSourceManagerBlock(Context.getSourceManager(), PP, isysroot);
+  WriteComments();
   
   if (Chain) {
     // Write the mapping information describing our module dependencies and how
diff --git a/test/Index/annotate-comments.cpp b/test/Index/annotate-comments.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..634f481139ce9c53eaf29f044d90004852dd936a
--- /dev/null
+++ b/test/Index/annotate-comments.cpp
@@ -0,0 +1,229 @@
+// Run lines are sensitive to line numbers and come below the code.
+
+#ifndef HEADER
+#define HEADER
+
+// Not a Doxygen comment.  NOT_DOXYGEN
+void notdoxy1(void);
+
+/* Not a Doxygen comment.  NOT_DOXYGEN */
+void notdoxy2(void);
+
+/*/ Not a Doxygen comment.  NOT_DOXYGEN */
+void notdoxy3(void);
+
+/** Doxygen comment.  isdoxy4 IS_DOXYGEN_SINGLE */
+void isdoxy4(void);
+
+/**
+ * Doxygen comment.  isdoxy5 IS_DOXYGEN_SINGLE */
+void isdoxy5(void);
+
+/**
+ * Doxygen comment.
+ * isdoxy6 IS_DOXYGEN_SINGLE */
+void isdoxy6(void);
+
+/**
+ * Doxygen comment.
+ * isdoxy7 IS_DOXYGEN_SINGLE
+ */
+void isdoxy7(void);
+
+/*! Doxygen comment.  isdoxy8 IS_DOXYGEN_SINGLE */
+void isdoxy8(void);
+
+/// Doxygen comment.  isdoxy9 IS_DOXYGEN_SINGLE
+void isdoxy9(void);
+
+// Not a Doxygen comment.  NOT_DOXYGEN
+/// Doxygen comment.  isdoxy10 IS_DOXYGEN_SINGLE
+void isdoxy10(void);
+
+/// Doxygen comment.  isdoxy11 IS_DOXYGEN_SINGLE
+// Not a Doxygen comment.  NOT_DOXYGEN
+void isdoxy11(void);
+
+/** Doxygen comment.  isdoxy12  IS_DOXYGEN_SINGLE */
+/* Not a Doxygen comment.  NOT_DOXYGEN */
+void isdoxy12(void);
+
+/// Doxygen comment.  isdoxy13 IS_DOXYGEN_START
+/// Doxygen comment.  IS_DOXYGEN_END
+void isdoxy13(void);
+
+/// Doxygen comment.  isdoxy14 IS_DOXYGEN_START
+/// Blah-blah-blah.
+/// Doxygen comment.  IS_DOXYGEN_END
+void isdoxy14(void);
+
+/// Doxygen comment.  isdoxy15 IS_DOXYGEN_START
+/** Blah-blah-blah */
+/// Doxygen comment.  IS_DOXYGEN_END
+void isdoxy15(void);
+
+/** Blah-blah-blah. isdoxy16 IS_DOXYGEN_START *//** Blah */
+/// Doxygen comment.  IS_DOXYGEN_END
+void isdoxy16(void);
+
+/// isdoxy17 IS_DOXYGEN_START
+// Not a Doxygen comment, but still picked up.
+/// IS_DOXYGEN_END
+void isdoxy17(void);
+
+unsigned
+// NOT_DOXYGEN
+/// isdoxy18 IS_DOXYGEN_START
+// Not a Doxygen comment, but still picked up.
+/// IS_DOXYGEN_END
+// NOT_DOXYGEN
+int isdoxy18(void);
+
+//! It all starts here. isdoxy19 IS_DOXYGEN_START
+/*! It's a little odd to continue line this,
+ *
+ * but we need more multi-line comments. */
+/// This comment comes before my other comments
+/** This is a block comment that is associated with the function f. It
+ *  runs for three lines.  IS_DOXYGEN_END
+ */
+void isdoxy19(int, int);
+
+// NOT IN THE COMMENT  NOT_DOXYGEN
+/// This is a BCPL comment.  isdoxy20 IS_DOXYGEN_START
+/// It has only two lines.
+/** But there are other blocks that are part of the comment, too.  IS_DOXYGEN_END */
+void isdoxy20(int);
+
+void isdoxy21(int); ///< This is a member comment.  isdoxy21 IS_DOXYGEN_SINGLE
+
+void isdoxy22(int); /*!< This is a member comment.  isdoxy22 IS_DOXYGEN_SINGLE */
+
+void isdoxy23(int); /**< This is a member comment.  isdoxy23 IS_DOXYGEN_SINGLE */
+
+void notdoxy24(int); // NOT_DOXYGEN
+
+/// IS_DOXYGEN_SINGLE
+struct isdoxy25 {
+};
+
+struct test26 {
+  /// IS_DOXYGEN_SINGLE
+  int isdoxy26;
+};
+
+struct test27 {
+  int isdoxy27; ///< IS_DOXYGEN_SINGLE
+};
+
+struct notdoxy28 {
+}; ///< IS_DOXYGEN_NOT_ATTACHED
+
+/// IS_DOXYGEN_SINGLE
+enum isdoxy29 {
+};
+
+enum notdoxy30 {
+}; ///< IS_DOXYGEN_NOT_ATTACHED
+
+/// IS_DOXYGEN_SINGLE
+namespace isdoxy31 {
+};
+
+namespace notdoxy32 {
+}; ///< IS_DOXYGEN_NOT_ATTACHED
+
+class test33 {
+                ///< IS_DOXYGEN_NOT_ATTACHED
+  int isdoxy33; ///< isdoxy33 IS_DOXYGEN_SINGLE
+  int isdoxy34; ///< isdoxy34 IS_DOXYGEN_SINGLE
+
+                ///< IS_DOXYGEN_NOT_ATTACHED
+  int isdoxy35, ///< isdoxy35 IS_DOXYGEN_SINGLE
+      isdoxy36; ///< isdoxy36 IS_DOXYGEN_SINGLE
+
+                ///< IS_DOXYGEN_NOT_ATTACHED
+  int isdoxy37  ///< isdoxy37 IS_DOXYGEN_SINGLE
+    , isdoxy38  ///< isdoxy38 IS_DOXYGEN_SINGLE
+    , isdoxy39; ///< isdoxy39 IS_DOXYGEN_SINGLE
+};
+
+// Verified that Doxygen attaches these.
+
+/// isdoxy40 IS_DOXYGEN_SINGLE
+// NOT_DOXYGEN
+void isdoxy40(int);
+
+unsigned
+/// isdoxy41 IS_DOXYGEN_SINGLE
+// NOT_DOXYGEN
+int isdoxy41(int);
+
+class test42 {
+  int isdoxy42; /* NOT_DOXYGEN */ ///< isdoxy42 IS_DOXYGEN_SINGLE
+};
+
+#endif
+
+// RUN: rm -rf %t
+// RUN: mkdir %t
+// RUN: %clang_cc1 -x c++ -emit-pch -o %t/out.pch %s
+// RUN: %clang_cc1 -x c++ -include-pch %t/out.pch -fsyntax-only %s
+
+// RUN: c-index-test -test-load-source all %s > %t/out.c-index-direct
+// RUN: c-index-test -test-load-tu %t/out.pch all > %t/out.c-index-pch
+
+// RUN: FileCheck %s -check-prefix=WRONG < %t/out.c-index-direct
+// RUN: FileCheck %s -check-prefix=WRONG < %t/out.c-index-pch
+
+// Declarations without Doxygen comments should not pick up some Doxygen comments.
+// WRONG-NOT: notdoxy{{.*}}Comment=
+// WRONG-NOT: test{{.*}}Comment=
+
+// Non-Doxygen comments should not be attached to anything.
+// WRONG-NOT: NOT_DOXYGEN
+
+// Some Doxygen comments are not attached to anything.
+// WRONG-NOT: IS_DOXYGEN_NOT_ATTACHED
+
+// Ensure we don't pick up extra comments.
+// WRONG-NOT: IS_DOXYGEN_START{{.*}}IS_DOXYGEN_START
+// WRONG-NOT: IS_DOXYGEN_END{{.*}}IS_DOXYGEN_END
+
+// RUN: FileCheck %s < %t/out.c-index-direct
+// RUN: FileCheck %s < %t/out.c-index-pch
+
+// CHECK: annotate-comments.cpp:16:6: FunctionDecl=isdoxy4:{{.*}} isdoxy4 IS_DOXYGEN_SINGLE
+// CHECK: annotate-comments.cpp:20:6: FunctionDecl=isdoxy5:{{.*}} isdoxy5 IS_DOXYGEN_SINGLE
+// CHECK: annotate-comments.cpp:25:6: FunctionDecl=isdoxy6:{{.*}} isdoxy6 IS_DOXYGEN_SINGLE
+// CHECK: annotate-comments.cpp:31:6: FunctionDecl=isdoxy7:{{.*}} isdoxy7 IS_DOXYGEN_SINGLE
+// CHECK: annotate-comments.cpp:34:6: FunctionDecl=isdoxy8:{{.*}} isdoxy8 IS_DOXYGEN_SINGLE
+// CHECK: annotate-comments.cpp:37:6: FunctionDecl=isdoxy9:{{.*}} isdoxy9 IS_DOXYGEN_SINGLE
+// CHECK: annotate-comments.cpp:41:6: FunctionDecl=isdoxy10:{{.*}} isdoxy10 IS_DOXYGEN_SINGLE
+// CHECK: annotate-comments.cpp:53:6: FunctionDecl=isdoxy13:{{.*}} isdoxy13 IS_DOXYGEN_START{{.*}} IS_DOXYGEN_END
+// CHECK: annotate-comments.cpp:58:6: FunctionDecl=isdoxy14:{{.*}} isdoxy14 IS_DOXYGEN_START{{.*}} IS_DOXYGEN_END
+// CHECK: annotate-comments.cpp:63:6: FunctionDecl=isdoxy15:{{.*}} isdoxy15 IS_DOXYGEN_START{{.*}} IS_DOXYGEN_END
+// CHECK: annotate-comments.cpp:67:6: FunctionDecl=isdoxy16:{{.*}} isdoxy16 IS_DOXYGEN_START{{.*}} IS_DOXYGEN_END
+// CHECK: annotate-comments.cpp:72:6: FunctionDecl=isdoxy17:{{.*}} isdoxy17 IS_DOXYGEN_START{{.*}} IS_DOXYGEN_END
+// CHECK: annotate-comments.cpp:80:5: FunctionDecl=isdoxy18:{{.*}} isdoxy18 IS_DOXYGEN_START{{.*}} IS_DOXYGEN_END
+// CHECK: annotate-comments.cpp:90:6: FunctionDecl=isdoxy19:{{.*}} isdoxy19 IS_DOXYGEN_START{{.*}} IS_DOXYGEN_END
+// CHECK: annotate-comments.cpp:96:6: FunctionDecl=isdoxy20:{{.*}} isdoxy20 IS_DOXYGEN_START{{.*}} IS_DOXYGEN_END
+// CHECK: annotate-comments.cpp:98:6: FunctionDecl=isdoxy21:{{.*}} isdoxy21 IS_DOXYGEN_SINGLE
+// CHECK: annotate-comments.cpp:100:6: FunctionDecl=isdoxy22:{{.*}} isdoxy22 IS_DOXYGEN_SINGLE
+// CHECK: annotate-comments.cpp:102:6: FunctionDecl=isdoxy23:{{.*}} isdoxy23 IS_DOXYGEN_SINGLE
+// CHECK: annotate-comments.cpp:107:8: StructDecl=isdoxy25:{{.*}} IS_DOXYGEN_SINGLE
+// CHECK: annotate-comments.cpp:112:7: FieldDecl=isdoxy26:{{.*}} IS_DOXYGEN_SINGLE
+// CHECK: annotate-comments.cpp:116:7: FieldDecl=isdoxy27:{{.*}} IS_DOXYGEN_SINGLE
+// CHECK: annotate-comments.cpp:123:6: EnumDecl=isdoxy29:{{.*}} IS_DOXYGEN_SINGLE
+// CHECK: annotate-comments.cpp:130:11: Namespace=isdoxy31:{{.*}} IS_DOXYGEN_SINGLE
+// CHECK: annotate-comments.cpp:138:7: FieldDecl=isdoxy33:{{.*}} isdoxy33 IS_DOXYGEN_SINGLE
+// CHECK: annotate-comments.cpp:139:7: FieldDecl=isdoxy34:{{.*}} isdoxy34 IS_DOXYGEN_SINGLE
+// CHECK: annotate-comments.cpp:142:7: FieldDecl=isdoxy35:{{.*}} isdoxy35 IS_DOXYGEN_SINGLE
+// CHECK: annotate-comments.cpp:143:7: FieldDecl=isdoxy36:{{.*}} isdoxy36 IS_DOXYGEN_SINGLE
+// CHECK: annotate-comments.cpp:146:7: FieldDecl=isdoxy37:{{.*}} isdoxy37 IS_DOXYGEN_SINGLE
+// CHECK: annotate-comments.cpp:147:7: FieldDecl=isdoxy38:{{.*}} isdoxy38 IS_DOXYGEN_SINGLE
+// CHECK: annotate-comments.cpp:148:7: FieldDecl=isdoxy39:{{.*}} isdoxy39 IS_DOXYGEN_SINGLE
+// CHECK: annotate-comments.cpp:155:6: FunctionDecl=isdoxy40:{{.*}} isdoxy40 IS_DOXYGEN_SINGLE
+// CHECK: annotate-comments.cpp:160:5: FunctionDecl=isdoxy41:{{.*}} isdoxy41 IS_DOXYGEN_SINGLE
+// CHECK: annotate-comments.cpp:163:7: FieldDecl=isdoxy42:{{.*}} isdoxy42 IS_DOXYGEN_SINGLE
+
diff --git a/tools/arcmt-test/CMakeLists.txt b/tools/arcmt-test/CMakeLists.txt
index a0029b416f292b5f39519e47894a8ed7a066f811..7950ee2a46261408948e2402d1baa9901e802ee3 100644
--- a/tools/arcmt-test/CMakeLists.txt
+++ b/tools/arcmt-test/CMakeLists.txt
@@ -1,5 +1,6 @@
 set(LLVM_USED_LIBS
   clangARCMigrate
+  clangComments
   clangEdit
   clangRewrite
   )
diff --git a/tools/arcmt-test/Makefile b/tools/arcmt-test/Makefile
index 57cd57482bf75d9c31ee0bc8933b7d2135b3f5dd..81f27c2b11df408b4e78c90d8d90d67142a56085 100644
--- a/tools/arcmt-test/Makefile
+++ b/tools/arcmt-test/Makefile
@@ -19,6 +19,7 @@ NO_INSTALL = 1
 LINK_COMPONENTS := support mc
 USEDLIBS = clangARCMigrate.a clangRewrite.a \
 		 clangFrontend.a clangDriver.a clangSerialization.a clangParse.a \
-		 clangSema.a clangEdit.a clangAnalysis.a clangAST.a clangLex.a clangBasic.a
+		 clangSema.a clangEdit.a clangAnalysis.a clangAST.a clangLex.a clangComments.a \
+		 clangBasic.a
 
 include $(CLANG_LEVEL)/Makefile
diff --git a/tools/c-index-test/Makefile b/tools/c-index-test/Makefile
index 03519b3823c0c541deb342065f7c79f0f9ad1d4c..b061492cafe9b3ab517095ab9970a638dc7ad1bd 100644
--- a/tools/c-index-test/Makefile
+++ b/tools/c-index-test/Makefile
@@ -20,6 +20,7 @@ TOOL_NO_EXPORTS = 1
 LINK_COMPONENTS := support mc
 USEDLIBS = clang.a clangFrontend.a clangDriver.a \
 	   clangSerialization.a clangParse.a clangSema.a \
-	   clangAnalysis.a clangEdit.a clangAST.a clangLex.a clangBasic.a
+	   clangAnalysis.a clangEdit.a clangAST.a clangLex.a clangComments.a \
+	   clangBasic.a
 
 include $(CLANG_LEVEL)/Makefile
diff --git a/tools/c-index-test/c-index-test.c b/tools/c-index-test/c-index-test.c
index 497c9ee6af53c259e451ff2490b6d9ef9f363fec..4c9723da019ea2a536ad03779cdb4e1785b76458 100644
--- a/tools/c-index-test/c-index-test.c
+++ b/tools/c-index-test/c-index-test.c
@@ -218,7 +218,9 @@ static void PrintCursor(CXCursor Cursor) {
     CXPlatformAvailability PlatformAvailability[2];
     int NumPlatformAvailability;
     int I;
-    
+    CXString Comment;
+    const char *CommentCString;
+
     ks = clang_getCursorKindSpelling(Cursor.kind);
     string = want_display_name? clang_getCursorDisplayName(Cursor) 
                               : clang_getCursorSpelling(Cursor);
@@ -398,6 +400,22 @@ static void PrintCursor(CXCursor Cursor) {
       if (!clang_equalRanges(CursorExtent, RefNameRange))
         PrintRange(RefNameRange, "RefName");
     }
+
+    Comment = clang_Cursor_getRawCommentText(Cursor);
+    CommentCString = clang_getCString(Comment);
+    if (CommentCString != NULL && CommentCString[0] != '\0') {
+      printf(" Comment=[");
+      for ( ; *CommentCString; ++CommentCString) {
+        if (*CommentCString != '\n')
+          putchar(*CommentCString);
+        else
+          printf("\\n");
+      }
+      printf("]");
+
+      PrintRange(clang_Cursor_getCommentRange(Cursor), "CommentRange");
+    }
+    clang_disposeString(Comment);
   }
 }
 
diff --git a/tools/clang-check/CMakeLists.txt b/tools/clang-check/CMakeLists.txt
index 851d6cdd1615c62fe13bb2467aa055af95aaa1f5..83fe10f80ee4b97887a20dfbf20c6471c8e22440 100644
--- a/tools/clang-check/CMakeLists.txt
+++ b/tools/clang-check/CMakeLists.txt
@@ -1,4 +1,4 @@
-set(LLVM_USED_LIBS clangTooling clangBasic)
+set(LLVM_USED_LIBS clangTooling clangBasic clangComments)
 
 add_clang_executable(clang-check
   ClangCheck.cpp
diff --git a/tools/clang-check/Makefile b/tools/clang-check/Makefile
index 49b1ada95b93819f884f9bffcf26aa6ce9c69bfe..5c54a6b33214418f74f05733863db5c3f1ca5140 100644
--- a/tools/clang-check/Makefile
+++ b/tools/clang-check/Makefile
@@ -18,7 +18,7 @@ TOOL_NO_EXPORTS = 1
 LINK_COMPONENTS := support mc
 USEDLIBS = clangFrontend.a clangSerialization.a clangDriver.a \
            clangTooling.a clangParse.a clangSema.a clangAnalysis.a \
-           clangEdit.a clangAST.a clangLex.a clangBasic.a
+           clangEdit.a clangAST.a clangLex.a clangComments.a clangBasic.a
 
 include $(CLANG_LEVEL)/Makefile
 
diff --git a/tools/diagtool/CMakeLists.txt b/tools/diagtool/CMakeLists.txt
index d2ad73533735bb44094186e3dc916a96d5d96af0..05b896975c925ba2e1df36b5a6972630c9ecee24 100644
--- a/tools/diagtool/CMakeLists.txt
+++ b/tools/diagtool/CMakeLists.txt
@@ -4,6 +4,7 @@ set( LLVM_LINK_COMPONENTS
 
 set( LLVM_USED_LIBS
   clangBasic
+  clangComments
   clangLex
   clangSema
   clangFrontend
diff --git a/tools/diagtool/Makefile b/tools/diagtool/Makefile
index 8af4cef6433cecb9c150e8088c040ebb50ebc02e..5f5fb2ae365ae9a8989a941a39570bd0385643ed 100644
--- a/tools/diagtool/Makefile
+++ b/tools/diagtool/Makefile
@@ -20,7 +20,7 @@ LINK_COMPONENTS := support mc
 
 USEDLIBS = clangFrontend.a clangDriver.a clangSerialization.a clangParse.a \
            clangSema.a clangAnalysis.a clangEdit.a clangAST.a clangLex.a \
-           clangBasic.a
+           clangComments.a clangBasic.a
 
 include $(CLANG_LEVEL)/Makefile
 
diff --git a/tools/driver/CMakeLists.txt b/tools/driver/CMakeLists.txt
index ae49ac1eeb6814d16871e3e0bd1346243ae66922..5413546e28b7a090a7e02c07f6e95cdf7eb56efe 100644
--- a/tools/driver/CMakeLists.txt
+++ b/tools/driver/CMakeLists.txt
@@ -4,6 +4,7 @@ set( LLVM_USED_LIBS
   clangAnalysis
   clangBasic
   clangCodeGen
+  clangComments
   clangDriver
   clangEdit
   clangFrontend
diff --git a/tools/driver/Makefile b/tools/driver/Makefile
index 270d4fdda863b0bd54135bf879e18519fb87af91..4105cb6d94f10f6459b047c4c36c20bcd591b50e 100644
--- a/tools/driver/Makefile
+++ b/tools/driver/Makefile
@@ -36,7 +36,7 @@ USEDLIBS = clangFrontendTool.a clangFrontend.a clangDriver.a \
            clangStaticAnalyzerFrontend.a clangStaticAnalyzerCheckers.a \
            clangStaticAnalyzerCore.a \
            clangAnalysis.a clangARCMigrate.a clangRewrite.a \
-           clangEdit.a clangAST.a clangLex.a clangBasic.a
+           clangEdit.a clangAST.a clangLex.a clangComments.a clangBasic.a
 
 include $(CLANG_LEVEL)/Makefile
 
diff --git a/tools/libclang/CIndex.cpp b/tools/libclang/CIndex.cpp
index 7fb944ddd3ab20e983a2f9fb7655542077a6b3fd..df8adb419e19b6ccf0664ecdeaf2591cda2a725c 100644
--- a/tools/libclang/CIndex.cpp
+++ b/tools/libclang/CIndex.cpp
@@ -5676,7 +5676,35 @@ CXFile clang_getIncludedFile(CXCursor cursor) {
   InclusionDirective *ID = getCursorInclusionDirective(cursor);
   return (void *)ID->getFile();
 }
-  
+
+CXSourceRange clang_Cursor_getCommentRange(CXCursor C) {
+  if (!clang_isDeclaration(C.kind))
+    return clang_getNullRange();
+
+  const Decl *D = getCursorDecl(C);
+  ASTContext &Context = getCursorContext(C);
+  const RawComment *RC = Context.getRawCommentForDecl(D);
+  if (!RC)
+    return clang_getNullRange();
+
+  return cxloc::translateSourceRange(Context, RC->getSourceRange());
+}
+
+CXString clang_Cursor_getRawCommentText(CXCursor C) {
+  if (!clang_isDeclaration(C.kind))
+    return createCXString((const char *) NULL);
+
+  const Decl *D = getCursorDecl(C);
+  ASTContext &Context = getCursorContext(C);
+  const RawComment *RC = Context.getRawCommentForDecl(D);
+  StringRef RawText = RC ? RC->getRawText(Context.getSourceManager()) :
+                           StringRef();
+
+  // Don't duplicate the string because RawText points directly into source
+  // code.
+  return createCXString(RawText, false);
+}
+
 } // end: extern "C"
 
 
diff --git a/tools/libclang/CMakeLists.txt b/tools/libclang/CMakeLists.txt
index 293dfa3ff94d675973b0c67111aabfb9d9a55eb9..7a6732a77a53a52f147d350a762113bf222bab62 100644
--- a/tools/libclang/CMakeLists.txt
+++ b/tools/libclang/CMakeLists.txt
@@ -6,6 +6,7 @@ set(LLVM_USED_LIBS
   clangSerialization
   clangSema
   clangEdit
+  clangComments
   clangAST
   clangLex
   clangBasic)
diff --git a/tools/libclang/Makefile b/tools/libclang/Makefile
index 8d0a614403added8c852cdcf8e3d60fc502f920e..78da0675dc36c93185e6b39c77b58ce0e4d1b447 100644
--- a/tools/libclang/Makefile
+++ b/tools/libclang/Makefile
@@ -19,7 +19,7 @@ LINK_COMPONENTS := support mc
 USEDLIBS = clangARCMigrate.a clangRewrite.a clangFrontend.a clangDriver.a \
      clangSerialization.a \
 		 clangParse.a clangSema.a clangEdit.a clangAnalysis.a \
-		 clangAST.a clangLex.a clangBasic.a
+		 clangAST.a clangLex.a clangComments.a clangBasic.a
 
 include $(CLANG_LEVEL)/Makefile
 
diff --git a/tools/libclang/libclang.exports b/tools/libclang/libclang.exports
index 594a7969e3975eb142748482134953f60ef97d23..d24960b297224c7e266d964d13dc1c1803b2343c 100644
--- a/tools/libclang/libclang.exports
+++ b/tools/libclang/libclang.exports
@@ -5,6 +5,8 @@ clang_CXIndex_setGlobalOptions
 clang_CXXMethod_isStatic
 clang_CXXMethod_isVirtual
 clang_Cursor_getArgument
+clang_Cursor_getCommentRange
+clang_Cursor_getRawCommentText
 clang_Cursor_getNumArguments
 clang_Cursor_getObjCSelectorIndex
 clang_Cursor_getSpellingNameRange
diff --git a/unittests/Frontend/Makefile b/unittests/Frontend/Makefile
index f3e639665849185d7a7e5715736c156086182d6b..357e24234fc3e05e586eca5805a2eac455380e01 100644
--- a/unittests/Frontend/Makefile
+++ b/unittests/Frontend/Makefile
@@ -14,6 +14,6 @@ USEDLIBS = clangFrontendTool.a clangFrontend.a clangDriver.a \
            clangSerialization.a clangCodeGen.a clangParse.a clangSema.a \
            clangStaticAnalyzerCheckers.a clangStaticAnalyzerCore.a \
            clangARCMigrate.a clangRewrite.a clangEdit.a \
-           clangAnalysis.a clangAST.a clangLex.a clangBasic.a
+           clangAnalysis.a clangAST.a clangLex.a clangComments.a clangBasic.a
 
 include $(CLANG_LEVEL)/unittests/Makefile
diff --git a/unittests/Tooling/Makefile b/unittests/Tooling/Makefile
index 5d6747dce803b883cee87fffb0a7755c6d1f73dd..84e05bb0e469127121d183f9b9646c155551db55 100644
--- a/unittests/Tooling/Makefile
+++ b/unittests/Tooling/Makefile
@@ -12,6 +12,6 @@ TESTNAME = Tooling
 LINK_COMPONENTS := support mc
 USEDLIBS = clangTooling.a clangFrontend.a clangSerialization.a clangDriver.a \
            clangParse.a clangRewrite.a clangSema.a clangAnalysis.a clangEdit.a \
-           clangAST.a clangLex.a clangBasic.a
+           clangAST.a clangLex.a clangComments.a clangBasic.a
 
 include $(CLANG_LEVEL)/unittests/Makefile