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