clang 22.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"
16#include "clang/Format/Format.h"
18#include "llvm/ADT/StringExtras.h"
19#include "llvm/ADT/TinyPtrVector.h"
20#include "llvm/Support/raw_ostream.h"
21
22using namespace clang;
23using namespace clang::comments;
24using namespace clang::index;
25
26namespace {
27
28/// This comparison will sort parameters with valid index by index, then vararg
29/// parameters, and invalid (unresolved) parameters last.
30class ParamCommandCommentCompareIndex {
31public:
32 bool operator()(const ParamCommandComment *LHS,
33 const ParamCommandComment *RHS) const {
34 unsigned LHSIndex = UINT_MAX;
35 unsigned RHSIndex = UINT_MAX;
36
37 if (LHS->isParamIndexValid()) {
38 if (LHS->isVarArgParam())
39 LHSIndex = UINT_MAX - 1;
40 else
41 LHSIndex = LHS->getParamIndex();
42 }
43 if (RHS->isParamIndexValid()) {
44 if (RHS->isVarArgParam())
45 RHSIndex = UINT_MAX - 1;
46 else
47 RHSIndex = RHS->getParamIndex();
48 }
49 return LHSIndex < RHSIndex;
50 }
51};
52
53/// This comparison will sort template parameters in the following order:
54/// \li real template parameters (depth = 1) in index order;
55/// \li all other names (depth > 1);
56/// \li unresolved names.
57class TParamCommandCommentComparePosition {
58public:
59 bool operator()(const TParamCommandComment *LHS,
60 const TParamCommandComment *RHS) const {
61 // Sort unresolved names last.
62 if (!LHS->isPositionValid())
63 return false;
64 if (!RHS->isPositionValid())
65 return true;
66
67 if (LHS->getDepth() > 1)
68 return false;
69 if (RHS->getDepth() > 1)
70 return true;
71
72 // Sort template parameters in index order.
73 if (LHS->getDepth() == 1 && RHS->getDepth() == 1)
74 return LHS->getIndex(0) < RHS->getIndex(0);
75
76 // Leave all other names in source order.
77 return true;
78 }
79};
80
81/// Separate parts of a FullComment.
82struct FullCommentParts {
83 /// Take a full comment apart and initialize members accordingly.
84 FullCommentParts(const FullComment *C,
85 const CommandTraits &Traits);
86
87 const BlockContentComment *Brief;
88 const BlockContentComment *Headerfile;
89 const ParagraphComment *FirstParagraph;
93 llvm::TinyPtrVector<const BlockCommandComment *> Exceptions;
95};
96
97FullCommentParts::FullCommentParts(const FullComment *C,
98 const CommandTraits &Traits) :
99 Brief(nullptr), Headerfile(nullptr), FirstParagraph(nullptr) {
100 for (Comment::child_iterator I = C->child_begin(), E = C->child_end();
101 I != E; ++I) {
102 const Comment *Child = *I;
103 if (!Child)
104 continue;
105 switch (Child->getCommentKind()) {
106 case CommentKind::None:
107 continue;
108
109 case CommentKind::ParagraphComment: {
110 const ParagraphComment *PC = cast<ParagraphComment>(Child);
111 if (PC->isWhitespace())
112 break;
113 if (!FirstParagraph)
114 FirstParagraph = PC;
115
116 MiscBlocks.push_back(PC);
117 break;
118 }
119
120 case CommentKind::BlockCommandComment: {
121 const BlockCommandComment *BCC = cast<BlockCommandComment>(Child);
122 const CommandInfo *Info = Traits.getCommandInfo(BCC->getCommandID());
123 if (!Brief && Info->IsBriefCommand) {
124 Brief = BCC;
125 break;
126 }
127 if (!Headerfile && Info->IsHeaderfileCommand) {
128 Headerfile = BCC;
129 break;
130 }
131 if (Info->IsReturnsCommand) {
132 Returns.push_back(BCC);
133 break;
134 }
135 if (Info->IsThrowsCommand) {
136 Exceptions.push_back(BCC);
137 break;
138 }
139 MiscBlocks.push_back(BCC);
140 break;
141 }
142
143 case CommentKind::ParamCommandComment: {
144 const ParamCommandComment *PCC = cast<ParamCommandComment>(Child);
145 if (!PCC->hasParamName())
146 break;
147
148 if (!PCC->isDirectionExplicit() && !PCC->hasNonWhitespaceParagraph())
149 break;
150
151 Params.push_back(PCC);
152 break;
153 }
154
155 case CommentKind::TParamCommandComment: {
156 const TParamCommandComment *TPCC = cast<TParamCommandComment>(Child);
157 if (!TPCC->hasParamName())
158 break;
159
160 if (!TPCC->hasNonWhitespaceParagraph())
161 break;
162
163 TParams.push_back(TPCC);
164 break;
165 }
166
167 case CommentKind::VerbatimBlockComment:
168 MiscBlocks.push_back(cast<BlockCommandComment>(Child));
169 break;
170
171 case CommentKind::VerbatimLineComment: {
172 const VerbatimLineComment *VLC = cast<VerbatimLineComment>(Child);
173 const CommandInfo *Info = Traits.getCommandInfo(VLC->getCommandID());
174 if (!Info->IsDeclarationCommand)
175 MiscBlocks.push_back(VLC);
176 break;
177 }
178
179 case CommentKind::TextComment:
180 case CommentKind::InlineCommandComment:
181 case CommentKind::HTMLStartTagComment:
182 case CommentKind::HTMLEndTagComment:
183 case CommentKind::VerbatimBlockLineComment:
184 case CommentKind::FullComment:
185 llvm_unreachable("AST node of this kind can't be a child of "
186 "a FullComment");
187 }
188 }
189
190 // Sort params in order they are declared in the function prototype.
191 // Unresolved parameters are put at the end of the list in the same order
192 // they were seen in the comment.
193 llvm::stable_sort(Params, ParamCommandCommentCompareIndex());
194 llvm::stable_sort(TParams, TParamCommandCommentComparePosition());
195}
196
197void printHTMLStartTagComment(const HTMLStartTagComment *C,
198 llvm::raw_svector_ostream &Result) {
199 Result << "<" << C->getTagName();
200
201 if (C->getNumAttrs() != 0) {
202 for (unsigned i = 0, e = C->getNumAttrs(); i != e; i++) {
203 Result << " ";
204 const HTMLStartTagComment::Attribute &Attr = C->getAttr(i);
205 Result << Attr.Name;
206 if (!Attr.Value.empty())
207 Result << "=\"" << Attr.Value << "\"";
208 }
209 }
210
211 if (!C->isSelfClosing())
212 Result << ">";
213 else
214 Result << "/>";
215}
216
217class CommentASTToHTMLConverter :
218 public ConstCommentVisitor<CommentASTToHTMLConverter> {
219public:
220 /// \param Str accumulator for HTML.
221 CommentASTToHTMLConverter(const FullComment *FC,
223 const CommandTraits &Traits) :
224 FC(FC), Result(Str), Traits(Traits)
225 { }
226
227 // Inline content.
228 void visitTextComment(const TextComment *C);
229 void visitInlineCommandComment(const InlineCommandComment *C);
230 void visitHTMLStartTagComment(const HTMLStartTagComment *C);
231 void visitHTMLEndTagComment(const HTMLEndTagComment *C);
232
233 // Block content.
234 void visitParagraphComment(const ParagraphComment *C);
235 void visitBlockCommandComment(const BlockCommandComment *C);
236 void visitParamCommandComment(const ParamCommandComment *C);
237 void visitTParamCommandComment(const TParamCommandComment *C);
238 void visitVerbatimBlockComment(const VerbatimBlockComment *C);
239 void visitVerbatimBlockLineComment(const VerbatimBlockLineComment *C);
240 void visitVerbatimLineComment(const VerbatimLineComment *C);
241
242 void visitFullComment(const FullComment *C);
243
244 // Helpers.
245
246 /// Convert a paragraph that is not a block by itself (an argument to some
247 /// command).
248 void visitNonStandaloneParagraphComment(const ParagraphComment *C);
249
250 void appendToResultWithHTMLEscaping(StringRef S);
251
252private:
253 const FullComment *FC;
254 /// Output stream for HTML.
255 llvm::raw_svector_ostream Result;
256
257 const CommandTraits &Traits;
258};
259} // end unnamed namespace
260
261void CommentASTToHTMLConverter::visitTextComment(const TextComment *C) {
262 appendToResultWithHTMLEscaping(C->getText());
263}
264
265void CommentASTToHTMLConverter::visitInlineCommandComment(
266 const InlineCommandComment *C) {
267 // Nothing to render if no arguments supplied.
268 if (C->getNumArgs() == 0)
269 return;
270
271 // Nothing to render if argument is empty.
272 StringRef Arg0 = C->getArgText(0);
273 if (Arg0.empty())
274 return;
275
276 switch (C->getRenderKind()) {
277 case InlineCommandRenderKind::Normal:
278 for (unsigned i = 0, e = C->getNumArgs(); i != e; ++i) {
279 appendToResultWithHTMLEscaping(C->getArgText(i));
280 Result << " ";
281 }
282 return;
283
284 case InlineCommandRenderKind::Bold:
285 assert(C->getNumArgs() == 1);
286 Result << "<b>";
287 appendToResultWithHTMLEscaping(Arg0);
288 Result << "</b>";
289 return;
290 case InlineCommandRenderKind::Monospaced:
291 assert(C->getNumArgs() == 1);
292 Result << "<tt>";
293 appendToResultWithHTMLEscaping(Arg0);
294 Result<< "</tt>";
295 return;
296 case InlineCommandRenderKind::Emphasized:
297 assert(C->getNumArgs() == 1);
298 Result << "<em>";
299 appendToResultWithHTMLEscaping(Arg0);
300 Result << "</em>";
301 return;
302 case InlineCommandRenderKind::Anchor:
303 assert(C->getNumArgs() == 1);
304 Result << "<span id=\"" << Arg0 << "\"></span>";
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(
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}
486
487void CommentASTToHTMLConverter::visitNonStandaloneParagraphComment(
488 const ParagraphComment *C) {
489 if (!C)
490 return;
491
492 for (Comment::child_iterator I = C->child_begin(), E = C->child_end();
493 I != E; ++I) {
494 visit(*I);
495 }
496}
497
498void CommentASTToHTMLConverter::appendToResultWithHTMLEscaping(StringRef S) {
499 for (StringRef::iterator I = S.begin(), E = S.end(); I != E; ++I) {
500 const char C = *I;
501 switch (C) {
502 case '&':
503 Result << "&amp;";
504 break;
505 case '<':
506 Result << "&lt;";
507 break;
508 case '>':
509 Result << "&gt;";
510 break;
511 case '"':
512 Result << "&quot;";
513 break;
514 case '\'':
515 Result << "&#39;";
516 break;
517 case '/':
518 Result << "&#47;";
519 break;
520 default:
521 Result << C;
522 break;
523 }
524 }
525}
526
527namespace {
528class CommentASTToXMLConverter :
529 public ConstCommentVisitor<CommentASTToXMLConverter> {
530public:
531 /// \param Str accumulator for XML.
532 CommentASTToXMLConverter(const FullComment *FC,
534 const CommandTraits &Traits,
535 const SourceManager &SM) :
536 FC(FC), Result(Str), Traits(Traits), SM(SM) { }
537
538 // Inline content.
539 void visitTextComment(const TextComment *C);
540 void visitInlineCommandComment(const InlineCommandComment *C);
541 void visitHTMLStartTagComment(const HTMLStartTagComment *C);
542 void visitHTMLEndTagComment(const HTMLEndTagComment *C);
543
544 // Block content.
545 void visitParagraphComment(const ParagraphComment *C);
546
547 void appendParagraphCommentWithKind(const ParagraphComment *C,
548 StringRef ParagraphKind,
549 StringRef PrependBodyText);
550
551 void visitBlockCommandComment(const BlockCommandComment *C);
552 void visitParamCommandComment(const ParamCommandComment *C);
553 void visitTParamCommandComment(const TParamCommandComment *C);
554 void visitVerbatimBlockComment(const VerbatimBlockComment *C);
555 void visitVerbatimBlockLineComment(const VerbatimBlockLineComment *C);
556 void visitVerbatimLineComment(const VerbatimLineComment *C);
557
558 void visitFullComment(const FullComment *C);
559
560 // Helpers.
561 void appendToResultWithXMLEscaping(StringRef S);
562 void appendToResultWithCDATAEscaping(StringRef S);
563
564 void formatTextOfDeclaration(const DeclInfo *DI,
566
567private:
568 const FullComment *FC;
569
570 /// Output stream for XML.
571 llvm::raw_svector_ostream Result;
572
573 const CommandTraits &Traits;
574 const SourceManager &SM;
575};
576
577void getSourceTextOfDeclaration(const DeclInfo *ThisDecl,
579 ASTContext &Context = ThisDecl->CurrentDecl->getASTContext();
580 const LangOptions &LangOpts = Context.getLangOpts();
581 llvm::raw_svector_ostream OS(Str);
582 PrintingPolicy PPolicy(LangOpts);
583 PPolicy.PolishForDeclaration = true;
584 PPolicy.TerseOutput = true;
585 PPolicy.ConstantsAsWritten = true;
586 ThisDecl->CurrentDecl->print(OS, PPolicy,
587 /*Indentation*/0, /*PrintInstantiation*/false);
588}
589
590void CommentASTToXMLConverter::formatTextOfDeclaration(
592 // Formatting API expects null terminated input string.
593 StringRef StringDecl(Declaration.c_str(), Declaration.size());
594
595 // Formatter specific code.
596 unsigned Offset = 0;
597 unsigned Length = Declaration.size();
598
600 Style.FixNamespaceComments = false;
601 tooling::Replacements Replaces =
602 reformat(Style, StringDecl, tooling::Range(Offset, Length), "xmldecl.xd");
603 auto FormattedStringDecl = applyAllReplacements(StringDecl, Replaces);
604 if (static_cast<bool>(FormattedStringDecl)) {
605 Declaration = *FormattedStringDecl;
606 }
607}
608
609} // end unnamed namespace
610
611void CommentASTToXMLConverter::visitTextComment(const TextComment *C) {
612 appendToResultWithXMLEscaping(C->getText());
613}
614
615void CommentASTToXMLConverter::visitInlineCommandComment(
616 const InlineCommandComment *C) {
617 // Nothing to render if no arguments supplied.
618 if (C->getNumArgs() == 0)
619 return;
620
621 // Nothing to render if argument is empty.
622 StringRef Arg0 = C->getArgText(0);
623 if (Arg0.empty())
624 return;
625
626 switch (C->getRenderKind()) {
627 case InlineCommandRenderKind::Normal:
628 for (unsigned i = 0, e = C->getNumArgs(); i != e; ++i) {
629 appendToResultWithXMLEscaping(C->getArgText(i));
630 Result << " ";
631 }
632 return;
633 case InlineCommandRenderKind::Bold:
634 assert(C->getNumArgs() == 1);
635 Result << "<bold>";
636 appendToResultWithXMLEscaping(Arg0);
637 Result << "</bold>";
638 return;
639 case InlineCommandRenderKind::Monospaced:
640 assert(C->getNumArgs() == 1);
641 Result << "<monospaced>";
642 appendToResultWithXMLEscaping(Arg0);
643 Result << "</monospaced>";
644 return;
645 case InlineCommandRenderKind::Emphasized:
646 assert(C->getNumArgs() == 1);
647 Result << "<emphasized>";
648 appendToResultWithXMLEscaping(Arg0);
649 Result << "</emphasized>";
650 return;
651 case InlineCommandRenderKind::Anchor:
652 assert(C->getNumArgs() == 1);
653 Result << "<anchor id=\"" << Arg0 << "\"></anchor>";
654 return;
655 }
656}
657
658void CommentASTToXMLConverter::visitHTMLStartTagComment(
659 const HTMLStartTagComment *C) {
660 Result << "<rawHTML";
661 if (C->isMalformed())
662 Result << " isMalformed=\"1\"";
663 Result << ">";
664 {
666 {
667 llvm::raw_svector_ostream TagOS(Tag);
668 printHTMLStartTagComment(C, TagOS);
669 }
670 appendToResultWithCDATAEscaping(Tag);
671 }
672 Result << "</rawHTML>";
673}
674
675void
676CommentASTToXMLConverter::visitHTMLEndTagComment(const HTMLEndTagComment *C) {
677 Result << "<rawHTML";
678 if (C->isMalformed())
679 Result << " isMalformed=\"1\"";
680 Result << ">&lt;/" << C->getTagName() << "&gt;</rawHTML>";
681}
682
683void CommentASTToXMLConverter::visitParagraphComment(
684 const ParagraphComment *C) {
685 appendParagraphCommentWithKind(C, StringRef(), StringRef());
686}
687
688void CommentASTToXMLConverter::appendParagraphCommentWithKind(
689 const ParagraphComment *C, StringRef ParagraphKind,
690 StringRef PrependBodyText) {
691 if (C->isWhitespace() && PrependBodyText.empty())
692 return;
693
694 if (ParagraphKind.empty())
695 Result << "<Para>";
696 else
697 Result << "<Para kind=\"" << ParagraphKind << "\">";
698
699 if (!PrependBodyText.empty())
700 Result << PrependBodyText << " ";
701
702 for (Comment::child_iterator I = C->child_begin(), E = C->child_end(); I != E;
703 ++I) {
704 visit(*I);
705 }
706 Result << "</Para>";
707}
708
709void CommentASTToXMLConverter::visitBlockCommandComment(
710 const BlockCommandComment *C) {
711 StringRef ParagraphKind;
712 StringRef ExceptionType;
713
714 const unsigned CommandID = C->getCommandID();
715 const CommandInfo *Info = Traits.getCommandInfo(CommandID);
716 if (Info->IsThrowsCommand && C->getNumArgs() > 0) {
717 ExceptionType = C->getArgText(0);
718 }
719
720 switch (CommandID) {
721 case CommandTraits::KCI_attention:
722 case CommandTraits::KCI_author:
723 case CommandTraits::KCI_authors:
724 case CommandTraits::KCI_bug:
725 case CommandTraits::KCI_copyright:
726 case CommandTraits::KCI_date:
727 case CommandTraits::KCI_invariant:
728 case CommandTraits::KCI_note:
729 case CommandTraits::KCI_post:
730 case CommandTraits::KCI_pre:
731 case CommandTraits::KCI_remark:
732 case CommandTraits::KCI_remarks:
733 case CommandTraits::KCI_sa:
734 case CommandTraits::KCI_see:
735 case CommandTraits::KCI_since:
736 case CommandTraits::KCI_todo:
737 case CommandTraits::KCI_version:
738 case CommandTraits::KCI_warning:
739 ParagraphKind = C->getCommandName(Traits);
740 break;
741 default:
742 break;
743 }
744
745 appendParagraphCommentWithKind(C->getParagraph(), ParagraphKind,
746 ExceptionType);
747}
748
749void CommentASTToXMLConverter::visitParamCommandComment(
750 const ParamCommandComment *C) {
751 Result << "<Parameter><Name>";
752 appendToResultWithXMLEscaping(C->isParamIndexValid()
753 ? C->getParamName(FC)
754 : C->getParamNameAsWritten());
755 Result << "</Name>";
756
757 if (C->isParamIndexValid()) {
758 if (C->isVarArgParam())
759 Result << "<IsVarArg />";
760 else
761 Result << "<Index>" << C->getParamIndex() << "</Index>";
762 }
763
764 Result << "<Direction isExplicit=\"" << C->isDirectionExplicit() << "\">";
765 switch (C->getDirection()) {
766 case ParamCommandPassDirection::In:
767 Result << "in";
768 break;
769 case ParamCommandPassDirection::Out:
770 Result << "out";
771 break;
772 case ParamCommandPassDirection::InOut:
773 Result << "in,out";
774 break;
775 }
776 Result << "</Direction><Discussion>";
777 visit(C->getParagraph());
778 Result << "</Discussion></Parameter>";
779}
780
781void CommentASTToXMLConverter::visitTParamCommandComment(
782 const TParamCommandComment *C) {
783 Result << "<Parameter><Name>";
784 appendToResultWithXMLEscaping(C->isPositionValid() ? C->getParamName(FC)
785 : C->getParamNameAsWritten());
786 Result << "</Name>";
787
788 if (C->isPositionValid() && C->getDepth() == 1) {
789 Result << "<Index>" << C->getIndex(0) << "</Index>";
790 }
791
792 Result << "<Discussion>";
793 visit(C->getParagraph());
794 Result << "</Discussion></Parameter>";
795}
796
797void CommentASTToXMLConverter::visitVerbatimBlockComment(
798 const VerbatimBlockComment *C) {
799 unsigned NumLines = C->getNumLines();
800 if (NumLines == 0)
801 return;
802
803 switch (C->getCommandID()) {
804 case CommandTraits::KCI_code:
805 Result << "<Verbatim xml:space=\"preserve\" kind=\"code\">";
806 break;
807 default:
808 Result << "<Verbatim xml:space=\"preserve\" kind=\"verbatim\">";
809 break;
810 }
811 for (unsigned i = 0; i != NumLines; ++i) {
812 appendToResultWithXMLEscaping(C->getText(i));
813 if (i + 1 != NumLines)
814 Result << '\n';
815 }
816 Result << "</Verbatim>";
817}
818
819void CommentASTToXMLConverter::visitVerbatimBlockLineComment(
821 llvm_unreachable("should not see this AST node");
822}
823
824void CommentASTToXMLConverter::visitVerbatimLineComment(
825 const VerbatimLineComment *C) {
826 Result << "<Verbatim xml:space=\"preserve\" kind=\"verbatim\">";
827 appendToResultWithXMLEscaping(C->getText());
828 Result << "</Verbatim>";
829}
830
831void CommentASTToXMLConverter::visitFullComment(const FullComment *C) {
832 FullCommentParts Parts(C, Traits);
833
834 const DeclInfo *DI = C->getDeclInfo();
835 StringRef RootEndTag;
836 if (DI) {
837 switch (DI->getKind()) {
839 RootEndTag = "</Other>";
840 Result << "<Other";
841 break;
843 RootEndTag = "</Function>";
844 Result << "<Function";
845 switch (DI->TemplateKind) {
847 break;
849 Result << " templateKind=\"template\"";
850 break;
852 Result << " templateKind=\"specialization\"";
853 break;
855 llvm_unreachable("partial specializations of functions "
856 "are not allowed in C++");
857 }
858 if (DI->IsInstanceMethod)
859 Result << " isInstanceMethod=\"1\"";
860 if (DI->IsClassMethod)
861 Result << " isClassMethod=\"1\"";
862 break;
864 RootEndTag = "</Class>";
865 Result << "<Class";
866 switch (DI->TemplateKind) {
868 break;
870 Result << " templateKind=\"template\"";
871 break;
873 Result << " templateKind=\"specialization\"";
874 break;
876 Result << " templateKind=\"partialSpecialization\"";
877 break;
878 }
879 break;
881 RootEndTag = "</Variable>";
882 Result << "<Variable";
883 break;
885 RootEndTag = "</Namespace>";
886 Result << "<Namespace";
887 break;
889 RootEndTag = "</Typedef>";
890 Result << "<Typedef";
891 break;
893 RootEndTag = "</Enum>";
894 Result << "<Enum";
895 break;
896 }
897
898 {
899 // Print line and column number.
901 FileIDAndOffset LocInfo = SM.getDecomposedLoc(Loc);
902 FileID FID = LocInfo.first;
903 unsigned FileOffset = LocInfo.second;
904
905 if (FID.isValid()) {
906 if (OptionalFileEntryRef FE = SM.getFileEntryRefForID(FID)) {
907 Result << " file=\"";
908 appendToResultWithXMLEscaping(FE->getName());
909 Result << "\"";
910 }
911 Result << " line=\"" << SM.getLineNumber(FID, FileOffset)
912 << "\" column=\"" << SM.getColumnNumber(FID, FileOffset)
913 << "\"";
914 }
915 }
916
917 // Finish the root tag.
918 Result << ">";
919
920 bool FoundName = false;
921 if (const NamedDecl *ND = dyn_cast<NamedDecl>(DI->CommentDecl)) {
922 if (DeclarationName DeclName = ND->getDeclName()) {
923 Result << "<Name>";
924 std::string Name = DeclName.getAsString();
925 appendToResultWithXMLEscaping(Name);
926 FoundName = true;
927 Result << "</Name>";
928 }
929 }
930 if (!FoundName)
931 Result << "<Name>&lt;anonymous&gt;</Name>";
932
933 {
934 // Print USR.
937 if (!USR.empty()) {
938 Result << "<USR>";
939 appendToResultWithXMLEscaping(USR);
940 Result << "</USR>";
941 }
942 }
943 } else {
944 // No DeclInfo -- just emit some root tag and name tag.
945 RootEndTag = "</Other>";
946 Result << "<Other><Name>unknown</Name>";
947 }
948
949 if (Parts.Headerfile) {
950 Result << "<Headerfile>";
951 visit(Parts.Headerfile);
952 Result << "</Headerfile>";
953 }
954
955 {
956 // Pretty-print the declaration.
957 Result << "<Declaration>";
959 getSourceTextOfDeclaration(DI, Declaration);
960 formatTextOfDeclaration(DI, Declaration);
961 appendToResultWithXMLEscaping(Declaration);
962 Result << "</Declaration>";
963 }
964
965 bool FirstParagraphIsBrief = false;
966 if (Parts.Brief) {
967 Result << "<Abstract>";
968 visit(Parts.Brief);
969 Result << "</Abstract>";
970 } else if (Parts.FirstParagraph) {
971 Result << "<Abstract>";
972 visit(Parts.FirstParagraph);
973 Result << "</Abstract>";
974 FirstParagraphIsBrief = true;
975 }
976
977 if (Parts.TParams.size() != 0) {
978 Result << "<TemplateParameters>";
979 for (unsigned i = 0, e = Parts.TParams.size(); i != e; ++i)
980 visit(Parts.TParams[i]);
981 Result << "</TemplateParameters>";
982 }
983
984 if (Parts.Params.size() != 0) {
985 Result << "<Parameters>";
986 for (unsigned i = 0, e = Parts.Params.size(); i != e; ++i)
987 visit(Parts.Params[i]);
988 Result << "</Parameters>";
989 }
990
991 if (Parts.Exceptions.size() != 0) {
992 Result << "<Exceptions>";
993 for (unsigned i = 0, e = Parts.Exceptions.size(); i != e; ++i)
994 visit(Parts.Exceptions[i]);
995 Result << "</Exceptions>";
996 }
997
998 if (Parts.Returns.size() != 0) {
999 Result << "<ResultDiscussion>";
1000 for (unsigned i = 0, e = Parts.Returns.size(); i != e; ++i)
1001 visit(Parts.Returns[i]);
1002 Result << "</ResultDiscussion>";
1003 }
1004
1005 if (DI->CommentDecl->hasAttrs()) {
1006 const AttrVec &Attrs = DI->CommentDecl->getAttrs();
1007 for (unsigned i = 0, e = Attrs.size(); i != e; i++) {
1008 const AvailabilityAttr *AA = dyn_cast<AvailabilityAttr>(Attrs[i]);
1009 if (!AA) {
1010 if (const DeprecatedAttr *DA = dyn_cast<DeprecatedAttr>(Attrs[i])) {
1011 if (DA->getMessage().empty())
1012 Result << "<Deprecated/>";
1013 else {
1014 Result << "<Deprecated>";
1015 appendToResultWithXMLEscaping(DA->getMessage());
1016 Result << "</Deprecated>";
1017 }
1018 }
1019 else if (const UnavailableAttr *UA = dyn_cast<UnavailableAttr>(Attrs[i])) {
1020 if (UA->getMessage().empty())
1021 Result << "<Unavailable/>";
1022 else {
1023 Result << "<Unavailable>";
1024 appendToResultWithXMLEscaping(UA->getMessage());
1025 Result << "</Unavailable>";
1026 }
1027 }
1028 continue;
1029 }
1030
1031 // 'availability' attribute.
1032 Result << "<Availability";
1033 StringRef Distribution;
1034 if (AA->getPlatform()) {
1035 Distribution = AvailabilityAttr::getPrettyPlatformName(
1036 AA->getPlatform()->getName());
1037 if (Distribution.empty())
1038 Distribution = AA->getPlatform()->getName();
1039 }
1040 Result << " distribution=\"" << Distribution << "\">";
1041 VersionTuple IntroducedInVersion = AA->getIntroduced();
1042 if (!IntroducedInVersion.empty()) {
1043 Result << "<IntroducedInVersion>"
1044 << IntroducedInVersion.getAsString()
1045 << "</IntroducedInVersion>";
1046 }
1047 VersionTuple DeprecatedInVersion = AA->getDeprecated();
1048 if (!DeprecatedInVersion.empty()) {
1049 Result << "<DeprecatedInVersion>"
1050 << DeprecatedInVersion.getAsString()
1051 << "</DeprecatedInVersion>";
1052 }
1053 VersionTuple RemovedAfterVersion = AA->getObsoleted();
1054 if (!RemovedAfterVersion.empty()) {
1055 Result << "<RemovedAfterVersion>"
1056 << RemovedAfterVersion.getAsString()
1057 << "</RemovedAfterVersion>";
1058 }
1059 StringRef DeprecationSummary = AA->getMessage();
1060 if (!DeprecationSummary.empty()) {
1061 Result << "<DeprecationSummary>";
1062 appendToResultWithXMLEscaping(DeprecationSummary);
1063 Result << "</DeprecationSummary>";
1064 }
1065 if (AA->getUnavailable())
1066 Result << "<Unavailable/>";
1067
1068 IdentifierInfo *Environment = AA->getEnvironment();
1069 if (Environment) {
1070 Result << "<Environment>" << Environment->getName() << "</Environment>";
1071 }
1072 Result << "</Availability>";
1073 }
1074 }
1075
1076 {
1077 bool StartTagEmitted = false;
1078 for (unsigned i = 0, e = Parts.MiscBlocks.size(); i != e; ++i) {
1079 const Comment *C = Parts.MiscBlocks[i];
1080 if (FirstParagraphIsBrief && C == Parts.FirstParagraph)
1081 continue;
1082 if (!StartTagEmitted) {
1083 Result << "<Discussion>";
1084 StartTagEmitted = true;
1085 }
1086 visit(C);
1087 }
1088 if (StartTagEmitted)
1089 Result << "</Discussion>";
1090 }
1091
1092 Result << RootEndTag;
1093}
1094
1095void CommentASTToXMLConverter::appendToResultWithXMLEscaping(StringRef S) {
1096 for (StringRef::iterator I = S.begin(), E = S.end(); I != E; ++I) {
1097 const char C = *I;
1098 switch (C) {
1099 case '&':
1100 Result << "&amp;";
1101 break;
1102 case '<':
1103 Result << "&lt;";
1104 break;
1105 case '>':
1106 Result << "&gt;";
1107 break;
1108 case '"':
1109 Result << "&quot;";
1110 break;
1111 case '\'':
1112 Result << "&apos;";
1113 break;
1114 default:
1115 Result << C;
1116 break;
1117 }
1118 }
1119}
1120
1121void CommentASTToXMLConverter::appendToResultWithCDATAEscaping(StringRef S) {
1122 if (S.empty())
1123 return;
1124
1125 Result << "<![CDATA[";
1126 while (!S.empty()) {
1127 size_t Pos = S.find("]]>");
1128 if (Pos == 0) {
1129 Result << "]]]]><![CDATA[>";
1130 S = S.drop_front(3);
1131 continue;
1132 }
1133 if (Pos == StringRef::npos)
1134 Pos = S.size();
1135
1136 Result << S.substr(0, Pos);
1137
1138 S = S.drop_front(Pos);
1139 }
1140 Result << "]]>";
1141}
1142
1145
1148 const ASTContext &Context) {
1149 CommentASTToHTMLConverter Converter(FC, HTML,
1150 Context.getCommentCommandTraits());
1151 Converter.visit(FC);
1152}
1153
1156 const ASTContext &Context) {
1157 CommentASTToHTMLConverter Converter(nullptr, Text,
1158 Context.getCommentCommandTraits());
1159 Converter.visit(HTC);
1160}
1161
1164 const ASTContext &Context) {
1165 CommentASTToXMLConverter Converter(FC, XML, Context.getCommentCommandTraits(),
1166 Context.getSourceManager());
1167 Converter.visit(FC);
1168}
Defines the clang::ASTContext interface.
Expr * E
Various functions to configurably format source code.
Defines the clang::IdentifierInfo, clang::IdentifierTable, and clang::Selector interfaces.
#define SM(sm)
Definition: OffloadArch.cpp:16
SourceLocation Loc
Definition: SemaObjC.cpp:754
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:188
SourceManager & getSourceManager()
Definition: ASTContext.h:801
comments::CommandTraits & getCommentCommandTraits() const
Definition: ASTContext.h:1058
const LangOptions & getLangOpts() const
Definition: ASTContext.h:894
Attr - This represents one attribute.
Definition: Attr.h:44
bool hasAttrs() const
Definition: DeclBase.h:518
ASTContext & getASTContext() const LLVM_READONLY
Definition: DeclBase.cpp:524
SourceLocation getLocation() const
Definition: DeclBase.h:439
void print(raw_ostream &Out, unsigned Indentation=0, bool PrintInstantiation=false) const
AttrVec & getAttrs()
Definition: DeclBase.h:524
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:434
This represents a decl that may have a name.
Definition: Decl.h:273
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:625
Block content (contains inline content).
Definition: Comment.h:559
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:66
Comment *const * child_iterator
Definition: Comment.h:257
A full comment attached to a declaration, contains block content.
Definition: Comment.h:1104
An opening HTML tag with attributes.
Definition: Comment.h:454
Abstract class for opening and closing HTML tags.
Definition: Comment.h:412
A command with word-like arguments that is considered inline content.
Definition: Comment.h:341
A single paragraph that contains inline content.
Definition: Comment.h:576
Doxygen \param command.
Definition: Comment.h:732
bool isDirectionExplicit() const LLVM_READONLY
Definition: Comment.h:764
unsigned getParamIndex() const LLVM_READONLY
Definition: Comment.h:800
bool isParamIndexValid() const LLVM_READONLY
Definition: Comment.h:787
bool isVarArgParam() const LLVM_READONLY
Definition: Comment.h:791
Doxygen \tparam command, describes a template parameter.
Definition: Comment.h:814
bool isPositionValid() const LLVM_READONLY
Definition: Comment.h:854
unsigned getIndex(unsigned Depth) const
Definition: Comment.h:863
A verbatim block command (e.
Definition: Comment.h:900
A line of text contained in a verbatim block.
Definition: Comment.h:875
A verbatim line command.
Definition: Comment.h:951
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::LK_Cpp)
Returns a format style complying with the LLVM coding standards: http://llvm.org/docs/CodingStandards...
Definition: Format.cpp:1525
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:4024
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.
std::pair< FileID, unsigned > FileIDAndOffset
@ 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:983
unsigned TemplateKind
Is CommentDecl a template declaration.
Definition: Comment.h:1068
unsigned IsClassMethod
Is CommentDecl a static member function of C++ class or class method of ObjC class.
Definition: Comment.h:1084
@ FunctionKind
Something that we consider a "function":
Definition: Comment.h:1025
@ EnumKind
An enumeration or scoped enumeration.
Definition: Comment.h:1047
@ OtherKind
Everything else not explicitly mentioned below.
Definition: Comment.h:1015
@ NamespaceKind
A C++ namespace.
Definition: Comment.h:1040
@ VariableKind
Something that we consider a "variable":
Definition: Comment.h:1037
@ ClassKind
Something that we consider a "class":
Definition: Comment.h:1031
@ TypedefKind
A C++ typedef-name (a 'typedef' decl specifier or alias-declaration), see TypedefNameDecl.
Definition: Comment.h:1044
DeclKind getKind() const LLVM_READONLY
Definition: Comment.h:1092
unsigned IsInstanceMethod
Is CommentDecl a non-static member function of C++ class or instance method of ObjC class.
Definition: Comment.h:1078
const Decl * CommentDecl
Declaration the comment is actually attached to (in the source).
Definition: Comment.h:986
const Decl * CurrentDecl
CurrentDecl is the declaration with which the FullComment is associated.
Definition: Comment.h:996
The FormatStyle is used to configure the formatting to follow specific guidelines.
Definition: Format.h:55