aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--include/clang-c/Index.h13
-rw-r--r--include/clang/AST/ASTContext.h26
-rw-r--r--include/clang/AST/ExternalASTSource.h3
-rw-r--r--include/clang/Basic/SourceManager.h42
-rw-r--r--include/clang/Comments/RawCommentList.h172
-rw-r--r--include/clang/Lex/Preprocessor.h4
-rw-r--r--include/clang/Parse/Parser.h1
-rw-r--r--include/clang/Sema/Sema.h2
-rw-r--r--include/clang/Serialization/ASTBitCodes.h12
-rw-r--r--include/clang/Serialization/ASTReader.h7
-rw-r--r--include/clang/Serialization/ASTWriter.h1
-rw-r--r--lib/ARCMigrate/TransEmptyStatementsAndDealloc.cpp2
-rw-r--r--lib/AST/ASTContext.cpp102
-rw-r--r--lib/CMakeLists.txt1
-rw-r--r--lib/Comments/CMakeLists.txt7
-rw-r--r--lib/Comments/Makefile14
-rw-r--r--lib/Comments/RawCommentList.cpp207
-rw-r--r--lib/Lex/Preprocessor.cpp4
-rwxr-xr-xlib/Makefile2
-rw-r--r--lib/Parse/Parser.cpp22
-rw-r--r--lib/Sema/Sema.cpp5
-rw-r--r--lib/Sema/SemaType.cpp2
-rw-r--r--lib/Serialization/ASTReader.cpp66
-rw-r--r--lib/Serialization/ASTWriter.cpp18
-rw-r--r--test/Index/annotate-comments.cpp229
-rw-r--r--tools/arcmt-test/CMakeLists.txt1
-rw-r--r--tools/arcmt-test/Makefile3
-rw-r--r--tools/c-index-test/Makefile3
-rw-r--r--tools/c-index-test/c-index-test.c20
-rw-r--r--tools/clang-check/CMakeLists.txt2
-rw-r--r--tools/clang-check/Makefile2
-rw-r--r--tools/diagtool/CMakeLists.txt1
-rw-r--r--tools/diagtool/Makefile2
-rw-r--r--tools/driver/CMakeLists.txt1
-rw-r--r--tools/driver/Makefile2
-rw-r--r--tools/libclang/CIndex.cpp30
-rw-r--r--tools/libclang/CMakeLists.txt1
-rw-r--r--tools/libclang/Makefile2
-rw-r--r--tools/libclang/libclang.exports2
-rw-r--r--unittests/Frontend/Makefile2
-rw-r--r--unittests/Tooling/Makefile2
41 files changed, 1005 insertions, 35 deletions
diff --git a/include/clang-c/Index.h b/include/clang-c/Index.h
index c9742e2483..b7bd8bb738 100644
--- a/include/clang-c/Index.h
+++ b/include/clang-c/Index.h
@@ -3188,6 +3188,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 6ceed17eb8..172ac88edf 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 e2a60d5cf0..7aedfe2ef6 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 603bfebfd3..19fa52ee1d 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 0000000000..bf0b616383
--- /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 7d3d6e1080..3b94a5725e 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 cf308327f3..b451ed97e9 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 408763f5cf..4be823a8db 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 42d0ad2467..2049da9730 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 21fd674b2a..d084254ead 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 16c3766f8f..472b407992 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 0f6c799374..d8fabcd948 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 9ad3a7f2e7..d8677c2ee1 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 dfb9d61ff5..228b43b235 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 0000000000..f9561c649c
--- /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 0000000000..0783f1f26c
--- /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 0000000000..7db9175c17
--- /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 5509f5f4e8..70be2cf7a6 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 2eb72a91e6..0e81af19ce 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 0d7d047e00..10fb8eb8e9 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 6323589bd8..9e4b291971 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 d6c8d92ce8..4bd9b14bf5 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 eb74566011..f5aa74ea98 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 de5816df9e..1f96180507 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 0000000000..634f481139
--- /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 a0029b416f..7950ee2a46 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 57cd57482b..81f27c2b11 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 03519b3823..b061492caf 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 497c9ee6af..4c9723da01 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 851d6cdd16..83fe10f80e 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 49b1ada95b..5c54a6b332 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 d2ad735337..05b896975c 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 8af4cef643..5f5fb2ae36 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 ae49ac1eeb..5413546e28 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 270d4fdda8..4105cb6d94 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 7fb944ddd3..df8adb419e 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 293dfa3ff9..7a6732a77a 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 8d0a614403..78da0675dc 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 594a7969e3..d24960b297 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 f3e6396658..357e24234f 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 5d6747dce8..84e05bb0e4 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