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