forked from OSchip/llvm-project
				
			
		
			
				
	
	
		
			1152 lines
		
	
	
		
			34 KiB
		
	
	
	
		
			C++
		
	
	
	
			
		
		
	
	
			1152 lines
		
	
	
		
			34 KiB
		
	
	
	
		
			C++
		
	
	
	
| //===--- CommentToXML.cpp - Convert comments to XML representation --------===//
 | |
| //
 | |
| // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
 | |
| // See https://llvm.org/LICENSE.txt for license information.
 | |
| // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
 | |
| //
 | |
| //===----------------------------------------------------------------------===//
 | |
| 
 | |
| #include "clang/Index/CommentToXML.h"
 | |
| #include "clang/AST/ASTContext.h"
 | |
| #include "clang/AST/Attr.h"
 | |
| #include "clang/AST/Comment.h"
 | |
| #include "clang/AST/CommentVisitor.h"
 | |
| #include "clang/Basic/FileManager.h"
 | |
| #include "clang/Basic/SourceManager.h"
 | |
| #include "clang/Format/Format.h"
 | |
| #include "clang/Index/USRGeneration.h"
 | |
| #include "llvm/ADT/StringExtras.h"
 | |
| #include "llvm/ADT/TinyPtrVector.h"
 | |
| #include "llvm/Support/raw_ostream.h"
 | |
| 
 | |
| using namespace clang;
 | |
| using namespace clang::comments;
 | |
| using namespace clang::index;
 | |
| 
 | |
| namespace {
 | |
| 
 | |
| /// This comparison will sort parameters with valid index by index, then vararg
 | |
| /// parameters, and invalid (unresolved) parameters last.
 | |
| class ParamCommandCommentCompareIndex {
 | |
| public:
 | |
|   bool operator()(const ParamCommandComment *LHS,
 | |
|                   const ParamCommandComment *RHS) const {
 | |
|     unsigned LHSIndex = UINT_MAX;
 | |
|     unsigned RHSIndex = UINT_MAX;
 | |
| 
 | |
|     if (LHS->isParamIndexValid()) {
 | |
|       if (LHS->isVarArgParam())
 | |
|         LHSIndex = UINT_MAX - 1;
 | |
|       else
 | |
|         LHSIndex = LHS->getParamIndex();
 | |
|     }
 | |
|     if (RHS->isParamIndexValid()) {
 | |
|       if (RHS->isVarArgParam())
 | |
|         RHSIndex = UINT_MAX - 1;
 | |
|       else
 | |
|         RHSIndex = RHS->getParamIndex();
 | |
|     }
 | |
|     return LHSIndex < RHSIndex;
 | |
|   }
 | |
| };
 | |
| 
 | |
| /// This comparison will sort template parameters in the following order:
 | |
| /// \li real template parameters (depth = 1) in index order;
 | |
| /// \li all other names (depth > 1);
 | |
| /// \li unresolved names.
 | |
| class TParamCommandCommentComparePosition {
 | |
| public:
 | |
|   bool operator()(const TParamCommandComment *LHS,
 | |
|                   const TParamCommandComment *RHS) const {
 | |
|     // Sort unresolved names last.
 | |
|     if (!LHS->isPositionValid())
 | |
|       return false;
 | |
|     if (!RHS->isPositionValid())
 | |
|       return true;
 | |
| 
 | |
|     if (LHS->getDepth() > 1)
 | |
|       return false;
 | |
|     if (RHS->getDepth() > 1)
 | |
|       return true;
 | |
| 
 | |
|     // Sort template parameters in index order.
 | |
|     if (LHS->getDepth() == 1 && RHS->getDepth() == 1)
 | |
|       return LHS->getIndex(0) < RHS->getIndex(0);
 | |
| 
 | |
|     // Leave all other names in source order.
 | |
|     return true;
 | |
|   }
 | |
| };
 | |
| 
 | |
| /// Separate parts of a FullComment.
 | |
| struct FullCommentParts {
 | |
|   /// Take a full comment apart and initialize members accordingly.
 | |
|   FullCommentParts(const FullComment *C,
 | |
|                    const CommandTraits &Traits);
 | |
| 
 | |
|   const BlockContentComment *Brief;
 | |
|   const BlockContentComment *Headerfile;
 | |
|   const ParagraphComment *FirstParagraph;
 | |
|   SmallVector<const BlockCommandComment *, 4> Returns;
 | |
|   SmallVector<const ParamCommandComment *, 8> Params;
 | |
|   SmallVector<const TParamCommandComment *, 4> TParams;
 | |
|   llvm::TinyPtrVector<const BlockCommandComment *> Exceptions;
 | |
|   SmallVector<const BlockContentComment *, 8> MiscBlocks;
 | |
| };
 | |
| 
 | |
| FullCommentParts::FullCommentParts(const FullComment *C,
 | |
|                                    const CommandTraits &Traits) :
 | |
|     Brief(nullptr), Headerfile(nullptr), FirstParagraph(nullptr) {
 | |
|   for (Comment::child_iterator I = C->child_begin(), E = C->child_end();
 | |
|        I != E; ++I) {
 | |
|     const Comment *Child = *I;
 | |
|     if (!Child)
 | |
|       continue;
 | |
|     switch (Child->getCommentKind()) {
 | |
|     case Comment::NoCommentKind:
 | |
|       continue;
 | |
| 
 | |
|     case Comment::ParagraphCommentKind: {
 | |
|       const ParagraphComment *PC = cast<ParagraphComment>(Child);
 | |
|       if (PC->isWhitespace())
 | |
|         break;
 | |
|       if (!FirstParagraph)
 | |
|         FirstParagraph = PC;
 | |
| 
 | |
|       MiscBlocks.push_back(PC);
 | |
|       break;
 | |
|     }
 | |
| 
 | |
|     case Comment::BlockCommandCommentKind: {
 | |
|       const BlockCommandComment *BCC = cast<BlockCommandComment>(Child);
 | |
|       const CommandInfo *Info = Traits.getCommandInfo(BCC->getCommandID());
 | |
|       if (!Brief && Info->IsBriefCommand) {
 | |
|         Brief = BCC;
 | |
|         break;
 | |
|       }
 | |
|       if (!Headerfile && Info->IsHeaderfileCommand) {
 | |
|         Headerfile = BCC;
 | |
|         break;
 | |
|       }
 | |
|       if (Info->IsReturnsCommand) {
 | |
|         Returns.push_back(BCC);
 | |
|         break;
 | |
|       }
 | |
|       if (Info->IsThrowsCommand) {
 | |
|         Exceptions.push_back(BCC);
 | |
|         break;
 | |
|       }
 | |
|       MiscBlocks.push_back(BCC);
 | |
|       break;
 | |
|     }
 | |
| 
 | |
|     case Comment::ParamCommandCommentKind: {
 | |
|       const ParamCommandComment *PCC = cast<ParamCommandComment>(Child);
 | |
|       if (!PCC->hasParamName())
 | |
|         break;
 | |
| 
 | |
|       if (!PCC->isDirectionExplicit() && !PCC->hasNonWhitespaceParagraph())
 | |
|         break;
 | |
| 
 | |
|       Params.push_back(PCC);
 | |
|       break;
 | |
|     }
 | |
| 
 | |
|     case Comment::TParamCommandCommentKind: {
 | |
|       const TParamCommandComment *TPCC = cast<TParamCommandComment>(Child);
 | |
|       if (!TPCC->hasParamName())
 | |
|         break;
 | |
| 
 | |
|       if (!TPCC->hasNonWhitespaceParagraph())
 | |
|         break;
 | |
| 
 | |
|       TParams.push_back(TPCC);
 | |
|       break;
 | |
|     }
 | |
| 
 | |
|     case Comment::VerbatimBlockCommentKind:
 | |
|       MiscBlocks.push_back(cast<BlockCommandComment>(Child));
 | |
|       break;
 | |
| 
 | |
|     case Comment::VerbatimLineCommentKind: {
 | |
|       const VerbatimLineComment *VLC = cast<VerbatimLineComment>(Child);
 | |
|       const CommandInfo *Info = Traits.getCommandInfo(VLC->getCommandID());
 | |
|       if (!Info->IsDeclarationCommand)
 | |
|         MiscBlocks.push_back(VLC);
 | |
|       break;
 | |
|     }
 | |
| 
 | |
|     case Comment::TextCommentKind:
 | |
|     case Comment::InlineCommandCommentKind:
 | |
|     case Comment::HTMLStartTagCommentKind:
 | |
|     case Comment::HTMLEndTagCommentKind:
 | |
|     case Comment::VerbatimBlockLineCommentKind:
 | |
|     case Comment::FullCommentKind:
 | |
|       llvm_unreachable("AST node of this kind can't be a child of "
 | |
|                        "a FullComment");
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   // Sort params in order they are declared in the function prototype.
 | |
|   // Unresolved parameters are put at the end of the list in the same order
 | |
|   // they were seen in the comment.
 | |
|   llvm::stable_sort(Params, ParamCommandCommentCompareIndex());
 | |
|   llvm::stable_sort(TParams, TParamCommandCommentComparePosition());
 | |
| }
 | |
| 
 | |
| void printHTMLStartTagComment(const HTMLStartTagComment *C,
 | |
|                               llvm::raw_svector_ostream &Result) {
 | |
|   Result << "<" << C->getTagName();
 | |
| 
 | |
|   if (C->getNumAttrs() != 0) {
 | |
|     for (unsigned i = 0, e = C->getNumAttrs(); i != e; i++) {
 | |
|       Result << " ";
 | |
|       const HTMLStartTagComment::Attribute &Attr = C->getAttr(i);
 | |
|       Result << Attr.Name;
 | |
|       if (!Attr.Value.empty())
 | |
|         Result << "=\"" << Attr.Value << "\"";
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   if (!C->isSelfClosing())
 | |
|     Result << ">";
 | |
|   else
 | |
|     Result << "/>";
 | |
| }
 | |
| 
 | |
| class CommentASTToHTMLConverter :
 | |
|     public ConstCommentVisitor<CommentASTToHTMLConverter> {
 | |
| public:
 | |
|   /// \param Str accumulator for HTML.
 | |
|   CommentASTToHTMLConverter(const FullComment *FC,
 | |
|                             SmallVectorImpl<char> &Str,
 | |
|                             const CommandTraits &Traits) :
 | |
|       FC(FC), Result(Str), Traits(Traits)
 | |
|   { }
 | |
| 
 | |
|   // Inline content.
 | |
|   void visitTextComment(const TextComment *C);
 | |
|   void visitInlineCommandComment(const InlineCommandComment *C);
 | |
|   void visitHTMLStartTagComment(const HTMLStartTagComment *C);
 | |
|   void visitHTMLEndTagComment(const HTMLEndTagComment *C);
 | |
| 
 | |
|   // Block content.
 | |
|   void visitParagraphComment(const ParagraphComment *C);
 | |
|   void visitBlockCommandComment(const BlockCommandComment *C);
 | |
|   void visitParamCommandComment(const ParamCommandComment *C);
 | |
|   void visitTParamCommandComment(const TParamCommandComment *C);
 | |
|   void visitVerbatimBlockComment(const VerbatimBlockComment *C);
 | |
|   void visitVerbatimBlockLineComment(const VerbatimBlockLineComment *C);
 | |
|   void visitVerbatimLineComment(const VerbatimLineComment *C);
 | |
| 
 | |
|   void visitFullComment(const FullComment *C);
 | |
| 
 | |
|   // Helpers.
 | |
| 
 | |
|   /// Convert a paragraph that is not a block by itself (an argument to some
 | |
|   /// command).
 | |
|   void visitNonStandaloneParagraphComment(const ParagraphComment *C);
 | |
| 
 | |
|   void appendToResultWithHTMLEscaping(StringRef S);
 | |
| 
 | |
| private:
 | |
|   const FullComment *FC;
 | |
|   /// Output stream for HTML.
 | |
|   llvm::raw_svector_ostream Result;
 | |
| 
 | |
|   const CommandTraits &Traits;
 | |
| };
 | |
| } // end unnamed namespace
 | |
| 
 | |
| void CommentASTToHTMLConverter::visitTextComment(const TextComment *C) {
 | |
|   appendToResultWithHTMLEscaping(C->getText());
 | |
| }
 | |
| 
 | |
| void CommentASTToHTMLConverter::visitInlineCommandComment(
 | |
|                                   const InlineCommandComment *C) {
 | |
|   // Nothing to render if no arguments supplied.
 | |
|   if (C->getNumArgs() == 0)
 | |
|     return;
 | |
| 
 | |
|   // Nothing to render if argument is empty.
 | |
|   StringRef Arg0 = C->getArgText(0);
 | |
|   if (Arg0.empty())
 | |
|     return;
 | |
| 
 | |
|   switch (C->getRenderKind()) {
 | |
|   case InlineCommandComment::RenderNormal:
 | |
|     for (unsigned i = 0, e = C->getNumArgs(); i != e; ++i) {
 | |
|       appendToResultWithHTMLEscaping(C->getArgText(i));
 | |
|       Result << " ";
 | |
|     }
 | |
|     return;
 | |
| 
 | |
|   case InlineCommandComment::RenderBold:
 | |
|     assert(C->getNumArgs() == 1);
 | |
|     Result << "<b>";
 | |
|     appendToResultWithHTMLEscaping(Arg0);
 | |
|     Result << "</b>";
 | |
|     return;
 | |
|   case InlineCommandComment::RenderMonospaced:
 | |
|     assert(C->getNumArgs() == 1);
 | |
|     Result << "<tt>";
 | |
|     appendToResultWithHTMLEscaping(Arg0);
 | |
|     Result<< "</tt>";
 | |
|     return;
 | |
|   case InlineCommandComment::RenderEmphasized:
 | |
|     assert(C->getNumArgs() == 1);
 | |
|     Result << "<em>";
 | |
|     appendToResultWithHTMLEscaping(Arg0);
 | |
|     Result << "</em>";
 | |
|     return;
 | |
|   case InlineCommandComment::RenderAnchor:
 | |
|     assert(C->getNumArgs() == 1);
 | |
|     Result << "<span id=\"" << Arg0 << "\"></span>";
 | |
|     return;
 | |
|   }
 | |
| }
 | |
| 
 | |
| void CommentASTToHTMLConverter::visitHTMLStartTagComment(
 | |
|                                   const HTMLStartTagComment *C) {
 | |
|   printHTMLStartTagComment(C, Result);
 | |
| }
 | |
| 
 | |
| void CommentASTToHTMLConverter::visitHTMLEndTagComment(
 | |
|                                   const HTMLEndTagComment *C) {
 | |
|   Result << "</" << C->getTagName() << ">";
 | |
| }
 | |
| 
 | |
| void CommentASTToHTMLConverter::visitParagraphComment(
 | |
|                                   const ParagraphComment *C) {
 | |
|   if (C->isWhitespace())
 | |
|     return;
 | |
| 
 | |
|   Result << "<p>";
 | |
|   for (Comment::child_iterator I = C->child_begin(), E = C->child_end();
 | |
|        I != E; ++I) {
 | |
|     visit(*I);
 | |
|   }
 | |
|   Result << "</p>";
 | |
| }
 | |
| 
 | |
| void CommentASTToHTMLConverter::visitBlockCommandComment(
 | |
|                                   const BlockCommandComment *C) {
 | |
|   const CommandInfo *Info = Traits.getCommandInfo(C->getCommandID());
 | |
|   if (Info->IsBriefCommand) {
 | |
|     Result << "<p class=\"para-brief\">";
 | |
|     visitNonStandaloneParagraphComment(C->getParagraph());
 | |
|     Result << "</p>";
 | |
|     return;
 | |
|   }
 | |
|   if (Info->IsReturnsCommand) {
 | |
|     Result << "<p class=\"para-returns\">"
 | |
|               "<span class=\"word-returns\">Returns</span> ";
 | |
|     visitNonStandaloneParagraphComment(C->getParagraph());
 | |
|     Result << "</p>";
 | |
|     return;
 | |
|   }
 | |
|   // We don't know anything about this command.  Just render the paragraph.
 | |
|   visit(C->getParagraph());
 | |
| }
 | |
| 
 | |
| void CommentASTToHTMLConverter::visitParamCommandComment(
 | |
|                                   const ParamCommandComment *C) {
 | |
|   if (C->isParamIndexValid()) {
 | |
|     if (C->isVarArgParam()) {
 | |
|       Result << "<dt class=\"param-name-index-vararg\">";
 | |
|       appendToResultWithHTMLEscaping(C->getParamNameAsWritten());
 | |
|     } else {
 | |
|       Result << "<dt class=\"param-name-index-"
 | |
|              << C->getParamIndex()
 | |
|              << "\">";
 | |
|       appendToResultWithHTMLEscaping(C->getParamName(FC));
 | |
|     }
 | |
|   } else {
 | |
|     Result << "<dt class=\"param-name-index-invalid\">";
 | |
|     appendToResultWithHTMLEscaping(C->getParamNameAsWritten());
 | |
|   }
 | |
|   Result << "</dt>";
 | |
| 
 | |
|   if (C->isParamIndexValid()) {
 | |
|     if (C->isVarArgParam())
 | |
|       Result << "<dd class=\"param-descr-index-vararg\">";
 | |
|     else
 | |
|       Result << "<dd class=\"param-descr-index-"
 | |
|              << C->getParamIndex()
 | |
|              << "\">";
 | |
|   } else
 | |
|     Result << "<dd class=\"param-descr-index-invalid\">";
 | |
| 
 | |
|   visitNonStandaloneParagraphComment(C->getParagraph());
 | |
|   Result << "</dd>";
 | |
| }
 | |
| 
 | |
| void CommentASTToHTMLConverter::visitTParamCommandComment(
 | |
|                                   const TParamCommandComment *C) {
 | |
|   if (C->isPositionValid()) {
 | |
|     if (C->getDepth() == 1)
 | |
|       Result << "<dt class=\"tparam-name-index-"
 | |
|              << C->getIndex(0)
 | |
|              << "\">";
 | |
|     else
 | |
|       Result << "<dt class=\"tparam-name-index-other\">";
 | |
|     appendToResultWithHTMLEscaping(C->getParamName(FC));
 | |
|   } else {
 | |
|     Result << "<dt class=\"tparam-name-index-invalid\">";
 | |
|     appendToResultWithHTMLEscaping(C->getParamNameAsWritten());
 | |
|   }
 | |
| 
 | |
|   Result << "</dt>";
 | |
| 
 | |
|   if (C->isPositionValid()) {
 | |
|     if (C->getDepth() == 1)
 | |
|       Result << "<dd class=\"tparam-descr-index-"
 | |
|              << C->getIndex(0)
 | |
|              << "\">";
 | |
|     else
 | |
|       Result << "<dd class=\"tparam-descr-index-other\">";
 | |
|   } else
 | |
|     Result << "<dd class=\"tparam-descr-index-invalid\">";
 | |
| 
 | |
|   visitNonStandaloneParagraphComment(C->getParagraph());
 | |
|   Result << "</dd>";
 | |
| }
 | |
| 
 | |
| void CommentASTToHTMLConverter::visitVerbatimBlockComment(
 | |
|                                   const VerbatimBlockComment *C) {
 | |
|   unsigned NumLines = C->getNumLines();
 | |
|   if (NumLines == 0)
 | |
|     return;
 | |
| 
 | |
|   Result << "<pre>";
 | |
|   for (unsigned i = 0; i != NumLines; ++i) {
 | |
|     appendToResultWithHTMLEscaping(C->getText(i));
 | |
|     if (i + 1 != NumLines)
 | |
|       Result << '\n';
 | |
|   }
 | |
|   Result << "</pre>";
 | |
| }
 | |
| 
 | |
| void CommentASTToHTMLConverter::visitVerbatimBlockLineComment(
 | |
|                                   const VerbatimBlockLineComment *C) {
 | |
|   llvm_unreachable("should not see this AST node");
 | |
| }
 | |
| 
 | |
| void CommentASTToHTMLConverter::visitVerbatimLineComment(
 | |
|                                   const VerbatimLineComment *C) {
 | |
|   Result << "<pre>";
 | |
|   appendToResultWithHTMLEscaping(C->getText());
 | |
|   Result << "</pre>";
 | |
| }
 | |
| 
 | |
| void CommentASTToHTMLConverter::visitFullComment(const FullComment *C) {
 | |
|   FullCommentParts Parts(C, Traits);
 | |
| 
 | |
|   bool FirstParagraphIsBrief = false;
 | |
|   if (Parts.Headerfile)
 | |
|     visit(Parts.Headerfile);
 | |
|   if (Parts.Brief)
 | |
|     visit(Parts.Brief);
 | |
|   else if (Parts.FirstParagraph) {
 | |
|     Result << "<p class=\"para-brief\">";
 | |
|     visitNonStandaloneParagraphComment(Parts.FirstParagraph);
 | |
|     Result << "</p>";
 | |
|     FirstParagraphIsBrief = true;
 | |
|   }
 | |
| 
 | |
|   for (unsigned i = 0, e = Parts.MiscBlocks.size(); i != e; ++i) {
 | |
|     const Comment *C = Parts.MiscBlocks[i];
 | |
|     if (FirstParagraphIsBrief && C == Parts.FirstParagraph)
 | |
|       continue;
 | |
|     visit(C);
 | |
|   }
 | |
| 
 | |
|   if (Parts.TParams.size() != 0) {
 | |
|     Result << "<dl>";
 | |
|     for (unsigned i = 0, e = Parts.TParams.size(); i != e; ++i)
 | |
|       visit(Parts.TParams[i]);
 | |
|     Result << "</dl>";
 | |
|   }
 | |
| 
 | |
|   if (Parts.Params.size() != 0) {
 | |
|     Result << "<dl>";
 | |
|     for (unsigned i = 0, e = Parts.Params.size(); i != e; ++i)
 | |
|       visit(Parts.Params[i]);
 | |
|     Result << "</dl>";
 | |
|   }
 | |
| 
 | |
|   if (Parts.Returns.size() != 0) {
 | |
|     Result << "<div class=\"result-discussion\">";
 | |
|     for (unsigned i = 0, e = Parts.Returns.size(); i != e; ++i)
 | |
|       visit(Parts.Returns[i]);
 | |
|     Result << "</div>";
 | |
|   }
 | |
| 
 | |
| }
 | |
| 
 | |
| void CommentASTToHTMLConverter::visitNonStandaloneParagraphComment(
 | |
|                                   const ParagraphComment *C) {
 | |
|   if (!C)
 | |
|     return;
 | |
| 
 | |
|   for (Comment::child_iterator I = C->child_begin(), E = C->child_end();
 | |
|        I != E; ++I) {
 | |
|     visit(*I);
 | |
|   }
 | |
| }
 | |
| 
 | |
| void CommentASTToHTMLConverter::appendToResultWithHTMLEscaping(StringRef S) {
 | |
|   for (StringRef::iterator I = S.begin(), E = S.end(); I != E; ++I) {
 | |
|     const char C = *I;
 | |
|     switch (C) {
 | |
|     case '&':
 | |
|       Result << "&";
 | |
|       break;
 | |
|     case '<':
 | |
|       Result << "<";
 | |
|       break;
 | |
|     case '>':
 | |
|       Result << ">";
 | |
|       break;
 | |
|     case '"':
 | |
|       Result << """;
 | |
|       break;
 | |
|     case '\'':
 | |
|       Result << "'";
 | |
|       break;
 | |
|     case '/':
 | |
|       Result << "/";
 | |
|       break;
 | |
|     default:
 | |
|       Result << C;
 | |
|       break;
 | |
|     }
 | |
|   }
 | |
| }
 | |
| 
 | |
| namespace {
 | |
| class CommentASTToXMLConverter :
 | |
|     public ConstCommentVisitor<CommentASTToXMLConverter> {
 | |
| public:
 | |
|   /// \param Str accumulator for XML.
 | |
|   CommentASTToXMLConverter(const FullComment *FC,
 | |
|                            SmallVectorImpl<char> &Str,
 | |
|                            const CommandTraits &Traits,
 | |
|                            const SourceManager &SM) :
 | |
|       FC(FC), Result(Str), Traits(Traits), SM(SM) { }
 | |
| 
 | |
|   // Inline content.
 | |
|   void visitTextComment(const TextComment *C);
 | |
|   void visitInlineCommandComment(const InlineCommandComment *C);
 | |
|   void visitHTMLStartTagComment(const HTMLStartTagComment *C);
 | |
|   void visitHTMLEndTagComment(const HTMLEndTagComment *C);
 | |
| 
 | |
|   // Block content.
 | |
|   void visitParagraphComment(const ParagraphComment *C);
 | |
| 
 | |
|   void appendParagraphCommentWithKind(const ParagraphComment *C,
 | |
|                                       StringRef Kind);
 | |
| 
 | |
|   void visitBlockCommandComment(const BlockCommandComment *C);
 | |
|   void visitParamCommandComment(const ParamCommandComment *C);
 | |
|   void visitTParamCommandComment(const TParamCommandComment *C);
 | |
|   void visitVerbatimBlockComment(const VerbatimBlockComment *C);
 | |
|   void visitVerbatimBlockLineComment(const VerbatimBlockLineComment *C);
 | |
|   void visitVerbatimLineComment(const VerbatimLineComment *C);
 | |
| 
 | |
|   void visitFullComment(const FullComment *C);
 | |
| 
 | |
|   // Helpers.
 | |
|   void appendToResultWithXMLEscaping(StringRef S);
 | |
|   void appendToResultWithCDATAEscaping(StringRef S);
 | |
| 
 | |
|   void formatTextOfDeclaration(const DeclInfo *DI,
 | |
|                                SmallString<128> &Declaration);
 | |
| 
 | |
| private:
 | |
|   const FullComment *FC;
 | |
| 
 | |
|   /// Output stream for XML.
 | |
|   llvm::raw_svector_ostream Result;
 | |
| 
 | |
|   const CommandTraits &Traits;
 | |
|   const SourceManager &SM;
 | |
| };
 | |
| 
 | |
| void getSourceTextOfDeclaration(const DeclInfo *ThisDecl,
 | |
|                                 SmallVectorImpl<char> &Str) {
 | |
|   ASTContext &Context = ThisDecl->CurrentDecl->getASTContext();
 | |
|   const LangOptions &LangOpts = Context.getLangOpts();
 | |
|   llvm::raw_svector_ostream OS(Str);
 | |
|   PrintingPolicy PPolicy(LangOpts);
 | |
|   PPolicy.PolishForDeclaration = true;
 | |
|   PPolicy.TerseOutput = true;
 | |
|   PPolicy.ConstantsAsWritten = true;
 | |
|   ThisDecl->CurrentDecl->print(OS, PPolicy,
 | |
|                                /*Indentation*/0, /*PrintInstantiation*/false);
 | |
| }
 | |
| 
 | |
| void CommentASTToXMLConverter::formatTextOfDeclaration(
 | |
|     const DeclInfo *DI, SmallString<128> &Declaration) {
 | |
|   // Formatting API expects null terminated input string.
 | |
|   StringRef StringDecl(Declaration.c_str(), Declaration.size());
 | |
| 
 | |
|   // Formatter specific code.
 | |
|   unsigned Offset = 0;
 | |
|   unsigned Length = Declaration.size();
 | |
| 
 | |
|   format::FormatStyle Style = format::getLLVMStyle();
 | |
|   Style.FixNamespaceComments = false;
 | |
|   tooling::Replacements Replaces =
 | |
|       reformat(Style, StringDecl, tooling::Range(Offset, Length), "xmldecl.xd");
 | |
|   auto FormattedStringDecl = applyAllReplacements(StringDecl, Replaces);
 | |
|   if (static_cast<bool>(FormattedStringDecl)) {
 | |
|     Declaration = *FormattedStringDecl;
 | |
|   }
 | |
| }
 | |
| 
 | |
| } // end unnamed namespace
 | |
| 
 | |
| void CommentASTToXMLConverter::visitTextComment(const TextComment *C) {
 | |
|   appendToResultWithXMLEscaping(C->getText());
 | |
| }
 | |
| 
 | |
| void CommentASTToXMLConverter::visitInlineCommandComment(
 | |
|     const InlineCommandComment *C) {
 | |
|   // Nothing to render if no arguments supplied.
 | |
|   if (C->getNumArgs() == 0)
 | |
|     return;
 | |
| 
 | |
|   // Nothing to render if argument is empty.
 | |
|   StringRef Arg0 = C->getArgText(0);
 | |
|   if (Arg0.empty())
 | |
|     return;
 | |
| 
 | |
|   switch (C->getRenderKind()) {
 | |
|   case InlineCommandComment::RenderNormal:
 | |
|     for (unsigned i = 0, e = C->getNumArgs(); i != e; ++i) {
 | |
|       appendToResultWithXMLEscaping(C->getArgText(i));
 | |
|       Result << " ";
 | |
|     }
 | |
|     return;
 | |
|   case InlineCommandComment::RenderBold:
 | |
|     assert(C->getNumArgs() == 1);
 | |
|     Result << "<bold>";
 | |
|     appendToResultWithXMLEscaping(Arg0);
 | |
|     Result << "</bold>";
 | |
|     return;
 | |
|   case InlineCommandComment::RenderMonospaced:
 | |
|     assert(C->getNumArgs() == 1);
 | |
|     Result << "<monospaced>";
 | |
|     appendToResultWithXMLEscaping(Arg0);
 | |
|     Result << "</monospaced>";
 | |
|     return;
 | |
|   case InlineCommandComment::RenderEmphasized:
 | |
|     assert(C->getNumArgs() == 1);
 | |
|     Result << "<emphasized>";
 | |
|     appendToResultWithXMLEscaping(Arg0);
 | |
|     Result << "</emphasized>";
 | |
|     return;
 | |
|   case InlineCommandComment::RenderAnchor:
 | |
|     assert(C->getNumArgs() == 1);
 | |
|     Result << "<anchor id=\"" << Arg0 << "\"></anchor>";
 | |
|     return;
 | |
|   }
 | |
| }
 | |
| 
 | |
| void CommentASTToXMLConverter::visitHTMLStartTagComment(
 | |
|     const HTMLStartTagComment *C) {
 | |
|   Result << "<rawHTML";
 | |
|   if (C->isMalformed())
 | |
|     Result << " isMalformed=\"1\"";
 | |
|   Result << ">";
 | |
|   {
 | |
|     SmallString<32> Tag;
 | |
|     {
 | |
|       llvm::raw_svector_ostream TagOS(Tag);
 | |
|       printHTMLStartTagComment(C, TagOS);
 | |
|     }
 | |
|     appendToResultWithCDATAEscaping(Tag);
 | |
|   }
 | |
|   Result << "</rawHTML>";
 | |
| }
 | |
| 
 | |
| void
 | |
| CommentASTToXMLConverter::visitHTMLEndTagComment(const HTMLEndTagComment *C) {
 | |
|   Result << "<rawHTML";
 | |
|   if (C->isMalformed())
 | |
|     Result << " isMalformed=\"1\"";
 | |
|   Result << "></" << C->getTagName() << "></rawHTML>";
 | |
| }
 | |
| 
 | |
| void
 | |
| CommentASTToXMLConverter::visitParagraphComment(const ParagraphComment *C) {
 | |
|   appendParagraphCommentWithKind(C, StringRef());
 | |
| }
 | |
| 
 | |
| void CommentASTToXMLConverter::appendParagraphCommentWithKind(
 | |
|                                   const ParagraphComment *C,
 | |
|                                   StringRef ParagraphKind) {
 | |
|   if (C->isWhitespace())
 | |
|     return;
 | |
| 
 | |
|   if (ParagraphKind.empty())
 | |
|     Result << "<Para>";
 | |
|   else
 | |
|     Result << "<Para kind=\"" << ParagraphKind << "\">";
 | |
| 
 | |
|   for (Comment::child_iterator I = C->child_begin(), E = C->child_end();
 | |
|        I != E; ++I) {
 | |
|     visit(*I);
 | |
|   }
 | |
|   Result << "</Para>";
 | |
| }
 | |
| 
 | |
| void CommentASTToXMLConverter::visitBlockCommandComment(
 | |
|     const BlockCommandComment *C) {
 | |
|   StringRef ParagraphKind;
 | |
| 
 | |
|   switch (C->getCommandID()) {
 | |
|   case CommandTraits::KCI_attention:
 | |
|   case CommandTraits::KCI_author:
 | |
|   case CommandTraits::KCI_authors:
 | |
|   case CommandTraits::KCI_bug:
 | |
|   case CommandTraits::KCI_copyright:
 | |
|   case CommandTraits::KCI_date:
 | |
|   case CommandTraits::KCI_invariant:
 | |
|   case CommandTraits::KCI_note:
 | |
|   case CommandTraits::KCI_post:
 | |
|   case CommandTraits::KCI_pre:
 | |
|   case CommandTraits::KCI_remark:
 | |
|   case CommandTraits::KCI_remarks:
 | |
|   case CommandTraits::KCI_sa:
 | |
|   case CommandTraits::KCI_see:
 | |
|   case CommandTraits::KCI_since:
 | |
|   case CommandTraits::KCI_todo:
 | |
|   case CommandTraits::KCI_version:
 | |
|   case CommandTraits::KCI_warning:
 | |
|     ParagraphKind = C->getCommandName(Traits);
 | |
|     break;
 | |
|   default:
 | |
|     break;
 | |
|   }
 | |
| 
 | |
|   appendParagraphCommentWithKind(C->getParagraph(), ParagraphKind);
 | |
| }
 | |
| 
 | |
| void CommentASTToXMLConverter::visitParamCommandComment(
 | |
|     const ParamCommandComment *C) {
 | |
|   Result << "<Parameter><Name>";
 | |
|   appendToResultWithXMLEscaping(C->isParamIndexValid()
 | |
|                                     ? C->getParamName(FC)
 | |
|                                     : C->getParamNameAsWritten());
 | |
|   Result << "</Name>";
 | |
| 
 | |
|   if (C->isParamIndexValid()) {
 | |
|     if (C->isVarArgParam())
 | |
|       Result << "<IsVarArg />";
 | |
|     else
 | |
|       Result << "<Index>" << C->getParamIndex() << "</Index>";
 | |
|   }
 | |
| 
 | |
|   Result << "<Direction isExplicit=\"" << C->isDirectionExplicit() << "\">";
 | |
|   switch (C->getDirection()) {
 | |
|   case ParamCommandComment::In:
 | |
|     Result << "in";
 | |
|     break;
 | |
|   case ParamCommandComment::Out:
 | |
|     Result << "out";
 | |
|     break;
 | |
|   case ParamCommandComment::InOut:
 | |
|     Result << "in,out";
 | |
|     break;
 | |
|   }
 | |
|   Result << "</Direction><Discussion>";
 | |
|   visit(C->getParagraph());
 | |
|   Result << "</Discussion></Parameter>";
 | |
| }
 | |
| 
 | |
| void CommentASTToXMLConverter::visitTParamCommandComment(
 | |
|                                   const TParamCommandComment *C) {
 | |
|   Result << "<Parameter><Name>";
 | |
|   appendToResultWithXMLEscaping(C->isPositionValid() ? C->getParamName(FC)
 | |
|                                 : C->getParamNameAsWritten());
 | |
|   Result << "</Name>";
 | |
| 
 | |
|   if (C->isPositionValid() && C->getDepth() == 1) {
 | |
|     Result << "<Index>" << C->getIndex(0) << "</Index>";
 | |
|   }
 | |
| 
 | |
|   Result << "<Discussion>";
 | |
|   visit(C->getParagraph());
 | |
|   Result << "</Discussion></Parameter>";
 | |
| }
 | |
| 
 | |
| void CommentASTToXMLConverter::visitVerbatimBlockComment(
 | |
|                                   const VerbatimBlockComment *C) {
 | |
|   unsigned NumLines = C->getNumLines();
 | |
|   if (NumLines == 0)
 | |
|     return;
 | |
| 
 | |
|   switch (C->getCommandID()) {
 | |
|   case CommandTraits::KCI_code:
 | |
|     Result << "<Verbatim xml:space=\"preserve\" kind=\"code\">";
 | |
|     break;
 | |
|   default:
 | |
|     Result << "<Verbatim xml:space=\"preserve\" kind=\"verbatim\">";
 | |
|     break;
 | |
|   }
 | |
|   for (unsigned i = 0; i != NumLines; ++i) {
 | |
|     appendToResultWithXMLEscaping(C->getText(i));
 | |
|     if (i + 1 != NumLines)
 | |
|       Result << '\n';
 | |
|   }
 | |
|   Result << "</Verbatim>";
 | |
| }
 | |
| 
 | |
| void CommentASTToXMLConverter::visitVerbatimBlockLineComment(
 | |
|                                   const VerbatimBlockLineComment *C) {
 | |
|   llvm_unreachable("should not see this AST node");
 | |
| }
 | |
| 
 | |
| void CommentASTToXMLConverter::visitVerbatimLineComment(
 | |
|                                   const VerbatimLineComment *C) {
 | |
|   Result << "<Verbatim xml:space=\"preserve\" kind=\"verbatim\">";
 | |
|   appendToResultWithXMLEscaping(C->getText());
 | |
|   Result << "</Verbatim>";
 | |
| }
 | |
| 
 | |
| void CommentASTToXMLConverter::visitFullComment(const FullComment *C) {
 | |
|   FullCommentParts Parts(C, Traits);
 | |
| 
 | |
|   const DeclInfo *DI = C->getDeclInfo();
 | |
|   StringRef RootEndTag;
 | |
|   if (DI) {
 | |
|     switch (DI->getKind()) {
 | |
|     case DeclInfo::OtherKind:
 | |
|       RootEndTag = "</Other>";
 | |
|       Result << "<Other";
 | |
|       break;
 | |
|     case DeclInfo::FunctionKind:
 | |
|       RootEndTag = "</Function>";
 | |
|       Result << "<Function";
 | |
|       switch (DI->TemplateKind) {
 | |
|       case DeclInfo::NotTemplate:
 | |
|         break;
 | |
|       case DeclInfo::Template:
 | |
|         Result << " templateKind=\"template\"";
 | |
|         break;
 | |
|       case DeclInfo::TemplateSpecialization:
 | |
|         Result << " templateKind=\"specialization\"";
 | |
|         break;
 | |
|       case DeclInfo::TemplatePartialSpecialization:
 | |
|         llvm_unreachable("partial specializations of functions "
 | |
|                          "are not allowed in C++");
 | |
|       }
 | |
|       if (DI->IsInstanceMethod)
 | |
|         Result << " isInstanceMethod=\"1\"";
 | |
|       if (DI->IsClassMethod)
 | |
|         Result << " isClassMethod=\"1\"";
 | |
|       break;
 | |
|     case DeclInfo::ClassKind:
 | |
|       RootEndTag = "</Class>";
 | |
|       Result << "<Class";
 | |
|       switch (DI->TemplateKind) {
 | |
|       case DeclInfo::NotTemplate:
 | |
|         break;
 | |
|       case DeclInfo::Template:
 | |
|         Result << " templateKind=\"template\"";
 | |
|         break;
 | |
|       case DeclInfo::TemplateSpecialization:
 | |
|         Result << " templateKind=\"specialization\"";
 | |
|         break;
 | |
|       case DeclInfo::TemplatePartialSpecialization:
 | |
|         Result << " templateKind=\"partialSpecialization\"";
 | |
|         break;
 | |
|       }
 | |
|       break;
 | |
|     case DeclInfo::VariableKind:
 | |
|       RootEndTag = "</Variable>";
 | |
|       Result << "<Variable";
 | |
|       break;
 | |
|     case DeclInfo::NamespaceKind:
 | |
|       RootEndTag = "</Namespace>";
 | |
|       Result << "<Namespace";
 | |
|       break;
 | |
|     case DeclInfo::TypedefKind:
 | |
|       RootEndTag = "</Typedef>";
 | |
|       Result << "<Typedef";
 | |
|       break;
 | |
|     case DeclInfo::EnumKind:
 | |
|       RootEndTag = "</Enum>";
 | |
|       Result << "<Enum";
 | |
|       break;
 | |
|     }
 | |
| 
 | |
|     {
 | |
|       // Print line and column number.
 | |
|       SourceLocation Loc = DI->CurrentDecl->getLocation();
 | |
|       std::pair<FileID, unsigned> LocInfo = SM.getDecomposedLoc(Loc);
 | |
|       FileID FID = LocInfo.first;
 | |
|       unsigned FileOffset = LocInfo.second;
 | |
| 
 | |
|       if (FID.isValid()) {
 | |
|         if (const FileEntry *FE = SM.getFileEntryForID(FID)) {
 | |
|           Result << " file=\"";
 | |
|           appendToResultWithXMLEscaping(FE->getName());
 | |
|           Result << "\"";
 | |
|         }
 | |
|         Result << " line=\"" << SM.getLineNumber(FID, FileOffset)
 | |
|                << "\" column=\"" << SM.getColumnNumber(FID, FileOffset)
 | |
|                << "\"";
 | |
|       }
 | |
|     }
 | |
| 
 | |
|     // Finish the root tag.
 | |
|     Result << ">";
 | |
| 
 | |
|     bool FoundName = false;
 | |
|     if (const NamedDecl *ND = dyn_cast<NamedDecl>(DI->CommentDecl)) {
 | |
|       if (DeclarationName DeclName = ND->getDeclName()) {
 | |
|         Result << "<Name>";
 | |
|         std::string Name = DeclName.getAsString();
 | |
|         appendToResultWithXMLEscaping(Name);
 | |
|         FoundName = true;
 | |
|         Result << "</Name>";
 | |
|       }
 | |
|     }
 | |
|     if (!FoundName)
 | |
|       Result << "<Name><anonymous></Name>";
 | |
| 
 | |
|     {
 | |
|       // Print USR.
 | |
|       SmallString<128> USR;
 | |
|       generateUSRForDecl(DI->CommentDecl, USR);
 | |
|       if (!USR.empty()) {
 | |
|         Result << "<USR>";
 | |
|         appendToResultWithXMLEscaping(USR);
 | |
|         Result << "</USR>";
 | |
|       }
 | |
|     }
 | |
|   } else {
 | |
|     // No DeclInfo -- just emit some root tag and name tag.
 | |
|     RootEndTag = "</Other>";
 | |
|     Result << "<Other><Name>unknown</Name>";
 | |
|   }
 | |
| 
 | |
|   if (Parts.Headerfile) {
 | |
|     Result << "<Headerfile>";
 | |
|     visit(Parts.Headerfile);
 | |
|     Result << "</Headerfile>";
 | |
|   }
 | |
| 
 | |
|   {
 | |
|     // Pretty-print the declaration.
 | |
|     Result << "<Declaration>";
 | |
|     SmallString<128> Declaration;
 | |
|     getSourceTextOfDeclaration(DI, Declaration);
 | |
|     formatTextOfDeclaration(DI, Declaration);
 | |
|     appendToResultWithXMLEscaping(Declaration);
 | |
|     Result << "</Declaration>";
 | |
|   }
 | |
| 
 | |
|   bool FirstParagraphIsBrief = false;
 | |
|   if (Parts.Brief) {
 | |
|     Result << "<Abstract>";
 | |
|     visit(Parts.Brief);
 | |
|     Result << "</Abstract>";
 | |
|   } else if (Parts.FirstParagraph) {
 | |
|     Result << "<Abstract>";
 | |
|     visit(Parts.FirstParagraph);
 | |
|     Result << "</Abstract>";
 | |
|     FirstParagraphIsBrief = true;
 | |
|   }
 | |
| 
 | |
|   if (Parts.TParams.size() != 0) {
 | |
|     Result << "<TemplateParameters>";
 | |
|     for (unsigned i = 0, e = Parts.TParams.size(); i != e; ++i)
 | |
|       visit(Parts.TParams[i]);
 | |
|     Result << "</TemplateParameters>";
 | |
|   }
 | |
| 
 | |
|   if (Parts.Params.size() != 0) {
 | |
|     Result << "<Parameters>";
 | |
|     for (unsigned i = 0, e = Parts.Params.size(); i != e; ++i)
 | |
|       visit(Parts.Params[i]);
 | |
|     Result << "</Parameters>";
 | |
|   }
 | |
| 
 | |
|   if (Parts.Exceptions.size() != 0) {
 | |
|     Result << "<Exceptions>";
 | |
|     for (unsigned i = 0, e = Parts.Exceptions.size(); i != e; ++i)
 | |
|       visit(Parts.Exceptions[i]);
 | |
|     Result << "</Exceptions>";
 | |
|   }
 | |
| 
 | |
|   if (Parts.Returns.size() != 0) {
 | |
|     Result << "<ResultDiscussion>";
 | |
|     for (unsigned i = 0, e = Parts.Returns.size(); i != e; ++i)
 | |
|       visit(Parts.Returns[i]);
 | |
|     Result << "</ResultDiscussion>";
 | |
|   }
 | |
| 
 | |
|   if (DI->CommentDecl->hasAttrs()) {
 | |
|     const AttrVec &Attrs = DI->CommentDecl->getAttrs();
 | |
|     for (unsigned i = 0, e = Attrs.size(); i != e; i++) {
 | |
|       const AvailabilityAttr *AA = dyn_cast<AvailabilityAttr>(Attrs[i]);
 | |
|       if (!AA) {
 | |
|         if (const DeprecatedAttr *DA = dyn_cast<DeprecatedAttr>(Attrs[i])) {
 | |
|           if (DA->getMessage().empty())
 | |
|             Result << "<Deprecated/>";
 | |
|           else {
 | |
|             Result << "<Deprecated>";
 | |
|             appendToResultWithXMLEscaping(DA->getMessage());
 | |
|             Result << "</Deprecated>";
 | |
|           }
 | |
|         }
 | |
|         else if (const UnavailableAttr *UA = dyn_cast<UnavailableAttr>(Attrs[i])) {
 | |
|           if (UA->getMessage().empty())
 | |
|             Result << "<Unavailable/>";
 | |
|           else {
 | |
|             Result << "<Unavailable>";
 | |
|             appendToResultWithXMLEscaping(UA->getMessage());
 | |
|             Result << "</Unavailable>";
 | |
|           }
 | |
|         }
 | |
|         continue;
 | |
|       }
 | |
| 
 | |
|       // 'availability' attribute.
 | |
|       Result << "<Availability";
 | |
|       StringRef Distribution;
 | |
|       if (AA->getPlatform()) {
 | |
|         Distribution = AvailabilityAttr::getPrettyPlatformName(
 | |
|                                         AA->getPlatform()->getName());
 | |
|         if (Distribution.empty())
 | |
|           Distribution = AA->getPlatform()->getName();
 | |
|       }
 | |
|       Result << " distribution=\"" << Distribution << "\">";
 | |
|       VersionTuple IntroducedInVersion = AA->getIntroduced();
 | |
|       if (!IntroducedInVersion.empty()) {
 | |
|         Result << "<IntroducedInVersion>"
 | |
|                << IntroducedInVersion.getAsString()
 | |
|                << "</IntroducedInVersion>";
 | |
|       }
 | |
|       VersionTuple DeprecatedInVersion = AA->getDeprecated();
 | |
|       if (!DeprecatedInVersion.empty()) {
 | |
|         Result << "<DeprecatedInVersion>"
 | |
|                << DeprecatedInVersion.getAsString()
 | |
|                << "</DeprecatedInVersion>";
 | |
|       }
 | |
|       VersionTuple RemovedAfterVersion = AA->getObsoleted();
 | |
|       if (!RemovedAfterVersion.empty()) {
 | |
|         Result << "<RemovedAfterVersion>"
 | |
|                << RemovedAfterVersion.getAsString()
 | |
|                << "</RemovedAfterVersion>";
 | |
|       }
 | |
|       StringRef DeprecationSummary = AA->getMessage();
 | |
|       if (!DeprecationSummary.empty()) {
 | |
|         Result << "<DeprecationSummary>";
 | |
|         appendToResultWithXMLEscaping(DeprecationSummary);
 | |
|         Result << "</DeprecationSummary>";
 | |
|       }
 | |
|       if (AA->getUnavailable())
 | |
|         Result << "<Unavailable/>";
 | |
|       Result << "</Availability>";
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   {
 | |
|     bool StartTagEmitted = false;
 | |
|     for (unsigned i = 0, e = Parts.MiscBlocks.size(); i != e; ++i) {
 | |
|       const Comment *C = Parts.MiscBlocks[i];
 | |
|       if (FirstParagraphIsBrief && C == Parts.FirstParagraph)
 | |
|         continue;
 | |
|       if (!StartTagEmitted) {
 | |
|         Result << "<Discussion>";
 | |
|         StartTagEmitted = true;
 | |
|       }
 | |
|       visit(C);
 | |
|     }
 | |
|     if (StartTagEmitted)
 | |
|       Result << "</Discussion>";
 | |
|   }
 | |
| 
 | |
|   Result << RootEndTag;
 | |
| }
 | |
| 
 | |
| void CommentASTToXMLConverter::appendToResultWithXMLEscaping(StringRef S) {
 | |
|   for (StringRef::iterator I = S.begin(), E = S.end(); I != E; ++I) {
 | |
|     const char C = *I;
 | |
|     switch (C) {
 | |
|     case '&':
 | |
|       Result << "&";
 | |
|       break;
 | |
|     case '<':
 | |
|       Result << "<";
 | |
|       break;
 | |
|     case '>':
 | |
|       Result << ">";
 | |
|       break;
 | |
|     case '"':
 | |
|       Result << """;
 | |
|       break;
 | |
|     case '\'':
 | |
|       Result << "'";
 | |
|       break;
 | |
|     default:
 | |
|       Result << C;
 | |
|       break;
 | |
|     }
 | |
|   }
 | |
| }
 | |
| 
 | |
| void CommentASTToXMLConverter::appendToResultWithCDATAEscaping(StringRef S) {
 | |
|   if (S.empty())
 | |
|     return;
 | |
| 
 | |
|   Result << "<![CDATA[";
 | |
|   while (!S.empty()) {
 | |
|     size_t Pos = S.find("]]>");
 | |
|     if (Pos == 0) {
 | |
|       Result << "]]]]><![CDATA[>";
 | |
|       S = S.drop_front(3);
 | |
|       continue;
 | |
|     }
 | |
|     if (Pos == StringRef::npos)
 | |
|       Pos = S.size();
 | |
| 
 | |
|     Result << S.substr(0, Pos);
 | |
| 
 | |
|     S = S.drop_front(Pos);
 | |
|   }
 | |
|   Result << "]]>";
 | |
| }
 | |
| 
 | |
| CommentToXMLConverter::CommentToXMLConverter() {}
 | |
| CommentToXMLConverter::~CommentToXMLConverter() {}
 | |
| 
 | |
| void CommentToXMLConverter::convertCommentToHTML(const FullComment *FC,
 | |
|                                                  SmallVectorImpl<char> &HTML,
 | |
|                                                  const ASTContext &Context) {
 | |
|   CommentASTToHTMLConverter Converter(FC, HTML,
 | |
|                                       Context.getCommentCommandTraits());
 | |
|   Converter.visit(FC);
 | |
| }
 | |
| 
 | |
| void CommentToXMLConverter::convertHTMLTagNodeToText(
 | |
|     const comments::HTMLTagComment *HTC, SmallVectorImpl<char> &Text,
 | |
|     const ASTContext &Context) {
 | |
|   CommentASTToHTMLConverter Converter(nullptr, Text,
 | |
|                                       Context.getCommentCommandTraits());
 | |
|   Converter.visit(HTC);
 | |
| }
 | |
| 
 | |
| void CommentToXMLConverter::convertCommentToXML(const FullComment *FC,
 | |
|                                                 SmallVectorImpl<char> &XML,
 | |
|                                                 const ASTContext &Context) {
 | |
|   CommentASTToXMLConverter Converter(FC, XML, Context.getCommentCommandTraits(),
 | |
|                                      Context.getSourceManager());
 | |
|   Converter.visit(FC);
 | |
| }
 |