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