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