aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--include/clang/AST/ASTContext.h16
-rw-r--r--include/clang/AST/ExternalASTSource.h9
-rw-r--r--include/clang/Frontend/PCHBitCodes.h6
-rw-r--r--include/clang/Frontend/PCHReader.h14
-rw-r--r--include/clang/Frontend/PCHWriter.h1
-rw-r--r--include/clang/Lex/Preprocessor.h24
-rw-r--r--include/clang/Parse/Action.h4
-rw-r--r--include/clang/Parse/Parser.h3
-rw-r--r--lib/AST/ASTContext.cpp204
-rw-r--r--lib/Frontend/PCHReader.cpp14
-rw-r--r--lib/Frontend/PCHWriter.cpp24
-rw-r--r--lib/Lex/Lexer.cpp9
-rw-r--r--lib/Lex/Pragma.cpp1
-rw-r--r--lib/Lex/Preprocessor.cpp23
-rw-r--r--lib/Parse/Parser.cpp17
-rw-r--r--lib/Sema/Sema.cpp4
-rw-r--r--lib/Sema/Sema.h2
-rw-r--r--test/CMakeLists.txt3
-rw-r--r--test/Index/comments.c30
-rw-r--r--tools/index-test/index-test.cpp3
20 files changed, 402 insertions, 9 deletions
diff --git a/include/clang/AST/ASTContext.h b/include/clang/AST/ASTContext.h
index 450b28aefb..041a0f33ad 100644
--- a/include/clang/AST/ASTContext.h
+++ b/include/clang/AST/ASTContext.h
@@ -141,10 +141,19 @@ class ASTContext {
/// this ASTContext object.
LangOptions LangOpts;
+ /// \brief Whether we have already loaded comment source ranges from an
+ /// external source.
+ bool LoadedExternalComments;
+
/// MallocAlloc/BumpAlloc - The allocator objects used to create AST objects.
bool FreeMemory;
llvm::MallocAllocator MallocAlloc;
llvm::BumpPtrAllocator BumpAlloc;
+
+ /// \brief Mapping from declarations to their comments, once we have
+ /// already looked up the comment associated with a given declaration.
+ llvm::DenseMap<const Decl *, std::string> DeclComments;
+
public:
TargetInfo &Target;
IdentifierTable &Idents;
@@ -154,6 +163,10 @@ public:
llvm::OwningPtr<ExternalASTSource> ExternalSource;
clang::PrintingPolicy PrintingPolicy;
+ /// \brief Source ranges for all of the comments in the source file,
+ /// sorted in order of appearance in the translation unit.
+ std::vector<SourceRange> Comments;
+
SourceManager& getSourceManager() { return SourceMgr; }
const SourceManager& getSourceManager() const { return SourceMgr; }
void *Allocate(unsigned Size, unsigned Align = 8) {
@@ -178,7 +191,8 @@ public:
TranslationUnitDecl *getTranslationUnitDecl() const { return TUDecl; }
-
+ const char *getCommentForDecl(const Decl *D);
+
// Builtin Types.
QualType VoidTy;
QualType BoolTy;
diff --git a/include/clang/AST/ExternalASTSource.h b/include/clang/AST/ExternalASTSource.h
index 4219bd507a..6f862a55d4 100644
--- a/include/clang/AST/ExternalASTSource.h
+++ b/include/clang/AST/ExternalASTSource.h
@@ -18,6 +18,7 @@
#include "clang/AST/Type.h"
#include "llvm/ADT/SmallVector.h"
#include <cassert>
+#include <vector>
namespace clang {
class ASTConsumer;
@@ -57,6 +58,14 @@ public:
virtual ~ExternalASTSource();
+ /// \brief Reads the source ranges that correspond to comments from
+ /// an external AST source.
+ ///
+ /// \param Comments the contents of this vector will be
+ /// replaced with the sorted set of source ranges corresponding to
+ /// comments in the source code.
+ virtual void ReadComments(std::vector<SourceRange> &Comments) = 0;
+
/// \brief Resolve a type ID into a type, potentially building a new
/// type.
virtual QualType GetType(uint32_t ID) = 0;
diff --git a/include/clang/Frontend/PCHBitCodes.h b/include/clang/Frontend/PCHBitCodes.h
index 63222725d5..80aa248a78 100644
--- a/include/clang/Frontend/PCHBitCodes.h
+++ b/include/clang/Frontend/PCHBitCodes.h
@@ -218,7 +218,11 @@ namespace clang {
/// \brief Record code for the original file that was used to
/// generate the precompiled header.
- ORIGINAL_FILE_NAME = 20
+ ORIGINAL_FILE_NAME = 20,
+
+ /// \brief Record code for the sorted array of source ranges where
+ /// comments were encountered in the source code.
+ COMMENT_RANGES = 21
};
/// \brief Record types used within a source manager block.
diff --git a/include/clang/Frontend/PCHReader.h b/include/clang/Frontend/PCHReader.h
index b3ed364343..8291f4697a 100644
--- a/include/clang/Frontend/PCHReader.h
+++ b/include/clang/Frontend/PCHReader.h
@@ -279,6 +279,12 @@ private:
/// been loaded.
llvm::SmallVector<Selector, 16> SelectorsLoaded;
+ /// \brief A sorted array of source ranges containing comments.
+ SourceRange *Comments;
+
+ /// \brief The number of source ranges in the Comments array.
+ unsigned NumComments;
+
/// \brief The set of external definitions stored in the the PCH
/// file.
llvm::SmallVector<uint64_t, 16> ExternalDefinitions;
@@ -452,6 +458,14 @@ public:
/// build prior to including the precompiled header.
const std::string &getSuggestedPredefines() { return SuggestedPredefines; }
+ /// \brief Reads the source ranges that correspond to comments from
+ /// an external AST source.
+ ///
+ /// \param Comments the contents of this vector will be
+ /// replaced with the sorted set of source ranges corresponding to
+ /// comments in the source code.
+ virtual void ReadComments(std::vector<SourceRange> &Comments);
+
/// \brief Resolve a type ID into a type, potentially building a new
/// type.
virtual QualType GetType(pch::TypeID ID);
diff --git a/include/clang/Frontend/PCHWriter.h b/include/clang/Frontend/PCHWriter.h
index 5cb939547f..c663442e64 100644
--- a/include/clang/Frontend/PCHWriter.h
+++ b/include/clang/Frontend/PCHWriter.h
@@ -166,6 +166,7 @@ private:
void WriteSourceManagerBlock(SourceManager &SourceMgr,
const Preprocessor &PP);
void WritePreprocessor(const Preprocessor &PP);
+ void WriteComments(ASTContext &Context);
void WriteType(const Type *T);
void WriteTypesBlock(ASTContext &Context);
uint64_t WriteDeclContextLexicalBlock(ASTContext &Context, DeclContext *DC);
diff --git a/include/clang/Lex/Preprocessor.h b/include/clang/Lex/Preprocessor.h
index f229881bfc..6d5ed72455 100644
--- a/include/clang/Lex/Preprocessor.h
+++ b/include/clang/Lex/Preprocessor.h
@@ -26,6 +26,7 @@
#include "llvm/ADT/DenseMap.h"
#include "llvm/ADT/OwningPtr.h"
#include "llvm/Support/Allocator.h"
+#include <vector>
namespace clang {
@@ -35,6 +36,7 @@ class FileEntry;
class HeaderSearch;
class PragmaNamespace;
class PragmaHandler;
+class CommentHandler;
class ScratchBuffer;
class TargetInfo;
class PPCallbacks;
@@ -109,6 +111,10 @@ class Preprocessor {
/// with this preprocessor.
PragmaNamespace *PragmaHandlers;
+ /// \brief Tracks all of the comment handlers that the client registered
+ /// with this preprocessor.
+ std::vector<CommentHandler *> CommentHandlers;
+
/// CurLexer - This is the current top of the stack that we're lexing from if
/// not expanding a macro and we are lexing directly from source code.
/// Only one of CurLexer, CurPTHLexer, or CurTokenLexer will be non-null.
@@ -301,6 +307,14 @@ public:
/// to remove a handler that has not been registered.
void RemovePragmaHandler(const char *Namespace, PragmaHandler *Handler);
+ /// \brief Add the specified comment handler to the preprocessor.
+ 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);
+
/// EnterMainSourceFile - Enter the specified FileID as the main source file,
/// which implicitly adds the builtin defines etc.
void EnterMainSourceFile();
@@ -791,6 +805,7 @@ public:
void HandlePragmaSystemHeader(Token &SysHeaderTok);
void HandlePragmaDependency(Token &DependencyTok);
void HandlePragmaComment(Token &CommentTok);
+ void HandleComment(SourceRange Comment);
};
/// PreprocessorFactory - A generic factory interface for lazily creating
@@ -801,6 +816,15 @@ public:
virtual Preprocessor* CreatePreprocessor() = 0;
};
+/// \brief Abstract base class that describes a handler that will receive
+/// source ranges for each of the comments encountered in the source file.
+class CommentHandler {
+public:
+ virtual ~CommentHandler();
+
+ virtual void HandleComment(Preprocessor &PP, SourceRange Comment) = 0;
+};
+
} // end namespace clang
#endif
diff --git a/include/clang/Parse/Action.h b/include/clang/Parse/Action.h
index 24262a1126..d969562977 100644
--- a/include/clang/Parse/Action.h
+++ b/include/clang/Parse/Action.h
@@ -152,6 +152,10 @@ public:
/// an empty string if not. This is used for pretty crash reporting.
virtual std::string getDeclName(DeclPtrTy D) { return ""; }
+ /// \brief Invoked for each comment in the source code, providing the source
+ /// range that contains the comment.
+ virtual void ActOnComment(SourceRange Comment) { }
+
//===--------------------------------------------------------------------===//
// Declaration Tracking Callbacks.
//===--------------------------------------------------------------------===//
diff --git a/include/clang/Parse/Parser.h b/include/clang/Parse/Parser.h
index e5f62ec683..e2380542ae 100644
--- a/include/clang/Parse/Parser.h
+++ b/include/clang/Parse/Parser.h
@@ -82,7 +82,8 @@ class Parser {
llvm::OwningPtr<PragmaHandler> PackHandler;
llvm::OwningPtr<PragmaHandler> UnusedHandler;
llvm::OwningPtr<PragmaHandler> WeakHandler;
-
+ llvm::OwningPtr<clang::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++
/// template argument list, where the '>' closes the template
diff --git a/lib/AST/ASTContext.cpp b/lib/AST/ASTContext.cpp
index 7f945db1d3..2877cc3b7f 100644
--- a/lib/AST/ASTContext.cpp
+++ b/lib/AST/ASTContext.cpp
@@ -37,7 +37,8 @@ ASTContext::ASTContext(const LangOptions& LOpts, SourceManager &SM,
bool FreeMem, unsigned size_reserve) :
GlobalNestedNameSpecifier(0), CFConstantStringTypeDecl(0),
ObjCFastEnumerationStateTypeDecl(0), SourceMgr(SM), LangOpts(LOpts),
- FreeMemory(FreeMem), Target(t), Idents(idents), Selectors(sels),
+ LoadedExternalComments(false), FreeMemory(FreeMem), Target(t),
+ Idents(idents), Selectors(sels),
BuiltinInfo(builtins), ExternalSource(0), PrintingPolicy(LOpts) {
if (size_reserve > 0) Types.reserve(size_reserve);
InitBuiltinTypes();
@@ -202,6 +203,207 @@ void ASTContext::InitBuiltinTypes() {
InitBuiltinType(NullPtrTy, BuiltinType::NullPtr);
}
+namespace {
+ class BeforeInTranslationUnit
+ : std::binary_function<SourceRange, SourceRange, bool> {
+ SourceManager *SourceMgr;
+
+ public:
+ explicit BeforeInTranslationUnit(SourceManager *SM) : SourceMgr(SM) { }
+
+ bool operator()(SourceRange X, SourceRange Y) {
+ return SourceMgr->isBeforeInTranslationUnit(X.getBegin(), Y.getBegin());
+ }
+ };
+}
+
+/// \brief Determine whether the given comment is a Doxygen-style comment.
+///
+/// \param Start the start of the comment text.
+///
+/// \param End the end of the comment text.
+///
+/// \param Member whether we want to check whether this is a member comment
+/// (which requires a < after the Doxygen-comment delimiter). Otherwise,
+/// we only return true when we find a non-member comment.
+static bool
+isDoxygenComment(SourceManager &SourceMgr, SourceRange Comment,
+ bool Member = false) {
+ const char *BufferStart
+ = SourceMgr.getBufferData(SourceMgr.getFileID(Comment.getBegin())).first;
+ const char *Start = BufferStart + SourceMgr.getFileOffset(Comment.getBegin());
+ const char* End = BufferStart + SourceMgr.getFileOffset(Comment.getEnd());
+
+ if (End - Start < 4)
+ return false;
+
+ assert(Start[0] == '/' && "Not a comment?");
+ if (Start[1] == '*' && !(Start[2] == '!' || Start[2] == '*'))
+ return false;
+ if (Start[1] == '/' && !(Start[2] == '!' || Start[2] == '/'))
+ return false;
+
+ return (Start[3] == '<') == Member;
+}
+
+/// \brief Retrieve the comment associated with the given declaration, if
+/// it has one.
+const char *ASTContext::getCommentForDecl(const Decl *D) {
+ if (!D)
+ return 0;
+
+ // Check whether we have cached a comment string for this declaration
+ // already.
+ llvm::DenseMap<const Decl *, std::string>::iterator Pos
+ = DeclComments.find(D);
+ if (Pos != DeclComments.end())
+ return Pos->second.c_str();
+
+ // If we have an external AST source and have not yet loaded comments from
+ // that source, do so now.
+ if (ExternalSource && !LoadedExternalComments) {
+ std::vector<SourceRange> LoadedComments;
+ ExternalSource->ReadComments(LoadedComments);
+
+ if (!LoadedComments.empty())
+ Comments.insert(Comments.begin(), LoadedComments.begin(),
+ LoadedComments.end());
+
+ LoadedExternalComments = true;
+ }
+
+ // If there are no comments anywhere, we won't find anything.
+ if (Comments.empty())
+ return 0;
+
+ // If the declaration doesn't map directly to a location in a file, we
+ // can't find the comment.
+ SourceLocation DeclStartLoc = D->getLocStart();
+ if (DeclStartLoc.isInvalid() || !DeclStartLoc.isFileID())
+ return 0;
+
+ // Find the comment that occurs just before this declaration.
+ std::vector<SourceRange>::iterator LastComment
+ = std::lower_bound(Comments.begin(), Comments.end(),
+ SourceRange(DeclStartLoc),
+ BeforeInTranslationUnit(&SourceMgr));
+
+ // Decompose the location for the start of the declaration and find the
+ // beginning of the file buffer.
+ std::pair<FileID, unsigned> DeclStartDecomp
+ = SourceMgr.getDecomposedLoc(DeclStartLoc);
+ const char *FileBufferStart
+ = SourceMgr.getBufferData(DeclStartDecomp.first).first;
+
+ // First check whether we have a comment for a member.
+ if (LastComment != Comments.end() &&
+ !isa<TagDecl>(D) && !isa<NamespaceDecl>(D) &&
+ isDoxygenComment(SourceMgr, *LastComment, true)) {
+ std::pair<FileID, unsigned> LastCommentEndDecomp
+ = SourceMgr.getDecomposedLoc(LastComment->getEnd());
+ if (DeclStartDecomp.first == LastCommentEndDecomp.first &&
+ SourceMgr.getLineNumber(DeclStartDecomp.first, DeclStartDecomp.second)
+ == SourceMgr.getLineNumber(LastCommentEndDecomp.first,
+ LastCommentEndDecomp.second)) {
+ // The Doxygen member comment comes after the declaration starts and
+ // is on the same line and in the same file as the declaration. This
+ // is the comment we want.
+ std::string &Result = DeclComments[D];
+ Result.append(FileBufferStart +
+ SourceMgr.getFileOffset(LastComment->getBegin()),
+ FileBufferStart + LastCommentEndDecomp.second + 1);
+ return Result.c_str();
+ }
+ }
+
+ if (LastComment == Comments.begin())
+ return 0;
+ --LastComment;
+
+ // Decompose the end of the comment.
+ std::pair<FileID, unsigned> LastCommentEndDecomp
+ = SourceMgr.getDecomposedLoc(LastComment->getEnd());
+
+ // If the comment and the declaration aren't in the same file, then they
+ // aren't related.
+ if (DeclStartDecomp.first != LastCommentEndDecomp.first)
+ return 0;
+
+ // Check that we actually have a Doxygen comment.
+ if (!isDoxygenComment(SourceMgr, *LastComment))
+ return 0;
+
+ // Compute the starting line for the declaration and for the end of the
+ // comment (this is expensive).
+ unsigned DeclStartLine
+ = SourceMgr.getLineNumber(DeclStartDecomp.first, DeclStartDecomp.second);
+ unsigned CommentEndLine
+ = SourceMgr.getLineNumber(LastCommentEndDecomp.first,
+ LastCommentEndDecomp.second);
+
+ // If the comment does not end on the line prior to the declaration, then
+ // the comment is not associated with the declaration at all.
+ if (CommentEndLine + 1 != DeclStartLine)
+ return 0;
+
+ // We have a comment, but there may be more comments on the previous lines.
+ // Keep looking so long as the comments are still Doxygen comments and are
+ // still adjacent.
+ unsigned ExpectedLine
+ = SourceMgr.getSpellingLineNumber(LastComment->getBegin()) - 1;
+ std::vector<SourceRange>::iterator FirstComment = LastComment;
+ while (FirstComment != Comments.begin()) {
+ // Look at the previous comment
+ --FirstComment;
+ std::pair<FileID, unsigned> Decomp
+ = SourceMgr.getDecomposedLoc(FirstComment->getEnd());
+
+ // If this previous comment is in a different file, we're done.
+ if (Decomp.first != DeclStartDecomp.first) {
+ ++FirstComment;
+ break;
+ }
+
+ // If this comment is not a Doxygen comment, we're done.
+ if (!isDoxygenComment(SourceMgr, *FirstComment)) {
+ ++FirstComment;
+ break;
+ }
+
+ // If the line number is not what we expected, we're done.
+ unsigned Line = SourceMgr.getLineNumber(Decomp.first, Decomp.second);
+ if (Line != ExpectedLine) {
+ ++FirstComment;
+ break;
+ }
+
+ // Set the next expected line number.
+ ExpectedLine
+ = SourceMgr.getSpellingLineNumber(FirstComment->getBegin()) - 1;
+ }
+
+ // The iterator range [FirstComment, LastComment] contains all of the
+ // BCPL comments that, together, are associated with this declaration.
+ // Form a single comment block string for this declaration that concatenates
+ // all of these comments.
+ std::string &Result = DeclComments[D];
+ while (FirstComment != LastComment) {
+ std::pair<FileID, unsigned> DecompStart
+ = SourceMgr.getDecomposedLoc(FirstComment->getBegin());
+ std::pair<FileID, unsigned> DecompEnd
+ = SourceMgr.getDecomposedLoc(FirstComment->getEnd());
+ Result.append(FileBufferStart + DecompStart.second,
+ FileBufferStart + DecompEnd.second + 1);
+ ++FirstComment;
+ }
+
+ // Append the last comment line.
+ Result.append(FileBufferStart +
+ SourceMgr.getFileOffset(LastComment->getBegin()),
+ FileBufferStart + LastCommentEndDecomp.second + 1);
+ return Result.c_str();
+}
+
//===----------------------------------------------------------------------===//
// Type Sizing and Analysis
//===----------------------------------------------------------------------===//
diff --git a/lib/Frontend/PCHReader.cpp b/lib/Frontend/PCHReader.cpp
index 765655b01d..95b166159e 100644
--- a/lib/Frontend/PCHReader.cpp
+++ b/lib/Frontend/PCHReader.cpp
@@ -344,7 +344,8 @@ PCHReader::PCHReader(Preprocessor &PP, ASTContext *Context)
IdentifierOffsets(0),
MethodPoolLookupTable(0), MethodPoolLookupTableData(0),
TotalSelectorsInMethodPool(0), SelectorOffsets(0),
- TotalNumSelectors(0), NumStatHits(0), NumStatMisses(0),
+ TotalNumSelectors(0), Comments(0), NumComments(0),
+ NumStatHits(0), NumStatMisses(0),
NumSLocEntriesRead(0), NumStatementsRead(0),
NumMacrosRead(0), NumMethodPoolSelectorsRead(0), NumMethodPoolMisses(0),
NumLexicalDeclContextsRead(0), NumVisibleDeclContextsRead(0) { }
@@ -1350,6 +1351,11 @@ PCHReader::ReadPCHBlock() {
case pch::ORIGINAL_FILE_NAME:
OriginalFileName.assign(BlobStart, BlobLen);
break;
+
+ case pch::COMMENT_RANGES:
+ Comments = (SourceRange *)BlobStart;
+ NumComments = BlobLen / sizeof(SourceRange);
+ break;
}
}
Error("premature end of bitstream in PCH file");
@@ -1664,6 +1670,12 @@ bool PCHReader::ParseLanguageOptions(
return false;
}
+void PCHReader::ReadComments(std::vector<SourceRange> &Comments) {
+ Comments.resize(NumComments);
+ std::copy(this->Comments, this->Comments + NumComments,
+ Comments.begin());
+}
+
/// \brief Read and return the type at the given offset.
///
/// This routine actually reads the record corresponding to the type
diff --git a/lib/Frontend/PCHWriter.cpp b/lib/Frontend/PCHWriter.cpp
index 41a5025455..3bfc9e89d1 100644
--- a/lib/Frontend/PCHWriter.cpp
+++ b/lib/Frontend/PCHWriter.cpp
@@ -374,7 +374,8 @@ void PCHWriter::WriteBlockInfoBlock() {
RECORD(STAT_CACHE);
RECORD(EXT_VECTOR_DECLS);
RECORD(OBJC_CATEGORY_IMPLEMENTATIONS);
-
+ RECORD(COMMENT_RANGES);
+
// SourceManager Block.
BLOCK(SOURCE_MANAGER_BLOCK);
RECORD(SM_SLOC_FILE_ENTRY);
@@ -989,6 +990,24 @@ void PCHWriter::WritePreprocessor(const Preprocessor &PP) {
Stream.ExitBlock();
}
+void PCHWriter::WriteComments(ASTContext &Context) {
+ using namespace llvm;
+
+ if (Context.Comments.empty())
+ return;
+
+ BitCodeAbbrev *CommentAbbrev = new BitCodeAbbrev();
+ CommentAbbrev->Add(BitCodeAbbrevOp(pch::COMMENT_RANGES));
+ CommentAbbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Blob));
+ unsigned CommentCode = Stream.EmitAbbrev(CommentAbbrev);
+
+ RecordData Record;
+ Record.push_back(pch::COMMENT_RANGES);
+ Stream.EmitRecordWithBlob(CommentCode, Record,
+ (const char*)&Context.Comments[0],
+ Context.Comments.size() * sizeof(SourceRange));
+}
+
//===----------------------------------------------------------------------===//
// Type Serialization
//===----------------------------------------------------------------------===//
@@ -1746,7 +1765,8 @@ void PCHWriter::WritePCH(Sema &SemaRef, MemorizeStatCalls *StatCalls) {
WriteStatCache(*StatCalls);
WriteSourceManagerBlock(Context.getSourceManager(), PP);
WritePreprocessor(PP);
-
+ WriteComments(Context);
+
// Keep writing types and declarations until all types and
// declarations have been written.
do {
diff --git a/lib/Lex/Lexer.cpp b/lib/Lex/Lexer.cpp
index 81ea606f0d..6f1043ae73 100644
--- a/lib/Lex/Lexer.cpp
+++ b/lib/Lex/Lexer.cpp
@@ -903,7 +903,10 @@ bool Lexer::SkipBCPLComment(Token &Result, const char *CurPtr) {
} while (C != '\n' && C != '\r');
// Found but did not consume the newline.
-
+ if (PP)
+ PP->HandleComment(SourceRange(getSourceLocation(BufferPtr),
+ getSourceLocation(CurPtr)));
+
// If we are returning comments as tokens, return this comment as a token.
if (inKeepCommentMode())
return SaveBCPLComment(Result, CurPtr);
@@ -1146,6 +1149,10 @@ bool Lexer::SkipBlockComment(Token &Result, const char *CurPtr) {
C = *CurPtr++;
}
+ if (PP)
+ PP->HandleComment(SourceRange(getSourceLocation(BufferPtr),
+ getSourceLocation(CurPtr)));
+
// If we are returning comments as tokens, return this comment as a token.
if (inKeepCommentMode()) {
FormTokenWithChars(Result, CurPtr, tok::comment);
diff --git a/lib/Lex/Pragma.cpp b/lib/Lex/Pragma.cpp
index 3227d1cf09..bb0b71e226 100644
--- a/lib/Lex/Pragma.cpp
+++ b/lib/Lex/Pragma.cpp
@@ -19,6 +19,7 @@
#include "clang/Lex/LexDiagnostic.h"
#include "clang/Basic/FileManager.h"
#include "clang/Basic/SourceManager.h"
+#include <algorithm>
using namespace clang;
// Out-of-line destructor to provide a home for the class.
diff --git a/lib/Lex/Preprocessor.cpp b/lib/Lex/Preprocessor.cpp
index 24ee5d8a8f..9f0c15f59e 100644
--- a/lib/Lex/Preprocessor.cpp
+++ b/lib/Lex/Preprocessor.cpp
@@ -476,3 +476,26 @@ void Preprocessor::HandleIdentifier(Token &Identifier) {
if (II.isExtensionToken() && !DisableMacroExpansion)
Diag(Identifier, diag::ext_token_used);
}
+
+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) {
+ std::vector<CommentHandler *>::iterator Pos
+ = std::find(CommentHandlers.begin(), CommentHandlers.end(), Handler);
+ assert(Pos != CommentHandlers.end() && "Comment handler not registered");
+ CommentHandlers.erase(Pos);
+}
+
+void Preprocessor::HandleComment(SourceRange Comment) {
+ for (std::vector<CommentHandler *>::iterator H = CommentHandlers.begin(),
+ HEnd = CommentHandlers.end();
+ H != HEnd; ++H)
+ (*H)->HandleComment(*this, Comment);
+}
+
+CommentHandler::~CommentHandler() { }
diff --git a/lib/Parse/Parser.cpp b/lib/Parse/Parser.cpp
index 29d1d8792e..9771cf7b84 100644
--- a/lib/Parse/Parser.cpp
+++ b/lib/Parse/Parser.cpp
@@ -20,6 +20,19 @@
#include "ParsePragma.h"
using namespace clang;
+/// \brief A comment handler that passes comments found by the preprocessor
+/// to the parser action.
+class ActionCommentHandler : public CommentHandler {
+ Action &Actions;
+
+public:
+ explicit ActionCommentHandler(Action &Actions) : Actions(Actions) { }
+
+ virtual void HandleComment(Preprocessor &PP, SourceRange Comment) {
+ Actions.ActOnComment(Comment);
+ }
+};
+
Parser::Parser(Preprocessor &pp, Action &actions)
: CrashInfo(*this), PP(pp), Actions(actions), Diags(PP.getDiagnostics()),
GreaterThanIsOperator(true) {
@@ -43,6 +56,9 @@ Parser::Parser(Preprocessor &pp, Action &actions)
WeakHandler.reset(new
PragmaWeakHandler(&PP.getIdentifierTable().get("weak"), actions));
PP.AddPragmaHandler(0, WeakHandler.get());
+
+ CommentHandler.reset(new ActionCommentHandler(actions));
+ PP.AddCommentHandler(CommentHandler.get());
}
/// If a crash happens while the parser is active, print out a line indicating
@@ -294,6 +310,7 @@ Parser::~Parser() {
UnusedHandler.reset();
PP.RemovePragmaHandler(0, WeakHandler.get());
WeakHandler.reset();
+ PP.RemoveCommentHandler(CommentHandler.get());
}
/// Initialize - Warm up the parser.
diff --git a/lib/Sema/Sema.cpp b/lib/Sema/Sema.cpp
index c37f66dae9..d1e8e2104d 100644
--- a/lib/Sema/Sema.cpp
+++ b/lib/Sema/Sema.cpp
@@ -345,3 +345,7 @@ Sema::SemaDiagnosticBuilder::~SemaDiagnosticBuilder() {
= SemaRef.ActiveTemplateInstantiations.back();
}
}
+
+void Sema::ActOnComment(SourceRange Comment) {
+ Context.Comments.push_back(Comment);
+}
diff --git a/lib/Sema/Sema.h b/lib/Sema/Sema.h
index a6c34e8885..72fb0a82a0 100644
--- a/lib/Sema/Sema.h
+++ b/lib/Sema/Sema.h
@@ -362,6 +362,8 @@ public:
return CurBlock ? CurBlock->SwitchStack : FunctionSwitchStack;
}
+ virtual void ActOnComment(SourceRange Comment);
+
//===--------------------------------------------------------------------===//
// Type Analysis / Processing: SemaType.cpp.
//
diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt
index 7521a1d167..d5e2327872 100644
--- a/test/CMakeLists.txt
+++ b/test/CMakeLists.txt
@@ -8,6 +8,7 @@ set(CLANG_TEST_DIRECTORIES
"Driver"
"FixIt"
"Frontend"
+ "Index"
"Lexer"
"Misc"
"PCH"
@@ -54,6 +55,6 @@ if(PYTHONINTERP_FOUND)
--clang-cc=${LLVM_TOOLS_PATH}/${CMAKE_CFG_INTDIR}/clang-cc
${all_testdirs}
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
- DEPENDS clang clang-cc
+ DEPENDS clang clang-cc index-test
COMMENT "Running Clang regression tests")
endif()
diff --git a/test/Index/comments.c b/test/Index/comments.c
new file mode 100644
index 0000000000..7ad8fd7e1f
--- /dev/null
+++ b/test/Index/comments.c
@@ -0,0 +1,30 @@
+// RUN: clang-cc -emit-pch -o %t.ast %s &&
+// RUN: index-test %t.ast -point-at %s:22:6 | grep "starts here" &&
+// RUN: index-test %t.ast -point-at %s:22:6 | grep "block comment" &&
+// RUN: index-test %t.ast -point-at %s:28:6 | grep "BCPL" &&
+// RUN: index-test %t.ast -point-at %s:28:6 | grep "But" &&
+// RUN: index-test %t.ast -point-at %s:28:6 | grep "NOT" | count 0 &&
+// RUN: index-test %t.ast -point-at %s:30:6 | grep "member"
+
+
+
+
+
+
+//! It all starts here.
+/*! 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.
+ */
+void f(int, int);
+
+// NOT IN THE COMMENT
+/// This is a BCPL comment that is associated with the function g.
+/// It has only two lines.
+/** But there are other blocks that are part of the comment, too. */
+void g(int);
+
+void h(int); ///< This is a member comment. \ No newline at end of file
diff --git a/tools/index-test/index-test.cpp b/tools/index-test/index-test.cpp
index 9b10c55bde..552b7b0149 100644
--- a/tools/index-test/index-test.cpp
+++ b/tools/index-test/index-test.cpp
@@ -130,6 +130,9 @@ int main(int argc, char **argv) {
OS << ND->getNameAsString();
OS << "\n";
+ if (const char *Comment = AST->getASTContext().getCommentForDecl(Point.D))
+ OS << "Comment associated with this declaration:\n" << Comment << "\n";
+
if (Point.Node) {
OS << "Statement node at point: " << Point.Node->getStmtClassName()
<< " ";