1169 lines · cpp
1//===--- CommentToXML.cpp - Convert comments to XML representation --------===//2//3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.4// See https://llvm.org/LICENSE.txt for license information.5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception6//7//===----------------------------------------------------------------------===//8 9#include "clang/Index/CommentToXML.h"10#include "clang/AST/ASTContext.h"11#include "clang/AST/Attr.h"12#include "clang/AST/Comment.h"13#include "clang/AST/CommentVisitor.h"14#include "clang/Basic/IdentifierTable.h"15#include "clang/Basic/SourceManager.h"16#include "clang/Format/Format.h"17#include "clang/Index/USRGeneration.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 vararg29/// 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 else41 LHSIndex = LHS->getParamIndex();42 }43 if (RHS->isParamIndexValid()) {44 if (RHS->isVarArgParam())45 RHSIndex = UINT_MAX - 1;46 else47 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;90 SmallVector<const BlockCommandComment *, 4> Returns;91 SmallVector<const ParamCommandComment *, 8> Params;92 SmallVector<const TParamCommandComment *, 4> TParams;93 llvm::TinyPtrVector<const BlockCommandComment *> Exceptions;94 SmallVector<const BlockContentComment *, 8> MiscBlocks;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 order192 // 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 else214 Result << "/>";215}216 217class CommentASTToHTMLConverter :218 public ConstCommentVisitor<CommentASTToHTMLConverter> {219public:220 /// \param Str accumulator for HTML.221 CommentASTToHTMLConverter(const FullComment *FC,222 SmallVectorImpl<char> &Str,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 some247 /// 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 namespace260 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 else374 Result << "<dd class=\"param-descr-index-"375 << C->getParamIndex()376 << "\">";377 } else378 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 else392 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 else407 Result << "<dd class=\"tparam-descr-index-other\">";408 } else409 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}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 << "&";504 break;505 case '<':506 Result << "<";507 break;508 case '>':509 Result << ">";510 break;511 case '"':512 Result << """;513 break;514 case '\'':515 Result << "'";516 break;517 case '/':518 Result << "/";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,533 SmallVectorImpl<char> &Str,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,565 SmallString<128> &Declaration);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,578 SmallVectorImpl<char> &Str) {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(591 const DeclInfo *DI, SmallString<128> &Declaration) {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 599 format::FormatStyle Style = format::getLLVMStyle();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 namespace610 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 {665 SmallString<32> Tag;666 {667 llvm::raw_svector_ostream TagOS(Tag);668 printHTMLStartTagComment(C, TagOS);669 }670 appendToResultWithCDATAEscaping(Tag);671 }672 Result << "</rawHTML>";673}674 675void676CommentASTToXMLConverter::visitHTMLEndTagComment(const HTMLEndTagComment *C) {677 Result << "<rawHTML";678 if (C->isMalformed())679 Result << " isMalformed=\"1\"";680 Result << "></" << C->getTagName() << "></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 else697 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 else761 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(820 const VerbatimBlockLineComment *C) {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()) {838 case DeclInfo::OtherKind:839 RootEndTag = "</Other>";840 Result << "<Other";841 break;842 case DeclInfo::FunctionKind:843 RootEndTag = "</Function>";844 Result << "<Function";845 switch (DI->TemplateKind) {846 case DeclInfo::NotTemplate:847 break;848 case DeclInfo::Template:849 Result << " templateKind=\"template\"";850 break;851 case DeclInfo::TemplateSpecialization:852 Result << " templateKind=\"specialization\"";853 break;854 case DeclInfo::TemplatePartialSpecialization: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;863 case DeclInfo::ClassKind:864 RootEndTag = "</Class>";865 Result << "<Class";866 switch (DI->TemplateKind) {867 case DeclInfo::NotTemplate:868 break;869 case DeclInfo::Template:870 Result << " templateKind=\"template\"";871 break;872 case DeclInfo::TemplateSpecialization:873 Result << " templateKind=\"specialization\"";874 break;875 case DeclInfo::TemplatePartialSpecialization:876 Result << " templateKind=\"partialSpecialization\"";877 break;878 }879 break;880 case DeclInfo::VariableKind:881 RootEndTag = "</Variable>";882 Result << "<Variable";883 break;884 case DeclInfo::NamespaceKind:885 RootEndTag = "</Namespace>";886 Result << "<Namespace";887 break;888 case DeclInfo::TypedefKind:889 RootEndTag = "</Typedef>";890 Result << "<Typedef";891 break;892 case DeclInfo::EnumKind:893 RootEndTag = "</Enum>";894 Result << "<Enum";895 break;896 }897 898 {899 // Print line and column number.900 SourceLocation Loc = DI->CurrentDecl->getLocation();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><anonymous></Name>";932 933 {934 // Print USR.935 SmallString<128> USR;936 generateUSRForDecl(DI->CommentDecl, 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>";958 SmallString<128> 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 << "&";1101 break;1102 case '<':1103 Result << "<";1104 break;1105 case '>':1106 Result << ">";1107 break;1108 case '"':1109 Result << """;1110 break;1111 case '\'':1112 Result << "'";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 1143CommentToXMLConverter::CommentToXMLConverter() {}1144CommentToXMLConverter::~CommentToXMLConverter() {}1145 1146void CommentToXMLConverter::convertCommentToHTML(const FullComment *FC,1147 SmallVectorImpl<char> &HTML,1148 const ASTContext &Context) {1149 CommentASTToHTMLConverter Converter(FC, HTML,1150 Context.getCommentCommandTraits());1151 Converter.visit(FC);1152}1153 1154void CommentToXMLConverter::convertHTMLTagNodeToText(1155 const comments::HTMLTagComment *HTC, SmallVectorImpl<char> &Text,1156 const ASTContext &Context) {1157 CommentASTToHTMLConverter Converter(nullptr, Text,1158 Context.getCommentCommandTraits());1159 Converter.visit(HTC);1160}1161 1162void CommentToXMLConverter::convertCommentToXML(const FullComment *FC,1163 SmallVectorImpl<char> &XML,1164 const ASTContext &Context) {1165 CommentASTToXMLConverter Converter(FC, XML, Context.getCommentCommandTraits(),1166 Context.getSourceManager());1167 Converter.visit(FC);1168}1169