clang 20.0.0git
CommentToXML.cpp
Go to the documentation of this file.
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
11#include "clang/AST/Attr.h"
12#include "clang/AST/Comment.h"
17#include "clang/Format/Format.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;
94 llvm::TinyPtrVector<const BlockCommandComment *> Exceptions;
96};
97
98FullCommentParts::FullCommentParts(const FullComment *C,
99 const CommandTraits &Traits) :
100 Brief(nullptr), Headerfile(nullptr), FirstParagraph(nullptr) {
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 CommentKind::None:
108 continue;
109
110 case CommentKind::ParagraphComment: {
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 CommentKind::BlockCommandComment: {
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 CommentKind::ParamCommandComment: {
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 CommentKind::TParamCommandComment: {
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 CommentKind::VerbatimBlockComment:
169 MiscBlocks.push_back(cast<BlockCommandComment>(Child));
170 break;
171
172 case CommentKind::VerbatimLineComment: {
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 CommentKind::TextComment:
181 case CommentKind::InlineCommandComment:
182 case CommentKind::HTMLStartTagComment:
183 case CommentKind::HTMLEndTagComment:
184 case CommentKind::VerbatimBlockLineComment:
185 case CommentKind::FullComment:
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 llvm::stable_sort(Params, ParamCommandCommentCompareIndex());
195 llvm::stable_sort(TParams, TParamCommandCommentComparePosition());
196}
197
198void printHTMLStartTagComment(const HTMLStartTagComment *C,
199 llvm::raw_svector_ostream &Result) {
200 Result << "<" << C->getTagName();
201
202 if (C->getNumAttrs() != 0) {
203 for (unsigned i = 0, e = C->getNumAttrs(); i != e; i++) {
204 Result << " ";
205 const HTMLStartTagComment::Attribute &Attr = C->getAttr(i);
206 Result << Attr.Name;
207 if (!Attr.Value.empty())
208 Result << "=\"" << Attr.Value << "\"";
209 }
210 }
211
212 if (!C->isSelfClosing())
213 Result << ">";
214 else
215 Result << "/>";
216}
217
218class CommentASTToHTMLConverter :
219 public ConstCommentVisitor<CommentASTToHTMLConverter> {
220public:
221 /// \param Str accumulator for HTML.
222 CommentASTToHTMLConverter(const FullComment *FC,
224 const CommandTraits &Traits) :
225 FC(FC), Result(Str), Traits(Traits)
226 { }
227
228 // Inline content.
229 void visitTextComment(const TextComment *C);
230 void visitInlineCommandComment(const InlineCommandComment *C);
231 void visitHTMLStartTagComment(const HTMLStartTagComment *C);
232 void visitHTMLEndTagComment(const HTMLEndTagComment *C);
233
234 // Block content.
235 void visitParagraphComment(const ParagraphComment *C);
236 void visitBlockCommandComment(const BlockCommandComment *C);
237 void visitParamCommandComment(const ParamCommandComment *C);
238 void visitTParamCommandComment(const TParamCommandComment *C);
239 void visitVerbatimBlockComment(const VerbatimBlockComment *C);
240 void visitVerbatimBlockLineComment(const VerbatimBlockLineComment *C);
241 void visitVerbatimLineComment(const VerbatimLineComment *C);
242
243 void visitFullComment(const FullComment *C);
244
245 // Helpers.
246
247 /// Convert a paragraph that is not a block by itself (an argument to some
248 /// command).
249 void visitNonStandaloneParagraphComment(const ParagraphComment *C);
250
251 void appendToResultWithHTMLEscaping(StringRef S);
252
253private:
254 const FullComment *FC;
255 /// Output stream for HTML.
256 llvm::raw_svector_ostream Result;
257
258 const CommandTraits &Traits;
259};
260} // end unnamed namespace
261
262void CommentASTToHTMLConverter::visitTextComment(const TextComment *C) {
263 appendToResultWithHTMLEscaping(C->getText());
264}
265
266void CommentASTToHTMLConverter::visitInlineCommandComment(
267 const InlineCommandComment *C) {
268 // Nothing to render if no arguments supplied.
269 if (C->getNumArgs() == 0)
270 return;
271
272 // Nothing to render if argument is empty.
273 StringRef Arg0 = C->getArgText(0);
274 if (Arg0.empty())
275 return;
276
277 switch (C->getRenderKind()) {
278 case InlineCommandRenderKind::Normal:
279 for (unsigned i = 0, e = C->getNumArgs(); i != e; ++i) {
280 appendToResultWithHTMLEscaping(C->getArgText(i));
281 Result << " ";
282 }
283 return;
284
285 case InlineCommandRenderKind::Bold:
286 assert(C->getNumArgs() == 1);
287 Result << "<b>";
288 appendToResultWithHTMLEscaping(Arg0);
289 Result << "</b>";
290 return;
291 case InlineCommandRenderKind::Monospaced:
292 assert(C->getNumArgs() == 1);
293 Result << "<tt>";
294 appendToResultWithHTMLEscaping(Arg0);
295 Result<< "</tt>";
296 return;
297 case InlineCommandRenderKind::Emphasized:
298 assert(C->getNumArgs() == 1);
299 Result << "<em>";
300 appendToResultWithHTMLEscaping(Arg0);
301 Result << "</em>";
302 return;
303 case InlineCommandRenderKind::Anchor:
304 assert(C->getNumArgs() == 1);
305 Result << "<span id=\"" << Arg0 << "\"></span>";
306 return;
307 }
308}
309
310void CommentASTToHTMLConverter::visitHTMLStartTagComment(
311 const HTMLStartTagComment *C) {
312 printHTMLStartTagComment(C, Result);
313}
314
315void CommentASTToHTMLConverter::visitHTMLEndTagComment(
316 const HTMLEndTagComment *C) {
317 Result << "</" << C->getTagName() << ">";
318}
319
320void CommentASTToHTMLConverter::visitParagraphComment(
321 const ParagraphComment *C) {
322 if (C->isWhitespace())
323 return;
324
325 Result << "<p>";
326 for (Comment::child_iterator I = C->child_begin(), E = C->child_end();
327 I != E; ++I) {
328 visit(*I);
329 }
330 Result << "</p>";
331}
332
333void CommentASTToHTMLConverter::visitBlockCommandComment(
334 const BlockCommandComment *C) {
335 const CommandInfo *Info = Traits.getCommandInfo(C->getCommandID());
336 if (Info->IsBriefCommand) {
337 Result << "<p class=\"para-brief\">";
338 visitNonStandaloneParagraphComment(C->getParagraph());
339 Result << "</p>";
340 return;
341 }
342 if (Info->IsReturnsCommand) {
343 Result << "<p class=\"para-returns\">"
344 "<span class=\"word-returns\">Returns</span> ";
345 visitNonStandaloneParagraphComment(C->getParagraph());
346 Result << "</p>";
347 return;
348 }
349 // We don't know anything about this command. Just render the paragraph.
350 visit(C->getParagraph());
351}
352
353void CommentASTToHTMLConverter::visitParamCommandComment(
354 const ParamCommandComment *C) {
355 if (C->isParamIndexValid()) {
356 if (C->isVarArgParam()) {
357 Result << "<dt class=\"param-name-index-vararg\">";
358 appendToResultWithHTMLEscaping(C->getParamNameAsWritten());
359 } else {
360 Result << "<dt class=\"param-name-index-"
361 << C->getParamIndex()
362 << "\">";
363 appendToResultWithHTMLEscaping(C->getParamName(FC));
364 }
365 } else {
366 Result << "<dt class=\"param-name-index-invalid\">";
367 appendToResultWithHTMLEscaping(C->getParamNameAsWritten());
368 }
369 Result << "</dt>";
370
371 if (C->isParamIndexValid()) {
372 if (C->isVarArgParam())
373 Result << "<dd class=\"param-descr-index-vararg\">";
374 else
375 Result << "<dd class=\"param-descr-index-"
376 << C->getParamIndex()
377 << "\">";
378 } else
379 Result << "<dd class=\"param-descr-index-invalid\">";
380
381 visitNonStandaloneParagraphComment(C->getParagraph());
382 Result << "</dd>";
383}
384
385void CommentASTToHTMLConverter::visitTParamCommandComment(
386 const TParamCommandComment *C) {
387 if (C->isPositionValid()) {
388 if (C->getDepth() == 1)
389 Result << "<dt class=\"tparam-name-index-"
390 << C->getIndex(0)
391 << "\">";
392 else
393 Result << "<dt class=\"tparam-name-index-other\">";
394 appendToResultWithHTMLEscaping(C->getParamName(FC));
395 } else {
396 Result << "<dt class=\"tparam-name-index-invalid\">";
397 appendToResultWithHTMLEscaping(C->getParamNameAsWritten());
398 }
399
400 Result << "</dt>";
401
402 if (C->isPositionValid()) {
403 if (C->getDepth() == 1)
404 Result << "<dd class=\"tparam-descr-index-"
405 << C->getIndex(0)
406 << "\">";
407 else
408 Result << "<dd class=\"tparam-descr-index-other\">";
409 } else
410 Result << "<dd class=\"tparam-descr-index-invalid\">";
411
412 visitNonStandaloneParagraphComment(C->getParagraph());
413 Result << "</dd>";
414}
415
416void CommentASTToHTMLConverter::visitVerbatimBlockComment(
417 const VerbatimBlockComment *C) {
418 unsigned NumLines = C->getNumLines();
419 if (NumLines == 0)
420 return;
421
422 Result << "<pre>";
423 for (unsigned i = 0; i != NumLines; ++i) {
424 appendToResultWithHTMLEscaping(C->getText(i));
425 if (i + 1 != NumLines)
426 Result << '\n';
427 }
428 Result << "</pre>";
429}
430
431void CommentASTToHTMLConverter::visitVerbatimBlockLineComment(
433 llvm_unreachable("should not see this AST node");
434}
435
436void CommentASTToHTMLConverter::visitVerbatimLineComment(
437 const VerbatimLineComment *C) {
438 Result << "<pre>";
439 appendToResultWithHTMLEscaping(C->getText());
440 Result << "</pre>";
441}
442
443void CommentASTToHTMLConverter::visitFullComment(const FullComment *C) {
444 FullCommentParts Parts(C, Traits);
445
446 bool FirstParagraphIsBrief = false;
447 if (Parts.Headerfile)
448 visit(Parts.Headerfile);
449 if (Parts.Brief)
450 visit(Parts.Brief);
451 else if (Parts.FirstParagraph) {
452 Result << "<p class=\"para-brief\">";
453 visitNonStandaloneParagraphComment(Parts.FirstParagraph);
454 Result << "</p>";
455 FirstParagraphIsBrief = true;
456 }
457
458 for (unsigned i = 0, e = Parts.MiscBlocks.size(); i != e; ++i) {
459 const Comment *C = Parts.MiscBlocks[i];
460 if (FirstParagraphIsBrief && C == Parts.FirstParagraph)
461 continue;
462 visit(C);
463 }
464
465 if (Parts.TParams.size() != 0) {
466 Result << "<dl>";
467 for (unsigned i = 0, e = Parts.TParams.size(); i != e; ++i)
468 visit(Parts.TParams[i]);
469 Result << "</dl>";
470 }
471
472 if (Parts.Params.size() != 0) {
473 Result << "<dl>";
474 for (unsigned i = 0, e = Parts.Params.size(); i != e; ++i)
475 visit(Parts.Params[i]);
476 Result << "</dl>";
477 }
478
479 if (Parts.Returns.size() != 0) {
480 Result << "<div class=\"result-discussion\">";
481 for (unsigned i = 0, e = Parts.Returns.size(); i != e; ++i)
482 visit(Parts.Returns[i]);
483 Result << "</div>";
484 }
485
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,
535 const CommandTraits &Traits,
536 const SourceManager &SM) :
537 FC(FC), Result(Str), Traits(Traits), SM(SM) { }
538
539 // Inline content.
540 void visitTextComment(const TextComment *C);
541 void visitInlineCommandComment(const InlineCommandComment *C);
542 void visitHTMLStartTagComment(const HTMLStartTagComment *C);
543 void visitHTMLEndTagComment(const HTMLEndTagComment *C);
544
545 // Block content.
546 void visitParagraphComment(const ParagraphComment *C);
547
548 void appendParagraphCommentWithKind(const ParagraphComment *C,
549 StringRef ParagraphKind,
550 StringRef PrependBodyText);
551
552 void visitBlockCommandComment(const BlockCommandComment *C);
553 void visitParamCommandComment(const ParamCommandComment *C);
554 void visitTParamCommandComment(const TParamCommandComment *C);
555 void visitVerbatimBlockComment(const VerbatimBlockComment *C);
556 void visitVerbatimBlockLineComment(const VerbatimBlockLineComment *C);
557 void visitVerbatimLineComment(const VerbatimLineComment *C);
558
559 void visitFullComment(const FullComment *C);
560
561 // Helpers.
562 void appendToResultWithXMLEscaping(StringRef S);
563 void appendToResultWithCDATAEscaping(StringRef S);
564
565 void formatTextOfDeclaration(const DeclInfo *DI,
567
568private:
569 const FullComment *FC;
570
571 /// Output stream for XML.
572 llvm::raw_svector_ostream Result;
573
574 const CommandTraits &Traits;
575 const SourceManager &SM;
576};
577
578void getSourceTextOfDeclaration(const DeclInfo *ThisDecl,
580 ASTContext &Context = ThisDecl->CurrentDecl->getASTContext();
581 const LangOptions &LangOpts = Context.getLangOpts();
582 llvm::raw_svector_ostream OS(Str);
583 PrintingPolicy PPolicy(LangOpts);
584 PPolicy.PolishForDeclaration = true;
585 PPolicy.TerseOutput = true;
586 PPolicy.ConstantsAsWritten = true;
587 ThisDecl->CurrentDecl->print(OS, PPolicy,
588 /*Indentation*/0, /*PrintInstantiation*/false);
589}
590
591void CommentASTToXMLConverter::formatTextOfDeclaration(
593 // Formatting API expects null terminated input string.
594 StringRef StringDecl(Declaration.c_str(), Declaration.size());
595
596 // Formatter specific code.
597 unsigned Offset = 0;
598 unsigned Length = Declaration.size();
599
601 Style.FixNamespaceComments = false;
602 tooling::Replacements Replaces =
603 reformat(Style, StringDecl, tooling::Range(Offset, Length), "xmldecl.xd");
604 auto FormattedStringDecl = applyAllReplacements(StringDecl, Replaces);
605 if (static_cast<bool>(FormattedStringDecl)) {
606 Declaration = *FormattedStringDecl;
607 }
608}
609
610} // end unnamed namespace
611
612void CommentASTToXMLConverter::visitTextComment(const TextComment *C) {
613 appendToResultWithXMLEscaping(C->getText());
614}
615
616void CommentASTToXMLConverter::visitInlineCommandComment(
617 const InlineCommandComment *C) {
618 // Nothing to render if no arguments supplied.
619 if (C->getNumArgs() == 0)
620 return;
621
622 // Nothing to render if argument is empty.
623 StringRef Arg0 = C->getArgText(0);
624 if (Arg0.empty())
625 return;
626
627 switch (C->getRenderKind()) {
628 case InlineCommandRenderKind::Normal:
629 for (unsigned i = 0, e = C->getNumArgs(); i != e; ++i) {
630 appendToResultWithXMLEscaping(C->getArgText(i));
631 Result << " ";
632 }
633 return;
634 case InlineCommandRenderKind::Bold:
635 assert(C->getNumArgs() == 1);
636 Result << "<bold>";
637 appendToResultWithXMLEscaping(Arg0);
638 Result << "</bold>";
639 return;
640 case InlineCommandRenderKind::Monospaced:
641 assert(C->getNumArgs() == 1);
642 Result << "<monospaced>";
643 appendToResultWithXMLEscaping(Arg0);
644 Result << "</monospaced>";
645 return;
646 case InlineCommandRenderKind::Emphasized:
647 assert(C->getNumArgs() == 1);
648 Result << "<emphasized>";
649 appendToResultWithXMLEscaping(Arg0);
650 Result << "</emphasized>";
651 return;
652 case InlineCommandRenderKind::Anchor:
653 assert(C->getNumArgs() == 1);
654 Result << "<anchor id=\"" << Arg0 << "\"></anchor>";
655 return;
656 }
657}
658
659void CommentASTToXMLConverter::visitHTMLStartTagComment(
660 const HTMLStartTagComment *C) {
661 Result << "<rawHTML";
662 if (C->isMalformed())
663 Result << " isMalformed=\"1\"";
664 Result << ">";
665 {
667 {
668 llvm::raw_svector_ostream TagOS(Tag);
669 printHTMLStartTagComment(C, TagOS);
670 }
671 appendToResultWithCDATAEscaping(Tag);
672 }
673 Result << "</rawHTML>";
674}
675
676void
677CommentASTToXMLConverter::visitHTMLEndTagComment(const HTMLEndTagComment *C) {
678 Result << "<rawHTML";
679 if (C->isMalformed())
680 Result << " isMalformed=\"1\"";
681 Result << ">&lt;/" << C->getTagName() << "&gt;</rawHTML>";
682}
683
684void CommentASTToXMLConverter::visitParagraphComment(
685 const ParagraphComment *C) {
686 appendParagraphCommentWithKind(C, StringRef(), StringRef());
687}
688
689void CommentASTToXMLConverter::appendParagraphCommentWithKind(
690 const ParagraphComment *C, StringRef ParagraphKind,
691 StringRef PrependBodyText) {
692 if (C->isWhitespace() && PrependBodyText.empty())
693 return;
694
695 if (ParagraphKind.empty())
696 Result << "<Para>";
697 else
698 Result << "<Para kind=\"" << ParagraphKind << "\">";
699
700 if (!PrependBodyText.empty())
701 Result << PrependBodyText << " ";
702
703 for (Comment::child_iterator I = C->child_begin(), E = C->child_end(); I != E;
704 ++I) {
705 visit(*I);
706 }
707 Result << "</Para>";
708}
709
710void CommentASTToXMLConverter::visitBlockCommandComment(
711 const BlockCommandComment *C) {
712 StringRef ParagraphKind;
713 StringRef ExceptionType;
714
715 const unsigned CommandID = C->getCommandID();
716 const CommandInfo *Info = Traits.getCommandInfo(CommandID);
717 if (Info->IsThrowsCommand && C->getNumArgs() > 0) {
718 ExceptionType = C->getArgText(0);
719 }
720
721 switch (CommandID) {
722 case CommandTraits::KCI_attention:
723 case CommandTraits::KCI_author:
724 case CommandTraits::KCI_authors:
725 case CommandTraits::KCI_bug:
726 case CommandTraits::KCI_copyright:
727 case CommandTraits::KCI_date:
728 case CommandTraits::KCI_invariant:
729 case CommandTraits::KCI_note:
730 case CommandTraits::KCI_post:
731 case CommandTraits::KCI_pre:
732 case CommandTraits::KCI_remark:
733 case CommandTraits::KCI_remarks:
734 case CommandTraits::KCI_sa:
735 case CommandTraits::KCI_see:
736 case CommandTraits::KCI_since:
737 case CommandTraits::KCI_todo:
738 case CommandTraits::KCI_version:
739 case CommandTraits::KCI_warning:
740 ParagraphKind = C->getCommandName(Traits);
741 break;
742 default:
743 break;
744 }
745
746 appendParagraphCommentWithKind(C->getParagraph(), ParagraphKind,
747 ExceptionType);
748}
749
750void CommentASTToXMLConverter::visitParamCommandComment(
751 const ParamCommandComment *C) {
752 Result << "<Parameter><Name>";
753 appendToResultWithXMLEscaping(C->isParamIndexValid()
754 ? C->getParamName(FC)
755 : C->getParamNameAsWritten());
756 Result << "</Name>";
757
758 if (C->isParamIndexValid()) {
759 if (C->isVarArgParam())
760 Result << "<IsVarArg />";
761 else
762 Result << "<Index>" << C->getParamIndex() << "</Index>";
763 }
764
765 Result << "<Direction isExplicit=\"" << C->isDirectionExplicit() << "\">";
766 switch (C->getDirection()) {
767 case ParamCommandPassDirection::In:
768 Result << "in";
769 break;
770 case ParamCommandPassDirection::Out:
771 Result << "out";
772 break;
773 case ParamCommandPassDirection::InOut:
774 Result << "in,out";
775 break;
776 }
777 Result << "</Direction><Discussion>";
778 visit(C->getParagraph());
779 Result << "</Discussion></Parameter>";
780}
781
782void CommentASTToXMLConverter::visitTParamCommandComment(
783 const TParamCommandComment *C) {
784 Result << "<Parameter><Name>";
785 appendToResultWithXMLEscaping(C->isPositionValid() ? C->getParamName(FC)
786 : C->getParamNameAsWritten());
787 Result << "</Name>";
788
789 if (C->isPositionValid() && C->getDepth() == 1) {
790 Result << "<Index>" << C->getIndex(0) << "</Index>";
791 }
792
793 Result << "<Discussion>";
794 visit(C->getParagraph());
795 Result << "</Discussion></Parameter>";
796}
797
798void CommentASTToXMLConverter::visitVerbatimBlockComment(
799 const VerbatimBlockComment *C) {
800 unsigned NumLines = C->getNumLines();
801 if (NumLines == 0)
802 return;
803
804 switch (C->getCommandID()) {
805 case CommandTraits::KCI_code:
806 Result << "<Verbatim xml:space=\"preserve\" kind=\"code\">";
807 break;
808 default:
809 Result << "<Verbatim xml:space=\"preserve\" kind=\"verbatim\">";
810 break;
811 }
812 for (unsigned i = 0; i != NumLines; ++i) {
813 appendToResultWithXMLEscaping(C->getText(i));
814 if (i + 1 != NumLines)
815 Result << '\n';
816 }
817 Result << "</Verbatim>";
818}
819
820void CommentASTToXMLConverter::visitVerbatimBlockLineComment(
822 llvm_unreachable("should not see this AST node");
823}
824
825void CommentASTToXMLConverter::visitVerbatimLineComment(
826 const VerbatimLineComment *C) {
827 Result << "<Verbatim xml:space=\"preserve\" kind=\"verbatim\">";
828 appendToResultWithXMLEscaping(C->getText());
829 Result << "</Verbatim>";
830}
831
832void CommentASTToXMLConverter::visitFullComment(const FullComment *C) {
833 FullCommentParts Parts(C, Traits);
834
835 const DeclInfo *DI = C->getDeclInfo();
836 StringRef RootEndTag;
837 if (DI) {
838 switch (DI->getKind()) {
840 RootEndTag = "</Other>";
841 Result << "<Other";
842 break;
844 RootEndTag = "</Function>";
845 Result << "<Function";
846 switch (DI->TemplateKind) {
848 break;
850 Result << " templateKind=\"template\"";
851 break;
853 Result << " templateKind=\"specialization\"";
854 break;
856 llvm_unreachable("partial specializations of functions "
857 "are not allowed in C++");
858 }
859 if (DI->IsInstanceMethod)
860 Result << " isInstanceMethod=\"1\"";
861 if (DI->IsClassMethod)
862 Result << " isClassMethod=\"1\"";
863 break;
865 RootEndTag = "</Class>";
866 Result << "<Class";
867 switch (DI->TemplateKind) {
869 break;
871 Result << " templateKind=\"template\"";
872 break;
874 Result << " templateKind=\"specialization\"";
875 break;
877 Result << " templateKind=\"partialSpecialization\"";
878 break;
879 }
880 break;
882 RootEndTag = "</Variable>";
883 Result << "<Variable";
884 break;
886 RootEndTag = "</Namespace>";
887 Result << "<Namespace";
888 break;
890 RootEndTag = "</Typedef>";
891 Result << "<Typedef";
892 break;
894 RootEndTag = "</Enum>";
895 Result << "<Enum";
896 break;
897 }
898
899 {
900 // Print line and column number.
902 std::pair<FileID, unsigned> LocInfo = SM.getDecomposedLoc(Loc);
903 FileID FID = LocInfo.first;
904 unsigned FileOffset = LocInfo.second;
905
906 if (FID.isValid()) {
907 if (OptionalFileEntryRef FE = SM.getFileEntryRefForID(FID)) {
908 Result << " file=\"";
909 appendToResultWithXMLEscaping(FE->getName());
910 Result << "\"";
911 }
912 Result << " line=\"" << SM.getLineNumber(FID, FileOffset)
913 << "\" column=\"" << SM.getColumnNumber(FID, FileOffset)
914 << "\"";
915 }
916 }
917
918 // Finish the root tag.
919 Result << ">";
920
921 bool FoundName = false;
922 if (const NamedDecl *ND = dyn_cast<NamedDecl>(DI->CommentDecl)) {
923 if (DeclarationName DeclName = ND->getDeclName()) {
924 Result << "<Name>";
925 std::string Name = DeclName.getAsString();
926 appendToResultWithXMLEscaping(Name);
927 FoundName = true;
928 Result << "</Name>";
929 }
930 }
931 if (!FoundName)
932 Result << "<Name>&lt;anonymous&gt;</Name>";
933
934 {
935 // Print USR.
938 if (!USR.empty()) {
939 Result << "<USR>";
940 appendToResultWithXMLEscaping(USR);
941 Result << "</USR>";
942 }
943 }
944 } else {
945 // No DeclInfo -- just emit some root tag and name tag.
946 RootEndTag = "</Other>";
947 Result << "<Other><Name>unknown</Name>";
948 }
949
950 if (Parts.Headerfile) {
951 Result << "<Headerfile>";
952 visit(Parts.Headerfile);
953 Result << "</Headerfile>";
954 }
955
956 {
957 // Pretty-print the declaration.
958 Result << "<Declaration>";
960 getSourceTextOfDeclaration(DI, Declaration);
961 formatTextOfDeclaration(DI, Declaration);
962 appendToResultWithXMLEscaping(Declaration);
963 Result << "</Declaration>";
964 }
965
966 bool FirstParagraphIsBrief = false;
967 if (Parts.Brief) {
968 Result << "<Abstract>";
969 visit(Parts.Brief);
970 Result << "</Abstract>";
971 } else if (Parts.FirstParagraph) {
972 Result << "<Abstract>";
973 visit(Parts.FirstParagraph);
974 Result << "</Abstract>";
975 FirstParagraphIsBrief = true;
976 }
977
978 if (Parts.TParams.size() != 0) {
979 Result << "<TemplateParameters>";
980 for (unsigned i = 0, e = Parts.TParams.size(); i != e; ++i)
981 visit(Parts.TParams[i]);
982 Result << "</TemplateParameters>";
983 }
984
985 if (Parts.Params.size() != 0) {
986 Result << "<Parameters>";
987 for (unsigned i = 0, e = Parts.Params.size(); i != e; ++i)
988 visit(Parts.Params[i]);
989 Result << "</Parameters>";
990 }
991
992 if (Parts.Exceptions.size() != 0) {
993 Result << "<Exceptions>";
994 for (unsigned i = 0, e = Parts.Exceptions.size(); i != e; ++i)
995 visit(Parts.Exceptions[i]);
996 Result << "</Exceptions>";
997 }
998
999 if (Parts.Returns.size() != 0) {
1000 Result << "<ResultDiscussion>";
1001 for (unsigned i = 0, e = Parts.Returns.size(); i != e; ++i)
1002 visit(Parts.Returns[i]);
1003 Result << "</ResultDiscussion>";
1004 }
1005
1006 if (DI->CommentDecl->hasAttrs()) {
1007 const AttrVec &Attrs = DI->CommentDecl->getAttrs();
1008 for (unsigned i = 0, e = Attrs.size(); i != e; i++) {
1009 const AvailabilityAttr *AA = dyn_cast<AvailabilityAttr>(Attrs[i]);
1010 if (!AA) {
1011 if (const DeprecatedAttr *DA = dyn_cast<DeprecatedAttr>(Attrs[i])) {
1012 if (DA->getMessage().empty())
1013 Result << "<Deprecated/>";
1014 else {
1015 Result << "<Deprecated>";
1016 appendToResultWithXMLEscaping(DA->getMessage());
1017 Result << "</Deprecated>";
1018 }
1019 }
1020 else if (const UnavailableAttr *UA = dyn_cast<UnavailableAttr>(Attrs[i])) {
1021 if (UA->getMessage().empty())
1022 Result << "<Unavailable/>";
1023 else {
1024 Result << "<Unavailable>";
1025 appendToResultWithXMLEscaping(UA->getMessage());
1026 Result << "</Unavailable>";
1027 }
1028 }
1029 continue;
1030 }
1031
1032 // 'availability' attribute.
1033 Result << "<Availability";
1034 StringRef Distribution;
1035 if (AA->getPlatform()) {
1036 Distribution = AvailabilityAttr::getPrettyPlatformName(
1037 AA->getPlatform()->getName());
1038 if (Distribution.empty())
1039 Distribution = AA->getPlatform()->getName();
1040 }
1041 Result << " distribution=\"" << Distribution << "\">";
1042 VersionTuple IntroducedInVersion = AA->getIntroduced();
1043 if (!IntroducedInVersion.empty()) {
1044 Result << "<IntroducedInVersion>"
1045 << IntroducedInVersion.getAsString()
1046 << "</IntroducedInVersion>";
1047 }
1048 VersionTuple DeprecatedInVersion = AA->getDeprecated();
1049 if (!DeprecatedInVersion.empty()) {
1050 Result << "<DeprecatedInVersion>"
1051 << DeprecatedInVersion.getAsString()
1052 << "</DeprecatedInVersion>";
1053 }
1054 VersionTuple RemovedAfterVersion = AA->getObsoleted();
1055 if (!RemovedAfterVersion.empty()) {
1056 Result << "<RemovedAfterVersion>"
1057 << RemovedAfterVersion.getAsString()
1058 << "</RemovedAfterVersion>";
1059 }
1060 StringRef DeprecationSummary = AA->getMessage();
1061 if (!DeprecationSummary.empty()) {
1062 Result << "<DeprecationSummary>";
1063 appendToResultWithXMLEscaping(DeprecationSummary);
1064 Result << "</DeprecationSummary>";
1065 }
1066 if (AA->getUnavailable())
1067 Result << "<Unavailable/>";
1068
1069 IdentifierInfo *Environment = AA->getEnvironment();
1070 if (Environment) {
1071 Result << "<Environment>" << Environment->getName() << "</Environment>";
1072 }
1073 Result << "</Availability>";
1074 }
1075 }
1076
1077 {
1078 bool StartTagEmitted = false;
1079 for (unsigned i = 0, e = Parts.MiscBlocks.size(); i != e; ++i) {
1080 const Comment *C = Parts.MiscBlocks[i];
1081 if (FirstParagraphIsBrief && C == Parts.FirstParagraph)
1082 continue;
1083 if (!StartTagEmitted) {
1084 Result << "<Discussion>";
1085 StartTagEmitted = true;
1086 }
1087 visit(C);
1088 }
1089 if (StartTagEmitted)
1090 Result << "</Discussion>";
1091 }
1092
1093 Result << RootEndTag;
1094}
1095
1096void CommentASTToXMLConverter::appendToResultWithXMLEscaping(StringRef S) {
1097 for (StringRef::iterator I = S.begin(), E = S.end(); I != E; ++I) {
1098 const char C = *I;
1099 switch (C) {
1100 case '&':
1101 Result << "&amp;";
1102 break;
1103 case '<':
1104 Result << "&lt;";
1105 break;
1106 case '>':
1107 Result << "&gt;";
1108 break;
1109 case '"':
1110 Result << "&quot;";
1111 break;
1112 case '\'':
1113 Result << "&apos;";
1114 break;
1115 default:
1116 Result << C;
1117 break;
1118 }
1119 }
1120}
1121
1122void CommentASTToXMLConverter::appendToResultWithCDATAEscaping(StringRef S) {
1123 if (S.empty())
1124 return;
1125
1126 Result << "<![CDATA[";
1127 while (!S.empty()) {
1128 size_t Pos = S.find("]]>");
1129 if (Pos == 0) {
1130 Result << "]]]]><![CDATA[>";
1131 S = S.drop_front(3);
1132 continue;
1133 }
1134 if (Pos == StringRef::npos)
1135 Pos = S.size();
1136
1137 Result << S.substr(0, Pos);
1138
1139 S = S.drop_front(Pos);
1140 }
1141 Result << "]]>";
1142}
1143
1146
1149 const ASTContext &Context) {
1150 CommentASTToHTMLConverter Converter(FC, HTML,
1151 Context.getCommentCommandTraits());
1152 Converter.visit(FC);
1153}
1154
1157 const ASTContext &Context) {
1158 CommentASTToHTMLConverter Converter(nullptr, Text,
1159 Context.getCommentCommandTraits());
1160 Converter.visit(HTC);
1161}
1162
1165 const ASTContext &Context) {
1166 CommentASTToXMLConverter Converter(FC, XML, Context.getCommentCommandTraits(),
1167 Context.getSourceManager());
1168 Converter.visit(FC);
1169}
Defines the clang::ASTContext interface.
#define SM(sm)
Definition: Cuda.cpp:83
Expr * E
Defines the clang::FileManager interface and associated types.
StringRef Text
Definition: Format.cpp:3002
Various functions to configurably format source code.
Defines the clang::IdentifierInfo, clang::IdentifierTable, and clang::Selector interfaces.
SourceLocation Loc
Definition: SemaObjC.cpp:759
Defines the SourceManager interface.
Holds long-lived AST nodes (such as types and decls) that can be referred to throughout the semantic ...
Definition: ASTContext.h:187
SourceManager & getSourceManager()
Definition: ASTContext.h:721
comments::CommandTraits & getCommentCommandTraits() const
Definition: ASTContext.h:958
const LangOptions & getLangOpts() const
Definition: ASTContext.h:797
Attr - This represents one attribute.
Definition: Attr.h:42
bool hasAttrs() const
Definition: DeclBase.h:525
ASTContext & getASTContext() const LLVM_READONLY
Definition: DeclBase.cpp:523
SourceLocation getLocation() const
Definition: DeclBase.h:446
void print(raw_ostream &Out, unsigned Indentation=0, bool PrintInstantiation=false) const
AttrVec & getAttrs()
Definition: DeclBase.h:531
The name of a declaration.
An opaque identifier used by SourceManager which refers to a source file (MemoryBuffer) along with it...
bool isValid() const
One of these records is kept for each identifier that is lexed.
StringRef getName() const
Return the actual identifier string.
Keeps track of the various options that can be enabled, which controls the dialect of C or C++ that i...
Definition: LangOptions.h:476
This represents a decl that may have a name.
Definition: Decl.h:249
Encodes a location in the source.
This class handles loading and caching of source files into memory.
A command that has zero or more word-like arguments (number of word-like arguments depends on command...
Definition: Comment.h:604
Block content (contains inline content).
Definition: Comment.h:538
This class provides information about commands that can be used in comments.
const CommandInfo * getCommandInfo(StringRef Name) const
Any part of the comment.
Definition: Comment.h:65
Comment *const * child_iterator
Definition: Comment.h:251
A full comment attached to a declaration, contains block content.
Definition: Comment.h:1083
An opening HTML tag with attributes.
Definition: Comment.h:433
Abstract class for opening and closing HTML tags.
Definition: Comment.h:391
A command with word-like arguments that is considered inline content.
Definition: Comment.h:335
A single paragraph that contains inline content.
Definition: Comment.h:555
Doxygen \param command.
Definition: Comment.h:711
bool isDirectionExplicit() const LLVM_READONLY
Definition: Comment.h:743
unsigned getParamIndex() const LLVM_READONLY
Definition: Comment.h:779
bool isParamIndexValid() const LLVM_READONLY
Definition: Comment.h:766
bool isVarArgParam() const LLVM_READONLY
Definition: Comment.h:770
Doxygen \tparam command, describes a template parameter.
Definition: Comment.h:793
bool isPositionValid() const LLVM_READONLY
Definition: Comment.h:833
unsigned getIndex(unsigned Depth) const
Definition: Comment.h:842
A verbatim block command (e.
Definition: Comment.h:879
A line of text contained in a verbatim block.
Definition: Comment.h:854
A verbatim line command.
Definition: Comment.h:930
void convertCommentToXML(const comments::FullComment *FC, SmallVectorImpl< char > &XML, const ASTContext &Context)
void convertHTMLTagNodeToText(const comments::HTMLTagComment *HTC, SmallVectorImpl< char > &Text, const ASTContext &Context)
void convertCommentToHTML(const comments::FullComment *FC, SmallVectorImpl< char > &HTML, const ASTContext &Context)
A source range independent of the SourceManager.
Definition: Replacement.h:44
Maintains a set of replacements that are conflict-free.
Definition: Replacement.h:212
#define UINT_MAX
Definition: limits.h:64
FormatStyle getLLVMStyle(FormatStyle::LanguageKind Language=FormatStyle::LanguageKind::LK_Cpp)
Returns a format style complying with the LLVM coding standards: http://llvm.org/docs/CodingStandards...
Definition: Format.cpp:1424
tooling::Replacements reformat(const FormatStyle &Style, StringRef Code, ArrayRef< tooling::Range > Ranges, StringRef FileName="<stdin>", FormattingAttemptStatus *Status=nullptr)
Reformats the given Ranges in Code.
Definition: Format.cpp:3820
bool generateUSRForDecl(const Decl *D, SmallVectorImpl< char > &Buf)
Generate a USR for a Decl, including the USR prefix.
bool applyAllReplacements(const Replacements &Replaces, Rewriter &Rewrite)
Apply all replacements in Replaces to the Rewriter Rewrite.
The JSON file list parser is used to communicate input to InstallAPI.
@ Result
The result type of a method or function.
Describes how types, statements, expressions, and declarations should be printed.
Definition: PrettyPrinter.h:57
Information about a single command.
unsigned IsDeclarationCommand
True if this command contains a declaration for the entity being documented.
unsigned IsThrowsCommand
True if this command is \throws or an alias.
unsigned IsReturnsCommand
True if this command is \returns or an alias.
unsigned IsBriefCommand
True if this command is introducing a brief documentation paragraph (\or an alias).
unsigned IsHeaderfileCommand
True if this is a \headerfile-like command.
Information about the declaration, useful to clients of FullComment.
Definition: Comment.h:962
unsigned TemplateKind
Is CommentDecl a template declaration.
Definition: Comment.h:1047
unsigned IsClassMethod
Is CommentDecl a static member function of C++ class or class method of ObjC class.
Definition: Comment.h:1063
@ FunctionKind
Something that we consider a "function":
Definition: Comment.h:1004
@ EnumKind
An enumeration or scoped enumeration.
Definition: Comment.h:1026
@ OtherKind
Everything else not explicitly mentioned below.
Definition: Comment.h:994
@ NamespaceKind
A C++ namespace.
Definition: Comment.h:1019
@ VariableKind
Something that we consider a "variable":
Definition: Comment.h:1016
@ ClassKind
Something that we consider a "class":
Definition: Comment.h:1010
@ TypedefKind
A C++ typedef-name (a 'typedef' decl specifier or alias-declaration), see TypedefNameDecl.
Definition: Comment.h:1023
DeclKind getKind() const LLVM_READONLY
Definition: Comment.h:1071
unsigned IsInstanceMethod
Is CommentDecl a non-static member function of C++ class or instance method of ObjC class.
Definition: Comment.h:1057
const Decl * CommentDecl
Declaration the comment is actually attached to (in the source).
Definition: Comment.h:965
const Decl * CurrentDecl
CurrentDecl is the declaration with which the FullComment is associated.
Definition: Comment.h:975
The FormatStyle is used to configure the formatting to follow specific guidelines.
Definition: Format.h:55