blob: 43c423274daac70ba18b70d623dc4495a8401882 [file] [log] [blame]
Dmitri Gribenko86cfda22013-11-13 22:16:51 +00001//===--- 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"
Dmitri Gribenko86cfda22013-11-13 22:16:51 +000012#include "clang/AST/ASTContext.h"
Stephen Hines651f13c2014-04-23 16:59:28 -070013#include "clang/AST/Attr.h"
Dmitri Gribenko86cfda22013-11-13 22:16:51 +000014#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
23using namespace clang;
24using namespace clang::comments;
25using namespace clang::index;
26
27namespace {
28
29/// This comparison will sort parameters with valid index by index, then vararg
30/// parameters, and invalid (unresolved) parameters last.
31class ParamCommandCommentCompareIndex {
32public:
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.
58class TParamCommandCommentComparePosition {
59public:
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.
83struct 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
98FullCommentParts::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
201void 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
221class CommentASTToHTMLConverter :
222 public ConstCommentVisitor<CommentASTToHTMLConverter> {
223public:
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
256private:
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
265void CommentASTToHTMLConverter::visitTextComment(const TextComment *C) {
266 appendToResultWithHTMLEscaping(C->getText());
267}
268
269void 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
309void CommentASTToHTMLConverter::visitHTMLStartTagComment(
310 const HTMLStartTagComment *C) {
311 printHTMLStartTagComment(C, Result);
312}
313
314void CommentASTToHTMLConverter::visitHTMLEndTagComment(
315 const HTMLEndTagComment *C) {
316 Result << "</" << C->getTagName() << ">";
317}
318
319void 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
332void 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
352void 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
384void 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
415void 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
430void CommentASTToHTMLConverter::visitVerbatimBlockLineComment(
431 const VerbatimBlockLineComment *C) {
432 llvm_unreachable("should not see this AST node");
433}
434
435void CommentASTToHTMLConverter::visitVerbatimLineComment(
436 const VerbatimLineComment *C) {
437 Result << "<pre>";
438 appendToResultWithHTMLEscaping(C->getText());
439 Result << "</pre>";
440}
441
442void 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
488void 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
499void 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
528namespace {
529class CommentASTToXMLConverter :
530 public ConstCommentVisitor<CommentASTToXMLConverter> {
531public:
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
570private:
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
582void 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
594void 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
625void CommentASTToXMLConverter::visitTextComment(const TextComment *C) {
626 appendToResultWithXMLEscaping(C->getText());
627}
628
629void 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
668void CommentASTToXMLConverter::visitHTMLStartTagComment(
669 const HTMLStartTagComment *C) {
670 Result << "<rawHTML><![CDATA[";
671 printHTMLStartTagComment(C, Result);
672 Result << "]]></rawHTML>";
673}
674
675void
676CommentASTToXMLConverter::visitHTMLEndTagComment(const HTMLEndTagComment *C) {
677 Result << "<rawHTML>&lt;/" << C->getTagName() << "&gt;</rawHTML>";
678}
679
680void
681CommentASTToXMLConverter::visitParagraphComment(const ParagraphComment *C) {
682 appendParagraphCommentWithKind(C, StringRef());
683}
684
685void 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
703void 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
734void 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
766void 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
782void 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
804void CommentASTToXMLConverter::visitVerbatimBlockLineComment(
805 const VerbatimBlockLineComment *C) {
806 llvm_unreachable("should not see this AST node");
807}
808
809void CommentASTToXMLConverter::visitVerbatimLineComment(
810 const VerbatimLineComment *C) {
811 Result << "<Verbatim xml:space=\"preserve\" kind=\"verbatim\">";
812 appendToResultWithXMLEscaping(C->getText());
813 Result << "</Verbatim>";
814}
815
816void 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
1077void 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
1103void 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
1111void 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
1119void 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