1259701Sdim//===--- CommentToXML.cpp - Convert comments to XML representation --------===//
2259701Sdim//
3259701Sdim//                     The LLVM Compiler Infrastructure
4259701Sdim//
5259701Sdim// This file is distributed under the University of Illinois Open Source
6259701Sdim// License. See LICENSE.TXT for details.
7259701Sdim//
8259701Sdim//===----------------------------------------------------------------------===//
9259701Sdim
10259701Sdim#include "clang/Index/CommentToXML.h"
11259701Sdim#include "SimpleFormatContext.h"
12259701Sdim#include "clang/AST/Attr.h"
13259701Sdim#include "clang/AST/ASTContext.h"
14259701Sdim#include "clang/AST/Comment.h"
15259701Sdim#include "clang/AST/CommentVisitor.h"
16259701Sdim#include "clang/Format/Format.h"
17259701Sdim#include "clang/Index/USRGeneration.h"
18259701Sdim#include "clang/Lex/Lexer.h"
19259701Sdim#include "llvm/ADT/StringExtras.h"
20259701Sdim#include "llvm/ADT/TinyPtrVector.h"
21259701Sdim#include "llvm/Support/raw_ostream.h"
22259701Sdim
23259701Sdimusing namespace clang;
24259701Sdimusing namespace clang::comments;
25259701Sdimusing namespace clang::index;
26259701Sdim
27259701Sdimnamespace {
28259701Sdim
29259701Sdim/// This comparison will sort parameters with valid index by index, then vararg
30259701Sdim/// parameters, and invalid (unresolved) parameters last.
31259701Sdimclass ParamCommandCommentCompareIndex {
32259701Sdimpublic:
33259701Sdim  bool operator()(const ParamCommandComment *LHS,
34259701Sdim                  const ParamCommandComment *RHS) const {
35259701Sdim    unsigned LHSIndex = UINT_MAX;
36259701Sdim    unsigned RHSIndex = UINT_MAX;
37259701Sdim
38259701Sdim    if (LHS->isParamIndexValid()) {
39259701Sdim      if (LHS->isVarArgParam())
40259701Sdim        LHSIndex = UINT_MAX - 1;
41259701Sdim      else
42259701Sdim        LHSIndex = LHS->getParamIndex();
43259701Sdim    }
44259701Sdim    if (RHS->isParamIndexValid()) {
45259701Sdim      if (RHS->isVarArgParam())
46259701Sdim        RHSIndex = UINT_MAX - 1;
47259701Sdim      else
48259701Sdim        RHSIndex = RHS->getParamIndex();
49259701Sdim    }
50259701Sdim    return LHSIndex < RHSIndex;
51259701Sdim  }
52259701Sdim};
53259701Sdim
54259701Sdim/// This comparison will sort template parameters in the following order:
55259701Sdim/// \li real template parameters (depth = 1) in index order;
56259701Sdim/// \li all other names (depth > 1);
57259701Sdim/// \li unresolved names.
58259701Sdimclass TParamCommandCommentComparePosition {
59259701Sdimpublic:
60259701Sdim  bool operator()(const TParamCommandComment *LHS,
61259701Sdim                  const TParamCommandComment *RHS) const {
62259701Sdim    // Sort unresolved names last.
63259701Sdim    if (!LHS->isPositionValid())
64259701Sdim      return false;
65259701Sdim    if (!RHS->isPositionValid())
66259701Sdim      return true;
67259701Sdim
68259701Sdim    if (LHS->getDepth() > 1)
69259701Sdim      return false;
70259701Sdim    if (RHS->getDepth() > 1)
71259701Sdim      return true;
72259701Sdim
73259701Sdim    // Sort template parameters in index order.
74259701Sdim    if (LHS->getDepth() == 1 && RHS->getDepth() == 1)
75259701Sdim      return LHS->getIndex(0) < RHS->getIndex(0);
76259701Sdim
77259701Sdim    // Leave all other names in source order.
78259701Sdim    return true;
79259701Sdim  }
80259701Sdim};
81259701Sdim
82259701Sdim/// Separate parts of a FullComment.
83259701Sdimstruct FullCommentParts {
84259701Sdim  /// Take a full comment apart and initialize members accordingly.
85259701Sdim  FullCommentParts(const FullComment *C,
86259701Sdim                   const CommandTraits &Traits);
87259701Sdim
88259701Sdim  const BlockContentComment *Brief;
89259701Sdim  const BlockContentComment *Headerfile;
90259701Sdim  const ParagraphComment *FirstParagraph;
91259701Sdim  SmallVector<const BlockCommandComment *, 4> Returns;
92259701Sdim  SmallVector<const ParamCommandComment *, 8> Params;
93259701Sdim  SmallVector<const TParamCommandComment *, 4> TParams;
94259701Sdim  llvm::TinyPtrVector<const BlockCommandComment *> Exceptions;
95259701Sdim  SmallVector<const BlockContentComment *, 8> MiscBlocks;
96259701Sdim};
97259701Sdim
98259701SdimFullCommentParts::FullCommentParts(const FullComment *C,
99259701Sdim                                   const CommandTraits &Traits) :
100259701Sdim    Brief(NULL), Headerfile(NULL), FirstParagraph(NULL) {
101259701Sdim  for (Comment::child_iterator I = C->child_begin(), E = C->child_end();
102259701Sdim       I != E; ++I) {
103259701Sdim    const Comment *Child = *I;
104259701Sdim    if (!Child)
105259701Sdim      continue;
106259701Sdim    switch (Child->getCommentKind()) {
107259701Sdim    case Comment::NoCommentKind:
108259701Sdim      continue;
109259701Sdim
110259701Sdim    case Comment::ParagraphCommentKind: {
111259701Sdim      const ParagraphComment *PC = cast<ParagraphComment>(Child);
112259701Sdim      if (PC->isWhitespace())
113259701Sdim        break;
114259701Sdim      if (!FirstParagraph)
115259701Sdim        FirstParagraph = PC;
116259701Sdim
117259701Sdim      MiscBlocks.push_back(PC);
118259701Sdim      break;
119259701Sdim    }
120259701Sdim
121259701Sdim    case Comment::BlockCommandCommentKind: {
122259701Sdim      const BlockCommandComment *BCC = cast<BlockCommandComment>(Child);
123259701Sdim      const CommandInfo *Info = Traits.getCommandInfo(BCC->getCommandID());
124259701Sdim      if (!Brief && Info->IsBriefCommand) {
125259701Sdim        Brief = BCC;
126259701Sdim        break;
127259701Sdim      }
128259701Sdim      if (!Headerfile && Info->IsHeaderfileCommand) {
129259701Sdim        Headerfile = BCC;
130259701Sdim        break;
131259701Sdim      }
132259701Sdim      if (Info->IsReturnsCommand) {
133259701Sdim        Returns.push_back(BCC);
134259701Sdim        break;
135259701Sdim      }
136259701Sdim      if (Info->IsThrowsCommand) {
137259701Sdim        Exceptions.push_back(BCC);
138259701Sdim        break;
139259701Sdim      }
140259701Sdim      MiscBlocks.push_back(BCC);
141259701Sdim      break;
142259701Sdim    }
143259701Sdim
144259701Sdim    case Comment::ParamCommandCommentKind: {
145259701Sdim      const ParamCommandComment *PCC = cast<ParamCommandComment>(Child);
146259701Sdim      if (!PCC->hasParamName())
147259701Sdim        break;
148259701Sdim
149259701Sdim      if (!PCC->isDirectionExplicit() && !PCC->hasNonWhitespaceParagraph())
150259701Sdim        break;
151259701Sdim
152259701Sdim      Params.push_back(PCC);
153259701Sdim      break;
154259701Sdim    }
155259701Sdim
156259701Sdim    case Comment::TParamCommandCommentKind: {
157259701Sdim      const TParamCommandComment *TPCC = cast<TParamCommandComment>(Child);
158259701Sdim      if (!TPCC->hasParamName())
159259701Sdim        break;
160259701Sdim
161259701Sdim      if (!TPCC->hasNonWhitespaceParagraph())
162259701Sdim        break;
163259701Sdim
164259701Sdim      TParams.push_back(TPCC);
165259701Sdim      break;
166259701Sdim    }
167259701Sdim
168259701Sdim    case Comment::VerbatimBlockCommentKind:
169259701Sdim      MiscBlocks.push_back(cast<BlockCommandComment>(Child));
170259701Sdim      break;
171259701Sdim
172259701Sdim    case Comment::VerbatimLineCommentKind: {
173259701Sdim      const VerbatimLineComment *VLC = cast<VerbatimLineComment>(Child);
174259701Sdim      const CommandInfo *Info = Traits.getCommandInfo(VLC->getCommandID());
175259701Sdim      if (!Info->IsDeclarationCommand)
176259701Sdim        MiscBlocks.push_back(VLC);
177259701Sdim      break;
178259701Sdim    }
179259701Sdim
180259701Sdim    case Comment::TextCommentKind:
181259701Sdim    case Comment::InlineCommandCommentKind:
182259701Sdim    case Comment::HTMLStartTagCommentKind:
183259701Sdim    case Comment::HTMLEndTagCommentKind:
184259701Sdim    case Comment::VerbatimBlockLineCommentKind:
185259701Sdim    case Comment::FullCommentKind:
186259701Sdim      llvm_unreachable("AST node of this kind can't be a child of "
187259701Sdim                       "a FullComment");
188259701Sdim    }
189259701Sdim  }
190259701Sdim
191259701Sdim  // Sort params in order they are declared in the function prototype.
192259701Sdim  // Unresolved parameters are put at the end of the list in the same order
193259701Sdim  // they were seen in the comment.
194259701Sdim  std::stable_sort(Params.begin(), Params.end(),
195259701Sdim                   ParamCommandCommentCompareIndex());
196259701Sdim
197259701Sdim  std::stable_sort(TParams.begin(), TParams.end(),
198259701Sdim                   TParamCommandCommentComparePosition());
199259701Sdim}
200259701Sdim
201259701Sdimvoid printHTMLStartTagComment(const HTMLStartTagComment *C,
202259701Sdim                              llvm::raw_svector_ostream &Result) {
203259701Sdim  Result << "<" << C->getTagName();
204259701Sdim
205259701Sdim  if (C->getNumAttrs() != 0) {
206259701Sdim    for (unsigned i = 0, e = C->getNumAttrs(); i != e; i++) {
207259701Sdim      Result << " ";
208259701Sdim      const HTMLStartTagComment::Attribute &Attr = C->getAttr(i);
209259701Sdim      Result << Attr.Name;
210259701Sdim      if (!Attr.Value.empty())
211259701Sdim        Result << "=\"" << Attr.Value << "\"";
212259701Sdim    }
213259701Sdim  }
214259701Sdim
215259701Sdim  if (!C->isSelfClosing())
216259701Sdim    Result << ">";
217259701Sdim  else
218259701Sdim    Result << "/>";
219259701Sdim}
220259701Sdim
221259701Sdimclass CommentASTToHTMLConverter :
222259701Sdim    public ConstCommentVisitor<CommentASTToHTMLConverter> {
223259701Sdimpublic:
224259701Sdim  /// \param Str accumulator for HTML.
225259701Sdim  CommentASTToHTMLConverter(const FullComment *FC,
226259701Sdim                            SmallVectorImpl<char> &Str,
227259701Sdim                            const CommandTraits &Traits) :
228259701Sdim      FC(FC), Result(Str), Traits(Traits)
229259701Sdim  { }
230259701Sdim
231259701Sdim  // Inline content.
232259701Sdim  void visitTextComment(const TextComment *C);
233259701Sdim  void visitInlineCommandComment(const InlineCommandComment *C);
234259701Sdim  void visitHTMLStartTagComment(const HTMLStartTagComment *C);
235259701Sdim  void visitHTMLEndTagComment(const HTMLEndTagComment *C);
236259701Sdim
237259701Sdim  // Block content.
238259701Sdim  void visitParagraphComment(const ParagraphComment *C);
239259701Sdim  void visitBlockCommandComment(const BlockCommandComment *C);
240259701Sdim  void visitParamCommandComment(const ParamCommandComment *C);
241259701Sdim  void visitTParamCommandComment(const TParamCommandComment *C);
242259701Sdim  void visitVerbatimBlockComment(const VerbatimBlockComment *C);
243259701Sdim  void visitVerbatimBlockLineComment(const VerbatimBlockLineComment *C);
244259701Sdim  void visitVerbatimLineComment(const VerbatimLineComment *C);
245259701Sdim
246259701Sdim  void visitFullComment(const FullComment *C);
247259701Sdim
248259701Sdim  // Helpers.
249259701Sdim
250259701Sdim  /// Convert a paragraph that is not a block by itself (an argument to some
251259701Sdim  /// command).
252259701Sdim  void visitNonStandaloneParagraphComment(const ParagraphComment *C);
253259701Sdim
254259701Sdim  void appendToResultWithHTMLEscaping(StringRef S);
255259701Sdim
256259701Sdimprivate:
257259701Sdim  const FullComment *FC;
258259701Sdim  /// Output stream for HTML.
259259701Sdim  llvm::raw_svector_ostream Result;
260259701Sdim
261259701Sdim  const CommandTraits &Traits;
262259701Sdim};
263259701Sdim} // end unnamed namespace
264259701Sdim
265259701Sdimvoid CommentASTToHTMLConverter::visitTextComment(const TextComment *C) {
266259701Sdim  appendToResultWithHTMLEscaping(C->getText());
267259701Sdim}
268259701Sdim
269259701Sdimvoid CommentASTToHTMLConverter::visitInlineCommandComment(
270259701Sdim                                  const InlineCommandComment *C) {
271259701Sdim  // Nothing to render if no arguments supplied.
272259701Sdim  if (C->getNumArgs() == 0)
273259701Sdim    return;
274259701Sdim
275259701Sdim  // Nothing to render if argument is empty.
276259701Sdim  StringRef Arg0 = C->getArgText(0);
277259701Sdim  if (Arg0.empty())
278259701Sdim    return;
279259701Sdim
280259701Sdim  switch (C->getRenderKind()) {
281259701Sdim  case InlineCommandComment::RenderNormal:
282259701Sdim    for (unsigned i = 0, e = C->getNumArgs(); i != e; ++i) {
283259701Sdim      appendToResultWithHTMLEscaping(C->getArgText(i));
284259701Sdim      Result << " ";
285259701Sdim    }
286259701Sdim    return;
287259701Sdim
288259701Sdim  case InlineCommandComment::RenderBold:
289259701Sdim    assert(C->getNumArgs() == 1);
290259701Sdim    Result << "<b>";
291259701Sdim    appendToResultWithHTMLEscaping(Arg0);
292259701Sdim    Result << "</b>";
293259701Sdim    return;
294259701Sdim  case InlineCommandComment::RenderMonospaced:
295259701Sdim    assert(C->getNumArgs() == 1);
296259701Sdim    Result << "<tt>";
297259701Sdim    appendToResultWithHTMLEscaping(Arg0);
298259701Sdim    Result<< "</tt>";
299259701Sdim    return;
300259701Sdim  case InlineCommandComment::RenderEmphasized:
301259701Sdim    assert(C->getNumArgs() == 1);
302259701Sdim    Result << "<em>";
303259701Sdim    appendToResultWithHTMLEscaping(Arg0);
304259701Sdim    Result << "</em>";
305259701Sdim    return;
306259701Sdim  }
307259701Sdim}
308259701Sdim
309259701Sdimvoid CommentASTToHTMLConverter::visitHTMLStartTagComment(
310259701Sdim                                  const HTMLStartTagComment *C) {
311259701Sdim  printHTMLStartTagComment(C, Result);
312259701Sdim}
313259701Sdim
314259701Sdimvoid CommentASTToHTMLConverter::visitHTMLEndTagComment(
315259701Sdim                                  const HTMLEndTagComment *C) {
316259701Sdim  Result << "</" << C->getTagName() << ">";
317259701Sdim}
318259701Sdim
319259701Sdimvoid CommentASTToHTMLConverter::visitParagraphComment(
320259701Sdim                                  const ParagraphComment *C) {
321259701Sdim  if (C->isWhitespace())
322259701Sdim    return;
323259701Sdim
324259701Sdim  Result << "<p>";
325259701Sdim  for (Comment::child_iterator I = C->child_begin(), E = C->child_end();
326259701Sdim       I != E; ++I) {
327259701Sdim    visit(*I);
328259701Sdim  }
329259701Sdim  Result << "</p>";
330259701Sdim}
331259701Sdim
332259701Sdimvoid CommentASTToHTMLConverter::visitBlockCommandComment(
333259701Sdim                                  const BlockCommandComment *C) {
334259701Sdim  const CommandInfo *Info = Traits.getCommandInfo(C->getCommandID());
335259701Sdim  if (Info->IsBriefCommand) {
336259701Sdim    Result << "<p class=\"para-brief\">";
337259701Sdim    visitNonStandaloneParagraphComment(C->getParagraph());
338259701Sdim    Result << "</p>";
339259701Sdim    return;
340259701Sdim  }
341259701Sdim  if (Info->IsReturnsCommand) {
342259701Sdim    Result << "<p class=\"para-returns\">"
343259701Sdim              "<span class=\"word-returns\">Returns</span> ";
344259701Sdim    visitNonStandaloneParagraphComment(C->getParagraph());
345259701Sdim    Result << "</p>";
346259701Sdim    return;
347259701Sdim  }
348259701Sdim  // We don't know anything about this command.  Just render the paragraph.
349259701Sdim  visit(C->getParagraph());
350259701Sdim}
351259701Sdim
352259701Sdimvoid CommentASTToHTMLConverter::visitParamCommandComment(
353259701Sdim                                  const ParamCommandComment *C) {
354259701Sdim  if (C->isParamIndexValid()) {
355259701Sdim    if (C->isVarArgParam()) {
356259701Sdim      Result << "<dt class=\"param-name-index-vararg\">";
357259701Sdim      appendToResultWithHTMLEscaping(C->getParamNameAsWritten());
358259701Sdim    } else {
359259701Sdim      Result << "<dt class=\"param-name-index-"
360259701Sdim             << C->getParamIndex()
361259701Sdim             << "\">";
362259701Sdim      appendToResultWithHTMLEscaping(C->getParamName(FC));
363259701Sdim    }
364259701Sdim  } else {
365259701Sdim    Result << "<dt class=\"param-name-index-invalid\">";
366259701Sdim    appendToResultWithHTMLEscaping(C->getParamNameAsWritten());
367259701Sdim  }
368259701Sdim  Result << "</dt>";
369259701Sdim
370259701Sdim  if (C->isParamIndexValid()) {
371259701Sdim    if (C->isVarArgParam())
372259701Sdim      Result << "<dd class=\"param-descr-index-vararg\">";
373259701Sdim    else
374259701Sdim      Result << "<dd class=\"param-descr-index-"
375259701Sdim             << C->getParamIndex()
376259701Sdim             << "\">";
377259701Sdim  } else
378259701Sdim    Result << "<dd class=\"param-descr-index-invalid\">";
379259701Sdim
380259701Sdim  visitNonStandaloneParagraphComment(C->getParagraph());
381259701Sdim  Result << "</dd>";
382259701Sdim}
383259701Sdim
384259701Sdimvoid CommentASTToHTMLConverter::visitTParamCommandComment(
385259701Sdim                                  const TParamCommandComment *C) {
386259701Sdim  if (C->isPositionValid()) {
387259701Sdim    if (C->getDepth() == 1)
388259701Sdim      Result << "<dt class=\"tparam-name-index-"
389259701Sdim             << C->getIndex(0)
390259701Sdim             << "\">";
391259701Sdim    else
392259701Sdim      Result << "<dt class=\"tparam-name-index-other\">";
393259701Sdim    appendToResultWithHTMLEscaping(C->getParamName(FC));
394259701Sdim  } else {
395259701Sdim    Result << "<dt class=\"tparam-name-index-invalid\">";
396259701Sdim    appendToResultWithHTMLEscaping(C->getParamNameAsWritten());
397259701Sdim  }
398259701Sdim
399259701Sdim  Result << "</dt>";
400259701Sdim
401259701Sdim  if (C->isPositionValid()) {
402259701Sdim    if (C->getDepth() == 1)
403259701Sdim      Result << "<dd class=\"tparam-descr-index-"
404259701Sdim             << C->getIndex(0)
405259701Sdim             << "\">";
406259701Sdim    else
407259701Sdim      Result << "<dd class=\"tparam-descr-index-other\">";
408259701Sdim  } else
409259701Sdim    Result << "<dd class=\"tparam-descr-index-invalid\">";
410259701Sdim
411259701Sdim  visitNonStandaloneParagraphComment(C->getParagraph());
412259701Sdim  Result << "</dd>";
413259701Sdim}
414259701Sdim
415259701Sdimvoid CommentASTToHTMLConverter::visitVerbatimBlockComment(
416259701Sdim                                  const VerbatimBlockComment *C) {
417259701Sdim  unsigned NumLines = C->getNumLines();
418259701Sdim  if (NumLines == 0)
419259701Sdim    return;
420259701Sdim
421259701Sdim  Result << "<pre>";
422259701Sdim  for (unsigned i = 0; i != NumLines; ++i) {
423259701Sdim    appendToResultWithHTMLEscaping(C->getText(i));
424259701Sdim    if (i + 1 != NumLines)
425259701Sdim      Result << '\n';
426259701Sdim  }
427259701Sdim  Result << "</pre>";
428259701Sdim}
429259701Sdim
430259701Sdimvoid CommentASTToHTMLConverter::visitVerbatimBlockLineComment(
431259701Sdim                                  const VerbatimBlockLineComment *C) {
432259701Sdim  llvm_unreachable("should not see this AST node");
433259701Sdim}
434259701Sdim
435259701Sdimvoid CommentASTToHTMLConverter::visitVerbatimLineComment(
436259701Sdim                                  const VerbatimLineComment *C) {
437259701Sdim  Result << "<pre>";
438259701Sdim  appendToResultWithHTMLEscaping(C->getText());
439259701Sdim  Result << "</pre>";
440259701Sdim}
441259701Sdim
442259701Sdimvoid CommentASTToHTMLConverter::visitFullComment(const FullComment *C) {
443259701Sdim  FullCommentParts Parts(C, Traits);
444259701Sdim
445259701Sdim  bool FirstParagraphIsBrief = false;
446259701Sdim  if (Parts.Headerfile)
447259701Sdim    visit(Parts.Headerfile);
448259701Sdim  if (Parts.Brief)
449259701Sdim    visit(Parts.Brief);
450259701Sdim  else if (Parts.FirstParagraph) {
451259701Sdim    Result << "<p class=\"para-brief\">";
452259701Sdim    visitNonStandaloneParagraphComment(Parts.FirstParagraph);
453259701Sdim    Result << "</p>";
454259701Sdim    FirstParagraphIsBrief = true;
455259701Sdim  }
456259701Sdim
457259701Sdim  for (unsigned i = 0, e = Parts.MiscBlocks.size(); i != e; ++i) {
458259701Sdim    const Comment *C = Parts.MiscBlocks[i];
459259701Sdim    if (FirstParagraphIsBrief && C == Parts.FirstParagraph)
460259701Sdim      continue;
461259701Sdim    visit(C);
462259701Sdim  }
463259701Sdim
464259701Sdim  if (Parts.TParams.size() != 0) {
465259701Sdim    Result << "<dl>";
466259701Sdim    for (unsigned i = 0, e = Parts.TParams.size(); i != e; ++i)
467259701Sdim      visit(Parts.TParams[i]);
468259701Sdim    Result << "</dl>";
469259701Sdim  }
470259701Sdim
471259701Sdim  if (Parts.Params.size() != 0) {
472259701Sdim    Result << "<dl>";
473259701Sdim    for (unsigned i = 0, e = Parts.Params.size(); i != e; ++i)
474259701Sdim      visit(Parts.Params[i]);
475259701Sdim    Result << "</dl>";
476259701Sdim  }
477259701Sdim
478259701Sdim  if (Parts.Returns.size() != 0) {
479259701Sdim    Result << "<div class=\"result-discussion\">";
480259701Sdim    for (unsigned i = 0, e = Parts.Returns.size(); i != e; ++i)
481259701Sdim      visit(Parts.Returns[i]);
482259701Sdim    Result << "</div>";
483259701Sdim  }
484259701Sdim
485259701Sdim  Result.flush();
486259701Sdim}
487259701Sdim
488259701Sdimvoid CommentASTToHTMLConverter::visitNonStandaloneParagraphComment(
489259701Sdim                                  const ParagraphComment *C) {
490259701Sdim  if (!C)
491259701Sdim    return;
492259701Sdim
493259701Sdim  for (Comment::child_iterator I = C->child_begin(), E = C->child_end();
494259701Sdim       I != E; ++I) {
495259701Sdim    visit(*I);
496259701Sdim  }
497259701Sdim}
498259701Sdim
499259701Sdimvoid CommentASTToHTMLConverter::appendToResultWithHTMLEscaping(StringRef S) {
500259701Sdim  for (StringRef::iterator I = S.begin(), E = S.end(); I != E; ++I) {
501259701Sdim    const char C = *I;
502259701Sdim    switch (C) {
503259701Sdim    case '&':
504259701Sdim      Result << "&amp;";
505259701Sdim      break;
506259701Sdim    case '<':
507259701Sdim      Result << "&lt;";
508259701Sdim      break;
509259701Sdim    case '>':
510259701Sdim      Result << "&gt;";
511259701Sdim      break;
512259701Sdim    case '"':
513259701Sdim      Result << "&quot;";
514259701Sdim      break;
515259701Sdim    case '\'':
516259701Sdim      Result << "&#39;";
517259701Sdim      break;
518259701Sdim    case '/':
519259701Sdim      Result << "&#47;";
520259701Sdim      break;
521259701Sdim    default:
522259701Sdim      Result << C;
523259701Sdim      break;
524259701Sdim    }
525259701Sdim  }
526259701Sdim}
527259701Sdim
528259701Sdimnamespace {
529259701Sdimclass CommentASTToXMLConverter :
530259701Sdim    public ConstCommentVisitor<CommentASTToXMLConverter> {
531259701Sdimpublic:
532259701Sdim  /// \param Str accumulator for XML.
533259701Sdim  CommentASTToXMLConverter(const FullComment *FC,
534259701Sdim                           SmallVectorImpl<char> &Str,
535259701Sdim                           const CommandTraits &Traits,
536259701Sdim                           const SourceManager &SM,
537259701Sdim                           SimpleFormatContext &SFC,
538259701Sdim                           unsigned FUID) :
539259701Sdim      FC(FC), Result(Str), Traits(Traits), SM(SM),
540259701Sdim      FormatRewriterContext(SFC),
541259701Sdim      FormatInMemoryUniqueId(FUID) { }
542259701Sdim
543259701Sdim  // Inline content.
544259701Sdim  void visitTextComment(const TextComment *C);
545259701Sdim  void visitInlineCommandComment(const InlineCommandComment *C);
546259701Sdim  void visitHTMLStartTagComment(const HTMLStartTagComment *C);
547259701Sdim  void visitHTMLEndTagComment(const HTMLEndTagComment *C);
548259701Sdim
549259701Sdim  // Block content.
550259701Sdim  void visitParagraphComment(const ParagraphComment *C);
551259701Sdim
552259701Sdim  void appendParagraphCommentWithKind(const ParagraphComment *C,
553259701Sdim                                      StringRef Kind);
554259701Sdim
555259701Sdim  void visitBlockCommandComment(const BlockCommandComment *C);
556259701Sdim  void visitParamCommandComment(const ParamCommandComment *C);
557259701Sdim  void visitTParamCommandComment(const TParamCommandComment *C);
558259701Sdim  void visitVerbatimBlockComment(const VerbatimBlockComment *C);
559259701Sdim  void visitVerbatimBlockLineComment(const VerbatimBlockLineComment *C);
560259701Sdim  void visitVerbatimLineComment(const VerbatimLineComment *C);
561259701Sdim
562259701Sdim  void visitFullComment(const FullComment *C);
563259701Sdim
564259701Sdim  // Helpers.
565259701Sdim  void appendToResultWithXMLEscaping(StringRef S);
566259701Sdim
567259701Sdim  void formatTextOfDeclaration(const DeclInfo *DI,
568259701Sdim                               SmallString<128> &Declaration);
569259701Sdim
570259701Sdimprivate:
571259701Sdim  const FullComment *FC;
572259701Sdim
573259701Sdim  /// Output stream for XML.
574259701Sdim  llvm::raw_svector_ostream Result;
575259701Sdim
576259701Sdim  const CommandTraits &Traits;
577259701Sdim  const SourceManager &SM;
578259701Sdim  SimpleFormatContext &FormatRewriterContext;
579259701Sdim  unsigned FormatInMemoryUniqueId;
580259701Sdim};
581259701Sdim
582259701Sdimvoid getSourceTextOfDeclaration(const DeclInfo *ThisDecl,
583259701Sdim                                SmallVectorImpl<char> &Str) {
584259701Sdim  ASTContext &Context = ThisDecl->CurrentDecl->getASTContext();
585259701Sdim  const LangOptions &LangOpts = Context.getLangOpts();
586259701Sdim  llvm::raw_svector_ostream OS(Str);
587259701Sdim  PrintingPolicy PPolicy(LangOpts);
588259701Sdim  PPolicy.PolishForDeclaration = true;
589259701Sdim  PPolicy.TerseOutput = true;
590259701Sdim  ThisDecl->CurrentDecl->print(OS, PPolicy,
591259701Sdim                               /*Indentation*/0, /*PrintInstantiation*/false);
592259701Sdim}
593259701Sdim
594259701Sdimvoid CommentASTToXMLConverter::formatTextOfDeclaration(
595259701Sdim    const DeclInfo *DI, SmallString<128> &Declaration) {
596259701Sdim  // FIXME. formatting API expects null terminated input string.
597259701Sdim  // There might be more efficient way of doing this.
598259701Sdim  std::string StringDecl = Declaration.str();
599259701Sdim
600259701Sdim  // Formatter specific code.
601259701Sdim  // Form a unique in memory buffer name.
602259701Sdim  SmallString<128> filename;
603259701Sdim  filename += "xmldecl";
604259701Sdim  filename += llvm::utostr(FormatInMemoryUniqueId);
605259701Sdim  filename += ".xd";
606259701Sdim  FileID ID = FormatRewriterContext.createInMemoryFile(filename, StringDecl);
607259701Sdim  SourceLocation Start = FormatRewriterContext.Sources.getLocForStartOfFile(ID)
608259701Sdim      .getLocWithOffset(0);
609259701Sdim  unsigned Length = Declaration.size();
610259701Sdim
611259701Sdim  std::vector<CharSourceRange> Ranges(
612259701Sdim      1, CharSourceRange::getCharRange(Start, Start.getLocWithOffset(Length)));
613259701Sdim  ASTContext &Context = DI->CurrentDecl->getASTContext();
614259701Sdim  const LangOptions &LangOpts = Context.getLangOpts();
615259701Sdim  Lexer Lex(ID, FormatRewriterContext.Sources.getBuffer(ID),
616259701Sdim            FormatRewriterContext.Sources, LangOpts);
617259701Sdim  tooling::Replacements Replace = reformat(
618259701Sdim      format::getLLVMStyle(), Lex, FormatRewriterContext.Sources, Ranges);
619259701Sdim  applyAllReplacements(Replace, FormatRewriterContext.Rewrite);
620259701Sdim  Declaration = FormatRewriterContext.getRewrittenText(ID);
621259701Sdim}
622259701Sdim
623259701Sdim} // end unnamed namespace
624259701Sdim
625259701Sdimvoid CommentASTToXMLConverter::visitTextComment(const TextComment *C) {
626259701Sdim  appendToResultWithXMLEscaping(C->getText());
627259701Sdim}
628259701Sdim
629259701Sdimvoid CommentASTToXMLConverter::visitInlineCommandComment(
630259701Sdim    const InlineCommandComment *C) {
631259701Sdim  // Nothing to render if no arguments supplied.
632259701Sdim  if (C->getNumArgs() == 0)
633259701Sdim    return;
634259701Sdim
635259701Sdim  // Nothing to render if argument is empty.
636259701Sdim  StringRef Arg0 = C->getArgText(0);
637259701Sdim  if (Arg0.empty())
638259701Sdim    return;
639259701Sdim
640259701Sdim  switch (C->getRenderKind()) {
641259701Sdim  case InlineCommandComment::RenderNormal:
642259701Sdim    for (unsigned i = 0, e = C->getNumArgs(); i != e; ++i) {
643259701Sdim      appendToResultWithXMLEscaping(C->getArgText(i));
644259701Sdim      Result << " ";
645259701Sdim    }
646259701Sdim    return;
647259701Sdim  case InlineCommandComment::RenderBold:
648259701Sdim    assert(C->getNumArgs() == 1);
649259701Sdim    Result << "<bold>";
650259701Sdim    appendToResultWithXMLEscaping(Arg0);
651259701Sdim    Result << "</bold>";
652259701Sdim    return;
653259701Sdim  case InlineCommandComment::RenderMonospaced:
654259701Sdim    assert(C->getNumArgs() == 1);
655259701Sdim    Result << "<monospaced>";
656259701Sdim    appendToResultWithXMLEscaping(Arg0);
657259701Sdim    Result << "</monospaced>";
658259701Sdim    return;
659259701Sdim  case InlineCommandComment::RenderEmphasized:
660259701Sdim    assert(C->getNumArgs() == 1);
661259701Sdim    Result << "<emphasized>";
662259701Sdim    appendToResultWithXMLEscaping(Arg0);
663259701Sdim    Result << "</emphasized>";
664259701Sdim    return;
665259701Sdim  }
666259701Sdim}
667259701Sdim
668259701Sdimvoid CommentASTToXMLConverter::visitHTMLStartTagComment(
669259701Sdim    const HTMLStartTagComment *C) {
670259701Sdim  Result << "<rawHTML><![CDATA[";
671259701Sdim  printHTMLStartTagComment(C, Result);
672259701Sdim  Result << "]]></rawHTML>";
673259701Sdim}
674259701Sdim
675259701Sdimvoid
676259701SdimCommentASTToXMLConverter::visitHTMLEndTagComment(const HTMLEndTagComment *C) {
677259701Sdim  Result << "<rawHTML>&lt;/" << C->getTagName() << "&gt;</rawHTML>";
678259701Sdim}
679259701Sdim
680259701Sdimvoid
681259701SdimCommentASTToXMLConverter::visitParagraphComment(const ParagraphComment *C) {
682259701Sdim  appendParagraphCommentWithKind(C, StringRef());
683259701Sdim}
684259701Sdim
685259701Sdimvoid CommentASTToXMLConverter::appendParagraphCommentWithKind(
686259701Sdim                                  const ParagraphComment *C,
687259701Sdim                                  StringRef ParagraphKind) {
688259701Sdim  if (C->isWhitespace())
689259701Sdim    return;
690259701Sdim
691259701Sdim  if (ParagraphKind.empty())
692259701Sdim    Result << "<Para>";
693259701Sdim  else
694259701Sdim    Result << "<Para kind=\"" << ParagraphKind << "\">";
695259701Sdim
696259701Sdim  for (Comment::child_iterator I = C->child_begin(), E = C->child_end();
697259701Sdim       I != E; ++I) {
698259701Sdim    visit(*I);
699259701Sdim  }
700259701Sdim  Result << "</Para>";
701259701Sdim}
702259701Sdim
703259701Sdimvoid CommentASTToXMLConverter::visitBlockCommandComment(
704259701Sdim    const BlockCommandComment *C) {
705259701Sdim  StringRef ParagraphKind;
706259701Sdim
707259701Sdim  switch (C->getCommandID()) {
708259701Sdim  case CommandTraits::KCI_attention:
709259701Sdim  case CommandTraits::KCI_author:
710259701Sdim  case CommandTraits::KCI_authors:
711259701Sdim  case CommandTraits::KCI_bug:
712259701Sdim  case CommandTraits::KCI_copyright:
713259701Sdim  case CommandTraits::KCI_date:
714259701Sdim  case CommandTraits::KCI_invariant:
715259701Sdim  case CommandTraits::KCI_note:
716259701Sdim  case CommandTraits::KCI_post:
717259701Sdim  case CommandTraits::KCI_pre:
718259701Sdim  case CommandTraits::KCI_remark:
719259701Sdim  case CommandTraits::KCI_remarks:
720259701Sdim  case CommandTraits::KCI_sa:
721259701Sdim  case CommandTraits::KCI_see:
722259701Sdim  case CommandTraits::KCI_since:
723259701Sdim  case CommandTraits::KCI_todo:
724259701Sdim  case CommandTraits::KCI_version:
725259701Sdim  case CommandTraits::KCI_warning:
726259701Sdim    ParagraphKind = C->getCommandName(Traits);
727259701Sdim  default:
728259701Sdim    break;
729259701Sdim  }
730259701Sdim
731259701Sdim  appendParagraphCommentWithKind(C->getParagraph(), ParagraphKind);
732259701Sdim}
733259701Sdim
734259701Sdimvoid CommentASTToXMLConverter::visitParamCommandComment(
735259701Sdim    const ParamCommandComment *C) {
736259701Sdim  Result << "<Parameter><Name>";
737259701Sdim  appendToResultWithXMLEscaping(C->isParamIndexValid()
738259701Sdim                                    ? C->getParamName(FC)
739259701Sdim                                    : C->getParamNameAsWritten());
740259701Sdim  Result << "</Name>";
741259701Sdim
742259701Sdim  if (C->isParamIndexValid()) {
743259701Sdim    if (C->isVarArgParam())
744259701Sdim      Result << "<IsVarArg />";
745259701Sdim    else
746259701Sdim      Result << "<Index>" << C->getParamIndex() << "</Index>";
747259701Sdim  }
748259701Sdim
749259701Sdim  Result << "<Direction isExplicit=\"" << C->isDirectionExplicit() << "\">";
750259701Sdim  switch (C->getDirection()) {
751259701Sdim  case ParamCommandComment::In:
752259701Sdim    Result << "in";
753259701Sdim    break;
754259701Sdim  case ParamCommandComment::Out:
755259701Sdim    Result << "out";
756259701Sdim    break;
757259701Sdim  case ParamCommandComment::InOut:
758259701Sdim    Result << "in,out";
759259701Sdim    break;
760259701Sdim  }
761259701Sdim  Result << "</Direction><Discussion>";
762259701Sdim  visit(C->getParagraph());
763259701Sdim  Result << "</Discussion></Parameter>";
764259701Sdim}
765259701Sdim
766259701Sdimvoid CommentASTToXMLConverter::visitTParamCommandComment(
767259701Sdim                                  const TParamCommandComment *C) {
768259701Sdim  Result << "<Parameter><Name>";
769259701Sdim  appendToResultWithXMLEscaping(C->isPositionValid() ? C->getParamName(FC)
770259701Sdim                                : C->getParamNameAsWritten());
771259701Sdim  Result << "</Name>";
772259701Sdim
773259701Sdim  if (C->isPositionValid() && C->getDepth() == 1) {
774259701Sdim    Result << "<Index>" << C->getIndex(0) << "</Index>";
775259701Sdim  }
776259701Sdim
777259701Sdim  Result << "<Discussion>";
778259701Sdim  visit(C->getParagraph());
779259701Sdim  Result << "</Discussion></Parameter>";
780259701Sdim}
781259701Sdim
782259701Sdimvoid CommentASTToXMLConverter::visitVerbatimBlockComment(
783259701Sdim                                  const VerbatimBlockComment *C) {
784259701Sdim  unsigned NumLines = C->getNumLines();
785259701Sdim  if (NumLines == 0)
786259701Sdim    return;
787259701Sdim
788259701Sdim  switch (C->getCommandID()) {
789259701Sdim  case CommandTraits::KCI_code:
790259701Sdim    Result << "<Verbatim xml:space=\"preserve\" kind=\"code\">";
791259701Sdim    break;
792259701Sdim  default:
793259701Sdim    Result << "<Verbatim xml:space=\"preserve\" kind=\"verbatim\">";
794259701Sdim    break;
795259701Sdim  }
796259701Sdim  for (unsigned i = 0; i != NumLines; ++i) {
797259701Sdim    appendToResultWithXMLEscaping(C->getText(i));
798259701Sdim    if (i + 1 != NumLines)
799259701Sdim      Result << '\n';
800259701Sdim  }
801259701Sdim  Result << "</Verbatim>";
802259701Sdim}
803259701Sdim
804259701Sdimvoid CommentASTToXMLConverter::visitVerbatimBlockLineComment(
805259701Sdim                                  const VerbatimBlockLineComment *C) {
806259701Sdim  llvm_unreachable("should not see this AST node");
807259701Sdim}
808259701Sdim
809259701Sdimvoid CommentASTToXMLConverter::visitVerbatimLineComment(
810259701Sdim                                  const VerbatimLineComment *C) {
811259701Sdim  Result << "<Verbatim xml:space=\"preserve\" kind=\"verbatim\">";
812259701Sdim  appendToResultWithXMLEscaping(C->getText());
813259701Sdim  Result << "</Verbatim>";
814259701Sdim}
815259701Sdim
816259701Sdimvoid CommentASTToXMLConverter::visitFullComment(const FullComment *C) {
817259701Sdim  FullCommentParts Parts(C, Traits);
818259701Sdim
819259701Sdim  const DeclInfo *DI = C->getDeclInfo();
820259701Sdim  StringRef RootEndTag;
821259701Sdim  if (DI) {
822259701Sdim    switch (DI->getKind()) {
823259701Sdim    case DeclInfo::OtherKind:
824259701Sdim      RootEndTag = "</Other>";
825259701Sdim      Result << "<Other";
826259701Sdim      break;
827259701Sdim    case DeclInfo::FunctionKind:
828259701Sdim      RootEndTag = "</Function>";
829259701Sdim      Result << "<Function";
830259701Sdim      switch (DI->TemplateKind) {
831259701Sdim      case DeclInfo::NotTemplate:
832259701Sdim        break;
833259701Sdim      case DeclInfo::Template:
834259701Sdim        Result << " templateKind=\"template\"";
835259701Sdim        break;
836259701Sdim      case DeclInfo::TemplateSpecialization:
837259701Sdim        Result << " templateKind=\"specialization\"";
838259701Sdim        break;
839259701Sdim      case DeclInfo::TemplatePartialSpecialization:
840259701Sdim        llvm_unreachable("partial specializations of functions "
841259701Sdim                         "are not allowed in C++");
842259701Sdim      }
843259701Sdim      if (DI->IsInstanceMethod)
844259701Sdim        Result << " isInstanceMethod=\"1\"";
845259701Sdim      if (DI->IsClassMethod)
846259701Sdim        Result << " isClassMethod=\"1\"";
847259701Sdim      break;
848259701Sdim    case DeclInfo::ClassKind:
849259701Sdim      RootEndTag = "</Class>";
850259701Sdim      Result << "<Class";
851259701Sdim      switch (DI->TemplateKind) {
852259701Sdim      case DeclInfo::NotTemplate:
853259701Sdim        break;
854259701Sdim      case DeclInfo::Template:
855259701Sdim        Result << " templateKind=\"template\"";
856259701Sdim        break;
857259701Sdim      case DeclInfo::TemplateSpecialization:
858259701Sdim        Result << " templateKind=\"specialization\"";
859259701Sdim        break;
860259701Sdim      case DeclInfo::TemplatePartialSpecialization:
861259701Sdim        Result << " templateKind=\"partialSpecialization\"";
862259701Sdim        break;
863259701Sdim      }
864259701Sdim      break;
865259701Sdim    case DeclInfo::VariableKind:
866259701Sdim      RootEndTag = "</Variable>";
867259701Sdim      Result << "<Variable";
868259701Sdim      break;
869259701Sdim    case DeclInfo::NamespaceKind:
870259701Sdim      RootEndTag = "</Namespace>";
871259701Sdim      Result << "<Namespace";
872259701Sdim      break;
873259701Sdim    case DeclInfo::TypedefKind:
874259701Sdim      RootEndTag = "</Typedef>";
875259701Sdim      Result << "<Typedef";
876259701Sdim      break;
877259701Sdim    case DeclInfo::EnumKind:
878259701Sdim      RootEndTag = "</Enum>";
879259701Sdim      Result << "<Enum";
880259701Sdim      break;
881259701Sdim    }
882259701Sdim
883259701Sdim    {
884259701Sdim      // Print line and column number.
885259701Sdim      SourceLocation Loc = DI->CurrentDecl->getLocation();
886259701Sdim      std::pair<FileID, unsigned> LocInfo = SM.getDecomposedLoc(Loc);
887259701Sdim      FileID FID = LocInfo.first;
888259701Sdim      unsigned FileOffset = LocInfo.second;
889259701Sdim
890259701Sdim      if (!FID.isInvalid()) {
891259701Sdim        if (const FileEntry *FE = SM.getFileEntryForID(FID)) {
892259701Sdim          Result << " file=\"";
893259701Sdim          appendToResultWithXMLEscaping(FE->getName());
894259701Sdim          Result << "\"";
895259701Sdim        }
896259701Sdim        Result << " line=\"" << SM.getLineNumber(FID, FileOffset)
897259701Sdim               << "\" column=\"" << SM.getColumnNumber(FID, FileOffset)
898259701Sdim               << "\"";
899259701Sdim      }
900259701Sdim    }
901259701Sdim
902259701Sdim    // Finish the root tag.
903259701Sdim    Result << ">";
904259701Sdim
905259701Sdim    bool FoundName = false;
906259701Sdim    if (const NamedDecl *ND = dyn_cast<NamedDecl>(DI->CommentDecl)) {
907259701Sdim      if (DeclarationName DeclName = ND->getDeclName()) {
908259701Sdim        Result << "<Name>";
909259701Sdim        std::string Name = DeclName.getAsString();
910259701Sdim        appendToResultWithXMLEscaping(Name);
911259701Sdim        FoundName = true;
912259701Sdim        Result << "</Name>";
913259701Sdim      }
914259701Sdim    }
915259701Sdim    if (!FoundName)
916259701Sdim      Result << "<Name>&lt;anonymous&gt;</Name>";
917259701Sdim
918259701Sdim    {
919259701Sdim      // Print USR.
920259701Sdim      SmallString<128> USR;
921259701Sdim      generateUSRForDecl(DI->CommentDecl, USR);
922259701Sdim      if (!USR.empty()) {
923259701Sdim        Result << "<USR>";
924259701Sdim        appendToResultWithXMLEscaping(USR);
925259701Sdim        Result << "</USR>";
926259701Sdim      }
927259701Sdim    }
928259701Sdim  } else {
929259701Sdim    // No DeclInfo -- just emit some root tag and name tag.
930259701Sdim    RootEndTag = "</Other>";
931259701Sdim    Result << "<Other><Name>unknown</Name>";
932259701Sdim  }
933259701Sdim
934259701Sdim  if (Parts.Headerfile) {
935259701Sdim    Result << "<Headerfile>";
936259701Sdim    visit(Parts.Headerfile);
937259701Sdim    Result << "</Headerfile>";
938259701Sdim  }
939259701Sdim
940259701Sdim  {
941259701Sdim    // Pretty-print the declaration.
942259701Sdim    Result << "<Declaration>";
943259701Sdim    SmallString<128> Declaration;
944259701Sdim    getSourceTextOfDeclaration(DI, Declaration);
945259701Sdim    formatTextOfDeclaration(DI, Declaration);
946259701Sdim    appendToResultWithXMLEscaping(Declaration);
947259701Sdim    Result << "</Declaration>";
948259701Sdim  }
949259701Sdim
950259701Sdim  bool FirstParagraphIsBrief = false;
951259701Sdim  if (Parts.Brief) {
952259701Sdim    Result << "<Abstract>";
953259701Sdim    visit(Parts.Brief);
954259701Sdim    Result << "</Abstract>";
955259701Sdim  } else if (Parts.FirstParagraph) {
956259701Sdim    Result << "<Abstract>";
957259701Sdim    visit(Parts.FirstParagraph);
958259701Sdim    Result << "</Abstract>";
959259701Sdim    FirstParagraphIsBrief = true;
960259701Sdim  }
961259701Sdim
962259701Sdim  if (Parts.TParams.size() != 0) {
963259701Sdim    Result << "<TemplateParameters>";
964259701Sdim    for (unsigned i = 0, e = Parts.TParams.size(); i != e; ++i)
965259701Sdim      visit(Parts.TParams[i]);
966259701Sdim    Result << "</TemplateParameters>";
967259701Sdim  }
968259701Sdim
969259701Sdim  if (Parts.Params.size() != 0) {
970259701Sdim    Result << "<Parameters>";
971259701Sdim    for (unsigned i = 0, e = Parts.Params.size(); i != e; ++i)
972259701Sdim      visit(Parts.Params[i]);
973259701Sdim    Result << "</Parameters>";
974259701Sdim  }
975259701Sdim
976259701Sdim  if (Parts.Exceptions.size() != 0) {
977259701Sdim    Result << "<Exceptions>";
978259701Sdim    for (unsigned i = 0, e = Parts.Exceptions.size(); i != e; ++i)
979259701Sdim      visit(Parts.Exceptions[i]);
980259701Sdim    Result << "</Exceptions>";
981259701Sdim  }
982259701Sdim
983259701Sdim  if (Parts.Returns.size() != 0) {
984259701Sdim    Result << "<ResultDiscussion>";
985259701Sdim    for (unsigned i = 0, e = Parts.Returns.size(); i != e; ++i)
986259701Sdim      visit(Parts.Returns[i]);
987259701Sdim    Result << "</ResultDiscussion>";
988259701Sdim  }
989259701Sdim
990259701Sdim  if (DI->CommentDecl->hasAttrs()) {
991259701Sdim    const AttrVec &Attrs = DI->CommentDecl->getAttrs();
992259701Sdim    for (unsigned i = 0, e = Attrs.size(); i != e; i++) {
993259701Sdim      const AvailabilityAttr *AA = dyn_cast<AvailabilityAttr>(Attrs[i]);
994259701Sdim      if (!AA) {
995259701Sdim        if (const DeprecatedAttr *DA = dyn_cast<DeprecatedAttr>(Attrs[i])) {
996259701Sdim          if (DA->getMessage().empty())
997259701Sdim            Result << "<Deprecated/>";
998259701Sdim          else {
999259701Sdim            Result << "<Deprecated>";
1000259701Sdim            appendToResultWithXMLEscaping(DA->getMessage());
1001259701Sdim            Result << "</Deprecated>";
1002259701Sdim          }
1003259701Sdim        }
1004259701Sdim        else if (const UnavailableAttr *UA = dyn_cast<UnavailableAttr>(Attrs[i])) {
1005259701Sdim          if (UA->getMessage().empty())
1006259701Sdim            Result << "<Unavailable/>";
1007259701Sdim          else {
1008259701Sdim            Result << "<Unavailable>";
1009259701Sdim            appendToResultWithXMLEscaping(UA->getMessage());
1010259701Sdim            Result << "</Unavailable>";
1011259701Sdim          }
1012259701Sdim        }
1013259701Sdim        continue;
1014259701Sdim      }
1015259701Sdim
1016259701Sdim      // 'availability' attribute.
1017259701Sdim      Result << "<Availability";
1018259701Sdim      StringRef Distribution;
1019259701Sdim      if (AA->getPlatform()) {
1020259701Sdim        Distribution = AvailabilityAttr::getPrettyPlatformName(
1021259701Sdim                                        AA->getPlatform()->getName());
1022259701Sdim        if (Distribution.empty())
1023259701Sdim          Distribution = AA->getPlatform()->getName();
1024259701Sdim      }
1025259701Sdim      Result << " distribution=\"" << Distribution << "\">";
1026259701Sdim      VersionTuple IntroducedInVersion = AA->getIntroduced();
1027259701Sdim      if (!IntroducedInVersion.empty()) {
1028259701Sdim        Result << "<IntroducedInVersion>"
1029259701Sdim               << IntroducedInVersion.getAsString()
1030259701Sdim               << "</IntroducedInVersion>";
1031259701Sdim      }
1032259701Sdim      VersionTuple DeprecatedInVersion = AA->getDeprecated();
1033259701Sdim      if (!DeprecatedInVersion.empty()) {
1034259701Sdim        Result << "<DeprecatedInVersion>"
1035259701Sdim               << DeprecatedInVersion.getAsString()
1036259701Sdim               << "</DeprecatedInVersion>";
1037259701Sdim      }
1038259701Sdim      VersionTuple RemovedAfterVersion = AA->getObsoleted();
1039259701Sdim      if (!RemovedAfterVersion.empty()) {
1040259701Sdim        Result << "<RemovedAfterVersion>"
1041259701Sdim               << RemovedAfterVersion.getAsString()
1042259701Sdim               << "</RemovedAfterVersion>";
1043259701Sdim      }
1044259701Sdim      StringRef DeprecationSummary = AA->getMessage();
1045259701Sdim      if (!DeprecationSummary.empty()) {
1046259701Sdim        Result << "<DeprecationSummary>";
1047259701Sdim        appendToResultWithXMLEscaping(DeprecationSummary);
1048259701Sdim        Result << "</DeprecationSummary>";
1049259701Sdim      }
1050259701Sdim      if (AA->getUnavailable())
1051259701Sdim        Result << "<Unavailable/>";
1052259701Sdim      Result << "</Availability>";
1053259701Sdim    }
1054259701Sdim  }
1055259701Sdim
1056259701Sdim  {
1057259701Sdim    bool StartTagEmitted = false;
1058259701Sdim    for (unsigned i = 0, e = Parts.MiscBlocks.size(); i != e; ++i) {
1059259701Sdim      const Comment *C = Parts.MiscBlocks[i];
1060259701Sdim      if (FirstParagraphIsBrief && C == Parts.FirstParagraph)
1061259701Sdim        continue;
1062259701Sdim      if (!StartTagEmitted) {
1063259701Sdim        Result << "<Discussion>";
1064259701Sdim        StartTagEmitted = true;
1065259701Sdim      }
1066259701Sdim      visit(C);
1067259701Sdim    }
1068259701Sdim    if (StartTagEmitted)
1069259701Sdim      Result << "</Discussion>";
1070259701Sdim  }
1071259701Sdim
1072259701Sdim  Result << RootEndTag;
1073259701Sdim
1074259701Sdim  Result.flush();
1075259701Sdim}
1076259701Sdim
1077259701Sdimvoid CommentASTToXMLConverter::appendToResultWithXMLEscaping(StringRef S) {
1078259701Sdim  for (StringRef::iterator I = S.begin(), E = S.end(); I != E; ++I) {
1079259701Sdim    const char C = *I;
1080259701Sdim    switch (C) {
1081259701Sdim    case '&':
1082259701Sdim      Result << "&amp;";
1083259701Sdim      break;
1084259701Sdim    case '<':
1085259701Sdim      Result << "&lt;";
1086259701Sdim      break;
1087259701Sdim    case '>':
1088259701Sdim      Result << "&gt;";
1089259701Sdim      break;
1090259701Sdim    case '"':
1091259701Sdim      Result << "&quot;";
1092259701Sdim      break;
1093259701Sdim    case '\'':
1094259701Sdim      Result << "&apos;";
1095259701Sdim      break;
1096259701Sdim    default:
1097259701Sdim      Result << C;
1098259701Sdim      break;
1099259701Sdim    }
1100259701Sdim  }
1101259701Sdim}
1102259701Sdim
1103259701Sdimvoid CommentToXMLConverter::convertCommentToHTML(const FullComment *FC,
1104259701Sdim                                                 SmallVectorImpl<char> &HTML,
1105259701Sdim                                                 const ASTContext &Context) {
1106259701Sdim  CommentASTToHTMLConverter Converter(FC, HTML,
1107259701Sdim                                      Context.getCommentCommandTraits());
1108259701Sdim  Converter.visit(FC);
1109259701Sdim}
1110259701Sdim
1111259701Sdimvoid CommentToXMLConverter::convertHTMLTagNodeToText(
1112259701Sdim    const comments::HTMLTagComment *HTC, SmallVectorImpl<char> &Text,
1113259701Sdim    const ASTContext &Context) {
1114259701Sdim  CommentASTToHTMLConverter Converter(0, Text,
1115259701Sdim                                      Context.getCommentCommandTraits());
1116259701Sdim  Converter.visit(HTC);
1117259701Sdim}
1118259701Sdim
1119259701Sdimvoid CommentToXMLConverter::convertCommentToXML(const FullComment *FC,
1120259701Sdim                                                SmallVectorImpl<char> &XML,
1121259701Sdim                                                const ASTContext &Context) {
1122259701Sdim  if (!FormatContext) {
1123259701Sdim    FormatContext = new SimpleFormatContext(Context.getLangOpts());
1124259701Sdim  } else if ((FormatInMemoryUniqueId % 1000) == 0) {
1125259701Sdim    // Delete after some number of iterations, so the buffers don't grow
1126259701Sdim    // too large.
1127259701Sdim    delete FormatContext;
1128259701Sdim    FormatContext = new SimpleFormatContext(Context.getLangOpts());
1129259701Sdim  }
1130259701Sdim
1131259701Sdim  CommentASTToXMLConverter Converter(FC, XML, Context.getCommentCommandTraits(),
1132259701Sdim                                     Context.getSourceManager(), *FormatContext,
1133259701Sdim                                     FormatInMemoryUniqueId++);
1134259701Sdim  Converter.visit(FC);
1135259701Sdim}
1136259701Sdim
1137