CXComment.cpp revision aca3e56144308f97f676fa30b07179e1203f7822
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  Result << llvm::StringSwitch<const char *>(C->getCommandName(Traits))
1111      .Case("code", "<Verbatim xml:space=\"preserve\" kind=\"code\">")
1112      .Default("<Verbatim xml:space=\"preserve\" kind=\"verbatim\">");
1113  for (unsigned i = 0; i != NumLines; ++i) {
1114    appendToResultWithXMLEscaping(C->getText(i));
1115    if (i + 1 != NumLines)
1116      Result << '\n';
1117  }
1118  Result << "</Verbatim>";
1119}
1120
1121void CommentASTToXMLConverter::visitVerbatimBlockLineComment(
1122                                  const VerbatimBlockLineComment *C) {
1123  llvm_unreachable("should not see this AST node");
1124}
1125
1126void CommentASTToXMLConverter::visitVerbatimLineComment(
1127                                  const VerbatimLineComment *C) {
1128  Result << "<Verbatim xml:space=\"preserve\" kind=\"verbatim\">";
1129  appendToResultWithXMLEscaping(C->getText());
1130  Result << "</Verbatim>";
1131}
1132
1133void CommentASTToXMLConverter::visitFullComment(const FullComment *C) {
1134  FullCommentParts Parts(C, Traits);
1135
1136  const DeclInfo *DI = C->getDeclInfo();
1137  StringRef RootEndTag;
1138  if (DI) {
1139    switch (DI->getKind()) {
1140    case DeclInfo::OtherKind:
1141      RootEndTag = "</Other>";
1142      Result << "<Other";
1143      break;
1144    case DeclInfo::FunctionKind:
1145      RootEndTag = "</Function>";
1146      Result << "<Function";
1147      switch (DI->TemplateKind) {
1148      case DeclInfo::NotTemplate:
1149        break;
1150      case DeclInfo::Template:
1151        Result << " templateKind=\"template\"";
1152        break;
1153      case DeclInfo::TemplateSpecialization:
1154        Result << " templateKind=\"specialization\"";
1155        break;
1156      case DeclInfo::TemplatePartialSpecialization:
1157        llvm_unreachable("partial specializations of functions "
1158                         "are not allowed in C++");
1159      }
1160      if (DI->IsInstanceMethod)
1161        Result << " isInstanceMethod=\"1\"";
1162      if (DI->IsClassMethod)
1163        Result << " isClassMethod=\"1\"";
1164      break;
1165    case DeclInfo::ClassKind:
1166      RootEndTag = "</Class>";
1167      Result << "<Class";
1168      switch (DI->TemplateKind) {
1169      case DeclInfo::NotTemplate:
1170        break;
1171      case DeclInfo::Template:
1172        Result << " templateKind=\"template\"";
1173        break;
1174      case DeclInfo::TemplateSpecialization:
1175        Result << " templateKind=\"specialization\"";
1176        break;
1177      case DeclInfo::TemplatePartialSpecialization:
1178        Result << " templateKind=\"partialSpecialization\"";
1179        break;
1180      }
1181      break;
1182    case DeclInfo::VariableKind:
1183      RootEndTag = "</Variable>";
1184      Result << "<Variable";
1185      break;
1186    case DeclInfo::NamespaceKind:
1187      RootEndTag = "</Namespace>";
1188      Result << "<Namespace";
1189      break;
1190    case DeclInfo::TypedefKind:
1191      RootEndTag = "</Typedef>";
1192      Result << "<Typedef";
1193      break;
1194    case DeclInfo::EnumKind:
1195      RootEndTag = "</Enum>";
1196      Result << "<Enum";
1197      break;
1198    }
1199
1200    {
1201      // Print line and column number.
1202      SourceLocation Loc = DI->CurrentDecl->getLocation();
1203      std::pair<FileID, unsigned> LocInfo = SM.getDecomposedLoc(Loc);
1204      FileID FID = LocInfo.first;
1205      unsigned FileOffset = LocInfo.second;
1206
1207      if (!FID.isInvalid()) {
1208        if (const FileEntry *FE = SM.getFileEntryForID(FID)) {
1209          Result << " file=\"";
1210          appendToResultWithXMLEscaping(FE->getName());
1211          Result << "\"";
1212        }
1213        Result << " line=\"" << SM.getLineNumber(FID, FileOffset)
1214               << "\" column=\"" << SM.getColumnNumber(FID, FileOffset)
1215               << "\"";
1216      }
1217    }
1218
1219    // Finish the root tag.
1220    Result << ">";
1221
1222    bool FoundName = false;
1223    if (const NamedDecl *ND = dyn_cast<NamedDecl>(DI->CommentDecl)) {
1224      if (DeclarationName DeclName = ND->getDeclName()) {
1225        Result << "<Name>";
1226        std::string Name = DeclName.getAsString();
1227        appendToResultWithXMLEscaping(Name);
1228        FoundName = true;
1229        Result << "</Name>";
1230      }
1231    }
1232    if (!FoundName)
1233      Result << "<Name>&lt;anonymous&gt;</Name>";
1234
1235    {
1236      // Print USR.
1237      SmallString<128> USR;
1238      cxcursor::getDeclCursorUSR(DI->CommentDecl, USR);
1239      if (!USR.empty()) {
1240        Result << "<USR>";
1241        appendToResultWithXMLEscaping(USR);
1242        Result << "</USR>";
1243      }
1244    }
1245  } else {
1246    // No DeclInfo -- just emit some root tag and name tag.
1247    RootEndTag = "</Other>";
1248    Result << "<Other><Name>unknown</Name>";
1249  }
1250
1251  if (Parts.Headerfile) {
1252    Result << "<Headerfile>";
1253    visit(Parts.Headerfile);
1254    Result << "</Headerfile>";
1255  }
1256
1257  {
1258    // Pretty-print the declaration.
1259    Result << "<Declaration>";
1260    SmallString<128> Declaration;
1261    getSourceTextOfDeclaration(DI, Declaration);
1262    formatTextOfDeclaration(DI, Declaration);
1263    appendToResultWithXMLEscaping(Declaration);
1264
1265    Result << "</Declaration>";
1266  }
1267
1268  bool FirstParagraphIsBrief = false;
1269  if (Parts.Brief) {
1270    Result << "<Abstract>";
1271    visit(Parts.Brief);
1272    Result << "</Abstract>";
1273  } else if (Parts.FirstParagraph) {
1274    Result << "<Abstract>";
1275    visit(Parts.FirstParagraph);
1276    Result << "</Abstract>";
1277    FirstParagraphIsBrief = true;
1278  }
1279
1280  if (Parts.TParams.size() != 0) {
1281    Result << "<TemplateParameters>";
1282    for (unsigned i = 0, e = Parts.TParams.size(); i != e; ++i)
1283      visit(Parts.TParams[i]);
1284    Result << "</TemplateParameters>";
1285  }
1286
1287  if (Parts.Params.size() != 0) {
1288    Result << "<Parameters>";
1289    for (unsigned i = 0, e = Parts.Params.size(); i != e; ++i)
1290      visit(Parts.Params[i]);
1291    Result << "</Parameters>";
1292  }
1293
1294  if (Parts.Returns) {
1295    Result << "<ResultDiscussion>";
1296    visit(Parts.Returns);
1297    Result << "</ResultDiscussion>";
1298  }
1299
1300  if (DI->CommentDecl->hasAttrs()) {
1301    const AttrVec &Attrs = DI->CommentDecl->getAttrs();
1302    for (unsigned i = 0, e = Attrs.size(); i != e; i++) {
1303      const AvailabilityAttr *AA = dyn_cast<AvailabilityAttr>(Attrs[i]);
1304      if (!AA) {
1305        if (const DeprecatedAttr *DA = dyn_cast<DeprecatedAttr>(Attrs[i])) {
1306          if (DA->getMessage().empty())
1307            Result << "<Deprecated/>";
1308          else {
1309            Result << "<Deprecated>";
1310            appendToResultWithXMLEscaping(DA->getMessage());
1311            Result << "</Deprecated>";
1312          }
1313        }
1314        else if (const UnavailableAttr *UA = dyn_cast<UnavailableAttr>(Attrs[i])) {
1315          if (UA->getMessage().empty())
1316            Result << "<Unavailable/>";
1317          else {
1318            Result << "<Unavailable>";
1319            appendToResultWithXMLEscaping(UA->getMessage());
1320            Result << "</Unavailable>";
1321          }
1322        }
1323        continue;
1324      }
1325
1326      // 'availability' attribute.
1327      Result << "<Availability";
1328      StringRef Distribution;
1329      if (AA->getPlatform()) {
1330        Distribution = AvailabilityAttr::getPrettyPlatformName(
1331                                        AA->getPlatform()->getName());
1332        if (Distribution.empty())
1333          Distribution = AA->getPlatform()->getName();
1334      }
1335      Result << " distribution=\"" << Distribution << "\">";
1336      VersionTuple IntroducedInVersion = AA->getIntroduced();
1337      if (!IntroducedInVersion.empty()) {
1338        Result << "<IntroducedInVersion>"
1339               << IntroducedInVersion.getAsString()
1340               << "</IntroducedInVersion>";
1341      }
1342      VersionTuple DeprecatedInVersion = AA->getDeprecated();
1343      if (!DeprecatedInVersion.empty()) {
1344        Result << "<DeprecatedInVersion>"
1345               << DeprecatedInVersion.getAsString()
1346               << "</DeprecatedInVersion>";
1347      }
1348      VersionTuple RemovedAfterVersion = AA->getObsoleted();
1349      if (!RemovedAfterVersion.empty()) {
1350        Result << "<RemovedAfterVersion>"
1351               << RemovedAfterVersion.getAsString()
1352               << "</RemovedAfterVersion>";
1353      }
1354      StringRef DeprecationSummary = AA->getMessage();
1355      if (!DeprecationSummary.empty()) {
1356        Result << "<DeprecationSummary>";
1357        appendToResultWithXMLEscaping(DeprecationSummary);
1358        Result << "</DeprecationSummary>";
1359      }
1360      if (AA->getUnavailable())
1361        Result << "<Unavailable/>";
1362      Result << "</Availability>";
1363    }
1364  }
1365
1366  {
1367    bool StartTagEmitted = false;
1368    for (unsigned i = 0, e = Parts.MiscBlocks.size(); i != e; ++i) {
1369      const Comment *C = Parts.MiscBlocks[i];
1370      if (FirstParagraphIsBrief && C == Parts.FirstParagraph)
1371        continue;
1372      if (!StartTagEmitted) {
1373        Result << "<Discussion>";
1374        StartTagEmitted = true;
1375      }
1376      visit(C);
1377    }
1378    if (StartTagEmitted)
1379      Result << "</Discussion>";
1380  }
1381
1382  Result << RootEndTag;
1383
1384  Result.flush();
1385}
1386
1387void CommentASTToXMLConverter::appendToResultWithXMLEscaping(StringRef S) {
1388  for (StringRef::iterator I = S.begin(), E = S.end(); I != E; ++I) {
1389    const char C = *I;
1390    switch (C) {
1391      case '&':
1392        Result << "&amp;";
1393        break;
1394      case '<':
1395        Result << "&lt;";
1396        break;
1397      case '>':
1398        Result << "&gt;";
1399        break;
1400      case '"':
1401        Result << "&quot;";
1402        break;
1403      case '\'':
1404        Result << "&apos;";
1405        break;
1406      default:
1407        Result << C;
1408        break;
1409    }
1410  }
1411}
1412
1413extern "C" {
1414
1415CXString clang_FullComment_getAsXML(CXComment CXC) {
1416  const FullComment *FC = getASTNodeAs<FullComment>(CXC);
1417  if (!FC)
1418    return cxstring::createNull();
1419  ASTContext &Context = FC->getDeclInfo()->CurrentDecl->getASTContext();
1420  CXTranslationUnit TU = CXC.TranslationUnit;
1421  SourceManager &SM = cxtu::getASTUnit(TU)->getSourceManager();
1422
1423  if (!TU->FormatContext) {
1424    TU->FormatContext = new SimpleFormatContext(Context.getLangOpts());
1425  } else if ((TU->FormatInMemoryUniqueId % 1000) == 0) {
1426    // Delete after some number of iterators, so the buffers don't grow
1427    // too large.
1428    delete TU->FormatContext;
1429    TU->FormatContext = new SimpleFormatContext(Context.getLangOpts());
1430  }
1431
1432  SmallString<1024> XML;
1433  CommentASTToXMLConverter Converter(FC, XML, getCommandTraits(CXC), SM,
1434                                     *TU->FormatContext,
1435                                     TU->FormatInMemoryUniqueId++);
1436  Converter.visit(FC);
1437  return cxstring::createDup(XML.str());
1438}
1439
1440} // end extern "C"
1441
1442