forked from OSchip/llvm-project
				
			
		
			
				
	
	
		
			1145 lines
		
	
	
		
			34 KiB
		
	
	
	
		
			C++
		
	
	
	
			
		
		
	
	
			1145 lines
		
	
	
		
			34 KiB
		
	
	
	
		
			C++
		
	
	
	
//===--- CommentToXML.cpp - Convert comments to XML representation --------===//
 | 
						|
//
 | 
						|
//                     The LLVM Compiler Infrastructure
 | 
						|
//
 | 
						|
// This file is distributed under the University of Illinois Open Source
 | 
						|
// License. See LICENSE.TXT for details.
 | 
						|
//
 | 
						|
//===----------------------------------------------------------------------===//
 | 
						|
 | 
						|
#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/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.
 | 
						|
  std::stable_sort(Params.begin(), Params.end(),
 | 
						|
                   ParamCommandCommentCompareIndex());
 | 
						|
 | 
						|
  std::stable_sort(TParams.begin(), TParams.end(),
 | 
						|
                   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;
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
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;
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
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);
 | 
						|
  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);
 | 
						|
}
 |