xref: /minix3/external/bsd/llvm/dist/clang/lib/AST/RawCommentList.cpp (revision 0a6a1f1d05b60e214de2f05a7310ddd1f0e590e7)
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