blob: 69002b4d2385c07dd766779bfdcc38d18a05356f [file] [log] [blame]
Dmitri Gribenkoae99b752012-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 Gribenkoae99b752012-07-20 21:34:34 +000015#include "CXComment.h"
Dmitri Gribenkof303d4c2012-08-07 17:54:38 +000016#include "CXCursor.h"
Chandler Carruthf59edb92012-12-04 09:25:21 +000017#include "CXString.h"
Fariborz Jahanian88b95212012-12-18 23:02:59 +000018#include "SimpleFormatContext.h"
Dmitri Gribenkod1db1252012-08-09 17:33:20 +000019#include "clang/AST/CommentCommandTraits.h"
Chandler Carruthf59edb92012-12-04 09:25:21 +000020#include "clang/AST/CommentVisitor.h"
Dmitri Gribenkof303d4c2012-08-07 17:54:38 +000021#include "clang/AST/Decl.h"
Chandler Carruthf59edb92012-12-04 09:25:21 +000022#include "clang/AST/PrettyPrinter.h"
Chandler Carruthb99083e2013-01-02 10:28:36 +000023#include "clang/Format/Format.h"
24#include "clang/Lex/Lexer.h"
Fariborz Jahanian88b95212012-12-18 23:02:59 +000025#include "llvm/ADT/StringExtras.h"
Dmitri Gribenkof303d4c2012-08-07 17:54:38 +000026#include "llvm/ADT/StringSwitch.h"
Dmitri Gribenkoae99b752012-07-20 21:34:34 +000027#include "llvm/Support/ErrorHandling.h"
Dmitri Gribenko3e63d332012-07-21 01:47:43 +000028#include "llvm/Support/raw_ostream.h"
Dmitri Gribenko221a6d72012-07-30 17:49:32 +000029#include <climits>
30
Dmitri Gribenkoae99b752012-07-20 21:34:34 +000031using namespace clang;
32using namespace clang::cxstring;
33using namespace clang::comments;
34using namespace clang::cxcomment;
35
36extern "C" {
37
38enum CXCommentKind clang_Comment_getKind(CXComment CXC) {
39 const Comment *C = getASTNode(CXC);
40 if (!C)
41 return CXComment_Null;
42
43 switch (C->getCommentKind()) {
44 case Comment::NoCommentKind:
45 return CXComment_Null;
46
47 case Comment::TextCommentKind:
48 return CXComment_Text;
49
50 case Comment::InlineCommandCommentKind:
51 return CXComment_InlineCommand;
52
53 case Comment::HTMLStartTagCommentKind:
54 return CXComment_HTMLStartTag;
55
56 case Comment::HTMLEndTagCommentKind:
57 return CXComment_HTMLEndTag;
58
59 case Comment::ParagraphCommentKind:
60 return CXComment_Paragraph;
61
62 case Comment::BlockCommandCommentKind:
63 return CXComment_BlockCommand;
64
65 case Comment::ParamCommandCommentKind:
66 return CXComment_ParamCommand;
67
Dmitri Gribenko96b09862012-07-31 22:37:06 +000068 case Comment::TParamCommandCommentKind:
69 return CXComment_TParamCommand;
70
Dmitri Gribenkoae99b752012-07-20 21:34:34 +000071 case Comment::VerbatimBlockCommentKind:
72 return CXComment_VerbatimBlockCommand;
73
74 case Comment::VerbatimBlockLineCommentKind:
75 return CXComment_VerbatimBlockLine;
76
77 case Comment::VerbatimLineCommentKind:
78 return CXComment_VerbatimLine;
79
80 case Comment::FullCommentKind:
81 return CXComment_FullComment;
82 }
83 llvm_unreachable("unknown CommentKind");
84}
85
86unsigned clang_Comment_getNumChildren(CXComment CXC) {
87 const Comment *C = getASTNode(CXC);
88 if (!C)
89 return 0;
90
91 return C->child_count();
92}
93
94CXComment clang_Comment_getChild(CXComment CXC, unsigned ChildIdx) {
95 const Comment *C = getASTNode(CXC);
96 if (!C || ChildIdx >= C->child_count())
Dmitri Gribenkoe4330a32012-09-10 20:32:42 +000097 return createCXComment(NULL, NULL);
Dmitri Gribenkoae99b752012-07-20 21:34:34 +000098
Dmitri Gribenkoe4330a32012-09-10 20:32:42 +000099 return createCXComment(*(C->child_begin() + ChildIdx), CXC.TranslationUnit);
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000100}
101
102unsigned clang_Comment_isWhitespace(CXComment CXC) {
103 const Comment *C = getASTNode(CXC);
104 if (!C)
105 return false;
106
107 if (const TextComment *TC = dyn_cast<TextComment>(C))
108 return TC->isWhitespace();
109
110 if (const ParagraphComment *PC = dyn_cast<ParagraphComment>(C))
111 return PC->isWhitespace();
112
113 return false;
114}
115
116unsigned clang_InlineContentComment_hasTrailingNewline(CXComment CXC) {
117 const InlineContentComment *ICC = getASTNodeAs<InlineContentComment>(CXC);
118 if (!ICC)
119 return false;
120
121 return ICC->hasTrailingNewline();
122}
123
124CXString clang_TextComment_getText(CXComment CXC) {
125 const TextComment *TC = getASTNodeAs<TextComment>(CXC);
126 if (!TC)
Dmitri Gribenkodad4c1a2013-02-01 14:13:32 +0000127 return cxstring::createNull();
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000128
129 return createCXString(TC->getText(), /*DupString=*/ false);
130}
131
132CXString clang_InlineCommandComment_getCommandName(CXComment CXC) {
133 const InlineCommandComment *ICC = getASTNodeAs<InlineCommandComment>(CXC);
134 if (!ICC)
Dmitri Gribenkodad4c1a2013-02-01 14:13:32 +0000135 return cxstring::createNull();
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000136
Dmitri Gribenkoe4330a32012-09-10 20:32:42 +0000137 const CommandTraits &Traits = getCommandTraits(CXC);
138 return createCXString(ICC->getCommandName(Traits), /*DupString=*/ false);
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000139}
140
Dmitri Gribenko2d66a502012-07-23 16:43:01 +0000141enum CXCommentInlineCommandRenderKind
142clang_InlineCommandComment_getRenderKind(CXComment CXC) {
143 const InlineCommandComment *ICC = getASTNodeAs<InlineCommandComment>(CXC);
144 if (!ICC)
145 return CXCommentInlineCommandRenderKind_Normal;
146
147 switch (ICC->getRenderKind()) {
148 case InlineCommandComment::RenderNormal:
149 return CXCommentInlineCommandRenderKind_Normal;
150
151 case InlineCommandComment::RenderBold:
152 return CXCommentInlineCommandRenderKind_Bold;
153
154 case InlineCommandComment::RenderMonospaced:
155 return CXCommentInlineCommandRenderKind_Monospaced;
156
157 case InlineCommandComment::RenderEmphasized:
158 return CXCommentInlineCommandRenderKind_Emphasized;
159 }
160 llvm_unreachable("unknown InlineCommandComment::RenderKind");
161}
162
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000163unsigned clang_InlineCommandComment_getNumArgs(CXComment CXC) {
164 const InlineCommandComment *ICC = getASTNodeAs<InlineCommandComment>(CXC);
165 if (!ICC)
166 return 0;
167
168 return ICC->getNumArgs();
169}
170
171CXString clang_InlineCommandComment_getArgText(CXComment CXC,
172 unsigned ArgIdx) {
173 const InlineCommandComment *ICC = getASTNodeAs<InlineCommandComment>(CXC);
174 if (!ICC || ArgIdx >= ICC->getNumArgs())
Dmitri Gribenkodad4c1a2013-02-01 14:13:32 +0000175 return cxstring::createNull();
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000176
177 return createCXString(ICC->getArgText(ArgIdx), /*DupString=*/ false);
178}
179
180CXString clang_HTMLTagComment_getTagName(CXComment CXC) {
181 const HTMLTagComment *HTC = getASTNodeAs<HTMLTagComment>(CXC);
182 if (!HTC)
Dmitri Gribenkodad4c1a2013-02-01 14:13:32 +0000183 return cxstring::createNull();
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000184
185 return createCXString(HTC->getTagName(), /*DupString=*/ false);
186}
187
188unsigned clang_HTMLStartTagComment_isSelfClosing(CXComment CXC) {
189 const HTMLStartTagComment *HST = getASTNodeAs<HTMLStartTagComment>(CXC);
190 if (!HST)
191 return false;
192
193 return HST->isSelfClosing();
194}
195
196unsigned clang_HTMLStartTag_getNumAttrs(CXComment CXC) {
197 const HTMLStartTagComment *HST = getASTNodeAs<HTMLStartTagComment>(CXC);
198 if (!HST)
199 return 0;
200
201 return HST->getNumAttrs();
202}
203
204CXString clang_HTMLStartTag_getAttrName(CXComment CXC, unsigned AttrIdx) {
205 const HTMLStartTagComment *HST = getASTNodeAs<HTMLStartTagComment>(CXC);
206 if (!HST || AttrIdx >= HST->getNumAttrs())
Dmitri Gribenkodad4c1a2013-02-01 14:13:32 +0000207 return cxstring::createNull();
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000208
209 return createCXString(HST->getAttr(AttrIdx).Name, /*DupString=*/ false);
210}
211
212CXString clang_HTMLStartTag_getAttrValue(CXComment CXC, unsigned AttrIdx) {
213 const HTMLStartTagComment *HST = getASTNodeAs<HTMLStartTagComment>(CXC);
214 if (!HST || AttrIdx >= HST->getNumAttrs())
Dmitri Gribenkodad4c1a2013-02-01 14:13:32 +0000215 return cxstring::createNull();
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000216
217 return createCXString(HST->getAttr(AttrIdx).Value, /*DupString=*/ false);
218}
219
220CXString clang_BlockCommandComment_getCommandName(CXComment CXC) {
221 const BlockCommandComment *BCC = getASTNodeAs<BlockCommandComment>(CXC);
222 if (!BCC)
Dmitri Gribenkodad4c1a2013-02-01 14:13:32 +0000223 return cxstring::createNull();
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000224
Dmitri Gribenkoe4330a32012-09-10 20:32:42 +0000225 const CommandTraits &Traits = getCommandTraits(CXC);
226 return createCXString(BCC->getCommandName(Traits), /*DupString=*/ false);
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000227}
228
229unsigned clang_BlockCommandComment_getNumArgs(CXComment CXC) {
230 const BlockCommandComment *BCC = getASTNodeAs<BlockCommandComment>(CXC);
231 if (!BCC)
232 return 0;
233
234 return BCC->getNumArgs();
235}
236
237CXString clang_BlockCommandComment_getArgText(CXComment CXC,
238 unsigned ArgIdx) {
239 const BlockCommandComment *BCC = getASTNodeAs<BlockCommandComment>(CXC);
240 if (!BCC || ArgIdx >= BCC->getNumArgs())
Dmitri Gribenkodad4c1a2013-02-01 14:13:32 +0000241 return cxstring::createNull();
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000242
243 return createCXString(BCC->getArgText(ArgIdx), /*DupString=*/ false);
244}
245
246CXComment clang_BlockCommandComment_getParagraph(CXComment CXC) {
247 const BlockCommandComment *BCC = getASTNodeAs<BlockCommandComment>(CXC);
248 if (!BCC)
Dmitri Gribenkoe4330a32012-09-10 20:32:42 +0000249 return createCXComment(NULL, NULL);
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000250
Dmitri Gribenkoe4330a32012-09-10 20:32:42 +0000251 return createCXComment(BCC->getParagraph(), CXC.TranslationUnit);
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000252}
253
254CXString clang_ParamCommandComment_getParamName(CXComment CXC) {
255 const ParamCommandComment *PCC = getASTNodeAs<ParamCommandComment>(CXC);
Dmitri Gribenko1f8c5292012-07-23 19:41:49 +0000256 if (!PCC || !PCC->hasParamName())
Dmitri Gribenkodad4c1a2013-02-01 14:13:32 +0000257 return cxstring::createNull();
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000258
Fariborz Jahanian262e60c2012-10-18 21:42:42 +0000259 return createCXString(PCC->getParamNameAsWritten(), /*DupString=*/ false);
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000260}
261
262unsigned clang_ParamCommandComment_isParamIndexValid(CXComment CXC) {
263 const ParamCommandComment *PCC = getASTNodeAs<ParamCommandComment>(CXC);
264 if (!PCC)
265 return false;
266
267 return PCC->isParamIndexValid();
268}
269
270unsigned clang_ParamCommandComment_getParamIndex(CXComment CXC) {
271 const ParamCommandComment *PCC = getASTNodeAs<ParamCommandComment>(CXC);
Dmitri Gribenkob7403162012-07-30 17:38:19 +0000272 if (!PCC || !PCC->isParamIndexValid())
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000273 return ParamCommandComment::InvalidParamIndex;
274
275 return PCC->getParamIndex();
276}
277
278unsigned clang_ParamCommandComment_isDirectionExplicit(CXComment CXC) {
279 const ParamCommandComment *PCC = getASTNodeAs<ParamCommandComment>(CXC);
280 if (!PCC)
281 return false;
282
283 return PCC->isDirectionExplicit();
284}
285
286enum CXCommentParamPassDirection clang_ParamCommandComment_getDirection(
287 CXComment CXC) {
288 const ParamCommandComment *PCC = getASTNodeAs<ParamCommandComment>(CXC);
289 if (!PCC)
290 return CXCommentParamPassDirection_In;
291
292 switch (PCC->getDirection()) {
293 case ParamCommandComment::In:
294 return CXCommentParamPassDirection_In;
295
296 case ParamCommandComment::Out:
297 return CXCommentParamPassDirection_Out;
298
299 case ParamCommandComment::InOut:
300 return CXCommentParamPassDirection_InOut;
301 }
302 llvm_unreachable("unknown ParamCommandComment::PassDirection");
303}
304
Dmitri Gribenko96b09862012-07-31 22:37:06 +0000305CXString clang_TParamCommandComment_getParamName(CXComment CXC) {
306 const TParamCommandComment *TPCC = getASTNodeAs<TParamCommandComment>(CXC);
307 if (!TPCC || !TPCC->hasParamName())
Dmitri Gribenkodad4c1a2013-02-01 14:13:32 +0000308 return cxstring::createNull();
Dmitri Gribenko96b09862012-07-31 22:37:06 +0000309
Fariborz Jahanian262e60c2012-10-18 21:42:42 +0000310 return createCXString(TPCC->getParamNameAsWritten(), /*DupString=*/ false);
Dmitri Gribenko96b09862012-07-31 22:37:06 +0000311}
312
313unsigned clang_TParamCommandComment_isParamPositionValid(CXComment CXC) {
314 const TParamCommandComment *TPCC = getASTNodeAs<TParamCommandComment>(CXC);
315 if (!TPCC)
316 return false;
317
318 return TPCC->isPositionValid();
319}
320
321unsigned clang_TParamCommandComment_getDepth(CXComment CXC) {
322 const TParamCommandComment *TPCC = getASTNodeAs<TParamCommandComment>(CXC);
323 if (!TPCC || !TPCC->isPositionValid())
324 return 0;
325
326 return TPCC->getDepth();
327}
328
329unsigned clang_TParamCommandComment_getIndex(CXComment CXC, unsigned Depth) {
330 const TParamCommandComment *TPCC = getASTNodeAs<TParamCommandComment>(CXC);
331 if (!TPCC || !TPCC->isPositionValid() || Depth >= TPCC->getDepth())
332 return 0;
333
334 return TPCC->getIndex(Depth);
335}
336
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000337CXString clang_VerbatimBlockLineComment_getText(CXComment CXC) {
338 const VerbatimBlockLineComment *VBL =
339 getASTNodeAs<VerbatimBlockLineComment>(CXC);
340 if (!VBL)
Dmitri Gribenkodad4c1a2013-02-01 14:13:32 +0000341 return cxstring::createNull();
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000342
343 return createCXString(VBL->getText(), /*DupString=*/ false);
344}
345
346CXString clang_VerbatimLineComment_getText(CXComment CXC) {
347 const VerbatimLineComment *VLC = getASTNodeAs<VerbatimLineComment>(CXC);
348 if (!VLC)
Dmitri Gribenkodad4c1a2013-02-01 14:13:32 +0000349 return cxstring::createNull();
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000350
351 return createCXString(VLC->getText(), /*DupString=*/ false);
352}
353
354} // end extern "C"
355
356//===----------------------------------------------------------------------===//
357// Helpers for converting comment AST to HTML.
358//===----------------------------------------------------------------------===//
359
360namespace {
361
Dmitri Gribenkoe5db09c2012-07-30 19:47:34 +0000362/// This comparison will sort parameters with valid index by index and
363/// invalid (unresolved) parameters last.
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000364class ParamCommandCommentCompareIndex {
365public:
366 bool operator()(const ParamCommandComment *LHS,
367 const ParamCommandComment *RHS) const {
Dmitri Gribenkob7403162012-07-30 17:38:19 +0000368 unsigned LHSIndex = UINT_MAX;
369 unsigned RHSIndex = UINT_MAX;
370 if (LHS->isParamIndexValid())
371 LHSIndex = LHS->getParamIndex();
372 if (RHS->isParamIndexValid())
373 RHSIndex = RHS->getParamIndex();
374
375 return LHSIndex < RHSIndex;
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000376 }
377};
378
Dmitri Gribenko96b09862012-07-31 22:37:06 +0000379/// This comparison will sort template parameters in the following order:
380/// \li real template parameters (depth = 1) in index order;
381/// \li all other names (depth > 1);
382/// \li unresolved names.
383class TParamCommandCommentComparePosition {
384public:
385 bool operator()(const TParamCommandComment *LHS,
386 const TParamCommandComment *RHS) const {
387 // Sort unresolved names last.
388 if (!LHS->isPositionValid())
389 return false;
390 if (!RHS->isPositionValid())
391 return true;
392
393 if (LHS->getDepth() > 1)
394 return false;
395 if (RHS->getDepth() > 1)
396 return true;
397
398 // Sort template parameters in index order.
399 if (LHS->getDepth() == 1 && RHS->getDepth() == 1)
400 return LHS->getIndex(0) < RHS->getIndex(0);
401
402 // Leave all other names in source order.
403 return true;
404 }
405};
406
Dmitri Gribenko2ff84b52012-08-01 22:48:16 +0000407/// Separate parts of a FullComment.
408struct FullCommentParts {
409 /// Take a full comment apart and initialize members accordingly.
Dmitri Gribenkoe4330a32012-09-10 20:32:42 +0000410 FullCommentParts(const FullComment *C,
411 const CommandTraits &Traits);
Dmitri Gribenko2ff84b52012-08-01 22:48:16 +0000412
413 const BlockContentComment *Brief;
Fariborz Jahanianf843a582013-01-31 23:12:39 +0000414 const BlockContentComment *Headerfile;
Dmitri Gribenko2ff84b52012-08-01 22:48:16 +0000415 const ParagraphComment *FirstParagraph;
416 const BlockCommandComment *Returns;
417 SmallVector<const ParamCommandComment *, 8> Params;
418 SmallVector<const TParamCommandComment *, 4> TParams;
419 SmallVector<const BlockContentComment *, 8> MiscBlocks;
420};
421
Dmitri Gribenkoe4330a32012-09-10 20:32:42 +0000422FullCommentParts::FullCommentParts(const FullComment *C,
423 const CommandTraits &Traits) :
Fariborz Jahanianf843a582013-01-31 23:12:39 +0000424 Brief(NULL), Headerfile(NULL), FirstParagraph(NULL), Returns(NULL) {
Dmitri Gribenko2ff84b52012-08-01 22:48:16 +0000425 for (Comment::child_iterator I = C->child_begin(), E = C->child_end();
426 I != E; ++I) {
427 const Comment *Child = *I;
428 if (!Child)
429 continue;
430 switch (Child->getCommentKind()) {
431 case Comment::NoCommentKind:
432 continue;
433
434 case Comment::ParagraphCommentKind: {
435 const ParagraphComment *PC = cast<ParagraphComment>(Child);
436 if (PC->isWhitespace())
437 break;
438 if (!FirstParagraph)
439 FirstParagraph = PC;
440
441 MiscBlocks.push_back(PC);
442 break;
443 }
444
445 case Comment::BlockCommandCommentKind: {
446 const BlockCommandComment *BCC = cast<BlockCommandComment>(Child);
Dmitri Gribenkoe4330a32012-09-10 20:32:42 +0000447 const CommandInfo *Info = Traits.getCommandInfo(BCC->getCommandID());
448 if (!Brief && Info->IsBriefCommand) {
Dmitri Gribenko2ff84b52012-08-01 22:48:16 +0000449 Brief = BCC;
450 break;
451 }
Fariborz Jahanianf843a582013-01-31 23:12:39 +0000452 if (!Headerfile && Info->IsHeaderfileCommand) {
453 Headerfile = BCC;
454 break;
455 }
Dmitri Gribenkoe4330a32012-09-10 20:32:42 +0000456 if (!Returns && Info->IsReturnsCommand) {
Dmitri Gribenko2ff84b52012-08-01 22:48:16 +0000457 Returns = BCC;
458 break;
459 }
460 MiscBlocks.push_back(BCC);
461 break;
462 }
463
464 case Comment::ParamCommandCommentKind: {
465 const ParamCommandComment *PCC = cast<ParamCommandComment>(Child);
466 if (!PCC->hasParamName())
467 break;
468
469 if (!PCC->isDirectionExplicit() && !PCC->hasNonWhitespaceParagraph())
470 break;
471
472 Params.push_back(PCC);
473 break;
474 }
475
476 case Comment::TParamCommandCommentKind: {
477 const TParamCommandComment *TPCC = cast<TParamCommandComment>(Child);
478 if (!TPCC->hasParamName())
479 break;
480
481 if (!TPCC->hasNonWhitespaceParagraph())
482 break;
483
484 TParams.push_back(TPCC);
485 break;
486 }
487
488 case Comment::VerbatimBlockCommentKind:
Dmitri Gribenko2ff84b52012-08-01 22:48:16 +0000489 MiscBlocks.push_back(cast<BlockCommandComment>(Child));
490 break;
491
Dmitri Gribenko62290ae2012-08-09 18:20:29 +0000492 case Comment::VerbatimLineCommentKind: {
493 const VerbatimLineComment *VLC = cast<VerbatimLineComment>(Child);
Dmitri Gribenkoe4330a32012-09-10 20:32:42 +0000494 const CommandInfo *Info = Traits.getCommandInfo(VLC->getCommandID());
495 if (!Info->IsDeclarationCommand)
Dmitri Gribenko62290ae2012-08-09 18:20:29 +0000496 MiscBlocks.push_back(VLC);
497 break;
498 }
499
Dmitri Gribenko2ff84b52012-08-01 22:48:16 +0000500 case Comment::TextCommentKind:
501 case Comment::InlineCommandCommentKind:
502 case Comment::HTMLStartTagCommentKind:
503 case Comment::HTMLEndTagCommentKind:
504 case Comment::VerbatimBlockLineCommentKind:
505 case Comment::FullCommentKind:
506 llvm_unreachable("AST node of this kind can't be a child of "
507 "a FullComment");
508 }
509 }
510
511 // Sort params in order they are declared in the function prototype.
512 // Unresolved parameters are put at the end of the list in the same order
513 // they were seen in the comment.
514 std::stable_sort(Params.begin(), Params.end(),
515 ParamCommandCommentCompareIndex());
516
517 std::stable_sort(TParams.begin(), TParams.end(),
518 TParamCommandCommentComparePosition());
519}
520
521void PrintHTMLStartTagComment(const HTMLStartTagComment *C,
522 llvm::raw_svector_ostream &Result) {
523 Result << "<" << C->getTagName();
524
525 if (C->getNumAttrs() != 0) {
526 for (unsigned i = 0, e = C->getNumAttrs(); i != e; i++) {
527 Result << " ";
528 const HTMLStartTagComment::Attribute &Attr = C->getAttr(i);
529 Result << Attr.Name;
530 if (!Attr.Value.empty())
531 Result << "=\"" << Attr.Value << "\"";
532 }
533 }
534
535 if (!C->isSelfClosing())
536 Result << ">";
537 else
538 Result << "/>";
539}
540
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000541class CommentASTToHTMLConverter :
542 public ConstCommentVisitor<CommentASTToHTMLConverter> {
543public:
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000544 /// \param Str accumulator for HTML.
Dmitri Gribenko8cfabf22012-10-19 16:51:38 +0000545 CommentASTToHTMLConverter(const FullComment *FC,
Fariborz Jahanianbf967be2012-10-10 18:34:52 +0000546 SmallVectorImpl<char> &Str,
Dmitri Gribenkoe4330a32012-09-10 20:32:42 +0000547 const CommandTraits &Traits) :
Fariborz Jahanianbf967be2012-10-10 18:34:52 +0000548 FC(FC), Result(Str), Traits(Traits)
Dmitri Gribenkoe4330a32012-09-10 20:32:42 +0000549 { }
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000550
551 // Inline content.
552 void visitTextComment(const TextComment *C);
553 void visitInlineCommandComment(const InlineCommandComment *C);
554 void visitHTMLStartTagComment(const HTMLStartTagComment *C);
555 void visitHTMLEndTagComment(const HTMLEndTagComment *C);
556
557 // Block content.
558 void visitParagraphComment(const ParagraphComment *C);
559 void visitBlockCommandComment(const BlockCommandComment *C);
560 void visitParamCommandComment(const ParamCommandComment *C);
Dmitri Gribenko96b09862012-07-31 22:37:06 +0000561 void visitTParamCommandComment(const TParamCommandComment *C);
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000562 void visitVerbatimBlockComment(const VerbatimBlockComment *C);
563 void visitVerbatimBlockLineComment(const VerbatimBlockLineComment *C);
564 void visitVerbatimLineComment(const VerbatimLineComment *C);
565
566 void visitFullComment(const FullComment *C);
567
568 // Helpers.
569
570 /// Convert a paragraph that is not a block by itself (an argument to some
571 /// command).
572 void visitNonStandaloneParagraphComment(const ParagraphComment *C);
573
574 void appendToResultWithHTMLEscaping(StringRef S);
575
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000576private:
Dmitri Gribenko8cfabf22012-10-19 16:51:38 +0000577 const FullComment *FC;
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000578 /// Output stream for HTML.
579 llvm::raw_svector_ostream Result;
Dmitri Gribenkoe4330a32012-09-10 20:32:42 +0000580
581 const CommandTraits &Traits;
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000582};
583} // end unnamed namespace
584
585void CommentASTToHTMLConverter::visitTextComment(const TextComment *C) {
586 appendToResultWithHTMLEscaping(C->getText());
587}
588
589void CommentASTToHTMLConverter::visitInlineCommandComment(
590 const InlineCommandComment *C) {
Dmitri Gribenko2d66a502012-07-23 16:43:01 +0000591 // Nothing to render if no arguments supplied.
592 if (C->getNumArgs() == 0)
593 return;
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000594
Dmitri Gribenko2d66a502012-07-23 16:43:01 +0000595 // Nothing to render if argument is empty.
596 StringRef Arg0 = C->getArgText(0);
597 if (Arg0.empty())
598 return;
599
600 switch (C->getRenderKind()) {
601 case InlineCommandComment::RenderNormal:
Dmitri Gribenko59500fe2012-08-01 00:21:12 +0000602 for (unsigned i = 0, e = C->getNumArgs(); i != e; ++i) {
603 appendToResultWithHTMLEscaping(C->getArgText(i));
604 Result << " ";
605 }
Dmitri Gribenko2d66a502012-07-23 16:43:01 +0000606 return;
607
608 case InlineCommandComment::RenderBold:
609 assert(C->getNumArgs() == 1);
Dmitri Gribenko59500fe2012-08-01 00:21:12 +0000610 Result << "<b>";
611 appendToResultWithHTMLEscaping(Arg0);
612 Result << "</b>";
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000613 return;
Dmitri Gribenko2d66a502012-07-23 16:43:01 +0000614 case InlineCommandComment::RenderMonospaced:
615 assert(C->getNumArgs() == 1);
Dmitri Gribenko59500fe2012-08-01 00:21:12 +0000616 Result << "<tt>";
617 appendToResultWithHTMLEscaping(Arg0);
618 Result<< "</tt>";
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000619 return;
Dmitri Gribenko2d66a502012-07-23 16:43:01 +0000620 case InlineCommandComment::RenderEmphasized:
621 assert(C->getNumArgs() == 1);
Dmitri Gribenko59500fe2012-08-01 00:21:12 +0000622 Result << "<em>";
623 appendToResultWithHTMLEscaping(Arg0);
624 Result << "</em>";
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000625 return;
626 }
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000627}
628
629void CommentASTToHTMLConverter::visitHTMLStartTagComment(
630 const HTMLStartTagComment *C) {
Dmitri Gribenko2ff84b52012-08-01 22:48:16 +0000631 PrintHTMLStartTagComment(C, Result);
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000632}
633
634void CommentASTToHTMLConverter::visitHTMLEndTagComment(
635 const HTMLEndTagComment *C) {
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000636 Result << "</" << C->getTagName() << ">";
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000637}
638
639void CommentASTToHTMLConverter::visitParagraphComment(
640 const ParagraphComment *C) {
641 if (C->isWhitespace())
642 return;
643
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000644 Result << "<p>";
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000645 for (Comment::child_iterator I = C->child_begin(), E = C->child_end();
646 I != E; ++I) {
647 visit(*I);
648 }
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000649 Result << "</p>";
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000650}
651
652void CommentASTToHTMLConverter::visitBlockCommandComment(
653 const BlockCommandComment *C) {
Dmitri Gribenkoe4330a32012-09-10 20:32:42 +0000654 const CommandInfo *Info = Traits.getCommandInfo(C->getCommandID());
655 if (Info->IsBriefCommand) {
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000656 Result << "<p class=\"para-brief\">";
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000657 visitNonStandaloneParagraphComment(C->getParagraph());
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000658 Result << "</p>";
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000659 return;
660 }
Dmitri Gribenkoe4330a32012-09-10 20:32:42 +0000661 if (Info->IsReturnsCommand) {
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000662 Result << "<p class=\"para-returns\">"
663 "<span class=\"word-returns\">Returns</span> ";
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000664 visitNonStandaloneParagraphComment(C->getParagraph());
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000665 Result << "</p>";
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000666 return;
667 }
668 // We don't know anything about this command. Just render the paragraph.
669 visit(C->getParagraph());
670}
671
672void CommentASTToHTMLConverter::visitParamCommandComment(
673 const ParamCommandComment *C) {
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000674 if (C->isParamIndexValid()) {
675 Result << "<dt class=\"param-name-index-"
676 << C->getParamIndex()
677 << "\">";
Fariborz Jahanian262e60c2012-10-18 21:42:42 +0000678 appendToResultWithHTMLEscaping(C->getParamName(FC));
679 } else {
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000680 Result << "<dt class=\"param-name-index-invalid\">";
Fariborz Jahanian262e60c2012-10-18 21:42:42 +0000681 appendToResultWithHTMLEscaping(C->getParamNameAsWritten());
682 }
Dmitri Gribenko59500fe2012-08-01 00:21:12 +0000683 Result << "</dt>";
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000684
685 if (C->isParamIndexValid()) {
686 Result << "<dd class=\"param-descr-index-"
687 << C->getParamIndex()
688 << "\">";
689 } else
690 Result << "<dd class=\"param-descr-index-invalid\">";
691
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000692 visitNonStandaloneParagraphComment(C->getParagraph());
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000693 Result << "</dd>";
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000694}
695
Dmitri Gribenko96b09862012-07-31 22:37:06 +0000696void CommentASTToHTMLConverter::visitTParamCommandComment(
697 const TParamCommandComment *C) {
698 if (C->isPositionValid()) {
699 if (C->getDepth() == 1)
Dmitri Gribenko6a425522012-08-01 23:47:30 +0000700 Result << "<dt class=\"tparam-name-index-"
Dmitri Gribenko96b09862012-07-31 22:37:06 +0000701 << C->getIndex(0)
702 << "\">";
703 else
Dmitri Gribenko6a425522012-08-01 23:47:30 +0000704 Result << "<dt class=\"tparam-name-index-other\">";
Fariborz Jahanian262e60c2012-10-18 21:42:42 +0000705 appendToResultWithHTMLEscaping(C->getParamName(FC));
706 } else {
Dmitri Gribenko96b09862012-07-31 22:37:06 +0000707 Result << "<dt class=\"tparam-name-index-invalid\">";
Fariborz Jahanian262e60c2012-10-18 21:42:42 +0000708 appendToResultWithHTMLEscaping(C->getParamNameAsWritten());
709 }
710
Dmitri Gribenko59500fe2012-08-01 00:21:12 +0000711 Result << "</dt>";
Dmitri Gribenko96b09862012-07-31 22:37:06 +0000712
713 if (C->isPositionValid()) {
714 if (C->getDepth() == 1)
715 Result << "<dd class=\"tparam-descr-index-"
716 << C->getIndex(0)
717 << "\">";
718 else
719 Result << "<dd class=\"tparam-descr-index-other\">";
720 } else
721 Result << "<dd class=\"tparam-descr-index-invalid\">";
722
723 visitNonStandaloneParagraphComment(C->getParagraph());
724 Result << "</dd>";
725}
726
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000727void CommentASTToHTMLConverter::visitVerbatimBlockComment(
728 const VerbatimBlockComment *C) {
729 unsigned NumLines = C->getNumLines();
730 if (NumLines == 0)
731 return;
732
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000733 Result << "<pre>";
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000734 for (unsigned i = 0; i != NumLines; ++i) {
735 appendToResultWithHTMLEscaping(C->getText(i));
736 if (i + 1 != NumLines)
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000737 Result << '\n';
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000738 }
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000739 Result << "</pre>";
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000740}
741
742void CommentASTToHTMLConverter::visitVerbatimBlockLineComment(
743 const VerbatimBlockLineComment *C) {
744 llvm_unreachable("should not see this AST node");
745}
746
747void CommentASTToHTMLConverter::visitVerbatimLineComment(
748 const VerbatimLineComment *C) {
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000749 Result << "<pre>";
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000750 appendToResultWithHTMLEscaping(C->getText());
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000751 Result << "</pre>";
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000752}
753
754void CommentASTToHTMLConverter::visitFullComment(const FullComment *C) {
Dmitri Gribenkoe4330a32012-09-10 20:32:42 +0000755 FullCommentParts Parts(C, Traits);
Dmitri Gribenko96b09862012-07-31 22:37:06 +0000756
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000757 bool FirstParagraphIsBrief = false;
Fariborz Jahanianf843a582013-01-31 23:12:39 +0000758 if (Parts.Headerfile)
759 visit(Parts.Headerfile);
Dmitri Gribenko2ff84b52012-08-01 22:48:16 +0000760 if (Parts.Brief)
761 visit(Parts.Brief);
762 else if (Parts.FirstParagraph) {
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000763 Result << "<p class=\"para-brief\">";
Dmitri Gribenko2ff84b52012-08-01 22:48:16 +0000764 visitNonStandaloneParagraphComment(Parts.FirstParagraph);
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000765 Result << "</p>";
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000766 FirstParagraphIsBrief = true;
767 }
768
Dmitri Gribenko2ff84b52012-08-01 22:48:16 +0000769 for (unsigned i = 0, e = Parts.MiscBlocks.size(); i != e; ++i) {
770 const Comment *C = Parts.MiscBlocks[i];
771 if (FirstParagraphIsBrief && C == Parts.FirstParagraph)
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000772 continue;
773 visit(C);
774 }
775
Dmitri Gribenko2ff84b52012-08-01 22:48:16 +0000776 if (Parts.TParams.size() != 0) {
Dmitri Gribenko96b09862012-07-31 22:37:06 +0000777 Result << "<dl>";
Dmitri Gribenko2ff84b52012-08-01 22:48:16 +0000778 for (unsigned i = 0, e = Parts.TParams.size(); i != e; ++i)
779 visit(Parts.TParams[i]);
Dmitri Gribenko96b09862012-07-31 22:37:06 +0000780 Result << "</dl>";
781 }
782
Dmitri Gribenko2ff84b52012-08-01 22:48:16 +0000783 if (Parts.Params.size() != 0) {
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000784 Result << "<dl>";
Dmitri Gribenko2ff84b52012-08-01 22:48:16 +0000785 for (unsigned i = 0, e = Parts.Params.size(); i != e; ++i)
786 visit(Parts.Params[i]);
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000787 Result << "</dl>";
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000788 }
789
Dmitri Gribenko2ff84b52012-08-01 22:48:16 +0000790 if (Parts.Returns)
791 visit(Parts.Returns);
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000792
793 Result.flush();
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000794}
795
796void CommentASTToHTMLConverter::visitNonStandaloneParagraphComment(
797 const ParagraphComment *C) {
798 if (!C)
799 return;
800
801 for (Comment::child_iterator I = C->child_begin(), E = C->child_end();
802 I != E; ++I) {
803 visit(*I);
804 }
805}
806
807void CommentASTToHTMLConverter::appendToResultWithHTMLEscaping(StringRef S) {
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000808 for (StringRef::iterator I = S.begin(), E = S.end(); I != E; ++I) {
809 const char C = *I;
810 switch (C) {
811 case '&':
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000812 Result << "&amp;";
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000813 break;
814 case '<':
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000815 Result << "&lt;";
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000816 break;
817 case '>':
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000818 Result << "&gt;";
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000819 break;
820 case '"':
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000821 Result << "&quot;";
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000822 break;
823 case '\'':
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000824 Result << "&#39;";
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000825 break;
826 case '/':
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000827 Result << "&#47;";
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000828 break;
829 default:
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000830 Result << C;
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000831 break;
832 }
833 }
834}
835
836extern "C" {
837
838CXString clang_HTMLTagComment_getAsString(CXComment CXC) {
839 const HTMLTagComment *HTC = getASTNodeAs<HTMLTagComment>(CXC);
840 if (!HTC)
Dmitri Gribenkodad4c1a2013-02-01 14:13:32 +0000841 return cxstring::createNull();
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000842
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000843 SmallString<128> HTML;
Fariborz Jahanianbf967be2012-10-10 18:34:52 +0000844 CommentASTToHTMLConverter Converter(0, HTML, getCommandTraits(CXC));
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000845 Converter.visit(HTC);
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000846 return createCXString(HTML.str(), /* DupString = */ true);
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000847}
848
849CXString clang_FullComment_getAsHTML(CXComment CXC) {
850 const FullComment *FC = getASTNodeAs<FullComment>(CXC);
851 if (!FC)
Dmitri Gribenkodad4c1a2013-02-01 14:13:32 +0000852 return cxstring::createNull();
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000853
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000854 SmallString<1024> HTML;
Dmitri Gribenko8cfabf22012-10-19 16:51:38 +0000855 CommentASTToHTMLConverter Converter(FC, HTML, getCommandTraits(CXC));
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000856 Converter.visit(FC);
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000857 return createCXString(HTML.str(), /* DupString = */ true);
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000858}
859
860} // end extern "C"
861
Dmitri Gribenkof303d4c2012-08-07 17:54:38 +0000862namespace {
863class CommentASTToXMLConverter :
864 public ConstCommentVisitor<CommentASTToXMLConverter> {
865public:
866 /// \param Str accumulator for XML.
Dmitri Gribenko8cfabf22012-10-19 16:51:38 +0000867 CommentASTToXMLConverter(const FullComment *FC,
Fariborz Jahanianbf967be2012-10-10 18:34:52 +0000868 SmallVectorImpl<char> &Str,
Dmitri Gribenkoe4330a32012-09-10 20:32:42 +0000869 const CommandTraits &Traits,
Fariborz Jahanian88b95212012-12-18 23:02:59 +0000870 const SourceManager &SM,
Fariborz Jahanian7c106832012-12-19 00:01:48 +0000871 SimpleFormatContext &SFC,
Fariborz Jahanian88b95212012-12-18 23:02:59 +0000872 unsigned FUID) :
873 FC(FC), Result(Str), Traits(Traits), SM(SM),
Fariborz Jahanian7c106832012-12-19 00:01:48 +0000874 FormatRewriterContext(SFC),
Fariborz Jahanian88b95212012-12-18 23:02:59 +0000875 FormatInMemoryUniqueId(FUID) { }
Dmitri Gribenkof303d4c2012-08-07 17:54:38 +0000876
877 // Inline content.
878 void visitTextComment(const TextComment *C);
879 void visitInlineCommandComment(const InlineCommandComment *C);
880 void visitHTMLStartTagComment(const HTMLStartTagComment *C);
881 void visitHTMLEndTagComment(const HTMLEndTagComment *C);
882
883 // Block content.
884 void visitParagraphComment(const ParagraphComment *C);
Dmitri Gribenkoaf01bed2013-02-01 20:23:57 +0000885
886 void appendParagraphCommentWithKind(const ParagraphComment *C,
887 StringRef Kind);
888
Dmitri Gribenkof303d4c2012-08-07 17:54:38 +0000889 void visitBlockCommandComment(const BlockCommandComment *C);
890 void visitParamCommandComment(const ParamCommandComment *C);
891 void visitTParamCommandComment(const TParamCommandComment *C);
892 void visitVerbatimBlockComment(const VerbatimBlockComment *C);
893 void visitVerbatimBlockLineComment(const VerbatimBlockLineComment *C);
894 void visitVerbatimLineComment(const VerbatimLineComment *C);
895
896 void visitFullComment(const FullComment *C);
897
898 // Helpers.
899 void appendToResultWithXMLEscaping(StringRef S);
Dmitri Gribenkoaf01bed2013-02-01 20:23:57 +0000900
Fariborz Jahanian7c106832012-12-19 00:01:48 +0000901 void formatTextOfDeclaration(const DeclInfo *DI,
902 SmallString<128> &Declaration);
Dmitri Gribenkof303d4c2012-08-07 17:54:38 +0000903
904private:
Dmitri Gribenko8cfabf22012-10-19 16:51:38 +0000905 const FullComment *FC;
906
Dmitri Gribenkof303d4c2012-08-07 17:54:38 +0000907 /// Output stream for XML.
908 llvm::raw_svector_ostream Result;
Dmitri Gribenkoe4330a32012-09-10 20:32:42 +0000909
910 const CommandTraits &Traits;
911 const SourceManager &SM;
Fariborz Jahanian88b95212012-12-18 23:02:59 +0000912 SimpleFormatContext &FormatRewriterContext;
913 unsigned FormatInMemoryUniqueId;
Dmitri Gribenkof303d4c2012-08-07 17:54:38 +0000914};
Dmitri Gribenko7c984992012-10-25 18:28:26 +0000915
916void getSourceTextOfDeclaration(const DeclInfo *ThisDecl,
917 SmallVectorImpl<char> &Str) {
918 ASTContext &Context = ThisDecl->CurrentDecl->getASTContext();
919 const LangOptions &LangOpts = Context.getLangOpts();
920 llvm::raw_svector_ostream OS(Str);
921 PrintingPolicy PPolicy(LangOpts);
Fariborz Jahanian40902d82012-12-19 23:36:00 +0000922 PPolicy.PolishForDeclaration = true;
Dmitri Gribenko7c984992012-10-25 18:28:26 +0000923 PPolicy.TerseOutput = true;
924 ThisDecl->CurrentDecl->print(OS, PPolicy,
Dmitri Gribenko27c2cb22013-01-07 18:45:48 +0000925 /*Indentation*/0, /*PrintInstantiation*/false);
Dmitri Gribenko7c984992012-10-25 18:28:26 +0000926}
Fariborz Jahanian88b95212012-12-18 23:02:59 +0000927
Fariborz Jahanian7c106832012-12-19 00:01:48 +0000928void CommentASTToXMLConverter::formatTextOfDeclaration(
929 const DeclInfo *DI,
930 SmallString<128> &Declaration) {
Fariborz Jahanian7c106832012-12-19 00:01:48 +0000931 // FIXME. formatting API expects null terminated input string.
932 // There might be more efficient way of doing this.
933 std::string StringDecl = Declaration.str();
Fariborz Jahanian88b95212012-12-18 23:02:59 +0000934
Fariborz Jahanian7c106832012-12-19 00:01:48 +0000935 // Formatter specific code.
936 // Form a unique in memory buffer name.
Dmitri Gribenkocfa88f82013-01-12 19:30:44 +0000937 SmallString<128> filename;
Fariborz Jahanian7c106832012-12-19 00:01:48 +0000938 filename += "xmldecl";
939 filename += llvm::utostr(FormatInMemoryUniqueId);
940 filename += ".xd";
941 FileID ID = FormatRewriterContext.createInMemoryFile(filename, StringDecl);
942 SourceLocation Start =
943 FormatRewriterContext.Sources.getLocForStartOfFile(ID).getLocWithOffset(0);
944 unsigned Length = Declaration.size();
Fariborz Jahanian88b95212012-12-18 23:02:59 +0000945
Fariborz Jahanian7c106832012-12-19 00:01:48 +0000946 std::vector<CharSourceRange>
947 Ranges(1, CharSourceRange::getCharRange(Start, Start.getLocWithOffset(Length)));
948 ASTContext &Context = DI->CurrentDecl->getASTContext();
949 const LangOptions &LangOpts = Context.getLangOpts();
950 Lexer Lex(ID, FormatRewriterContext.Sources.getBuffer(ID),
951 FormatRewriterContext.Sources, LangOpts);
952 tooling::Replacements Replace =
953 reformat(format::getLLVMStyle(), Lex, FormatRewriterContext.Sources, Ranges);
954 applyAllReplacements(Replace, FormatRewriterContext.Rewrite);
955 Declaration = FormatRewriterContext.getRewrittenText(ID);
Fariborz Jahanian88b95212012-12-18 23:02:59 +0000956}
Dmitri Gribenko7c984992012-10-25 18:28:26 +0000957
Dmitri Gribenkof303d4c2012-08-07 17:54:38 +0000958} // end unnamed namespace
959
960void CommentASTToXMLConverter::visitTextComment(const TextComment *C) {
961 appendToResultWithXMLEscaping(C->getText());
962}
963
964void CommentASTToXMLConverter::visitInlineCommandComment(const InlineCommandComment *C) {
965 // Nothing to render if no arguments supplied.
966 if (C->getNumArgs() == 0)
967 return;
968
969 // Nothing to render if argument is empty.
970 StringRef Arg0 = C->getArgText(0);
971 if (Arg0.empty())
972 return;
973
974 switch (C->getRenderKind()) {
975 case InlineCommandComment::RenderNormal:
976 for (unsigned i = 0, e = C->getNumArgs(); i != e; ++i) {
977 appendToResultWithXMLEscaping(C->getArgText(i));
978 Result << " ";
979 }
980 return;
981 case InlineCommandComment::RenderBold:
982 assert(C->getNumArgs() == 1);
983 Result << "<bold>";
984 appendToResultWithXMLEscaping(Arg0);
985 Result << "</bold>";
986 return;
987 case InlineCommandComment::RenderMonospaced:
988 assert(C->getNumArgs() == 1);
989 Result << "<monospaced>";
990 appendToResultWithXMLEscaping(Arg0);
991 Result << "</monospaced>";
992 return;
993 case InlineCommandComment::RenderEmphasized:
994 assert(C->getNumArgs() == 1);
995 Result << "<emphasized>";
996 appendToResultWithXMLEscaping(Arg0);
997 Result << "</emphasized>";
998 return;
999 }
1000}
1001
1002void CommentASTToXMLConverter::visitHTMLStartTagComment(const HTMLStartTagComment *C) {
1003 Result << "<rawHTML><![CDATA[";
1004 PrintHTMLStartTagComment(C, Result);
1005 Result << "]]></rawHTML>";
1006}
1007
1008void CommentASTToXMLConverter::visitHTMLEndTagComment(const HTMLEndTagComment *C) {
1009 Result << "<rawHTML>&lt;/" << C->getTagName() << "&gt;</rawHTML>";
1010}
1011
1012void CommentASTToXMLConverter::visitParagraphComment(const ParagraphComment *C) {
Dmitri Gribenkoaf01bed2013-02-01 20:23:57 +00001013 appendParagraphCommentWithKind(C, StringRef());
1014}
1015
1016void CommentASTToXMLConverter::appendParagraphCommentWithKind(
1017 const ParagraphComment *C,
1018 StringRef ParagraphKind) {
Dmitri Gribenkof303d4c2012-08-07 17:54:38 +00001019 if (C->isWhitespace())
1020 return;
1021
Dmitri Gribenkoaf01bed2013-02-01 20:23:57 +00001022 if (ParagraphKind.empty())
1023 Result << "<Para>";
1024 else
1025 Result << "<Para kind=\"" << ParagraphKind << "\">";
1026
Dmitri Gribenkof303d4c2012-08-07 17:54:38 +00001027 for (Comment::child_iterator I = C->child_begin(), E = C->child_end();
1028 I != E; ++I) {
1029 visit(*I);
1030 }
1031 Result << "</Para>";
1032}
1033
1034void CommentASTToXMLConverter::visitBlockCommandComment(const BlockCommandComment *C) {
Dmitri Gribenkoaf01bed2013-02-01 20:23:57 +00001035 StringRef ParagraphKind;
1036
1037 switch (C->getCommandID()) {
1038 case CommandTraits::KCI_author:
1039 case CommandTraits::KCI_authors:
1040 case CommandTraits::KCI_bug:
1041 case CommandTraits::KCI_copyright:
1042 case CommandTraits::KCI_date:
1043 case CommandTraits::KCI_invariant:
1044 case CommandTraits::KCI_note:
1045 case CommandTraits::KCI_post:
1046 case CommandTraits::KCI_pre:
1047 case CommandTraits::KCI_remark:
1048 case CommandTraits::KCI_remarks:
1049 case CommandTraits::KCI_sa:
1050 case CommandTraits::KCI_see:
1051 case CommandTraits::KCI_since:
1052 case CommandTraits::KCI_todo:
1053 case CommandTraits::KCI_version:
1054 case CommandTraits::KCI_warning:
1055 ParagraphKind = C->getCommandName(Traits);
1056 default:
1057 break;
1058 }
1059
1060 appendParagraphCommentWithKind(C->getParagraph(), ParagraphKind);
Dmitri Gribenkof303d4c2012-08-07 17:54:38 +00001061}
1062
1063void CommentASTToXMLConverter::visitParamCommandComment(const ParamCommandComment *C) {
1064 Result << "<Parameter><Name>";
Fariborz Jahanian262e60c2012-10-18 21:42:42 +00001065 appendToResultWithXMLEscaping(C->isParamIndexValid() ? C->getParamName(FC)
1066 : C->getParamNameAsWritten());
Dmitri Gribenkof303d4c2012-08-07 17:54:38 +00001067 Result << "</Name>";
1068
1069 if (C->isParamIndexValid())
1070 Result << "<Index>" << C->getParamIndex() << "</Index>";
1071
1072 Result << "<Direction isExplicit=\"" << C->isDirectionExplicit() << "\">";
1073 switch (C->getDirection()) {
1074 case ParamCommandComment::In:
1075 Result << "in";
1076 break;
1077 case ParamCommandComment::Out:
1078 Result << "out";
1079 break;
1080 case ParamCommandComment::InOut:
1081 Result << "in,out";
1082 break;
1083 }
1084 Result << "</Direction><Discussion>";
1085 visit(C->getParagraph());
1086 Result << "</Discussion></Parameter>";
1087}
1088
1089void CommentASTToXMLConverter::visitTParamCommandComment(
1090 const TParamCommandComment *C) {
1091 Result << "<Parameter><Name>";
Fariborz Jahanian262e60c2012-10-18 21:42:42 +00001092 appendToResultWithXMLEscaping(C->isPositionValid() ? C->getParamName(FC)
1093 : C->getParamNameAsWritten());
Dmitri Gribenkof303d4c2012-08-07 17:54:38 +00001094 Result << "</Name>";
1095
1096 if (C->isPositionValid() && C->getDepth() == 1) {
1097 Result << "<Index>" << C->getIndex(0) << "</Index>";
1098 }
1099
1100 Result << "<Discussion>";
1101 visit(C->getParagraph());
1102 Result << "</Discussion></Parameter>";
1103}
1104
1105void CommentASTToXMLConverter::visitVerbatimBlockComment(
1106 const VerbatimBlockComment *C) {
1107 unsigned NumLines = C->getNumLines();
1108 if (NumLines == 0)
1109 return;
1110
Dmitri Gribenkoe4330a32012-09-10 20:32:42 +00001111 Result << llvm::StringSwitch<const char *>(C->getCommandName(Traits))
Dmitri Gribenko6cd44202012-08-08 22:10:24 +00001112 .Case("code", "<Verbatim xml:space=\"preserve\" kind=\"code\">")
1113 .Default("<Verbatim xml:space=\"preserve\" kind=\"verbatim\">");
Dmitri Gribenkof303d4c2012-08-07 17:54:38 +00001114 for (unsigned i = 0; i != NumLines; ++i) {
1115 appendToResultWithXMLEscaping(C->getText(i));
1116 if (i + 1 != NumLines)
1117 Result << '\n';
1118 }
1119 Result << "</Verbatim>";
1120}
1121
1122void CommentASTToXMLConverter::visitVerbatimBlockLineComment(
1123 const VerbatimBlockLineComment *C) {
1124 llvm_unreachable("should not see this AST node");
1125}
1126
1127void CommentASTToXMLConverter::visitVerbatimLineComment(
1128 const VerbatimLineComment *C) {
Dmitri Gribenko6cd44202012-08-08 22:10:24 +00001129 Result << "<Verbatim xml:space=\"preserve\" kind=\"verbatim\">";
Dmitri Gribenkof303d4c2012-08-07 17:54:38 +00001130 appendToResultWithXMLEscaping(C->getText());
1131 Result << "</Verbatim>";
1132}
1133
1134void CommentASTToXMLConverter::visitFullComment(const FullComment *C) {
Dmitri Gribenkoe4330a32012-09-10 20:32:42 +00001135 FullCommentParts Parts(C, Traits);
Dmitri Gribenkof303d4c2012-08-07 17:54:38 +00001136
1137 const DeclInfo *DI = C->getDeclInfo();
1138 StringRef RootEndTag;
1139 if (DI) {
1140 switch (DI->getKind()) {
1141 case DeclInfo::OtherKind:
1142 RootEndTag = "</Other>";
1143 Result << "<Other";
1144 break;
1145 case DeclInfo::FunctionKind:
1146 RootEndTag = "</Function>";
1147 Result << "<Function";
1148 switch (DI->TemplateKind) {
1149 case DeclInfo::NotTemplate:
1150 break;
1151 case DeclInfo::Template:
1152 Result << " templateKind=\"template\"";
1153 break;
1154 case DeclInfo::TemplateSpecialization:
1155 Result << " templateKind=\"specialization\"";
1156 break;
1157 case DeclInfo::TemplatePartialSpecialization:
1158 llvm_unreachable("partial specializations of functions "
1159 "are not allowed in C++");
1160 }
1161 if (DI->IsInstanceMethod)
1162 Result << " isInstanceMethod=\"1\"";
1163 if (DI->IsClassMethod)
1164 Result << " isClassMethod=\"1\"";
1165 break;
1166 case DeclInfo::ClassKind:
1167 RootEndTag = "</Class>";
1168 Result << "<Class";
1169 switch (DI->TemplateKind) {
1170 case DeclInfo::NotTemplate:
1171 break;
1172 case DeclInfo::Template:
1173 Result << " templateKind=\"template\"";
1174 break;
1175 case DeclInfo::TemplateSpecialization:
1176 Result << " templateKind=\"specialization\"";
1177 break;
1178 case DeclInfo::TemplatePartialSpecialization:
1179 Result << " templateKind=\"partialSpecialization\"";
1180 break;
1181 }
1182 break;
1183 case DeclInfo::VariableKind:
1184 RootEndTag = "</Variable>";
1185 Result << "<Variable";
1186 break;
1187 case DeclInfo::NamespaceKind:
1188 RootEndTag = "</Namespace>";
1189 Result << "<Namespace";
1190 break;
1191 case DeclInfo::TypedefKind:
1192 RootEndTag = "</Typedef>";
1193 Result << "<Typedef";
1194 break;
Dmitri Gribenkocff339a2012-08-07 18:59:04 +00001195 case DeclInfo::EnumKind:
1196 RootEndTag = "</Enum>";
1197 Result << "<Enum";
1198 break;
Dmitri Gribenkof303d4c2012-08-07 17:54:38 +00001199 }
1200
1201 {
1202 // Print line and column number.
Fariborz Jahanian1bfb00d2012-10-17 21:58:03 +00001203 SourceLocation Loc = DI->CurrentDecl->getLocation();
Dmitri Gribenkof303d4c2012-08-07 17:54:38 +00001204 std::pair<FileID, unsigned> LocInfo = SM.getDecomposedLoc(Loc);
1205 FileID FID = LocInfo.first;
1206 unsigned FileOffset = LocInfo.second;
1207
1208 if (!FID.isInvalid()) {
1209 if (const FileEntry *FE = SM.getFileEntryForID(FID)) {
1210 Result << " file=\"";
1211 appendToResultWithXMLEscaping(FE->getName());
1212 Result << "\"";
1213 }
1214 Result << " line=\"" << SM.getLineNumber(FID, FileOffset)
1215 << "\" column=\"" << SM.getColumnNumber(FID, FileOffset)
1216 << "\"";
1217 }
1218 }
1219
1220 // Finish the root tag.
1221 Result << ">";
1222
1223 bool FoundName = false;
Fariborz Jahanianbf967be2012-10-10 18:34:52 +00001224 if (const NamedDecl *ND = dyn_cast<NamedDecl>(DI->CommentDecl)) {
Dmitri Gribenkof303d4c2012-08-07 17:54:38 +00001225 if (DeclarationName DeclName = ND->getDeclName()) {
1226 Result << "<Name>";
1227 std::string Name = DeclName.getAsString();
1228 appendToResultWithXMLEscaping(Name);
1229 FoundName = true;
1230 Result << "</Name>";
1231 }
1232 }
1233 if (!FoundName)
1234 Result << "<Name>&lt;anonymous&gt;</Name>";
1235
1236 {
1237 // Print USR.
1238 SmallString<128> USR;
Fariborz Jahanianbf967be2012-10-10 18:34:52 +00001239 cxcursor::getDeclCursorUSR(DI->CommentDecl, USR);
Dmitri Gribenkof303d4c2012-08-07 17:54:38 +00001240 if (!USR.empty()) {
1241 Result << "<USR>";
1242 appendToResultWithXMLEscaping(USR);
1243 Result << "</USR>";
1244 }
1245 }
1246 } else {
1247 // No DeclInfo -- just emit some root tag and name tag.
1248 RootEndTag = "</Other>";
1249 Result << "<Other><Name>unknown</Name>";
1250 }
Fariborz Jahanianf843a582013-01-31 23:12:39 +00001251
1252 if (Parts.Headerfile) {
1253 Result << "<Headerfile>";
1254 visit(Parts.Headerfile);
1255 Result << "</Headerfile>";
1256 }
Dmitri Gribenkof303d4c2012-08-07 17:54:38 +00001257
Dmitri Gribenko7c984992012-10-25 18:28:26 +00001258 {
1259 // Pretty-print the declaration.
1260 Result << "<Declaration>";
1261 SmallString<128> Declaration;
1262 getSourceTextOfDeclaration(DI, Declaration);
Fariborz Jahanian7c106832012-12-19 00:01:48 +00001263 formatTextOfDeclaration(DI, Declaration);
Dmitri Gribenko7c984992012-10-25 18:28:26 +00001264 appendToResultWithXMLEscaping(Declaration);
Fariborz Jahanian88b95212012-12-18 23:02:59 +00001265
Dmitri Gribenko7c984992012-10-25 18:28:26 +00001266 Result << "</Declaration>";
1267 }
1268
Dmitri Gribenkof303d4c2012-08-07 17:54:38 +00001269 bool FirstParagraphIsBrief = false;
1270 if (Parts.Brief) {
1271 Result << "<Abstract>";
1272 visit(Parts.Brief);
1273 Result << "</Abstract>";
1274 } else if (Parts.FirstParagraph) {
1275 Result << "<Abstract>";
1276 visit(Parts.FirstParagraph);
1277 Result << "</Abstract>";
1278 FirstParagraphIsBrief = true;
1279 }
Fariborz Jahanianf843a582013-01-31 23:12:39 +00001280
Dmitri Gribenkof303d4c2012-08-07 17:54:38 +00001281 if (Parts.TParams.size() != 0) {
1282 Result << "<TemplateParameters>";
1283 for (unsigned i = 0, e = Parts.TParams.size(); i != e; ++i)
1284 visit(Parts.TParams[i]);
1285 Result << "</TemplateParameters>";
1286 }
1287
1288 if (Parts.Params.size() != 0) {
1289 Result << "<Parameters>";
1290 for (unsigned i = 0, e = Parts.Params.size(); i != e; ++i)
1291 visit(Parts.Params[i]);
1292 Result << "</Parameters>";
1293 }
1294
1295 if (Parts.Returns) {
1296 Result << "<ResultDiscussion>";
1297 visit(Parts.Returns);
1298 Result << "</ResultDiscussion>";
1299 }
Fariborz Jahanian257e2e82012-09-28 22:35:49 +00001300
Fariborz Jahanianbf967be2012-10-10 18:34:52 +00001301 if (DI->CommentDecl->hasAttrs()) {
1302 const AttrVec &Attrs = DI->CommentDecl->getAttrs();
Fariborz Jahanianfaab5612012-10-01 18:42:25 +00001303 for (unsigned i = 0, e = Attrs.size(); i != e; i++) {
1304 const AvailabilityAttr *AA = dyn_cast<AvailabilityAttr>(Attrs[i]);
Fariborz Jahanian2a465332012-10-02 20:05:47 +00001305 if (!AA) {
Fariborz Jahanian8da68b82012-10-02 23:01:04 +00001306 if (const DeprecatedAttr *DA = dyn_cast<DeprecatedAttr>(Attrs[i])) {
1307 if (DA->getMessage().empty())
1308 Result << "<Deprecated/>";
1309 else {
Dmitri Gribenko7d9c9752012-10-03 09:04:56 +00001310 Result << "<Deprecated>";
1311 appendToResultWithXMLEscaping(DA->getMessage());
1312 Result << "</Deprecated>";
Fariborz Jahanian8da68b82012-10-02 23:01:04 +00001313 }
1314 }
1315 else if (const UnavailableAttr *UA = dyn_cast<UnavailableAttr>(Attrs[i])) {
1316 if (UA->getMessage().empty())
1317 Result << "<Unavailable/>";
1318 else {
Dmitri Gribenko7d9c9752012-10-03 09:04:56 +00001319 Result << "<Unavailable>";
1320 appendToResultWithXMLEscaping(UA->getMessage());
1321 Result << "</Unavailable>";
Fariborz Jahanian8da68b82012-10-02 23:01:04 +00001322 }
1323 }
Fariborz Jahanian257e2e82012-09-28 22:35:49 +00001324 continue;
Fariborz Jahanian2a465332012-10-02 20:05:47 +00001325 }
Fariborz Jahanianfaab5612012-10-01 18:42:25 +00001326
1327 // 'availability' attribute.
Fariborz Jahanian257e2e82012-09-28 22:35:49 +00001328 Result << "<Availability";
Fariborz Jahanianfaab5612012-10-01 18:42:25 +00001329 StringRef Distribution;
Fariborz Jahanian257e2e82012-09-28 22:35:49 +00001330 if (AA->getPlatform()) {
Fariborz Jahanianfaab5612012-10-01 18:42:25 +00001331 Distribution = AvailabilityAttr::getPrettyPlatformName(
1332 AA->getPlatform()->getName());
1333 if (Distribution.empty())
1334 Distribution = AA->getPlatform()->getName();
Fariborz Jahanian257e2e82012-09-28 22:35:49 +00001335 }
Fariborz Jahanianfaab5612012-10-01 18:42:25 +00001336 Result << " distribution=\"" << Distribution << "\">";
Fariborz Jahanian257e2e82012-09-28 22:35:49 +00001337 VersionTuple IntroducedInVersion = AA->getIntroduced();
1338 if (!IntroducedInVersion.empty()) {
Fariborz Jahanianfaab5612012-10-01 18:42:25 +00001339 Result << "<IntroducedInVersion>"
1340 << IntroducedInVersion.getAsString()
1341 << "</IntroducedInVersion>";
Fariborz Jahanian257e2e82012-09-28 22:35:49 +00001342 }
1343 VersionTuple DeprecatedInVersion = AA->getDeprecated();
1344 if (!DeprecatedInVersion.empty()) {
Fariborz Jahanianfaab5612012-10-01 18:42:25 +00001345 Result << "<DeprecatedInVersion>"
1346 << DeprecatedInVersion.getAsString()
1347 << "</DeprecatedInVersion>";
Fariborz Jahanian257e2e82012-09-28 22:35:49 +00001348 }
1349 VersionTuple RemovedAfterVersion = AA->getObsoleted();
1350 if (!RemovedAfterVersion.empty()) {
Fariborz Jahanianfaab5612012-10-01 18:42:25 +00001351 Result << "<RemovedAfterVersion>"
1352 << RemovedAfterVersion.getAsString()
1353 << "</RemovedAfterVersion>";
Fariborz Jahanian257e2e82012-09-28 22:35:49 +00001354 }
1355 StringRef DeprecationSummary = AA->getMessage();
1356 if (!DeprecationSummary.empty()) {
Dmitri Gribenko7d9c9752012-10-03 09:04:56 +00001357 Result << "<DeprecationSummary>";
1358 appendToResultWithXMLEscaping(DeprecationSummary);
1359 Result << "</DeprecationSummary>";
Fariborz Jahanian257e2e82012-09-28 22:35:49 +00001360 }
Fariborz Jahanian257e2e82012-09-28 22:35:49 +00001361 if (AA->getUnavailable())
Fariborz Jahanian8da68b82012-10-02 23:01:04 +00001362 Result << "<Unavailable/>";
Fariborz Jahanianfaab5612012-10-01 18:42:25 +00001363 Result << "</Availability>";
Fariborz Jahanian257e2e82012-09-28 22:35:49 +00001364 }
1365 }
Fariborz Jahanianfaab5612012-10-01 18:42:25 +00001366
Dmitri Gribenkof303d4c2012-08-07 17:54:38 +00001367 {
1368 bool StartTagEmitted = false;
1369 for (unsigned i = 0, e = Parts.MiscBlocks.size(); i != e; ++i) {
1370 const Comment *C = Parts.MiscBlocks[i];
1371 if (FirstParagraphIsBrief && C == Parts.FirstParagraph)
1372 continue;
1373 if (!StartTagEmitted) {
1374 Result << "<Discussion>";
1375 StartTagEmitted = true;
1376 }
1377 visit(C);
1378 }
1379 if (StartTagEmitted)
1380 Result << "</Discussion>";
1381 }
1382
1383 Result << RootEndTag;
1384
1385 Result.flush();
1386}
1387
1388void CommentASTToXMLConverter::appendToResultWithXMLEscaping(StringRef S) {
1389 for (StringRef::iterator I = S.begin(), E = S.end(); I != E; ++I) {
1390 const char C = *I;
1391 switch (C) {
1392 case '&':
1393 Result << "&amp;";
1394 break;
1395 case '<':
1396 Result << "&lt;";
1397 break;
1398 case '>':
1399 Result << "&gt;";
1400 break;
1401 case '"':
1402 Result << "&quot;";
1403 break;
1404 case '\'':
1405 Result << "&apos;";
1406 break;
1407 default:
1408 Result << C;
1409 break;
1410 }
1411 }
1412}
1413
1414extern "C" {
1415
Dmitri Gribenkoe4330a32012-09-10 20:32:42 +00001416CXString clang_FullComment_getAsXML(CXComment CXC) {
Dmitri Gribenkof303d4c2012-08-07 17:54:38 +00001417 const FullComment *FC = getASTNodeAs<FullComment>(CXC);
1418 if (!FC)
Dmitri Gribenkodad4c1a2013-02-01 14:13:32 +00001419 return cxstring::createNull();
Fariborz Jahanian88b95212012-12-18 23:02:59 +00001420 ASTContext &Context = FC->getDeclInfo()->CurrentDecl->getASTContext();
Dmitri Gribenkoe4330a32012-09-10 20:32:42 +00001421 CXTranslationUnit TU = CXC.TranslationUnit;
Dmitri Gribenko5694feb2013-01-26 18:53:38 +00001422 SourceManager &SM = cxtu::getASTUnit(TU)->getSourceManager();
Dmitri Gribenko337ee242013-01-26 21:39:50 +00001423
1424 if (!TU->FormatContext) {
1425 TU->FormatContext = new SimpleFormatContext(Context.getLangOpts());
Fariborz Jahanian8a68da12012-12-19 00:35:23 +00001426 } else if ((TU->FormatInMemoryUniqueId % 1000) == 0) {
Fariborz Jahanian7c106832012-12-19 00:01:48 +00001427 // Delete after some number of iterators, so the buffers don't grow
1428 // too large.
Dmitri Gribenko337ee242013-01-26 21:39:50 +00001429 delete TU->FormatContext;
1430 TU->FormatContext = new SimpleFormatContext(Context.getLangOpts());
Fariborz Jahanian88b95212012-12-18 23:02:59 +00001431 }
Dmitri Gribenkof303d4c2012-08-07 17:54:38 +00001432
1433 SmallString<1024> XML;
Fariborz Jahanian88b95212012-12-18 23:02:59 +00001434 CommentASTToXMLConverter Converter(FC, XML, getCommandTraits(CXC), SM,
Dmitri Gribenko337ee242013-01-26 21:39:50 +00001435 *TU->FormatContext,
1436 TU->FormatInMemoryUniqueId++);
Dmitri Gribenkof303d4c2012-08-07 17:54:38 +00001437 Converter.visit(FC);
1438 return createCXString(XML.str(), /* DupString = */ true);
1439}
1440
1441} // end extern "C"
1442