]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - contrib/llvm/tools/clang/lib/Index/CommentToXML.cpp
Merge llvm, clang, lld and lldb trunk r300890, and update build glue.
[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 "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   format::FormatStyle Style = format::getLLVMStyle();
596   Style.FixNamespaceComments = false;
597   tooling::Replacements Replaces =
598       reformat(Style, StringDecl, tooling::Range(Offset, Length), "xmldecl.xd");
599   auto FormattedStringDecl = applyAllReplacements(StringDecl, Replaces);
600   if (static_cast<bool>(FormattedStringDecl)) {
601     Declaration = *FormattedStringDecl;
602   }
603 }
604
605 } // end unnamed namespace
606
607 void CommentASTToXMLConverter::visitTextComment(const TextComment *C) {
608   appendToResultWithXMLEscaping(C->getText());
609 }
610
611 void CommentASTToXMLConverter::visitInlineCommandComment(
612     const InlineCommandComment *C) {
613   // Nothing to render if no arguments supplied.
614   if (C->getNumArgs() == 0)
615     return;
616
617   // Nothing to render if argument is empty.
618   StringRef Arg0 = C->getArgText(0);
619   if (Arg0.empty())
620     return;
621
622   switch (C->getRenderKind()) {
623   case InlineCommandComment::RenderNormal:
624     for (unsigned i = 0, e = C->getNumArgs(); i != e; ++i) {
625       appendToResultWithXMLEscaping(C->getArgText(i));
626       Result << " ";
627     }
628     return;
629   case InlineCommandComment::RenderBold:
630     assert(C->getNumArgs() == 1);
631     Result << "<bold>";
632     appendToResultWithXMLEscaping(Arg0);
633     Result << "</bold>";
634     return;
635   case InlineCommandComment::RenderMonospaced:
636     assert(C->getNumArgs() == 1);
637     Result << "<monospaced>";
638     appendToResultWithXMLEscaping(Arg0);
639     Result << "</monospaced>";
640     return;
641   case InlineCommandComment::RenderEmphasized:
642     assert(C->getNumArgs() == 1);
643     Result << "<emphasized>";
644     appendToResultWithXMLEscaping(Arg0);
645     Result << "</emphasized>";
646     return;
647   }
648 }
649
650 void CommentASTToXMLConverter::visitHTMLStartTagComment(
651     const HTMLStartTagComment *C) {
652   Result << "<rawHTML";
653   if (C->isMalformed())
654     Result << " isMalformed=\"1\"";
655   Result << ">";
656   {
657     SmallString<32> Tag;
658     {
659       llvm::raw_svector_ostream TagOS(Tag);
660       printHTMLStartTagComment(C, TagOS);
661     }
662     appendToResultWithCDATAEscaping(Tag);
663   }
664   Result << "</rawHTML>";
665 }
666
667 void
668 CommentASTToXMLConverter::visitHTMLEndTagComment(const HTMLEndTagComment *C) {
669   Result << "<rawHTML";
670   if (C->isMalformed())
671     Result << " isMalformed=\"1\"";
672   Result << ">&lt;/" << C->getTagName() << "&gt;</rawHTML>";
673 }
674
675 void
676 CommentASTToXMLConverter::visitParagraphComment(const ParagraphComment *C) {
677   appendParagraphCommentWithKind(C, StringRef());
678 }
679
680 void CommentASTToXMLConverter::appendParagraphCommentWithKind(
681                                   const ParagraphComment *C,
682                                   StringRef ParagraphKind) {
683   if (C->isWhitespace())
684     return;
685
686   if (ParagraphKind.empty())
687     Result << "<Para>";
688   else
689     Result << "<Para kind=\"" << ParagraphKind << "\">";
690
691   for (Comment::child_iterator I = C->child_begin(), E = C->child_end();
692        I != E; ++I) {
693     visit(*I);
694   }
695   Result << "</Para>";
696 }
697
698 void CommentASTToXMLConverter::visitBlockCommandComment(
699     const BlockCommandComment *C) {
700   StringRef ParagraphKind;
701
702   switch (C->getCommandID()) {
703   case CommandTraits::KCI_attention:
704   case CommandTraits::KCI_author:
705   case CommandTraits::KCI_authors:
706   case CommandTraits::KCI_bug:
707   case CommandTraits::KCI_copyright:
708   case CommandTraits::KCI_date:
709   case CommandTraits::KCI_invariant:
710   case CommandTraits::KCI_note:
711   case CommandTraits::KCI_post:
712   case CommandTraits::KCI_pre:
713   case CommandTraits::KCI_remark:
714   case CommandTraits::KCI_remarks:
715   case CommandTraits::KCI_sa:
716   case CommandTraits::KCI_see:
717   case CommandTraits::KCI_since:
718   case CommandTraits::KCI_todo:
719   case CommandTraits::KCI_version:
720   case CommandTraits::KCI_warning:
721     ParagraphKind = C->getCommandName(Traits);
722   default:
723     break;
724   }
725
726   appendParagraphCommentWithKind(C->getParagraph(), ParagraphKind);
727 }
728
729 void CommentASTToXMLConverter::visitParamCommandComment(
730     const ParamCommandComment *C) {
731   Result << "<Parameter><Name>";
732   appendToResultWithXMLEscaping(C->isParamIndexValid()
733                                     ? C->getParamName(FC)
734                                     : C->getParamNameAsWritten());
735   Result << "</Name>";
736
737   if (C->isParamIndexValid()) {
738     if (C->isVarArgParam())
739       Result << "<IsVarArg />";
740     else
741       Result << "<Index>" << C->getParamIndex() << "</Index>";
742   }
743
744   Result << "<Direction isExplicit=\"" << C->isDirectionExplicit() << "\">";
745   switch (C->getDirection()) {
746   case ParamCommandComment::In:
747     Result << "in";
748     break;
749   case ParamCommandComment::Out:
750     Result << "out";
751     break;
752   case ParamCommandComment::InOut:
753     Result << "in,out";
754     break;
755   }
756   Result << "</Direction><Discussion>";
757   visit(C->getParagraph());
758   Result << "</Discussion></Parameter>";
759 }
760
761 void CommentASTToXMLConverter::visitTParamCommandComment(
762                                   const TParamCommandComment *C) {
763   Result << "<Parameter><Name>";
764   appendToResultWithXMLEscaping(C->isPositionValid() ? C->getParamName(FC)
765                                 : C->getParamNameAsWritten());
766   Result << "</Name>";
767
768   if (C->isPositionValid() && C->getDepth() == 1) {
769     Result << "<Index>" << C->getIndex(0) << "</Index>";
770   }
771
772   Result << "<Discussion>";
773   visit(C->getParagraph());
774   Result << "</Discussion></Parameter>";
775 }
776
777 void CommentASTToXMLConverter::visitVerbatimBlockComment(
778                                   const VerbatimBlockComment *C) {
779   unsigned NumLines = C->getNumLines();
780   if (NumLines == 0)
781     return;
782
783   switch (C->getCommandID()) {
784   case CommandTraits::KCI_code:
785     Result << "<Verbatim xml:space=\"preserve\" kind=\"code\">";
786     break;
787   default:
788     Result << "<Verbatim xml:space=\"preserve\" kind=\"verbatim\">";
789     break;
790   }
791   for (unsigned i = 0; i != NumLines; ++i) {
792     appendToResultWithXMLEscaping(C->getText(i));
793     if (i + 1 != NumLines)
794       Result << '\n';
795   }
796   Result << "</Verbatim>";
797 }
798
799 void CommentASTToXMLConverter::visitVerbatimBlockLineComment(
800                                   const VerbatimBlockLineComment *C) {
801   llvm_unreachable("should not see this AST node");
802 }
803
804 void CommentASTToXMLConverter::visitVerbatimLineComment(
805                                   const VerbatimLineComment *C) {
806   Result << "<Verbatim xml:space=\"preserve\" kind=\"verbatim\">";
807   appendToResultWithXMLEscaping(C->getText());
808   Result << "</Verbatim>";
809 }
810
811 void CommentASTToXMLConverter::visitFullComment(const FullComment *C) {
812   FullCommentParts Parts(C, Traits);
813
814   const DeclInfo *DI = C->getDeclInfo();
815   StringRef RootEndTag;
816   if (DI) {
817     switch (DI->getKind()) {
818     case DeclInfo::OtherKind:
819       RootEndTag = "</Other>";
820       Result << "<Other";
821       break;
822     case DeclInfo::FunctionKind:
823       RootEndTag = "</Function>";
824       Result << "<Function";
825       switch (DI->TemplateKind) {
826       case DeclInfo::NotTemplate:
827         break;
828       case DeclInfo::Template:
829         Result << " templateKind=\"template\"";
830         break;
831       case DeclInfo::TemplateSpecialization:
832         Result << " templateKind=\"specialization\"";
833         break;
834       case DeclInfo::TemplatePartialSpecialization:
835         llvm_unreachable("partial specializations of functions "
836                          "are not allowed in C++");
837       }
838       if (DI->IsInstanceMethod)
839         Result << " isInstanceMethod=\"1\"";
840       if (DI->IsClassMethod)
841         Result << " isClassMethod=\"1\"";
842       break;
843     case DeclInfo::ClassKind:
844       RootEndTag = "</Class>";
845       Result << "<Class";
846       switch (DI->TemplateKind) {
847       case DeclInfo::NotTemplate:
848         break;
849       case DeclInfo::Template:
850         Result << " templateKind=\"template\"";
851         break;
852       case DeclInfo::TemplateSpecialization:
853         Result << " templateKind=\"specialization\"";
854         break;
855       case DeclInfo::TemplatePartialSpecialization:
856         Result << " templateKind=\"partialSpecialization\"";
857         break;
858       }
859       break;
860     case DeclInfo::VariableKind:
861       RootEndTag = "</Variable>";
862       Result << "<Variable";
863       break;
864     case DeclInfo::NamespaceKind:
865       RootEndTag = "</Namespace>";
866       Result << "<Namespace";
867       break;
868     case DeclInfo::TypedefKind:
869       RootEndTag = "</Typedef>";
870       Result << "<Typedef";
871       break;
872     case DeclInfo::EnumKind:
873       RootEndTag = "</Enum>";
874       Result << "<Enum";
875       break;
876     }
877
878     {
879       // Print line and column number.
880       SourceLocation Loc = DI->CurrentDecl->getLocation();
881       std::pair<FileID, unsigned> LocInfo = SM.getDecomposedLoc(Loc);
882       FileID FID = LocInfo.first;
883       unsigned FileOffset = LocInfo.second;
884
885       if (FID.isValid()) {
886         if (const FileEntry *FE = SM.getFileEntryForID(FID)) {
887           Result << " file=\"";
888           appendToResultWithXMLEscaping(FE->getName());
889           Result << "\"";
890         }
891         Result << " line=\"" << SM.getLineNumber(FID, FileOffset)
892                << "\" column=\"" << SM.getColumnNumber(FID, FileOffset)
893                << "\"";
894       }
895     }
896
897     // Finish the root tag.
898     Result << ">";
899
900     bool FoundName = false;
901     if (const NamedDecl *ND = dyn_cast<NamedDecl>(DI->CommentDecl)) {
902       if (DeclarationName DeclName = ND->getDeclName()) {
903         Result << "<Name>";
904         std::string Name = DeclName.getAsString();
905         appendToResultWithXMLEscaping(Name);
906         FoundName = true;
907         Result << "</Name>";
908       }
909     }
910     if (!FoundName)
911       Result << "<Name>&lt;anonymous&gt;</Name>";
912
913     {
914       // Print USR.
915       SmallString<128> USR;
916       generateUSRForDecl(DI->CommentDecl, USR);
917       if (!USR.empty()) {
918         Result << "<USR>";
919         appendToResultWithXMLEscaping(USR);
920         Result << "</USR>";
921       }
922     }
923   } else {
924     // No DeclInfo -- just emit some root tag and name tag.
925     RootEndTag = "</Other>";
926     Result << "<Other><Name>unknown</Name>";
927   }
928
929   if (Parts.Headerfile) {
930     Result << "<Headerfile>";
931     visit(Parts.Headerfile);
932     Result << "</Headerfile>";
933   }
934
935   {
936     // Pretty-print the declaration.
937     Result << "<Declaration>";
938     SmallString<128> Declaration;
939     getSourceTextOfDeclaration(DI, Declaration);
940     formatTextOfDeclaration(DI, Declaration);
941     appendToResultWithXMLEscaping(Declaration);
942     Result << "</Declaration>";
943   }
944
945   bool FirstParagraphIsBrief = false;
946   if (Parts.Brief) {
947     Result << "<Abstract>";
948     visit(Parts.Brief);
949     Result << "</Abstract>";
950   } else if (Parts.FirstParagraph) {
951     Result << "<Abstract>";
952     visit(Parts.FirstParagraph);
953     Result << "</Abstract>";
954     FirstParagraphIsBrief = true;
955   }
956
957   if (Parts.TParams.size() != 0) {
958     Result << "<TemplateParameters>";
959     for (unsigned i = 0, e = Parts.TParams.size(); i != e; ++i)
960       visit(Parts.TParams[i]);
961     Result << "</TemplateParameters>";
962   }
963
964   if (Parts.Params.size() != 0) {
965     Result << "<Parameters>";
966     for (unsigned i = 0, e = Parts.Params.size(); i != e; ++i)
967       visit(Parts.Params[i]);
968     Result << "</Parameters>";
969   }
970
971   if (Parts.Exceptions.size() != 0) {
972     Result << "<Exceptions>";
973     for (unsigned i = 0, e = Parts.Exceptions.size(); i != e; ++i)
974       visit(Parts.Exceptions[i]);
975     Result << "</Exceptions>";
976   }
977
978   if (Parts.Returns.size() != 0) {
979     Result << "<ResultDiscussion>";
980     for (unsigned i = 0, e = Parts.Returns.size(); i != e; ++i)
981       visit(Parts.Returns[i]);
982     Result << "</ResultDiscussion>";
983   }
984
985   if (DI->CommentDecl->hasAttrs()) {
986     const AttrVec &Attrs = DI->CommentDecl->getAttrs();
987     for (unsigned i = 0, e = Attrs.size(); i != e; i++) {
988       const AvailabilityAttr *AA = dyn_cast<AvailabilityAttr>(Attrs[i]);
989       if (!AA) {
990         if (const DeprecatedAttr *DA = dyn_cast<DeprecatedAttr>(Attrs[i])) {
991           if (DA->getMessage().empty())
992             Result << "<Deprecated/>";
993           else {
994             Result << "<Deprecated>";
995             appendToResultWithXMLEscaping(DA->getMessage());
996             Result << "</Deprecated>";
997           }
998         }
999         else if (const UnavailableAttr *UA = dyn_cast<UnavailableAttr>(Attrs[i])) {
1000           if (UA->getMessage().empty())
1001             Result << "<Unavailable/>";
1002           else {
1003             Result << "<Unavailable>";
1004             appendToResultWithXMLEscaping(UA->getMessage());
1005             Result << "</Unavailable>";
1006           }
1007         }
1008         continue;
1009       }
1010
1011       // 'availability' attribute.
1012       Result << "<Availability";
1013       StringRef Distribution;
1014       if (AA->getPlatform()) {
1015         Distribution = AvailabilityAttr::getPrettyPlatformName(
1016                                         AA->getPlatform()->getName());
1017         if (Distribution.empty())
1018           Distribution = AA->getPlatform()->getName();
1019       }
1020       Result << " distribution=\"" << Distribution << "\">";
1021       VersionTuple IntroducedInVersion = AA->getIntroduced();
1022       if (!IntroducedInVersion.empty()) {
1023         Result << "<IntroducedInVersion>"
1024                << IntroducedInVersion.getAsString()
1025                << "</IntroducedInVersion>";
1026       }
1027       VersionTuple DeprecatedInVersion = AA->getDeprecated();
1028       if (!DeprecatedInVersion.empty()) {
1029         Result << "<DeprecatedInVersion>"
1030                << DeprecatedInVersion.getAsString()
1031                << "</DeprecatedInVersion>";
1032       }
1033       VersionTuple RemovedAfterVersion = AA->getObsoleted();
1034       if (!RemovedAfterVersion.empty()) {
1035         Result << "<RemovedAfterVersion>"
1036                << RemovedAfterVersion.getAsString()
1037                << "</RemovedAfterVersion>";
1038       }
1039       StringRef DeprecationSummary = AA->getMessage();
1040       if (!DeprecationSummary.empty()) {
1041         Result << "<DeprecationSummary>";
1042         appendToResultWithXMLEscaping(DeprecationSummary);
1043         Result << "</DeprecationSummary>";
1044       }
1045       if (AA->getUnavailable())
1046         Result << "<Unavailable/>";
1047       Result << "</Availability>";
1048     }
1049   }
1050
1051   {
1052     bool StartTagEmitted = false;
1053     for (unsigned i = 0, e = Parts.MiscBlocks.size(); i != e; ++i) {
1054       const Comment *C = Parts.MiscBlocks[i];
1055       if (FirstParagraphIsBrief && C == Parts.FirstParagraph)
1056         continue;
1057       if (!StartTagEmitted) {
1058         Result << "<Discussion>";
1059         StartTagEmitted = true;
1060       }
1061       visit(C);
1062     }
1063     if (StartTagEmitted)
1064       Result << "</Discussion>";
1065   }
1066
1067   Result << RootEndTag;
1068 }
1069
1070 void CommentASTToXMLConverter::appendToResultWithXMLEscaping(StringRef S) {
1071   for (StringRef::iterator I = S.begin(), E = S.end(); I != E; ++I) {
1072     const char C = *I;
1073     switch (C) {
1074     case '&':
1075       Result << "&amp;";
1076       break;
1077     case '<':
1078       Result << "&lt;";
1079       break;
1080     case '>':
1081       Result << "&gt;";
1082       break;
1083     case '"':
1084       Result << "&quot;";
1085       break;
1086     case '\'':
1087       Result << "&apos;";
1088       break;
1089     default:
1090       Result << C;
1091       break;
1092     }
1093   }
1094 }
1095
1096 void CommentASTToXMLConverter::appendToResultWithCDATAEscaping(StringRef S) {
1097   if (S.empty())
1098     return;
1099
1100   Result << "<![CDATA[";
1101   while (!S.empty()) {
1102     size_t Pos = S.find("]]>");
1103     if (Pos == 0) {
1104       Result << "]]]]><![CDATA[>";
1105       S = S.drop_front(3);
1106       continue;
1107     }
1108     if (Pos == StringRef::npos)
1109       Pos = S.size();
1110
1111     Result << S.substr(0, Pos);
1112
1113     S = S.drop_front(Pos);
1114   }
1115   Result << "]]>";
1116 }
1117
1118 CommentToXMLConverter::CommentToXMLConverter() {}
1119 CommentToXMLConverter::~CommentToXMLConverter() {}
1120
1121 void CommentToXMLConverter::convertCommentToHTML(const FullComment *FC,
1122                                                  SmallVectorImpl<char> &HTML,
1123                                                  const ASTContext &Context) {
1124   CommentASTToHTMLConverter Converter(FC, HTML,
1125                                       Context.getCommentCommandTraits());
1126   Converter.visit(FC);
1127 }
1128
1129 void CommentToXMLConverter::convertHTMLTagNodeToText(
1130     const comments::HTMLTagComment *HTC, SmallVectorImpl<char> &Text,
1131     const ASTContext &Context) {
1132   CommentASTToHTMLConverter Converter(nullptr, Text,
1133                                       Context.getCommentCommandTraits());
1134   Converter.visit(HTC);
1135 }
1136
1137 void CommentToXMLConverter::convertCommentToXML(const FullComment *FC,
1138                                                 SmallVectorImpl<char> &XML,
1139                                                 const ASTContext &Context) {
1140   CommentASTToXMLConverter Converter(FC, XML, Context.getCommentCommandTraits(),
1141                                      Context.getSourceManager());
1142   Converter.visit(FC);
1143 }