blob: 8c8fe74bba5a9d5d9b97e775d51778eaf58844f7 [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"
Dmitri Gribenkod1db1252012-08-09 17:33:20 +000018#include "clang/AST/CommentCommandTraits.h"
Chandler Carruthf59edb92012-12-04 09:25:21 +000019#include "clang/AST/CommentVisitor.h"
Dmitri Gribenkof303d4c2012-08-07 17:54:38 +000020#include "clang/AST/Decl.h"
Chandler Carruthf59edb92012-12-04 09:25:21 +000021#include "clang/AST/PrettyPrinter.h"
Dmitri Gribenkof303d4c2012-08-07 17:54:38 +000022#include "llvm/ADT/StringSwitch.h"
Dmitri Gribenkoae99b752012-07-20 21:34:34 +000023#include "llvm/Support/ErrorHandling.h"
Dmitri Gribenko3e63d332012-07-21 01:47:43 +000024#include "llvm/Support/raw_ostream.h"
Dmitri Gribenko221a6d72012-07-30 17:49:32 +000025#include <climits>
26
Dmitri Gribenkoae99b752012-07-20 21:34:34 +000027using namespace clang;
28using namespace clang::cxstring;
29using namespace clang::comments;
30using namespace clang::cxcomment;
31
32extern "C" {
33
34enum CXCommentKind clang_Comment_getKind(CXComment CXC) {
35 const Comment *C = getASTNode(CXC);
36 if (!C)
37 return CXComment_Null;
38
39 switch (C->getCommentKind()) {
40 case Comment::NoCommentKind:
41 return CXComment_Null;
42
43 case Comment::TextCommentKind:
44 return CXComment_Text;
45
46 case Comment::InlineCommandCommentKind:
47 return CXComment_InlineCommand;
48
49 case Comment::HTMLStartTagCommentKind:
50 return CXComment_HTMLStartTag;
51
52 case Comment::HTMLEndTagCommentKind:
53 return CXComment_HTMLEndTag;
54
55 case Comment::ParagraphCommentKind:
56 return CXComment_Paragraph;
57
58 case Comment::BlockCommandCommentKind:
59 return CXComment_BlockCommand;
60
61 case Comment::ParamCommandCommentKind:
62 return CXComment_ParamCommand;
63
Dmitri Gribenko96b09862012-07-31 22:37:06 +000064 case Comment::TParamCommandCommentKind:
65 return CXComment_TParamCommand;
66
Dmitri Gribenkoae99b752012-07-20 21:34:34 +000067 case Comment::VerbatimBlockCommentKind:
68 return CXComment_VerbatimBlockCommand;
69
70 case Comment::VerbatimBlockLineCommentKind:
71 return CXComment_VerbatimBlockLine;
72
73 case Comment::VerbatimLineCommentKind:
74 return CXComment_VerbatimLine;
75
76 case Comment::FullCommentKind:
77 return CXComment_FullComment;
78 }
79 llvm_unreachable("unknown CommentKind");
80}
81
82unsigned clang_Comment_getNumChildren(CXComment CXC) {
83 const Comment *C = getASTNode(CXC);
84 if (!C)
85 return 0;
86
87 return C->child_count();
88}
89
90CXComment clang_Comment_getChild(CXComment CXC, unsigned ChildIdx) {
91 const Comment *C = getASTNode(CXC);
92 if (!C || ChildIdx >= C->child_count())
Dmitri Gribenkoe4330a32012-09-10 20:32:42 +000093 return createCXComment(NULL, NULL);
Dmitri Gribenkoae99b752012-07-20 21:34:34 +000094
Dmitri Gribenkoe4330a32012-09-10 20:32:42 +000095 return createCXComment(*(C->child_begin() + ChildIdx), CXC.TranslationUnit);
Dmitri Gribenkoae99b752012-07-20 21:34:34 +000096}
97
98unsigned clang_Comment_isWhitespace(CXComment CXC) {
99 const Comment *C = getASTNode(CXC);
100 if (!C)
101 return false;
102
103 if (const TextComment *TC = dyn_cast<TextComment>(C))
104 return TC->isWhitespace();
105
106 if (const ParagraphComment *PC = dyn_cast<ParagraphComment>(C))
107 return PC->isWhitespace();
108
109 return false;
110}
111
112unsigned clang_InlineContentComment_hasTrailingNewline(CXComment CXC) {
113 const InlineContentComment *ICC = getASTNodeAs<InlineContentComment>(CXC);
114 if (!ICC)
115 return false;
116
117 return ICC->hasTrailingNewline();
118}
119
120CXString clang_TextComment_getText(CXComment CXC) {
121 const TextComment *TC = getASTNodeAs<TextComment>(CXC);
122 if (!TC)
123 return createCXString((const char *) 0);
124
125 return createCXString(TC->getText(), /*DupString=*/ false);
126}
127
128CXString clang_InlineCommandComment_getCommandName(CXComment CXC) {
129 const InlineCommandComment *ICC = getASTNodeAs<InlineCommandComment>(CXC);
130 if (!ICC)
131 return createCXString((const char *) 0);
132
Dmitri Gribenkoe4330a32012-09-10 20:32:42 +0000133 const CommandTraits &Traits = getCommandTraits(CXC);
134 return createCXString(ICC->getCommandName(Traits), /*DupString=*/ false);
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000135}
136
Dmitri Gribenko2d66a502012-07-23 16:43:01 +0000137enum CXCommentInlineCommandRenderKind
138clang_InlineCommandComment_getRenderKind(CXComment CXC) {
139 const InlineCommandComment *ICC = getASTNodeAs<InlineCommandComment>(CXC);
140 if (!ICC)
141 return CXCommentInlineCommandRenderKind_Normal;
142
143 switch (ICC->getRenderKind()) {
144 case InlineCommandComment::RenderNormal:
145 return CXCommentInlineCommandRenderKind_Normal;
146
147 case InlineCommandComment::RenderBold:
148 return CXCommentInlineCommandRenderKind_Bold;
149
150 case InlineCommandComment::RenderMonospaced:
151 return CXCommentInlineCommandRenderKind_Monospaced;
152
153 case InlineCommandComment::RenderEmphasized:
154 return CXCommentInlineCommandRenderKind_Emphasized;
155 }
156 llvm_unreachable("unknown InlineCommandComment::RenderKind");
157}
158
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000159unsigned clang_InlineCommandComment_getNumArgs(CXComment CXC) {
160 const InlineCommandComment *ICC = getASTNodeAs<InlineCommandComment>(CXC);
161 if (!ICC)
162 return 0;
163
164 return ICC->getNumArgs();
165}
166
167CXString clang_InlineCommandComment_getArgText(CXComment CXC,
168 unsigned ArgIdx) {
169 const InlineCommandComment *ICC = getASTNodeAs<InlineCommandComment>(CXC);
170 if (!ICC || ArgIdx >= ICC->getNumArgs())
171 return createCXString((const char *) 0);
172
173 return createCXString(ICC->getArgText(ArgIdx), /*DupString=*/ false);
174}
175
176CXString clang_HTMLTagComment_getTagName(CXComment CXC) {
177 const HTMLTagComment *HTC = getASTNodeAs<HTMLTagComment>(CXC);
178 if (!HTC)
179 return createCXString((const char *) 0);
180
181 return createCXString(HTC->getTagName(), /*DupString=*/ false);
182}
183
184unsigned clang_HTMLStartTagComment_isSelfClosing(CXComment CXC) {
185 const HTMLStartTagComment *HST = getASTNodeAs<HTMLStartTagComment>(CXC);
186 if (!HST)
187 return false;
188
189 return HST->isSelfClosing();
190}
191
192unsigned clang_HTMLStartTag_getNumAttrs(CXComment CXC) {
193 const HTMLStartTagComment *HST = getASTNodeAs<HTMLStartTagComment>(CXC);
194 if (!HST)
195 return 0;
196
197 return HST->getNumAttrs();
198}
199
200CXString clang_HTMLStartTag_getAttrName(CXComment CXC, unsigned AttrIdx) {
201 const HTMLStartTagComment *HST = getASTNodeAs<HTMLStartTagComment>(CXC);
202 if (!HST || AttrIdx >= HST->getNumAttrs())
203 return createCXString((const char *) 0);
204
205 return createCXString(HST->getAttr(AttrIdx).Name, /*DupString=*/ false);
206}
207
208CXString clang_HTMLStartTag_getAttrValue(CXComment CXC, unsigned AttrIdx) {
209 const HTMLStartTagComment *HST = getASTNodeAs<HTMLStartTagComment>(CXC);
210 if (!HST || AttrIdx >= HST->getNumAttrs())
211 return createCXString((const char *) 0);
212
213 return createCXString(HST->getAttr(AttrIdx).Value, /*DupString=*/ false);
214}
215
216CXString clang_BlockCommandComment_getCommandName(CXComment CXC) {
217 const BlockCommandComment *BCC = getASTNodeAs<BlockCommandComment>(CXC);
218 if (!BCC)
219 return createCXString((const char *) 0);
220
Dmitri Gribenkoe4330a32012-09-10 20:32:42 +0000221 const CommandTraits &Traits = getCommandTraits(CXC);
222 return createCXString(BCC->getCommandName(Traits), /*DupString=*/ false);
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000223}
224
225unsigned clang_BlockCommandComment_getNumArgs(CXComment CXC) {
226 const BlockCommandComment *BCC = getASTNodeAs<BlockCommandComment>(CXC);
227 if (!BCC)
228 return 0;
229
230 return BCC->getNumArgs();
231}
232
233CXString clang_BlockCommandComment_getArgText(CXComment CXC,
234 unsigned ArgIdx) {
235 const BlockCommandComment *BCC = getASTNodeAs<BlockCommandComment>(CXC);
236 if (!BCC || ArgIdx >= BCC->getNumArgs())
237 return createCXString((const char *) 0);
238
239 return createCXString(BCC->getArgText(ArgIdx), /*DupString=*/ false);
240}
241
242CXComment clang_BlockCommandComment_getParagraph(CXComment CXC) {
243 const BlockCommandComment *BCC = getASTNodeAs<BlockCommandComment>(CXC);
244 if (!BCC)
Dmitri Gribenkoe4330a32012-09-10 20:32:42 +0000245 return createCXComment(NULL, NULL);
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000246
Dmitri Gribenkoe4330a32012-09-10 20:32:42 +0000247 return createCXComment(BCC->getParagraph(), CXC.TranslationUnit);
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000248}
249
250CXString clang_ParamCommandComment_getParamName(CXComment CXC) {
251 const ParamCommandComment *PCC = getASTNodeAs<ParamCommandComment>(CXC);
Dmitri Gribenko1f8c5292012-07-23 19:41:49 +0000252 if (!PCC || !PCC->hasParamName())
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000253 return createCXString((const char *) 0);
254
Fariborz Jahanian262e60c2012-10-18 21:42:42 +0000255 return createCXString(PCC->getParamNameAsWritten(), /*DupString=*/ false);
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000256}
257
258unsigned clang_ParamCommandComment_isParamIndexValid(CXComment CXC) {
259 const ParamCommandComment *PCC = getASTNodeAs<ParamCommandComment>(CXC);
260 if (!PCC)
261 return false;
262
263 return PCC->isParamIndexValid();
264}
265
266unsigned clang_ParamCommandComment_getParamIndex(CXComment CXC) {
267 const ParamCommandComment *PCC = getASTNodeAs<ParamCommandComment>(CXC);
Dmitri Gribenkob7403162012-07-30 17:38:19 +0000268 if (!PCC || !PCC->isParamIndexValid())
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000269 return ParamCommandComment::InvalidParamIndex;
270
271 return PCC->getParamIndex();
272}
273
274unsigned clang_ParamCommandComment_isDirectionExplicit(CXComment CXC) {
275 const ParamCommandComment *PCC = getASTNodeAs<ParamCommandComment>(CXC);
276 if (!PCC)
277 return false;
278
279 return PCC->isDirectionExplicit();
280}
281
282enum CXCommentParamPassDirection clang_ParamCommandComment_getDirection(
283 CXComment CXC) {
284 const ParamCommandComment *PCC = getASTNodeAs<ParamCommandComment>(CXC);
285 if (!PCC)
286 return CXCommentParamPassDirection_In;
287
288 switch (PCC->getDirection()) {
289 case ParamCommandComment::In:
290 return CXCommentParamPassDirection_In;
291
292 case ParamCommandComment::Out:
293 return CXCommentParamPassDirection_Out;
294
295 case ParamCommandComment::InOut:
296 return CXCommentParamPassDirection_InOut;
297 }
298 llvm_unreachable("unknown ParamCommandComment::PassDirection");
299}
300
Dmitri Gribenko96b09862012-07-31 22:37:06 +0000301CXString clang_TParamCommandComment_getParamName(CXComment CXC) {
302 const TParamCommandComment *TPCC = getASTNodeAs<TParamCommandComment>(CXC);
303 if (!TPCC || !TPCC->hasParamName())
304 return createCXString((const char *) 0);
305
Fariborz Jahanian262e60c2012-10-18 21:42:42 +0000306 return createCXString(TPCC->getParamNameAsWritten(), /*DupString=*/ false);
Dmitri Gribenko96b09862012-07-31 22:37:06 +0000307}
308
309unsigned clang_TParamCommandComment_isParamPositionValid(CXComment CXC) {
310 const TParamCommandComment *TPCC = getASTNodeAs<TParamCommandComment>(CXC);
311 if (!TPCC)
312 return false;
313
314 return TPCC->isPositionValid();
315}
316
317unsigned clang_TParamCommandComment_getDepth(CXComment CXC) {
318 const TParamCommandComment *TPCC = getASTNodeAs<TParamCommandComment>(CXC);
319 if (!TPCC || !TPCC->isPositionValid())
320 return 0;
321
322 return TPCC->getDepth();
323}
324
325unsigned clang_TParamCommandComment_getIndex(CXComment CXC, unsigned Depth) {
326 const TParamCommandComment *TPCC = getASTNodeAs<TParamCommandComment>(CXC);
327 if (!TPCC || !TPCC->isPositionValid() || Depth >= TPCC->getDepth())
328 return 0;
329
330 return TPCC->getIndex(Depth);
331}
332
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000333CXString clang_VerbatimBlockLineComment_getText(CXComment CXC) {
334 const VerbatimBlockLineComment *VBL =
335 getASTNodeAs<VerbatimBlockLineComment>(CXC);
336 if (!VBL)
337 return createCXString((const char *) 0);
338
339 return createCXString(VBL->getText(), /*DupString=*/ false);
340}
341
342CXString clang_VerbatimLineComment_getText(CXComment CXC) {
343 const VerbatimLineComment *VLC = getASTNodeAs<VerbatimLineComment>(CXC);
344 if (!VLC)
345 return createCXString((const char *) 0);
346
347 return createCXString(VLC->getText(), /*DupString=*/ false);
348}
349
350} // end extern "C"
351
352//===----------------------------------------------------------------------===//
353// Helpers for converting comment AST to HTML.
354//===----------------------------------------------------------------------===//
355
356namespace {
357
Dmitri Gribenkoe5db09c2012-07-30 19:47:34 +0000358/// This comparison will sort parameters with valid index by index and
359/// invalid (unresolved) parameters last.
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000360class ParamCommandCommentCompareIndex {
361public:
362 bool operator()(const ParamCommandComment *LHS,
363 const ParamCommandComment *RHS) const {
Dmitri Gribenkob7403162012-07-30 17:38:19 +0000364 unsigned LHSIndex = UINT_MAX;
365 unsigned RHSIndex = UINT_MAX;
366 if (LHS->isParamIndexValid())
367 LHSIndex = LHS->getParamIndex();
368 if (RHS->isParamIndexValid())
369 RHSIndex = RHS->getParamIndex();
370
371 return LHSIndex < RHSIndex;
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000372 }
373};
374
Dmitri Gribenko96b09862012-07-31 22:37:06 +0000375/// This comparison will sort template parameters in the following order:
376/// \li real template parameters (depth = 1) in index order;
377/// \li all other names (depth > 1);
378/// \li unresolved names.
379class TParamCommandCommentComparePosition {
380public:
381 bool operator()(const TParamCommandComment *LHS,
382 const TParamCommandComment *RHS) const {
383 // Sort unresolved names last.
384 if (!LHS->isPositionValid())
385 return false;
386 if (!RHS->isPositionValid())
387 return true;
388
389 if (LHS->getDepth() > 1)
390 return false;
391 if (RHS->getDepth() > 1)
392 return true;
393
394 // Sort template parameters in index order.
395 if (LHS->getDepth() == 1 && RHS->getDepth() == 1)
396 return LHS->getIndex(0) < RHS->getIndex(0);
397
398 // Leave all other names in source order.
399 return true;
400 }
401};
402
Dmitri Gribenko2ff84b52012-08-01 22:48:16 +0000403/// Separate parts of a FullComment.
404struct FullCommentParts {
405 /// Take a full comment apart and initialize members accordingly.
Dmitri Gribenkoe4330a32012-09-10 20:32:42 +0000406 FullCommentParts(const FullComment *C,
407 const CommandTraits &Traits);
Dmitri Gribenko2ff84b52012-08-01 22:48:16 +0000408
409 const BlockContentComment *Brief;
410 const ParagraphComment *FirstParagraph;
411 const BlockCommandComment *Returns;
412 SmallVector<const ParamCommandComment *, 8> Params;
413 SmallVector<const TParamCommandComment *, 4> TParams;
414 SmallVector<const BlockContentComment *, 8> MiscBlocks;
415};
416
Dmitri Gribenkoe4330a32012-09-10 20:32:42 +0000417FullCommentParts::FullCommentParts(const FullComment *C,
418 const CommandTraits &Traits) :
Dmitri Gribenko2ff84b52012-08-01 22:48:16 +0000419 Brief(NULL), FirstParagraph(NULL), Returns(NULL) {
420 for (Comment::child_iterator I = C->child_begin(), E = C->child_end();
421 I != E; ++I) {
422 const Comment *Child = *I;
423 if (!Child)
424 continue;
425 switch (Child->getCommentKind()) {
426 case Comment::NoCommentKind:
427 continue;
428
429 case Comment::ParagraphCommentKind: {
430 const ParagraphComment *PC = cast<ParagraphComment>(Child);
431 if (PC->isWhitespace())
432 break;
433 if (!FirstParagraph)
434 FirstParagraph = PC;
435
436 MiscBlocks.push_back(PC);
437 break;
438 }
439
440 case Comment::BlockCommandCommentKind: {
441 const BlockCommandComment *BCC = cast<BlockCommandComment>(Child);
Dmitri Gribenkoe4330a32012-09-10 20:32:42 +0000442 const CommandInfo *Info = Traits.getCommandInfo(BCC->getCommandID());
443 if (!Brief && Info->IsBriefCommand) {
Dmitri Gribenko2ff84b52012-08-01 22:48:16 +0000444 Brief = BCC;
445 break;
446 }
Dmitri Gribenkoe4330a32012-09-10 20:32:42 +0000447 if (!Returns && Info->IsReturnsCommand) {
Dmitri Gribenko2ff84b52012-08-01 22:48:16 +0000448 Returns = BCC;
449 break;
450 }
451 MiscBlocks.push_back(BCC);
452 break;
453 }
454
455 case Comment::ParamCommandCommentKind: {
456 const ParamCommandComment *PCC = cast<ParamCommandComment>(Child);
457 if (!PCC->hasParamName())
458 break;
459
460 if (!PCC->isDirectionExplicit() && !PCC->hasNonWhitespaceParagraph())
461 break;
462
463 Params.push_back(PCC);
464 break;
465 }
466
467 case Comment::TParamCommandCommentKind: {
468 const TParamCommandComment *TPCC = cast<TParamCommandComment>(Child);
469 if (!TPCC->hasParamName())
470 break;
471
472 if (!TPCC->hasNonWhitespaceParagraph())
473 break;
474
475 TParams.push_back(TPCC);
476 break;
477 }
478
479 case Comment::VerbatimBlockCommentKind:
Dmitri Gribenko2ff84b52012-08-01 22:48:16 +0000480 MiscBlocks.push_back(cast<BlockCommandComment>(Child));
481 break;
482
Dmitri Gribenko62290ae2012-08-09 18:20:29 +0000483 case Comment::VerbatimLineCommentKind: {
484 const VerbatimLineComment *VLC = cast<VerbatimLineComment>(Child);
Dmitri Gribenkoe4330a32012-09-10 20:32:42 +0000485 const CommandInfo *Info = Traits.getCommandInfo(VLC->getCommandID());
486 if (!Info->IsDeclarationCommand)
Dmitri Gribenko62290ae2012-08-09 18:20:29 +0000487 MiscBlocks.push_back(VLC);
488 break;
489 }
490
Dmitri Gribenko2ff84b52012-08-01 22:48:16 +0000491 case Comment::TextCommentKind:
492 case Comment::InlineCommandCommentKind:
493 case Comment::HTMLStartTagCommentKind:
494 case Comment::HTMLEndTagCommentKind:
495 case Comment::VerbatimBlockLineCommentKind:
496 case Comment::FullCommentKind:
497 llvm_unreachable("AST node of this kind can't be a child of "
498 "a FullComment");
499 }
500 }
501
502 // Sort params in order they are declared in the function prototype.
503 // Unresolved parameters are put at the end of the list in the same order
504 // they were seen in the comment.
505 std::stable_sort(Params.begin(), Params.end(),
506 ParamCommandCommentCompareIndex());
507
508 std::stable_sort(TParams.begin(), TParams.end(),
509 TParamCommandCommentComparePosition());
510}
511
512void PrintHTMLStartTagComment(const HTMLStartTagComment *C,
513 llvm::raw_svector_ostream &Result) {
514 Result << "<" << C->getTagName();
515
516 if (C->getNumAttrs() != 0) {
517 for (unsigned i = 0, e = C->getNumAttrs(); i != e; i++) {
518 Result << " ";
519 const HTMLStartTagComment::Attribute &Attr = C->getAttr(i);
520 Result << Attr.Name;
521 if (!Attr.Value.empty())
522 Result << "=\"" << Attr.Value << "\"";
523 }
524 }
525
526 if (!C->isSelfClosing())
527 Result << ">";
528 else
529 Result << "/>";
530}
531
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000532class CommentASTToHTMLConverter :
533 public ConstCommentVisitor<CommentASTToHTMLConverter> {
534public:
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000535 /// \param Str accumulator for HTML.
Dmitri Gribenko8cfabf22012-10-19 16:51:38 +0000536 CommentASTToHTMLConverter(const FullComment *FC,
Fariborz Jahanianbf967be2012-10-10 18:34:52 +0000537 SmallVectorImpl<char> &Str,
Dmitri Gribenkoe4330a32012-09-10 20:32:42 +0000538 const CommandTraits &Traits) :
Fariborz Jahanianbf967be2012-10-10 18:34:52 +0000539 FC(FC), Result(Str), Traits(Traits)
Dmitri Gribenkoe4330a32012-09-10 20:32:42 +0000540 { }
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000541
542 // Inline content.
543 void visitTextComment(const TextComment *C);
544 void visitInlineCommandComment(const InlineCommandComment *C);
545 void visitHTMLStartTagComment(const HTMLStartTagComment *C);
546 void visitHTMLEndTagComment(const HTMLEndTagComment *C);
547
548 // Block content.
549 void visitParagraphComment(const ParagraphComment *C);
550 void visitBlockCommandComment(const BlockCommandComment *C);
551 void visitParamCommandComment(const ParamCommandComment *C);
Dmitri Gribenko96b09862012-07-31 22:37:06 +0000552 void visitTParamCommandComment(const TParamCommandComment *C);
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000553 void visitVerbatimBlockComment(const VerbatimBlockComment *C);
554 void visitVerbatimBlockLineComment(const VerbatimBlockLineComment *C);
555 void visitVerbatimLineComment(const VerbatimLineComment *C);
556
557 void visitFullComment(const FullComment *C);
558
559 // Helpers.
560
561 /// Convert a paragraph that is not a block by itself (an argument to some
562 /// command).
563 void visitNonStandaloneParagraphComment(const ParagraphComment *C);
564
565 void appendToResultWithHTMLEscaping(StringRef S);
566
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000567private:
Dmitri Gribenko8cfabf22012-10-19 16:51:38 +0000568 const FullComment *FC;
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000569 /// Output stream for HTML.
570 llvm::raw_svector_ostream Result;
Dmitri Gribenkoe4330a32012-09-10 20:32:42 +0000571
572 const CommandTraits &Traits;
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000573};
574} // end unnamed namespace
575
576void CommentASTToHTMLConverter::visitTextComment(const TextComment *C) {
577 appendToResultWithHTMLEscaping(C->getText());
578}
579
580void CommentASTToHTMLConverter::visitInlineCommandComment(
581 const InlineCommandComment *C) {
Dmitri Gribenko2d66a502012-07-23 16:43:01 +0000582 // Nothing to render if no arguments supplied.
583 if (C->getNumArgs() == 0)
584 return;
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000585
Dmitri Gribenko2d66a502012-07-23 16:43:01 +0000586 // Nothing to render if argument is empty.
587 StringRef Arg0 = C->getArgText(0);
588 if (Arg0.empty())
589 return;
590
591 switch (C->getRenderKind()) {
592 case InlineCommandComment::RenderNormal:
Dmitri Gribenko59500fe2012-08-01 00:21:12 +0000593 for (unsigned i = 0, e = C->getNumArgs(); i != e; ++i) {
594 appendToResultWithHTMLEscaping(C->getArgText(i));
595 Result << " ";
596 }
Dmitri Gribenko2d66a502012-07-23 16:43:01 +0000597 return;
598
599 case InlineCommandComment::RenderBold:
600 assert(C->getNumArgs() == 1);
Dmitri Gribenko59500fe2012-08-01 00:21:12 +0000601 Result << "<b>";
602 appendToResultWithHTMLEscaping(Arg0);
603 Result << "</b>";
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000604 return;
Dmitri Gribenko2d66a502012-07-23 16:43:01 +0000605 case InlineCommandComment::RenderMonospaced:
606 assert(C->getNumArgs() == 1);
Dmitri Gribenko59500fe2012-08-01 00:21:12 +0000607 Result << "<tt>";
608 appendToResultWithHTMLEscaping(Arg0);
609 Result<< "</tt>";
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000610 return;
Dmitri Gribenko2d66a502012-07-23 16:43:01 +0000611 case InlineCommandComment::RenderEmphasized:
612 assert(C->getNumArgs() == 1);
Dmitri Gribenko59500fe2012-08-01 00:21:12 +0000613 Result << "<em>";
614 appendToResultWithHTMLEscaping(Arg0);
615 Result << "</em>";
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000616 return;
617 }
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000618}
619
620void CommentASTToHTMLConverter::visitHTMLStartTagComment(
621 const HTMLStartTagComment *C) {
Dmitri Gribenko2ff84b52012-08-01 22:48:16 +0000622 PrintHTMLStartTagComment(C, Result);
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000623}
624
625void CommentASTToHTMLConverter::visitHTMLEndTagComment(
626 const HTMLEndTagComment *C) {
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000627 Result << "</" << C->getTagName() << ">";
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000628}
629
630void CommentASTToHTMLConverter::visitParagraphComment(
631 const ParagraphComment *C) {
632 if (C->isWhitespace())
633 return;
634
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000635 Result << "<p>";
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000636 for (Comment::child_iterator I = C->child_begin(), E = C->child_end();
637 I != E; ++I) {
638 visit(*I);
639 }
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000640 Result << "</p>";
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000641}
642
643void CommentASTToHTMLConverter::visitBlockCommandComment(
644 const BlockCommandComment *C) {
Dmitri Gribenkoe4330a32012-09-10 20:32:42 +0000645 const CommandInfo *Info = Traits.getCommandInfo(C->getCommandID());
646 if (Info->IsBriefCommand) {
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000647 Result << "<p class=\"para-brief\">";
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000648 visitNonStandaloneParagraphComment(C->getParagraph());
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000649 Result << "</p>";
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000650 return;
651 }
Dmitri Gribenkoe4330a32012-09-10 20:32:42 +0000652 if (Info->IsReturnsCommand) {
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000653 Result << "<p class=\"para-returns\">"
654 "<span class=\"word-returns\">Returns</span> ";
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000655 visitNonStandaloneParagraphComment(C->getParagraph());
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000656 Result << "</p>";
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000657 return;
658 }
659 // We don't know anything about this command. Just render the paragraph.
660 visit(C->getParagraph());
661}
662
663void CommentASTToHTMLConverter::visitParamCommandComment(
664 const ParamCommandComment *C) {
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000665 if (C->isParamIndexValid()) {
666 Result << "<dt class=\"param-name-index-"
667 << C->getParamIndex()
668 << "\">";
Fariborz Jahanian262e60c2012-10-18 21:42:42 +0000669 appendToResultWithHTMLEscaping(C->getParamName(FC));
670 } else {
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000671 Result << "<dt class=\"param-name-index-invalid\">";
Fariborz Jahanian262e60c2012-10-18 21:42:42 +0000672 appendToResultWithHTMLEscaping(C->getParamNameAsWritten());
673 }
Dmitri Gribenko59500fe2012-08-01 00:21:12 +0000674 Result << "</dt>";
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000675
676 if (C->isParamIndexValid()) {
677 Result << "<dd class=\"param-descr-index-"
678 << C->getParamIndex()
679 << "\">";
680 } else
681 Result << "<dd class=\"param-descr-index-invalid\">";
682
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000683 visitNonStandaloneParagraphComment(C->getParagraph());
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000684 Result << "</dd>";
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000685}
686
Dmitri Gribenko96b09862012-07-31 22:37:06 +0000687void CommentASTToHTMLConverter::visitTParamCommandComment(
688 const TParamCommandComment *C) {
689 if (C->isPositionValid()) {
690 if (C->getDepth() == 1)
Dmitri Gribenko6a425522012-08-01 23:47:30 +0000691 Result << "<dt class=\"tparam-name-index-"
Dmitri Gribenko96b09862012-07-31 22:37:06 +0000692 << C->getIndex(0)
693 << "\">";
694 else
Dmitri Gribenko6a425522012-08-01 23:47:30 +0000695 Result << "<dt class=\"tparam-name-index-other\">";
Fariborz Jahanian262e60c2012-10-18 21:42:42 +0000696 appendToResultWithHTMLEscaping(C->getParamName(FC));
697 } else {
Dmitri Gribenko96b09862012-07-31 22:37:06 +0000698 Result << "<dt class=\"tparam-name-index-invalid\">";
Fariborz Jahanian262e60c2012-10-18 21:42:42 +0000699 appendToResultWithHTMLEscaping(C->getParamNameAsWritten());
700 }
701
Dmitri Gribenko59500fe2012-08-01 00:21:12 +0000702 Result << "</dt>";
Dmitri Gribenko96b09862012-07-31 22:37:06 +0000703
704 if (C->isPositionValid()) {
705 if (C->getDepth() == 1)
706 Result << "<dd class=\"tparam-descr-index-"
707 << C->getIndex(0)
708 << "\">";
709 else
710 Result << "<dd class=\"tparam-descr-index-other\">";
711 } else
712 Result << "<dd class=\"tparam-descr-index-invalid\">";
713
714 visitNonStandaloneParagraphComment(C->getParagraph());
715 Result << "</dd>";
716}
717
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000718void CommentASTToHTMLConverter::visitVerbatimBlockComment(
719 const VerbatimBlockComment *C) {
720 unsigned NumLines = C->getNumLines();
721 if (NumLines == 0)
722 return;
723
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000724 Result << "<pre>";
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000725 for (unsigned i = 0; i != NumLines; ++i) {
726 appendToResultWithHTMLEscaping(C->getText(i));
727 if (i + 1 != NumLines)
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000728 Result << '\n';
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000729 }
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000730 Result << "</pre>";
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000731}
732
733void CommentASTToHTMLConverter::visitVerbatimBlockLineComment(
734 const VerbatimBlockLineComment *C) {
735 llvm_unreachable("should not see this AST node");
736}
737
738void CommentASTToHTMLConverter::visitVerbatimLineComment(
739 const VerbatimLineComment *C) {
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000740 Result << "<pre>";
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000741 appendToResultWithHTMLEscaping(C->getText());
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000742 Result << "</pre>";
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000743}
744
745void CommentASTToHTMLConverter::visitFullComment(const FullComment *C) {
Dmitri Gribenkoe4330a32012-09-10 20:32:42 +0000746 FullCommentParts Parts(C, Traits);
Dmitri Gribenko96b09862012-07-31 22:37:06 +0000747
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000748 bool FirstParagraphIsBrief = false;
Dmitri Gribenko2ff84b52012-08-01 22:48:16 +0000749 if (Parts.Brief)
750 visit(Parts.Brief);
751 else if (Parts.FirstParagraph) {
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000752 Result << "<p class=\"para-brief\">";
Dmitri Gribenko2ff84b52012-08-01 22:48:16 +0000753 visitNonStandaloneParagraphComment(Parts.FirstParagraph);
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000754 Result << "</p>";
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000755 FirstParagraphIsBrief = true;
756 }
757
Dmitri Gribenko2ff84b52012-08-01 22:48:16 +0000758 for (unsigned i = 0, e = Parts.MiscBlocks.size(); i != e; ++i) {
759 const Comment *C = Parts.MiscBlocks[i];
760 if (FirstParagraphIsBrief && C == Parts.FirstParagraph)
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000761 continue;
762 visit(C);
763 }
764
Dmitri Gribenko2ff84b52012-08-01 22:48:16 +0000765 if (Parts.TParams.size() != 0) {
Dmitri Gribenko96b09862012-07-31 22:37:06 +0000766 Result << "<dl>";
Dmitri Gribenko2ff84b52012-08-01 22:48:16 +0000767 for (unsigned i = 0, e = Parts.TParams.size(); i != e; ++i)
768 visit(Parts.TParams[i]);
Dmitri Gribenko96b09862012-07-31 22:37:06 +0000769 Result << "</dl>";
770 }
771
Dmitri Gribenko2ff84b52012-08-01 22:48:16 +0000772 if (Parts.Params.size() != 0) {
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000773 Result << "<dl>";
Dmitri Gribenko2ff84b52012-08-01 22:48:16 +0000774 for (unsigned i = 0, e = Parts.Params.size(); i != e; ++i)
775 visit(Parts.Params[i]);
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000776 Result << "</dl>";
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000777 }
778
Dmitri Gribenko2ff84b52012-08-01 22:48:16 +0000779 if (Parts.Returns)
780 visit(Parts.Returns);
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000781
782 Result.flush();
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000783}
784
785void CommentASTToHTMLConverter::visitNonStandaloneParagraphComment(
786 const ParagraphComment *C) {
787 if (!C)
788 return;
789
790 for (Comment::child_iterator I = C->child_begin(), E = C->child_end();
791 I != E; ++I) {
792 visit(*I);
793 }
794}
795
796void CommentASTToHTMLConverter::appendToResultWithHTMLEscaping(StringRef S) {
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000797 for (StringRef::iterator I = S.begin(), E = S.end(); I != E; ++I) {
798 const char C = *I;
799 switch (C) {
800 case '&':
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000801 Result << "&amp;";
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000802 break;
803 case '<':
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000804 Result << "&lt;";
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000805 break;
806 case '>':
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000807 Result << "&gt;";
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000808 break;
809 case '"':
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000810 Result << "&quot;";
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000811 break;
812 case '\'':
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000813 Result << "&#39;";
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000814 break;
815 case '/':
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000816 Result << "&#47;";
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000817 break;
818 default:
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000819 Result << C;
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000820 break;
821 }
822 }
823}
824
825extern "C" {
826
827CXString clang_HTMLTagComment_getAsString(CXComment CXC) {
828 const HTMLTagComment *HTC = getASTNodeAs<HTMLTagComment>(CXC);
829 if (!HTC)
830 return createCXString((const char *) 0);
831
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000832 SmallString<128> HTML;
Fariborz Jahanianbf967be2012-10-10 18:34:52 +0000833 CommentASTToHTMLConverter Converter(0, HTML, getCommandTraits(CXC));
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000834 Converter.visit(HTC);
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000835 return createCXString(HTML.str(), /* DupString = */ true);
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000836}
837
838CXString clang_FullComment_getAsHTML(CXComment CXC) {
839 const FullComment *FC = getASTNodeAs<FullComment>(CXC);
840 if (!FC)
841 return createCXString((const char *) 0);
842
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000843 SmallString<1024> HTML;
Dmitri Gribenko8cfabf22012-10-19 16:51:38 +0000844 CommentASTToHTMLConverter Converter(FC, HTML, getCommandTraits(CXC));
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000845 Converter.visit(FC);
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000846 return createCXString(HTML.str(), /* DupString = */ true);
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000847}
848
849} // end extern "C"
850
Dmitri Gribenkof303d4c2012-08-07 17:54:38 +0000851namespace {
852class CommentASTToXMLConverter :
853 public ConstCommentVisitor<CommentASTToXMLConverter> {
854public:
855 /// \param Str accumulator for XML.
Dmitri Gribenko8cfabf22012-10-19 16:51:38 +0000856 CommentASTToXMLConverter(const FullComment *FC,
Fariborz Jahanianbf967be2012-10-10 18:34:52 +0000857 SmallVectorImpl<char> &Str,
Dmitri Gribenkoe4330a32012-09-10 20:32:42 +0000858 const CommandTraits &Traits,
859 const SourceManager &SM) :
Fariborz Jahanianbf967be2012-10-10 18:34:52 +0000860 FC(FC), Result(Str), Traits(Traits), SM(SM) { }
Dmitri Gribenkof303d4c2012-08-07 17:54:38 +0000861
862 // Inline content.
863 void visitTextComment(const TextComment *C);
864 void visitInlineCommandComment(const InlineCommandComment *C);
865 void visitHTMLStartTagComment(const HTMLStartTagComment *C);
866 void visitHTMLEndTagComment(const HTMLEndTagComment *C);
867
868 // Block content.
869 void visitParagraphComment(const ParagraphComment *C);
870 void visitBlockCommandComment(const BlockCommandComment *C);
871 void visitParamCommandComment(const ParamCommandComment *C);
872 void visitTParamCommandComment(const TParamCommandComment *C);
873 void visitVerbatimBlockComment(const VerbatimBlockComment *C);
874 void visitVerbatimBlockLineComment(const VerbatimBlockLineComment *C);
875 void visitVerbatimLineComment(const VerbatimLineComment *C);
876
877 void visitFullComment(const FullComment *C);
878
879 // Helpers.
880 void appendToResultWithXMLEscaping(StringRef S);
881
882private:
Dmitri Gribenko8cfabf22012-10-19 16:51:38 +0000883 const FullComment *FC;
884
Dmitri Gribenkof303d4c2012-08-07 17:54:38 +0000885 /// Output stream for XML.
886 llvm::raw_svector_ostream Result;
Dmitri Gribenkoe4330a32012-09-10 20:32:42 +0000887
888 const CommandTraits &Traits;
889 const SourceManager &SM;
Dmitri Gribenkof303d4c2012-08-07 17:54:38 +0000890};
Dmitri Gribenko7c984992012-10-25 18:28:26 +0000891
892void getSourceTextOfDeclaration(const DeclInfo *ThisDecl,
893 SmallVectorImpl<char> &Str) {
894 ASTContext &Context = ThisDecl->CurrentDecl->getASTContext();
895 const LangOptions &LangOpts = Context.getLangOpts();
896 llvm::raw_svector_ostream OS(Str);
897 PrintingPolicy PPolicy(LangOpts);
898 PPolicy.SuppressAttributes = true;
899 PPolicy.TerseOutput = true;
900 ThisDecl->CurrentDecl->print(OS, PPolicy,
901 /*Indentation*/0, /*PrintInstantiation*/true);
902}
903
Dmitri Gribenkof303d4c2012-08-07 17:54:38 +0000904} // end unnamed namespace
905
906void CommentASTToXMLConverter::visitTextComment(const TextComment *C) {
907 appendToResultWithXMLEscaping(C->getText());
908}
909
910void CommentASTToXMLConverter::visitInlineCommandComment(const InlineCommandComment *C) {
911 // Nothing to render if no arguments supplied.
912 if (C->getNumArgs() == 0)
913 return;
914
915 // Nothing to render if argument is empty.
916 StringRef Arg0 = C->getArgText(0);
917 if (Arg0.empty())
918 return;
919
920 switch (C->getRenderKind()) {
921 case InlineCommandComment::RenderNormal:
922 for (unsigned i = 0, e = C->getNumArgs(); i != e; ++i) {
923 appendToResultWithXMLEscaping(C->getArgText(i));
924 Result << " ";
925 }
926 return;
927 case InlineCommandComment::RenderBold:
928 assert(C->getNumArgs() == 1);
929 Result << "<bold>";
930 appendToResultWithXMLEscaping(Arg0);
931 Result << "</bold>";
932 return;
933 case InlineCommandComment::RenderMonospaced:
934 assert(C->getNumArgs() == 1);
935 Result << "<monospaced>";
936 appendToResultWithXMLEscaping(Arg0);
937 Result << "</monospaced>";
938 return;
939 case InlineCommandComment::RenderEmphasized:
940 assert(C->getNumArgs() == 1);
941 Result << "<emphasized>";
942 appendToResultWithXMLEscaping(Arg0);
943 Result << "</emphasized>";
944 return;
945 }
946}
947
948void CommentASTToXMLConverter::visitHTMLStartTagComment(const HTMLStartTagComment *C) {
949 Result << "<rawHTML><![CDATA[";
950 PrintHTMLStartTagComment(C, Result);
951 Result << "]]></rawHTML>";
952}
953
954void CommentASTToXMLConverter::visitHTMLEndTagComment(const HTMLEndTagComment *C) {
955 Result << "<rawHTML>&lt;/" << C->getTagName() << "&gt;</rawHTML>";
956}
957
958void CommentASTToXMLConverter::visitParagraphComment(const ParagraphComment *C) {
959 if (C->isWhitespace())
960 return;
961
962 Result << "<Para>";
963 for (Comment::child_iterator I = C->child_begin(), E = C->child_end();
964 I != E; ++I) {
965 visit(*I);
966 }
967 Result << "</Para>";
968}
969
970void CommentASTToXMLConverter::visitBlockCommandComment(const BlockCommandComment *C) {
971 visit(C->getParagraph());
972}
973
974void CommentASTToXMLConverter::visitParamCommandComment(const ParamCommandComment *C) {
975 Result << "<Parameter><Name>";
Fariborz Jahanian262e60c2012-10-18 21:42:42 +0000976 appendToResultWithXMLEscaping(C->isParamIndexValid() ? C->getParamName(FC)
977 : C->getParamNameAsWritten());
Dmitri Gribenkof303d4c2012-08-07 17:54:38 +0000978 Result << "</Name>";
979
980 if (C->isParamIndexValid())
981 Result << "<Index>" << C->getParamIndex() << "</Index>";
982
983 Result << "<Direction isExplicit=\"" << C->isDirectionExplicit() << "\">";
984 switch (C->getDirection()) {
985 case ParamCommandComment::In:
986 Result << "in";
987 break;
988 case ParamCommandComment::Out:
989 Result << "out";
990 break;
991 case ParamCommandComment::InOut:
992 Result << "in,out";
993 break;
994 }
995 Result << "</Direction><Discussion>";
996 visit(C->getParagraph());
997 Result << "</Discussion></Parameter>";
998}
999
1000void CommentASTToXMLConverter::visitTParamCommandComment(
1001 const TParamCommandComment *C) {
1002 Result << "<Parameter><Name>";
Fariborz Jahanian262e60c2012-10-18 21:42:42 +00001003 appendToResultWithXMLEscaping(C->isPositionValid() ? C->getParamName(FC)
1004 : C->getParamNameAsWritten());
Dmitri Gribenkof303d4c2012-08-07 17:54:38 +00001005 Result << "</Name>";
1006
1007 if (C->isPositionValid() && C->getDepth() == 1) {
1008 Result << "<Index>" << C->getIndex(0) << "</Index>";
1009 }
1010
1011 Result << "<Discussion>";
1012 visit(C->getParagraph());
1013 Result << "</Discussion></Parameter>";
1014}
1015
1016void CommentASTToXMLConverter::visitVerbatimBlockComment(
1017 const VerbatimBlockComment *C) {
1018 unsigned NumLines = C->getNumLines();
1019 if (NumLines == 0)
1020 return;
1021
Dmitri Gribenkoe4330a32012-09-10 20:32:42 +00001022 Result << llvm::StringSwitch<const char *>(C->getCommandName(Traits))
Dmitri Gribenko6cd44202012-08-08 22:10:24 +00001023 .Case("code", "<Verbatim xml:space=\"preserve\" kind=\"code\">")
1024 .Default("<Verbatim xml:space=\"preserve\" kind=\"verbatim\">");
Dmitri Gribenkof303d4c2012-08-07 17:54:38 +00001025 for (unsigned i = 0; i != NumLines; ++i) {
1026 appendToResultWithXMLEscaping(C->getText(i));
1027 if (i + 1 != NumLines)
1028 Result << '\n';
1029 }
1030 Result << "</Verbatim>";
1031}
1032
1033void CommentASTToXMLConverter::visitVerbatimBlockLineComment(
1034 const VerbatimBlockLineComment *C) {
1035 llvm_unreachable("should not see this AST node");
1036}
1037
1038void CommentASTToXMLConverter::visitVerbatimLineComment(
1039 const VerbatimLineComment *C) {
Dmitri Gribenko6cd44202012-08-08 22:10:24 +00001040 Result << "<Verbatim xml:space=\"preserve\" kind=\"verbatim\">";
Dmitri Gribenkof303d4c2012-08-07 17:54:38 +00001041 appendToResultWithXMLEscaping(C->getText());
1042 Result << "</Verbatim>";
1043}
1044
1045void CommentASTToXMLConverter::visitFullComment(const FullComment *C) {
Dmitri Gribenkoe4330a32012-09-10 20:32:42 +00001046 FullCommentParts Parts(C, Traits);
Dmitri Gribenkof303d4c2012-08-07 17:54:38 +00001047
1048 const DeclInfo *DI = C->getDeclInfo();
1049 StringRef RootEndTag;
1050 if (DI) {
1051 switch (DI->getKind()) {
1052 case DeclInfo::OtherKind:
1053 RootEndTag = "</Other>";
1054 Result << "<Other";
1055 break;
1056 case DeclInfo::FunctionKind:
1057 RootEndTag = "</Function>";
1058 Result << "<Function";
1059 switch (DI->TemplateKind) {
1060 case DeclInfo::NotTemplate:
1061 break;
1062 case DeclInfo::Template:
1063 Result << " templateKind=\"template\"";
1064 break;
1065 case DeclInfo::TemplateSpecialization:
1066 Result << " templateKind=\"specialization\"";
1067 break;
1068 case DeclInfo::TemplatePartialSpecialization:
1069 llvm_unreachable("partial specializations of functions "
1070 "are not allowed in C++");
1071 }
1072 if (DI->IsInstanceMethod)
1073 Result << " isInstanceMethod=\"1\"";
1074 if (DI->IsClassMethod)
1075 Result << " isClassMethod=\"1\"";
1076 break;
1077 case DeclInfo::ClassKind:
1078 RootEndTag = "</Class>";
1079 Result << "<Class";
1080 switch (DI->TemplateKind) {
1081 case DeclInfo::NotTemplate:
1082 break;
1083 case DeclInfo::Template:
1084 Result << " templateKind=\"template\"";
1085 break;
1086 case DeclInfo::TemplateSpecialization:
1087 Result << " templateKind=\"specialization\"";
1088 break;
1089 case DeclInfo::TemplatePartialSpecialization:
1090 Result << " templateKind=\"partialSpecialization\"";
1091 break;
1092 }
1093 break;
1094 case DeclInfo::VariableKind:
1095 RootEndTag = "</Variable>";
1096 Result << "<Variable";
1097 break;
1098 case DeclInfo::NamespaceKind:
1099 RootEndTag = "</Namespace>";
1100 Result << "<Namespace";
1101 break;
1102 case DeclInfo::TypedefKind:
1103 RootEndTag = "</Typedef>";
1104 Result << "<Typedef";
1105 break;
Dmitri Gribenkocff339a2012-08-07 18:59:04 +00001106 case DeclInfo::EnumKind:
1107 RootEndTag = "</Enum>";
1108 Result << "<Enum";
1109 break;
Dmitri Gribenkof303d4c2012-08-07 17:54:38 +00001110 }
1111
1112 {
1113 // Print line and column number.
Fariborz Jahanian1bfb00d2012-10-17 21:58:03 +00001114 SourceLocation Loc = DI->CurrentDecl->getLocation();
Dmitri Gribenkof303d4c2012-08-07 17:54:38 +00001115 std::pair<FileID, unsigned> LocInfo = SM.getDecomposedLoc(Loc);
1116 FileID FID = LocInfo.first;
1117 unsigned FileOffset = LocInfo.second;
1118
1119 if (!FID.isInvalid()) {
1120 if (const FileEntry *FE = SM.getFileEntryForID(FID)) {
1121 Result << " file=\"";
1122 appendToResultWithXMLEscaping(FE->getName());
1123 Result << "\"";
1124 }
1125 Result << " line=\"" << SM.getLineNumber(FID, FileOffset)
1126 << "\" column=\"" << SM.getColumnNumber(FID, FileOffset)
1127 << "\"";
1128 }
1129 }
1130
1131 // Finish the root tag.
1132 Result << ">";
1133
1134 bool FoundName = false;
Fariborz Jahanianbf967be2012-10-10 18:34:52 +00001135 if (const NamedDecl *ND = dyn_cast<NamedDecl>(DI->CommentDecl)) {
Dmitri Gribenkof303d4c2012-08-07 17:54:38 +00001136 if (DeclarationName DeclName = ND->getDeclName()) {
1137 Result << "<Name>";
1138 std::string Name = DeclName.getAsString();
1139 appendToResultWithXMLEscaping(Name);
1140 FoundName = true;
1141 Result << "</Name>";
1142 }
1143 }
1144 if (!FoundName)
1145 Result << "<Name>&lt;anonymous&gt;</Name>";
1146
1147 {
1148 // Print USR.
1149 SmallString<128> USR;
Fariborz Jahanianbf967be2012-10-10 18:34:52 +00001150 cxcursor::getDeclCursorUSR(DI->CommentDecl, USR);
Dmitri Gribenkof303d4c2012-08-07 17:54:38 +00001151 if (!USR.empty()) {
1152 Result << "<USR>";
1153 appendToResultWithXMLEscaping(USR);
1154 Result << "</USR>";
1155 }
1156 }
1157 } else {
1158 // No DeclInfo -- just emit some root tag and name tag.
1159 RootEndTag = "</Other>";
1160 Result << "<Other><Name>unknown</Name>";
1161 }
1162
Dmitri Gribenko7c984992012-10-25 18:28:26 +00001163 {
1164 // Pretty-print the declaration.
1165 Result << "<Declaration>";
1166 SmallString<128> Declaration;
1167 getSourceTextOfDeclaration(DI, Declaration);
1168 appendToResultWithXMLEscaping(Declaration);
1169 Result << "</Declaration>";
1170 }
1171
Dmitri Gribenkof303d4c2012-08-07 17:54:38 +00001172 bool FirstParagraphIsBrief = false;
1173 if (Parts.Brief) {
1174 Result << "<Abstract>";
1175 visit(Parts.Brief);
1176 Result << "</Abstract>";
1177 } else if (Parts.FirstParagraph) {
1178 Result << "<Abstract>";
1179 visit(Parts.FirstParagraph);
1180 Result << "</Abstract>";
1181 FirstParagraphIsBrief = true;
1182 }
1183
1184 if (Parts.TParams.size() != 0) {
1185 Result << "<TemplateParameters>";
1186 for (unsigned i = 0, e = Parts.TParams.size(); i != e; ++i)
1187 visit(Parts.TParams[i]);
1188 Result << "</TemplateParameters>";
1189 }
1190
1191 if (Parts.Params.size() != 0) {
1192 Result << "<Parameters>";
1193 for (unsigned i = 0, e = Parts.Params.size(); i != e; ++i)
1194 visit(Parts.Params[i]);
1195 Result << "</Parameters>";
1196 }
1197
1198 if (Parts.Returns) {
1199 Result << "<ResultDiscussion>";
1200 visit(Parts.Returns);
1201 Result << "</ResultDiscussion>";
1202 }
Fariborz Jahanian257e2e82012-09-28 22:35:49 +00001203
Fariborz Jahanianbf967be2012-10-10 18:34:52 +00001204 if (DI->CommentDecl->hasAttrs()) {
1205 const AttrVec &Attrs = DI->CommentDecl->getAttrs();
Fariborz Jahanianfaab5612012-10-01 18:42:25 +00001206 for (unsigned i = 0, e = Attrs.size(); i != e; i++) {
1207 const AvailabilityAttr *AA = dyn_cast<AvailabilityAttr>(Attrs[i]);
Fariborz Jahanian2a465332012-10-02 20:05:47 +00001208 if (!AA) {
Fariborz Jahanian8da68b82012-10-02 23:01:04 +00001209 if (const DeprecatedAttr *DA = dyn_cast<DeprecatedAttr>(Attrs[i])) {
1210 if (DA->getMessage().empty())
1211 Result << "<Deprecated/>";
1212 else {
Dmitri Gribenko7d9c9752012-10-03 09:04:56 +00001213 Result << "<Deprecated>";
1214 appendToResultWithXMLEscaping(DA->getMessage());
1215 Result << "</Deprecated>";
Fariborz Jahanian8da68b82012-10-02 23:01:04 +00001216 }
1217 }
1218 else if (const UnavailableAttr *UA = dyn_cast<UnavailableAttr>(Attrs[i])) {
1219 if (UA->getMessage().empty())
1220 Result << "<Unavailable/>";
1221 else {
Dmitri Gribenko7d9c9752012-10-03 09:04:56 +00001222 Result << "<Unavailable>";
1223 appendToResultWithXMLEscaping(UA->getMessage());
1224 Result << "</Unavailable>";
Fariborz Jahanian8da68b82012-10-02 23:01:04 +00001225 }
1226 }
Fariborz Jahanian257e2e82012-09-28 22:35:49 +00001227 continue;
Fariborz Jahanian2a465332012-10-02 20:05:47 +00001228 }
Fariborz Jahanianfaab5612012-10-01 18:42:25 +00001229
1230 // 'availability' attribute.
Fariborz Jahanian257e2e82012-09-28 22:35:49 +00001231 Result << "<Availability";
Fariborz Jahanianfaab5612012-10-01 18:42:25 +00001232 StringRef Distribution;
Fariborz Jahanian257e2e82012-09-28 22:35:49 +00001233 if (AA->getPlatform()) {
Fariborz Jahanianfaab5612012-10-01 18:42:25 +00001234 Distribution = AvailabilityAttr::getPrettyPlatformName(
1235 AA->getPlatform()->getName());
1236 if (Distribution.empty())
1237 Distribution = AA->getPlatform()->getName();
Fariborz Jahanian257e2e82012-09-28 22:35:49 +00001238 }
Fariborz Jahanianfaab5612012-10-01 18:42:25 +00001239 Result << " distribution=\"" << Distribution << "\">";
Fariborz Jahanian257e2e82012-09-28 22:35:49 +00001240 VersionTuple IntroducedInVersion = AA->getIntroduced();
1241 if (!IntroducedInVersion.empty()) {
Fariborz Jahanianfaab5612012-10-01 18:42:25 +00001242 Result << "<IntroducedInVersion>"
1243 << IntroducedInVersion.getAsString()
1244 << "</IntroducedInVersion>";
Fariborz Jahanian257e2e82012-09-28 22:35:49 +00001245 }
1246 VersionTuple DeprecatedInVersion = AA->getDeprecated();
1247 if (!DeprecatedInVersion.empty()) {
Fariborz Jahanianfaab5612012-10-01 18:42:25 +00001248 Result << "<DeprecatedInVersion>"
1249 << DeprecatedInVersion.getAsString()
1250 << "</DeprecatedInVersion>";
Fariborz Jahanian257e2e82012-09-28 22:35:49 +00001251 }
1252 VersionTuple RemovedAfterVersion = AA->getObsoleted();
1253 if (!RemovedAfterVersion.empty()) {
Fariborz Jahanianfaab5612012-10-01 18:42:25 +00001254 Result << "<RemovedAfterVersion>"
1255 << RemovedAfterVersion.getAsString()
1256 << "</RemovedAfterVersion>";
Fariborz Jahanian257e2e82012-09-28 22:35:49 +00001257 }
1258 StringRef DeprecationSummary = AA->getMessage();
1259 if (!DeprecationSummary.empty()) {
Dmitri Gribenko7d9c9752012-10-03 09:04:56 +00001260 Result << "<DeprecationSummary>";
1261 appendToResultWithXMLEscaping(DeprecationSummary);
1262 Result << "</DeprecationSummary>";
Fariborz Jahanian257e2e82012-09-28 22:35:49 +00001263 }
Fariborz Jahanian257e2e82012-09-28 22:35:49 +00001264 if (AA->getUnavailable())
Fariborz Jahanian8da68b82012-10-02 23:01:04 +00001265 Result << "<Unavailable/>";
Fariborz Jahanianfaab5612012-10-01 18:42:25 +00001266 Result << "</Availability>";
Fariborz Jahanian257e2e82012-09-28 22:35:49 +00001267 }
1268 }
Fariborz Jahanianfaab5612012-10-01 18:42:25 +00001269
Dmitri Gribenkof303d4c2012-08-07 17:54:38 +00001270 {
1271 bool StartTagEmitted = false;
1272 for (unsigned i = 0, e = Parts.MiscBlocks.size(); i != e; ++i) {
1273 const Comment *C = Parts.MiscBlocks[i];
1274 if (FirstParagraphIsBrief && C == Parts.FirstParagraph)
1275 continue;
1276 if (!StartTagEmitted) {
1277 Result << "<Discussion>";
1278 StartTagEmitted = true;
1279 }
1280 visit(C);
1281 }
1282 if (StartTagEmitted)
1283 Result << "</Discussion>";
1284 }
1285
1286 Result << RootEndTag;
1287
1288 Result.flush();
1289}
1290
1291void CommentASTToXMLConverter::appendToResultWithXMLEscaping(StringRef S) {
1292 for (StringRef::iterator I = S.begin(), E = S.end(); I != E; ++I) {
1293 const char C = *I;
1294 switch (C) {
1295 case '&':
1296 Result << "&amp;";
1297 break;
1298 case '<':
1299 Result << "&lt;";
1300 break;
1301 case '>':
1302 Result << "&gt;";
1303 break;
1304 case '"':
1305 Result << "&quot;";
1306 break;
1307 case '\'':
1308 Result << "&apos;";
1309 break;
1310 default:
1311 Result << C;
1312 break;
1313 }
1314 }
1315}
1316
1317extern "C" {
1318
Dmitri Gribenkoe4330a32012-09-10 20:32:42 +00001319CXString clang_FullComment_getAsXML(CXComment CXC) {
Dmitri Gribenkof303d4c2012-08-07 17:54:38 +00001320 const FullComment *FC = getASTNodeAs<FullComment>(CXC);
1321 if (!FC)
1322 return createCXString((const char *) 0);
1323
Dmitri Gribenkoe4330a32012-09-10 20:32:42 +00001324 CXTranslationUnit TU = CXC.TranslationUnit;
Dmitri Gribenkof303d4c2012-08-07 17:54:38 +00001325 SourceManager &SM = static_cast<ASTUnit *>(TU->TUData)->getSourceManager();
1326
1327 SmallString<1024> XML;
Dmitri Gribenko8cfabf22012-10-19 16:51:38 +00001328 CommentASTToXMLConverter Converter(FC, XML, getCommandTraits(CXC), SM);
Dmitri Gribenkof303d4c2012-08-07 17:54:38 +00001329 Converter.visit(FC);
1330 return createCXString(XML.str(), /* DupString = */ true);
1331}
1332
1333} // end extern "C"
1334