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