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