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