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