1f4a2713aSLionel Sambuc //===--- RawCommentList.cpp - Processing raw comments -----------*- C++ -*-===//
2f4a2713aSLionel Sambuc //
3f4a2713aSLionel Sambuc // The LLVM Compiler Infrastructure
4f4a2713aSLionel Sambuc //
5f4a2713aSLionel Sambuc // This file is distributed under the University of Illinois Open Source
6f4a2713aSLionel Sambuc // License. See LICENSE.TXT for details.
7f4a2713aSLionel Sambuc //
8f4a2713aSLionel Sambuc //===----------------------------------------------------------------------===//
9f4a2713aSLionel Sambuc
10f4a2713aSLionel Sambuc #include "clang/AST/RawCommentList.h"
11f4a2713aSLionel Sambuc #include "clang/AST/ASTContext.h"
12f4a2713aSLionel Sambuc #include "clang/AST/Comment.h"
13f4a2713aSLionel Sambuc #include "clang/AST/CommentBriefParser.h"
14f4a2713aSLionel Sambuc #include "clang/AST/CommentCommandTraits.h"
15f4a2713aSLionel Sambuc #include "clang/AST/CommentLexer.h"
16f4a2713aSLionel Sambuc #include "clang/AST/CommentParser.h"
17f4a2713aSLionel Sambuc #include "clang/AST/CommentSema.h"
18f4a2713aSLionel Sambuc #include "llvm/ADT/STLExtras.h"
19f4a2713aSLionel Sambuc
20f4a2713aSLionel Sambuc using namespace clang;
21f4a2713aSLionel Sambuc
22f4a2713aSLionel Sambuc namespace {
23f4a2713aSLionel Sambuc /// Get comment kind and bool describing if it is a trailing comment.
getCommentKind(StringRef Comment,bool ParseAllComments)24f4a2713aSLionel Sambuc std::pair<RawComment::CommentKind, bool> getCommentKind(StringRef Comment,
25f4a2713aSLionel Sambuc bool ParseAllComments) {
26f4a2713aSLionel Sambuc const size_t MinCommentLength = ParseAllComments ? 2 : 3;
27f4a2713aSLionel Sambuc if ((Comment.size() < MinCommentLength) || Comment[0] != '/')
28f4a2713aSLionel Sambuc return std::make_pair(RawComment::RCK_Invalid, false);
29f4a2713aSLionel Sambuc
30f4a2713aSLionel Sambuc RawComment::CommentKind K;
31f4a2713aSLionel Sambuc if (Comment[1] == '/') {
32f4a2713aSLionel Sambuc if (Comment.size() < 3)
33f4a2713aSLionel Sambuc return std::make_pair(RawComment::RCK_OrdinaryBCPL, false);
34f4a2713aSLionel Sambuc
35f4a2713aSLionel Sambuc if (Comment[2] == '/')
36f4a2713aSLionel Sambuc K = RawComment::RCK_BCPLSlash;
37f4a2713aSLionel Sambuc else if (Comment[2] == '!')
38f4a2713aSLionel Sambuc K = RawComment::RCK_BCPLExcl;
39f4a2713aSLionel Sambuc else
40f4a2713aSLionel Sambuc return std::make_pair(RawComment::RCK_OrdinaryBCPL, false);
41f4a2713aSLionel Sambuc } else {
42f4a2713aSLionel Sambuc assert(Comment.size() >= 4);
43f4a2713aSLionel Sambuc
44f4a2713aSLionel Sambuc // Comment lexer does not understand escapes in comment markers, so pretend
45f4a2713aSLionel Sambuc // that this is not a comment.
46f4a2713aSLionel Sambuc if (Comment[1] != '*' ||
47f4a2713aSLionel Sambuc Comment[Comment.size() - 2] != '*' ||
48f4a2713aSLionel Sambuc Comment[Comment.size() - 1] != '/')
49f4a2713aSLionel Sambuc return std::make_pair(RawComment::RCK_Invalid, false);
50f4a2713aSLionel Sambuc
51f4a2713aSLionel Sambuc if (Comment[2] == '*')
52f4a2713aSLionel Sambuc K = RawComment::RCK_JavaDoc;
53f4a2713aSLionel Sambuc else if (Comment[2] == '!')
54f4a2713aSLionel Sambuc K = RawComment::RCK_Qt;
55f4a2713aSLionel Sambuc else
56f4a2713aSLionel Sambuc return std::make_pair(RawComment::RCK_OrdinaryC, false);
57f4a2713aSLionel Sambuc }
58f4a2713aSLionel Sambuc const bool TrailingComment = (Comment.size() > 3) && (Comment[3] == '<');
59f4a2713aSLionel Sambuc return std::make_pair(K, TrailingComment);
60f4a2713aSLionel Sambuc }
61f4a2713aSLionel Sambuc
mergedCommentIsTrailingComment(StringRef Comment)62f4a2713aSLionel Sambuc bool mergedCommentIsTrailingComment(StringRef Comment) {
63f4a2713aSLionel Sambuc return (Comment.size() > 3) && (Comment[3] == '<');
64f4a2713aSLionel Sambuc }
65f4a2713aSLionel Sambuc } // unnamed namespace
66f4a2713aSLionel Sambuc
RawComment(const SourceManager & SourceMgr,SourceRange SR,bool Merged,bool ParseAllComments)67f4a2713aSLionel Sambuc RawComment::RawComment(const SourceManager &SourceMgr, SourceRange SR,
68f4a2713aSLionel Sambuc bool Merged, bool ParseAllComments) :
69f4a2713aSLionel Sambuc Range(SR), RawTextValid(false), BriefTextValid(false),
70f4a2713aSLionel Sambuc IsAttached(false), IsAlmostTrailingComment(false),
71f4a2713aSLionel Sambuc ParseAllComments(ParseAllComments) {
72f4a2713aSLionel Sambuc // Extract raw comment text, if possible.
73f4a2713aSLionel Sambuc if (SR.getBegin() == SR.getEnd() || getRawText(SourceMgr).empty()) {
74f4a2713aSLionel Sambuc Kind = RCK_Invalid;
75f4a2713aSLionel Sambuc return;
76f4a2713aSLionel Sambuc }
77f4a2713aSLionel Sambuc
78f4a2713aSLionel Sambuc if (!Merged) {
79f4a2713aSLionel Sambuc // Guess comment kind.
80f4a2713aSLionel Sambuc std::pair<CommentKind, bool> K = getCommentKind(RawText, ParseAllComments);
81f4a2713aSLionel Sambuc Kind = K.first;
82f4a2713aSLionel Sambuc IsTrailingComment = K.second;
83f4a2713aSLionel Sambuc
84f4a2713aSLionel Sambuc IsAlmostTrailingComment = RawText.startswith("//<") ||
85f4a2713aSLionel Sambuc RawText.startswith("/*<");
86f4a2713aSLionel Sambuc } else {
87f4a2713aSLionel Sambuc Kind = RCK_Merged;
88f4a2713aSLionel Sambuc IsTrailingComment = mergedCommentIsTrailingComment(RawText);
89f4a2713aSLionel Sambuc }
90f4a2713aSLionel Sambuc }
91f4a2713aSLionel Sambuc
getRawTextSlow(const SourceManager & SourceMgr) const92f4a2713aSLionel Sambuc StringRef RawComment::getRawTextSlow(const SourceManager &SourceMgr) const {
93f4a2713aSLionel Sambuc FileID BeginFileID;
94f4a2713aSLionel Sambuc FileID EndFileID;
95f4a2713aSLionel Sambuc unsigned BeginOffset;
96f4a2713aSLionel Sambuc unsigned EndOffset;
97f4a2713aSLionel Sambuc
98*0a6a1f1dSLionel Sambuc std::tie(BeginFileID, BeginOffset) =
99f4a2713aSLionel Sambuc SourceMgr.getDecomposedLoc(Range.getBegin());
100*0a6a1f1dSLionel Sambuc std::tie(EndFileID, EndOffset) = SourceMgr.getDecomposedLoc(Range.getEnd());
101f4a2713aSLionel Sambuc
102f4a2713aSLionel Sambuc const unsigned Length = EndOffset - BeginOffset;
103f4a2713aSLionel Sambuc if (Length < 2)
104f4a2713aSLionel Sambuc return StringRef();
105f4a2713aSLionel Sambuc
106f4a2713aSLionel Sambuc // The comment can't begin in one file and end in another.
107f4a2713aSLionel Sambuc assert(BeginFileID == EndFileID);
108f4a2713aSLionel Sambuc
109f4a2713aSLionel Sambuc bool Invalid = false;
110f4a2713aSLionel Sambuc const char *BufferStart = SourceMgr.getBufferData(BeginFileID,
111f4a2713aSLionel Sambuc &Invalid).data();
112f4a2713aSLionel Sambuc if (Invalid)
113f4a2713aSLionel Sambuc return StringRef();
114f4a2713aSLionel Sambuc
115f4a2713aSLionel Sambuc return StringRef(BufferStart + BeginOffset, Length);
116f4a2713aSLionel Sambuc }
117f4a2713aSLionel Sambuc
extractBriefText(const ASTContext & Context) const118f4a2713aSLionel Sambuc const char *RawComment::extractBriefText(const ASTContext &Context) const {
119f4a2713aSLionel Sambuc // Make sure that RawText is valid.
120f4a2713aSLionel Sambuc getRawText(Context.getSourceManager());
121f4a2713aSLionel Sambuc
122f4a2713aSLionel Sambuc // Since we will be copying the resulting text, all allocations made during
123f4a2713aSLionel Sambuc // parsing are garbage after resulting string is formed. Thus we can use
124f4a2713aSLionel Sambuc // a separate allocator for all temporary stuff.
125f4a2713aSLionel Sambuc llvm::BumpPtrAllocator Allocator;
126f4a2713aSLionel Sambuc
127f4a2713aSLionel Sambuc comments::Lexer L(Allocator, Context.getDiagnostics(),
128f4a2713aSLionel Sambuc Context.getCommentCommandTraits(),
129f4a2713aSLionel Sambuc Range.getBegin(),
130f4a2713aSLionel Sambuc RawText.begin(), RawText.end());
131f4a2713aSLionel Sambuc comments::BriefParser P(L, Context.getCommentCommandTraits());
132f4a2713aSLionel Sambuc
133f4a2713aSLionel Sambuc const std::string Result = P.Parse();
134f4a2713aSLionel Sambuc const unsigned BriefTextLength = Result.size();
135f4a2713aSLionel Sambuc char *BriefTextPtr = new (Context) char[BriefTextLength + 1];
136f4a2713aSLionel Sambuc memcpy(BriefTextPtr, Result.c_str(), BriefTextLength + 1);
137f4a2713aSLionel Sambuc BriefText = BriefTextPtr;
138f4a2713aSLionel Sambuc BriefTextValid = true;
139f4a2713aSLionel Sambuc
140f4a2713aSLionel Sambuc return BriefTextPtr;
141f4a2713aSLionel Sambuc }
142f4a2713aSLionel Sambuc
parse(const ASTContext & Context,const Preprocessor * PP,const Decl * D) const143f4a2713aSLionel Sambuc comments::FullComment *RawComment::parse(const ASTContext &Context,
144f4a2713aSLionel Sambuc const Preprocessor *PP,
145f4a2713aSLionel Sambuc const Decl *D) const {
146f4a2713aSLionel Sambuc // Make sure that RawText is valid.
147f4a2713aSLionel Sambuc getRawText(Context.getSourceManager());
148f4a2713aSLionel Sambuc
149f4a2713aSLionel Sambuc comments::Lexer L(Context.getAllocator(), Context.getDiagnostics(),
150f4a2713aSLionel Sambuc Context.getCommentCommandTraits(),
151f4a2713aSLionel Sambuc getSourceRange().getBegin(),
152f4a2713aSLionel Sambuc RawText.begin(), RawText.end());
153f4a2713aSLionel Sambuc comments::Sema S(Context.getAllocator(), Context.getSourceManager(),
154f4a2713aSLionel Sambuc Context.getDiagnostics(),
155f4a2713aSLionel Sambuc Context.getCommentCommandTraits(),
156f4a2713aSLionel Sambuc PP);
157f4a2713aSLionel Sambuc S.setDecl(D);
158f4a2713aSLionel Sambuc comments::Parser P(L, S, Context.getAllocator(), Context.getSourceManager(),
159f4a2713aSLionel Sambuc Context.getDiagnostics(),
160f4a2713aSLionel Sambuc Context.getCommentCommandTraits());
161f4a2713aSLionel Sambuc
162f4a2713aSLionel Sambuc return P.parseFullComment();
163f4a2713aSLionel Sambuc }
164f4a2713aSLionel Sambuc
onlyWhitespaceBetween(SourceManager & SM,SourceLocation Loc1,SourceLocation Loc2,unsigned MaxNewlinesAllowed)165f4a2713aSLionel Sambuc static bool onlyWhitespaceBetween(SourceManager &SM,
166f4a2713aSLionel Sambuc SourceLocation Loc1, SourceLocation Loc2,
167f4a2713aSLionel Sambuc unsigned MaxNewlinesAllowed) {
168f4a2713aSLionel Sambuc std::pair<FileID, unsigned> Loc1Info = SM.getDecomposedLoc(Loc1);
169f4a2713aSLionel Sambuc std::pair<FileID, unsigned> Loc2Info = SM.getDecomposedLoc(Loc2);
170f4a2713aSLionel Sambuc
171f4a2713aSLionel Sambuc // Question does not make sense if locations are in different files.
172f4a2713aSLionel Sambuc if (Loc1Info.first != Loc2Info.first)
173f4a2713aSLionel Sambuc return false;
174f4a2713aSLionel Sambuc
175f4a2713aSLionel Sambuc bool Invalid = false;
176f4a2713aSLionel Sambuc const char *Buffer = SM.getBufferData(Loc1Info.first, &Invalid).data();
177f4a2713aSLionel Sambuc if (Invalid)
178f4a2713aSLionel Sambuc return false;
179f4a2713aSLionel Sambuc
180f4a2713aSLionel Sambuc unsigned NumNewlines = 0;
181f4a2713aSLionel Sambuc assert(Loc1Info.second <= Loc2Info.second && "Loc1 after Loc2!");
182f4a2713aSLionel Sambuc // Look for non-whitespace characters and remember any newlines seen.
183f4a2713aSLionel Sambuc for (unsigned I = Loc1Info.second; I != Loc2Info.second; ++I) {
184f4a2713aSLionel Sambuc switch (Buffer[I]) {
185f4a2713aSLionel Sambuc default:
186f4a2713aSLionel Sambuc return false;
187f4a2713aSLionel Sambuc case ' ':
188f4a2713aSLionel Sambuc case '\t':
189f4a2713aSLionel Sambuc case '\f':
190f4a2713aSLionel Sambuc case '\v':
191f4a2713aSLionel Sambuc break;
192f4a2713aSLionel Sambuc case '\r':
193f4a2713aSLionel Sambuc case '\n':
194f4a2713aSLionel Sambuc ++NumNewlines;
195f4a2713aSLionel Sambuc
196f4a2713aSLionel Sambuc // Check if we have found more than the maximum allowed number of
197f4a2713aSLionel Sambuc // newlines.
198f4a2713aSLionel Sambuc if (NumNewlines > MaxNewlinesAllowed)
199f4a2713aSLionel Sambuc return false;
200f4a2713aSLionel Sambuc
201f4a2713aSLionel Sambuc // Collapse \r\n and \n\r into a single newline.
202f4a2713aSLionel Sambuc if (I + 1 != Loc2Info.second &&
203f4a2713aSLionel Sambuc (Buffer[I + 1] == '\n' || Buffer[I + 1] == '\r') &&
204f4a2713aSLionel Sambuc Buffer[I] != Buffer[I + 1])
205f4a2713aSLionel Sambuc ++I;
206f4a2713aSLionel Sambuc break;
207f4a2713aSLionel Sambuc }
208f4a2713aSLionel Sambuc }
209f4a2713aSLionel Sambuc
210f4a2713aSLionel Sambuc return true;
211f4a2713aSLionel Sambuc }
212f4a2713aSLionel Sambuc
addComment(const RawComment & RC,llvm::BumpPtrAllocator & Allocator)213f4a2713aSLionel Sambuc void RawCommentList::addComment(const RawComment &RC,
214f4a2713aSLionel Sambuc llvm::BumpPtrAllocator &Allocator) {
215f4a2713aSLionel Sambuc if (RC.isInvalid())
216f4a2713aSLionel Sambuc return;
217f4a2713aSLionel Sambuc
218f4a2713aSLionel Sambuc // Check if the comments are not in source order.
219f4a2713aSLionel Sambuc while (!Comments.empty() &&
220f4a2713aSLionel Sambuc !SourceMgr.isBeforeInTranslationUnit(Comments.back()->getLocStart(),
221f4a2713aSLionel Sambuc RC.getLocStart())) {
222f4a2713aSLionel Sambuc // If they are, just pop a few last comments that don't fit.
223f4a2713aSLionel Sambuc // This happens if an \#include directive contains comments.
224f4a2713aSLionel Sambuc Comments.pop_back();
225f4a2713aSLionel Sambuc }
226f4a2713aSLionel Sambuc
227f4a2713aSLionel Sambuc // Ordinary comments are not interesting for us.
228f4a2713aSLionel Sambuc if (RC.isOrdinary())
229f4a2713aSLionel Sambuc return;
230f4a2713aSLionel Sambuc
231f4a2713aSLionel Sambuc // If this is the first Doxygen comment, save it (because there isn't
232f4a2713aSLionel Sambuc // anything to merge it with).
233f4a2713aSLionel Sambuc if (Comments.empty()) {
234f4a2713aSLionel Sambuc Comments.push_back(new (Allocator) RawComment(RC));
235f4a2713aSLionel Sambuc return;
236f4a2713aSLionel Sambuc }
237f4a2713aSLionel Sambuc
238f4a2713aSLionel Sambuc const RawComment &C1 = *Comments.back();
239f4a2713aSLionel Sambuc const RawComment &C2 = RC;
240f4a2713aSLionel Sambuc
241f4a2713aSLionel Sambuc // Merge comments only if there is only whitespace between them.
242f4a2713aSLionel Sambuc // Can't merge trailing and non-trailing comments.
243f4a2713aSLionel Sambuc // Merge comments if they are on same or consecutive lines.
244f4a2713aSLionel Sambuc if (C1.isTrailingComment() == C2.isTrailingComment() &&
245f4a2713aSLionel Sambuc onlyWhitespaceBetween(SourceMgr, C1.getLocEnd(), C2.getLocStart(),
246f4a2713aSLionel Sambuc /*MaxNewlinesAllowed=*/1)) {
247f4a2713aSLionel Sambuc SourceRange MergedRange(C1.getLocStart(), C2.getLocEnd());
248f4a2713aSLionel Sambuc *Comments.back() = RawComment(SourceMgr, MergedRange, true,
249f4a2713aSLionel Sambuc RC.isParseAllComments());
250f4a2713aSLionel Sambuc } else {
251f4a2713aSLionel Sambuc Comments.push_back(new (Allocator) RawComment(RC));
252f4a2713aSLionel Sambuc }
253f4a2713aSLionel Sambuc }
254*0a6a1f1dSLionel Sambuc
addDeserializedComments(ArrayRef<RawComment * > DeserializedComments)255*0a6a1f1dSLionel Sambuc void RawCommentList::addDeserializedComments(ArrayRef<RawComment *> DeserializedComments) {
256*0a6a1f1dSLionel Sambuc std::vector<RawComment *> MergedComments;
257*0a6a1f1dSLionel Sambuc MergedComments.reserve(Comments.size() + DeserializedComments.size());
258*0a6a1f1dSLionel Sambuc
259*0a6a1f1dSLionel Sambuc std::merge(Comments.begin(), Comments.end(),
260*0a6a1f1dSLionel Sambuc DeserializedComments.begin(), DeserializedComments.end(),
261*0a6a1f1dSLionel Sambuc std::back_inserter(MergedComments),
262*0a6a1f1dSLionel Sambuc BeforeThanCompare<RawComment>(SourceMgr));
263*0a6a1f1dSLionel Sambuc std::swap(Comments, MergedComments);
264*0a6a1f1dSLionel Sambuc }
265*0a6a1f1dSLionel Sambuc
266