blob: fa149a0ff9d0077c43c83e4532d23f933abc16d8 [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"
15#include "CXString.h"
16#include "CXComment.h"
Dmitri Gribenkof303d4c2012-08-07 17:54:38 +000017#include "CXCursor.h"
Dmitri Gribenkoae99b752012-07-20 21:34:34 +000018
Fariborz Jahanian1bfb00d2012-10-17 21:58:03 +000019#include "clang/AST/PrettyPrinter.h"
Dmitri Gribenkoae99b752012-07-20 21:34:34 +000020#include "clang/AST/CommentVisitor.h"
Dmitri Gribenkod1db1252012-08-09 17:33:20 +000021#include "clang/AST/CommentCommandTraits.h"
Dmitri Gribenkof303d4c2012-08-07 17:54:38 +000022#include "clang/AST/Decl.h"
Dmitri Gribenkoae99b752012-07-20 21:34:34 +000023
Dmitri Gribenkof303d4c2012-08-07 17:54:38 +000024#include "llvm/ADT/StringSwitch.h"
Dmitri Gribenkoae99b752012-07-20 21:34:34 +000025#include "llvm/Support/ErrorHandling.h"
Dmitri Gribenko3e63d332012-07-21 01:47:43 +000026#include "llvm/Support/raw_ostream.h"
Dmitri Gribenkoae99b752012-07-20 21:34:34 +000027
Dmitri Gribenko221a6d72012-07-30 17:49:32 +000028#include <climits>
29
Dmitri Gribenkoae99b752012-07-20 21:34:34 +000030using namespace clang;
31using namespace clang::cxstring;
32using 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 Gribenko96b09862012-07-31 22:37:06 +000067 case Comment::TParamCommandCommentKind:
68 return CXComment_TParamCommand;
69
Dmitri Gribenkoae99b752012-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 Gribenkoe4330a32012-09-10 20:32:42 +000096 return createCXComment(NULL, NULL);
Dmitri Gribenkoae99b752012-07-20 21:34:34 +000097
Dmitri Gribenkoe4330a32012-09-10 20:32:42 +000098 return createCXComment(*(C->child_begin() + ChildIdx), CXC.TranslationUnit);
Dmitri Gribenkoae99b752012-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)
126 return createCXString((const char *) 0);
127
128 return createCXString(TC->getText(), /*DupString=*/ false);
129}
130
131CXString clang_InlineCommandComment_getCommandName(CXComment CXC) {
132 const InlineCommandComment *ICC = getASTNodeAs<InlineCommandComment>(CXC);
133 if (!ICC)
134 return createCXString((const char *) 0);
135
Dmitri Gribenkoe4330a32012-09-10 20:32:42 +0000136 const CommandTraits &Traits = getCommandTraits(CXC);
137 return createCXString(ICC->getCommandName(Traits), /*DupString=*/ false);
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000138}
139
Dmitri Gribenko2d66a502012-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 Gribenkoae99b752012-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())
174 return createCXString((const char *) 0);
175
176 return createCXString(ICC->getArgText(ArgIdx), /*DupString=*/ false);
177}
178
179CXString clang_HTMLTagComment_getTagName(CXComment CXC) {
180 const HTMLTagComment *HTC = getASTNodeAs<HTMLTagComment>(CXC);
181 if (!HTC)
182 return createCXString((const char *) 0);
183
184 return createCXString(HTC->getTagName(), /*DupString=*/ false);
185}
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())
206 return createCXString((const char *) 0);
207
208 return createCXString(HST->getAttr(AttrIdx).Name, /*DupString=*/ false);
209}
210
211CXString clang_HTMLStartTag_getAttrValue(CXComment CXC, unsigned AttrIdx) {
212 const HTMLStartTagComment *HST = getASTNodeAs<HTMLStartTagComment>(CXC);
213 if (!HST || AttrIdx >= HST->getNumAttrs())
214 return createCXString((const char *) 0);
215
216 return createCXString(HST->getAttr(AttrIdx).Value, /*DupString=*/ false);
217}
218
219CXString clang_BlockCommandComment_getCommandName(CXComment CXC) {
220 const BlockCommandComment *BCC = getASTNodeAs<BlockCommandComment>(CXC);
221 if (!BCC)
222 return createCXString((const char *) 0);
223
Dmitri Gribenkoe4330a32012-09-10 20:32:42 +0000224 const CommandTraits &Traits = getCommandTraits(CXC);
225 return createCXString(BCC->getCommandName(Traits), /*DupString=*/ false);
Dmitri Gribenkoae99b752012-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())
240 return createCXString((const char *) 0);
241
242 return createCXString(BCC->getArgText(ArgIdx), /*DupString=*/ false);
243}
244
245CXComment clang_BlockCommandComment_getParagraph(CXComment CXC) {
246 const BlockCommandComment *BCC = getASTNodeAs<BlockCommandComment>(CXC);
247 if (!BCC)
Dmitri Gribenkoe4330a32012-09-10 20:32:42 +0000248 return createCXComment(NULL, NULL);
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000249
Dmitri Gribenkoe4330a32012-09-10 20:32:42 +0000250 return createCXComment(BCC->getParagraph(), CXC.TranslationUnit);
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000251}
252
253CXString clang_ParamCommandComment_getParamName(CXComment CXC) {
254 const ParamCommandComment *PCC = getASTNodeAs<ParamCommandComment>(CXC);
Dmitri Gribenko1f8c5292012-07-23 19:41:49 +0000255 if (!PCC || !PCC->hasParamName())
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000256 return createCXString((const char *) 0);
257
Fariborz Jahanian262e60c2012-10-18 21:42:42 +0000258 return createCXString(PCC->getParamNameAsWritten(), /*DupString=*/ false);
Dmitri Gribenkoae99b752012-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 Gribenkob7403162012-07-30 17:38:19 +0000271 if (!PCC || !PCC->isParamIndexValid())
Dmitri Gribenkoae99b752012-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 Gribenko96b09862012-07-31 22:37:06 +0000304CXString clang_TParamCommandComment_getParamName(CXComment CXC) {
305 const TParamCommandComment *TPCC = getASTNodeAs<TParamCommandComment>(CXC);
306 if (!TPCC || !TPCC->hasParamName())
307 return createCXString((const char *) 0);
308
Fariborz Jahanian262e60c2012-10-18 21:42:42 +0000309 return createCXString(TPCC->getParamNameAsWritten(), /*DupString=*/ false);
Dmitri Gribenko96b09862012-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 Gribenkoae99b752012-07-20 21:34:34 +0000336CXString clang_VerbatimBlockLineComment_getText(CXComment CXC) {
337 const VerbatimBlockLineComment *VBL =
338 getASTNodeAs<VerbatimBlockLineComment>(CXC);
339 if (!VBL)
340 return createCXString((const char *) 0);
341
342 return createCXString(VBL->getText(), /*DupString=*/ false);
343}
344
345CXString clang_VerbatimLineComment_getText(CXComment CXC) {
346 const VerbatimLineComment *VLC = getASTNodeAs<VerbatimLineComment>(CXC);
347 if (!VLC)
348 return createCXString((const char *) 0);
349
350 return createCXString(VLC->getText(), /*DupString=*/ false);
351}
352
353} // end extern "C"
354
355//===----------------------------------------------------------------------===//
356// Helpers for converting comment AST to HTML.
357//===----------------------------------------------------------------------===//
358
359namespace {
360
Dmitri Gribenkoe5db09c2012-07-30 19:47:34 +0000361/// This comparison will sort parameters with valid index by index and
362/// invalid (unresolved) parameters last.
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000363class ParamCommandCommentCompareIndex {
364public:
365 bool operator()(const ParamCommandComment *LHS,
366 const ParamCommandComment *RHS) const {
Dmitri Gribenkob7403162012-07-30 17:38:19 +0000367 unsigned LHSIndex = UINT_MAX;
368 unsigned RHSIndex = UINT_MAX;
369 if (LHS->isParamIndexValid())
370 LHSIndex = LHS->getParamIndex();
371 if (RHS->isParamIndexValid())
372 RHSIndex = RHS->getParamIndex();
373
374 return LHSIndex < RHSIndex;
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000375 }
376};
377
Dmitri Gribenko96b09862012-07-31 22:37:06 +0000378/// This comparison will sort template parameters in the following order:
379/// \li real template parameters (depth = 1) in index order;
380/// \li all other names (depth > 1);
381/// \li unresolved names.
382class TParamCommandCommentComparePosition {
383public:
384 bool operator()(const TParamCommandComment *LHS,
385 const TParamCommandComment *RHS) const {
386 // Sort unresolved names last.
387 if (!LHS->isPositionValid())
388 return false;
389 if (!RHS->isPositionValid())
390 return true;
391
392 if (LHS->getDepth() > 1)
393 return false;
394 if (RHS->getDepth() > 1)
395 return true;
396
397 // Sort template parameters in index order.
398 if (LHS->getDepth() == 1 && RHS->getDepth() == 1)
399 return LHS->getIndex(0) < RHS->getIndex(0);
400
401 // Leave all other names in source order.
402 return true;
403 }
404};
405
Dmitri Gribenko2ff84b52012-08-01 22:48:16 +0000406/// Separate parts of a FullComment.
407struct FullCommentParts {
408 /// Take a full comment apart and initialize members accordingly.
Dmitri Gribenkoe4330a32012-09-10 20:32:42 +0000409 FullCommentParts(const FullComment *C,
410 const CommandTraits &Traits);
Dmitri Gribenko2ff84b52012-08-01 22:48:16 +0000411
412 const BlockContentComment *Brief;
413 const ParagraphComment *FirstParagraph;
414 const BlockCommandComment *Returns;
415 SmallVector<const ParamCommandComment *, 8> Params;
416 SmallVector<const TParamCommandComment *, 4> TParams;
417 SmallVector<const BlockContentComment *, 8> MiscBlocks;
418};
419
Dmitri Gribenkoe4330a32012-09-10 20:32:42 +0000420FullCommentParts::FullCommentParts(const FullComment *C,
421 const CommandTraits &Traits) :
Dmitri Gribenko2ff84b52012-08-01 22:48:16 +0000422 Brief(NULL), FirstParagraph(NULL), Returns(NULL) {
423 for (Comment::child_iterator I = C->child_begin(), E = C->child_end();
424 I != E; ++I) {
425 const Comment *Child = *I;
426 if (!Child)
427 continue;
428 switch (Child->getCommentKind()) {
429 case Comment::NoCommentKind:
430 continue;
431
432 case Comment::ParagraphCommentKind: {
433 const ParagraphComment *PC = cast<ParagraphComment>(Child);
434 if (PC->isWhitespace())
435 break;
436 if (!FirstParagraph)
437 FirstParagraph = PC;
438
439 MiscBlocks.push_back(PC);
440 break;
441 }
442
443 case Comment::BlockCommandCommentKind: {
444 const BlockCommandComment *BCC = cast<BlockCommandComment>(Child);
Dmitri Gribenkoe4330a32012-09-10 20:32:42 +0000445 const CommandInfo *Info = Traits.getCommandInfo(BCC->getCommandID());
446 if (!Brief && Info->IsBriefCommand) {
Dmitri Gribenko2ff84b52012-08-01 22:48:16 +0000447 Brief = BCC;
448 break;
449 }
Dmitri Gribenkoe4330a32012-09-10 20:32:42 +0000450 if (!Returns && Info->IsReturnsCommand) {
Dmitri Gribenko2ff84b52012-08-01 22:48:16 +0000451 Returns = BCC;
452 break;
453 }
454 MiscBlocks.push_back(BCC);
455 break;
456 }
457
458 case Comment::ParamCommandCommentKind: {
459 const ParamCommandComment *PCC = cast<ParamCommandComment>(Child);
460 if (!PCC->hasParamName())
461 break;
462
463 if (!PCC->isDirectionExplicit() && !PCC->hasNonWhitespaceParagraph())
464 break;
465
466 Params.push_back(PCC);
467 break;
468 }
469
470 case Comment::TParamCommandCommentKind: {
471 const TParamCommandComment *TPCC = cast<TParamCommandComment>(Child);
472 if (!TPCC->hasParamName())
473 break;
474
475 if (!TPCC->hasNonWhitespaceParagraph())
476 break;
477
478 TParams.push_back(TPCC);
479 break;
480 }
481
482 case Comment::VerbatimBlockCommentKind:
Dmitri Gribenko2ff84b52012-08-01 22:48:16 +0000483 MiscBlocks.push_back(cast<BlockCommandComment>(Child));
484 break;
485
Dmitri Gribenko62290ae2012-08-09 18:20:29 +0000486 case Comment::VerbatimLineCommentKind: {
487 const VerbatimLineComment *VLC = cast<VerbatimLineComment>(Child);
Dmitri Gribenkoe4330a32012-09-10 20:32:42 +0000488 const CommandInfo *Info = Traits.getCommandInfo(VLC->getCommandID());
489 if (!Info->IsDeclarationCommand)
Dmitri Gribenko62290ae2012-08-09 18:20:29 +0000490 MiscBlocks.push_back(VLC);
491 break;
492 }
493
Dmitri Gribenko2ff84b52012-08-01 22:48:16 +0000494 case Comment::TextCommentKind:
495 case Comment::InlineCommandCommentKind:
496 case Comment::HTMLStartTagCommentKind:
497 case Comment::HTMLEndTagCommentKind:
498 case Comment::VerbatimBlockLineCommentKind:
499 case Comment::FullCommentKind:
500 llvm_unreachable("AST node of this kind can't be a child of "
501 "a FullComment");
502 }
503 }
504
505 // Sort params in order they are declared in the function prototype.
506 // Unresolved parameters are put at the end of the list in the same order
507 // they were seen in the comment.
508 std::stable_sort(Params.begin(), Params.end(),
509 ParamCommandCommentCompareIndex());
510
511 std::stable_sort(TParams.begin(), TParams.end(),
512 TParamCommandCommentComparePosition());
513}
514
515void PrintHTMLStartTagComment(const HTMLStartTagComment *C,
516 llvm::raw_svector_ostream &Result) {
517 Result << "<" << C->getTagName();
518
519 if (C->getNumAttrs() != 0) {
520 for (unsigned i = 0, e = C->getNumAttrs(); i != e; i++) {
521 Result << " ";
522 const HTMLStartTagComment::Attribute &Attr = C->getAttr(i);
523 Result << Attr.Name;
524 if (!Attr.Value.empty())
525 Result << "=\"" << Attr.Value << "\"";
526 }
527 }
528
529 if (!C->isSelfClosing())
530 Result << ">";
531 else
532 Result << "/>";
533}
534
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000535class CommentASTToHTMLConverter :
536 public ConstCommentVisitor<CommentASTToHTMLConverter> {
537public:
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000538 /// \param Str accumulator for HTML.
Dmitri Gribenko8cfabf22012-10-19 16:51:38 +0000539 CommentASTToHTMLConverter(const FullComment *FC,
Fariborz Jahanianbf967be2012-10-10 18:34:52 +0000540 SmallVectorImpl<char> &Str,
Dmitri Gribenkoe4330a32012-09-10 20:32:42 +0000541 const CommandTraits &Traits) :
Fariborz Jahanianbf967be2012-10-10 18:34:52 +0000542 FC(FC), Result(Str), Traits(Traits)
Dmitri Gribenkoe4330a32012-09-10 20:32:42 +0000543 { }
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000544
545 // Inline content.
546 void visitTextComment(const TextComment *C);
547 void visitInlineCommandComment(const InlineCommandComment *C);
548 void visitHTMLStartTagComment(const HTMLStartTagComment *C);
549 void visitHTMLEndTagComment(const HTMLEndTagComment *C);
550
551 // Block content.
552 void visitParagraphComment(const ParagraphComment *C);
553 void visitBlockCommandComment(const BlockCommandComment *C);
554 void visitParamCommandComment(const ParamCommandComment *C);
Dmitri Gribenko96b09862012-07-31 22:37:06 +0000555 void visitTParamCommandComment(const TParamCommandComment *C);
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000556 void visitVerbatimBlockComment(const VerbatimBlockComment *C);
557 void visitVerbatimBlockLineComment(const VerbatimBlockLineComment *C);
558 void visitVerbatimLineComment(const VerbatimLineComment *C);
559
560 void visitFullComment(const FullComment *C);
561
562 // Helpers.
563
564 /// Convert a paragraph that is not a block by itself (an argument to some
565 /// command).
566 void visitNonStandaloneParagraphComment(const ParagraphComment *C);
567
568 void appendToResultWithHTMLEscaping(StringRef S);
569
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000570private:
Dmitri Gribenko8cfabf22012-10-19 16:51:38 +0000571 const FullComment *FC;
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000572 /// Output stream for HTML.
573 llvm::raw_svector_ostream Result;
Dmitri Gribenkoe4330a32012-09-10 20:32:42 +0000574
575 const CommandTraits &Traits;
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000576};
577} // end unnamed namespace
578
579void CommentASTToHTMLConverter::visitTextComment(const TextComment *C) {
580 appendToResultWithHTMLEscaping(C->getText());
581}
582
583void CommentASTToHTMLConverter::visitInlineCommandComment(
584 const InlineCommandComment *C) {
Dmitri Gribenko2d66a502012-07-23 16:43:01 +0000585 // Nothing to render if no arguments supplied.
586 if (C->getNumArgs() == 0)
587 return;
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000588
Dmitri Gribenko2d66a502012-07-23 16:43:01 +0000589 // Nothing to render if argument is empty.
590 StringRef Arg0 = C->getArgText(0);
591 if (Arg0.empty())
592 return;
593
594 switch (C->getRenderKind()) {
595 case InlineCommandComment::RenderNormal:
Dmitri Gribenko59500fe2012-08-01 00:21:12 +0000596 for (unsigned i = 0, e = C->getNumArgs(); i != e; ++i) {
597 appendToResultWithHTMLEscaping(C->getArgText(i));
598 Result << " ";
599 }
Dmitri Gribenko2d66a502012-07-23 16:43:01 +0000600 return;
601
602 case InlineCommandComment::RenderBold:
603 assert(C->getNumArgs() == 1);
Dmitri Gribenko59500fe2012-08-01 00:21:12 +0000604 Result << "<b>";
605 appendToResultWithHTMLEscaping(Arg0);
606 Result << "</b>";
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000607 return;
Dmitri Gribenko2d66a502012-07-23 16:43:01 +0000608 case InlineCommandComment::RenderMonospaced:
609 assert(C->getNumArgs() == 1);
Dmitri Gribenko59500fe2012-08-01 00:21:12 +0000610 Result << "<tt>";
611 appendToResultWithHTMLEscaping(Arg0);
612 Result<< "</tt>";
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000613 return;
Dmitri Gribenko2d66a502012-07-23 16:43:01 +0000614 case InlineCommandComment::RenderEmphasized:
615 assert(C->getNumArgs() == 1);
Dmitri Gribenko59500fe2012-08-01 00:21:12 +0000616 Result << "<em>";
617 appendToResultWithHTMLEscaping(Arg0);
618 Result << "</em>";
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000619 return;
620 }
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000621}
622
623void CommentASTToHTMLConverter::visitHTMLStartTagComment(
624 const HTMLStartTagComment *C) {
Dmitri Gribenko2ff84b52012-08-01 22:48:16 +0000625 PrintHTMLStartTagComment(C, Result);
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000626}
627
628void CommentASTToHTMLConverter::visitHTMLEndTagComment(
629 const HTMLEndTagComment *C) {
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000630 Result << "</" << C->getTagName() << ">";
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000631}
632
633void CommentASTToHTMLConverter::visitParagraphComment(
634 const ParagraphComment *C) {
635 if (C->isWhitespace())
636 return;
637
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000638 Result << "<p>";
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000639 for (Comment::child_iterator I = C->child_begin(), E = C->child_end();
640 I != E; ++I) {
641 visit(*I);
642 }
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000643 Result << "</p>";
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000644}
645
646void CommentASTToHTMLConverter::visitBlockCommandComment(
647 const BlockCommandComment *C) {
Dmitri Gribenkoe4330a32012-09-10 20:32:42 +0000648 const CommandInfo *Info = Traits.getCommandInfo(C->getCommandID());
649 if (Info->IsBriefCommand) {
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000650 Result << "<p class=\"para-brief\">";
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000651 visitNonStandaloneParagraphComment(C->getParagraph());
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000652 Result << "</p>";
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000653 return;
654 }
Dmitri Gribenkoe4330a32012-09-10 20:32:42 +0000655 if (Info->IsReturnsCommand) {
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000656 Result << "<p class=\"para-returns\">"
657 "<span class=\"word-returns\">Returns</span> ";
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000658 visitNonStandaloneParagraphComment(C->getParagraph());
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000659 Result << "</p>";
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000660 return;
661 }
662 // We don't know anything about this command. Just render the paragraph.
663 visit(C->getParagraph());
664}
665
666void CommentASTToHTMLConverter::visitParamCommandComment(
667 const ParamCommandComment *C) {
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000668 if (C->isParamIndexValid()) {
669 Result << "<dt class=\"param-name-index-"
670 << C->getParamIndex()
671 << "\">";
Fariborz Jahanian262e60c2012-10-18 21:42:42 +0000672 appendToResultWithHTMLEscaping(C->getParamName(FC));
673 } else {
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000674 Result << "<dt class=\"param-name-index-invalid\">";
Fariborz Jahanian262e60c2012-10-18 21:42:42 +0000675 appendToResultWithHTMLEscaping(C->getParamNameAsWritten());
676 }
Dmitri Gribenko59500fe2012-08-01 00:21:12 +0000677 Result << "</dt>";
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000678
679 if (C->isParamIndexValid()) {
680 Result << "<dd class=\"param-descr-index-"
681 << C->getParamIndex()
682 << "\">";
683 } else
684 Result << "<dd class=\"param-descr-index-invalid\">";
685
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000686 visitNonStandaloneParagraphComment(C->getParagraph());
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000687 Result << "</dd>";
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000688}
689
Dmitri Gribenko96b09862012-07-31 22:37:06 +0000690void CommentASTToHTMLConverter::visitTParamCommandComment(
691 const TParamCommandComment *C) {
692 if (C->isPositionValid()) {
693 if (C->getDepth() == 1)
Dmitri Gribenko6a425522012-08-01 23:47:30 +0000694 Result << "<dt class=\"tparam-name-index-"
Dmitri Gribenko96b09862012-07-31 22:37:06 +0000695 << C->getIndex(0)
696 << "\">";
697 else
Dmitri Gribenko6a425522012-08-01 23:47:30 +0000698 Result << "<dt class=\"tparam-name-index-other\">";
Fariborz Jahanian262e60c2012-10-18 21:42:42 +0000699 appendToResultWithHTMLEscaping(C->getParamName(FC));
700 } else {
Dmitri Gribenko96b09862012-07-31 22:37:06 +0000701 Result << "<dt class=\"tparam-name-index-invalid\">";
Fariborz Jahanian262e60c2012-10-18 21:42:42 +0000702 appendToResultWithHTMLEscaping(C->getParamNameAsWritten());
703 }
704
Dmitri Gribenko59500fe2012-08-01 00:21:12 +0000705 Result << "</dt>";
Dmitri Gribenko96b09862012-07-31 22:37:06 +0000706
707 if (C->isPositionValid()) {
708 if (C->getDepth() == 1)
709 Result << "<dd class=\"tparam-descr-index-"
710 << C->getIndex(0)
711 << "\">";
712 else
713 Result << "<dd class=\"tparam-descr-index-other\">";
714 } else
715 Result << "<dd class=\"tparam-descr-index-invalid\">";
716
717 visitNonStandaloneParagraphComment(C->getParagraph());
718 Result << "</dd>";
719}
720
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000721void CommentASTToHTMLConverter::visitVerbatimBlockComment(
722 const VerbatimBlockComment *C) {
723 unsigned NumLines = C->getNumLines();
724 if (NumLines == 0)
725 return;
726
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000727 Result << "<pre>";
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000728 for (unsigned i = 0; i != NumLines; ++i) {
729 appendToResultWithHTMLEscaping(C->getText(i));
730 if (i + 1 != NumLines)
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000731 Result << '\n';
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000732 }
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000733 Result << "</pre>";
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000734}
735
736void CommentASTToHTMLConverter::visitVerbatimBlockLineComment(
737 const VerbatimBlockLineComment *C) {
738 llvm_unreachable("should not see this AST node");
739}
740
741void CommentASTToHTMLConverter::visitVerbatimLineComment(
742 const VerbatimLineComment *C) {
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000743 Result << "<pre>";
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000744 appendToResultWithHTMLEscaping(C->getText());
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000745 Result << "</pre>";
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000746}
747
748void CommentASTToHTMLConverter::visitFullComment(const FullComment *C) {
Dmitri Gribenkoe4330a32012-09-10 20:32:42 +0000749 FullCommentParts Parts(C, Traits);
Dmitri Gribenko96b09862012-07-31 22:37:06 +0000750
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000751 bool FirstParagraphIsBrief = false;
Dmitri Gribenko2ff84b52012-08-01 22:48:16 +0000752 if (Parts.Brief)
753 visit(Parts.Brief);
754 else if (Parts.FirstParagraph) {
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000755 Result << "<p class=\"para-brief\">";
Dmitri Gribenko2ff84b52012-08-01 22:48:16 +0000756 visitNonStandaloneParagraphComment(Parts.FirstParagraph);
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000757 Result << "</p>";
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000758 FirstParagraphIsBrief = true;
759 }
760
Dmitri Gribenko2ff84b52012-08-01 22:48:16 +0000761 for (unsigned i = 0, e = Parts.MiscBlocks.size(); i != e; ++i) {
762 const Comment *C = Parts.MiscBlocks[i];
763 if (FirstParagraphIsBrief && C == Parts.FirstParagraph)
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000764 continue;
765 visit(C);
766 }
767
Dmitri Gribenko2ff84b52012-08-01 22:48:16 +0000768 if (Parts.TParams.size() != 0) {
Dmitri Gribenko96b09862012-07-31 22:37:06 +0000769 Result << "<dl>";
Dmitri Gribenko2ff84b52012-08-01 22:48:16 +0000770 for (unsigned i = 0, e = Parts.TParams.size(); i != e; ++i)
771 visit(Parts.TParams[i]);
Dmitri Gribenko96b09862012-07-31 22:37:06 +0000772 Result << "</dl>";
773 }
774
Dmitri Gribenko2ff84b52012-08-01 22:48:16 +0000775 if (Parts.Params.size() != 0) {
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000776 Result << "<dl>";
Dmitri Gribenko2ff84b52012-08-01 22:48:16 +0000777 for (unsigned i = 0, e = Parts.Params.size(); i != e; ++i)
778 visit(Parts.Params[i]);
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000779 Result << "</dl>";
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000780 }
781
Dmitri Gribenko2ff84b52012-08-01 22:48:16 +0000782 if (Parts.Returns)
783 visit(Parts.Returns);
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000784
785 Result.flush();
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000786}
787
788void CommentASTToHTMLConverter::visitNonStandaloneParagraphComment(
789 const ParagraphComment *C) {
790 if (!C)
791 return;
792
793 for (Comment::child_iterator I = C->child_begin(), E = C->child_end();
794 I != E; ++I) {
795 visit(*I);
796 }
797}
798
799void CommentASTToHTMLConverter::appendToResultWithHTMLEscaping(StringRef S) {
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000800 for (StringRef::iterator I = S.begin(), E = S.end(); I != E; ++I) {
801 const char C = *I;
802 switch (C) {
803 case '&':
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000804 Result << "&amp;";
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000805 break;
806 case '<':
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000807 Result << "&lt;";
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000808 break;
809 case '>':
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000810 Result << "&gt;";
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000811 break;
812 case '"':
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000813 Result << "&quot;";
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000814 break;
815 case '\'':
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000816 Result << "&#39;";
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000817 break;
818 case '/':
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000819 Result << "&#47;";
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000820 break;
821 default:
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000822 Result << C;
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000823 break;
824 }
825 }
826}
827
828extern "C" {
829
830CXString clang_HTMLTagComment_getAsString(CXComment CXC) {
831 const HTMLTagComment *HTC = getASTNodeAs<HTMLTagComment>(CXC);
832 if (!HTC)
833 return createCXString((const char *) 0);
834
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000835 SmallString<128> HTML;
Fariborz Jahanianbf967be2012-10-10 18:34:52 +0000836 CommentASTToHTMLConverter Converter(0, HTML, getCommandTraits(CXC));
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000837 Converter.visit(HTC);
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000838 return createCXString(HTML.str(), /* DupString = */ true);
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000839}
840
841CXString clang_FullComment_getAsHTML(CXComment CXC) {
842 const FullComment *FC = getASTNodeAs<FullComment>(CXC);
843 if (!FC)
844 return createCXString((const char *) 0);
845
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000846 SmallString<1024> HTML;
Dmitri Gribenko8cfabf22012-10-19 16:51:38 +0000847 CommentASTToHTMLConverter Converter(FC, HTML, getCommandTraits(CXC));
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000848 Converter.visit(FC);
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000849 return createCXString(HTML.str(), /* DupString = */ true);
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000850}
851
852} // end extern "C"
853
Dmitri Gribenkof303d4c2012-08-07 17:54:38 +0000854namespace {
855class CommentASTToXMLConverter :
856 public ConstCommentVisitor<CommentASTToXMLConverter> {
857public:
858 /// \param Str accumulator for XML.
Dmitri Gribenko8cfabf22012-10-19 16:51:38 +0000859 CommentASTToXMLConverter(const FullComment *FC,
Fariborz Jahanianbf967be2012-10-10 18:34:52 +0000860 SmallVectorImpl<char> &Str,
Dmitri Gribenkoe4330a32012-09-10 20:32:42 +0000861 const CommandTraits &Traits,
862 const SourceManager &SM) :
Fariborz Jahanianbf967be2012-10-10 18:34:52 +0000863 FC(FC), Result(Str), Traits(Traits), SM(SM) { }
Dmitri Gribenkof303d4c2012-08-07 17:54:38 +0000864
865 // Inline content.
866 void visitTextComment(const TextComment *C);
867 void visitInlineCommandComment(const InlineCommandComment *C);
868 void visitHTMLStartTagComment(const HTMLStartTagComment *C);
869 void visitHTMLEndTagComment(const HTMLEndTagComment *C);
870
871 // Block content.
872 void visitParagraphComment(const ParagraphComment *C);
873 void visitBlockCommandComment(const BlockCommandComment *C);
874 void visitParamCommandComment(const ParamCommandComment *C);
875 void visitTParamCommandComment(const TParamCommandComment *C);
876 void visitVerbatimBlockComment(const VerbatimBlockComment *C);
877 void visitVerbatimBlockLineComment(const VerbatimBlockLineComment *C);
878 void visitVerbatimLineComment(const VerbatimLineComment *C);
879
880 void visitFullComment(const FullComment *C);
881
882 // Helpers.
883 void appendToResultWithXMLEscaping(StringRef S);
884
885private:
Dmitri Gribenko8cfabf22012-10-19 16:51:38 +0000886 const FullComment *FC;
887
Dmitri Gribenkof303d4c2012-08-07 17:54:38 +0000888 /// Output stream for XML.
889 llvm::raw_svector_ostream Result;
Dmitri Gribenkoe4330a32012-09-10 20:32:42 +0000890
891 const CommandTraits &Traits;
892 const SourceManager &SM;
Dmitri Gribenkof303d4c2012-08-07 17:54:38 +0000893};
Dmitri Gribenko7c984992012-10-25 18:28:26 +0000894
895void getSourceTextOfDeclaration(const DeclInfo *ThisDecl,
896 SmallVectorImpl<char> &Str) {
897 ASTContext &Context = ThisDecl->CurrentDecl->getASTContext();
898 const LangOptions &LangOpts = Context.getLangOpts();
899 llvm::raw_svector_ostream OS(Str);
900 PrintingPolicy PPolicy(LangOpts);
901 PPolicy.SuppressAttributes = true;
902 PPolicy.TerseOutput = true;
903 ThisDecl->CurrentDecl->print(OS, PPolicy,
904 /*Indentation*/0, /*PrintInstantiation*/true);
905}
906
Dmitri Gribenkof303d4c2012-08-07 17:54:38 +0000907} // end unnamed namespace
908
909void CommentASTToXMLConverter::visitTextComment(const TextComment *C) {
910 appendToResultWithXMLEscaping(C->getText());
911}
912
913void CommentASTToXMLConverter::visitInlineCommandComment(const InlineCommandComment *C) {
914 // Nothing to render if no arguments supplied.
915 if (C->getNumArgs() == 0)
916 return;
917
918 // Nothing to render if argument is empty.
919 StringRef Arg0 = C->getArgText(0);
920 if (Arg0.empty())
921 return;
922
923 switch (C->getRenderKind()) {
924 case InlineCommandComment::RenderNormal:
925 for (unsigned i = 0, e = C->getNumArgs(); i != e; ++i) {
926 appendToResultWithXMLEscaping(C->getArgText(i));
927 Result << " ";
928 }
929 return;
930 case InlineCommandComment::RenderBold:
931 assert(C->getNumArgs() == 1);
932 Result << "<bold>";
933 appendToResultWithXMLEscaping(Arg0);
934 Result << "</bold>";
935 return;
936 case InlineCommandComment::RenderMonospaced:
937 assert(C->getNumArgs() == 1);
938 Result << "<monospaced>";
939 appendToResultWithXMLEscaping(Arg0);
940 Result << "</monospaced>";
941 return;
942 case InlineCommandComment::RenderEmphasized:
943 assert(C->getNumArgs() == 1);
944 Result << "<emphasized>";
945 appendToResultWithXMLEscaping(Arg0);
946 Result << "</emphasized>";
947 return;
948 }
949}
950
951void CommentASTToXMLConverter::visitHTMLStartTagComment(const HTMLStartTagComment *C) {
952 Result << "<rawHTML><![CDATA[";
953 PrintHTMLStartTagComment(C, Result);
954 Result << "]]></rawHTML>";
955}
956
957void CommentASTToXMLConverter::visitHTMLEndTagComment(const HTMLEndTagComment *C) {
958 Result << "<rawHTML>&lt;/" << C->getTagName() << "&gt;</rawHTML>";
959}
960
961void CommentASTToXMLConverter::visitParagraphComment(const ParagraphComment *C) {
962 if (C->isWhitespace())
963 return;
964
965 Result << "<Para>";
966 for (Comment::child_iterator I = C->child_begin(), E = C->child_end();
967 I != E; ++I) {
968 visit(*I);
969 }
970 Result << "</Para>";
971}
972
973void CommentASTToXMLConverter::visitBlockCommandComment(const BlockCommandComment *C) {
974 visit(C->getParagraph());
975}
976
977void CommentASTToXMLConverter::visitParamCommandComment(const ParamCommandComment *C) {
978 Result << "<Parameter><Name>";
Fariborz Jahanian262e60c2012-10-18 21:42:42 +0000979 appendToResultWithXMLEscaping(C->isParamIndexValid() ? C->getParamName(FC)
980 : C->getParamNameAsWritten());
Dmitri Gribenkof303d4c2012-08-07 17:54:38 +0000981 Result << "</Name>";
982
983 if (C->isParamIndexValid())
984 Result << "<Index>" << C->getParamIndex() << "</Index>";
985
986 Result << "<Direction isExplicit=\"" << C->isDirectionExplicit() << "\">";
987 switch (C->getDirection()) {
988 case ParamCommandComment::In:
989 Result << "in";
990 break;
991 case ParamCommandComment::Out:
992 Result << "out";
993 break;
994 case ParamCommandComment::InOut:
995 Result << "in,out";
996 break;
997 }
998 Result << "</Direction><Discussion>";
999 visit(C->getParagraph());
1000 Result << "</Discussion></Parameter>";
1001}
1002
1003void CommentASTToXMLConverter::visitTParamCommandComment(
1004 const TParamCommandComment *C) {
1005 Result << "<Parameter><Name>";
Fariborz Jahanian262e60c2012-10-18 21:42:42 +00001006 appendToResultWithXMLEscaping(C->isPositionValid() ? C->getParamName(FC)
1007 : C->getParamNameAsWritten());
Dmitri Gribenkof303d4c2012-08-07 17:54:38 +00001008 Result << "</Name>";
1009
1010 if (C->isPositionValid() && C->getDepth() == 1) {
1011 Result << "<Index>" << C->getIndex(0) << "</Index>";
1012 }
1013
1014 Result << "<Discussion>";
1015 visit(C->getParagraph());
1016 Result << "</Discussion></Parameter>";
1017}
1018
1019void CommentASTToXMLConverter::visitVerbatimBlockComment(
1020 const VerbatimBlockComment *C) {
1021 unsigned NumLines = C->getNumLines();
1022 if (NumLines == 0)
1023 return;
1024
Dmitri Gribenkoe4330a32012-09-10 20:32:42 +00001025 Result << llvm::StringSwitch<const char *>(C->getCommandName(Traits))
Dmitri Gribenko6cd44202012-08-08 22:10:24 +00001026 .Case("code", "<Verbatim xml:space=\"preserve\" kind=\"code\">")
1027 .Default("<Verbatim xml:space=\"preserve\" kind=\"verbatim\">");
Dmitri Gribenkof303d4c2012-08-07 17:54:38 +00001028 for (unsigned i = 0; i != NumLines; ++i) {
1029 appendToResultWithXMLEscaping(C->getText(i));
1030 if (i + 1 != NumLines)
1031 Result << '\n';
1032 }
1033 Result << "</Verbatim>";
1034}
1035
1036void CommentASTToXMLConverter::visitVerbatimBlockLineComment(
1037 const VerbatimBlockLineComment *C) {
1038 llvm_unreachable("should not see this AST node");
1039}
1040
1041void CommentASTToXMLConverter::visitVerbatimLineComment(
1042 const VerbatimLineComment *C) {
Dmitri Gribenko6cd44202012-08-08 22:10:24 +00001043 Result << "<Verbatim xml:space=\"preserve\" kind=\"verbatim\">";
Dmitri Gribenkof303d4c2012-08-07 17:54:38 +00001044 appendToResultWithXMLEscaping(C->getText());
1045 Result << "</Verbatim>";
1046}
1047
1048void CommentASTToXMLConverter::visitFullComment(const FullComment *C) {
Dmitri Gribenkoe4330a32012-09-10 20:32:42 +00001049 FullCommentParts Parts(C, Traits);
Dmitri Gribenkof303d4c2012-08-07 17:54:38 +00001050
1051 const DeclInfo *DI = C->getDeclInfo();
1052 StringRef RootEndTag;
1053 if (DI) {
1054 switch (DI->getKind()) {
1055 case DeclInfo::OtherKind:
1056 RootEndTag = "</Other>";
1057 Result << "<Other";
1058 break;
1059 case DeclInfo::FunctionKind:
1060 RootEndTag = "</Function>";
1061 Result << "<Function";
1062 switch (DI->TemplateKind) {
1063 case DeclInfo::NotTemplate:
1064 break;
1065 case DeclInfo::Template:
1066 Result << " templateKind=\"template\"";
1067 break;
1068 case DeclInfo::TemplateSpecialization:
1069 Result << " templateKind=\"specialization\"";
1070 break;
1071 case DeclInfo::TemplatePartialSpecialization:
1072 llvm_unreachable("partial specializations of functions "
1073 "are not allowed in C++");
1074 }
1075 if (DI->IsInstanceMethod)
1076 Result << " isInstanceMethod=\"1\"";
1077 if (DI->IsClassMethod)
1078 Result << " isClassMethod=\"1\"";
1079 break;
1080 case DeclInfo::ClassKind:
1081 RootEndTag = "</Class>";
1082 Result << "<Class";
1083 switch (DI->TemplateKind) {
1084 case DeclInfo::NotTemplate:
1085 break;
1086 case DeclInfo::Template:
1087 Result << " templateKind=\"template\"";
1088 break;
1089 case DeclInfo::TemplateSpecialization:
1090 Result << " templateKind=\"specialization\"";
1091 break;
1092 case DeclInfo::TemplatePartialSpecialization:
1093 Result << " templateKind=\"partialSpecialization\"";
1094 break;
1095 }
1096 break;
1097 case DeclInfo::VariableKind:
1098 RootEndTag = "</Variable>";
1099 Result << "<Variable";
1100 break;
1101 case DeclInfo::NamespaceKind:
1102 RootEndTag = "</Namespace>";
1103 Result << "<Namespace";
1104 break;
1105 case DeclInfo::TypedefKind:
1106 RootEndTag = "</Typedef>";
1107 Result << "<Typedef";
1108 break;
Dmitri Gribenkocff339a2012-08-07 18:59:04 +00001109 case DeclInfo::EnumKind:
1110 RootEndTag = "</Enum>";
1111 Result << "<Enum";
1112 break;
Dmitri Gribenkof303d4c2012-08-07 17:54:38 +00001113 }
1114
1115 {
1116 // Print line and column number.
Fariborz Jahanian1bfb00d2012-10-17 21:58:03 +00001117 SourceLocation Loc = DI->CurrentDecl->getLocation();
Dmitri Gribenkof303d4c2012-08-07 17:54:38 +00001118 std::pair<FileID, unsigned> LocInfo = SM.getDecomposedLoc(Loc);
1119 FileID FID = LocInfo.first;
1120 unsigned FileOffset = LocInfo.second;
1121
1122 if (!FID.isInvalid()) {
1123 if (const FileEntry *FE = SM.getFileEntryForID(FID)) {
1124 Result << " file=\"";
1125 appendToResultWithXMLEscaping(FE->getName());
1126 Result << "\"";
1127 }
1128 Result << " line=\"" << SM.getLineNumber(FID, FileOffset)
1129 << "\" column=\"" << SM.getColumnNumber(FID, FileOffset)
1130 << "\"";
1131 }
1132 }
1133
1134 // Finish the root tag.
1135 Result << ">";
1136
1137 bool FoundName = false;
Fariborz Jahanianbf967be2012-10-10 18:34:52 +00001138 if (const NamedDecl *ND = dyn_cast<NamedDecl>(DI->CommentDecl)) {
Dmitri Gribenkof303d4c2012-08-07 17:54:38 +00001139 if (DeclarationName DeclName = ND->getDeclName()) {
1140 Result << "<Name>";
1141 std::string Name = DeclName.getAsString();
1142 appendToResultWithXMLEscaping(Name);
1143 FoundName = true;
1144 Result << "</Name>";
1145 }
1146 }
1147 if (!FoundName)
1148 Result << "<Name>&lt;anonymous&gt;</Name>";
1149
1150 {
1151 // Print USR.
1152 SmallString<128> USR;
Fariborz Jahanianbf967be2012-10-10 18:34:52 +00001153 cxcursor::getDeclCursorUSR(DI->CommentDecl, USR);
Dmitri Gribenkof303d4c2012-08-07 17:54:38 +00001154 if (!USR.empty()) {
1155 Result << "<USR>";
1156 appendToResultWithXMLEscaping(USR);
1157 Result << "</USR>";
1158 }
1159 }
1160 } else {
1161 // No DeclInfo -- just emit some root tag and name tag.
1162 RootEndTag = "</Other>";
1163 Result << "<Other><Name>unknown</Name>";
1164 }
1165
Dmitri Gribenko7c984992012-10-25 18:28:26 +00001166 {
1167 // Pretty-print the declaration.
1168 Result << "<Declaration>";
1169 SmallString<128> Declaration;
1170 getSourceTextOfDeclaration(DI, Declaration);
1171 appendToResultWithXMLEscaping(Declaration);
1172 Result << "</Declaration>";
1173 }
1174
Dmitri Gribenkof303d4c2012-08-07 17:54:38 +00001175 bool FirstParagraphIsBrief = false;
1176 if (Parts.Brief) {
1177 Result << "<Abstract>";
1178 visit(Parts.Brief);
1179 Result << "</Abstract>";
1180 } else if (Parts.FirstParagraph) {
1181 Result << "<Abstract>";
1182 visit(Parts.FirstParagraph);
1183 Result << "</Abstract>";
1184 FirstParagraphIsBrief = true;
1185 }
1186
1187 if (Parts.TParams.size() != 0) {
1188 Result << "<TemplateParameters>";
1189 for (unsigned i = 0, e = Parts.TParams.size(); i != e; ++i)
1190 visit(Parts.TParams[i]);
1191 Result << "</TemplateParameters>";
1192 }
1193
1194 if (Parts.Params.size() != 0) {
1195 Result << "<Parameters>";
1196 for (unsigned i = 0, e = Parts.Params.size(); i != e; ++i)
1197 visit(Parts.Params[i]);
1198 Result << "</Parameters>";
1199 }
1200
1201 if (Parts.Returns) {
1202 Result << "<ResultDiscussion>";
1203 visit(Parts.Returns);
1204 Result << "</ResultDiscussion>";
1205 }
Fariborz Jahanian257e2e82012-09-28 22:35:49 +00001206
Fariborz Jahanianbf967be2012-10-10 18:34:52 +00001207 if (DI->CommentDecl->hasAttrs()) {
1208 const AttrVec &Attrs = DI->CommentDecl->getAttrs();
Fariborz Jahanianfaab5612012-10-01 18:42:25 +00001209 for (unsigned i = 0, e = Attrs.size(); i != e; i++) {
1210 const AvailabilityAttr *AA = dyn_cast<AvailabilityAttr>(Attrs[i]);
Fariborz Jahanian2a465332012-10-02 20:05:47 +00001211 if (!AA) {
Fariborz Jahanian8da68b82012-10-02 23:01:04 +00001212 if (const DeprecatedAttr *DA = dyn_cast<DeprecatedAttr>(Attrs[i])) {
1213 if (DA->getMessage().empty())
1214 Result << "<Deprecated/>";
1215 else {
Dmitri Gribenko7d9c9752012-10-03 09:04:56 +00001216 Result << "<Deprecated>";
1217 appendToResultWithXMLEscaping(DA->getMessage());
1218 Result << "</Deprecated>";
Fariborz Jahanian8da68b82012-10-02 23:01:04 +00001219 }
1220 }
1221 else if (const UnavailableAttr *UA = dyn_cast<UnavailableAttr>(Attrs[i])) {
1222 if (UA->getMessage().empty())
1223 Result << "<Unavailable/>";
1224 else {
Dmitri Gribenko7d9c9752012-10-03 09:04:56 +00001225 Result << "<Unavailable>";
1226 appendToResultWithXMLEscaping(UA->getMessage());
1227 Result << "</Unavailable>";
Fariborz Jahanian8da68b82012-10-02 23:01:04 +00001228 }
1229 }
Fariborz Jahanian257e2e82012-09-28 22:35:49 +00001230 continue;
Fariborz Jahanian2a465332012-10-02 20:05:47 +00001231 }
Fariborz Jahanianfaab5612012-10-01 18:42:25 +00001232
1233 // 'availability' attribute.
Fariborz Jahanian257e2e82012-09-28 22:35:49 +00001234 Result << "<Availability";
Fariborz Jahanianfaab5612012-10-01 18:42:25 +00001235 StringRef Distribution;
Fariborz Jahanian257e2e82012-09-28 22:35:49 +00001236 if (AA->getPlatform()) {
Fariborz Jahanianfaab5612012-10-01 18:42:25 +00001237 Distribution = AvailabilityAttr::getPrettyPlatformName(
1238 AA->getPlatform()->getName());
1239 if (Distribution.empty())
1240 Distribution = AA->getPlatform()->getName();
Fariborz Jahanian257e2e82012-09-28 22:35:49 +00001241 }
Fariborz Jahanianfaab5612012-10-01 18:42:25 +00001242 Result << " distribution=\"" << Distribution << "\">";
Fariborz Jahanian257e2e82012-09-28 22:35:49 +00001243 VersionTuple IntroducedInVersion = AA->getIntroduced();
1244 if (!IntroducedInVersion.empty()) {
Fariborz Jahanianfaab5612012-10-01 18:42:25 +00001245 Result << "<IntroducedInVersion>"
1246 << IntroducedInVersion.getAsString()
1247 << "</IntroducedInVersion>";
Fariborz Jahanian257e2e82012-09-28 22:35:49 +00001248 }
1249 VersionTuple DeprecatedInVersion = AA->getDeprecated();
1250 if (!DeprecatedInVersion.empty()) {
Fariborz Jahanianfaab5612012-10-01 18:42:25 +00001251 Result << "<DeprecatedInVersion>"
1252 << DeprecatedInVersion.getAsString()
1253 << "</DeprecatedInVersion>";
Fariborz Jahanian257e2e82012-09-28 22:35:49 +00001254 }
1255 VersionTuple RemovedAfterVersion = AA->getObsoleted();
1256 if (!RemovedAfterVersion.empty()) {
Fariborz Jahanianfaab5612012-10-01 18:42:25 +00001257 Result << "<RemovedAfterVersion>"
1258 << RemovedAfterVersion.getAsString()
1259 << "</RemovedAfterVersion>";
Fariborz Jahanian257e2e82012-09-28 22:35:49 +00001260 }
1261 StringRef DeprecationSummary = AA->getMessage();
1262 if (!DeprecationSummary.empty()) {
Dmitri Gribenko7d9c9752012-10-03 09:04:56 +00001263 Result << "<DeprecationSummary>";
1264 appendToResultWithXMLEscaping(DeprecationSummary);
1265 Result << "</DeprecationSummary>";
Fariborz Jahanian257e2e82012-09-28 22:35:49 +00001266 }
Fariborz Jahanian257e2e82012-09-28 22:35:49 +00001267 if (AA->getUnavailable())
Fariborz Jahanian8da68b82012-10-02 23:01:04 +00001268 Result << "<Unavailable/>";
Fariborz Jahanianfaab5612012-10-01 18:42:25 +00001269 Result << "</Availability>";
Fariborz Jahanian257e2e82012-09-28 22:35:49 +00001270 }
1271 }
Fariborz Jahanianfaab5612012-10-01 18:42:25 +00001272
Dmitri Gribenkof303d4c2012-08-07 17:54:38 +00001273 {
1274 bool StartTagEmitted = false;
1275 for (unsigned i = 0, e = Parts.MiscBlocks.size(); i != e; ++i) {
1276 const Comment *C = Parts.MiscBlocks[i];
1277 if (FirstParagraphIsBrief && C == Parts.FirstParagraph)
1278 continue;
1279 if (!StartTagEmitted) {
1280 Result << "<Discussion>";
1281 StartTagEmitted = true;
1282 }
1283 visit(C);
1284 }
1285 if (StartTagEmitted)
1286 Result << "</Discussion>";
1287 }
1288
1289 Result << RootEndTag;
1290
1291 Result.flush();
1292}
1293
1294void CommentASTToXMLConverter::appendToResultWithXMLEscaping(StringRef S) {
1295 for (StringRef::iterator I = S.begin(), E = S.end(); I != E; ++I) {
1296 const char C = *I;
1297 switch (C) {
1298 case '&':
1299 Result << "&amp;";
1300 break;
1301 case '<':
1302 Result << "&lt;";
1303 break;
1304 case '>':
1305 Result << "&gt;";
1306 break;
1307 case '"':
1308 Result << "&quot;";
1309 break;
1310 case '\'':
1311 Result << "&apos;";
1312 break;
1313 default:
1314 Result << C;
1315 break;
1316 }
1317 }
1318}
1319
1320extern "C" {
1321
Dmitri Gribenkoe4330a32012-09-10 20:32:42 +00001322CXString clang_FullComment_getAsXML(CXComment CXC) {
Dmitri Gribenkof303d4c2012-08-07 17:54:38 +00001323 const FullComment *FC = getASTNodeAs<FullComment>(CXC);
1324 if (!FC)
1325 return createCXString((const char *) 0);
1326
Dmitri Gribenkoe4330a32012-09-10 20:32:42 +00001327 CXTranslationUnit TU = CXC.TranslationUnit;
Dmitri Gribenkof303d4c2012-08-07 17:54:38 +00001328 SourceManager &SM = static_cast<ASTUnit *>(TU->TUData)->getSourceManager();
1329
1330 SmallString<1024> XML;
Dmitri Gribenko8cfabf22012-10-19 16:51:38 +00001331 CommentASTToXMLConverter Converter(FC, XML, getCommandTraits(CXC), SM);
Dmitri Gribenkof303d4c2012-08-07 17:54:38 +00001332 Converter.visit(FC);
1333 return createCXString(XML.str(), /* DupString = */ true);
1334}
1335
1336} // end extern "C"
1337