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