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