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