Path: blob/main/contrib/llvm-project/clang/lib/Index/CommentToXML.cpp
35232 views
//===--- CommentToXML.cpp - Convert comments to XML representation --------===//1//2// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.3// See https://llvm.org/LICENSE.txt for license information.4// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception5//6//===----------------------------------------------------------------------===//78#include "clang/Index/CommentToXML.h"9#include "clang/AST/ASTContext.h"10#include "clang/AST/Attr.h"11#include "clang/AST/Comment.h"12#include "clang/AST/CommentVisitor.h"13#include "clang/Basic/FileManager.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"2122using namespace clang;23using namespace clang::comments;24using namespace clang::index;2526namespace {2728/// This comparison will sort parameters with valid index by index, then vararg29/// parameters, and invalid (unresolved) parameters last.30class ParamCommandCommentCompareIndex {31public:32bool operator()(const ParamCommandComment *LHS,33const ParamCommandComment *RHS) const {34unsigned LHSIndex = UINT_MAX;35unsigned RHSIndex = UINT_MAX;3637if (LHS->isParamIndexValid()) {38if (LHS->isVarArgParam())39LHSIndex = UINT_MAX - 1;40else41LHSIndex = LHS->getParamIndex();42}43if (RHS->isParamIndexValid()) {44if (RHS->isVarArgParam())45RHSIndex = UINT_MAX - 1;46else47RHSIndex = RHS->getParamIndex();48}49return LHSIndex < RHSIndex;50}51};5253/// 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:59bool operator()(const TParamCommandComment *LHS,60const TParamCommandComment *RHS) const {61// Sort unresolved names last.62if (!LHS->isPositionValid())63return false;64if (!RHS->isPositionValid())65return true;6667if (LHS->getDepth() > 1)68return false;69if (RHS->getDepth() > 1)70return true;7172// Sort template parameters in index order.73if (LHS->getDepth() == 1 && RHS->getDepth() == 1)74return LHS->getIndex(0) < RHS->getIndex(0);7576// Leave all other names in source order.77return true;78}79};8081/// Separate parts of a FullComment.82struct FullCommentParts {83/// Take a full comment apart and initialize members accordingly.84FullCommentParts(const FullComment *C,85const CommandTraits &Traits);8687const BlockContentComment *Brief;88const BlockContentComment *Headerfile;89const ParagraphComment *FirstParagraph;90SmallVector<const BlockCommandComment *, 4> Returns;91SmallVector<const ParamCommandComment *, 8> Params;92SmallVector<const TParamCommandComment *, 4> TParams;93llvm::TinyPtrVector<const BlockCommandComment *> Exceptions;94SmallVector<const BlockContentComment *, 8> MiscBlocks;95};9697FullCommentParts::FullCommentParts(const FullComment *C,98const CommandTraits &Traits) :99Brief(nullptr), Headerfile(nullptr), FirstParagraph(nullptr) {100for (Comment::child_iterator I = C->child_begin(), E = C->child_end();101I != E; ++I) {102const Comment *Child = *I;103if (!Child)104continue;105switch (Child->getCommentKind()) {106case CommentKind::None:107continue;108109case CommentKind::ParagraphComment: {110const ParagraphComment *PC = cast<ParagraphComment>(Child);111if (PC->isWhitespace())112break;113if (!FirstParagraph)114FirstParagraph = PC;115116MiscBlocks.push_back(PC);117break;118}119120case CommentKind::BlockCommandComment: {121const BlockCommandComment *BCC = cast<BlockCommandComment>(Child);122const CommandInfo *Info = Traits.getCommandInfo(BCC->getCommandID());123if (!Brief && Info->IsBriefCommand) {124Brief = BCC;125break;126}127if (!Headerfile && Info->IsHeaderfileCommand) {128Headerfile = BCC;129break;130}131if (Info->IsReturnsCommand) {132Returns.push_back(BCC);133break;134}135if (Info->IsThrowsCommand) {136Exceptions.push_back(BCC);137break;138}139MiscBlocks.push_back(BCC);140break;141}142143case CommentKind::ParamCommandComment: {144const ParamCommandComment *PCC = cast<ParamCommandComment>(Child);145if (!PCC->hasParamName())146break;147148if (!PCC->isDirectionExplicit() && !PCC->hasNonWhitespaceParagraph())149break;150151Params.push_back(PCC);152break;153}154155case CommentKind::TParamCommandComment: {156const TParamCommandComment *TPCC = cast<TParamCommandComment>(Child);157if (!TPCC->hasParamName())158break;159160if (!TPCC->hasNonWhitespaceParagraph())161break;162163TParams.push_back(TPCC);164break;165}166167case CommentKind::VerbatimBlockComment:168MiscBlocks.push_back(cast<BlockCommandComment>(Child));169break;170171case CommentKind::VerbatimLineComment: {172const VerbatimLineComment *VLC = cast<VerbatimLineComment>(Child);173const CommandInfo *Info = Traits.getCommandInfo(VLC->getCommandID());174if (!Info->IsDeclarationCommand)175MiscBlocks.push_back(VLC);176break;177}178179case CommentKind::TextComment:180case CommentKind::InlineCommandComment:181case CommentKind::HTMLStartTagComment:182case CommentKind::HTMLEndTagComment:183case CommentKind::VerbatimBlockLineComment:184case CommentKind::FullComment:185llvm_unreachable("AST node of this kind can't be a child of "186"a FullComment");187}188}189190// 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.193llvm::stable_sort(Params, ParamCommandCommentCompareIndex());194llvm::stable_sort(TParams, TParamCommandCommentComparePosition());195}196197void printHTMLStartTagComment(const HTMLStartTagComment *C,198llvm::raw_svector_ostream &Result) {199Result << "<" << C->getTagName();200201if (C->getNumAttrs() != 0) {202for (unsigned i = 0, e = C->getNumAttrs(); i != e; i++) {203Result << " ";204const HTMLStartTagComment::Attribute &Attr = C->getAttr(i);205Result << Attr.Name;206if (!Attr.Value.empty())207Result << "=\"" << Attr.Value << "\"";208}209}210211if (!C->isSelfClosing())212Result << ">";213else214Result << "/>";215}216217class CommentASTToHTMLConverter :218public ConstCommentVisitor<CommentASTToHTMLConverter> {219public:220/// \param Str accumulator for HTML.221CommentASTToHTMLConverter(const FullComment *FC,222SmallVectorImpl<char> &Str,223const CommandTraits &Traits) :224FC(FC), Result(Str), Traits(Traits)225{ }226227// Inline content.228void visitTextComment(const TextComment *C);229void visitInlineCommandComment(const InlineCommandComment *C);230void visitHTMLStartTagComment(const HTMLStartTagComment *C);231void visitHTMLEndTagComment(const HTMLEndTagComment *C);232233// Block content.234void visitParagraphComment(const ParagraphComment *C);235void visitBlockCommandComment(const BlockCommandComment *C);236void visitParamCommandComment(const ParamCommandComment *C);237void visitTParamCommandComment(const TParamCommandComment *C);238void visitVerbatimBlockComment(const VerbatimBlockComment *C);239void visitVerbatimBlockLineComment(const VerbatimBlockLineComment *C);240void visitVerbatimLineComment(const VerbatimLineComment *C);241242void visitFullComment(const FullComment *C);243244// Helpers.245246/// Convert a paragraph that is not a block by itself (an argument to some247/// command).248void visitNonStandaloneParagraphComment(const ParagraphComment *C);249250void appendToResultWithHTMLEscaping(StringRef S);251252private:253const FullComment *FC;254/// Output stream for HTML.255llvm::raw_svector_ostream Result;256257const CommandTraits &Traits;258};259} // end unnamed namespace260261void CommentASTToHTMLConverter::visitTextComment(const TextComment *C) {262appendToResultWithHTMLEscaping(C->getText());263}264265void CommentASTToHTMLConverter::visitInlineCommandComment(266const InlineCommandComment *C) {267// Nothing to render if no arguments supplied.268if (C->getNumArgs() == 0)269return;270271// Nothing to render if argument is empty.272StringRef Arg0 = C->getArgText(0);273if (Arg0.empty())274return;275276switch (C->getRenderKind()) {277case InlineCommandRenderKind::Normal:278for (unsigned i = 0, e = C->getNumArgs(); i != e; ++i) {279appendToResultWithHTMLEscaping(C->getArgText(i));280Result << " ";281}282return;283284case InlineCommandRenderKind::Bold:285assert(C->getNumArgs() == 1);286Result << "<b>";287appendToResultWithHTMLEscaping(Arg0);288Result << "</b>";289return;290case InlineCommandRenderKind::Monospaced:291assert(C->getNumArgs() == 1);292Result << "<tt>";293appendToResultWithHTMLEscaping(Arg0);294Result<< "</tt>";295return;296case InlineCommandRenderKind::Emphasized:297assert(C->getNumArgs() == 1);298Result << "<em>";299appendToResultWithHTMLEscaping(Arg0);300Result << "</em>";301return;302case InlineCommandRenderKind::Anchor:303assert(C->getNumArgs() == 1);304Result << "<span id=\"" << Arg0 << "\"></span>";305return;306}307}308309void CommentASTToHTMLConverter::visitHTMLStartTagComment(310const HTMLStartTagComment *C) {311printHTMLStartTagComment(C, Result);312}313314void CommentASTToHTMLConverter::visitHTMLEndTagComment(315const HTMLEndTagComment *C) {316Result << "</" << C->getTagName() << ">";317}318319void CommentASTToHTMLConverter::visitParagraphComment(320const ParagraphComment *C) {321if (C->isWhitespace())322return;323324Result << "<p>";325for (Comment::child_iterator I = C->child_begin(), E = C->child_end();326I != E; ++I) {327visit(*I);328}329Result << "</p>";330}331332void CommentASTToHTMLConverter::visitBlockCommandComment(333const BlockCommandComment *C) {334const CommandInfo *Info = Traits.getCommandInfo(C->getCommandID());335if (Info->IsBriefCommand) {336Result << "<p class=\"para-brief\">";337visitNonStandaloneParagraphComment(C->getParagraph());338Result << "</p>";339return;340}341if (Info->IsReturnsCommand) {342Result << "<p class=\"para-returns\">"343"<span class=\"word-returns\">Returns</span> ";344visitNonStandaloneParagraphComment(C->getParagraph());345Result << "</p>";346return;347}348// We don't know anything about this command. Just render the paragraph.349visit(C->getParagraph());350}351352void CommentASTToHTMLConverter::visitParamCommandComment(353const ParamCommandComment *C) {354if (C->isParamIndexValid()) {355if (C->isVarArgParam()) {356Result << "<dt class=\"param-name-index-vararg\">";357appendToResultWithHTMLEscaping(C->getParamNameAsWritten());358} else {359Result << "<dt class=\"param-name-index-"360<< C->getParamIndex()361<< "\">";362appendToResultWithHTMLEscaping(C->getParamName(FC));363}364} else {365Result << "<dt class=\"param-name-index-invalid\">";366appendToResultWithHTMLEscaping(C->getParamNameAsWritten());367}368Result << "</dt>";369370if (C->isParamIndexValid()) {371if (C->isVarArgParam())372Result << "<dd class=\"param-descr-index-vararg\">";373else374Result << "<dd class=\"param-descr-index-"375<< C->getParamIndex()376<< "\">";377} else378Result << "<dd class=\"param-descr-index-invalid\">";379380visitNonStandaloneParagraphComment(C->getParagraph());381Result << "</dd>";382}383384void CommentASTToHTMLConverter::visitTParamCommandComment(385const TParamCommandComment *C) {386if (C->isPositionValid()) {387if (C->getDepth() == 1)388Result << "<dt class=\"tparam-name-index-"389<< C->getIndex(0)390<< "\">";391else392Result << "<dt class=\"tparam-name-index-other\">";393appendToResultWithHTMLEscaping(C->getParamName(FC));394} else {395Result << "<dt class=\"tparam-name-index-invalid\">";396appendToResultWithHTMLEscaping(C->getParamNameAsWritten());397}398399Result << "</dt>";400401if (C->isPositionValid()) {402if (C->getDepth() == 1)403Result << "<dd class=\"tparam-descr-index-"404<< C->getIndex(0)405<< "\">";406else407Result << "<dd class=\"tparam-descr-index-other\">";408} else409Result << "<dd class=\"tparam-descr-index-invalid\">";410411visitNonStandaloneParagraphComment(C->getParagraph());412Result << "</dd>";413}414415void CommentASTToHTMLConverter::visitVerbatimBlockComment(416const VerbatimBlockComment *C) {417unsigned NumLines = C->getNumLines();418if (NumLines == 0)419return;420421Result << "<pre>";422for (unsigned i = 0; i != NumLines; ++i) {423appendToResultWithHTMLEscaping(C->getText(i));424if (i + 1 != NumLines)425Result << '\n';426}427Result << "</pre>";428}429430void CommentASTToHTMLConverter::visitVerbatimBlockLineComment(431const VerbatimBlockLineComment *C) {432llvm_unreachable("should not see this AST node");433}434435void CommentASTToHTMLConverter::visitVerbatimLineComment(436const VerbatimLineComment *C) {437Result << "<pre>";438appendToResultWithHTMLEscaping(C->getText());439Result << "</pre>";440}441442void CommentASTToHTMLConverter::visitFullComment(const FullComment *C) {443FullCommentParts Parts(C, Traits);444445bool FirstParagraphIsBrief = false;446if (Parts.Headerfile)447visit(Parts.Headerfile);448if (Parts.Brief)449visit(Parts.Brief);450else if (Parts.FirstParagraph) {451Result << "<p class=\"para-brief\">";452visitNonStandaloneParagraphComment(Parts.FirstParagraph);453Result << "</p>";454FirstParagraphIsBrief = true;455}456457for (unsigned i = 0, e = Parts.MiscBlocks.size(); i != e; ++i) {458const Comment *C = Parts.MiscBlocks[i];459if (FirstParagraphIsBrief && C == Parts.FirstParagraph)460continue;461visit(C);462}463464if (Parts.TParams.size() != 0) {465Result << "<dl>";466for (unsigned i = 0, e = Parts.TParams.size(); i != e; ++i)467visit(Parts.TParams[i]);468Result << "</dl>";469}470471if (Parts.Params.size() != 0) {472Result << "<dl>";473for (unsigned i = 0, e = Parts.Params.size(); i != e; ++i)474visit(Parts.Params[i]);475Result << "</dl>";476}477478if (Parts.Returns.size() != 0) {479Result << "<div class=\"result-discussion\">";480for (unsigned i = 0, e = Parts.Returns.size(); i != e; ++i)481visit(Parts.Returns[i]);482Result << "</div>";483}484485}486487void CommentASTToHTMLConverter::visitNonStandaloneParagraphComment(488const ParagraphComment *C) {489if (!C)490return;491492for (Comment::child_iterator I = C->child_begin(), E = C->child_end();493I != E; ++I) {494visit(*I);495}496}497498void CommentASTToHTMLConverter::appendToResultWithHTMLEscaping(StringRef S) {499for (StringRef::iterator I = S.begin(), E = S.end(); I != E; ++I) {500const char C = *I;501switch (C) {502case '&':503Result << "&";504break;505case '<':506Result << "<";507break;508case '>':509Result << ">";510break;511case '"':512Result << """;513break;514case '\'':515Result << "'";516break;517case '/':518Result << "/";519break;520default:521Result << C;522break;523}524}525}526527namespace {528class CommentASTToXMLConverter :529public ConstCommentVisitor<CommentASTToXMLConverter> {530public:531/// \param Str accumulator for XML.532CommentASTToXMLConverter(const FullComment *FC,533SmallVectorImpl<char> &Str,534const CommandTraits &Traits,535const SourceManager &SM) :536FC(FC), Result(Str), Traits(Traits), SM(SM) { }537538// Inline content.539void visitTextComment(const TextComment *C);540void visitInlineCommandComment(const InlineCommandComment *C);541void visitHTMLStartTagComment(const HTMLStartTagComment *C);542void visitHTMLEndTagComment(const HTMLEndTagComment *C);543544// Block content.545void visitParagraphComment(const ParagraphComment *C);546547void appendParagraphCommentWithKind(const ParagraphComment *C,548StringRef ParagraphKind,549StringRef PrependBodyText);550551void visitBlockCommandComment(const BlockCommandComment *C);552void visitParamCommandComment(const ParamCommandComment *C);553void visitTParamCommandComment(const TParamCommandComment *C);554void visitVerbatimBlockComment(const VerbatimBlockComment *C);555void visitVerbatimBlockLineComment(const VerbatimBlockLineComment *C);556void visitVerbatimLineComment(const VerbatimLineComment *C);557558void visitFullComment(const FullComment *C);559560// Helpers.561void appendToResultWithXMLEscaping(StringRef S);562void appendToResultWithCDATAEscaping(StringRef S);563564void formatTextOfDeclaration(const DeclInfo *DI,565SmallString<128> &Declaration);566567private:568const FullComment *FC;569570/// Output stream for XML.571llvm::raw_svector_ostream Result;572573const CommandTraits &Traits;574const SourceManager &SM;575};576577void getSourceTextOfDeclaration(const DeclInfo *ThisDecl,578SmallVectorImpl<char> &Str) {579ASTContext &Context = ThisDecl->CurrentDecl->getASTContext();580const LangOptions &LangOpts = Context.getLangOpts();581llvm::raw_svector_ostream OS(Str);582PrintingPolicy PPolicy(LangOpts);583PPolicy.PolishForDeclaration = true;584PPolicy.TerseOutput = true;585PPolicy.ConstantsAsWritten = true;586ThisDecl->CurrentDecl->print(OS, PPolicy,587/*Indentation*/0, /*PrintInstantiation*/false);588}589590void CommentASTToXMLConverter::formatTextOfDeclaration(591const DeclInfo *DI, SmallString<128> &Declaration) {592// Formatting API expects null terminated input string.593StringRef StringDecl(Declaration.c_str(), Declaration.size());594595// Formatter specific code.596unsigned Offset = 0;597unsigned Length = Declaration.size();598599format::FormatStyle Style = format::getLLVMStyle();600Style.FixNamespaceComments = false;601tooling::Replacements Replaces =602reformat(Style, StringDecl, tooling::Range(Offset, Length), "xmldecl.xd");603auto FormattedStringDecl = applyAllReplacements(StringDecl, Replaces);604if (static_cast<bool>(FormattedStringDecl)) {605Declaration = *FormattedStringDecl;606}607}608609} // end unnamed namespace610611void CommentASTToXMLConverter::visitTextComment(const TextComment *C) {612appendToResultWithXMLEscaping(C->getText());613}614615void CommentASTToXMLConverter::visitInlineCommandComment(616const InlineCommandComment *C) {617// Nothing to render if no arguments supplied.618if (C->getNumArgs() == 0)619return;620621// Nothing to render if argument is empty.622StringRef Arg0 = C->getArgText(0);623if (Arg0.empty())624return;625626switch (C->getRenderKind()) {627case InlineCommandRenderKind::Normal:628for (unsigned i = 0, e = C->getNumArgs(); i != e; ++i) {629appendToResultWithXMLEscaping(C->getArgText(i));630Result << " ";631}632return;633case InlineCommandRenderKind::Bold:634assert(C->getNumArgs() == 1);635Result << "<bold>";636appendToResultWithXMLEscaping(Arg0);637Result << "</bold>";638return;639case InlineCommandRenderKind::Monospaced:640assert(C->getNumArgs() == 1);641Result << "<monospaced>";642appendToResultWithXMLEscaping(Arg0);643Result << "</monospaced>";644return;645case InlineCommandRenderKind::Emphasized:646assert(C->getNumArgs() == 1);647Result << "<emphasized>";648appendToResultWithXMLEscaping(Arg0);649Result << "</emphasized>";650return;651case InlineCommandRenderKind::Anchor:652assert(C->getNumArgs() == 1);653Result << "<anchor id=\"" << Arg0 << "\"></anchor>";654return;655}656}657658void CommentASTToXMLConverter::visitHTMLStartTagComment(659const HTMLStartTagComment *C) {660Result << "<rawHTML";661if (C->isMalformed())662Result << " isMalformed=\"1\"";663Result << ">";664{665SmallString<32> Tag;666{667llvm::raw_svector_ostream TagOS(Tag);668printHTMLStartTagComment(C, TagOS);669}670appendToResultWithCDATAEscaping(Tag);671}672Result << "</rawHTML>";673}674675void676CommentASTToXMLConverter::visitHTMLEndTagComment(const HTMLEndTagComment *C) {677Result << "<rawHTML";678if (C->isMalformed())679Result << " isMalformed=\"1\"";680Result << "></" << C->getTagName() << "></rawHTML>";681}682683void CommentASTToXMLConverter::visitParagraphComment(684const ParagraphComment *C) {685appendParagraphCommentWithKind(C, StringRef(), StringRef());686}687688void CommentASTToXMLConverter::appendParagraphCommentWithKind(689const ParagraphComment *C, StringRef ParagraphKind,690StringRef PrependBodyText) {691if (C->isWhitespace() && PrependBodyText.empty())692return;693694if (ParagraphKind.empty())695Result << "<Para>";696else697Result << "<Para kind=\"" << ParagraphKind << "\">";698699if (!PrependBodyText.empty())700Result << PrependBodyText << " ";701702for (Comment::child_iterator I = C->child_begin(), E = C->child_end(); I != E;703++I) {704visit(*I);705}706Result << "</Para>";707}708709void CommentASTToXMLConverter::visitBlockCommandComment(710const BlockCommandComment *C) {711StringRef ParagraphKind;712StringRef ExceptionType;713714const unsigned CommandID = C->getCommandID();715const CommandInfo *Info = Traits.getCommandInfo(CommandID);716if (Info->IsThrowsCommand && C->getNumArgs() > 0) {717ExceptionType = C->getArgText(0);718}719720switch (CommandID) {721case CommandTraits::KCI_attention:722case CommandTraits::KCI_author:723case CommandTraits::KCI_authors:724case CommandTraits::KCI_bug:725case CommandTraits::KCI_copyright:726case CommandTraits::KCI_date:727case CommandTraits::KCI_invariant:728case CommandTraits::KCI_note:729case CommandTraits::KCI_post:730case CommandTraits::KCI_pre:731case CommandTraits::KCI_remark:732case CommandTraits::KCI_remarks:733case CommandTraits::KCI_sa:734case CommandTraits::KCI_see:735case CommandTraits::KCI_since:736case CommandTraits::KCI_todo:737case CommandTraits::KCI_version:738case CommandTraits::KCI_warning:739ParagraphKind = C->getCommandName(Traits);740break;741default:742break;743}744745appendParagraphCommentWithKind(C->getParagraph(), ParagraphKind,746ExceptionType);747}748749void CommentASTToXMLConverter::visitParamCommandComment(750const ParamCommandComment *C) {751Result << "<Parameter><Name>";752appendToResultWithXMLEscaping(C->isParamIndexValid()753? C->getParamName(FC)754: C->getParamNameAsWritten());755Result << "</Name>";756757if (C->isParamIndexValid()) {758if (C->isVarArgParam())759Result << "<IsVarArg />";760else761Result << "<Index>" << C->getParamIndex() << "</Index>";762}763764Result << "<Direction isExplicit=\"" << C->isDirectionExplicit() << "\">";765switch (C->getDirection()) {766case ParamCommandPassDirection::In:767Result << "in";768break;769case ParamCommandPassDirection::Out:770Result << "out";771break;772case ParamCommandPassDirection::InOut:773Result << "in,out";774break;775}776Result << "</Direction><Discussion>";777visit(C->getParagraph());778Result << "</Discussion></Parameter>";779}780781void CommentASTToXMLConverter::visitTParamCommandComment(782const TParamCommandComment *C) {783Result << "<Parameter><Name>";784appendToResultWithXMLEscaping(C->isPositionValid() ? C->getParamName(FC)785: C->getParamNameAsWritten());786Result << "</Name>";787788if (C->isPositionValid() && C->getDepth() == 1) {789Result << "<Index>" << C->getIndex(0) << "</Index>";790}791792Result << "<Discussion>";793visit(C->getParagraph());794Result << "</Discussion></Parameter>";795}796797void CommentASTToXMLConverter::visitVerbatimBlockComment(798const VerbatimBlockComment *C) {799unsigned NumLines = C->getNumLines();800if (NumLines == 0)801return;802803switch (C->getCommandID()) {804case CommandTraits::KCI_code:805Result << "<Verbatim xml:space=\"preserve\" kind=\"code\">";806break;807default:808Result << "<Verbatim xml:space=\"preserve\" kind=\"verbatim\">";809break;810}811for (unsigned i = 0; i != NumLines; ++i) {812appendToResultWithXMLEscaping(C->getText(i));813if (i + 1 != NumLines)814Result << '\n';815}816Result << "</Verbatim>";817}818819void CommentASTToXMLConverter::visitVerbatimBlockLineComment(820const VerbatimBlockLineComment *C) {821llvm_unreachable("should not see this AST node");822}823824void CommentASTToXMLConverter::visitVerbatimLineComment(825const VerbatimLineComment *C) {826Result << "<Verbatim xml:space=\"preserve\" kind=\"verbatim\">";827appendToResultWithXMLEscaping(C->getText());828Result << "</Verbatim>";829}830831void CommentASTToXMLConverter::visitFullComment(const FullComment *C) {832FullCommentParts Parts(C, Traits);833834const DeclInfo *DI = C->getDeclInfo();835StringRef RootEndTag;836if (DI) {837switch (DI->getKind()) {838case DeclInfo::OtherKind:839RootEndTag = "</Other>";840Result << "<Other";841break;842case DeclInfo::FunctionKind:843RootEndTag = "</Function>";844Result << "<Function";845switch (DI->TemplateKind) {846case DeclInfo::NotTemplate:847break;848case DeclInfo::Template:849Result << " templateKind=\"template\"";850break;851case DeclInfo::TemplateSpecialization:852Result << " templateKind=\"specialization\"";853break;854case DeclInfo::TemplatePartialSpecialization:855llvm_unreachable("partial specializations of functions "856"are not allowed in C++");857}858if (DI->IsInstanceMethod)859Result << " isInstanceMethod=\"1\"";860if (DI->IsClassMethod)861Result << " isClassMethod=\"1\"";862break;863case DeclInfo::ClassKind:864RootEndTag = "</Class>";865Result << "<Class";866switch (DI->TemplateKind) {867case DeclInfo::NotTemplate:868break;869case DeclInfo::Template:870Result << " templateKind=\"template\"";871break;872case DeclInfo::TemplateSpecialization:873Result << " templateKind=\"specialization\"";874break;875case DeclInfo::TemplatePartialSpecialization:876Result << " templateKind=\"partialSpecialization\"";877break;878}879break;880case DeclInfo::VariableKind:881RootEndTag = "</Variable>";882Result << "<Variable";883break;884case DeclInfo::NamespaceKind:885RootEndTag = "</Namespace>";886Result << "<Namespace";887break;888case DeclInfo::TypedefKind:889RootEndTag = "</Typedef>";890Result << "<Typedef";891break;892case DeclInfo::EnumKind:893RootEndTag = "</Enum>";894Result << "<Enum";895break;896}897898{899// Print line and column number.900SourceLocation Loc = DI->CurrentDecl->getLocation();901std::pair<FileID, unsigned> LocInfo = SM.getDecomposedLoc(Loc);902FileID FID = LocInfo.first;903unsigned FileOffset = LocInfo.second;904905if (FID.isValid()) {906if (OptionalFileEntryRef FE = SM.getFileEntryRefForID(FID)) {907Result << " file=\"";908appendToResultWithXMLEscaping(FE->getName());909Result << "\"";910}911Result << " line=\"" << SM.getLineNumber(FID, FileOffset)912<< "\" column=\"" << SM.getColumnNumber(FID, FileOffset)913<< "\"";914}915}916917// Finish the root tag.918Result << ">";919920bool FoundName = false;921if (const NamedDecl *ND = dyn_cast<NamedDecl>(DI->CommentDecl)) {922if (DeclarationName DeclName = ND->getDeclName()) {923Result << "<Name>";924std::string Name = DeclName.getAsString();925appendToResultWithXMLEscaping(Name);926FoundName = true;927Result << "</Name>";928}929}930if (!FoundName)931Result << "<Name><anonymous></Name>";932933{934// Print USR.935SmallString<128> USR;936generateUSRForDecl(DI->CommentDecl, USR);937if (!USR.empty()) {938Result << "<USR>";939appendToResultWithXMLEscaping(USR);940Result << "</USR>";941}942}943} else {944// No DeclInfo -- just emit some root tag and name tag.945RootEndTag = "</Other>";946Result << "<Other><Name>unknown</Name>";947}948949if (Parts.Headerfile) {950Result << "<Headerfile>";951visit(Parts.Headerfile);952Result << "</Headerfile>";953}954955{956// Pretty-print the declaration.957Result << "<Declaration>";958SmallString<128> Declaration;959getSourceTextOfDeclaration(DI, Declaration);960formatTextOfDeclaration(DI, Declaration);961appendToResultWithXMLEscaping(Declaration);962Result << "</Declaration>";963}964965bool FirstParagraphIsBrief = false;966if (Parts.Brief) {967Result << "<Abstract>";968visit(Parts.Brief);969Result << "</Abstract>";970} else if (Parts.FirstParagraph) {971Result << "<Abstract>";972visit(Parts.FirstParagraph);973Result << "</Abstract>";974FirstParagraphIsBrief = true;975}976977if (Parts.TParams.size() != 0) {978Result << "<TemplateParameters>";979for (unsigned i = 0, e = Parts.TParams.size(); i != e; ++i)980visit(Parts.TParams[i]);981Result << "</TemplateParameters>";982}983984if (Parts.Params.size() != 0) {985Result << "<Parameters>";986for (unsigned i = 0, e = Parts.Params.size(); i != e; ++i)987visit(Parts.Params[i]);988Result << "</Parameters>";989}990991if (Parts.Exceptions.size() != 0) {992Result << "<Exceptions>";993for (unsigned i = 0, e = Parts.Exceptions.size(); i != e; ++i)994visit(Parts.Exceptions[i]);995Result << "</Exceptions>";996}997998if (Parts.Returns.size() != 0) {999Result << "<ResultDiscussion>";1000for (unsigned i = 0, e = Parts.Returns.size(); i != e; ++i)1001visit(Parts.Returns[i]);1002Result << "</ResultDiscussion>";1003}10041005if (DI->CommentDecl->hasAttrs()) {1006const AttrVec &Attrs = DI->CommentDecl->getAttrs();1007for (unsigned i = 0, e = Attrs.size(); i != e; i++) {1008const AvailabilityAttr *AA = dyn_cast<AvailabilityAttr>(Attrs[i]);1009if (!AA) {1010if (const DeprecatedAttr *DA = dyn_cast<DeprecatedAttr>(Attrs[i])) {1011if (DA->getMessage().empty())1012Result << "<Deprecated/>";1013else {1014Result << "<Deprecated>";1015appendToResultWithXMLEscaping(DA->getMessage());1016Result << "</Deprecated>";1017}1018}1019else if (const UnavailableAttr *UA = dyn_cast<UnavailableAttr>(Attrs[i])) {1020if (UA->getMessage().empty())1021Result << "<Unavailable/>";1022else {1023Result << "<Unavailable>";1024appendToResultWithXMLEscaping(UA->getMessage());1025Result << "</Unavailable>";1026}1027}1028continue;1029}10301031// 'availability' attribute.1032Result << "<Availability";1033StringRef Distribution;1034if (AA->getPlatform()) {1035Distribution = AvailabilityAttr::getPrettyPlatformName(1036AA->getPlatform()->getName());1037if (Distribution.empty())1038Distribution = AA->getPlatform()->getName();1039}1040Result << " distribution=\"" << Distribution << "\">";1041VersionTuple IntroducedInVersion = AA->getIntroduced();1042if (!IntroducedInVersion.empty()) {1043Result << "<IntroducedInVersion>"1044<< IntroducedInVersion.getAsString()1045<< "</IntroducedInVersion>";1046}1047VersionTuple DeprecatedInVersion = AA->getDeprecated();1048if (!DeprecatedInVersion.empty()) {1049Result << "<DeprecatedInVersion>"1050<< DeprecatedInVersion.getAsString()1051<< "</DeprecatedInVersion>";1052}1053VersionTuple RemovedAfterVersion = AA->getObsoleted();1054if (!RemovedAfterVersion.empty()) {1055Result << "<RemovedAfterVersion>"1056<< RemovedAfterVersion.getAsString()1057<< "</RemovedAfterVersion>";1058}1059StringRef DeprecationSummary = AA->getMessage();1060if (!DeprecationSummary.empty()) {1061Result << "<DeprecationSummary>";1062appendToResultWithXMLEscaping(DeprecationSummary);1063Result << "</DeprecationSummary>";1064}1065if (AA->getUnavailable())1066Result << "<Unavailable/>";10671068IdentifierInfo *Environment = AA->getEnvironment();1069if (Environment) {1070Result << "<Environment>" << Environment->getName() << "</Environment>";1071}1072Result << "</Availability>";1073}1074}10751076{1077bool StartTagEmitted = false;1078for (unsigned i = 0, e = Parts.MiscBlocks.size(); i != e; ++i) {1079const Comment *C = Parts.MiscBlocks[i];1080if (FirstParagraphIsBrief && C == Parts.FirstParagraph)1081continue;1082if (!StartTagEmitted) {1083Result << "<Discussion>";1084StartTagEmitted = true;1085}1086visit(C);1087}1088if (StartTagEmitted)1089Result << "</Discussion>";1090}10911092Result << RootEndTag;1093}10941095void CommentASTToXMLConverter::appendToResultWithXMLEscaping(StringRef S) {1096for (StringRef::iterator I = S.begin(), E = S.end(); I != E; ++I) {1097const char C = *I;1098switch (C) {1099case '&':1100Result << "&";1101break;1102case '<':1103Result << "<";1104break;1105case '>':1106Result << ">";1107break;1108case '"':1109Result << """;1110break;1111case '\'':1112Result << "'";1113break;1114default:1115Result << C;1116break;1117}1118}1119}11201121void CommentASTToXMLConverter::appendToResultWithCDATAEscaping(StringRef S) {1122if (S.empty())1123return;11241125Result << "<![CDATA[";1126while (!S.empty()) {1127size_t Pos = S.find("]]>");1128if (Pos == 0) {1129Result << "]]]]><![CDATA[>";1130S = S.drop_front(3);1131continue;1132}1133if (Pos == StringRef::npos)1134Pos = S.size();11351136Result << S.substr(0, Pos);11371138S = S.drop_front(Pos);1139}1140Result << "]]>";1141}11421143CommentToXMLConverter::CommentToXMLConverter() {}1144CommentToXMLConverter::~CommentToXMLConverter() {}11451146void CommentToXMLConverter::convertCommentToHTML(const FullComment *FC,1147SmallVectorImpl<char> &HTML,1148const ASTContext &Context) {1149CommentASTToHTMLConverter Converter(FC, HTML,1150Context.getCommentCommandTraits());1151Converter.visit(FC);1152}11531154void CommentToXMLConverter::convertHTMLTagNodeToText(1155const comments::HTMLTagComment *HTC, SmallVectorImpl<char> &Text,1156const ASTContext &Context) {1157CommentASTToHTMLConverter Converter(nullptr, Text,1158Context.getCommentCommandTraits());1159Converter.visit(HTC);1160}11611162void CommentToXMLConverter::convertCommentToXML(const FullComment *FC,1163SmallVectorImpl<char> &XML,1164const ASTContext &Context) {1165CommentASTToXMLConverter Converter(FC, XML, Context.getCommentCommandTraits(),1166Context.getSourceManager());1167Converter.visit(FC);1168}116911701171