diff options
author | Dmitri Gribenko <gribozavr@gmail.com> | 2012-06-20 00:34:58 +0000 |
---|---|---|
committer | Dmitri Gribenko <gribozavr@gmail.com> | 2012-06-20 00:34:58 +0000 |
commit | aa0cd85838f2a024e589ea4e8c2094130065af21 (patch) | |
tree | b4e013c1268210fa0769c263d702c637395b59ae | |
parent | ed36b2a80878c29603bdc89a7969253fb6446174 (diff) |
Structured comment parsing, first step.
* Retain comments in the AST
* Serialize/deserialize comments
* Find comments attached to a certain Decl
* Expose raw comment text and SourceRange via libclang
git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@158771 91177308-0d34-0410-b5e6-96231b3b80d8
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( |