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