blob: ad131a5221d5b40caacc26951bccb1d5e9d8e186 [file] [log] [blame]
Dmitri Gribenko5e4fe002012-07-20 21:34:34 +00001//===- CXComment.cpp - libclang APIs for manipulating CXComments ----------===//
2//
3// The LLVM Compiler Infrastructure
4//
5// This file is distributed under the University of Illinois Open Source
6// License. See LICENSE.TXT for details.
7//
8//===----------------------------------------------------------------------===//
9//
10// This file defines all libclang APIs related to walking comment AST.
11//
12//===----------------------------------------------------------------------===//
13
14#include "clang-c/Index.h"
Dmitri Gribenko5e4fe002012-07-20 21:34:34 +000015#include "CXComment.h"
Dmitri Gribenko740c0fb2012-08-07 17:54:38 +000016#include "CXCursor.h"
Chandler Carruthcc0694c2012-12-04 09:25:21 +000017#include "CXString.h"
Fariborz Jahanian9b7ab872012-12-18 23:02:59 +000018#include "SimpleFormatContext.h"
Dmitri Gribenkodcbc8ce2012-08-09 17:33:20 +000019#include "clang/AST/CommentCommandTraits.h"
Chandler Carruthcc0694c2012-12-04 09:25:21 +000020#include "clang/AST/CommentVisitor.h"
Dmitri Gribenko740c0fb2012-08-07 17:54:38 +000021#include "clang/AST/Decl.h"
Chandler Carruthcc0694c2012-12-04 09:25:21 +000022#include "clang/AST/PrettyPrinter.h"
Chandler Carruth44eb4f62013-01-02 10:28:36 +000023#include "clang/Format/Format.h"
24#include "clang/Lex/Lexer.h"
Fariborz Jahanian9b7ab872012-12-18 23:02:59 +000025#include "llvm/ADT/StringExtras.h"
Dmitri Gribenko740c0fb2012-08-07 17:54:38 +000026#include "llvm/ADT/StringSwitch.h"
Dmitri Gribenko5e4fe002012-07-20 21:34:34 +000027#include "llvm/Support/ErrorHandling.h"
Dmitri Gribenko4c6d7a22012-07-21 01:47:43 +000028#include "llvm/Support/raw_ostream.h"
Dmitri Gribenko5de4c062012-07-30 17:49:32 +000029#include <climits>
30
Dmitri Gribenko5e4fe002012-07-20 21:34:34 +000031using namespace clang;
Dmitri Gribenko5e4fe002012-07-20 21:34:34 +000032using namespace clang::comments;
33using namespace clang::cxcomment;
34
35extern "C" {
36
37enum CXCommentKind clang_Comment_getKind(CXComment CXC) {
38 const Comment *C = getASTNode(CXC);
39 if (!C)
40 return CXComment_Null;
41
42 switch (C->getCommentKind()) {
43 case Comment::NoCommentKind:
44 return CXComment_Null;
45
46 case Comment::TextCommentKind:
47 return CXComment_Text;
48
49 case Comment::InlineCommandCommentKind:
50 return CXComment_InlineCommand;
51
52 case Comment::HTMLStartTagCommentKind:
53 return CXComment_HTMLStartTag;
54
55 case Comment::HTMLEndTagCommentKind:
56 return CXComment_HTMLEndTag;
57
58 case Comment::ParagraphCommentKind:
59 return CXComment_Paragraph;
60
61 case Comment::BlockCommandCommentKind:
62 return CXComment_BlockCommand;
63
64 case Comment::ParamCommandCommentKind:
65 return CXComment_ParamCommand;
66
Dmitri Gribenko34df2202012-07-31 22:37:06 +000067 case Comment::TParamCommandCommentKind:
68 return CXComment_TParamCommand;
69
Dmitri Gribenko5e4fe002012-07-20 21:34:34 +000070 case Comment::VerbatimBlockCommentKind:
71 return CXComment_VerbatimBlockCommand;
72
73 case Comment::VerbatimBlockLineCommentKind:
74 return CXComment_VerbatimBlockLine;
75
76 case Comment::VerbatimLineCommentKind:
77 return CXComment_VerbatimLine;
78
79 case Comment::FullCommentKind:
80 return CXComment_FullComment;
81 }
82 llvm_unreachable("unknown CommentKind");
83}
84
85unsigned clang_Comment_getNumChildren(CXComment CXC) {
86 const Comment *C = getASTNode(CXC);
87 if (!C)
88 return 0;
89
90 return C->child_count();
91}
92
93CXComment clang_Comment_getChild(CXComment CXC, unsigned ChildIdx) {
94 const Comment *C = getASTNode(CXC);
95 if (!C || ChildIdx >= C->child_count())
Dmitri Gribenko7acbf002012-09-10 20:32:42 +000096 return createCXComment(NULL, NULL);
Dmitri Gribenko5e4fe002012-07-20 21:34:34 +000097
Dmitri Gribenko7acbf002012-09-10 20:32:42 +000098 return createCXComment(*(C->child_begin() + ChildIdx), CXC.TranslationUnit);
Dmitri Gribenko5e4fe002012-07-20 21:34:34 +000099}
100
101unsigned clang_Comment_isWhitespace(CXComment CXC) {
102 const Comment *C = getASTNode(CXC);
103 if (!C)
104 return false;
105
106 if (const TextComment *TC = dyn_cast<TextComment>(C))
107 return TC->isWhitespace();
108
109 if (const ParagraphComment *PC = dyn_cast<ParagraphComment>(C))
110 return PC->isWhitespace();
111
112 return false;
113}
114
115unsigned clang_InlineContentComment_hasTrailingNewline(CXComment CXC) {
116 const InlineContentComment *ICC = getASTNodeAs<InlineContentComment>(CXC);
117 if (!ICC)
118 return false;
119
120 return ICC->hasTrailingNewline();
121}
122
123CXString clang_TextComment_getText(CXComment CXC) {
124 const TextComment *TC = getASTNodeAs<TextComment>(CXC);
125 if (!TC)
Dmitri Gribenkof98dfba2013-02-01 14:13:32 +0000126 return cxstring::createNull();
Dmitri Gribenko5e4fe002012-07-20 21:34:34 +0000127
Dmitri Gribenko2f23e9c2013-02-02 02:19:29 +0000128 return cxstring::createRef(TC->getText());
Dmitri Gribenko5e4fe002012-07-20 21:34:34 +0000129}
130
131CXString clang_InlineCommandComment_getCommandName(CXComment CXC) {
132 const InlineCommandComment *ICC = getASTNodeAs<InlineCommandComment>(CXC);
133 if (!ICC)
Dmitri Gribenkof98dfba2013-02-01 14:13:32 +0000134 return cxstring::createNull();
Dmitri Gribenko5e4fe002012-07-20 21:34:34 +0000135
Dmitri Gribenko7acbf002012-09-10 20:32:42 +0000136 const CommandTraits &Traits = getCommandTraits(CXC);
Dmitri Gribenko2f23e9c2013-02-02 02:19:29 +0000137 return cxstring::createRef(ICC->getCommandName(Traits));
Dmitri Gribenko5e4fe002012-07-20 21:34:34 +0000138}
139
Dmitri Gribenkod73e4ce2012-07-23 16:43:01 +0000140enum CXCommentInlineCommandRenderKind
141clang_InlineCommandComment_getRenderKind(CXComment CXC) {
142 const InlineCommandComment *ICC = getASTNodeAs<InlineCommandComment>(CXC);
143 if (!ICC)
144 return CXCommentInlineCommandRenderKind_Normal;
145
146 switch (ICC->getRenderKind()) {
147 case InlineCommandComment::RenderNormal:
148 return CXCommentInlineCommandRenderKind_Normal;
149
150 case InlineCommandComment::RenderBold:
151 return CXCommentInlineCommandRenderKind_Bold;
152
153 case InlineCommandComment::RenderMonospaced:
154 return CXCommentInlineCommandRenderKind_Monospaced;
155
156 case InlineCommandComment::RenderEmphasized:
157 return CXCommentInlineCommandRenderKind_Emphasized;
158 }
159 llvm_unreachable("unknown InlineCommandComment::RenderKind");
160}
161
Dmitri Gribenko5e4fe002012-07-20 21:34:34 +0000162unsigned clang_InlineCommandComment_getNumArgs(CXComment CXC) {
163 const InlineCommandComment *ICC = getASTNodeAs<InlineCommandComment>(CXC);
164 if (!ICC)
165 return 0;
166
167 return ICC->getNumArgs();
168}
169
170CXString clang_InlineCommandComment_getArgText(CXComment CXC,
171 unsigned ArgIdx) {
172 const InlineCommandComment *ICC = getASTNodeAs<InlineCommandComment>(CXC);
173 if (!ICC || ArgIdx >= ICC->getNumArgs())
Dmitri Gribenkof98dfba2013-02-01 14:13:32 +0000174 return cxstring::createNull();
Dmitri Gribenko5e4fe002012-07-20 21:34:34 +0000175
Dmitri Gribenko2f23e9c2013-02-02 02:19:29 +0000176 return cxstring::createRef(ICC->getArgText(ArgIdx));
Dmitri Gribenko5e4fe002012-07-20 21:34:34 +0000177}
178
179CXString clang_HTMLTagComment_getTagName(CXComment CXC) {
180 const HTMLTagComment *HTC = getASTNodeAs<HTMLTagComment>(CXC);
181 if (!HTC)
Dmitri Gribenkof98dfba2013-02-01 14:13:32 +0000182 return cxstring::createNull();
Dmitri Gribenko5e4fe002012-07-20 21:34:34 +0000183
Dmitri Gribenko2f23e9c2013-02-02 02:19:29 +0000184 return cxstring::createRef(HTC->getTagName());
Dmitri Gribenko5e4fe002012-07-20 21:34:34 +0000185}
186
187unsigned clang_HTMLStartTagComment_isSelfClosing(CXComment CXC) {
188 const HTMLStartTagComment *HST = getASTNodeAs<HTMLStartTagComment>(CXC);
189 if (!HST)
190 return false;
191
192 return HST->isSelfClosing();
193}
194
195unsigned clang_HTMLStartTag_getNumAttrs(CXComment CXC) {
196 const HTMLStartTagComment *HST = getASTNodeAs<HTMLStartTagComment>(CXC);
197 if (!HST)
198 return 0;
199
200 return HST->getNumAttrs();
201}
202
203CXString clang_HTMLStartTag_getAttrName(CXComment CXC, unsigned AttrIdx) {
204 const HTMLStartTagComment *HST = getASTNodeAs<HTMLStartTagComment>(CXC);
205 if (!HST || AttrIdx >= HST->getNumAttrs())
Dmitri Gribenkof98dfba2013-02-01 14:13:32 +0000206 return cxstring::createNull();
Dmitri Gribenko5e4fe002012-07-20 21:34:34 +0000207
Dmitri Gribenko2f23e9c2013-02-02 02:19:29 +0000208 return cxstring::createRef(HST->getAttr(AttrIdx).Name);
Dmitri Gribenko5e4fe002012-07-20 21:34:34 +0000209}
210
211CXString clang_HTMLStartTag_getAttrValue(CXComment CXC, unsigned AttrIdx) {
212 const HTMLStartTagComment *HST = getASTNodeAs<HTMLStartTagComment>(CXC);
213 if (!HST || AttrIdx >= HST->getNumAttrs())
Dmitri Gribenkof98dfba2013-02-01 14:13:32 +0000214 return cxstring::createNull();
Dmitri Gribenko5e4fe002012-07-20 21:34:34 +0000215
Dmitri Gribenko2f23e9c2013-02-02 02:19:29 +0000216 return cxstring::createRef(HST->getAttr(AttrIdx).Value);
Dmitri Gribenko5e4fe002012-07-20 21:34:34 +0000217}
218
219CXString clang_BlockCommandComment_getCommandName(CXComment CXC) {
220 const BlockCommandComment *BCC = getASTNodeAs<BlockCommandComment>(CXC);
221 if (!BCC)
Dmitri Gribenkof98dfba2013-02-01 14:13:32 +0000222 return cxstring::createNull();
Dmitri Gribenko5e4fe002012-07-20 21:34:34 +0000223
Dmitri Gribenko7acbf002012-09-10 20:32:42 +0000224 const CommandTraits &Traits = getCommandTraits(CXC);
Dmitri Gribenko2f23e9c2013-02-02 02:19:29 +0000225 return cxstring::createRef(BCC->getCommandName(Traits));
Dmitri Gribenko5e4fe002012-07-20 21:34:34 +0000226}
227
228unsigned clang_BlockCommandComment_getNumArgs(CXComment CXC) {
229 const BlockCommandComment *BCC = getASTNodeAs<BlockCommandComment>(CXC);
230 if (!BCC)
231 return 0;
232
233 return BCC->getNumArgs();
234}
235
236CXString clang_BlockCommandComment_getArgText(CXComment CXC,
237 unsigned ArgIdx) {
238 const BlockCommandComment *BCC = getASTNodeAs<BlockCommandComment>(CXC);
239 if (!BCC || ArgIdx >= BCC->getNumArgs())
Dmitri Gribenkof98dfba2013-02-01 14:13:32 +0000240 return cxstring::createNull();
Dmitri Gribenko5e4fe002012-07-20 21:34:34 +0000241
Dmitri Gribenko2f23e9c2013-02-02 02:19:29 +0000242 return cxstring::createRef(BCC->getArgText(ArgIdx));
Dmitri Gribenko5e4fe002012-07-20 21:34:34 +0000243}
244
245CXComment clang_BlockCommandComment_getParagraph(CXComment CXC) {
246 const BlockCommandComment *BCC = getASTNodeAs<BlockCommandComment>(CXC);
247 if (!BCC)
Dmitri Gribenko7acbf002012-09-10 20:32:42 +0000248 return createCXComment(NULL, NULL);
Dmitri Gribenko5e4fe002012-07-20 21:34:34 +0000249
Dmitri Gribenko7acbf002012-09-10 20:32:42 +0000250 return createCXComment(BCC->getParagraph(), CXC.TranslationUnit);
Dmitri Gribenko5e4fe002012-07-20 21:34:34 +0000251}
252
253CXString clang_ParamCommandComment_getParamName(CXComment CXC) {
254 const ParamCommandComment *PCC = getASTNodeAs<ParamCommandComment>(CXC);
Dmitri Gribenko378458d2012-07-23 19:41:49 +0000255 if (!PCC || !PCC->hasParamName())
Dmitri Gribenkof98dfba2013-02-01 14:13:32 +0000256 return cxstring::createNull();
Dmitri Gribenko5e4fe002012-07-20 21:34:34 +0000257
Dmitri Gribenko2f23e9c2013-02-02 02:19:29 +0000258 return cxstring::createRef(PCC->getParamNameAsWritten());
Dmitri Gribenko5e4fe002012-07-20 21:34:34 +0000259}
260
261unsigned clang_ParamCommandComment_isParamIndexValid(CXComment CXC) {
262 const ParamCommandComment *PCC = getASTNodeAs<ParamCommandComment>(CXC);
263 if (!PCC)
264 return false;
265
266 return PCC->isParamIndexValid();
267}
268
269unsigned clang_ParamCommandComment_getParamIndex(CXComment CXC) {
270 const ParamCommandComment *PCC = getASTNodeAs<ParamCommandComment>(CXC);
Dmitri Gribenko02489eb2013-06-24 04:41:32 +0000271 if (!PCC || !PCC->isParamIndexValid() || PCC->isVarArgParam())
Dmitri Gribenko5e4fe002012-07-20 21:34:34 +0000272 return ParamCommandComment::InvalidParamIndex;
273
274 return PCC->getParamIndex();
275}
276
277unsigned clang_ParamCommandComment_isDirectionExplicit(CXComment CXC) {
278 const ParamCommandComment *PCC = getASTNodeAs<ParamCommandComment>(CXC);
279 if (!PCC)
280 return false;
281
282 return PCC->isDirectionExplicit();
283}
284
285enum CXCommentParamPassDirection clang_ParamCommandComment_getDirection(
286 CXComment CXC) {
287 const ParamCommandComment *PCC = getASTNodeAs<ParamCommandComment>(CXC);
288 if (!PCC)
289 return CXCommentParamPassDirection_In;
290
291 switch (PCC->getDirection()) {
292 case ParamCommandComment::In:
293 return CXCommentParamPassDirection_In;
294
295 case ParamCommandComment::Out:
296 return CXCommentParamPassDirection_Out;
297
298 case ParamCommandComment::InOut:
299 return CXCommentParamPassDirection_InOut;
300 }
301 llvm_unreachable("unknown ParamCommandComment::PassDirection");
302}
303
Dmitri Gribenko34df2202012-07-31 22:37:06 +0000304CXString clang_TParamCommandComment_getParamName(CXComment CXC) {
305 const TParamCommandComment *TPCC = getASTNodeAs<TParamCommandComment>(CXC);
306 if (!TPCC || !TPCC->hasParamName())
Dmitri Gribenkof98dfba2013-02-01 14:13:32 +0000307 return cxstring::createNull();
Dmitri Gribenko34df2202012-07-31 22:37:06 +0000308
Dmitri Gribenko2f23e9c2013-02-02 02:19:29 +0000309 return cxstring::createRef(TPCC->getParamNameAsWritten());
Dmitri Gribenko34df2202012-07-31 22:37:06 +0000310}
311
312unsigned clang_TParamCommandComment_isParamPositionValid(CXComment CXC) {
313 const TParamCommandComment *TPCC = getASTNodeAs<TParamCommandComment>(CXC);
314 if (!TPCC)
315 return false;
316
317 return TPCC->isPositionValid();
318}
319
320unsigned clang_TParamCommandComment_getDepth(CXComment CXC) {
321 const TParamCommandComment *TPCC = getASTNodeAs<TParamCommandComment>(CXC);
322 if (!TPCC || !TPCC->isPositionValid())
323 return 0;
324
325 return TPCC->getDepth();
326}
327
328unsigned clang_TParamCommandComment_getIndex(CXComment CXC, unsigned Depth) {
329 const TParamCommandComment *TPCC = getASTNodeAs<TParamCommandComment>(CXC);
330 if (!TPCC || !TPCC->isPositionValid() || Depth >= TPCC->getDepth())
331 return 0;
332
333 return TPCC->getIndex(Depth);
334}
335
Dmitri Gribenko5e4fe002012-07-20 21:34:34 +0000336CXString clang_VerbatimBlockLineComment_getText(CXComment CXC) {
337 const VerbatimBlockLineComment *VBL =
338 getASTNodeAs<VerbatimBlockLineComment>(CXC);
339 if (!VBL)
Dmitri Gribenkof98dfba2013-02-01 14:13:32 +0000340 return cxstring::createNull();
Dmitri Gribenko5e4fe002012-07-20 21:34:34 +0000341
Dmitri Gribenko2f23e9c2013-02-02 02:19:29 +0000342 return cxstring::createRef(VBL->getText());
Dmitri Gribenko5e4fe002012-07-20 21:34:34 +0000343}
344
345CXString clang_VerbatimLineComment_getText(CXComment CXC) {
346 const VerbatimLineComment *VLC = getASTNodeAs<VerbatimLineComment>(CXC);
347 if (!VLC)
Dmitri Gribenkof98dfba2013-02-01 14:13:32 +0000348 return cxstring::createNull();
Dmitri Gribenko5e4fe002012-07-20 21:34:34 +0000349
Dmitri Gribenko2f23e9c2013-02-02 02:19:29 +0000350 return cxstring::createRef(VLC->getText());
Dmitri Gribenko5e4fe002012-07-20 21:34:34 +0000351}
352
353} // end extern "C"
354
355//===----------------------------------------------------------------------===//
356// Helpers for converting comment AST to HTML.
357//===----------------------------------------------------------------------===//
358
359namespace {
360
Dmitri Gribenko02489eb2013-06-24 04:41:32 +0000361/// This comparison will sort parameters with valid index by index, then vararg
362/// parameters, and invalid (unresolved) parameters last.
Dmitri Gribenko5e4fe002012-07-20 21:34:34 +0000363class ParamCommandCommentCompareIndex {
364public:
365 bool operator()(const ParamCommandComment *LHS,
366 const ParamCommandComment *RHS) const {
Dmitri Gribenkoa260c032012-07-30 17:38:19 +0000367 unsigned LHSIndex = UINT_MAX;
368 unsigned RHSIndex = UINT_MAX;
Dmitri Gribenkoa260c032012-07-30 17:38:19 +0000369
Dmitri Gribenko02489eb2013-06-24 04:41:32 +0000370 if (LHS->isParamIndexValid()) {
371 if (LHS->isVarArgParam())
372 LHSIndex = UINT_MAX - 1;
373 else
374 LHSIndex = LHS->getParamIndex();
375 }
376 if (RHS->isParamIndexValid()) {
377 if (RHS->isVarArgParam())
378 RHSIndex = UINT_MAX - 1;
379 else
380 RHSIndex = RHS->getParamIndex();
381 }
Dmitri Gribenkoa260c032012-07-30 17:38:19 +0000382 return LHSIndex < RHSIndex;
Dmitri Gribenko5e4fe002012-07-20 21:34:34 +0000383 }
384};
385
Dmitri Gribenko34df2202012-07-31 22:37:06 +0000386/// This comparison will sort template parameters in the following order:
387/// \li real template parameters (depth = 1) in index order;
388/// \li all other names (depth > 1);
389/// \li unresolved names.
390class TParamCommandCommentComparePosition {
391public:
392 bool operator()(const TParamCommandComment *LHS,
393 const TParamCommandComment *RHS) const {
394 // Sort unresolved names last.
395 if (!LHS->isPositionValid())
396 return false;
397 if (!RHS->isPositionValid())
398 return true;
399
400 if (LHS->getDepth() > 1)
401 return false;
402 if (RHS->getDepth() > 1)
403 return true;
404
405 // Sort template parameters in index order.
406 if (LHS->getDepth() == 1 && RHS->getDepth() == 1)
407 return LHS->getIndex(0) < RHS->getIndex(0);
408
409 // Leave all other names in source order.
410 return true;
411 }
412};
413
Dmitri Gribenko3a770d02012-08-01 22:48:16 +0000414/// Separate parts of a FullComment.
415struct FullCommentParts {
416 /// Take a full comment apart and initialize members accordingly.
Dmitri Gribenko7acbf002012-09-10 20:32:42 +0000417 FullCommentParts(const FullComment *C,
418 const CommandTraits &Traits);
Dmitri Gribenko3a770d02012-08-01 22:48:16 +0000419
420 const BlockContentComment *Brief;
Fariborz Jahanian1a0cf802013-01-31 23:12:39 +0000421 const BlockContentComment *Headerfile;
Dmitri Gribenko3a770d02012-08-01 22:48:16 +0000422 const ParagraphComment *FirstParagraph;
Fariborz Jahanianfb6f6f62013-06-21 23:49:29 +0000423 SmallVector<const BlockCommandComment *, 4> Returns;
Dmitri Gribenko3a770d02012-08-01 22:48:16 +0000424 SmallVector<const ParamCommandComment *, 8> Params;
425 SmallVector<const TParamCommandComment *, 4> TParams;
426 SmallVector<const BlockContentComment *, 8> MiscBlocks;
427};
428
Dmitri Gribenko7acbf002012-09-10 20:32:42 +0000429FullCommentParts::FullCommentParts(const FullComment *C,
430 const CommandTraits &Traits) :
Fariborz Jahanianfb6f6f62013-06-21 23:49:29 +0000431 Brief(NULL), Headerfile(NULL), FirstParagraph(NULL) {
Dmitri Gribenko3a770d02012-08-01 22:48:16 +0000432 for (Comment::child_iterator I = C->child_begin(), E = C->child_end();
433 I != E; ++I) {
434 const Comment *Child = *I;
435 if (!Child)
436 continue;
437 switch (Child->getCommentKind()) {
438 case Comment::NoCommentKind:
439 continue;
440
441 case Comment::ParagraphCommentKind: {
442 const ParagraphComment *PC = cast<ParagraphComment>(Child);
443 if (PC->isWhitespace())
444 break;
445 if (!FirstParagraph)
446 FirstParagraph = PC;
447
448 MiscBlocks.push_back(PC);
449 break;
450 }
451
452 case Comment::BlockCommandCommentKind: {
453 const BlockCommandComment *BCC = cast<BlockCommandComment>(Child);
Dmitri Gribenko7acbf002012-09-10 20:32:42 +0000454 const CommandInfo *Info = Traits.getCommandInfo(BCC->getCommandID());
455 if (!Brief && Info->IsBriefCommand) {
Dmitri Gribenko3a770d02012-08-01 22:48:16 +0000456 Brief = BCC;
457 break;
458 }
Fariborz Jahanian1a0cf802013-01-31 23:12:39 +0000459 if (!Headerfile && Info->IsHeaderfileCommand) {
460 Headerfile = BCC;
461 break;
462 }
Fariborz Jahanianfb6f6f62013-06-21 23:49:29 +0000463 if (Info->IsReturnsCommand) {
464 Returns.push_back(BCC);
Dmitri Gribenko3a770d02012-08-01 22:48:16 +0000465 break;
466 }
467 MiscBlocks.push_back(BCC);
468 break;
469 }
470
471 case Comment::ParamCommandCommentKind: {
472 const ParamCommandComment *PCC = cast<ParamCommandComment>(Child);
473 if (!PCC->hasParamName())
474 break;
475
476 if (!PCC->isDirectionExplicit() && !PCC->hasNonWhitespaceParagraph())
477 break;
478
479 Params.push_back(PCC);
480 break;
481 }
482
483 case Comment::TParamCommandCommentKind: {
484 const TParamCommandComment *TPCC = cast<TParamCommandComment>(Child);
485 if (!TPCC->hasParamName())
486 break;
487
488 if (!TPCC->hasNonWhitespaceParagraph())
489 break;
490
491 TParams.push_back(TPCC);
492 break;
493 }
494
495 case Comment::VerbatimBlockCommentKind:
Dmitri Gribenko3a770d02012-08-01 22:48:16 +0000496 MiscBlocks.push_back(cast<BlockCommandComment>(Child));
497 break;
498
Dmitri Gribenkoba7aca32012-08-09 18:20:29 +0000499 case Comment::VerbatimLineCommentKind: {
500 const VerbatimLineComment *VLC = cast<VerbatimLineComment>(Child);
Dmitri Gribenko7acbf002012-09-10 20:32:42 +0000501 const CommandInfo *Info = Traits.getCommandInfo(VLC->getCommandID());
502 if (!Info->IsDeclarationCommand)
Dmitri Gribenkoba7aca32012-08-09 18:20:29 +0000503 MiscBlocks.push_back(VLC);
504 break;
505 }
506
Dmitri Gribenko3a770d02012-08-01 22:48:16 +0000507 case Comment::TextCommentKind:
508 case Comment::InlineCommandCommentKind:
509 case Comment::HTMLStartTagCommentKind:
510 case Comment::HTMLEndTagCommentKind:
511 case Comment::VerbatimBlockLineCommentKind:
512 case Comment::FullCommentKind:
513 llvm_unreachable("AST node of this kind can't be a child of "
514 "a FullComment");
515 }
516 }
517
518 // Sort params in order they are declared in the function prototype.
519 // Unresolved parameters are put at the end of the list in the same order
520 // they were seen in the comment.
521 std::stable_sort(Params.begin(), Params.end(),
522 ParamCommandCommentCompareIndex());
523
524 std::stable_sort(TParams.begin(), TParams.end(),
525 TParamCommandCommentComparePosition());
526}
527
528void PrintHTMLStartTagComment(const HTMLStartTagComment *C,
529 llvm::raw_svector_ostream &Result) {
530 Result << "<" << C->getTagName();
531
532 if (C->getNumAttrs() != 0) {
533 for (unsigned i = 0, e = C->getNumAttrs(); i != e; i++) {
534 Result << " ";
535 const HTMLStartTagComment::Attribute &Attr = C->getAttr(i);
536 Result << Attr.Name;
537 if (!Attr.Value.empty())
538 Result << "=\"" << Attr.Value << "\"";
539 }
540 }
541
542 if (!C->isSelfClosing())
543 Result << ">";
544 else
545 Result << "/>";
546}
547
Dmitri Gribenko5e4fe002012-07-20 21:34:34 +0000548class CommentASTToHTMLConverter :
549 public ConstCommentVisitor<CommentASTToHTMLConverter> {
550public:
Dmitri Gribenko4c6d7a22012-07-21 01:47:43 +0000551 /// \param Str accumulator for HTML.
Dmitri Gribenko923f3052012-10-19 16:51:38 +0000552 CommentASTToHTMLConverter(const FullComment *FC,
Fariborz Jahanian1c883b92012-10-10 18:34:52 +0000553 SmallVectorImpl<char> &Str,
Dmitri Gribenko7acbf002012-09-10 20:32:42 +0000554 const CommandTraits &Traits) :
Fariborz Jahanian1c883b92012-10-10 18:34:52 +0000555 FC(FC), Result(Str), Traits(Traits)
Dmitri Gribenko7acbf002012-09-10 20:32:42 +0000556 { }
Dmitri Gribenko5e4fe002012-07-20 21:34:34 +0000557
558 // Inline content.
559 void visitTextComment(const TextComment *C);
560 void visitInlineCommandComment(const InlineCommandComment *C);
561 void visitHTMLStartTagComment(const HTMLStartTagComment *C);
562 void visitHTMLEndTagComment(const HTMLEndTagComment *C);
563
564 // Block content.
565 void visitParagraphComment(const ParagraphComment *C);
566 void visitBlockCommandComment(const BlockCommandComment *C);
567 void visitParamCommandComment(const ParamCommandComment *C);
Dmitri Gribenko34df2202012-07-31 22:37:06 +0000568 void visitTParamCommandComment(const TParamCommandComment *C);
Dmitri Gribenko5e4fe002012-07-20 21:34:34 +0000569 void visitVerbatimBlockComment(const VerbatimBlockComment *C);
570 void visitVerbatimBlockLineComment(const VerbatimBlockLineComment *C);
571 void visitVerbatimLineComment(const VerbatimLineComment *C);
572
573 void visitFullComment(const FullComment *C);
574
575 // Helpers.
576
577 /// Convert a paragraph that is not a block by itself (an argument to some
578 /// command).
579 void visitNonStandaloneParagraphComment(const ParagraphComment *C);
580
581 void appendToResultWithHTMLEscaping(StringRef S);
582
Dmitri Gribenko5e4fe002012-07-20 21:34:34 +0000583private:
Dmitri Gribenko923f3052012-10-19 16:51:38 +0000584 const FullComment *FC;
Dmitri Gribenko4c6d7a22012-07-21 01:47:43 +0000585 /// Output stream for HTML.
586 llvm::raw_svector_ostream Result;
Dmitri Gribenko7acbf002012-09-10 20:32:42 +0000587
588 const CommandTraits &Traits;
Dmitri Gribenko5e4fe002012-07-20 21:34:34 +0000589};
590} // end unnamed namespace
591
592void CommentASTToHTMLConverter::visitTextComment(const TextComment *C) {
593 appendToResultWithHTMLEscaping(C->getText());
594}
595
596void CommentASTToHTMLConverter::visitInlineCommandComment(
597 const InlineCommandComment *C) {
Dmitri Gribenkod73e4ce2012-07-23 16:43:01 +0000598 // Nothing to render if no arguments supplied.
599 if (C->getNumArgs() == 0)
600 return;
Dmitri Gribenko5e4fe002012-07-20 21:34:34 +0000601
Dmitri Gribenkod73e4ce2012-07-23 16:43:01 +0000602 // Nothing to render if argument is empty.
603 StringRef Arg0 = C->getArgText(0);
604 if (Arg0.empty())
605 return;
606
607 switch (C->getRenderKind()) {
608 case InlineCommandComment::RenderNormal:
Dmitri Gribenko7c0456f2012-08-01 00:21:12 +0000609 for (unsigned i = 0, e = C->getNumArgs(); i != e; ++i) {
610 appendToResultWithHTMLEscaping(C->getArgText(i));
611 Result << " ";
612 }
Dmitri Gribenkod73e4ce2012-07-23 16:43:01 +0000613 return;
614
615 case InlineCommandComment::RenderBold:
616 assert(C->getNumArgs() == 1);
Dmitri Gribenko7c0456f2012-08-01 00:21:12 +0000617 Result << "<b>";
618 appendToResultWithHTMLEscaping(Arg0);
619 Result << "</b>";
Dmitri Gribenko5e4fe002012-07-20 21:34:34 +0000620 return;
Dmitri Gribenkod73e4ce2012-07-23 16:43:01 +0000621 case InlineCommandComment::RenderMonospaced:
622 assert(C->getNumArgs() == 1);
Dmitri Gribenko7c0456f2012-08-01 00:21:12 +0000623 Result << "<tt>";
624 appendToResultWithHTMLEscaping(Arg0);
625 Result<< "</tt>";
Dmitri Gribenko5e4fe002012-07-20 21:34:34 +0000626 return;
Dmitri Gribenkod73e4ce2012-07-23 16:43:01 +0000627 case InlineCommandComment::RenderEmphasized:
628 assert(C->getNumArgs() == 1);
Dmitri Gribenko7c0456f2012-08-01 00:21:12 +0000629 Result << "<em>";
630 appendToResultWithHTMLEscaping(Arg0);
631 Result << "</em>";
Dmitri Gribenko5e4fe002012-07-20 21:34:34 +0000632 return;
633 }
Dmitri Gribenko5e4fe002012-07-20 21:34:34 +0000634}
635
636void CommentASTToHTMLConverter::visitHTMLStartTagComment(
637 const HTMLStartTagComment *C) {
Dmitri Gribenko3a770d02012-08-01 22:48:16 +0000638 PrintHTMLStartTagComment(C, Result);
Dmitri Gribenko5e4fe002012-07-20 21:34:34 +0000639}
640
641void CommentASTToHTMLConverter::visitHTMLEndTagComment(
642 const HTMLEndTagComment *C) {
Dmitri Gribenko4c6d7a22012-07-21 01:47:43 +0000643 Result << "</" << C->getTagName() << ">";
Dmitri Gribenko5e4fe002012-07-20 21:34:34 +0000644}
645
646void CommentASTToHTMLConverter::visitParagraphComment(
647 const ParagraphComment *C) {
648 if (C->isWhitespace())
649 return;
650
Dmitri Gribenko4c6d7a22012-07-21 01:47:43 +0000651 Result << "<p>";
Dmitri Gribenko5e4fe002012-07-20 21:34:34 +0000652 for (Comment::child_iterator I = C->child_begin(), E = C->child_end();
653 I != E; ++I) {
654 visit(*I);
655 }
Dmitri Gribenko4c6d7a22012-07-21 01:47:43 +0000656 Result << "</p>";
Dmitri Gribenko5e4fe002012-07-20 21:34:34 +0000657}
658
659void CommentASTToHTMLConverter::visitBlockCommandComment(
660 const BlockCommandComment *C) {
Dmitri Gribenko7acbf002012-09-10 20:32:42 +0000661 const CommandInfo *Info = Traits.getCommandInfo(C->getCommandID());
662 if (Info->IsBriefCommand) {
Dmitri Gribenko4c6d7a22012-07-21 01:47:43 +0000663 Result << "<p class=\"para-brief\">";
Dmitri Gribenko5e4fe002012-07-20 21:34:34 +0000664 visitNonStandaloneParagraphComment(C->getParagraph());
Dmitri Gribenko4c6d7a22012-07-21 01:47:43 +0000665 Result << "</p>";
Dmitri Gribenko5e4fe002012-07-20 21:34:34 +0000666 return;
667 }
Dmitri Gribenko7acbf002012-09-10 20:32:42 +0000668 if (Info->IsReturnsCommand) {
Dmitri Gribenko4c6d7a22012-07-21 01:47:43 +0000669 Result << "<p class=\"para-returns\">"
670 "<span class=\"word-returns\">Returns</span> ";
Dmitri Gribenko5e4fe002012-07-20 21:34:34 +0000671 visitNonStandaloneParagraphComment(C->getParagraph());
Dmitri Gribenko4c6d7a22012-07-21 01:47:43 +0000672 Result << "</p>";
Dmitri Gribenko5e4fe002012-07-20 21:34:34 +0000673 return;
674 }
675 // We don't know anything about this command. Just render the paragraph.
676 visit(C->getParagraph());
677}
678
679void CommentASTToHTMLConverter::visitParamCommandComment(
680 const ParamCommandComment *C) {
Dmitri Gribenko4c6d7a22012-07-21 01:47:43 +0000681 if (C->isParamIndexValid()) {
Dmitri Gribenko02489eb2013-06-24 04:41:32 +0000682 if (C->isVarArgParam()) {
683 Result << "<dt class=\"param-name-index-vararg\">";
684 appendToResultWithHTMLEscaping(C->getParamNameAsWritten());
685 } else {
686 Result << "<dt class=\"param-name-index-"
687 << C->getParamIndex()
688 << "\">";
689 appendToResultWithHTMLEscaping(C->getParamName(FC));
690 }
Fariborz Jahanian9d2f1e72012-10-18 21:42:42 +0000691 } else {
Dmitri Gribenko4c6d7a22012-07-21 01:47:43 +0000692 Result << "<dt class=\"param-name-index-invalid\">";
Fariborz Jahanian9d2f1e72012-10-18 21:42:42 +0000693 appendToResultWithHTMLEscaping(C->getParamNameAsWritten());
694 }
Dmitri Gribenko7c0456f2012-08-01 00:21:12 +0000695 Result << "</dt>";
Dmitri Gribenko4c6d7a22012-07-21 01:47:43 +0000696
697 if (C->isParamIndexValid()) {
Dmitri Gribenko02489eb2013-06-24 04:41:32 +0000698 if (C->isVarArgParam())
699 Result << "<dd class=\"param-descr-index-vararg\">";
700 else
701 Result << "<dd class=\"param-descr-index-"
702 << C->getParamIndex()
703 << "\">";
Dmitri Gribenko4c6d7a22012-07-21 01:47:43 +0000704 } else
705 Result << "<dd class=\"param-descr-index-invalid\">";
706
Dmitri Gribenko5e4fe002012-07-20 21:34:34 +0000707 visitNonStandaloneParagraphComment(C->getParagraph());
Dmitri Gribenko4c6d7a22012-07-21 01:47:43 +0000708 Result << "</dd>";
Dmitri Gribenko5e4fe002012-07-20 21:34:34 +0000709}
710
Dmitri Gribenko34df2202012-07-31 22:37:06 +0000711void CommentASTToHTMLConverter::visitTParamCommandComment(
712 const TParamCommandComment *C) {
713 if (C->isPositionValid()) {
714 if (C->getDepth() == 1)
Dmitri Gribenko58e41312012-08-01 23:47:30 +0000715 Result << "<dt class=\"tparam-name-index-"
Dmitri Gribenko34df2202012-07-31 22:37:06 +0000716 << C->getIndex(0)
717 << "\">";
718 else
Dmitri Gribenko58e41312012-08-01 23:47:30 +0000719 Result << "<dt class=\"tparam-name-index-other\">";
Fariborz Jahanian9d2f1e72012-10-18 21:42:42 +0000720 appendToResultWithHTMLEscaping(C->getParamName(FC));
721 } else {
Dmitri Gribenko34df2202012-07-31 22:37:06 +0000722 Result << "<dt class=\"tparam-name-index-invalid\">";
Fariborz Jahanian9d2f1e72012-10-18 21:42:42 +0000723 appendToResultWithHTMLEscaping(C->getParamNameAsWritten());
724 }
725
Dmitri Gribenko7c0456f2012-08-01 00:21:12 +0000726 Result << "</dt>";
Dmitri Gribenko34df2202012-07-31 22:37:06 +0000727
728 if (C->isPositionValid()) {
729 if (C->getDepth() == 1)
730 Result << "<dd class=\"tparam-descr-index-"
731 << C->getIndex(0)
732 << "\">";
733 else
734 Result << "<dd class=\"tparam-descr-index-other\">";
735 } else
736 Result << "<dd class=\"tparam-descr-index-invalid\">";
737
738 visitNonStandaloneParagraphComment(C->getParagraph());
739 Result << "</dd>";
740}
741
Dmitri Gribenko5e4fe002012-07-20 21:34:34 +0000742void CommentASTToHTMLConverter::visitVerbatimBlockComment(
743 const VerbatimBlockComment *C) {
744 unsigned NumLines = C->getNumLines();
745 if (NumLines == 0)
746 return;
747
Dmitri Gribenko4c6d7a22012-07-21 01:47:43 +0000748 Result << "<pre>";
Dmitri Gribenko5e4fe002012-07-20 21:34:34 +0000749 for (unsigned i = 0; i != NumLines; ++i) {
750 appendToResultWithHTMLEscaping(C->getText(i));
751 if (i + 1 != NumLines)
Dmitri Gribenko4c6d7a22012-07-21 01:47:43 +0000752 Result << '\n';
Dmitri Gribenko5e4fe002012-07-20 21:34:34 +0000753 }
Dmitri Gribenko4c6d7a22012-07-21 01:47:43 +0000754 Result << "</pre>";
Dmitri Gribenko5e4fe002012-07-20 21:34:34 +0000755}
756
757void CommentASTToHTMLConverter::visitVerbatimBlockLineComment(
758 const VerbatimBlockLineComment *C) {
759 llvm_unreachable("should not see this AST node");
760}
761
762void CommentASTToHTMLConverter::visitVerbatimLineComment(
763 const VerbatimLineComment *C) {
Dmitri Gribenko4c6d7a22012-07-21 01:47:43 +0000764 Result << "<pre>";
Dmitri Gribenko5e4fe002012-07-20 21:34:34 +0000765 appendToResultWithHTMLEscaping(C->getText());
Dmitri Gribenko4c6d7a22012-07-21 01:47:43 +0000766 Result << "</pre>";
Dmitri Gribenko5e4fe002012-07-20 21:34:34 +0000767}
768
769void CommentASTToHTMLConverter::visitFullComment(const FullComment *C) {
Dmitri Gribenko7acbf002012-09-10 20:32:42 +0000770 FullCommentParts Parts(C, Traits);
Dmitri Gribenko34df2202012-07-31 22:37:06 +0000771
Dmitri Gribenko5e4fe002012-07-20 21:34:34 +0000772 bool FirstParagraphIsBrief = false;
Fariborz Jahanian1a0cf802013-01-31 23:12:39 +0000773 if (Parts.Headerfile)
774 visit(Parts.Headerfile);
Dmitri Gribenko3a770d02012-08-01 22:48:16 +0000775 if (Parts.Brief)
776 visit(Parts.Brief);
777 else if (Parts.FirstParagraph) {
Dmitri Gribenko4c6d7a22012-07-21 01:47:43 +0000778 Result << "<p class=\"para-brief\">";
Dmitri Gribenko3a770d02012-08-01 22:48:16 +0000779 visitNonStandaloneParagraphComment(Parts.FirstParagraph);
Dmitri Gribenko4c6d7a22012-07-21 01:47:43 +0000780 Result << "</p>";
Dmitri Gribenko5e4fe002012-07-20 21:34:34 +0000781 FirstParagraphIsBrief = true;
782 }
783
Dmitri Gribenko3a770d02012-08-01 22:48:16 +0000784 for (unsigned i = 0, e = Parts.MiscBlocks.size(); i != e; ++i) {
785 const Comment *C = Parts.MiscBlocks[i];
786 if (FirstParagraphIsBrief && C == Parts.FirstParagraph)
Dmitri Gribenko5e4fe002012-07-20 21:34:34 +0000787 continue;
788 visit(C);
789 }
790
Dmitri Gribenko3a770d02012-08-01 22:48:16 +0000791 if (Parts.TParams.size() != 0) {
Dmitri Gribenko34df2202012-07-31 22:37:06 +0000792 Result << "<dl>";
Dmitri Gribenko3a770d02012-08-01 22:48:16 +0000793 for (unsigned i = 0, e = Parts.TParams.size(); i != e; ++i)
794 visit(Parts.TParams[i]);
Dmitri Gribenko34df2202012-07-31 22:37:06 +0000795 Result << "</dl>";
796 }
797
Dmitri Gribenko3a770d02012-08-01 22:48:16 +0000798 if (Parts.Params.size() != 0) {
Dmitri Gribenko4c6d7a22012-07-21 01:47:43 +0000799 Result << "<dl>";
Dmitri Gribenko3a770d02012-08-01 22:48:16 +0000800 for (unsigned i = 0, e = Parts.Params.size(); i != e; ++i)
801 visit(Parts.Params[i]);
Dmitri Gribenko4c6d7a22012-07-21 01:47:43 +0000802 Result << "</dl>";
Dmitri Gribenko5e4fe002012-07-20 21:34:34 +0000803 }
804
Fariborz Jahanianfb6f6f62013-06-21 23:49:29 +0000805 if (Parts.Returns.size() != 0) {
Dmitri Gribenkod6662932013-06-22 23:03:37 +0000806 Result << "<div class=\"result-discussion\">";
Fariborz Jahanianfb6f6f62013-06-21 23:49:29 +0000807 for (unsigned i = 0, e = Parts.Returns.size(); i != e; ++i)
808 visit(Parts.Returns[i]);
Dmitri Gribenkod6662932013-06-22 23:03:37 +0000809 Result << "</div>";
Fariborz Jahanianfb6f6f62013-06-21 23:49:29 +0000810 }
Dmitri Gribenko4c6d7a22012-07-21 01:47:43 +0000811
812 Result.flush();
Dmitri Gribenko5e4fe002012-07-20 21:34:34 +0000813}
814
815void CommentASTToHTMLConverter::visitNonStandaloneParagraphComment(
816 const ParagraphComment *C) {
817 if (!C)
818 return;
819
820 for (Comment::child_iterator I = C->child_begin(), E = C->child_end();
821 I != E; ++I) {
822 visit(*I);
823 }
824}
825
826void CommentASTToHTMLConverter::appendToResultWithHTMLEscaping(StringRef S) {
Dmitri Gribenko5e4fe002012-07-20 21:34:34 +0000827 for (StringRef::iterator I = S.begin(), E = S.end(); I != E; ++I) {
828 const char C = *I;
829 switch (C) {
830 case '&':
Dmitri Gribenko4c6d7a22012-07-21 01:47:43 +0000831 Result << "&amp;";
Dmitri Gribenko5e4fe002012-07-20 21:34:34 +0000832 break;
833 case '<':
Dmitri Gribenko4c6d7a22012-07-21 01:47:43 +0000834 Result << "&lt;";
Dmitri Gribenko5e4fe002012-07-20 21:34:34 +0000835 break;
836 case '>':
Dmitri Gribenko4c6d7a22012-07-21 01:47:43 +0000837 Result << "&gt;";
Dmitri Gribenko5e4fe002012-07-20 21:34:34 +0000838 break;
839 case '"':
Dmitri Gribenko4c6d7a22012-07-21 01:47:43 +0000840 Result << "&quot;";
Dmitri Gribenko5e4fe002012-07-20 21:34:34 +0000841 break;
842 case '\'':
Dmitri Gribenko4c6d7a22012-07-21 01:47:43 +0000843 Result << "&#39;";
Dmitri Gribenko5e4fe002012-07-20 21:34:34 +0000844 break;
845 case '/':
Dmitri Gribenko4c6d7a22012-07-21 01:47:43 +0000846 Result << "&#47;";
Dmitri Gribenko5e4fe002012-07-20 21:34:34 +0000847 break;
848 default:
Dmitri Gribenko4c6d7a22012-07-21 01:47:43 +0000849 Result << C;
Dmitri Gribenko5e4fe002012-07-20 21:34:34 +0000850 break;
851 }
852 }
853}
854
855extern "C" {
856
857CXString clang_HTMLTagComment_getAsString(CXComment CXC) {
858 const HTMLTagComment *HTC = getASTNodeAs<HTMLTagComment>(CXC);
859 if (!HTC)
Dmitri Gribenkof98dfba2013-02-01 14:13:32 +0000860 return cxstring::createNull();
Dmitri Gribenko5e4fe002012-07-20 21:34:34 +0000861
Dmitri Gribenko4c6d7a22012-07-21 01:47:43 +0000862 SmallString<128> HTML;
Fariborz Jahanian1c883b92012-10-10 18:34:52 +0000863 CommentASTToHTMLConverter Converter(0, HTML, getCommandTraits(CXC));
Dmitri Gribenko5e4fe002012-07-20 21:34:34 +0000864 Converter.visit(HTC);
Dmitri Gribenko2f23e9c2013-02-02 02:19:29 +0000865 return cxstring::createDup(HTML.str());
Dmitri Gribenko5e4fe002012-07-20 21:34:34 +0000866}
867
868CXString clang_FullComment_getAsHTML(CXComment CXC) {
869 const FullComment *FC = getASTNodeAs<FullComment>(CXC);
870 if (!FC)
Dmitri Gribenkof98dfba2013-02-01 14:13:32 +0000871 return cxstring::createNull();
Dmitri Gribenko5e4fe002012-07-20 21:34:34 +0000872
Dmitri Gribenko4c6d7a22012-07-21 01:47:43 +0000873 SmallString<1024> HTML;
Dmitri Gribenko923f3052012-10-19 16:51:38 +0000874 CommentASTToHTMLConverter Converter(FC, HTML, getCommandTraits(CXC));
Dmitri Gribenko5e4fe002012-07-20 21:34:34 +0000875 Converter.visit(FC);
Dmitri Gribenko2f23e9c2013-02-02 02:19:29 +0000876 return cxstring::createDup(HTML.str());
Dmitri Gribenko5e4fe002012-07-20 21:34:34 +0000877}
878
879} // end extern "C"
880
Dmitri Gribenko740c0fb2012-08-07 17:54:38 +0000881namespace {
882class CommentASTToXMLConverter :
883 public ConstCommentVisitor<CommentASTToXMLConverter> {
884public:
885 /// \param Str accumulator for XML.
Dmitri Gribenko923f3052012-10-19 16:51:38 +0000886 CommentASTToXMLConverter(const FullComment *FC,
Fariborz Jahanian1c883b92012-10-10 18:34:52 +0000887 SmallVectorImpl<char> &Str,
Dmitri Gribenko7acbf002012-09-10 20:32:42 +0000888 const CommandTraits &Traits,
Fariborz Jahanian9b7ab872012-12-18 23:02:59 +0000889 const SourceManager &SM,
Fariborz Jahanianb67908a2012-12-19 00:01:48 +0000890 SimpleFormatContext &SFC,
Fariborz Jahanian9b7ab872012-12-18 23:02:59 +0000891 unsigned FUID) :
892 FC(FC), Result(Str), Traits(Traits), SM(SM),
Fariborz Jahanianb67908a2012-12-19 00:01:48 +0000893 FormatRewriterContext(SFC),
Fariborz Jahanian9b7ab872012-12-18 23:02:59 +0000894 FormatInMemoryUniqueId(FUID) { }
Dmitri Gribenko740c0fb2012-08-07 17:54:38 +0000895
896 // Inline content.
897 void visitTextComment(const TextComment *C);
898 void visitInlineCommandComment(const InlineCommandComment *C);
899 void visitHTMLStartTagComment(const HTMLStartTagComment *C);
900 void visitHTMLEndTagComment(const HTMLEndTagComment *C);
901
902 // Block content.
903 void visitParagraphComment(const ParagraphComment *C);
Dmitri Gribenko2e72dd42013-02-01 20:23:57 +0000904
905 void appendParagraphCommentWithKind(const ParagraphComment *C,
906 StringRef Kind);
907
Dmitri Gribenko740c0fb2012-08-07 17:54:38 +0000908 void visitBlockCommandComment(const BlockCommandComment *C);
909 void visitParamCommandComment(const ParamCommandComment *C);
910 void visitTParamCommandComment(const TParamCommandComment *C);
911 void visitVerbatimBlockComment(const VerbatimBlockComment *C);
912 void visitVerbatimBlockLineComment(const VerbatimBlockLineComment *C);
913 void visitVerbatimLineComment(const VerbatimLineComment *C);
914
915 void visitFullComment(const FullComment *C);
916
917 // Helpers.
918 void appendToResultWithXMLEscaping(StringRef S);
Dmitri Gribenko2e72dd42013-02-01 20:23:57 +0000919
Fariborz Jahanianb67908a2012-12-19 00:01:48 +0000920 void formatTextOfDeclaration(const DeclInfo *DI,
921 SmallString<128> &Declaration);
Dmitri Gribenko740c0fb2012-08-07 17:54:38 +0000922
923private:
Dmitri Gribenko923f3052012-10-19 16:51:38 +0000924 const FullComment *FC;
925
Dmitri Gribenko740c0fb2012-08-07 17:54:38 +0000926 /// Output stream for XML.
927 llvm::raw_svector_ostream Result;
Dmitri Gribenko7acbf002012-09-10 20:32:42 +0000928
929 const CommandTraits &Traits;
930 const SourceManager &SM;
Fariborz Jahanian9b7ab872012-12-18 23:02:59 +0000931 SimpleFormatContext &FormatRewriterContext;
932 unsigned FormatInMemoryUniqueId;
Dmitri Gribenko740c0fb2012-08-07 17:54:38 +0000933};
Dmitri Gribenko1c1395e2012-10-25 18:28:26 +0000934
935void getSourceTextOfDeclaration(const DeclInfo *ThisDecl,
936 SmallVectorImpl<char> &Str) {
937 ASTContext &Context = ThisDecl->CurrentDecl->getASTContext();
938 const LangOptions &LangOpts = Context.getLangOpts();
939 llvm::raw_svector_ostream OS(Str);
940 PrintingPolicy PPolicy(LangOpts);
Fariborz Jahanian0389e522012-12-19 23:36:00 +0000941 PPolicy.PolishForDeclaration = true;
Dmitri Gribenko1c1395e2012-10-25 18:28:26 +0000942 PPolicy.TerseOutput = true;
943 ThisDecl->CurrentDecl->print(OS, PPolicy,
Dmitri Gribenkoa9cc2492013-01-07 18:45:48 +0000944 /*Indentation*/0, /*PrintInstantiation*/false);
Dmitri Gribenko1c1395e2012-10-25 18:28:26 +0000945}
Fariborz Jahanian9b7ab872012-12-18 23:02:59 +0000946
Fariborz Jahanianb67908a2012-12-19 00:01:48 +0000947void CommentASTToXMLConverter::formatTextOfDeclaration(
948 const DeclInfo *DI,
949 SmallString<128> &Declaration) {
Fariborz Jahanianb67908a2012-12-19 00:01:48 +0000950 // FIXME. formatting API expects null terminated input string.
951 // There might be more efficient way of doing this.
952 std::string StringDecl = Declaration.str();
Fariborz Jahanian9b7ab872012-12-18 23:02:59 +0000953
Fariborz Jahanianb67908a2012-12-19 00:01:48 +0000954 // Formatter specific code.
955 // Form a unique in memory buffer name.
Dmitri Gribenkof8579502013-01-12 19:30:44 +0000956 SmallString<128> filename;
Fariborz Jahanianb67908a2012-12-19 00:01:48 +0000957 filename += "xmldecl";
958 filename += llvm::utostr(FormatInMemoryUniqueId);
959 filename += ".xd";
960 FileID ID = FormatRewriterContext.createInMemoryFile(filename, StringDecl);
961 SourceLocation Start =
962 FormatRewriterContext.Sources.getLocForStartOfFile(ID).getLocWithOffset(0);
963 unsigned Length = Declaration.size();
Fariborz Jahanian9b7ab872012-12-18 23:02:59 +0000964
Fariborz Jahanianb67908a2012-12-19 00:01:48 +0000965 std::vector<CharSourceRange>
966 Ranges(1, CharSourceRange::getCharRange(Start, Start.getLocWithOffset(Length)));
967 ASTContext &Context = DI->CurrentDecl->getASTContext();
968 const LangOptions &LangOpts = Context.getLangOpts();
969 Lexer Lex(ID, FormatRewriterContext.Sources.getBuffer(ID),
970 FormatRewriterContext.Sources, LangOpts);
971 tooling::Replacements Replace =
972 reformat(format::getLLVMStyle(), Lex, FormatRewriterContext.Sources, Ranges);
973 applyAllReplacements(Replace, FormatRewriterContext.Rewrite);
974 Declaration = FormatRewriterContext.getRewrittenText(ID);
Fariborz Jahanian9b7ab872012-12-18 23:02:59 +0000975}
Dmitri Gribenko1c1395e2012-10-25 18:28:26 +0000976
Dmitri Gribenko740c0fb2012-08-07 17:54:38 +0000977} // end unnamed namespace
978
979void CommentASTToXMLConverter::visitTextComment(const TextComment *C) {
980 appendToResultWithXMLEscaping(C->getText());
981}
982
983void CommentASTToXMLConverter::visitInlineCommandComment(const InlineCommandComment *C) {
984 // Nothing to render if no arguments supplied.
985 if (C->getNumArgs() == 0)
986 return;
987
988 // Nothing to render if argument is empty.
989 StringRef Arg0 = C->getArgText(0);
990 if (Arg0.empty())
991 return;
992
993 switch (C->getRenderKind()) {
994 case InlineCommandComment::RenderNormal:
995 for (unsigned i = 0, e = C->getNumArgs(); i != e; ++i) {
996 appendToResultWithXMLEscaping(C->getArgText(i));
997 Result << " ";
998 }
999 return;
1000 case InlineCommandComment::RenderBold:
1001 assert(C->getNumArgs() == 1);
1002 Result << "<bold>";
1003 appendToResultWithXMLEscaping(Arg0);
1004 Result << "</bold>";
1005 return;
1006 case InlineCommandComment::RenderMonospaced:
1007 assert(C->getNumArgs() == 1);
1008 Result << "<monospaced>";
1009 appendToResultWithXMLEscaping(Arg0);
1010 Result << "</monospaced>";
1011 return;
1012 case InlineCommandComment::RenderEmphasized:
1013 assert(C->getNumArgs() == 1);
1014 Result << "<emphasized>";
1015 appendToResultWithXMLEscaping(Arg0);
1016 Result << "</emphasized>";
1017 return;
1018 }
1019}
1020
1021void CommentASTToXMLConverter::visitHTMLStartTagComment(const HTMLStartTagComment *C) {
1022 Result << "<rawHTML><![CDATA[";
1023 PrintHTMLStartTagComment(C, Result);
1024 Result << "]]></rawHTML>";
1025}
1026
1027void CommentASTToXMLConverter::visitHTMLEndTagComment(const HTMLEndTagComment *C) {
1028 Result << "<rawHTML>&lt;/" << C->getTagName() << "&gt;</rawHTML>";
1029}
1030
1031void CommentASTToXMLConverter::visitParagraphComment(const ParagraphComment *C) {
Dmitri Gribenko2e72dd42013-02-01 20:23:57 +00001032 appendParagraphCommentWithKind(C, StringRef());
1033}
1034
1035void CommentASTToXMLConverter::appendParagraphCommentWithKind(
1036 const ParagraphComment *C,
1037 StringRef ParagraphKind) {
Dmitri Gribenko740c0fb2012-08-07 17:54:38 +00001038 if (C->isWhitespace())
1039 return;
1040
Dmitri Gribenko2e72dd42013-02-01 20:23:57 +00001041 if (ParagraphKind.empty())
1042 Result << "<Para>";
1043 else
1044 Result << "<Para kind=\"" << ParagraphKind << "\">";
1045
Dmitri Gribenko740c0fb2012-08-07 17:54:38 +00001046 for (Comment::child_iterator I = C->child_begin(), E = C->child_end();
1047 I != E; ++I) {
1048 visit(*I);
1049 }
1050 Result << "</Para>";
1051}
1052
1053void CommentASTToXMLConverter::visitBlockCommandComment(const BlockCommandComment *C) {
Dmitri Gribenko2e72dd42013-02-01 20:23:57 +00001054 StringRef ParagraphKind;
1055
1056 switch (C->getCommandID()) {
Fariborz Jahanian828b8d22013-02-26 22:12:16 +00001057 case CommandTraits::KCI_attention:
Dmitri Gribenko2e72dd42013-02-01 20:23:57 +00001058 case CommandTraits::KCI_author:
1059 case CommandTraits::KCI_authors:
1060 case CommandTraits::KCI_bug:
1061 case CommandTraits::KCI_copyright:
1062 case CommandTraits::KCI_date:
1063 case CommandTraits::KCI_invariant:
1064 case CommandTraits::KCI_note:
1065 case CommandTraits::KCI_post:
1066 case CommandTraits::KCI_pre:
1067 case CommandTraits::KCI_remark:
1068 case CommandTraits::KCI_remarks:
1069 case CommandTraits::KCI_sa:
1070 case CommandTraits::KCI_see:
1071 case CommandTraits::KCI_since:
1072 case CommandTraits::KCI_todo:
1073 case CommandTraits::KCI_version:
1074 case CommandTraits::KCI_warning:
1075 ParagraphKind = C->getCommandName(Traits);
1076 default:
1077 break;
1078 }
1079
1080 appendParagraphCommentWithKind(C->getParagraph(), ParagraphKind);
Dmitri Gribenko740c0fb2012-08-07 17:54:38 +00001081}
1082
1083void CommentASTToXMLConverter::visitParamCommandComment(const ParamCommandComment *C) {
1084 Result << "<Parameter><Name>";
Fariborz Jahanian9d2f1e72012-10-18 21:42:42 +00001085 appendToResultWithXMLEscaping(C->isParamIndexValid() ? C->getParamName(FC)
1086 : C->getParamNameAsWritten());
Dmitri Gribenko740c0fb2012-08-07 17:54:38 +00001087 Result << "</Name>";
1088
Dmitri Gribenko02489eb2013-06-24 04:41:32 +00001089 if (C->isParamIndexValid()) {
1090 if (C->isVarArgParam())
1091 Result << "<IsVarArg />";
1092 else
1093 Result << "<Index>" << C->getParamIndex() << "</Index>";
1094 }
Dmitri Gribenko740c0fb2012-08-07 17:54:38 +00001095
1096 Result << "<Direction isExplicit=\"" << C->isDirectionExplicit() << "\">";
1097 switch (C->getDirection()) {
1098 case ParamCommandComment::In:
1099 Result << "in";
1100 break;
1101 case ParamCommandComment::Out:
1102 Result << "out";
1103 break;
1104 case ParamCommandComment::InOut:
1105 Result << "in,out";
1106 break;
1107 }
1108 Result << "</Direction><Discussion>";
1109 visit(C->getParagraph());
1110 Result << "</Discussion></Parameter>";
1111}
1112
1113void CommentASTToXMLConverter::visitTParamCommandComment(
1114 const TParamCommandComment *C) {
1115 Result << "<Parameter><Name>";
Fariborz Jahanian9d2f1e72012-10-18 21:42:42 +00001116 appendToResultWithXMLEscaping(C->isPositionValid() ? C->getParamName(FC)
1117 : C->getParamNameAsWritten());
Dmitri Gribenko740c0fb2012-08-07 17:54:38 +00001118 Result << "</Name>";
1119
1120 if (C->isPositionValid() && C->getDepth() == 1) {
1121 Result << "<Index>" << C->getIndex(0) << "</Index>";
1122 }
1123
1124 Result << "<Discussion>";
1125 visit(C->getParagraph());
1126 Result << "</Discussion></Parameter>";
1127}
1128
1129void CommentASTToXMLConverter::visitVerbatimBlockComment(
1130 const VerbatimBlockComment *C) {
1131 unsigned NumLines = C->getNumLines();
1132 if (NumLines == 0)
1133 return;
1134
Dmitri Gribenko2aedfbd2013-02-03 17:48:05 +00001135 switch (C->getCommandID()) {
1136 case CommandTraits::KCI_code:
1137 Result << "<Verbatim xml:space=\"preserve\" kind=\"code\">";
1138 break;
1139 default:
1140 Result << "<Verbatim xml:space=\"preserve\" kind=\"verbatim\">";
1141 break;
1142 }
Dmitri Gribenko740c0fb2012-08-07 17:54:38 +00001143 for (unsigned i = 0; i != NumLines; ++i) {
1144 appendToResultWithXMLEscaping(C->getText(i));
1145 if (i + 1 != NumLines)
1146 Result << '\n';
1147 }
1148 Result << "</Verbatim>";
1149}
1150
1151void CommentASTToXMLConverter::visitVerbatimBlockLineComment(
1152 const VerbatimBlockLineComment *C) {
1153 llvm_unreachable("should not see this AST node");
1154}
1155
1156void CommentASTToXMLConverter::visitVerbatimLineComment(
1157 const VerbatimLineComment *C) {
Dmitri Gribenko6cffc192012-08-08 22:10:24 +00001158 Result << "<Verbatim xml:space=\"preserve\" kind=\"verbatim\">";
Dmitri Gribenko740c0fb2012-08-07 17:54:38 +00001159 appendToResultWithXMLEscaping(C->getText());
1160 Result << "</Verbatim>";
1161}
1162
1163void CommentASTToXMLConverter::visitFullComment(const FullComment *C) {
Dmitri Gribenko7acbf002012-09-10 20:32:42 +00001164 FullCommentParts Parts(C, Traits);
Dmitri Gribenko740c0fb2012-08-07 17:54:38 +00001165
1166 const DeclInfo *DI = C->getDeclInfo();
1167 StringRef RootEndTag;
1168 if (DI) {
1169 switch (DI->getKind()) {
1170 case DeclInfo::OtherKind:
1171 RootEndTag = "</Other>";
1172 Result << "<Other";
1173 break;
1174 case DeclInfo::FunctionKind:
1175 RootEndTag = "</Function>";
1176 Result << "<Function";
1177 switch (DI->TemplateKind) {
1178 case DeclInfo::NotTemplate:
1179 break;
1180 case DeclInfo::Template:
1181 Result << " templateKind=\"template\"";
1182 break;
1183 case DeclInfo::TemplateSpecialization:
1184 Result << " templateKind=\"specialization\"";
1185 break;
1186 case DeclInfo::TemplatePartialSpecialization:
1187 llvm_unreachable("partial specializations of functions "
1188 "are not allowed in C++");
1189 }
1190 if (DI->IsInstanceMethod)
1191 Result << " isInstanceMethod=\"1\"";
1192 if (DI->IsClassMethod)
1193 Result << " isClassMethod=\"1\"";
1194 break;
1195 case DeclInfo::ClassKind:
1196 RootEndTag = "</Class>";
1197 Result << "<Class";
1198 switch (DI->TemplateKind) {
1199 case DeclInfo::NotTemplate:
1200 break;
1201 case DeclInfo::Template:
1202 Result << " templateKind=\"template\"";
1203 break;
1204 case DeclInfo::TemplateSpecialization:
1205 Result << " templateKind=\"specialization\"";
1206 break;
1207 case DeclInfo::TemplatePartialSpecialization:
1208 Result << " templateKind=\"partialSpecialization\"";
1209 break;
1210 }
1211 break;
1212 case DeclInfo::VariableKind:
1213 RootEndTag = "</Variable>";
1214 Result << "<Variable";
1215 break;
1216 case DeclInfo::NamespaceKind:
1217 RootEndTag = "</Namespace>";
1218 Result << "<Namespace";
1219 break;
1220 case DeclInfo::TypedefKind:
1221 RootEndTag = "</Typedef>";
1222 Result << "<Typedef";
1223 break;
Dmitri Gribenko168d2342012-08-07 18:59:04 +00001224 case DeclInfo::EnumKind:
1225 RootEndTag = "</Enum>";
1226 Result << "<Enum";
1227 break;
Dmitri Gribenko740c0fb2012-08-07 17:54:38 +00001228 }
1229
1230 {
1231 // Print line and column number.
Fariborz Jahaniana7d76d22012-10-17 21:58:03 +00001232 SourceLocation Loc = DI->CurrentDecl->getLocation();
Dmitri Gribenko740c0fb2012-08-07 17:54:38 +00001233 std::pair<FileID, unsigned> LocInfo = SM.getDecomposedLoc(Loc);
1234 FileID FID = LocInfo.first;
1235 unsigned FileOffset = LocInfo.second;
1236
1237 if (!FID.isInvalid()) {
1238 if (const FileEntry *FE = SM.getFileEntryForID(FID)) {
1239 Result << " file=\"";
1240 appendToResultWithXMLEscaping(FE->getName());
1241 Result << "\"";
1242 }
1243 Result << " line=\"" << SM.getLineNumber(FID, FileOffset)
1244 << "\" column=\"" << SM.getColumnNumber(FID, FileOffset)
1245 << "\"";
1246 }
1247 }
1248
1249 // Finish the root tag.
1250 Result << ">";
1251
1252 bool FoundName = false;
Fariborz Jahanian1c883b92012-10-10 18:34:52 +00001253 if (const NamedDecl *ND = dyn_cast<NamedDecl>(DI->CommentDecl)) {
Dmitri Gribenko740c0fb2012-08-07 17:54:38 +00001254 if (DeclarationName DeclName = ND->getDeclName()) {
1255 Result << "<Name>";
1256 std::string Name = DeclName.getAsString();
1257 appendToResultWithXMLEscaping(Name);
1258 FoundName = true;
1259 Result << "</Name>";
1260 }
1261 }
1262 if (!FoundName)
1263 Result << "<Name>&lt;anonymous&gt;</Name>";
1264
1265 {
1266 // Print USR.
1267 SmallString<128> USR;
Fariborz Jahanian1c883b92012-10-10 18:34:52 +00001268 cxcursor::getDeclCursorUSR(DI->CommentDecl, USR);
Dmitri Gribenko740c0fb2012-08-07 17:54:38 +00001269 if (!USR.empty()) {
1270 Result << "<USR>";
1271 appendToResultWithXMLEscaping(USR);
1272 Result << "</USR>";
1273 }
1274 }
1275 } else {
1276 // No DeclInfo -- just emit some root tag and name tag.
1277 RootEndTag = "</Other>";
1278 Result << "<Other><Name>unknown</Name>";
1279 }
Fariborz Jahanian1a0cf802013-01-31 23:12:39 +00001280
1281 if (Parts.Headerfile) {
1282 Result << "<Headerfile>";
1283 visit(Parts.Headerfile);
1284 Result << "</Headerfile>";
1285 }
Dmitri Gribenko740c0fb2012-08-07 17:54:38 +00001286
Dmitri Gribenko1c1395e2012-10-25 18:28:26 +00001287 {
1288 // Pretty-print the declaration.
1289 Result << "<Declaration>";
1290 SmallString<128> Declaration;
1291 getSourceTextOfDeclaration(DI, Declaration);
Fariborz Jahanianb67908a2012-12-19 00:01:48 +00001292 formatTextOfDeclaration(DI, Declaration);
Dmitri Gribenko1c1395e2012-10-25 18:28:26 +00001293 appendToResultWithXMLEscaping(Declaration);
Fariborz Jahanian9b7ab872012-12-18 23:02:59 +00001294
Dmitri Gribenko1c1395e2012-10-25 18:28:26 +00001295 Result << "</Declaration>";
1296 }
1297
Dmitri Gribenko740c0fb2012-08-07 17:54:38 +00001298 bool FirstParagraphIsBrief = false;
1299 if (Parts.Brief) {
1300 Result << "<Abstract>";
1301 visit(Parts.Brief);
1302 Result << "</Abstract>";
1303 } else if (Parts.FirstParagraph) {
1304 Result << "<Abstract>";
1305 visit(Parts.FirstParagraph);
1306 Result << "</Abstract>";
1307 FirstParagraphIsBrief = true;
1308 }
Fariborz Jahanian1a0cf802013-01-31 23:12:39 +00001309
Dmitri Gribenko740c0fb2012-08-07 17:54:38 +00001310 if (Parts.TParams.size() != 0) {
1311 Result << "<TemplateParameters>";
1312 for (unsigned i = 0, e = Parts.TParams.size(); i != e; ++i)
1313 visit(Parts.TParams[i]);
1314 Result << "</TemplateParameters>";
1315 }
1316
1317 if (Parts.Params.size() != 0) {
1318 Result << "<Parameters>";
1319 for (unsigned i = 0, e = Parts.Params.size(); i != e; ++i)
1320 visit(Parts.Params[i]);
1321 Result << "</Parameters>";
1322 }
1323
Fariborz Jahanianfb6f6f62013-06-21 23:49:29 +00001324 if (Parts.Returns.size() != 0) {
Dmitri Gribenko740c0fb2012-08-07 17:54:38 +00001325 Result << "<ResultDiscussion>";
Fariborz Jahanianfb6f6f62013-06-21 23:49:29 +00001326 for (unsigned i = 0, e = Parts.Returns.size(); i != e; ++i)
1327 visit(Parts.Returns[i]);
Dmitri Gribenko740c0fb2012-08-07 17:54:38 +00001328 Result << "</ResultDiscussion>";
1329 }
Fariborz Jahanian35760a82012-09-28 22:35:49 +00001330
Fariborz Jahanian1c883b92012-10-10 18:34:52 +00001331 if (DI->CommentDecl->hasAttrs()) {
1332 const AttrVec &Attrs = DI->CommentDecl->getAttrs();
Fariborz Jahanianc491c3f2012-10-01 18:42:25 +00001333 for (unsigned i = 0, e = Attrs.size(); i != e; i++) {
1334 const AvailabilityAttr *AA = dyn_cast<AvailabilityAttr>(Attrs[i]);
Fariborz Jahaniande0ae1e2012-10-02 20:05:47 +00001335 if (!AA) {
Fariborz Jahaniane61fc442012-10-02 23:01:04 +00001336 if (const DeprecatedAttr *DA = dyn_cast<DeprecatedAttr>(Attrs[i])) {
1337 if (DA->getMessage().empty())
1338 Result << "<Deprecated/>";
1339 else {
Dmitri Gribenko61b1db12012-10-03 09:04:56 +00001340 Result << "<Deprecated>";
1341 appendToResultWithXMLEscaping(DA->getMessage());
1342 Result << "</Deprecated>";
Fariborz Jahaniane61fc442012-10-02 23:01:04 +00001343 }
1344 }
1345 else if (const UnavailableAttr *UA = dyn_cast<UnavailableAttr>(Attrs[i])) {
1346 if (UA->getMessage().empty())
1347 Result << "<Unavailable/>";
1348 else {
Dmitri Gribenko61b1db12012-10-03 09:04:56 +00001349 Result << "<Unavailable>";
1350 appendToResultWithXMLEscaping(UA->getMessage());
1351 Result << "</Unavailable>";
Fariborz Jahaniane61fc442012-10-02 23:01:04 +00001352 }
1353 }
Fariborz Jahanian35760a82012-09-28 22:35:49 +00001354 continue;
Fariborz Jahaniande0ae1e2012-10-02 20:05:47 +00001355 }
Fariborz Jahanianc491c3f2012-10-01 18:42:25 +00001356
1357 // 'availability' attribute.
Fariborz Jahanian35760a82012-09-28 22:35:49 +00001358 Result << "<Availability";
Fariborz Jahanianc491c3f2012-10-01 18:42:25 +00001359 StringRef Distribution;
Fariborz Jahanian35760a82012-09-28 22:35:49 +00001360 if (AA->getPlatform()) {
Fariborz Jahanianc491c3f2012-10-01 18:42:25 +00001361 Distribution = AvailabilityAttr::getPrettyPlatformName(
1362 AA->getPlatform()->getName());
1363 if (Distribution.empty())
1364 Distribution = AA->getPlatform()->getName();
Fariborz Jahanian35760a82012-09-28 22:35:49 +00001365 }
Fariborz Jahanianc491c3f2012-10-01 18:42:25 +00001366 Result << " distribution=\"" << Distribution << "\">";
Fariborz Jahanian35760a82012-09-28 22:35:49 +00001367 VersionTuple IntroducedInVersion = AA->getIntroduced();
1368 if (!IntroducedInVersion.empty()) {
Fariborz Jahanianc491c3f2012-10-01 18:42:25 +00001369 Result << "<IntroducedInVersion>"
1370 << IntroducedInVersion.getAsString()
1371 << "</IntroducedInVersion>";
Fariborz Jahanian35760a82012-09-28 22:35:49 +00001372 }
1373 VersionTuple DeprecatedInVersion = AA->getDeprecated();
1374 if (!DeprecatedInVersion.empty()) {
Fariborz Jahanianc491c3f2012-10-01 18:42:25 +00001375 Result << "<DeprecatedInVersion>"
1376 << DeprecatedInVersion.getAsString()
1377 << "</DeprecatedInVersion>";
Fariborz Jahanian35760a82012-09-28 22:35:49 +00001378 }
1379 VersionTuple RemovedAfterVersion = AA->getObsoleted();
1380 if (!RemovedAfterVersion.empty()) {
Fariborz Jahanianc491c3f2012-10-01 18:42:25 +00001381 Result << "<RemovedAfterVersion>"
1382 << RemovedAfterVersion.getAsString()
1383 << "</RemovedAfterVersion>";
Fariborz Jahanian35760a82012-09-28 22:35:49 +00001384 }
1385 StringRef DeprecationSummary = AA->getMessage();
1386 if (!DeprecationSummary.empty()) {
Dmitri Gribenko61b1db12012-10-03 09:04:56 +00001387 Result << "<DeprecationSummary>";
1388 appendToResultWithXMLEscaping(DeprecationSummary);
1389 Result << "</DeprecationSummary>";
Fariborz Jahanian35760a82012-09-28 22:35:49 +00001390 }
Fariborz Jahanian35760a82012-09-28 22:35:49 +00001391 if (AA->getUnavailable())
Fariborz Jahaniane61fc442012-10-02 23:01:04 +00001392 Result << "<Unavailable/>";
Fariborz Jahanianc491c3f2012-10-01 18:42:25 +00001393 Result << "</Availability>";
Fariborz Jahanian35760a82012-09-28 22:35:49 +00001394 }
1395 }
Fariborz Jahanianc491c3f2012-10-01 18:42:25 +00001396
Dmitri Gribenko740c0fb2012-08-07 17:54:38 +00001397 {
1398 bool StartTagEmitted = false;
1399 for (unsigned i = 0, e = Parts.MiscBlocks.size(); i != e; ++i) {
1400 const Comment *C = Parts.MiscBlocks[i];
1401 if (FirstParagraphIsBrief && C == Parts.FirstParagraph)
1402 continue;
1403 if (!StartTagEmitted) {
1404 Result << "<Discussion>";
1405 StartTagEmitted = true;
1406 }
1407 visit(C);
1408 }
1409 if (StartTagEmitted)
1410 Result << "</Discussion>";
1411 }
1412
1413 Result << RootEndTag;
1414
1415 Result.flush();
1416}
1417
1418void CommentASTToXMLConverter::appendToResultWithXMLEscaping(StringRef S) {
1419 for (StringRef::iterator I = S.begin(), E = S.end(); I != E; ++I) {
1420 const char C = *I;
1421 switch (C) {
1422 case '&':
1423 Result << "&amp;";
1424 break;
1425 case '<':
1426 Result << "&lt;";
1427 break;
1428 case '>':
1429 Result << "&gt;";
1430 break;
1431 case '"':
1432 Result << "&quot;";
1433 break;
1434 case '\'':
1435 Result << "&apos;";
1436 break;
1437 default:
1438 Result << C;
1439 break;
1440 }
1441 }
1442}
1443
1444extern "C" {
1445
Dmitri Gribenko7acbf002012-09-10 20:32:42 +00001446CXString clang_FullComment_getAsXML(CXComment CXC) {
Dmitri Gribenko740c0fb2012-08-07 17:54:38 +00001447 const FullComment *FC = getASTNodeAs<FullComment>(CXC);
1448 if (!FC)
Dmitri Gribenkof98dfba2013-02-01 14:13:32 +00001449 return cxstring::createNull();
Fariborz Jahanian9b7ab872012-12-18 23:02:59 +00001450 ASTContext &Context = FC->getDeclInfo()->CurrentDecl->getASTContext();
Dmitri Gribenko7acbf002012-09-10 20:32:42 +00001451 CXTranslationUnit TU = CXC.TranslationUnit;
Dmitri Gribenkoc22ea1c2013-01-26 18:53:38 +00001452 SourceManager &SM = cxtu::getASTUnit(TU)->getSourceManager();
Dmitri Gribenkoba82fea2013-01-26 21:39:50 +00001453
1454 if (!TU->FormatContext) {
1455 TU->FormatContext = new SimpleFormatContext(Context.getLangOpts());
Fariborz Jahanianbbdb9f22012-12-19 00:35:23 +00001456 } else if ((TU->FormatInMemoryUniqueId % 1000) == 0) {
Fariborz Jahanianb67908a2012-12-19 00:01:48 +00001457 // Delete after some number of iterators, so the buffers don't grow
1458 // too large.
Dmitri Gribenkoba82fea2013-01-26 21:39:50 +00001459 delete TU->FormatContext;
1460 TU->FormatContext = new SimpleFormatContext(Context.getLangOpts());
Fariborz Jahanian9b7ab872012-12-18 23:02:59 +00001461 }
Dmitri Gribenko740c0fb2012-08-07 17:54:38 +00001462
1463 SmallString<1024> XML;
Fariborz Jahanian9b7ab872012-12-18 23:02:59 +00001464 CommentASTToXMLConverter Converter(FC, XML, getCommandTraits(CXC), SM,
Dmitri Gribenkoba82fea2013-01-26 21:39:50 +00001465 *TU->FormatContext,
1466 TU->FormatInMemoryUniqueId++);
Dmitri Gribenko740c0fb2012-08-07 17:54:38 +00001467 Converter.visit(FC);
Dmitri Gribenko2f23e9c2013-02-02 02:19:29 +00001468 return cxstring::createDup(XML.str());
Dmitri Gribenko740c0fb2012-08-07 17:54:38 +00001469}
1470
1471} // end extern "C"
1472