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