CommentToXML.cpp revision 651f13cea278ec967336033dd032faef0e9fc2ec
1//===--- CommentToXML.cpp - Convert comments to XML representation --------===//
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#include "clang/Index/CommentToXML.h"
11#include "SimpleFormatContext.h"
12#include "clang/AST/ASTContext.h"
13#include "clang/AST/Attr.h"
14#include "clang/AST/Comment.h"
15#include "clang/AST/CommentVisitor.h"
16#include "clang/Format/Format.h"
17#include "clang/Index/USRGeneration.h"
18#include "clang/Lex/Lexer.h"
19#include "llvm/ADT/StringExtras.h"
20#include "llvm/ADT/TinyPtrVector.h"
21#include "llvm/Support/raw_ostream.h"
22
23using namespace clang;
24using namespace clang::comments;
25using namespace clang::index;
26
27namespace {
28
29/// This comparison will sort parameters with valid index by index, then vararg
30/// parameters, and invalid (unresolved) parameters last.
31class ParamCommandCommentCompareIndex {
32public:
33  bool operator()(const ParamCommandComment *LHS,
34                  const ParamCommandComment *RHS) const {
35    unsigned LHSIndex = UINT_MAX;
36    unsigned RHSIndex = UINT_MAX;
37
38    if (LHS->isParamIndexValid()) {
39      if (LHS->isVarArgParam())
40        LHSIndex = UINT_MAX - 1;
41      else
42        LHSIndex = LHS->getParamIndex();
43    }
44    if (RHS->isParamIndexValid()) {
45      if (RHS->isVarArgParam())
46        RHSIndex = UINT_MAX - 1;
47      else
48        RHSIndex = RHS->getParamIndex();
49    }
50    return LHSIndex < RHSIndex;
51  }
52};
53
54/// This comparison will sort template parameters in the following order:
55/// \li real template parameters (depth = 1) in index order;
56/// \li all other names (depth > 1);
57/// \li unresolved names.
58class TParamCommandCommentComparePosition {
59public:
60  bool operator()(const TParamCommandComment *LHS,
61                  const TParamCommandComment *RHS) const {
62    // Sort unresolved names last.
63    if (!LHS->isPositionValid())
64      return false;
65    if (!RHS->isPositionValid())
66      return true;
67
68    if (LHS->getDepth() > 1)
69      return false;
70    if (RHS->getDepth() > 1)
71      return true;
72
73    // Sort template parameters in index order.
74    if (LHS->getDepth() == 1 && RHS->getDepth() == 1)
75      return LHS->getIndex(0) < RHS->getIndex(0);
76
77    // Leave all other names in source order.
78    return true;
79  }
80};
81
82/// Separate parts of a FullComment.
83struct FullCommentParts {
84  /// Take a full comment apart and initialize members accordingly.
85  FullCommentParts(const FullComment *C,
86                   const CommandTraits &Traits);
87
88  const BlockContentComment *Brief;
89  const BlockContentComment *Headerfile;
90  const ParagraphComment *FirstParagraph;
91  SmallVector<const BlockCommandComment *, 4> Returns;
92  SmallVector<const ParamCommandComment *, 8> Params;
93  SmallVector<const TParamCommandComment *, 4> TParams;
94  llvm::TinyPtrVector<const BlockCommandComment *> Exceptions;
95  SmallVector<const BlockContentComment *, 8> MiscBlocks;
96};
97
98FullCommentParts::FullCommentParts(const FullComment *C,
99                                   const CommandTraits &Traits) :
100    Brief(NULL), Headerfile(NULL), FirstParagraph(NULL) {
101  for (Comment::child_iterator I = C->child_begin(), E = C->child_end();
102       I != E; ++I) {
103    const Comment *Child = *I;
104    if (!Child)
105      continue;
106    switch (Child->getCommentKind()) {
107    case Comment::NoCommentKind:
108      continue;
109
110    case Comment::ParagraphCommentKind: {
111      const ParagraphComment *PC = cast<ParagraphComment>(Child);
112      if (PC->isWhitespace())
113        break;
114      if (!FirstParagraph)
115        FirstParagraph = PC;
116
117      MiscBlocks.push_back(PC);
118      break;
119    }
120
121    case Comment::BlockCommandCommentKind: {
122      const BlockCommandComment *BCC = cast<BlockCommandComment>(Child);
123      const CommandInfo *Info = Traits.getCommandInfo(BCC->getCommandID());
124      if (!Brief && Info->IsBriefCommand) {
125        Brief = BCC;
126        break;
127      }
128      if (!Headerfile && Info->IsHeaderfileCommand) {
129        Headerfile = BCC;
130        break;
131      }
132      if (Info->IsReturnsCommand) {
133        Returns.push_back(BCC);
134        break;
135      }
136      if (Info->IsThrowsCommand) {
137        Exceptions.push_back(BCC);
138        break;
139      }
140      MiscBlocks.push_back(BCC);
141      break;
142    }
143
144    case Comment::ParamCommandCommentKind: {
145      const ParamCommandComment *PCC = cast<ParamCommandComment>(Child);
146      if (!PCC->hasParamName())
147        break;
148
149      if (!PCC->isDirectionExplicit() && !PCC->hasNonWhitespaceParagraph())
150        break;
151
152      Params.push_back(PCC);
153      break;
154    }
155
156    case Comment::TParamCommandCommentKind: {
157      const TParamCommandComment *TPCC = cast<TParamCommandComment>(Child);
158      if (!TPCC->hasParamName())
159        break;
160
161      if (!TPCC->hasNonWhitespaceParagraph())
162        break;
163
164      TParams.push_back(TPCC);
165      break;
166    }
167
168    case Comment::VerbatimBlockCommentKind:
169      MiscBlocks.push_back(cast<BlockCommandComment>(Child));
170      break;
171
172    case Comment::VerbatimLineCommentKind: {
173      const VerbatimLineComment *VLC = cast<VerbatimLineComment>(Child);
174      const CommandInfo *Info = Traits.getCommandInfo(VLC->getCommandID());
175      if (!Info->IsDeclarationCommand)
176        MiscBlocks.push_back(VLC);
177      break;
178    }
179
180    case Comment::TextCommentKind:
181    case Comment::InlineCommandCommentKind:
182    case Comment::HTMLStartTagCommentKind:
183    case Comment::HTMLEndTagCommentKind:
184    case Comment::VerbatimBlockLineCommentKind:
185    case Comment::FullCommentKind:
186      llvm_unreachable("AST node of this kind can't be a child of "
187                       "a FullComment");
188    }
189  }
190
191  // Sort params in order they are declared in the function prototype.
192  // Unresolved parameters are put at the end of the list in the same order
193  // they were seen in the comment.
194  std::stable_sort(Params.begin(), Params.end(),
195                   ParamCommandCommentCompareIndex());
196
197  std::stable_sort(TParams.begin(), TParams.end(),
198                   TParamCommandCommentComparePosition());
199}
200
201void printHTMLStartTagComment(const HTMLStartTagComment *C,
202                              llvm::raw_svector_ostream &Result) {
203  Result << "<" << C->getTagName();
204
205  if (C->getNumAttrs() != 0) {
206    for (unsigned i = 0, e = C->getNumAttrs(); i != e; i++) {
207      Result << " ";
208      const HTMLStartTagComment::Attribute &Attr = C->getAttr(i);
209      Result << Attr.Name;
210      if (!Attr.Value.empty())
211        Result << "=\"" << Attr.Value << "\"";
212    }
213  }
214
215  if (!C->isSelfClosing())
216    Result << ">";
217  else
218    Result << "/>";
219}
220
221class CommentASTToHTMLConverter :
222    public ConstCommentVisitor<CommentASTToHTMLConverter> {
223public:
224  /// \param Str accumulator for HTML.
225  CommentASTToHTMLConverter(const FullComment *FC,
226                            SmallVectorImpl<char> &Str,
227                            const CommandTraits &Traits) :
228      FC(FC), Result(Str), Traits(Traits)
229  { }
230
231  // Inline content.
232  void visitTextComment(const TextComment *C);
233  void visitInlineCommandComment(const InlineCommandComment *C);
234  void visitHTMLStartTagComment(const HTMLStartTagComment *C);
235  void visitHTMLEndTagComment(const HTMLEndTagComment *C);
236
237  // Block content.
238  void visitParagraphComment(const ParagraphComment *C);
239  void visitBlockCommandComment(const BlockCommandComment *C);
240  void visitParamCommandComment(const ParamCommandComment *C);
241  void visitTParamCommandComment(const TParamCommandComment *C);
242  void visitVerbatimBlockComment(const VerbatimBlockComment *C);
243  void visitVerbatimBlockLineComment(const VerbatimBlockLineComment *C);
244  void visitVerbatimLineComment(const VerbatimLineComment *C);
245
246  void visitFullComment(const FullComment *C);
247
248  // Helpers.
249
250  /// Convert a paragraph that is not a block by itself (an argument to some
251  /// command).
252  void visitNonStandaloneParagraphComment(const ParagraphComment *C);
253
254  void appendToResultWithHTMLEscaping(StringRef S);
255
256private:
257  const FullComment *FC;
258  /// Output stream for HTML.
259  llvm::raw_svector_ostream Result;
260
261  const CommandTraits &Traits;
262};
263} // end unnamed namespace
264
265void CommentASTToHTMLConverter::visitTextComment(const TextComment *C) {
266  appendToResultWithHTMLEscaping(C->getText());
267}
268
269void CommentASTToHTMLConverter::visitInlineCommandComment(
270                                  const InlineCommandComment *C) {
271  // Nothing to render if no arguments supplied.
272  if (C->getNumArgs() == 0)
273    return;
274
275  // Nothing to render if argument is empty.
276  StringRef Arg0 = C->getArgText(0);
277  if (Arg0.empty())
278    return;
279
280  switch (C->getRenderKind()) {
281  case InlineCommandComment::RenderNormal:
282    for (unsigned i = 0, e = C->getNumArgs(); i != e; ++i) {
283      appendToResultWithHTMLEscaping(C->getArgText(i));
284      Result << " ";
285    }
286    return;
287
288  case InlineCommandComment::RenderBold:
289    assert(C->getNumArgs() == 1);
290    Result << "<b>";
291    appendToResultWithHTMLEscaping(Arg0);
292    Result << "</b>";
293    return;
294  case InlineCommandComment::RenderMonospaced:
295    assert(C->getNumArgs() == 1);
296    Result << "<tt>";
297    appendToResultWithHTMLEscaping(Arg0);
298    Result<< "</tt>";
299    return;
300  case InlineCommandComment::RenderEmphasized:
301    assert(C->getNumArgs() == 1);
302    Result << "<em>";
303    appendToResultWithHTMLEscaping(Arg0);
304    Result << "</em>";
305    return;
306  }
307}
308
309void CommentASTToHTMLConverter::visitHTMLStartTagComment(
310                                  const HTMLStartTagComment *C) {
311  printHTMLStartTagComment(C, Result);
312}
313
314void CommentASTToHTMLConverter::visitHTMLEndTagComment(
315                                  const HTMLEndTagComment *C) {
316  Result << "</" << C->getTagName() << ">";
317}
318
319void CommentASTToHTMLConverter::visitParagraphComment(
320                                  const ParagraphComment *C) {
321  if (C->isWhitespace())
322    return;
323
324  Result << "<p>";
325  for (Comment::child_iterator I = C->child_begin(), E = C->child_end();
326       I != E; ++I) {
327    visit(*I);
328  }
329  Result << "</p>";
330}
331
332void CommentASTToHTMLConverter::visitBlockCommandComment(
333                                  const BlockCommandComment *C) {
334  const CommandInfo *Info = Traits.getCommandInfo(C->getCommandID());
335  if (Info->IsBriefCommand) {
336    Result << "<p class=\"para-brief\">";
337    visitNonStandaloneParagraphComment(C->getParagraph());
338    Result << "</p>";
339    return;
340  }
341  if (Info->IsReturnsCommand) {
342    Result << "<p class=\"para-returns\">"
343              "<span class=\"word-returns\">Returns</span> ";
344    visitNonStandaloneParagraphComment(C->getParagraph());
345    Result << "</p>";
346    return;
347  }
348  // We don't know anything about this command.  Just render the paragraph.
349  visit(C->getParagraph());
350}
351
352void CommentASTToHTMLConverter::visitParamCommandComment(
353                                  const ParamCommandComment *C) {
354  if (C->isParamIndexValid()) {
355    if (C->isVarArgParam()) {
356      Result << "<dt class=\"param-name-index-vararg\">";
357      appendToResultWithHTMLEscaping(C->getParamNameAsWritten());
358    } else {
359      Result << "<dt class=\"param-name-index-"
360             << C->getParamIndex()
361             << "\">";
362      appendToResultWithHTMLEscaping(C->getParamName(FC));
363    }
364  } else {
365    Result << "<dt class=\"param-name-index-invalid\">";
366    appendToResultWithHTMLEscaping(C->getParamNameAsWritten());
367  }
368  Result << "</dt>";
369
370  if (C->isParamIndexValid()) {
371    if (C->isVarArgParam())
372      Result << "<dd class=\"param-descr-index-vararg\">";
373    else
374      Result << "<dd class=\"param-descr-index-"
375             << C->getParamIndex()
376             << "\">";
377  } else
378    Result << "<dd class=\"param-descr-index-invalid\">";
379
380  visitNonStandaloneParagraphComment(C->getParagraph());
381  Result << "</dd>";
382}
383
384void CommentASTToHTMLConverter::visitTParamCommandComment(
385                                  const TParamCommandComment *C) {
386  if (C->isPositionValid()) {
387    if (C->getDepth() == 1)
388      Result << "<dt class=\"tparam-name-index-"
389             << C->getIndex(0)
390             << "\">";
391    else
392      Result << "<dt class=\"tparam-name-index-other\">";
393    appendToResultWithHTMLEscaping(C->getParamName(FC));
394  } else {
395    Result << "<dt class=\"tparam-name-index-invalid\">";
396    appendToResultWithHTMLEscaping(C->getParamNameAsWritten());
397  }
398
399  Result << "</dt>";
400
401  if (C->isPositionValid()) {
402    if (C->getDepth() == 1)
403      Result << "<dd class=\"tparam-descr-index-"
404             << C->getIndex(0)
405             << "\">";
406    else
407      Result << "<dd class=\"tparam-descr-index-other\">";
408  } else
409    Result << "<dd class=\"tparam-descr-index-invalid\">";
410
411  visitNonStandaloneParagraphComment(C->getParagraph());
412  Result << "</dd>";
413}
414
415void CommentASTToHTMLConverter::visitVerbatimBlockComment(
416                                  const VerbatimBlockComment *C) {
417  unsigned NumLines = C->getNumLines();
418  if (NumLines == 0)
419    return;
420
421  Result << "<pre>";
422  for (unsigned i = 0; i != NumLines; ++i) {
423    appendToResultWithHTMLEscaping(C->getText(i));
424    if (i + 1 != NumLines)
425      Result << '\n';
426  }
427  Result << "</pre>";
428}
429
430void CommentASTToHTMLConverter::visitVerbatimBlockLineComment(
431                                  const VerbatimBlockLineComment *C) {
432  llvm_unreachable("should not see this AST node");
433}
434
435void CommentASTToHTMLConverter::visitVerbatimLineComment(
436                                  const VerbatimLineComment *C) {
437  Result << "<pre>";
438  appendToResultWithHTMLEscaping(C->getText());
439  Result << "</pre>";
440}
441
442void CommentASTToHTMLConverter::visitFullComment(const FullComment *C) {
443  FullCommentParts Parts(C, Traits);
444
445  bool FirstParagraphIsBrief = false;
446  if (Parts.Headerfile)
447    visit(Parts.Headerfile);
448  if (Parts.Brief)
449    visit(Parts.Brief);
450  else if (Parts.FirstParagraph) {
451    Result << "<p class=\"para-brief\">";
452    visitNonStandaloneParagraphComment(Parts.FirstParagraph);
453    Result << "</p>";
454    FirstParagraphIsBrief = true;
455  }
456
457  for (unsigned i = 0, e = Parts.MiscBlocks.size(); i != e; ++i) {
458    const Comment *C = Parts.MiscBlocks[i];
459    if (FirstParagraphIsBrief && C == Parts.FirstParagraph)
460      continue;
461    visit(C);
462  }
463
464  if (Parts.TParams.size() != 0) {
465    Result << "<dl>";
466    for (unsigned i = 0, e = Parts.TParams.size(); i != e; ++i)
467      visit(Parts.TParams[i]);
468    Result << "</dl>";
469  }
470
471  if (Parts.Params.size() != 0) {
472    Result << "<dl>";
473    for (unsigned i = 0, e = Parts.Params.size(); i != e; ++i)
474      visit(Parts.Params[i]);
475    Result << "</dl>";
476  }
477
478  if (Parts.Returns.size() != 0) {
479    Result << "<div class=\"result-discussion\">";
480    for (unsigned i = 0, e = Parts.Returns.size(); i != e; ++i)
481      visit(Parts.Returns[i]);
482    Result << "</div>";
483  }
484
485  Result.flush();
486}
487
488void CommentASTToHTMLConverter::visitNonStandaloneParagraphComment(
489                                  const ParagraphComment *C) {
490  if (!C)
491    return;
492
493  for (Comment::child_iterator I = C->child_begin(), E = C->child_end();
494       I != E; ++I) {
495    visit(*I);
496  }
497}
498
499void CommentASTToHTMLConverter::appendToResultWithHTMLEscaping(StringRef S) {
500  for (StringRef::iterator I = S.begin(), E = S.end(); I != E; ++I) {
501    const char C = *I;
502    switch (C) {
503    case '&':
504      Result << "&amp;";
505      break;
506    case '<':
507      Result << "&lt;";
508      break;
509    case '>':
510      Result << "&gt;";
511      break;
512    case '"':
513      Result << "&quot;";
514      break;
515    case '\'':
516      Result << "&#39;";
517      break;
518    case '/':
519      Result << "&#47;";
520      break;
521    default:
522      Result << C;
523      break;
524    }
525  }
526}
527
528namespace {
529class CommentASTToXMLConverter :
530    public ConstCommentVisitor<CommentASTToXMLConverter> {
531public:
532  /// \param Str accumulator for XML.
533  CommentASTToXMLConverter(const FullComment *FC,
534                           SmallVectorImpl<char> &Str,
535                           const CommandTraits &Traits,
536                           const SourceManager &SM,
537                           SimpleFormatContext &SFC,
538                           unsigned FUID) :
539      FC(FC), Result(Str), Traits(Traits), SM(SM),
540      FormatRewriterContext(SFC),
541      FormatInMemoryUniqueId(FUID) { }
542
543  // Inline content.
544  void visitTextComment(const TextComment *C);
545  void visitInlineCommandComment(const InlineCommandComment *C);
546  void visitHTMLStartTagComment(const HTMLStartTagComment *C);
547  void visitHTMLEndTagComment(const HTMLEndTagComment *C);
548
549  // Block content.
550  void visitParagraphComment(const ParagraphComment *C);
551
552  void appendParagraphCommentWithKind(const ParagraphComment *C,
553                                      StringRef Kind);
554
555  void visitBlockCommandComment(const BlockCommandComment *C);
556  void visitParamCommandComment(const ParamCommandComment *C);
557  void visitTParamCommandComment(const TParamCommandComment *C);
558  void visitVerbatimBlockComment(const VerbatimBlockComment *C);
559  void visitVerbatimBlockLineComment(const VerbatimBlockLineComment *C);
560  void visitVerbatimLineComment(const VerbatimLineComment *C);
561
562  void visitFullComment(const FullComment *C);
563
564  // Helpers.
565  void appendToResultWithXMLEscaping(StringRef S);
566
567  void formatTextOfDeclaration(const DeclInfo *DI,
568                               SmallString<128> &Declaration);
569
570private:
571  const FullComment *FC;
572
573  /// Output stream for XML.
574  llvm::raw_svector_ostream Result;
575
576  const CommandTraits &Traits;
577  const SourceManager &SM;
578  SimpleFormatContext &FormatRewriterContext;
579  unsigned FormatInMemoryUniqueId;
580};
581
582void getSourceTextOfDeclaration(const DeclInfo *ThisDecl,
583                                SmallVectorImpl<char> &Str) {
584  ASTContext &Context = ThisDecl->CurrentDecl->getASTContext();
585  const LangOptions &LangOpts = Context.getLangOpts();
586  llvm::raw_svector_ostream OS(Str);
587  PrintingPolicy PPolicy(LangOpts);
588  PPolicy.PolishForDeclaration = true;
589  PPolicy.TerseOutput = true;
590  ThisDecl->CurrentDecl->print(OS, PPolicy,
591                               /*Indentation*/0, /*PrintInstantiation*/false);
592}
593
594void CommentASTToXMLConverter::formatTextOfDeclaration(
595    const DeclInfo *DI, SmallString<128> &Declaration) {
596  // FIXME. formatting API expects null terminated input string.
597  // There might be more efficient way of doing this.
598  std::string StringDecl = Declaration.str();
599
600  // Formatter specific code.
601  // Form a unique in memory buffer name.
602  SmallString<128> filename;
603  filename += "xmldecl";
604  filename += llvm::utostr(FormatInMemoryUniqueId);
605  filename += ".xd";
606  FileID ID = FormatRewriterContext.createInMemoryFile(filename, StringDecl);
607  SourceLocation Start = FormatRewriterContext.Sources.getLocForStartOfFile(ID)
608      .getLocWithOffset(0);
609  unsigned Length = Declaration.size();
610
611  std::vector<CharSourceRange> Ranges(
612      1, CharSourceRange::getCharRange(Start, Start.getLocWithOffset(Length)));
613  ASTContext &Context = DI->CurrentDecl->getASTContext();
614  const LangOptions &LangOpts = Context.getLangOpts();
615  Lexer Lex(ID, FormatRewriterContext.Sources.getBuffer(ID),
616            FormatRewriterContext.Sources, LangOpts);
617  tooling::Replacements Replace = reformat(
618      format::getLLVMStyle(), Lex, FormatRewriterContext.Sources, Ranges);
619  applyAllReplacements(Replace, FormatRewriterContext.Rewrite);
620  Declaration = FormatRewriterContext.getRewrittenText(ID);
621}
622
623} // end unnamed namespace
624
625void CommentASTToXMLConverter::visitTextComment(const TextComment *C) {
626  appendToResultWithXMLEscaping(C->getText());
627}
628
629void CommentASTToXMLConverter::visitInlineCommandComment(
630    const InlineCommandComment *C) {
631  // Nothing to render if no arguments supplied.
632  if (C->getNumArgs() == 0)
633    return;
634
635  // Nothing to render if argument is empty.
636  StringRef Arg0 = C->getArgText(0);
637  if (Arg0.empty())
638    return;
639
640  switch (C->getRenderKind()) {
641  case InlineCommandComment::RenderNormal:
642    for (unsigned i = 0, e = C->getNumArgs(); i != e; ++i) {
643      appendToResultWithXMLEscaping(C->getArgText(i));
644      Result << " ";
645    }
646    return;
647  case InlineCommandComment::RenderBold:
648    assert(C->getNumArgs() == 1);
649    Result << "<bold>";
650    appendToResultWithXMLEscaping(Arg0);
651    Result << "</bold>";
652    return;
653  case InlineCommandComment::RenderMonospaced:
654    assert(C->getNumArgs() == 1);
655    Result << "<monospaced>";
656    appendToResultWithXMLEscaping(Arg0);
657    Result << "</monospaced>";
658    return;
659  case InlineCommandComment::RenderEmphasized:
660    assert(C->getNumArgs() == 1);
661    Result << "<emphasized>";
662    appendToResultWithXMLEscaping(Arg0);
663    Result << "</emphasized>";
664    return;
665  }
666}
667
668void CommentASTToXMLConverter::visitHTMLStartTagComment(
669    const HTMLStartTagComment *C) {
670  Result << "<rawHTML><![CDATA[";
671  printHTMLStartTagComment(C, Result);
672  Result << "]]></rawHTML>";
673}
674
675void
676CommentASTToXMLConverter::visitHTMLEndTagComment(const HTMLEndTagComment *C) {
677  Result << "<rawHTML>&lt;/" << C->getTagName() << "&gt;</rawHTML>";
678}
679
680void
681CommentASTToXMLConverter::visitParagraphComment(const ParagraphComment *C) {
682  appendParagraphCommentWithKind(C, StringRef());
683}
684
685void CommentASTToXMLConverter::appendParagraphCommentWithKind(
686                                  const ParagraphComment *C,
687                                  StringRef ParagraphKind) {
688  if (C->isWhitespace())
689    return;
690
691  if (ParagraphKind.empty())
692    Result << "<Para>";
693  else
694    Result << "<Para kind=\"" << ParagraphKind << "\">";
695
696  for (Comment::child_iterator I = C->child_begin(), E = C->child_end();
697       I != E; ++I) {
698    visit(*I);
699  }
700  Result << "</Para>";
701}
702
703void CommentASTToXMLConverter::visitBlockCommandComment(
704    const BlockCommandComment *C) {
705  StringRef ParagraphKind;
706
707  switch (C->getCommandID()) {
708  case CommandTraits::KCI_attention:
709  case CommandTraits::KCI_author:
710  case CommandTraits::KCI_authors:
711  case CommandTraits::KCI_bug:
712  case CommandTraits::KCI_copyright:
713  case CommandTraits::KCI_date:
714  case CommandTraits::KCI_invariant:
715  case CommandTraits::KCI_note:
716  case CommandTraits::KCI_post:
717  case CommandTraits::KCI_pre:
718  case CommandTraits::KCI_remark:
719  case CommandTraits::KCI_remarks:
720  case CommandTraits::KCI_sa:
721  case CommandTraits::KCI_see:
722  case CommandTraits::KCI_since:
723  case CommandTraits::KCI_todo:
724  case CommandTraits::KCI_version:
725  case CommandTraits::KCI_warning:
726    ParagraphKind = C->getCommandName(Traits);
727  default:
728    break;
729  }
730
731  appendParagraphCommentWithKind(C->getParagraph(), ParagraphKind);
732}
733
734void CommentASTToXMLConverter::visitParamCommandComment(
735    const ParamCommandComment *C) {
736  Result << "<Parameter><Name>";
737  appendToResultWithXMLEscaping(C->isParamIndexValid()
738                                    ? C->getParamName(FC)
739                                    : C->getParamNameAsWritten());
740  Result << "</Name>";
741
742  if (C->isParamIndexValid()) {
743    if (C->isVarArgParam())
744      Result << "<IsVarArg />";
745    else
746      Result << "<Index>" << C->getParamIndex() << "</Index>";
747  }
748
749  Result << "<Direction isExplicit=\"" << C->isDirectionExplicit() << "\">";
750  switch (C->getDirection()) {
751  case ParamCommandComment::In:
752    Result << "in";
753    break;
754  case ParamCommandComment::Out:
755    Result << "out";
756    break;
757  case ParamCommandComment::InOut:
758    Result << "in,out";
759    break;
760  }
761  Result << "</Direction><Discussion>";
762  visit(C->getParagraph());
763  Result << "</Discussion></Parameter>";
764}
765
766void CommentASTToXMLConverter::visitTParamCommandComment(
767                                  const TParamCommandComment *C) {
768  Result << "<Parameter><Name>";
769  appendToResultWithXMLEscaping(C->isPositionValid() ? C->getParamName(FC)
770                                : C->getParamNameAsWritten());
771  Result << "</Name>";
772
773  if (C->isPositionValid() && C->getDepth() == 1) {
774    Result << "<Index>" << C->getIndex(0) << "</Index>";
775  }
776
777  Result << "<Discussion>";
778  visit(C->getParagraph());
779  Result << "</Discussion></Parameter>";
780}
781
782void CommentASTToXMLConverter::visitVerbatimBlockComment(
783                                  const VerbatimBlockComment *C) {
784  unsigned NumLines = C->getNumLines();
785  if (NumLines == 0)
786    return;
787
788  switch (C->getCommandID()) {
789  case CommandTraits::KCI_code:
790    Result << "<Verbatim xml:space=\"preserve\" kind=\"code\">";
791    break;
792  default:
793    Result << "<Verbatim xml:space=\"preserve\" kind=\"verbatim\">";
794    break;
795  }
796  for (unsigned i = 0; i != NumLines; ++i) {
797    appendToResultWithXMLEscaping(C->getText(i));
798    if (i + 1 != NumLines)
799      Result << '\n';
800  }
801  Result << "</Verbatim>";
802}
803
804void CommentASTToXMLConverter::visitVerbatimBlockLineComment(
805                                  const VerbatimBlockLineComment *C) {
806  llvm_unreachable("should not see this AST node");
807}
808
809void CommentASTToXMLConverter::visitVerbatimLineComment(
810                                  const VerbatimLineComment *C) {
811  Result << "<Verbatim xml:space=\"preserve\" kind=\"verbatim\">";
812  appendToResultWithXMLEscaping(C->getText());
813  Result << "</Verbatim>";
814}
815
816void CommentASTToXMLConverter::visitFullComment(const FullComment *C) {
817  FullCommentParts Parts(C, Traits);
818
819  const DeclInfo *DI = C->getDeclInfo();
820  StringRef RootEndTag;
821  if (DI) {
822    switch (DI->getKind()) {
823    case DeclInfo::OtherKind:
824      RootEndTag = "</Other>";
825      Result << "<Other";
826      break;
827    case DeclInfo::FunctionKind:
828      RootEndTag = "</Function>";
829      Result << "<Function";
830      switch (DI->TemplateKind) {
831      case DeclInfo::NotTemplate:
832        break;
833      case DeclInfo::Template:
834        Result << " templateKind=\"template\"";
835        break;
836      case DeclInfo::TemplateSpecialization:
837        Result << " templateKind=\"specialization\"";
838        break;
839      case DeclInfo::TemplatePartialSpecialization:
840        llvm_unreachable("partial specializations of functions "
841                         "are not allowed in C++");
842      }
843      if (DI->IsInstanceMethod)
844        Result << " isInstanceMethod=\"1\"";
845      if (DI->IsClassMethod)
846        Result << " isClassMethod=\"1\"";
847      break;
848    case DeclInfo::ClassKind:
849      RootEndTag = "</Class>";
850      Result << "<Class";
851      switch (DI->TemplateKind) {
852      case DeclInfo::NotTemplate:
853        break;
854      case DeclInfo::Template:
855        Result << " templateKind=\"template\"";
856        break;
857      case DeclInfo::TemplateSpecialization:
858        Result << " templateKind=\"specialization\"";
859        break;
860      case DeclInfo::TemplatePartialSpecialization:
861        Result << " templateKind=\"partialSpecialization\"";
862        break;
863      }
864      break;
865    case DeclInfo::VariableKind:
866      RootEndTag = "</Variable>";
867      Result << "<Variable";
868      break;
869    case DeclInfo::NamespaceKind:
870      RootEndTag = "</Namespace>";
871      Result << "<Namespace";
872      break;
873    case DeclInfo::TypedefKind:
874      RootEndTag = "</Typedef>";
875      Result << "<Typedef";
876      break;
877    case DeclInfo::EnumKind:
878      RootEndTag = "</Enum>";
879      Result << "<Enum";
880      break;
881    }
882
883    {
884      // Print line and column number.
885      SourceLocation Loc = DI->CurrentDecl->getLocation();
886      std::pair<FileID, unsigned> LocInfo = SM.getDecomposedLoc(Loc);
887      FileID FID = LocInfo.first;
888      unsigned FileOffset = LocInfo.second;
889
890      if (!FID.isInvalid()) {
891        if (const FileEntry *FE = SM.getFileEntryForID(FID)) {
892          Result << " file=\"";
893          appendToResultWithXMLEscaping(FE->getName());
894          Result << "\"";
895        }
896        Result << " line=\"" << SM.getLineNumber(FID, FileOffset)
897               << "\" column=\"" << SM.getColumnNumber(FID, FileOffset)
898               << "\"";
899      }
900    }
901
902    // Finish the root tag.
903    Result << ">";
904
905    bool FoundName = false;
906    if (const NamedDecl *ND = dyn_cast<NamedDecl>(DI->CommentDecl)) {
907      if (DeclarationName DeclName = ND->getDeclName()) {
908        Result << "<Name>";
909        std::string Name = DeclName.getAsString();
910        appendToResultWithXMLEscaping(Name);
911        FoundName = true;
912        Result << "</Name>";
913      }
914    }
915    if (!FoundName)
916      Result << "<Name>&lt;anonymous&gt;</Name>";
917
918    {
919      // Print USR.
920      SmallString<128> USR;
921      generateUSRForDecl(DI->CommentDecl, USR);
922      if (!USR.empty()) {
923        Result << "<USR>";
924        appendToResultWithXMLEscaping(USR);
925        Result << "</USR>";
926      }
927    }
928  } else {
929    // No DeclInfo -- just emit some root tag and name tag.
930    RootEndTag = "</Other>";
931    Result << "<Other><Name>unknown</Name>";
932  }
933
934  if (Parts.Headerfile) {
935    Result << "<Headerfile>";
936    visit(Parts.Headerfile);
937    Result << "</Headerfile>";
938  }
939
940  {
941    // Pretty-print the declaration.
942    Result << "<Declaration>";
943    SmallString<128> Declaration;
944    getSourceTextOfDeclaration(DI, Declaration);
945    formatTextOfDeclaration(DI, Declaration);
946    appendToResultWithXMLEscaping(Declaration);
947    Result << "</Declaration>";
948  }
949
950  bool FirstParagraphIsBrief = false;
951  if (Parts.Brief) {
952    Result << "<Abstract>";
953    visit(Parts.Brief);
954    Result << "</Abstract>";
955  } else if (Parts.FirstParagraph) {
956    Result << "<Abstract>";
957    visit(Parts.FirstParagraph);
958    Result << "</Abstract>";
959    FirstParagraphIsBrief = true;
960  }
961
962  if (Parts.TParams.size() != 0) {
963    Result << "<TemplateParameters>";
964    for (unsigned i = 0, e = Parts.TParams.size(); i != e; ++i)
965      visit(Parts.TParams[i]);
966    Result << "</TemplateParameters>";
967  }
968
969  if (Parts.Params.size() != 0) {
970    Result << "<Parameters>";
971    for (unsigned i = 0, e = Parts.Params.size(); i != e; ++i)
972      visit(Parts.Params[i]);
973    Result << "</Parameters>";
974  }
975
976  if (Parts.Exceptions.size() != 0) {
977    Result << "<Exceptions>";
978    for (unsigned i = 0, e = Parts.Exceptions.size(); i != e; ++i)
979      visit(Parts.Exceptions[i]);
980    Result << "</Exceptions>";
981  }
982
983  if (Parts.Returns.size() != 0) {
984    Result << "<ResultDiscussion>";
985    for (unsigned i = 0, e = Parts.Returns.size(); i != e; ++i)
986      visit(Parts.Returns[i]);
987    Result << "</ResultDiscussion>";
988  }
989
990  if (DI->CommentDecl->hasAttrs()) {
991    const AttrVec &Attrs = DI->CommentDecl->getAttrs();
992    for (unsigned i = 0, e = Attrs.size(); i != e; i++) {
993      const AvailabilityAttr *AA = dyn_cast<AvailabilityAttr>(Attrs[i]);
994      if (!AA) {
995        if (const DeprecatedAttr *DA = dyn_cast<DeprecatedAttr>(Attrs[i])) {
996          if (DA->getMessage().empty())
997            Result << "<Deprecated/>";
998          else {
999            Result << "<Deprecated>";
1000            appendToResultWithXMLEscaping(DA->getMessage());
1001            Result << "</Deprecated>";
1002          }
1003        }
1004        else if (const UnavailableAttr *UA = dyn_cast<UnavailableAttr>(Attrs[i])) {
1005          if (UA->getMessage().empty())
1006            Result << "<Unavailable/>";
1007          else {
1008            Result << "<Unavailable>";
1009            appendToResultWithXMLEscaping(UA->getMessage());
1010            Result << "</Unavailable>";
1011          }
1012        }
1013        continue;
1014      }
1015
1016      // 'availability' attribute.
1017      Result << "<Availability";
1018      StringRef Distribution;
1019      if (AA->getPlatform()) {
1020        Distribution = AvailabilityAttr::getPrettyPlatformName(
1021                                        AA->getPlatform()->getName());
1022        if (Distribution.empty())
1023          Distribution = AA->getPlatform()->getName();
1024      }
1025      Result << " distribution=\"" << Distribution << "\">";
1026      VersionTuple IntroducedInVersion = AA->getIntroduced();
1027      if (!IntroducedInVersion.empty()) {
1028        Result << "<IntroducedInVersion>"
1029               << IntroducedInVersion.getAsString()
1030               << "</IntroducedInVersion>";
1031      }
1032      VersionTuple DeprecatedInVersion = AA->getDeprecated();
1033      if (!DeprecatedInVersion.empty()) {
1034        Result << "<DeprecatedInVersion>"
1035               << DeprecatedInVersion.getAsString()
1036               << "</DeprecatedInVersion>";
1037      }
1038      VersionTuple RemovedAfterVersion = AA->getObsoleted();
1039      if (!RemovedAfterVersion.empty()) {
1040        Result << "<RemovedAfterVersion>"
1041               << RemovedAfterVersion.getAsString()
1042               << "</RemovedAfterVersion>";
1043      }
1044      StringRef DeprecationSummary = AA->getMessage();
1045      if (!DeprecationSummary.empty()) {
1046        Result << "<DeprecationSummary>";
1047        appendToResultWithXMLEscaping(DeprecationSummary);
1048        Result << "</DeprecationSummary>";
1049      }
1050      if (AA->getUnavailable())
1051        Result << "<Unavailable/>";
1052      Result << "</Availability>";
1053    }
1054  }
1055
1056  {
1057    bool StartTagEmitted = false;
1058    for (unsigned i = 0, e = Parts.MiscBlocks.size(); i != e; ++i) {
1059      const Comment *C = Parts.MiscBlocks[i];
1060      if (FirstParagraphIsBrief && C == Parts.FirstParagraph)
1061        continue;
1062      if (!StartTagEmitted) {
1063        Result << "<Discussion>";
1064        StartTagEmitted = true;
1065      }
1066      visit(C);
1067    }
1068    if (StartTagEmitted)
1069      Result << "</Discussion>";
1070  }
1071
1072  Result << RootEndTag;
1073
1074  Result.flush();
1075}
1076
1077void CommentASTToXMLConverter::appendToResultWithXMLEscaping(StringRef S) {
1078  for (StringRef::iterator I = S.begin(), E = S.end(); I != E; ++I) {
1079    const char C = *I;
1080    switch (C) {
1081    case '&':
1082      Result << "&amp;";
1083      break;
1084    case '<':
1085      Result << "&lt;";
1086      break;
1087    case '>':
1088      Result << "&gt;";
1089      break;
1090    case '"':
1091      Result << "&quot;";
1092      break;
1093    case '\'':
1094      Result << "&apos;";
1095      break;
1096    default:
1097      Result << C;
1098      break;
1099    }
1100  }
1101}
1102
1103void CommentToXMLConverter::convertCommentToHTML(const FullComment *FC,
1104                                                 SmallVectorImpl<char> &HTML,
1105                                                 const ASTContext &Context) {
1106  CommentASTToHTMLConverter Converter(FC, HTML,
1107                                      Context.getCommentCommandTraits());
1108  Converter.visit(FC);
1109}
1110
1111void CommentToXMLConverter::convertHTMLTagNodeToText(
1112    const comments::HTMLTagComment *HTC, SmallVectorImpl<char> &Text,
1113    const ASTContext &Context) {
1114  CommentASTToHTMLConverter Converter(0, Text,
1115                                      Context.getCommentCommandTraits());
1116  Converter.visit(HTC);
1117}
1118
1119void CommentToXMLConverter::convertCommentToXML(const FullComment *FC,
1120                                                SmallVectorImpl<char> &XML,
1121                                                const ASTContext &Context) {
1122  if (!FormatContext) {
1123    FormatContext = new SimpleFormatContext(Context.getLangOpts());
1124  } else if ((FormatInMemoryUniqueId % 1000) == 0) {
1125    // Delete after some number of iterations, so the buffers don't grow
1126    // too large.
1127    delete FormatContext;
1128    FormatContext = new SimpleFormatContext(Context.getLangOpts());
1129  }
1130
1131  CommentASTToXMLConverter Converter(FC, XML, Context.getCommentCommandTraits(),
1132                                     Context.getSourceManager(), *FormatContext,
1133                                     FormatInMemoryUniqueId++);
1134  Converter.visit(FC);
1135}
1136
1137