forked from OSchip/llvm-project
1473 lines
44 KiB
C++
1473 lines
44 KiB
C++
//===- CXComment.cpp - libclang APIs for manipulating CXComments ----------===//
|
|
//
|
|
// The LLVM Compiler Infrastructure
|
|
//
|
|
// This file is distributed under the University of Illinois Open Source
|
|
// License. See LICENSE.TXT for details.
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
//
|
|
// This file defines all libclang APIs related to walking comment AST.
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#include "clang-c/Index.h"
|
|
#include "CXComment.h"
|
|
#include "CXCursor.h"
|
|
#include "CXString.h"
|
|
#include "SimpleFormatContext.h"
|
|
#include "clang/AST/CommentCommandTraits.h"
|
|
#include "clang/AST/CommentVisitor.h"
|
|
#include "clang/AST/Decl.h"
|
|
#include "clang/AST/PrettyPrinter.h"
|
|
#include "clang/Format/Format.h"
|
|
#include "clang/Lex/Lexer.h"
|
|
#include "llvm/ADT/StringExtras.h"
|
|
#include "llvm/ADT/StringSwitch.h"
|
|
#include "llvm/Support/ErrorHandling.h"
|
|
#include "llvm/Support/raw_ostream.h"
|
|
#include <climits>
|
|
|
|
using namespace clang;
|
|
using namespace clang::comments;
|
|
using namespace clang::cxcomment;
|
|
|
|
extern "C" {
|
|
|
|
enum CXCommentKind clang_Comment_getKind(CXComment CXC) {
|
|
const Comment *C = getASTNode(CXC);
|
|
if (!C)
|
|
return CXComment_Null;
|
|
|
|
switch (C->getCommentKind()) {
|
|
case Comment::NoCommentKind:
|
|
return CXComment_Null;
|
|
|
|
case Comment::TextCommentKind:
|
|
return CXComment_Text;
|
|
|
|
case Comment::InlineCommandCommentKind:
|
|
return CXComment_InlineCommand;
|
|
|
|
case Comment::HTMLStartTagCommentKind:
|
|
return CXComment_HTMLStartTag;
|
|
|
|
case Comment::HTMLEndTagCommentKind:
|
|
return CXComment_HTMLEndTag;
|
|
|
|
case Comment::ParagraphCommentKind:
|
|
return CXComment_Paragraph;
|
|
|
|
case Comment::BlockCommandCommentKind:
|
|
return CXComment_BlockCommand;
|
|
|
|
case Comment::ParamCommandCommentKind:
|
|
return CXComment_ParamCommand;
|
|
|
|
case Comment::TParamCommandCommentKind:
|
|
return CXComment_TParamCommand;
|
|
|
|
case Comment::VerbatimBlockCommentKind:
|
|
return CXComment_VerbatimBlockCommand;
|
|
|
|
case Comment::VerbatimBlockLineCommentKind:
|
|
return CXComment_VerbatimBlockLine;
|
|
|
|
case Comment::VerbatimLineCommentKind:
|
|
return CXComment_VerbatimLine;
|
|
|
|
case Comment::FullCommentKind:
|
|
return CXComment_FullComment;
|
|
}
|
|
llvm_unreachable("unknown CommentKind");
|
|
}
|
|
|
|
unsigned clang_Comment_getNumChildren(CXComment CXC) {
|
|
const Comment *C = getASTNode(CXC);
|
|
if (!C)
|
|
return 0;
|
|
|
|
return C->child_count();
|
|
}
|
|
|
|
CXComment clang_Comment_getChild(CXComment CXC, unsigned ChildIdx) {
|
|
const Comment *C = getASTNode(CXC);
|
|
if (!C || ChildIdx >= C->child_count())
|
|
return createCXComment(NULL, NULL);
|
|
|
|
return createCXComment(*(C->child_begin() + ChildIdx), CXC.TranslationUnit);
|
|
}
|
|
|
|
unsigned clang_Comment_isWhitespace(CXComment CXC) {
|
|
const Comment *C = getASTNode(CXC);
|
|
if (!C)
|
|
return false;
|
|
|
|
if (const TextComment *TC = dyn_cast<TextComment>(C))
|
|
return TC->isWhitespace();
|
|
|
|
if (const ParagraphComment *PC = dyn_cast<ParagraphComment>(C))
|
|
return PC->isWhitespace();
|
|
|
|
return false;
|
|
}
|
|
|
|
unsigned clang_InlineContentComment_hasTrailingNewline(CXComment CXC) {
|
|
const InlineContentComment *ICC = getASTNodeAs<InlineContentComment>(CXC);
|
|
if (!ICC)
|
|
return false;
|
|
|
|
return ICC->hasTrailingNewline();
|
|
}
|
|
|
|
CXString clang_TextComment_getText(CXComment CXC) {
|
|
const TextComment *TC = getASTNodeAs<TextComment>(CXC);
|
|
if (!TC)
|
|
return cxstring::createNull();
|
|
|
|
return cxstring::createRef(TC->getText());
|
|
}
|
|
|
|
CXString clang_InlineCommandComment_getCommandName(CXComment CXC) {
|
|
const InlineCommandComment *ICC = getASTNodeAs<InlineCommandComment>(CXC);
|
|
if (!ICC)
|
|
return cxstring::createNull();
|
|
|
|
const CommandTraits &Traits = getCommandTraits(CXC);
|
|
return cxstring::createRef(ICC->getCommandName(Traits));
|
|
}
|
|
|
|
enum CXCommentInlineCommandRenderKind
|
|
clang_InlineCommandComment_getRenderKind(CXComment CXC) {
|
|
const InlineCommandComment *ICC = getASTNodeAs<InlineCommandComment>(CXC);
|
|
if (!ICC)
|
|
return CXCommentInlineCommandRenderKind_Normal;
|
|
|
|
switch (ICC->getRenderKind()) {
|
|
case InlineCommandComment::RenderNormal:
|
|
return CXCommentInlineCommandRenderKind_Normal;
|
|
|
|
case InlineCommandComment::RenderBold:
|
|
return CXCommentInlineCommandRenderKind_Bold;
|
|
|
|
case InlineCommandComment::RenderMonospaced:
|
|
return CXCommentInlineCommandRenderKind_Monospaced;
|
|
|
|
case InlineCommandComment::RenderEmphasized:
|
|
return CXCommentInlineCommandRenderKind_Emphasized;
|
|
}
|
|
llvm_unreachable("unknown InlineCommandComment::RenderKind");
|
|
}
|
|
|
|
unsigned clang_InlineCommandComment_getNumArgs(CXComment CXC) {
|
|
const InlineCommandComment *ICC = getASTNodeAs<InlineCommandComment>(CXC);
|
|
if (!ICC)
|
|
return 0;
|
|
|
|
return ICC->getNumArgs();
|
|
}
|
|
|
|
CXString clang_InlineCommandComment_getArgText(CXComment CXC,
|
|
unsigned ArgIdx) {
|
|
const InlineCommandComment *ICC = getASTNodeAs<InlineCommandComment>(CXC);
|
|
if (!ICC || ArgIdx >= ICC->getNumArgs())
|
|
return cxstring::createNull();
|
|
|
|
return cxstring::createRef(ICC->getArgText(ArgIdx));
|
|
}
|
|
|
|
CXString clang_HTMLTagComment_getTagName(CXComment CXC) {
|
|
const HTMLTagComment *HTC = getASTNodeAs<HTMLTagComment>(CXC);
|
|
if (!HTC)
|
|
return cxstring::createNull();
|
|
|
|
return cxstring::createRef(HTC->getTagName());
|
|
}
|
|
|
|
unsigned clang_HTMLStartTagComment_isSelfClosing(CXComment CXC) {
|
|
const HTMLStartTagComment *HST = getASTNodeAs<HTMLStartTagComment>(CXC);
|
|
if (!HST)
|
|
return false;
|
|
|
|
return HST->isSelfClosing();
|
|
}
|
|
|
|
unsigned clang_HTMLStartTag_getNumAttrs(CXComment CXC) {
|
|
const HTMLStartTagComment *HST = getASTNodeAs<HTMLStartTagComment>(CXC);
|
|
if (!HST)
|
|
return 0;
|
|
|
|
return HST->getNumAttrs();
|
|
}
|
|
|
|
CXString clang_HTMLStartTag_getAttrName(CXComment CXC, unsigned AttrIdx) {
|
|
const HTMLStartTagComment *HST = getASTNodeAs<HTMLStartTagComment>(CXC);
|
|
if (!HST || AttrIdx >= HST->getNumAttrs())
|
|
return cxstring::createNull();
|
|
|
|
return cxstring::createRef(HST->getAttr(AttrIdx).Name);
|
|
}
|
|
|
|
CXString clang_HTMLStartTag_getAttrValue(CXComment CXC, unsigned AttrIdx) {
|
|
const HTMLStartTagComment *HST = getASTNodeAs<HTMLStartTagComment>(CXC);
|
|
if (!HST || AttrIdx >= HST->getNumAttrs())
|
|
return cxstring::createNull();
|
|
|
|
return cxstring::createRef(HST->getAttr(AttrIdx).Value);
|
|
}
|
|
|
|
CXString clang_BlockCommandComment_getCommandName(CXComment CXC) {
|
|
const BlockCommandComment *BCC = getASTNodeAs<BlockCommandComment>(CXC);
|
|
if (!BCC)
|
|
return cxstring::createNull();
|
|
|
|
const CommandTraits &Traits = getCommandTraits(CXC);
|
|
return cxstring::createRef(BCC->getCommandName(Traits));
|
|
}
|
|
|
|
unsigned clang_BlockCommandComment_getNumArgs(CXComment CXC) {
|
|
const BlockCommandComment *BCC = getASTNodeAs<BlockCommandComment>(CXC);
|
|
if (!BCC)
|
|
return 0;
|
|
|
|
return BCC->getNumArgs();
|
|
}
|
|
|
|
CXString clang_BlockCommandComment_getArgText(CXComment CXC,
|
|
unsigned ArgIdx) {
|
|
const BlockCommandComment *BCC = getASTNodeAs<BlockCommandComment>(CXC);
|
|
if (!BCC || ArgIdx >= BCC->getNumArgs())
|
|
return cxstring::createNull();
|
|
|
|
return cxstring::createRef(BCC->getArgText(ArgIdx));
|
|
}
|
|
|
|
CXComment clang_BlockCommandComment_getParagraph(CXComment CXC) {
|
|
const BlockCommandComment *BCC = getASTNodeAs<BlockCommandComment>(CXC);
|
|
if (!BCC)
|
|
return createCXComment(NULL, NULL);
|
|
|
|
return createCXComment(BCC->getParagraph(), CXC.TranslationUnit);
|
|
}
|
|
|
|
CXString clang_ParamCommandComment_getParamName(CXComment CXC) {
|
|
const ParamCommandComment *PCC = getASTNodeAs<ParamCommandComment>(CXC);
|
|
if (!PCC || !PCC->hasParamName())
|
|
return cxstring::createNull();
|
|
|
|
return cxstring::createRef(PCC->getParamNameAsWritten());
|
|
}
|
|
|
|
unsigned clang_ParamCommandComment_isParamIndexValid(CXComment CXC) {
|
|
const ParamCommandComment *PCC = getASTNodeAs<ParamCommandComment>(CXC);
|
|
if (!PCC)
|
|
return false;
|
|
|
|
return PCC->isParamIndexValid();
|
|
}
|
|
|
|
unsigned clang_ParamCommandComment_getParamIndex(CXComment CXC) {
|
|
const ParamCommandComment *PCC = getASTNodeAs<ParamCommandComment>(CXC);
|
|
if (!PCC || !PCC->isParamIndexValid() || PCC->isVarArgParam())
|
|
return ParamCommandComment::InvalidParamIndex;
|
|
|
|
return PCC->getParamIndex();
|
|
}
|
|
|
|
unsigned clang_ParamCommandComment_isDirectionExplicit(CXComment CXC) {
|
|
const ParamCommandComment *PCC = getASTNodeAs<ParamCommandComment>(CXC);
|
|
if (!PCC)
|
|
return false;
|
|
|
|
return PCC->isDirectionExplicit();
|
|
}
|
|
|
|
enum CXCommentParamPassDirection clang_ParamCommandComment_getDirection(
|
|
CXComment CXC) {
|
|
const ParamCommandComment *PCC = getASTNodeAs<ParamCommandComment>(CXC);
|
|
if (!PCC)
|
|
return CXCommentParamPassDirection_In;
|
|
|
|
switch (PCC->getDirection()) {
|
|
case ParamCommandComment::In:
|
|
return CXCommentParamPassDirection_In;
|
|
|
|
case ParamCommandComment::Out:
|
|
return CXCommentParamPassDirection_Out;
|
|
|
|
case ParamCommandComment::InOut:
|
|
return CXCommentParamPassDirection_InOut;
|
|
}
|
|
llvm_unreachable("unknown ParamCommandComment::PassDirection");
|
|
}
|
|
|
|
CXString clang_TParamCommandComment_getParamName(CXComment CXC) {
|
|
const TParamCommandComment *TPCC = getASTNodeAs<TParamCommandComment>(CXC);
|
|
if (!TPCC || !TPCC->hasParamName())
|
|
return cxstring::createNull();
|
|
|
|
return cxstring::createRef(TPCC->getParamNameAsWritten());
|
|
}
|
|
|
|
unsigned clang_TParamCommandComment_isParamPositionValid(CXComment CXC) {
|
|
const TParamCommandComment *TPCC = getASTNodeAs<TParamCommandComment>(CXC);
|
|
if (!TPCC)
|
|
return false;
|
|
|
|
return TPCC->isPositionValid();
|
|
}
|
|
|
|
unsigned clang_TParamCommandComment_getDepth(CXComment CXC) {
|
|
const TParamCommandComment *TPCC = getASTNodeAs<TParamCommandComment>(CXC);
|
|
if (!TPCC || !TPCC->isPositionValid())
|
|
return 0;
|
|
|
|
return TPCC->getDepth();
|
|
}
|
|
|
|
unsigned clang_TParamCommandComment_getIndex(CXComment CXC, unsigned Depth) {
|
|
const TParamCommandComment *TPCC = getASTNodeAs<TParamCommandComment>(CXC);
|
|
if (!TPCC || !TPCC->isPositionValid() || Depth >= TPCC->getDepth())
|
|
return 0;
|
|
|
|
return TPCC->getIndex(Depth);
|
|
}
|
|
|
|
CXString clang_VerbatimBlockLineComment_getText(CXComment CXC) {
|
|
const VerbatimBlockLineComment *VBL =
|
|
getASTNodeAs<VerbatimBlockLineComment>(CXC);
|
|
if (!VBL)
|
|
return cxstring::createNull();
|
|
|
|
return cxstring::createRef(VBL->getText());
|
|
}
|
|
|
|
CXString clang_VerbatimLineComment_getText(CXComment CXC) {
|
|
const VerbatimLineComment *VLC = getASTNodeAs<VerbatimLineComment>(CXC);
|
|
if (!VLC)
|
|
return cxstring::createNull();
|
|
|
|
return cxstring::createRef(VLC->getText());
|
|
}
|
|
|
|
} // end extern "C"
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
// Helpers for converting comment AST to HTML.
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
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;
|
|
SmallVector<const BlockContentComment *, 8> MiscBlocks;
|
|
};
|
|
|
|
FullCommentParts::FullCommentParts(const FullComment *C,
|
|
const CommandTraits &Traits) :
|
|
Brief(NULL), Headerfile(NULL), FirstParagraph(NULL) {
|
|
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;
|
|
}
|
|
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>";
|
|
}
|
|
|
|
Result.flush();
|
|
}
|
|
|
|
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;
|
|
}
|
|
}
|
|
}
|
|
|
|
extern "C" {
|
|
|
|
CXString clang_HTMLTagComment_getAsString(CXComment CXC) {
|
|
const HTMLTagComment *HTC = getASTNodeAs<HTMLTagComment>(CXC);
|
|
if (!HTC)
|
|
return cxstring::createNull();
|
|
|
|
SmallString<128> HTML;
|
|
CommentASTToHTMLConverter Converter(0, HTML, getCommandTraits(CXC));
|
|
Converter.visit(HTC);
|
|
return cxstring::createDup(HTML.str());
|
|
}
|
|
|
|
CXString clang_FullComment_getAsHTML(CXComment CXC) {
|
|
const FullComment *FC = getASTNodeAs<FullComment>(CXC);
|
|
if (!FC)
|
|
return cxstring::createNull();
|
|
|
|
SmallString<1024> HTML;
|
|
CommentASTToHTMLConverter Converter(FC, HTML, getCommandTraits(CXC));
|
|
Converter.visit(FC);
|
|
return cxstring::createDup(HTML.str());
|
|
}
|
|
|
|
} // end extern "C"
|
|
|
|
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,
|
|
SimpleFormatContext &SFC,
|
|
unsigned FUID) :
|
|
FC(FC), Result(Str), Traits(Traits), SM(SM),
|
|
FormatRewriterContext(SFC),
|
|
FormatInMemoryUniqueId(FUID) { }
|
|
|
|
// 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 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;
|
|
SimpleFormatContext &FormatRewriterContext;
|
|
unsigned FormatInMemoryUniqueId;
|
|
};
|
|
|
|
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;
|
|
ThisDecl->CurrentDecl->print(OS, PPolicy,
|
|
/*Indentation*/0, /*PrintInstantiation*/false);
|
|
}
|
|
|
|
void CommentASTToXMLConverter::formatTextOfDeclaration(
|
|
const DeclInfo *DI,
|
|
SmallString<128> &Declaration) {
|
|
// FIXME. formatting API expects null terminated input string.
|
|
// There might be more efficient way of doing this.
|
|
std::string StringDecl = Declaration.str();
|
|
|
|
// Formatter specific code.
|
|
// Form a unique in memory buffer name.
|
|
SmallString<128> filename;
|
|
filename += "xmldecl";
|
|
filename += llvm::utostr(FormatInMemoryUniqueId);
|
|
filename += ".xd";
|
|
FileID ID = FormatRewriterContext.createInMemoryFile(filename, StringDecl);
|
|
SourceLocation Start =
|
|
FormatRewriterContext.Sources.getLocForStartOfFile(ID).getLocWithOffset(0);
|
|
unsigned Length = Declaration.size();
|
|
|
|
std::vector<CharSourceRange>
|
|
Ranges(1, CharSourceRange::getCharRange(Start, Start.getLocWithOffset(Length)));
|
|
ASTContext &Context = DI->CurrentDecl->getASTContext();
|
|
const LangOptions &LangOpts = Context.getLangOpts();
|
|
Lexer Lex(ID, FormatRewriterContext.Sources.getBuffer(ID),
|
|
FormatRewriterContext.Sources, LangOpts);
|
|
tooling::Replacements Replace =
|
|
reformat(format::getLLVMStyle(), Lex, FormatRewriterContext.Sources, Ranges);
|
|
applyAllReplacements(Replace, FormatRewriterContext.Rewrite);
|
|
Declaration = FormatRewriterContext.getRewrittenText(ID);
|
|
}
|
|
|
|
} // 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><![CDATA[";
|
|
PrintHTMLStartTagComment(C, Result);
|
|
Result << "]]></rawHTML>";
|
|
}
|
|
|
|
void CommentASTToXMLConverter::visitHTMLEndTagComment(const HTMLEndTagComment *C) {
|
|
Result << "<rawHTML></" << 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.isInvalid()) {
|
|
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;
|
|
cxcursor::getDeclCursorUSR(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.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;
|
|
|
|
Result.flush();
|
|
}
|
|
|
|
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;
|
|
}
|
|
}
|
|
}
|
|
|
|
extern "C" {
|
|
|
|
CXString clang_FullComment_getAsXML(CXComment CXC) {
|
|
const FullComment *FC = getASTNodeAs<FullComment>(CXC);
|
|
if (!FC)
|
|
return cxstring::createNull();
|
|
ASTContext &Context = FC->getDeclInfo()->CurrentDecl->getASTContext();
|
|
CXTranslationUnit TU = CXC.TranslationUnit;
|
|
SourceManager &SM = cxtu::getASTUnit(TU)->getSourceManager();
|
|
|
|
if (!TU->FormatContext) {
|
|
TU->FormatContext = new SimpleFormatContext(Context.getLangOpts());
|
|
} else if ((TU->FormatInMemoryUniqueId % 1000) == 0) {
|
|
// Delete after some number of iterators, so the buffers don't grow
|
|
// too large.
|
|
delete TU->FormatContext;
|
|
TU->FormatContext = new SimpleFormatContext(Context.getLangOpts());
|
|
}
|
|
|
|
SmallString<1024> XML;
|
|
CommentASTToXMLConverter Converter(FC, XML, getCommandTraits(CXC), SM,
|
|
*TU->FormatContext,
|
|
TU->FormatInMemoryUniqueId++);
|
|
Converter.visit(FC);
|
|
return cxstring::createDup(XML.str());
|
|
}
|
|
|
|
} // end extern "C"
|
|
|