1 //===- 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"
17 #include "CXCursor.h"
18 #include "CXTranslationUnit.h"
19 
20 #include "clang/AST/CommentVisitor.h"
21 #include "clang/AST/CommentCommandTraits.h"
22 #include "clang/AST/Decl.h"
23 #include "clang/Frontend/ASTUnit.h"
24 
25 #include "llvm/ADT/StringSwitch.h"
26 #include "llvm/Support/ErrorHandling.h"
27 #include "llvm/Support/raw_ostream.h"
28 
29 #include <climits>
30 
31 using namespace clang;
32 using namespace clang::cxstring;
33 using namespace clang::comments;
34 using namespace clang::cxcomment;
35 
36 extern "C" {
37 
38 enum CXCommentKind clang_Comment_getKind(CXComment CXC) {
39   const Comment *C = getASTNode(CXC);
40   if (!C)
41     return CXComment_Null;
42 
43   switch (C->getCommentKind()) {
44   case Comment::NoCommentKind:
45     return CXComment_Null;
46 
47   case Comment::TextCommentKind:
48     return CXComment_Text;
49 
50   case Comment::InlineCommandCommentKind:
51     return CXComment_InlineCommand;
52 
53   case Comment::HTMLStartTagCommentKind:
54     return CXComment_HTMLStartTag;
55 
56   case Comment::HTMLEndTagCommentKind:
57     return CXComment_HTMLEndTag;
58 
59   case Comment::ParagraphCommentKind:
60     return CXComment_Paragraph;
61 
62   case Comment::BlockCommandCommentKind:
63     return CXComment_BlockCommand;
64 
65   case Comment::ParamCommandCommentKind:
66     return CXComment_ParamCommand;
67 
68   case Comment::TParamCommandCommentKind:
69     return CXComment_TParamCommand;
70 
71   case Comment::VerbatimBlockCommentKind:
72     return CXComment_VerbatimBlockCommand;
73 
74   case Comment::VerbatimBlockLineCommentKind:
75     return CXComment_VerbatimBlockLine;
76 
77   case Comment::VerbatimLineCommentKind:
78     return CXComment_VerbatimLine;
79 
80   case Comment::FullCommentKind:
81     return CXComment_FullComment;
82   }
83   llvm_unreachable("unknown CommentKind");
84 }
85 
86 unsigned clang_Comment_getNumChildren(CXComment CXC) {
87   const Comment *C = getASTNode(CXC);
88   if (!C)
89     return 0;
90 
91   return C->child_count();
92 }
93 
94 CXComment clang_Comment_getChild(CXComment CXC, unsigned ChildIdx) {
95   const Comment *C = getASTNode(CXC);
96   if (!C || ChildIdx >= C->child_count())
97     return createCXComment(NULL);
98 
99   return createCXComment(*(C->child_begin() + ChildIdx));
100 }
101 
102 unsigned clang_Comment_isWhitespace(CXComment CXC) {
103   const Comment *C = getASTNode(CXC);
104   if (!C)
105     return false;
106 
107   if (const TextComment *TC = dyn_cast<TextComment>(C))
108     return TC->isWhitespace();
109 
110   if (const ParagraphComment *PC = dyn_cast<ParagraphComment>(C))
111     return PC->isWhitespace();
112 
113   return false;
114 }
115 
116 unsigned clang_InlineContentComment_hasTrailingNewline(CXComment CXC) {
117   const InlineContentComment *ICC = getASTNodeAs<InlineContentComment>(CXC);
118   if (!ICC)
119     return false;
120 
121   return ICC->hasTrailingNewline();
122 }
123 
124 CXString clang_TextComment_getText(CXComment CXC) {
125   const TextComment *TC = getASTNodeAs<TextComment>(CXC);
126   if (!TC)
127     return createCXString((const char *) 0);
128 
129   return createCXString(TC->getText(), /*DupString=*/ false);
130 }
131 
132 CXString clang_InlineCommandComment_getCommandName(CXComment CXC) {
133   const InlineCommandComment *ICC = getASTNodeAs<InlineCommandComment>(CXC);
134   if (!ICC)
135     return createCXString((const char *) 0);
136 
137   return createCXString(ICC->getCommandName(), /*DupString=*/ false);
138 }
139 
140 enum CXCommentInlineCommandRenderKind
141 clang_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 
162 unsigned 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 
170 CXString 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 
179 CXString 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 
187 unsigned 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 
195 unsigned 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 
203 CXString 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 
211 CXString 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 
219 CXString clang_BlockCommandComment_getCommandName(CXComment CXC) {
220   const BlockCommandComment *BCC = getASTNodeAs<BlockCommandComment>(CXC);
221   if (!BCC)
222     return createCXString((const char *) 0);
223 
224   return createCXString(BCC->getCommandName(), /*DupString=*/ false);
225 }
226 
227 unsigned clang_BlockCommandComment_getNumArgs(CXComment CXC) {
228   const BlockCommandComment *BCC = getASTNodeAs<BlockCommandComment>(CXC);
229   if (!BCC)
230     return 0;
231 
232   return BCC->getNumArgs();
233 }
234 
235 CXString clang_BlockCommandComment_getArgText(CXComment CXC,
236                                               unsigned ArgIdx) {
237   const BlockCommandComment *BCC = getASTNodeAs<BlockCommandComment>(CXC);
238   if (!BCC || ArgIdx >= BCC->getNumArgs())
239     return createCXString((const char *) 0);
240 
241   return createCXString(BCC->getArgText(ArgIdx), /*DupString=*/ false);
242 }
243 
244 CXComment clang_BlockCommandComment_getParagraph(CXComment CXC) {
245   const BlockCommandComment *BCC = getASTNodeAs<BlockCommandComment>(CXC);
246   if (!BCC)
247     return createCXComment(NULL);
248 
249   return createCXComment(BCC->getParagraph());
250 }
251 
252 CXString clang_ParamCommandComment_getParamName(CXComment CXC) {
253   const ParamCommandComment *PCC = getASTNodeAs<ParamCommandComment>(CXC);
254   if (!PCC || !PCC->hasParamName())
255     return createCXString((const char *) 0);
256 
257   return createCXString(PCC->getParamName(), /*DupString=*/ false);
258 }
259 
260 unsigned clang_ParamCommandComment_isParamIndexValid(CXComment CXC) {
261   const ParamCommandComment *PCC = getASTNodeAs<ParamCommandComment>(CXC);
262   if (!PCC)
263     return false;
264 
265   return PCC->isParamIndexValid();
266 }
267 
268 unsigned clang_ParamCommandComment_getParamIndex(CXComment CXC) {
269   const ParamCommandComment *PCC = getASTNodeAs<ParamCommandComment>(CXC);
270   if (!PCC || !PCC->isParamIndexValid())
271     return ParamCommandComment::InvalidParamIndex;
272 
273   return PCC->getParamIndex();
274 }
275 
276 unsigned clang_ParamCommandComment_isDirectionExplicit(CXComment CXC) {
277   const ParamCommandComment *PCC = getASTNodeAs<ParamCommandComment>(CXC);
278   if (!PCC)
279     return false;
280 
281   return PCC->isDirectionExplicit();
282 }
283 
284 enum CXCommentParamPassDirection clang_ParamCommandComment_getDirection(
285                                                             CXComment CXC) {
286   const ParamCommandComment *PCC = getASTNodeAs<ParamCommandComment>(CXC);
287   if (!PCC)
288     return CXCommentParamPassDirection_In;
289 
290   switch (PCC->getDirection()) {
291   case ParamCommandComment::In:
292     return CXCommentParamPassDirection_In;
293 
294   case ParamCommandComment::Out:
295     return CXCommentParamPassDirection_Out;
296 
297   case ParamCommandComment::InOut:
298     return CXCommentParamPassDirection_InOut;
299   }
300   llvm_unreachable("unknown ParamCommandComment::PassDirection");
301 }
302 
303 CXString clang_TParamCommandComment_getParamName(CXComment CXC) {
304   const TParamCommandComment *TPCC = getASTNodeAs<TParamCommandComment>(CXC);
305   if (!TPCC || !TPCC->hasParamName())
306     return createCXString((const char *) 0);
307 
308   return createCXString(TPCC->getParamName(), /*DupString=*/ false);
309 }
310 
311 unsigned clang_TParamCommandComment_isParamPositionValid(CXComment CXC) {
312   const TParamCommandComment *TPCC = getASTNodeAs<TParamCommandComment>(CXC);
313   if (!TPCC)
314     return false;
315 
316   return TPCC->isPositionValid();
317 }
318 
319 unsigned clang_TParamCommandComment_getDepth(CXComment CXC) {
320   const TParamCommandComment *TPCC = getASTNodeAs<TParamCommandComment>(CXC);
321   if (!TPCC || !TPCC->isPositionValid())
322     return 0;
323 
324   return TPCC->getDepth();
325 }
326 
327 unsigned clang_TParamCommandComment_getIndex(CXComment CXC, unsigned Depth) {
328   const TParamCommandComment *TPCC = getASTNodeAs<TParamCommandComment>(CXC);
329   if (!TPCC || !TPCC->isPositionValid() || Depth >= TPCC->getDepth())
330     return 0;
331 
332   return TPCC->getIndex(Depth);
333 }
334 
335 CXString clang_VerbatimBlockLineComment_getText(CXComment CXC) {
336   const VerbatimBlockLineComment *VBL =
337       getASTNodeAs<VerbatimBlockLineComment>(CXC);
338   if (!VBL)
339     return createCXString((const char *) 0);
340 
341   return createCXString(VBL->getText(), /*DupString=*/ false);
342 }
343 
344 CXString clang_VerbatimLineComment_getText(CXComment CXC) {
345   const VerbatimLineComment *VLC = getASTNodeAs<VerbatimLineComment>(CXC);
346   if (!VLC)
347     return createCXString((const char *) 0);
348 
349   return createCXString(VLC->getText(), /*DupString=*/ false);
350 }
351 
352 } // end extern "C"
353 
354 //===----------------------------------------------------------------------===//
355 // Helpers for converting comment AST to HTML.
356 //===----------------------------------------------------------------------===//
357 
358 namespace {
359 
360 /// This comparison will sort parameters with valid index by index and
361 /// invalid (unresolved) parameters last.
362 class ParamCommandCommentCompareIndex {
363 public:
364   bool operator()(const ParamCommandComment *LHS,
365                   const ParamCommandComment *RHS) const {
366     unsigned LHSIndex = UINT_MAX;
367     unsigned RHSIndex = UINT_MAX;
368     if (LHS->isParamIndexValid())
369       LHSIndex = LHS->getParamIndex();
370     if (RHS->isParamIndexValid())
371       RHSIndex = RHS->getParamIndex();
372 
373     return LHSIndex < RHSIndex;
374   }
375 };
376 
377 /// This comparison will sort template parameters in the following order:
378 /// \li real template parameters (depth = 1) in index order;
379 /// \li all other names (depth > 1);
380 /// \li unresolved names.
381 class TParamCommandCommentComparePosition {
382 public:
383   bool operator()(const TParamCommandComment *LHS,
384                   const TParamCommandComment *RHS) const {
385     // Sort unresolved names last.
386     if (!LHS->isPositionValid())
387       return false;
388     if (!RHS->isPositionValid())
389       return true;
390 
391     if (LHS->getDepth() > 1)
392       return false;
393     if (RHS->getDepth() > 1)
394       return true;
395 
396     // Sort template parameters in index order.
397     if (LHS->getDepth() == 1 && RHS->getDepth() == 1)
398       return LHS->getIndex(0) < RHS->getIndex(0);
399 
400     // Leave all other names in source order.
401     return true;
402   }
403 };
404 
405 /// Separate parts of a FullComment.
406 struct FullCommentParts {
407   /// Take a full comment apart and initialize members accordingly.
408   FullCommentParts(const FullComment *C);
409 
410   const BlockContentComment *Brief;
411   const ParagraphComment *FirstParagraph;
412   const BlockCommandComment *Returns;
413   SmallVector<const ParamCommandComment *, 8> Params;
414   SmallVector<const TParamCommandComment *, 4> TParams;
415   SmallVector<const BlockContentComment *, 8> MiscBlocks;
416 };
417 
418 FullCommentParts::FullCommentParts(const FullComment *C) :
419     Brief(NULL), FirstParagraph(NULL), Returns(NULL) {
420   const CommandTraits Traits;
421   for (Comment::child_iterator I = C->child_begin(), E = C->child_end();
422        I != E; ++I) {
423     const Comment *Child = *I;
424     if (!Child)
425       continue;
426     switch (Child->getCommentKind()) {
427     case Comment::NoCommentKind:
428       continue;
429 
430     case Comment::ParagraphCommentKind: {
431       const ParagraphComment *PC = cast<ParagraphComment>(Child);
432       if (PC->isWhitespace())
433         break;
434       if (!FirstParagraph)
435         FirstParagraph = PC;
436 
437       MiscBlocks.push_back(PC);
438       break;
439     }
440 
441     case Comment::BlockCommandCommentKind: {
442       const BlockCommandComment *BCC = cast<BlockCommandComment>(Child);
443       StringRef CommandName = BCC->getCommandName();
444       if (!Brief && Traits.isBriefCommand(CommandName)) {
445         Brief = BCC;
446         break;
447       }
448       if (!Returns && Traits.isReturnsCommand(CommandName)) {
449         Returns = BCC;
450         break;
451       }
452       MiscBlocks.push_back(BCC);
453       break;
454     }
455 
456     case Comment::ParamCommandCommentKind: {
457       const ParamCommandComment *PCC = cast<ParamCommandComment>(Child);
458       if (!PCC->hasParamName())
459         break;
460 
461       if (!PCC->isDirectionExplicit() && !PCC->hasNonWhitespaceParagraph())
462         break;
463 
464       Params.push_back(PCC);
465       break;
466     }
467 
468     case Comment::TParamCommandCommentKind: {
469       const TParamCommandComment *TPCC = cast<TParamCommandComment>(Child);
470       if (!TPCC->hasParamName())
471         break;
472 
473       if (!TPCC->hasNonWhitespaceParagraph())
474         break;
475 
476       TParams.push_back(TPCC);
477       break;
478     }
479 
480     case Comment::VerbatimBlockCommentKind:
481       MiscBlocks.push_back(cast<BlockCommandComment>(Child));
482       break;
483 
484     case Comment::VerbatimLineCommentKind: {
485       const VerbatimLineComment *VLC = cast<VerbatimLineComment>(Child);
486       if (!Traits.isDeclarationCommand(VLC->getCommandName()))
487         MiscBlocks.push_back(VLC);
488       break;
489     }
490 
491     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 
512 void 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 
532 class CommentASTToHTMLConverter :
533     public ConstCommentVisitor<CommentASTToHTMLConverter> {
534 public:
535   /// \param Str accumulator for HTML.
536   CommentASTToHTMLConverter(SmallVectorImpl<char> &Str) : Result(Str) { }
537 
538   // Inline content.
539   void visitTextComment(const TextComment *C);
540   void visitInlineCommandComment(const InlineCommandComment *C);
541   void visitHTMLStartTagComment(const HTMLStartTagComment *C);
542   void visitHTMLEndTagComment(const HTMLEndTagComment *C);
543 
544   // Block content.
545   void visitParagraphComment(const ParagraphComment *C);
546   void visitBlockCommandComment(const BlockCommandComment *C);
547   void visitParamCommandComment(const ParamCommandComment *C);
548   void visitTParamCommandComment(const TParamCommandComment *C);
549   void visitVerbatimBlockComment(const VerbatimBlockComment *C);
550   void visitVerbatimBlockLineComment(const VerbatimBlockLineComment *C);
551   void visitVerbatimLineComment(const VerbatimLineComment *C);
552 
553   void visitFullComment(const FullComment *C);
554 
555   // Helpers.
556 
557   /// Convert a paragraph that is not a block by itself (an argument to some
558   /// command).
559   void visitNonStandaloneParagraphComment(const ParagraphComment *C);
560 
561   void appendToResultWithHTMLEscaping(StringRef S);
562 
563 private:
564   const CommandTraits Traits;
565 
566   /// Output stream for HTML.
567   llvm::raw_svector_ostream Result;
568 };
569 } // end unnamed namespace
570 
571 void CommentASTToHTMLConverter::visitTextComment(const TextComment *C) {
572   appendToResultWithHTMLEscaping(C->getText());
573 }
574 
575 void CommentASTToHTMLConverter::visitInlineCommandComment(
576                                   const InlineCommandComment *C) {
577   // Nothing to render if no arguments supplied.
578   if (C->getNumArgs() == 0)
579     return;
580 
581   // Nothing to render if argument is empty.
582   StringRef Arg0 = C->getArgText(0);
583   if (Arg0.empty())
584     return;
585 
586   switch (C->getRenderKind()) {
587   case InlineCommandComment::RenderNormal:
588     for (unsigned i = 0, e = C->getNumArgs(); i != e; ++i) {
589       appendToResultWithHTMLEscaping(C->getArgText(i));
590       Result << " ";
591     }
592     return;
593 
594   case InlineCommandComment::RenderBold:
595     assert(C->getNumArgs() == 1);
596     Result << "<b>";
597     appendToResultWithHTMLEscaping(Arg0);
598     Result << "</b>";
599     return;
600   case InlineCommandComment::RenderMonospaced:
601     assert(C->getNumArgs() == 1);
602     Result << "<tt>";
603     appendToResultWithHTMLEscaping(Arg0);
604     Result<< "</tt>";
605     return;
606   case InlineCommandComment::RenderEmphasized:
607     assert(C->getNumArgs() == 1);
608     Result << "<em>";
609     appendToResultWithHTMLEscaping(Arg0);
610     Result << "</em>";
611     return;
612   }
613 }
614 
615 void CommentASTToHTMLConverter::visitHTMLStartTagComment(
616                                   const HTMLStartTagComment *C) {
617   PrintHTMLStartTagComment(C, Result);
618 }
619 
620 void CommentASTToHTMLConverter::visitHTMLEndTagComment(
621                                   const HTMLEndTagComment *C) {
622   Result << "</" << C->getTagName() << ">";
623 }
624 
625 void CommentASTToHTMLConverter::visitParagraphComment(
626                                   const ParagraphComment *C) {
627   if (C->isWhitespace())
628     return;
629 
630   Result << "<p>";
631   for (Comment::child_iterator I = C->child_begin(), E = C->child_end();
632        I != E; ++I) {
633     visit(*I);
634   }
635   Result << "</p>";
636 }
637 
638 void CommentASTToHTMLConverter::visitBlockCommandComment(
639                                   const BlockCommandComment *C) {
640   StringRef CommandName = C->getCommandName();
641   if (Traits.isBriefCommand(CommandName)) {
642     Result << "<p class=\"para-brief\">";
643     visitNonStandaloneParagraphComment(C->getParagraph());
644     Result << "</p>";
645     return;
646   }
647   if (Traits.isReturnsCommand(CommandName)) {
648     Result << "<p class=\"para-returns\">"
649               "<span class=\"word-returns\">Returns</span> ";
650     visitNonStandaloneParagraphComment(C->getParagraph());
651     Result << "</p>";
652     return;
653   }
654   // We don't know anything about this command.  Just render the paragraph.
655   visit(C->getParagraph());
656 }
657 
658 void CommentASTToHTMLConverter::visitParamCommandComment(
659                                   const ParamCommandComment *C) {
660   if (C->isParamIndexValid()) {
661     Result << "<dt class=\"param-name-index-"
662            << C->getParamIndex()
663            << "\">";
664   } else
665     Result << "<dt class=\"param-name-index-invalid\">";
666 
667   appendToResultWithHTMLEscaping(C->getParamName());
668   Result << "</dt>";
669 
670   if (C->isParamIndexValid()) {
671     Result << "<dd class=\"param-descr-index-"
672            << C->getParamIndex()
673            << "\">";
674   } else
675     Result << "<dd class=\"param-descr-index-invalid\">";
676 
677   visitNonStandaloneParagraphComment(C->getParagraph());
678   Result << "</dd>";
679 }
680 
681 void CommentASTToHTMLConverter::visitTParamCommandComment(
682                                   const TParamCommandComment *C) {
683   if (C->isPositionValid()) {
684     if (C->getDepth() == 1)
685       Result << "<dt class=\"tparam-name-index-"
686              << C->getIndex(0)
687              << "\">";
688     else
689       Result << "<dt class=\"tparam-name-index-other\">";
690   } else
691     Result << "<dt class=\"tparam-name-index-invalid\">";
692 
693   appendToResultWithHTMLEscaping(C->getParamName());
694   Result << "</dt>";
695 
696   if (C->isPositionValid()) {
697     if (C->getDepth() == 1)
698       Result << "<dd class=\"tparam-descr-index-"
699              << C->getIndex(0)
700              << "\">";
701     else
702       Result << "<dd class=\"tparam-descr-index-other\">";
703   } else
704     Result << "<dd class=\"tparam-descr-index-invalid\">";
705 
706   visitNonStandaloneParagraphComment(C->getParagraph());
707   Result << "</dd>";
708 }
709 
710 void CommentASTToHTMLConverter::visitVerbatimBlockComment(
711                                   const VerbatimBlockComment *C) {
712   unsigned NumLines = C->getNumLines();
713   if (NumLines == 0)
714     return;
715 
716   Result << "<pre>";
717   for (unsigned i = 0; i != NumLines; ++i) {
718     appendToResultWithHTMLEscaping(C->getText(i));
719     if (i + 1 != NumLines)
720       Result << '\n';
721   }
722   Result << "</pre>";
723 }
724 
725 void CommentASTToHTMLConverter::visitVerbatimBlockLineComment(
726                                   const VerbatimBlockLineComment *C) {
727   llvm_unreachable("should not see this AST node");
728 }
729 
730 void CommentASTToHTMLConverter::visitVerbatimLineComment(
731                                   const VerbatimLineComment *C) {
732   Result << "<pre>";
733   appendToResultWithHTMLEscaping(C->getText());
734   Result << "</pre>";
735 }
736 
737 void CommentASTToHTMLConverter::visitFullComment(const FullComment *C) {
738   FullCommentParts Parts(C);
739 
740   bool FirstParagraphIsBrief = false;
741   if (Parts.Brief)
742     visit(Parts.Brief);
743   else if (Parts.FirstParagraph) {
744     Result << "<p class=\"para-brief\">";
745     visitNonStandaloneParagraphComment(Parts.FirstParagraph);
746     Result << "</p>";
747     FirstParagraphIsBrief = true;
748   }
749 
750   for (unsigned i = 0, e = Parts.MiscBlocks.size(); i != e; ++i) {
751     const Comment *C = Parts.MiscBlocks[i];
752     if (FirstParagraphIsBrief && C == Parts.FirstParagraph)
753       continue;
754     visit(C);
755   }
756 
757   if (Parts.TParams.size() != 0) {
758     Result << "<dl>";
759     for (unsigned i = 0, e = Parts.TParams.size(); i != e; ++i)
760       visit(Parts.TParams[i]);
761     Result << "</dl>";
762   }
763 
764   if (Parts.Params.size() != 0) {
765     Result << "<dl>";
766     for (unsigned i = 0, e = Parts.Params.size(); i != e; ++i)
767       visit(Parts.Params[i]);
768     Result << "</dl>";
769   }
770 
771   if (Parts.Returns)
772     visit(Parts.Returns);
773 
774   Result.flush();
775 }
776 
777 void CommentASTToHTMLConverter::visitNonStandaloneParagraphComment(
778                                   const ParagraphComment *C) {
779   if (!C)
780     return;
781 
782   for (Comment::child_iterator I = C->child_begin(), E = C->child_end();
783        I != E; ++I) {
784     visit(*I);
785   }
786 }
787 
788 void CommentASTToHTMLConverter::appendToResultWithHTMLEscaping(StringRef S) {
789   for (StringRef::iterator I = S.begin(), E = S.end(); I != E; ++I) {
790     const char C = *I;
791     switch (C) {
792       case '&':
793         Result << "&amp;";
794         break;
795       case '<':
796         Result << "&lt;";
797         break;
798       case '>':
799         Result << "&gt;";
800         break;
801       case '"':
802         Result << "&quot;";
803         break;
804       case '\'':
805         Result << "&#39;";
806         break;
807       case '/':
808         Result << "&#47;";
809         break;
810       default:
811         Result << C;
812         break;
813     }
814   }
815 }
816 
817 extern "C" {
818 
819 CXString clang_HTMLTagComment_getAsString(CXComment CXC) {
820   const HTMLTagComment *HTC = getASTNodeAs<HTMLTagComment>(CXC);
821   if (!HTC)
822     return createCXString((const char *) 0);
823 
824   SmallString<128> HTML;
825   CommentASTToHTMLConverter Converter(HTML);
826   Converter.visit(HTC);
827   return createCXString(HTML.str(), /* DupString = */ true);
828 }
829 
830 CXString clang_FullComment_getAsHTML(CXComment CXC) {
831   const FullComment *FC = getASTNodeAs<FullComment>(CXC);
832   if (!FC)
833     return createCXString((const char *) 0);
834 
835   SmallString<1024> HTML;
836   CommentASTToHTMLConverter Converter(HTML);
837   Converter.visit(FC);
838   return createCXString(HTML.str(), /* DupString = */ true);
839 }
840 
841 } // end extern "C"
842 
843 namespace {
844 class CommentASTToXMLConverter :
845     public ConstCommentVisitor<CommentASTToXMLConverter> {
846 public:
847   /// \param Str accumulator for XML.
848   CommentASTToXMLConverter(const SourceManager &SM,
849                            SmallVectorImpl<char> &Str) :
850     SM(SM), Result(Str) { }
851 
852   // Inline content.
853   void visitTextComment(const TextComment *C);
854   void visitInlineCommandComment(const InlineCommandComment *C);
855   void visitHTMLStartTagComment(const HTMLStartTagComment *C);
856   void visitHTMLEndTagComment(const HTMLEndTagComment *C);
857 
858   // Block content.
859   void visitParagraphComment(const ParagraphComment *C);
860   void visitBlockCommandComment(const BlockCommandComment *C);
861   void visitParamCommandComment(const ParamCommandComment *C);
862   void visitTParamCommandComment(const TParamCommandComment *C);
863   void visitVerbatimBlockComment(const VerbatimBlockComment *C);
864   void visitVerbatimBlockLineComment(const VerbatimBlockLineComment *C);
865   void visitVerbatimLineComment(const VerbatimLineComment *C);
866 
867   void visitFullComment(const FullComment *C);
868 
869   // Helpers.
870   void appendToResultWithXMLEscaping(StringRef S);
871 
872 private:
873   const SourceManager &SM;
874 
875   /// Output stream for XML.
876   llvm::raw_svector_ostream Result;
877 };
878 } // end unnamed namespace
879 
880 void CommentASTToXMLConverter::visitTextComment(const TextComment *C) {
881   appendToResultWithXMLEscaping(C->getText());
882 }
883 
884 void CommentASTToXMLConverter::visitInlineCommandComment(const InlineCommandComment *C) {
885   // Nothing to render if no arguments supplied.
886   if (C->getNumArgs() == 0)
887     return;
888 
889   // Nothing to render if argument is empty.
890   StringRef Arg0 = C->getArgText(0);
891   if (Arg0.empty())
892     return;
893 
894   switch (C->getRenderKind()) {
895   case InlineCommandComment::RenderNormal:
896     for (unsigned i = 0, e = C->getNumArgs(); i != e; ++i) {
897       appendToResultWithXMLEscaping(C->getArgText(i));
898       Result << " ";
899     }
900     return;
901   case InlineCommandComment::RenderBold:
902     assert(C->getNumArgs() == 1);
903     Result << "<bold>";
904     appendToResultWithXMLEscaping(Arg0);
905     Result << "</bold>";
906     return;
907   case InlineCommandComment::RenderMonospaced:
908     assert(C->getNumArgs() == 1);
909     Result << "<monospaced>";
910     appendToResultWithXMLEscaping(Arg0);
911     Result << "</monospaced>";
912     return;
913   case InlineCommandComment::RenderEmphasized:
914     assert(C->getNumArgs() == 1);
915     Result << "<emphasized>";
916     appendToResultWithXMLEscaping(Arg0);
917     Result << "</emphasized>";
918     return;
919   }
920 }
921 
922 void CommentASTToXMLConverter::visitHTMLStartTagComment(const HTMLStartTagComment *C) {
923   Result << "<rawHTML><![CDATA[";
924   PrintHTMLStartTagComment(C, Result);
925   Result << "]]></rawHTML>";
926 }
927 
928 void CommentASTToXMLConverter::visitHTMLEndTagComment(const HTMLEndTagComment *C) {
929   Result << "<rawHTML>&lt;/" << C->getTagName() << "&gt;</rawHTML>";
930 }
931 
932 void CommentASTToXMLConverter::visitParagraphComment(const ParagraphComment *C) {
933   if (C->isWhitespace())
934     return;
935 
936   Result << "<Para>";
937   for (Comment::child_iterator I = C->child_begin(), E = C->child_end();
938        I != E; ++I) {
939     visit(*I);
940   }
941   Result << "</Para>";
942 }
943 
944 void CommentASTToXMLConverter::visitBlockCommandComment(const BlockCommandComment *C) {
945   visit(C->getParagraph());
946 }
947 
948 void CommentASTToXMLConverter::visitParamCommandComment(const ParamCommandComment *C) {
949   Result << "<Parameter><Name>";
950   appendToResultWithXMLEscaping(C->getParamName());
951   Result << "</Name>";
952 
953   if (C->isParamIndexValid())
954     Result << "<Index>" << C->getParamIndex() << "</Index>";
955 
956   Result << "<Direction isExplicit=\"" << C->isDirectionExplicit() << "\">";
957   switch (C->getDirection()) {
958   case ParamCommandComment::In:
959     Result << "in";
960     break;
961   case ParamCommandComment::Out:
962     Result << "out";
963     break;
964   case ParamCommandComment::InOut:
965     Result << "in,out";
966     break;
967   }
968   Result << "</Direction><Discussion>";
969   visit(C->getParagraph());
970   Result << "</Discussion></Parameter>";
971 }
972 
973 void CommentASTToXMLConverter::visitTParamCommandComment(
974                                   const TParamCommandComment *C) {
975   Result << "<Parameter><Name>";
976   appendToResultWithXMLEscaping(C->getParamName());
977   Result << "</Name>";
978 
979   if (C->isPositionValid() && C->getDepth() == 1) {
980     Result << "<Index>" << C->getIndex(0) << "</Index>";
981   }
982 
983   Result << "<Discussion>";
984   visit(C->getParagraph());
985   Result << "</Discussion></Parameter>";
986 }
987 
988 void CommentASTToXMLConverter::visitVerbatimBlockComment(
989                                   const VerbatimBlockComment *C) {
990   unsigned NumLines = C->getNumLines();
991   if (NumLines == 0)
992     return;
993 
994   Result << llvm::StringSwitch<const char *>(C->getCommandName())
995       .Case("code", "<Verbatim xml:space=\"preserve\" kind=\"code\">")
996       .Default("<Verbatim xml:space=\"preserve\" kind=\"verbatim\">");
997   for (unsigned i = 0; i != NumLines; ++i) {
998     appendToResultWithXMLEscaping(C->getText(i));
999     if (i + 1 != NumLines)
1000       Result << '\n';
1001   }
1002   Result << "</Verbatim>";
1003 }
1004 
1005 void CommentASTToXMLConverter::visitVerbatimBlockLineComment(
1006                                   const VerbatimBlockLineComment *C) {
1007   llvm_unreachable("should not see this AST node");
1008 }
1009 
1010 void CommentASTToXMLConverter::visitVerbatimLineComment(
1011                                   const VerbatimLineComment *C) {
1012   Result << "<Verbatim xml:space=\"preserve\" kind=\"verbatim\">";
1013   appendToResultWithXMLEscaping(C->getText());
1014   Result << "</Verbatim>";
1015 }
1016 
1017 void CommentASTToXMLConverter::visitFullComment(const FullComment *C) {
1018   FullCommentParts Parts(C);
1019 
1020   const DeclInfo *DI = C->getDeclInfo();
1021   StringRef RootEndTag;
1022   if (DI) {
1023     switch (DI->getKind()) {
1024     case DeclInfo::OtherKind:
1025       RootEndTag = "</Other>";
1026       Result << "<Other";
1027       break;
1028     case DeclInfo::FunctionKind:
1029       RootEndTag = "</Function>";
1030       Result << "<Function";
1031       switch (DI->TemplateKind) {
1032       case DeclInfo::NotTemplate:
1033         break;
1034       case DeclInfo::Template:
1035         Result << " templateKind=\"template\"";
1036         break;
1037       case DeclInfo::TemplateSpecialization:
1038         Result << " templateKind=\"specialization\"";
1039         break;
1040       case DeclInfo::TemplatePartialSpecialization:
1041         llvm_unreachable("partial specializations of functions "
1042                          "are not allowed in C++");
1043       }
1044       if (DI->IsInstanceMethod)
1045         Result << " isInstanceMethod=\"1\"";
1046       if (DI->IsClassMethod)
1047         Result << " isClassMethod=\"1\"";
1048       break;
1049     case DeclInfo::ClassKind:
1050       RootEndTag = "</Class>";
1051       Result << "<Class";
1052       switch (DI->TemplateKind) {
1053       case DeclInfo::NotTemplate:
1054         break;
1055       case DeclInfo::Template:
1056         Result << " templateKind=\"template\"";
1057         break;
1058       case DeclInfo::TemplateSpecialization:
1059         Result << " templateKind=\"specialization\"";
1060         break;
1061       case DeclInfo::TemplatePartialSpecialization:
1062         Result << " templateKind=\"partialSpecialization\"";
1063         break;
1064       }
1065       break;
1066     case DeclInfo::VariableKind:
1067       RootEndTag = "</Variable>";
1068       Result << "<Variable";
1069       break;
1070     case DeclInfo::NamespaceKind:
1071       RootEndTag = "</Namespace>";
1072       Result << "<Namespace";
1073       break;
1074     case DeclInfo::TypedefKind:
1075       RootEndTag = "</Typedef>";
1076       Result << "<Typedef";
1077       break;
1078     case DeclInfo::EnumKind:
1079       RootEndTag = "</Enum>";
1080       Result << "<Enum";
1081       break;
1082     }
1083 
1084     {
1085       // Print line and column number.
1086       SourceLocation Loc = DI->ThisDecl->getLocation();
1087       std::pair<FileID, unsigned> LocInfo = SM.getDecomposedLoc(Loc);
1088       FileID FID = LocInfo.first;
1089       unsigned FileOffset = LocInfo.second;
1090 
1091       if (!FID.isInvalid()) {
1092         if (const FileEntry *FE = SM.getFileEntryForID(FID)) {
1093           Result << " file=\"";
1094           appendToResultWithXMLEscaping(FE->getName());
1095           Result << "\"";
1096         }
1097         Result << " line=\"" << SM.getLineNumber(FID, FileOffset)
1098                << "\" column=\"" << SM.getColumnNumber(FID, FileOffset)
1099                << "\"";
1100       }
1101     }
1102 
1103     // Finish the root tag.
1104     Result << ">";
1105 
1106     bool FoundName = false;
1107     if (const NamedDecl *ND = dyn_cast<NamedDecl>(DI->ThisDecl)) {
1108       if (DeclarationName DeclName = ND->getDeclName()) {
1109         Result << "<Name>";
1110         std::string Name = DeclName.getAsString();
1111         appendToResultWithXMLEscaping(Name);
1112         FoundName = true;
1113         Result << "</Name>";
1114       }
1115     }
1116     if (!FoundName)
1117       Result << "<Name>&lt;anonymous&gt;</Name>";
1118 
1119     {
1120       // Print USR.
1121       SmallString<128> USR;
1122       cxcursor::getDeclCursorUSR(DI->ThisDecl, USR);
1123       if (!USR.empty()) {
1124         Result << "<USR>";
1125         appendToResultWithXMLEscaping(USR);
1126         Result << "</USR>";
1127       }
1128     }
1129   } else {
1130     // No DeclInfo -- just emit some root tag and name tag.
1131     RootEndTag = "</Other>";
1132     Result << "<Other><Name>unknown</Name>";
1133   }
1134 
1135   bool FirstParagraphIsBrief = false;
1136   if (Parts.Brief) {
1137     Result << "<Abstract>";
1138     visit(Parts.Brief);
1139     Result << "</Abstract>";
1140   } else if (Parts.FirstParagraph) {
1141     Result << "<Abstract>";
1142     visit(Parts.FirstParagraph);
1143     Result << "</Abstract>";
1144     FirstParagraphIsBrief = true;
1145   }
1146 
1147   if (Parts.TParams.size() != 0) {
1148     Result << "<TemplateParameters>";
1149     for (unsigned i = 0, e = Parts.TParams.size(); i != e; ++i)
1150       visit(Parts.TParams[i]);
1151     Result << "</TemplateParameters>";
1152   }
1153 
1154   if (Parts.Params.size() != 0) {
1155     Result << "<Parameters>";
1156     for (unsigned i = 0, e = Parts.Params.size(); i != e; ++i)
1157       visit(Parts.Params[i]);
1158     Result << "</Parameters>";
1159   }
1160 
1161   if (Parts.Returns) {
1162     Result << "<ResultDiscussion>";
1163     visit(Parts.Returns);
1164     Result << "</ResultDiscussion>";
1165   }
1166 
1167   {
1168     bool StartTagEmitted = false;
1169     for (unsigned i = 0, e = Parts.MiscBlocks.size(); i != e; ++i) {
1170       const Comment *C = Parts.MiscBlocks[i];
1171       if (FirstParagraphIsBrief && C == Parts.FirstParagraph)
1172         continue;
1173       if (!StartTagEmitted) {
1174         Result << "<Discussion>";
1175         StartTagEmitted = true;
1176       }
1177       visit(C);
1178     }
1179     if (StartTagEmitted)
1180       Result << "</Discussion>";
1181   }
1182 
1183   Result << RootEndTag;
1184 
1185   Result.flush();
1186 }
1187 
1188 void CommentASTToXMLConverter::appendToResultWithXMLEscaping(StringRef S) {
1189   for (StringRef::iterator I = S.begin(), E = S.end(); I != E; ++I) {
1190     const char C = *I;
1191     switch (C) {
1192       case '&':
1193         Result << "&amp;";
1194         break;
1195       case '<':
1196         Result << "&lt;";
1197         break;
1198       case '>':
1199         Result << "&gt;";
1200         break;
1201       case '"':
1202         Result << "&quot;";
1203         break;
1204       case '\'':
1205         Result << "&apos;";
1206         break;
1207       default:
1208         Result << C;
1209         break;
1210     }
1211   }
1212 }
1213 
1214 extern "C" {
1215 
1216 CXString clang_FullComment_getAsXML(CXTranslationUnit TU, CXComment CXC) {
1217   const FullComment *FC = getASTNodeAs<FullComment>(CXC);
1218   if (!FC)
1219     return createCXString((const char *) 0);
1220 
1221   SourceManager &SM = static_cast<ASTUnit *>(TU->TUData)->getSourceManager();
1222 
1223   SmallString<1024> XML;
1224   CommentASTToXMLConverter Converter(SM, XML);
1225   Converter.visit(FC);
1226   return createCXString(XML.str(), /* DupString = */ true);
1227 }
1228 
1229 } // end extern "C"
1230 
1231