brintos

brintos / llvm-project-archived public Read only

0
0
Text · 34.6 KiB · f54d8be Raw
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 << "&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,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 << ">&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  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>&lt;anonymous&gt;</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 << "&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 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