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 "CXComment.h"
16 #include "CXCursor.h"
17 #include "CXString.h"
18 #include "SimpleFormatContext.h"
19 #include "clang/AST/Attr.h"
20 #include "clang/AST/CommentCommandTraits.h"
21 #include "clang/AST/CommentVisitor.h"
22 #include "clang/AST/Decl.h"
23 #include "clang/AST/PrettyPrinter.h"
24 #include "clang/Format/Format.h"
25 #include "clang/Lex/Lexer.h"
26 #include "llvm/ADT/StringExtras.h"
27 #include "llvm/ADT/StringSwitch.h"
28 #include "llvm/Support/ErrorHandling.h"
29 #include "llvm/Support/raw_ostream.h"
30 #include <climits>
31 
32 using namespace clang;
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, NULL);
98 
99   return createCXComment(*(C->child_begin() + ChildIdx), CXC.TranslationUnit);
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 cxstring::createNull();
128 
129   return cxstring::createRef(TC->getText());
130 }
131 
132 CXString clang_InlineCommandComment_getCommandName(CXComment CXC) {
133   const InlineCommandComment *ICC = getASTNodeAs<InlineCommandComment>(CXC);
134   if (!ICC)
135     return cxstring::createNull();
136 
137   const CommandTraits &Traits = getCommandTraits(CXC);
138   return cxstring::createRef(ICC->getCommandName(Traits));
139 }
140 
141 enum CXCommentInlineCommandRenderKind
142 clang_InlineCommandComment_getRenderKind(CXComment CXC) {
143   const InlineCommandComment *ICC = getASTNodeAs<InlineCommandComment>(CXC);
144   if (!ICC)
145     return CXCommentInlineCommandRenderKind_Normal;
146 
147   switch (ICC->getRenderKind()) {
148   case InlineCommandComment::RenderNormal:
149     return CXCommentInlineCommandRenderKind_Normal;
150 
151   case InlineCommandComment::RenderBold:
152     return CXCommentInlineCommandRenderKind_Bold;
153 
154   case InlineCommandComment::RenderMonospaced:
155     return CXCommentInlineCommandRenderKind_Monospaced;
156 
157   case InlineCommandComment::RenderEmphasized:
158     return CXCommentInlineCommandRenderKind_Emphasized;
159   }
160   llvm_unreachable("unknown InlineCommandComment::RenderKind");
161 }
162 
163 unsigned clang_InlineCommandComment_getNumArgs(CXComment CXC) {
164   const InlineCommandComment *ICC = getASTNodeAs<InlineCommandComment>(CXC);
165   if (!ICC)
166     return 0;
167 
168   return ICC->getNumArgs();
169 }
170 
171 CXString clang_InlineCommandComment_getArgText(CXComment CXC,
172                                                unsigned ArgIdx) {
173   const InlineCommandComment *ICC = getASTNodeAs<InlineCommandComment>(CXC);
174   if (!ICC || ArgIdx >= ICC->getNumArgs())
175     return cxstring::createNull();
176 
177   return cxstring::createRef(ICC->getArgText(ArgIdx));
178 }
179 
180 CXString clang_HTMLTagComment_getTagName(CXComment CXC) {
181   const HTMLTagComment *HTC = getASTNodeAs<HTMLTagComment>(CXC);
182   if (!HTC)
183     return cxstring::createNull();
184 
185   return cxstring::createRef(HTC->getTagName());
186 }
187 
188 unsigned clang_HTMLStartTagComment_isSelfClosing(CXComment CXC) {
189   const HTMLStartTagComment *HST = getASTNodeAs<HTMLStartTagComment>(CXC);
190   if (!HST)
191     return false;
192 
193   return HST->isSelfClosing();
194 }
195 
196 unsigned clang_HTMLStartTag_getNumAttrs(CXComment CXC) {
197   const HTMLStartTagComment *HST = getASTNodeAs<HTMLStartTagComment>(CXC);
198   if (!HST)
199     return 0;
200 
201   return HST->getNumAttrs();
202 }
203 
204 CXString clang_HTMLStartTag_getAttrName(CXComment CXC, unsigned AttrIdx) {
205   const HTMLStartTagComment *HST = getASTNodeAs<HTMLStartTagComment>(CXC);
206   if (!HST || AttrIdx >= HST->getNumAttrs())
207     return cxstring::createNull();
208 
209   return cxstring::createRef(HST->getAttr(AttrIdx).Name);
210 }
211 
212 CXString clang_HTMLStartTag_getAttrValue(CXComment CXC, unsigned AttrIdx) {
213   const HTMLStartTagComment *HST = getASTNodeAs<HTMLStartTagComment>(CXC);
214   if (!HST || AttrIdx >= HST->getNumAttrs())
215     return cxstring::createNull();
216 
217   return cxstring::createRef(HST->getAttr(AttrIdx).Value);
218 }
219 
220 CXString clang_BlockCommandComment_getCommandName(CXComment CXC) {
221   const BlockCommandComment *BCC = getASTNodeAs<BlockCommandComment>(CXC);
222   if (!BCC)
223     return cxstring::createNull();
224 
225   const CommandTraits &Traits = getCommandTraits(CXC);
226   return cxstring::createRef(BCC->getCommandName(Traits));
227 }
228 
229 unsigned clang_BlockCommandComment_getNumArgs(CXComment CXC) {
230   const BlockCommandComment *BCC = getASTNodeAs<BlockCommandComment>(CXC);
231   if (!BCC)
232     return 0;
233 
234   return BCC->getNumArgs();
235 }
236 
237 CXString clang_BlockCommandComment_getArgText(CXComment CXC,
238                                               unsigned ArgIdx) {
239   const BlockCommandComment *BCC = getASTNodeAs<BlockCommandComment>(CXC);
240   if (!BCC || ArgIdx >= BCC->getNumArgs())
241     return cxstring::createNull();
242 
243   return cxstring::createRef(BCC->getArgText(ArgIdx));
244 }
245 
246 CXComment clang_BlockCommandComment_getParagraph(CXComment CXC) {
247   const BlockCommandComment *BCC = getASTNodeAs<BlockCommandComment>(CXC);
248   if (!BCC)
249     return createCXComment(NULL, NULL);
250 
251   return createCXComment(BCC->getParagraph(), CXC.TranslationUnit);
252 }
253 
254 CXString clang_ParamCommandComment_getParamName(CXComment CXC) {
255   const ParamCommandComment *PCC = getASTNodeAs<ParamCommandComment>(CXC);
256   if (!PCC || !PCC->hasParamName())
257     return cxstring::createNull();
258 
259   return cxstring::createRef(PCC->getParamNameAsWritten());
260 }
261 
262 unsigned clang_ParamCommandComment_isParamIndexValid(CXComment CXC) {
263   const ParamCommandComment *PCC = getASTNodeAs<ParamCommandComment>(CXC);
264   if (!PCC)
265     return false;
266 
267   return PCC->isParamIndexValid();
268 }
269 
270 unsigned clang_ParamCommandComment_getParamIndex(CXComment CXC) {
271   const ParamCommandComment *PCC = getASTNodeAs<ParamCommandComment>(CXC);
272   if (!PCC || !PCC->isParamIndexValid() || PCC->isVarArgParam())
273     return ParamCommandComment::InvalidParamIndex;
274 
275   return PCC->getParamIndex();
276 }
277 
278 unsigned clang_ParamCommandComment_isDirectionExplicit(CXComment CXC) {
279   const ParamCommandComment *PCC = getASTNodeAs<ParamCommandComment>(CXC);
280   if (!PCC)
281     return false;
282 
283   return PCC->isDirectionExplicit();
284 }
285 
286 enum CXCommentParamPassDirection clang_ParamCommandComment_getDirection(
287                                                             CXComment CXC) {
288   const ParamCommandComment *PCC = getASTNodeAs<ParamCommandComment>(CXC);
289   if (!PCC)
290     return CXCommentParamPassDirection_In;
291 
292   switch (PCC->getDirection()) {
293   case ParamCommandComment::In:
294     return CXCommentParamPassDirection_In;
295 
296   case ParamCommandComment::Out:
297     return CXCommentParamPassDirection_Out;
298 
299   case ParamCommandComment::InOut:
300     return CXCommentParamPassDirection_InOut;
301   }
302   llvm_unreachable("unknown ParamCommandComment::PassDirection");
303 }
304 
305 CXString clang_TParamCommandComment_getParamName(CXComment CXC) {
306   const TParamCommandComment *TPCC = getASTNodeAs<TParamCommandComment>(CXC);
307   if (!TPCC || !TPCC->hasParamName())
308     return cxstring::createNull();
309 
310   return cxstring::createRef(TPCC->getParamNameAsWritten());
311 }
312 
313 unsigned clang_TParamCommandComment_isParamPositionValid(CXComment CXC) {
314   const TParamCommandComment *TPCC = getASTNodeAs<TParamCommandComment>(CXC);
315   if (!TPCC)
316     return false;
317 
318   return TPCC->isPositionValid();
319 }
320 
321 unsigned clang_TParamCommandComment_getDepth(CXComment CXC) {
322   const TParamCommandComment *TPCC = getASTNodeAs<TParamCommandComment>(CXC);
323   if (!TPCC || !TPCC->isPositionValid())
324     return 0;
325 
326   return TPCC->getDepth();
327 }
328 
329 unsigned clang_TParamCommandComment_getIndex(CXComment CXC, unsigned Depth) {
330   const TParamCommandComment *TPCC = getASTNodeAs<TParamCommandComment>(CXC);
331   if (!TPCC || !TPCC->isPositionValid() || Depth >= TPCC->getDepth())
332     return 0;
333 
334   return TPCC->getIndex(Depth);
335 }
336 
337 CXString clang_VerbatimBlockLineComment_getText(CXComment CXC) {
338   const VerbatimBlockLineComment *VBL =
339       getASTNodeAs<VerbatimBlockLineComment>(CXC);
340   if (!VBL)
341     return cxstring::createNull();
342 
343   return cxstring::createRef(VBL->getText());
344 }
345 
346 CXString clang_VerbatimLineComment_getText(CXComment CXC) {
347   const VerbatimLineComment *VLC = getASTNodeAs<VerbatimLineComment>(CXC);
348   if (!VLC)
349     return cxstring::createNull();
350 
351   return cxstring::createRef(VLC->getText());
352 }
353 
354 } // end extern "C"
355 
356 //===----------------------------------------------------------------------===//
357 // Helpers for converting comment AST to HTML.
358 //===----------------------------------------------------------------------===//
359 
360 namespace {
361 
362 /// This comparison will sort parameters with valid index by index, then vararg
363 /// parameters, and invalid (unresolved) parameters last.
364 class ParamCommandCommentCompareIndex {
365 public:
366   bool operator()(const ParamCommandComment *LHS,
367                   const ParamCommandComment *RHS) const {
368     unsigned LHSIndex = UINT_MAX;
369     unsigned RHSIndex = UINT_MAX;
370 
371     if (LHS->isParamIndexValid()) {
372       if (LHS->isVarArgParam())
373         LHSIndex = UINT_MAX - 1;
374       else
375         LHSIndex = LHS->getParamIndex();
376     }
377     if (RHS->isParamIndexValid()) {
378       if (RHS->isVarArgParam())
379         RHSIndex = UINT_MAX - 1;
380       else
381         RHSIndex = RHS->getParamIndex();
382     }
383     return LHSIndex < RHSIndex;
384   }
385 };
386 
387 /// This comparison will sort template parameters in the following order:
388 /// \li real template parameters (depth = 1) in index order;
389 /// \li all other names (depth > 1);
390 /// \li unresolved names.
391 class TParamCommandCommentComparePosition {
392 public:
393   bool operator()(const TParamCommandComment *LHS,
394                   const TParamCommandComment *RHS) const {
395     // Sort unresolved names last.
396     if (!LHS->isPositionValid())
397       return false;
398     if (!RHS->isPositionValid())
399       return true;
400 
401     if (LHS->getDepth() > 1)
402       return false;
403     if (RHS->getDepth() > 1)
404       return true;
405 
406     // Sort template parameters in index order.
407     if (LHS->getDepth() == 1 && RHS->getDepth() == 1)
408       return LHS->getIndex(0) < RHS->getIndex(0);
409 
410     // Leave all other names in source order.
411     return true;
412   }
413 };
414 
415 /// Separate parts of a FullComment.
416 struct FullCommentParts {
417   /// Take a full comment apart and initialize members accordingly.
418   FullCommentParts(const FullComment *C,
419                    const CommandTraits &Traits);
420 
421   const BlockContentComment *Brief;
422   const BlockContentComment *Headerfile;
423   const ParagraphComment *FirstParagraph;
424   SmallVector<const BlockCommandComment *, 4> Returns;
425   SmallVector<const ParamCommandComment *, 8> Params;
426   SmallVector<const TParamCommandComment *, 4> TParams;
427   SmallVector<const BlockContentComment *, 8> MiscBlocks;
428 };
429 
430 FullCommentParts::FullCommentParts(const FullComment *C,
431                                    const CommandTraits &Traits) :
432     Brief(NULL), Headerfile(NULL), FirstParagraph(NULL) {
433   for (Comment::child_iterator I = C->child_begin(), E = C->child_end();
434        I != E; ++I) {
435     const Comment *Child = *I;
436     if (!Child)
437       continue;
438     switch (Child->getCommentKind()) {
439     case Comment::NoCommentKind:
440       continue;
441 
442     case Comment::ParagraphCommentKind: {
443       const ParagraphComment *PC = cast<ParagraphComment>(Child);
444       if (PC->isWhitespace())
445         break;
446       if (!FirstParagraph)
447         FirstParagraph = PC;
448 
449       MiscBlocks.push_back(PC);
450       break;
451     }
452 
453     case Comment::BlockCommandCommentKind: {
454       const BlockCommandComment *BCC = cast<BlockCommandComment>(Child);
455       const CommandInfo *Info = Traits.getCommandInfo(BCC->getCommandID());
456       if (!Brief && Info->IsBriefCommand) {
457         Brief = BCC;
458         break;
459       }
460       if (!Headerfile && Info->IsHeaderfileCommand) {
461         Headerfile = BCC;
462         break;
463       }
464       if (Info->IsReturnsCommand) {
465         Returns.push_back(BCC);
466         break;
467       }
468       MiscBlocks.push_back(BCC);
469       break;
470     }
471 
472     case Comment::ParamCommandCommentKind: {
473       const ParamCommandComment *PCC = cast<ParamCommandComment>(Child);
474       if (!PCC->hasParamName())
475         break;
476 
477       if (!PCC->isDirectionExplicit() && !PCC->hasNonWhitespaceParagraph())
478         break;
479 
480       Params.push_back(PCC);
481       break;
482     }
483 
484     case Comment::TParamCommandCommentKind: {
485       const TParamCommandComment *TPCC = cast<TParamCommandComment>(Child);
486       if (!TPCC->hasParamName())
487         break;
488 
489       if (!TPCC->hasNonWhitespaceParagraph())
490         break;
491 
492       TParams.push_back(TPCC);
493       break;
494     }
495 
496     case Comment::VerbatimBlockCommentKind:
497       MiscBlocks.push_back(cast<BlockCommandComment>(Child));
498       break;
499 
500     case Comment::VerbatimLineCommentKind: {
501       const VerbatimLineComment *VLC = cast<VerbatimLineComment>(Child);
502       const CommandInfo *Info = Traits.getCommandInfo(VLC->getCommandID());
503       if (!Info->IsDeclarationCommand)
504         MiscBlocks.push_back(VLC);
505       break;
506     }
507 
508     case Comment::TextCommentKind:
509     case Comment::InlineCommandCommentKind:
510     case Comment::HTMLStartTagCommentKind:
511     case Comment::HTMLEndTagCommentKind:
512     case Comment::VerbatimBlockLineCommentKind:
513     case Comment::FullCommentKind:
514       llvm_unreachable("AST node of this kind can't be a child of "
515                        "a FullComment");
516     }
517   }
518 
519   // Sort params in order they are declared in the function prototype.
520   // Unresolved parameters are put at the end of the list in the same order
521   // they were seen in the comment.
522   std::stable_sort(Params.begin(), Params.end(),
523                    ParamCommandCommentCompareIndex());
524 
525   std::stable_sort(TParams.begin(), TParams.end(),
526                    TParamCommandCommentComparePosition());
527 }
528 
529 void PrintHTMLStartTagComment(const HTMLStartTagComment *C,
530                               llvm::raw_svector_ostream &Result) {
531   Result << "<" << C->getTagName();
532 
533   if (C->getNumAttrs() != 0) {
534     for (unsigned i = 0, e = C->getNumAttrs(); i != e; i++) {
535       Result << " ";
536       const HTMLStartTagComment::Attribute &Attr = C->getAttr(i);
537       Result << Attr.Name;
538       if (!Attr.Value.empty())
539         Result << "=\"" << Attr.Value << "\"";
540     }
541   }
542 
543   if (!C->isSelfClosing())
544     Result << ">";
545   else
546     Result << "/>";
547 }
548 
549 class CommentASTToHTMLConverter :
550     public ConstCommentVisitor<CommentASTToHTMLConverter> {
551 public:
552   /// \param Str accumulator for HTML.
553   CommentASTToHTMLConverter(const FullComment *FC,
554                             SmallVectorImpl<char> &Str,
555                             const CommandTraits &Traits) :
556       FC(FC), Result(Str), Traits(Traits)
557   { }
558 
559   // Inline content.
560   void visitTextComment(const TextComment *C);
561   void visitInlineCommandComment(const InlineCommandComment *C);
562   void visitHTMLStartTagComment(const HTMLStartTagComment *C);
563   void visitHTMLEndTagComment(const HTMLEndTagComment *C);
564 
565   // Block content.
566   void visitParagraphComment(const ParagraphComment *C);
567   void visitBlockCommandComment(const BlockCommandComment *C);
568   void visitParamCommandComment(const ParamCommandComment *C);
569   void visitTParamCommandComment(const TParamCommandComment *C);
570   void visitVerbatimBlockComment(const VerbatimBlockComment *C);
571   void visitVerbatimBlockLineComment(const VerbatimBlockLineComment *C);
572   void visitVerbatimLineComment(const VerbatimLineComment *C);
573 
574   void visitFullComment(const FullComment *C);
575 
576   // Helpers.
577 
578   /// Convert a paragraph that is not a block by itself (an argument to some
579   /// command).
580   void visitNonStandaloneParagraphComment(const ParagraphComment *C);
581 
582   void appendToResultWithHTMLEscaping(StringRef S);
583 
584 private:
585   const FullComment *FC;
586   /// Output stream for HTML.
587   llvm::raw_svector_ostream Result;
588 
589   const CommandTraits &Traits;
590 };
591 } // end unnamed namespace
592 
593 void CommentASTToHTMLConverter::visitTextComment(const TextComment *C) {
594   appendToResultWithHTMLEscaping(C->getText());
595 }
596 
597 void CommentASTToHTMLConverter::visitInlineCommandComment(
598                                   const InlineCommandComment *C) {
599   // Nothing to render if no arguments supplied.
600   if (C->getNumArgs() == 0)
601     return;
602 
603   // Nothing to render if argument is empty.
604   StringRef Arg0 = C->getArgText(0);
605   if (Arg0.empty())
606     return;
607 
608   switch (C->getRenderKind()) {
609   case InlineCommandComment::RenderNormal:
610     for (unsigned i = 0, e = C->getNumArgs(); i != e; ++i) {
611       appendToResultWithHTMLEscaping(C->getArgText(i));
612       Result << " ";
613     }
614     return;
615 
616   case InlineCommandComment::RenderBold:
617     assert(C->getNumArgs() == 1);
618     Result << "<b>";
619     appendToResultWithHTMLEscaping(Arg0);
620     Result << "</b>";
621     return;
622   case InlineCommandComment::RenderMonospaced:
623     assert(C->getNumArgs() == 1);
624     Result << "<tt>";
625     appendToResultWithHTMLEscaping(Arg0);
626     Result<< "</tt>";
627     return;
628   case InlineCommandComment::RenderEmphasized:
629     assert(C->getNumArgs() == 1);
630     Result << "<em>";
631     appendToResultWithHTMLEscaping(Arg0);
632     Result << "</em>";
633     return;
634   }
635 }
636 
637 void CommentASTToHTMLConverter::visitHTMLStartTagComment(
638                                   const HTMLStartTagComment *C) {
639   PrintHTMLStartTagComment(C, Result);
640 }
641 
642 void CommentASTToHTMLConverter::visitHTMLEndTagComment(
643                                   const HTMLEndTagComment *C) {
644   Result << "</" << C->getTagName() << ">";
645 }
646 
647 void CommentASTToHTMLConverter::visitParagraphComment(
648                                   const ParagraphComment *C) {
649   if (C->isWhitespace())
650     return;
651 
652   Result << "<p>";
653   for (Comment::child_iterator I = C->child_begin(), E = C->child_end();
654        I != E; ++I) {
655     visit(*I);
656   }
657   Result << "</p>";
658 }
659 
660 void CommentASTToHTMLConverter::visitBlockCommandComment(
661                                   const BlockCommandComment *C) {
662   const CommandInfo *Info = Traits.getCommandInfo(C->getCommandID());
663   if (Info->IsBriefCommand) {
664     Result << "<p class=\"para-brief\">";
665     visitNonStandaloneParagraphComment(C->getParagraph());
666     Result << "</p>";
667     return;
668   }
669   if (Info->IsReturnsCommand) {
670     Result << "<p class=\"para-returns\">"
671               "<span class=\"word-returns\">Returns</span> ";
672     visitNonStandaloneParagraphComment(C->getParagraph());
673     Result << "</p>";
674     return;
675   }
676   // We don't know anything about this command.  Just render the paragraph.
677   visit(C->getParagraph());
678 }
679 
680 void CommentASTToHTMLConverter::visitParamCommandComment(
681                                   const ParamCommandComment *C) {
682   if (C->isParamIndexValid()) {
683     if (C->isVarArgParam()) {
684       Result << "<dt class=\"param-name-index-vararg\">";
685       appendToResultWithHTMLEscaping(C->getParamNameAsWritten());
686     } else {
687       Result << "<dt class=\"param-name-index-"
688              << C->getParamIndex()
689              << "\">";
690       appendToResultWithHTMLEscaping(C->getParamName(FC));
691     }
692   } else {
693     Result << "<dt class=\"param-name-index-invalid\">";
694     appendToResultWithHTMLEscaping(C->getParamNameAsWritten());
695   }
696   Result << "</dt>";
697 
698   if (C->isParamIndexValid()) {
699     if (C->isVarArgParam())
700       Result << "<dd class=\"param-descr-index-vararg\">";
701     else
702       Result << "<dd class=\"param-descr-index-"
703              << C->getParamIndex()
704              << "\">";
705   } else
706     Result << "<dd class=\"param-descr-index-invalid\">";
707 
708   visitNonStandaloneParagraphComment(C->getParagraph());
709   Result << "</dd>";
710 }
711 
712 void CommentASTToHTMLConverter::visitTParamCommandComment(
713                                   const TParamCommandComment *C) {
714   if (C->isPositionValid()) {
715     if (C->getDepth() == 1)
716       Result << "<dt class=\"tparam-name-index-"
717              << C->getIndex(0)
718              << "\">";
719     else
720       Result << "<dt class=\"tparam-name-index-other\">";
721     appendToResultWithHTMLEscaping(C->getParamName(FC));
722   } else {
723     Result << "<dt class=\"tparam-name-index-invalid\">";
724     appendToResultWithHTMLEscaping(C->getParamNameAsWritten());
725   }
726 
727   Result << "</dt>";
728 
729   if (C->isPositionValid()) {
730     if (C->getDepth() == 1)
731       Result << "<dd class=\"tparam-descr-index-"
732              << C->getIndex(0)
733              << "\">";
734     else
735       Result << "<dd class=\"tparam-descr-index-other\">";
736   } else
737     Result << "<dd class=\"tparam-descr-index-invalid\">";
738 
739   visitNonStandaloneParagraphComment(C->getParagraph());
740   Result << "</dd>";
741 }
742 
743 void CommentASTToHTMLConverter::visitVerbatimBlockComment(
744                                   const VerbatimBlockComment *C) {
745   unsigned NumLines = C->getNumLines();
746   if (NumLines == 0)
747     return;
748 
749   Result << "<pre>";
750   for (unsigned i = 0; i != NumLines; ++i) {
751     appendToResultWithHTMLEscaping(C->getText(i));
752     if (i + 1 != NumLines)
753       Result << '\n';
754   }
755   Result << "</pre>";
756 }
757 
758 void CommentASTToHTMLConverter::visitVerbatimBlockLineComment(
759                                   const VerbatimBlockLineComment *C) {
760   llvm_unreachable("should not see this AST node");
761 }
762 
763 void CommentASTToHTMLConverter::visitVerbatimLineComment(
764                                   const VerbatimLineComment *C) {
765   Result << "<pre>";
766   appendToResultWithHTMLEscaping(C->getText());
767   Result << "</pre>";
768 }
769 
770 void CommentASTToHTMLConverter::visitFullComment(const FullComment *C) {
771   FullCommentParts Parts(C, Traits);
772 
773   bool FirstParagraphIsBrief = false;
774   if (Parts.Headerfile)
775     visit(Parts.Headerfile);
776   if (Parts.Brief)
777     visit(Parts.Brief);
778   else if (Parts.FirstParagraph) {
779     Result << "<p class=\"para-brief\">";
780     visitNonStandaloneParagraphComment(Parts.FirstParagraph);
781     Result << "</p>";
782     FirstParagraphIsBrief = true;
783   }
784 
785   for (unsigned i = 0, e = Parts.MiscBlocks.size(); i != e; ++i) {
786     const Comment *C = Parts.MiscBlocks[i];
787     if (FirstParagraphIsBrief && C == Parts.FirstParagraph)
788       continue;
789     visit(C);
790   }
791 
792   if (Parts.TParams.size() != 0) {
793     Result << "<dl>";
794     for (unsigned i = 0, e = Parts.TParams.size(); i != e; ++i)
795       visit(Parts.TParams[i]);
796     Result << "</dl>";
797   }
798 
799   if (Parts.Params.size() != 0) {
800     Result << "<dl>";
801     for (unsigned i = 0, e = Parts.Params.size(); i != e; ++i)
802       visit(Parts.Params[i]);
803     Result << "</dl>";
804   }
805 
806   if (Parts.Returns.size() != 0) {
807     Result << "<div class=\"result-discussion\">";
808     for (unsigned i = 0, e = Parts.Returns.size(); i != e; ++i)
809       visit(Parts.Returns[i]);
810     Result << "</div>";
811   }
812 
813   Result.flush();
814 }
815 
816 void CommentASTToHTMLConverter::visitNonStandaloneParagraphComment(
817                                   const ParagraphComment *C) {
818   if (!C)
819     return;
820 
821   for (Comment::child_iterator I = C->child_begin(), E = C->child_end();
822        I != E; ++I) {
823     visit(*I);
824   }
825 }
826 
827 void CommentASTToHTMLConverter::appendToResultWithHTMLEscaping(StringRef S) {
828   for (StringRef::iterator I = S.begin(), E = S.end(); I != E; ++I) {
829     const char C = *I;
830     switch (C) {
831       case '&':
832         Result << "&amp;";
833         break;
834       case '<':
835         Result << "&lt;";
836         break;
837       case '>':
838         Result << "&gt;";
839         break;
840       case '"':
841         Result << "&quot;";
842         break;
843       case '\'':
844         Result << "&#39;";
845         break;
846       case '/':
847         Result << "&#47;";
848         break;
849       default:
850         Result << C;
851         break;
852     }
853   }
854 }
855 
856 extern "C" {
857 
858 CXString clang_HTMLTagComment_getAsString(CXComment CXC) {
859   const HTMLTagComment *HTC = getASTNodeAs<HTMLTagComment>(CXC);
860   if (!HTC)
861     return cxstring::createNull();
862 
863   SmallString<128> HTML;
864   CommentASTToHTMLConverter Converter(0, HTML, getCommandTraits(CXC));
865   Converter.visit(HTC);
866   return cxstring::createDup(HTML.str());
867 }
868 
869 CXString clang_FullComment_getAsHTML(CXComment CXC) {
870   const FullComment *FC = getASTNodeAs<FullComment>(CXC);
871   if (!FC)
872     return cxstring::createNull();
873 
874   SmallString<1024> HTML;
875   CommentASTToHTMLConverter Converter(FC, HTML, getCommandTraits(CXC));
876   Converter.visit(FC);
877   return cxstring::createDup(HTML.str());
878 }
879 
880 } // end extern "C"
881 
882 namespace {
883 class CommentASTToXMLConverter :
884     public ConstCommentVisitor<CommentASTToXMLConverter> {
885 public:
886   /// \param Str accumulator for XML.
887   CommentASTToXMLConverter(const FullComment *FC,
888                            SmallVectorImpl<char> &Str,
889                            const CommandTraits &Traits,
890                            const SourceManager &SM,
891                            SimpleFormatContext &SFC,
892                            unsigned FUID) :
893       FC(FC), Result(Str), Traits(Traits), SM(SM),
894       FormatRewriterContext(SFC),
895       FormatInMemoryUniqueId(FUID) { }
896 
897   // Inline content.
898   void visitTextComment(const TextComment *C);
899   void visitInlineCommandComment(const InlineCommandComment *C);
900   void visitHTMLStartTagComment(const HTMLStartTagComment *C);
901   void visitHTMLEndTagComment(const HTMLEndTagComment *C);
902 
903   // Block content.
904   void visitParagraphComment(const ParagraphComment *C);
905 
906   void appendParagraphCommentWithKind(const ParagraphComment *C,
907                                       StringRef Kind);
908 
909   void visitBlockCommandComment(const BlockCommandComment *C);
910   void visitParamCommandComment(const ParamCommandComment *C);
911   void visitTParamCommandComment(const TParamCommandComment *C);
912   void visitVerbatimBlockComment(const VerbatimBlockComment *C);
913   void visitVerbatimBlockLineComment(const VerbatimBlockLineComment *C);
914   void visitVerbatimLineComment(const VerbatimLineComment *C);
915 
916   void visitFullComment(const FullComment *C);
917 
918   // Helpers.
919   void appendToResultWithXMLEscaping(StringRef S);
920 
921   void formatTextOfDeclaration(const DeclInfo *DI,
922                                SmallString<128> &Declaration);
923 
924 private:
925   const FullComment *FC;
926 
927   /// Output stream for XML.
928   llvm::raw_svector_ostream Result;
929 
930   const CommandTraits &Traits;
931   const SourceManager &SM;
932   SimpleFormatContext &FormatRewriterContext;
933   unsigned FormatInMemoryUniqueId;
934 };
935 
936 void getSourceTextOfDeclaration(const DeclInfo *ThisDecl,
937                                 SmallVectorImpl<char> &Str) {
938   ASTContext &Context = ThisDecl->CurrentDecl->getASTContext();
939   const LangOptions &LangOpts = Context.getLangOpts();
940   llvm::raw_svector_ostream OS(Str);
941   PrintingPolicy PPolicy(LangOpts);
942   PPolicy.PolishForDeclaration = true;
943   PPolicy.TerseOutput = true;
944   ThisDecl->CurrentDecl->print(OS, PPolicy,
945                                /*Indentation*/0, /*PrintInstantiation*/false);
946 }
947 
948 void CommentASTToXMLConverter::formatTextOfDeclaration(
949                                               const DeclInfo *DI,
950                                               SmallString<128> &Declaration) {
951   // FIXME. formatting API expects null terminated input string.
952   // There might be more efficient way of doing this.
953   std::string StringDecl = Declaration.str();
954 
955   // Formatter specific code.
956   // Form a unique in memory buffer name.
957   SmallString<128> filename;
958   filename += "xmldecl";
959   filename += llvm::utostr(FormatInMemoryUniqueId);
960   filename += ".xd";
961   FileID ID = FormatRewriterContext.createInMemoryFile(filename, StringDecl);
962   SourceLocation Start =
963     FormatRewriterContext.Sources.getLocForStartOfFile(ID).getLocWithOffset(0);
964   unsigned Length = Declaration.size();
965 
966   std::vector<CharSourceRange>
967     Ranges(1, CharSourceRange::getCharRange(Start, Start.getLocWithOffset(Length)));
968   ASTContext &Context = DI->CurrentDecl->getASTContext();
969   const LangOptions &LangOpts = Context.getLangOpts();
970   Lexer Lex(ID, FormatRewriterContext.Sources.getBuffer(ID),
971             FormatRewriterContext.Sources, LangOpts);
972   tooling::Replacements Replace =
973     reformat(format::getLLVMStyle(), Lex, FormatRewriterContext.Sources, Ranges);
974   applyAllReplacements(Replace, FormatRewriterContext.Rewrite);
975   Declaration = FormatRewriterContext.getRewrittenText(ID);
976 }
977 
978 } // end unnamed namespace
979 
980 void CommentASTToXMLConverter::visitTextComment(const TextComment *C) {
981   appendToResultWithXMLEscaping(C->getText());
982 }
983 
984 void CommentASTToXMLConverter::visitInlineCommandComment(const InlineCommandComment *C) {
985   // Nothing to render if no arguments supplied.
986   if (C->getNumArgs() == 0)
987     return;
988 
989   // Nothing to render if argument is empty.
990   StringRef Arg0 = C->getArgText(0);
991   if (Arg0.empty())
992     return;
993 
994   switch (C->getRenderKind()) {
995   case InlineCommandComment::RenderNormal:
996     for (unsigned i = 0, e = C->getNumArgs(); i != e; ++i) {
997       appendToResultWithXMLEscaping(C->getArgText(i));
998       Result << " ";
999     }
1000     return;
1001   case InlineCommandComment::RenderBold:
1002     assert(C->getNumArgs() == 1);
1003     Result << "<bold>";
1004     appendToResultWithXMLEscaping(Arg0);
1005     Result << "</bold>";
1006     return;
1007   case InlineCommandComment::RenderMonospaced:
1008     assert(C->getNumArgs() == 1);
1009     Result << "<monospaced>";
1010     appendToResultWithXMLEscaping(Arg0);
1011     Result << "</monospaced>";
1012     return;
1013   case InlineCommandComment::RenderEmphasized:
1014     assert(C->getNumArgs() == 1);
1015     Result << "<emphasized>";
1016     appendToResultWithXMLEscaping(Arg0);
1017     Result << "</emphasized>";
1018     return;
1019   }
1020 }
1021 
1022 void CommentASTToXMLConverter::visitHTMLStartTagComment(const HTMLStartTagComment *C) {
1023   Result << "<rawHTML><![CDATA[";
1024   PrintHTMLStartTagComment(C, Result);
1025   Result << "]]></rawHTML>";
1026 }
1027 
1028 void CommentASTToXMLConverter::visitHTMLEndTagComment(const HTMLEndTagComment *C) {
1029   Result << "<rawHTML>&lt;/" << C->getTagName() << "&gt;</rawHTML>";
1030 }
1031 
1032 void CommentASTToXMLConverter::visitParagraphComment(const ParagraphComment *C) {
1033   appendParagraphCommentWithKind(C, StringRef());
1034 }
1035 
1036 void CommentASTToXMLConverter::appendParagraphCommentWithKind(
1037                                   const ParagraphComment *C,
1038                                   StringRef ParagraphKind) {
1039   if (C->isWhitespace())
1040     return;
1041 
1042   if (ParagraphKind.empty())
1043     Result << "<Para>";
1044   else
1045     Result << "<Para kind=\"" << ParagraphKind << "\">";
1046 
1047   for (Comment::child_iterator I = C->child_begin(), E = C->child_end();
1048        I != E; ++I) {
1049     visit(*I);
1050   }
1051   Result << "</Para>";
1052 }
1053 
1054 void CommentASTToXMLConverter::visitBlockCommandComment(const BlockCommandComment *C) {
1055   StringRef ParagraphKind;
1056 
1057   switch (C->getCommandID()) {
1058   case CommandTraits::KCI_attention:
1059   case CommandTraits::KCI_author:
1060   case CommandTraits::KCI_authors:
1061   case CommandTraits::KCI_bug:
1062   case CommandTraits::KCI_copyright:
1063   case CommandTraits::KCI_date:
1064   case CommandTraits::KCI_invariant:
1065   case CommandTraits::KCI_note:
1066   case CommandTraits::KCI_post:
1067   case CommandTraits::KCI_pre:
1068   case CommandTraits::KCI_remark:
1069   case CommandTraits::KCI_remarks:
1070   case CommandTraits::KCI_sa:
1071   case CommandTraits::KCI_see:
1072   case CommandTraits::KCI_since:
1073   case CommandTraits::KCI_todo:
1074   case CommandTraits::KCI_version:
1075   case CommandTraits::KCI_warning:
1076     ParagraphKind = C->getCommandName(Traits);
1077   default:
1078     break;
1079   }
1080 
1081   appendParagraphCommentWithKind(C->getParagraph(), ParagraphKind);
1082 }
1083 
1084 void CommentASTToXMLConverter::visitParamCommandComment(const ParamCommandComment *C) {
1085   Result << "<Parameter><Name>";
1086   appendToResultWithXMLEscaping(C->isParamIndexValid() ? C->getParamName(FC)
1087                                                        : C->getParamNameAsWritten());
1088   Result << "</Name>";
1089 
1090   if (C->isParamIndexValid()) {
1091     if (C->isVarArgParam())
1092       Result << "<IsVarArg />";
1093     else
1094       Result << "<Index>" << C->getParamIndex() << "</Index>";
1095   }
1096 
1097   Result << "<Direction isExplicit=\"" << C->isDirectionExplicit() << "\">";
1098   switch (C->getDirection()) {
1099   case ParamCommandComment::In:
1100     Result << "in";
1101     break;
1102   case ParamCommandComment::Out:
1103     Result << "out";
1104     break;
1105   case ParamCommandComment::InOut:
1106     Result << "in,out";
1107     break;
1108   }
1109   Result << "</Direction><Discussion>";
1110   visit(C->getParagraph());
1111   Result << "</Discussion></Parameter>";
1112 }
1113 
1114 void CommentASTToXMLConverter::visitTParamCommandComment(
1115                                   const TParamCommandComment *C) {
1116   Result << "<Parameter><Name>";
1117   appendToResultWithXMLEscaping(C->isPositionValid() ? C->getParamName(FC)
1118                                 : C->getParamNameAsWritten());
1119   Result << "</Name>";
1120 
1121   if (C->isPositionValid() && C->getDepth() == 1) {
1122     Result << "<Index>" << C->getIndex(0) << "</Index>";
1123   }
1124 
1125   Result << "<Discussion>";
1126   visit(C->getParagraph());
1127   Result << "</Discussion></Parameter>";
1128 }
1129 
1130 void CommentASTToXMLConverter::visitVerbatimBlockComment(
1131                                   const VerbatimBlockComment *C) {
1132   unsigned NumLines = C->getNumLines();
1133   if (NumLines == 0)
1134     return;
1135 
1136   switch (C->getCommandID()) {
1137   case CommandTraits::KCI_code:
1138     Result << "<Verbatim xml:space=\"preserve\" kind=\"code\">";
1139     break;
1140   default:
1141     Result << "<Verbatim xml:space=\"preserve\" kind=\"verbatim\">";
1142     break;
1143   }
1144   for (unsigned i = 0; i != NumLines; ++i) {
1145     appendToResultWithXMLEscaping(C->getText(i));
1146     if (i + 1 != NumLines)
1147       Result << '\n';
1148   }
1149   Result << "</Verbatim>";
1150 }
1151 
1152 void CommentASTToXMLConverter::visitVerbatimBlockLineComment(
1153                                   const VerbatimBlockLineComment *C) {
1154   llvm_unreachable("should not see this AST node");
1155 }
1156 
1157 void CommentASTToXMLConverter::visitVerbatimLineComment(
1158                                   const VerbatimLineComment *C) {
1159   Result << "<Verbatim xml:space=\"preserve\" kind=\"verbatim\">";
1160   appendToResultWithXMLEscaping(C->getText());
1161   Result << "</Verbatim>";
1162 }
1163 
1164 void CommentASTToXMLConverter::visitFullComment(const FullComment *C) {
1165   FullCommentParts Parts(C, Traits);
1166 
1167   const DeclInfo *DI = C->getDeclInfo();
1168   StringRef RootEndTag;
1169   if (DI) {
1170     switch (DI->getKind()) {
1171     case DeclInfo::OtherKind:
1172       RootEndTag = "</Other>";
1173       Result << "<Other";
1174       break;
1175     case DeclInfo::FunctionKind:
1176       RootEndTag = "</Function>";
1177       Result << "<Function";
1178       switch (DI->TemplateKind) {
1179       case DeclInfo::NotTemplate:
1180         break;
1181       case DeclInfo::Template:
1182         Result << " templateKind=\"template\"";
1183         break;
1184       case DeclInfo::TemplateSpecialization:
1185         Result << " templateKind=\"specialization\"";
1186         break;
1187       case DeclInfo::TemplatePartialSpecialization:
1188         llvm_unreachable("partial specializations of functions "
1189                          "are not allowed in C++");
1190       }
1191       if (DI->IsInstanceMethod)
1192         Result << " isInstanceMethod=\"1\"";
1193       if (DI->IsClassMethod)
1194         Result << " isClassMethod=\"1\"";
1195       break;
1196     case DeclInfo::ClassKind:
1197       RootEndTag = "</Class>";
1198       Result << "<Class";
1199       switch (DI->TemplateKind) {
1200       case DeclInfo::NotTemplate:
1201         break;
1202       case DeclInfo::Template:
1203         Result << " templateKind=\"template\"";
1204         break;
1205       case DeclInfo::TemplateSpecialization:
1206         Result << " templateKind=\"specialization\"";
1207         break;
1208       case DeclInfo::TemplatePartialSpecialization:
1209         Result << " templateKind=\"partialSpecialization\"";
1210         break;
1211       }
1212       break;
1213     case DeclInfo::VariableKind:
1214       RootEndTag = "</Variable>";
1215       Result << "<Variable";
1216       break;
1217     case DeclInfo::NamespaceKind:
1218       RootEndTag = "</Namespace>";
1219       Result << "<Namespace";
1220       break;
1221     case DeclInfo::TypedefKind:
1222       RootEndTag = "</Typedef>";
1223       Result << "<Typedef";
1224       break;
1225     case DeclInfo::EnumKind:
1226       RootEndTag = "</Enum>";
1227       Result << "<Enum";
1228       break;
1229     }
1230 
1231     {
1232       // Print line and column number.
1233       SourceLocation Loc = DI->CurrentDecl->getLocation();
1234       std::pair<FileID, unsigned> LocInfo = SM.getDecomposedLoc(Loc);
1235       FileID FID = LocInfo.first;
1236       unsigned FileOffset = LocInfo.second;
1237 
1238       if (!FID.isInvalid()) {
1239         if (const FileEntry *FE = SM.getFileEntryForID(FID)) {
1240           Result << " file=\"";
1241           appendToResultWithXMLEscaping(FE->getName());
1242           Result << "\"";
1243         }
1244         Result << " line=\"" << SM.getLineNumber(FID, FileOffset)
1245                << "\" column=\"" << SM.getColumnNumber(FID, FileOffset)
1246                << "\"";
1247       }
1248     }
1249 
1250     // Finish the root tag.
1251     Result << ">";
1252 
1253     bool FoundName = false;
1254     if (const NamedDecl *ND = dyn_cast<NamedDecl>(DI->CommentDecl)) {
1255       if (DeclarationName DeclName = ND->getDeclName()) {
1256         Result << "<Name>";
1257         std::string Name = DeclName.getAsString();
1258         appendToResultWithXMLEscaping(Name);
1259         FoundName = true;
1260         Result << "</Name>";
1261       }
1262     }
1263     if (!FoundName)
1264       Result << "<Name>&lt;anonymous&gt;</Name>";
1265 
1266     {
1267       // Print USR.
1268       SmallString<128> USR;
1269       cxcursor::getDeclCursorUSR(DI->CommentDecl, USR);
1270       if (!USR.empty()) {
1271         Result << "<USR>";
1272         appendToResultWithXMLEscaping(USR);
1273         Result << "</USR>";
1274       }
1275     }
1276   } else {
1277     // No DeclInfo -- just emit some root tag and name tag.
1278     RootEndTag = "</Other>";
1279     Result << "<Other><Name>unknown</Name>";
1280   }
1281 
1282   if (Parts.Headerfile) {
1283     Result << "<Headerfile>";
1284     visit(Parts.Headerfile);
1285     Result << "</Headerfile>";
1286   }
1287 
1288   {
1289     // Pretty-print the declaration.
1290     Result << "<Declaration>";
1291     SmallString<128> Declaration;
1292     getSourceTextOfDeclaration(DI, Declaration);
1293     formatTextOfDeclaration(DI, Declaration);
1294     appendToResultWithXMLEscaping(Declaration);
1295 
1296     Result << "</Declaration>";
1297   }
1298 
1299   bool FirstParagraphIsBrief = false;
1300   if (Parts.Brief) {
1301     Result << "<Abstract>";
1302     visit(Parts.Brief);
1303     Result << "</Abstract>";
1304   } else if (Parts.FirstParagraph) {
1305     Result << "<Abstract>";
1306     visit(Parts.FirstParagraph);
1307     Result << "</Abstract>";
1308     FirstParagraphIsBrief = true;
1309   }
1310 
1311   if (Parts.TParams.size() != 0) {
1312     Result << "<TemplateParameters>";
1313     for (unsigned i = 0, e = Parts.TParams.size(); i != e; ++i)
1314       visit(Parts.TParams[i]);
1315     Result << "</TemplateParameters>";
1316   }
1317 
1318   if (Parts.Params.size() != 0) {
1319     Result << "<Parameters>";
1320     for (unsigned i = 0, e = Parts.Params.size(); i != e; ++i)
1321       visit(Parts.Params[i]);
1322     Result << "</Parameters>";
1323   }
1324 
1325   if (Parts.Returns.size() != 0) {
1326     Result << "<ResultDiscussion>";
1327     for (unsigned i = 0, e = Parts.Returns.size(); i != e; ++i)
1328       visit(Parts.Returns[i]);
1329     Result << "</ResultDiscussion>";
1330   }
1331 
1332   if (DI->CommentDecl->hasAttrs()) {
1333     const AttrVec &Attrs = DI->CommentDecl->getAttrs();
1334     for (unsigned i = 0, e = Attrs.size(); i != e; i++) {
1335       const AvailabilityAttr *AA = dyn_cast<AvailabilityAttr>(Attrs[i]);
1336       if (!AA) {
1337         if (const DeprecatedAttr *DA = dyn_cast<DeprecatedAttr>(Attrs[i])) {
1338           if (DA->getMessage().empty())
1339             Result << "<Deprecated/>";
1340           else {
1341             Result << "<Deprecated>";
1342             appendToResultWithXMLEscaping(DA->getMessage());
1343             Result << "</Deprecated>";
1344           }
1345         }
1346         else if (const UnavailableAttr *UA = dyn_cast<UnavailableAttr>(Attrs[i])) {
1347           if (UA->getMessage().empty())
1348             Result << "<Unavailable/>";
1349           else {
1350             Result << "<Unavailable>";
1351             appendToResultWithXMLEscaping(UA->getMessage());
1352             Result << "</Unavailable>";
1353           }
1354         }
1355         continue;
1356       }
1357 
1358       // 'availability' attribute.
1359       Result << "<Availability";
1360       StringRef Distribution;
1361       if (AA->getPlatform()) {
1362         Distribution = AvailabilityAttr::getPrettyPlatformName(
1363                                         AA->getPlatform()->getName());
1364         if (Distribution.empty())
1365           Distribution = AA->getPlatform()->getName();
1366       }
1367       Result << " distribution=\"" << Distribution << "\">";
1368       VersionTuple IntroducedInVersion = AA->getIntroduced();
1369       if (!IntroducedInVersion.empty()) {
1370         Result << "<IntroducedInVersion>"
1371                << IntroducedInVersion.getAsString()
1372                << "</IntroducedInVersion>";
1373       }
1374       VersionTuple DeprecatedInVersion = AA->getDeprecated();
1375       if (!DeprecatedInVersion.empty()) {
1376         Result << "<DeprecatedInVersion>"
1377                << DeprecatedInVersion.getAsString()
1378                << "</DeprecatedInVersion>";
1379       }
1380       VersionTuple RemovedAfterVersion = AA->getObsoleted();
1381       if (!RemovedAfterVersion.empty()) {
1382         Result << "<RemovedAfterVersion>"
1383                << RemovedAfterVersion.getAsString()
1384                << "</RemovedAfterVersion>";
1385       }
1386       StringRef DeprecationSummary = AA->getMessage();
1387       if (!DeprecationSummary.empty()) {
1388         Result << "<DeprecationSummary>";
1389         appendToResultWithXMLEscaping(DeprecationSummary);
1390         Result << "</DeprecationSummary>";
1391       }
1392       if (AA->getUnavailable())
1393         Result << "<Unavailable/>";
1394       Result << "</Availability>";
1395     }
1396   }
1397 
1398   {
1399     bool StartTagEmitted = false;
1400     for (unsigned i = 0, e = Parts.MiscBlocks.size(); i != e; ++i) {
1401       const Comment *C = Parts.MiscBlocks[i];
1402       if (FirstParagraphIsBrief && C == Parts.FirstParagraph)
1403         continue;
1404       if (!StartTagEmitted) {
1405         Result << "<Discussion>";
1406         StartTagEmitted = true;
1407       }
1408       visit(C);
1409     }
1410     if (StartTagEmitted)
1411       Result << "</Discussion>";
1412   }
1413 
1414   Result << RootEndTag;
1415 
1416   Result.flush();
1417 }
1418 
1419 void CommentASTToXMLConverter::appendToResultWithXMLEscaping(StringRef S) {
1420   for (StringRef::iterator I = S.begin(), E = S.end(); I != E; ++I) {
1421     const char C = *I;
1422     switch (C) {
1423       case '&':
1424         Result << "&amp;";
1425         break;
1426       case '<':
1427         Result << "&lt;";
1428         break;
1429       case '>':
1430         Result << "&gt;";
1431         break;
1432       case '"':
1433         Result << "&quot;";
1434         break;
1435       case '\'':
1436         Result << "&apos;";
1437         break;
1438       default:
1439         Result << C;
1440         break;
1441     }
1442   }
1443 }
1444 
1445 extern "C" {
1446 
1447 CXString clang_FullComment_getAsXML(CXComment CXC) {
1448   const FullComment *FC = getASTNodeAs<FullComment>(CXC);
1449   if (!FC)
1450     return cxstring::createNull();
1451   ASTContext &Context = FC->getDeclInfo()->CurrentDecl->getASTContext();
1452   CXTranslationUnit TU = CXC.TranslationUnit;
1453   SourceManager &SM = cxtu::getASTUnit(TU)->getSourceManager();
1454 
1455   if (!TU->FormatContext) {
1456     TU->FormatContext = new SimpleFormatContext(Context.getLangOpts());
1457   } else if ((TU->FormatInMemoryUniqueId % 1000) == 0) {
1458     // Delete after some number of iterators, so the buffers don't grow
1459     // too large.
1460     delete TU->FormatContext;
1461     TU->FormatContext = new SimpleFormatContext(Context.getLangOpts());
1462   }
1463 
1464   SmallString<1024> XML;
1465   CommentASTToXMLConverter Converter(FC, XML, getCommandTraits(CXC), SM,
1466                                      *TU->FormatContext,
1467                                      TU->FormatInMemoryUniqueId++);
1468   Converter.visit(FC);
1469   return cxstring::createDup(XML.str());
1470 }
1471 
1472 } // end extern "C"
1473 
1474