diff options
-rw-r--r-- | include/clang/AST/ASTContext.h | 16 | ||||
-rw-r--r-- | include/clang/AST/ExternalASTSource.h | 9 | ||||
-rw-r--r-- | include/clang/Frontend/PCHBitCodes.h | 6 | ||||
-rw-r--r-- | include/clang/Frontend/PCHReader.h | 14 | ||||
-rw-r--r-- | include/clang/Frontend/PCHWriter.h | 1 | ||||
-rw-r--r-- | include/clang/Lex/Preprocessor.h | 24 | ||||
-rw-r--r-- | include/clang/Parse/Action.h | 4 | ||||
-rw-r--r-- | include/clang/Parse/Parser.h | 3 | ||||
-rw-r--r-- | lib/AST/ASTContext.cpp | 204 | ||||
-rw-r--r-- | lib/Frontend/PCHReader.cpp | 14 | ||||
-rw-r--r-- | lib/Frontend/PCHWriter.cpp | 24 | ||||
-rw-r--r-- | lib/Lex/Lexer.cpp | 9 | ||||
-rw-r--r-- | lib/Lex/Pragma.cpp | 1 | ||||
-rw-r--r-- | lib/Lex/Preprocessor.cpp | 23 | ||||
-rw-r--r-- | lib/Parse/Parser.cpp | 17 | ||||
-rw-r--r-- | lib/Sema/Sema.cpp | 4 | ||||
-rw-r--r-- | lib/Sema/Sema.h | 2 | ||||
-rw-r--r-- | test/CMakeLists.txt | 3 | ||||
-rw-r--r-- | test/Index/comments.c | 30 | ||||
-rw-r--r-- | tools/index-test/index-test.cpp | 3 |
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() << " "; |