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