86 changes: 86 additions & 0 deletions clang-tools-extra/clang-doc/Mapper.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
//===-- Mapper.cpp - ClangDoc Mapper ----------------------------*- C++ -*-===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//

#include "Mapper.h"
#include "BitcodeWriter.h"
#include "Serialize.h"
#include "clang/AST/Comment.h"
#include "clang/Index/USRGeneration.h"
#include "llvm/ADT/StringExtras.h"

using clang::comments::FullComment;

namespace clang {
namespace doc {

void MapASTVisitor::HandleTranslationUnit(ASTContext &Context) {
TraverseDecl(Context.getTranslationUnitDecl());
}

template <typename T> bool MapASTVisitor::mapDecl(const T *D) {
// If we're looking a decl not in user files, skip this decl.
if (D->getASTContext().getSourceManager().isInSystemHeader(D->getLocation()))
return true;

llvm::SmallString<128> USR;
// If there is an error generating a USR for the decl, skip this decl.
if (index::generateUSRForDecl(D, USR))
return true;

ECtx->reportResult(llvm::toHex(llvm::toStringRef(serialize::hashUSR(USR))),
serialize::emitInfo(D, getComment(D, D->getASTContext()),
getLine(D, D->getASTContext()),
getFile(D, D->getASTContext())));
return true;
}

bool MapASTVisitor::VisitNamespaceDecl(const NamespaceDecl *D) {
return mapDecl(D);
}

bool MapASTVisitor::VisitRecordDecl(const RecordDecl *D) { return mapDecl(D); }

bool MapASTVisitor::VisitEnumDecl(const EnumDecl *D) { return mapDecl(D); }

bool MapASTVisitor::VisitCXXMethodDecl(const CXXMethodDecl *D) {
return mapDecl(D);
}

bool MapASTVisitor::VisitFunctionDecl(const FunctionDecl *D) {
// Don't visit CXXMethodDecls twice
if (dyn_cast<CXXMethodDecl>(D))
return true;
return mapDecl(D);
}

comments::FullComment *
MapASTVisitor::getComment(const NamedDecl *D, const ASTContext &Context) const {
RawComment *Comment = Context.getRawCommentForDeclNoCache(D);
// FIXME: Move setAttached to the initial comment parsing.
if (Comment) {
Comment->setAttached();
return Comment->parse(Context, nullptr, D);
}
return nullptr;
}

int MapASTVisitor::getLine(const NamedDecl *D,
const ASTContext &Context) const {
return Context.getSourceManager().getPresumedLoc(D->getLocStart()).getLine();
}

llvm::StringRef MapASTVisitor::getFile(const NamedDecl *D,
const ASTContext &Context) const {
return Context.getSourceManager()
.getPresumedLoc(D->getLocStart())
.getFilename();
}

} // namespace doc
} // namespace clang
57 changes: 57 additions & 0 deletions clang-tools-extra/clang-doc/Mapper.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
//===-- Mapper.h - ClangDoc Mapper ------------------------------*- C++ -*-===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// This file implements the Mapper piece of the clang-doc tool. It implements
// a RecursiveASTVisitor to look at each declaration and populate the info
// into the internal representation. Each seen declaration is serialized to
// to bitcode and written out to the ExecutionContext as a KV pair where the
// key is the declaration's USR and the value is the serialized bitcode.
//
//===----------------------------------------------------------------------===//

#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_DOC_MAPPER_H
#define LLVM_CLANG_TOOLS_EXTRA_CLANG_DOC_MAPPER_H

#include "clang/AST/RecursiveASTVisitor.h"
#include "clang/Tooling/Execution.h"

using namespace clang::comments;
using namespace clang::tooling;

namespace clang {
namespace doc {

class MapASTVisitor : public clang::RecursiveASTVisitor<MapASTVisitor>,
public ASTConsumer {
public:
explicit MapASTVisitor(ASTContext *Ctx, ExecutionContext *ECtx)
: ECtx(ECtx) {}

void HandleTranslationUnit(ASTContext &Context) override;
bool VisitNamespaceDecl(const NamespaceDecl *D);
bool VisitRecordDecl(const RecordDecl *D);
bool VisitEnumDecl(const EnumDecl *D);
bool VisitCXXMethodDecl(const CXXMethodDecl *D);
bool VisitFunctionDecl(const FunctionDecl *D);

private:
template <typename T> bool mapDecl(const T *D);

int getLine(const NamedDecl *D, const ASTContext &Context) const;
StringRef getFile(const NamedDecl *D, const ASTContext &Context) const;
comments::FullComment *getComment(const NamedDecl *D,
const ASTContext &Context) const;

ExecutionContext *ECtx;
};

} // namespace doc
} // namespace clang

#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_DOC_MAPPER_H
184 changes: 184 additions & 0 deletions clang-tools-extra/clang-doc/Representation.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,184 @@
///===-- Representation.h - ClangDoc Represenation --------------*- C++ -*-===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// This file defines the internal representations of different declaration
// types for the clang-doc tool.
//
//===----------------------------------------------------------------------===//

#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_DOC_REPRESENTATION_H
#define LLVM_CLANG_TOOLS_EXTRA_CLANG_DOC_REPRESENTATION_H

#include "clang/AST/Type.h"
#include "clang/Basic/Specifiers.h"
#include "llvm/ADT/Optional.h"
#include "llvm/ADT/SmallVector.h"
#include "llvm/ADT/StringExtras.h"
#include <array>
#include <string>

namespace clang {
namespace doc {

using SymbolID = std::array<uint8_t, 20>;

struct Info;
enum class InfoType {
IT_namespace,
IT_record,
IT_function,
IT_enum,
IT_default
};

// A representation of a parsed comment.
struct CommentInfo {
CommentInfo() = default;
CommentInfo(CommentInfo &&Other) : Children(std::move(Other.Children)) {}

SmallString<16>
Kind; // Kind of comment (TextComment, InlineCommandComment,
// HTMLStartTagComment, HTMLEndTagComment, BlockCommandComment,
// ParamCommandComment, TParamCommandComment, VerbatimBlockComment,
// VerbatimBlockLineComment, VerbatimLineComment).
SmallString<64> Text; // Text of the comment.
SmallString<16> Name; // Name of the comment (for Verbatim and HTML).
SmallString<8> Direction; // Parameter direction (for (T)ParamCommand).
SmallString<16> ParamName; // Parameter name (for (T)ParamCommand).
SmallString<16> CloseName; // Closing tag name (for VerbatimBlock).
bool SelfClosing = false; // Indicates if tag is self-closing (for HTML).
bool Explicit = false; // Indicates if the direction of a param is explicit
// (for (T)ParamCommand).
llvm::SmallVector<SmallString<16>, 4>
AttrKeys; // List of attribute keys (for HTML).
llvm::SmallVector<SmallString<16>, 4>
AttrValues; // List of attribute values for each key (for HTML).
llvm::SmallVector<SmallString<16>, 4>
Args; // List of arguments to commands (for InlineCommand).
std::vector<std::unique_ptr<CommentInfo>>
Children; // List of child comments for this CommentInfo.
};

struct Reference {
Reference() = default;
Reference(llvm::StringRef Name) : UnresolvedName(Name) {}
Reference(SymbolID USR, InfoType IT) : USR(USR), RefType(IT) {}

SymbolID USR; // Unique identifer for referenced decl
SmallString<16> UnresolvedName; // Name of unresolved type.
InfoType RefType =
InfoType::IT_default; // Indicates the type of this Reference (namespace,
// record, function, enum, default).
};

// A base struct for TypeInfos
struct TypeInfo {
TypeInfo() = default;
TypeInfo(SymbolID &Type, InfoType IT) : Type(Type, IT) {}
TypeInfo(llvm::StringRef RefName) : Type(RefName) {}

Reference Type; // Referenced type in this info.
};

// Info for field types.
struct FieldTypeInfo : public TypeInfo {
FieldTypeInfo() = default;
FieldTypeInfo(SymbolID &Type, InfoType IT, llvm::StringRef Name)
: TypeInfo(Type, IT), Name(Name) {}
FieldTypeInfo(llvm::StringRef RefName, llvm::StringRef Name)
: TypeInfo(RefName), Name(Name) {}

SmallString<16> Name; // Name associated with this info.
};

// Info for member types.
struct MemberTypeInfo : public FieldTypeInfo {
MemberTypeInfo() = default;
MemberTypeInfo(SymbolID &Type, InfoType IT, llvm::StringRef Name)
: FieldTypeInfo(Type, IT, Name) {}
MemberTypeInfo(llvm::StringRef RefName, llvm::StringRef Name)
: FieldTypeInfo(RefName, Name) {}

AccessSpecifier Access =
clang::AccessSpecifier::AS_none; // Access level associated with this
// info (public, protected, private,
// none).
};

struct Location {
Location() = default;
Location(int LineNumber, SmallString<16> Filename)
: LineNumber(LineNumber), Filename(std::move(Filename)) {}

int LineNumber; // Line number of this Location.
SmallString<32> Filename; // File for this Location.
};

/// A base struct for Infos.
struct Info {
Info() = default;
Info(Info &&Other) : Description(std::move(Other.Description)) {}
virtual ~Info() = default;

SymbolID USR; // Unique identifier for the decl described by this Info.
SmallString<16> Name; // Unqualified name of the decl.
llvm::SmallVector<Reference, 4>
Namespace; // List of parent namespaces for this decl.
std::vector<CommentInfo> Description; // Comment description of this decl.
};

// Info for namespaces.
struct NamespaceInfo : public Info {};

// Info for symbols.
struct SymbolInfo : public Info {
llvm::Optional<Location> DefLoc; // Location where this decl is defined.
llvm::SmallVector<Location, 2> Loc; // Locations where this decl is declared.
};

// TODO: Expand to allow for documenting templating and default args.
// Info for functions.
struct FunctionInfo : public SymbolInfo {
bool IsMethod = false; // Indicates whether this function is a class method.
Reference Parent; // Reference to the parent class decl for this method.
TypeInfo ReturnType; // Info about the return type of this function.
llvm::SmallVector<FieldTypeInfo, 4> Params; // List of parameters.
AccessSpecifier Access =
AccessSpecifier::AS_none; // Access level for this method (public,
// private, protected, none).
};

// TODO: Expand to allow for documenting templating, inheritance access,
// friend classes
// Info for types.
struct RecordInfo : public SymbolInfo {
TagTypeKind TagType = TagTypeKind::TTK_Struct; // Type of this record (struct,
// class, union, interface).
llvm::SmallVector<MemberTypeInfo, 4>
Members; // List of info about record members.
llvm::SmallVector<Reference, 4> Parents; // List of base/parent records (does
// not include virtual parents).
llvm::SmallVector<Reference, 4>
VirtualParents; // List of virtual base/parent records.
};

// TODO: Expand to allow for documenting templating.
// Info for types.
struct EnumInfo : public SymbolInfo {
bool Scoped =
false; // Indicates whether this enum is scoped (e.g. enum class).
llvm::SmallVector<SmallString<16>, 4> Members; // List of enum members.
};

// TODO: Add functionality to include separate markdown pages.

} // namespace doc
} // namespace clang

#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_DOC_REPRESENTATION_H
336 changes: 336 additions & 0 deletions clang-tools-extra/clang-doc/Serialize.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,336 @@
//===-- Serializer.cpp - ClangDoc Serializer --------------------*- C++ -*-===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//

#include "Serialize.h"
#include "BitcodeWriter.h"
#include "clang/AST/Comment.h"
#include "clang/Index/USRGeneration.h"
#include "llvm/ADT/Hashing.h"
#include "llvm/ADT/StringExtras.h"
#include "llvm/Support/SHA1.h"

using clang::comments::FullComment;

namespace clang {
namespace doc {
namespace serialize {

SymbolID hashUSR(llvm::StringRef USR) {
return llvm::SHA1::hash(arrayRefFromStringRef(USR));
}

class ClangDocCommentVisitor
: public ConstCommentVisitor<ClangDocCommentVisitor> {
public:
ClangDocCommentVisitor(CommentInfo &CI) : CurrentCI(CI) {}

void parseComment(const comments::Comment *C);

void visitTextComment(const TextComment *C);
void visitInlineCommandComment(const InlineCommandComment *C);
void visitHTMLStartTagComment(const HTMLStartTagComment *C);
void visitHTMLEndTagComment(const HTMLEndTagComment *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);

private:
std::string getCommandName(unsigned CommandID) const;
bool isWhitespaceOnly(StringRef S) const;

CommentInfo &CurrentCI;
};

void ClangDocCommentVisitor::parseComment(const comments::Comment *C) {
CurrentCI.Kind = C->getCommentKindName();
ConstCommentVisitor<ClangDocCommentVisitor>::visit(C);
for (comments::Comment *Child :
llvm::make_range(C->child_begin(), C->child_end())) {
CurrentCI.Children.emplace_back(llvm::make_unique<CommentInfo>());
ClangDocCommentVisitor Visitor(*CurrentCI.Children.back());
Visitor.parseComment(Child);
}
}

void ClangDocCommentVisitor::visitTextComment(const TextComment *C) {
if (!isWhitespaceOnly(C->getText()))
CurrentCI.Text = C->getText();
}

void ClangDocCommentVisitor::visitInlineCommandComment(
const InlineCommandComment *C) {
CurrentCI.Name = getCommandName(C->getCommandID());
for (unsigned I = 0, E = C->getNumArgs(); I != E; ++I)
CurrentCI.Args.push_back(C->getArgText(I));
}

void ClangDocCommentVisitor::visitHTMLStartTagComment(
const HTMLStartTagComment *C) {
CurrentCI.Name = C->getTagName();
CurrentCI.SelfClosing = C->isSelfClosing();
for (unsigned I = 0, E = C->getNumAttrs(); I < E; ++I) {
const HTMLStartTagComment::Attribute &Attr = C->getAttr(I);
CurrentCI.AttrKeys.push_back(Attr.Name);
CurrentCI.AttrValues.push_back(Attr.Value);
}
}

void ClangDocCommentVisitor::visitHTMLEndTagComment(
const HTMLEndTagComment *C) {
CurrentCI.Name = C->getTagName();
CurrentCI.SelfClosing = true;
}

void ClangDocCommentVisitor::visitBlockCommandComment(
const BlockCommandComment *C) {
CurrentCI.Name = getCommandName(C->getCommandID());
for (unsigned I = 0, E = C->getNumArgs(); I < E; ++I)
CurrentCI.Args.push_back(C->getArgText(I));
}

void ClangDocCommentVisitor::visitParamCommandComment(
const ParamCommandComment *C) {
CurrentCI.Direction =
ParamCommandComment::getDirectionAsString(C->getDirection());
CurrentCI.Explicit = C->isDirectionExplicit();
if (C->hasParamName())
CurrentCI.ParamName = C->getParamNameAsWritten();
}

void ClangDocCommentVisitor::visitTParamCommandComment(
const TParamCommandComment *C) {
if (C->hasParamName())
CurrentCI.ParamName = C->getParamNameAsWritten();
}

void ClangDocCommentVisitor::visitVerbatimBlockComment(
const VerbatimBlockComment *C) {
CurrentCI.Name = getCommandName(C->getCommandID());
CurrentCI.CloseName = C->getCloseName();
}

void ClangDocCommentVisitor::visitVerbatimBlockLineComment(
const VerbatimBlockLineComment *C) {
if (!isWhitespaceOnly(C->getText()))
CurrentCI.Text = C->getText();
}

void ClangDocCommentVisitor::visitVerbatimLineComment(
const VerbatimLineComment *C) {
if (!isWhitespaceOnly(C->getText()))
CurrentCI.Text = C->getText();
}

bool ClangDocCommentVisitor::isWhitespaceOnly(llvm::StringRef S) const {
return std::all_of(S.begin(), S.end(), isspace);
}

std::string ClangDocCommentVisitor::getCommandName(unsigned CommandID) const {
const CommandInfo *Info = CommandTraits::getBuiltinCommandInfo(CommandID);
if (Info)
return Info->Name;
// TODO: Add parsing for \file command.
return "<not a builtin command>";
}

// Serializing functions.

template <typename T> static std::string serialize(T &I) {
SmallString<2048> Buffer;
llvm::BitstreamWriter Stream(Buffer);
ClangDocBitcodeWriter Writer(Stream);
Writer.emitBlock(I);
return Buffer.str().str();
}

static void parseFullComment(const FullComment *C, CommentInfo &CI) {
ClangDocCommentVisitor Visitor(CI);
Visitor.parseComment(C);
}

static SymbolID getUSRForDecl(const Decl *D) {
llvm::SmallString<128> USR;
if (index::generateUSRForDecl(D, USR))
return SymbolID();
return hashUSR(USR);
}

static RecordDecl *getDeclForType(const QualType &T) {
auto *Ty = T->getAs<RecordType>();
if (!Ty)
return nullptr;
return Ty->getDecl()->getDefinition();
}

static void parseFields(RecordInfo &I, const RecordDecl *D) {
for (const FieldDecl *F : D->fields()) {
// FIXME: Set Access to the appropriate value.
SymbolID Type;
std::string Name;
InfoType RefType;
if (const auto *T = getDeclForType(F->getTypeSourceInfo()->getType())) {
Type = getUSRForDecl(T);
if (dyn_cast<EnumDecl>(T))
RefType = InfoType::IT_enum;
else if (dyn_cast<RecordDecl>(T))
RefType = InfoType::IT_record;
I.Members.emplace_back(Type, RefType, F->getQualifiedNameAsString());
} else {
Name = F->getTypeSourceInfo()->getType().getAsString();
I.Members.emplace_back(Name, F->getQualifiedNameAsString());
}
}
}

static void parseEnumerators(EnumInfo &I, const EnumDecl *D) {
for (const EnumConstantDecl *E : D->enumerators())
I.Members.emplace_back(E->getNameAsString());
}

static void parseParameters(FunctionInfo &I, const FunctionDecl *D) {
for (const ParmVarDecl *P : D->parameters()) {
SymbolID Type;
std::string Name;
InfoType RefType;
if (const auto *T = getDeclForType(P->getOriginalType())) {
Type = getUSRForDecl(T);
if (dyn_cast<EnumDecl>(T))
RefType = InfoType::IT_enum;
else if (dyn_cast<RecordDecl>(T))
RefType = InfoType::IT_record;
I.Params.emplace_back(Type, RefType, P->getQualifiedNameAsString());
} else {
Name = P->getOriginalType().getAsString();
I.Params.emplace_back(Name, P->getQualifiedNameAsString());
}
}
}

static void parseBases(RecordInfo &I, const CXXRecordDecl *D) {
for (const CXXBaseSpecifier &B : D->bases()) {
if (B.isVirtual())
continue;
if (const auto *P = getDeclForType(B.getType()))
I.Parents.emplace_back(getUSRForDecl(P), InfoType::IT_record);
else
I.Parents.emplace_back(B.getType().getAsString());
}
for (const CXXBaseSpecifier &B : D->vbases()) {
if (const auto *P = getDeclForType(B.getType()))
I.VirtualParents.emplace_back(getUSRForDecl(P), InfoType::IT_record);
else
I.VirtualParents.emplace_back(B.getType().getAsString());
}
}

template <typename T>
static void
populateParentNamespaces(llvm::SmallVector<Reference, 4> &Namespaces,
const T *D) {
const auto *DC = dyn_cast<DeclContext>(D);
while ((DC = DC->getParent())) {
if (const auto *N = dyn_cast<NamespaceDecl>(DC))
Namespaces.emplace_back(getUSRForDecl(N), InfoType::IT_namespace);
else if (const auto *N = dyn_cast<RecordDecl>(DC))
Namespaces.emplace_back(getUSRForDecl(N), InfoType::IT_record);
else if (const auto *N = dyn_cast<FunctionDecl>(DC))
Namespaces.emplace_back(getUSRForDecl(N), InfoType::IT_function);
else if (const auto *N = dyn_cast<EnumDecl>(DC))
Namespaces.emplace_back(getUSRForDecl(N), InfoType::IT_enum);
}
}

template <typename T>
static void populateInfo(Info &I, const T *D, const FullComment *C) {
I.USR = getUSRForDecl(D);
I.Name = D->getNameAsString();
populateParentNamespaces(I.Namespace, D);
if (C) {
I.Description.emplace_back();
parseFullComment(C, I.Description.back());
}
}

template <typename T>
static void populateSymbolInfo(SymbolInfo &I, const T *D, const FullComment *C,
int LineNumber, StringRef Filename) {
populateInfo(I, D, C);
if (D->isThisDeclarationADefinition())
I.DefLoc.emplace(LineNumber, Filename);
else
I.Loc.emplace_back(LineNumber, Filename);
}

static void populateFunctionInfo(FunctionInfo &I, const FunctionDecl *D,
const FullComment *FC, int LineNumber,
StringRef Filename) {
populateSymbolInfo(I, D, FC, LineNumber, Filename);
if (const auto *T = getDeclForType(D->getReturnType())) {
I.ReturnType.Type.USR = getUSRForDecl(T);
if (dyn_cast<EnumDecl>(T))
I.ReturnType.Type.RefType = InfoType::IT_enum;
else if (dyn_cast<RecordDecl>(T))
I.ReturnType.Type.RefType = InfoType::IT_record;
} else {
I.ReturnType.Type.UnresolvedName = D->getReturnType().getAsString();
}
parseParameters(I, D);
}

std::string emitInfo(const NamespaceDecl *D, const FullComment *FC,
int LineNumber, llvm::StringRef File) {
NamespaceInfo I;
populateInfo(I, D, FC);
return serialize(I);
}

std::string emitInfo(const RecordDecl *D, const FullComment *FC, int LineNumber,
llvm::StringRef File) {
RecordInfo I;
populateSymbolInfo(I, D, FC, LineNumber, File);
I.TagType = D->getTagKind();
parseFields(I, D);
if (const auto *C = dyn_cast<CXXRecordDecl>(D))
parseBases(I, C);
return serialize(I);
}

std::string emitInfo(const FunctionDecl *D, const FullComment *FC,
int LineNumber, llvm::StringRef File) {
FunctionInfo I;
populateFunctionInfo(I, D, FC, LineNumber, File);
I.Access = clang::AccessSpecifier::AS_none;
return serialize(I);
}

std::string emitInfo(const CXXMethodDecl *D, const FullComment *FC,
int LineNumber, llvm::StringRef File) {
FunctionInfo I;
populateFunctionInfo(I, D, FC, LineNumber, File);
I.IsMethod = true;
I.Parent = Reference(getUSRForDecl(D->getParent()), InfoType::IT_record);
I.Access = D->getAccess();
return serialize(I);
}

std::string emitInfo(const EnumDecl *D, const FullComment *FC, int LineNumber,
llvm::StringRef File) {
EnumInfo I;
populateSymbolInfo(I, D, FC, LineNumber, File);
I.Scoped = D->isScoped();
parseEnumerators(I, D);
return serialize(I);
}

} // namespace serialize
} // namespace doc
} // namespace clang
53 changes: 53 additions & 0 deletions clang-tools-extra/clang-doc/Serialize.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
//===-- Serializer.h - ClangDoc Serializer ----------------------*- C++ -*-===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// This file implements the serializing functions fro the clang-doc tool. Given
// a particular declaration, it collects the appropriate information and returns
// a serialized bitcode string for the declaration.
//
//===----------------------------------------------------------------------===//

#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_DOC_SERIALIZE_H
#define LLVM_CLANG_TOOLS_EXTRA_CLANG_DOC_SERIALIZE_H

#include "Representation.h"
#include "clang/AST/AST.h"
#include "clang/AST/CommentVisitor.h"
#include <string>
#include <vector>

using namespace clang::comments;

namespace clang {
namespace doc {
namespace serialize {

std::string emitInfo(const NamespaceDecl *D, const FullComment *FC,
int LineNumber, StringRef File);
std::string emitInfo(const RecordDecl *D, const FullComment *FC, int LineNumber,
StringRef File);
std::string emitInfo(const EnumDecl *D, const FullComment *FC, int LineNumber,
StringRef File);
std::string emitInfo(const FunctionDecl *D, const FullComment *FC,
int LineNumber, StringRef File);
std::string emitInfo(const CXXMethodDecl *D, const FullComment *FC,
int LineNumber, StringRef File);

// Function to hash a given USR value for storage.
// As USRs (Unified Symbol Resolution) could be large, especially for functions
// with long type arguments, we use 160-bits SHA1(USR) values to
// guarantee the uniqueness of symbols while using a relatively small amount of
// memory (vs storing USRs directly).
SymbolID hashUSR(llvm::StringRef USR);

} // namespace serialize
} // namespace doc
} // namespace clang

#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_DOC_SERIALIZE_H
17 changes: 17 additions & 0 deletions clang-tools-extra/clang-doc/tool/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
include_directories(${CMAKE_CURRENT_SOURCE_DIR}/..)

add_clang_executable(clang-doc
ClangDocMain.cpp
)

target_link_libraries(clang-doc
PRIVATE
clangAST
clangASTMatchers
clangBasic
clangFrontend
clangDoc
clangTooling
clangToolingCore
)

114 changes: 114 additions & 0 deletions clang-tools-extra/clang-doc/tool/ClangDocMain.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
//===-- ClangDocMain.cpp - ClangDoc -----------------------------*- C++ -*-===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// This tool for generating C and C++ documenation from source code
// and comments. Generally, it runs a LibTooling FrontendAction on source files,
// mapping each declaration in those files to its USR and serializing relevant
// information into LLVM bitcode. It then runs a pass over the collected
// declaration information, reducing by USR. There is an option to dump this
// intermediate result to bitcode. Finally, it hands the reduced information
// off to a generator, which does the final parsing from the intermediate
// representation to the desired output format.
//
//===----------------------------------------------------------------------===//

#include "ClangDoc.h"
#include "clang/AST/AST.h"
#include "clang/AST/Decl.h"
#include "clang/ASTMatchers/ASTMatchFinder.h"
#include "clang/ASTMatchers/ASTMatchersInternal.h"
#include "clang/Driver/Options.h"
#include "clang/Frontend/FrontendActions.h"
#include "clang/Tooling/CommonOptionsParser.h"
#include "clang/Tooling/Execution.h"
#include "clang/Tooling/StandaloneExecution.h"
#include "clang/Tooling/Tooling.h"
#include "llvm/ADT/APFloat.h"
#include "llvm/Support/FileSystem.h"
#include "llvm/Support/Path.h"
#include "llvm/Support/Process.h"
#include "llvm/Support/Signals.h"
#include "llvm/Support/raw_ostream.h"
#include <string>

using namespace clang::ast_matchers;
using namespace clang::tooling;
using namespace clang;

static llvm::cl::extrahelp CommonHelp(CommonOptionsParser::HelpMessage);
static llvm::cl::OptionCategory ClangDocCategory("clang-doc options");

static llvm::cl::opt<std::string>
OutDirectory("output",
llvm::cl::desc("Directory for outputting generated files."),
llvm::cl::init("docs"), llvm::cl::cat(ClangDocCategory));

static llvm::cl::opt<bool>
DumpMapperResult("dump-mapper",
llvm::cl::desc("Dump mapper results to bitcode file."),
llvm::cl::init(false), llvm::cl::cat(ClangDocCategory));

static llvm::cl::opt<bool> DoxygenOnly(
"doxygen",
llvm::cl::desc("Use only doxygen-style comments to generate docs."),
llvm::cl::init(false), llvm::cl::cat(ClangDocCategory));

int main(int argc, const char **argv) {
llvm::sys::PrintStackTraceOnErrorSignal(argv[0]);
std::error_code OK;

auto Exec = clang::tooling::createExecutorFromCommandLineArgs(
argc, argv, ClangDocCategory);

if (!Exec) {
llvm::errs() << toString(Exec.takeError()) << "\n";
return 1;
}

ArgumentsAdjuster ArgAdjuster;
if (!DoxygenOnly)
ArgAdjuster = combineAdjusters(
getInsertArgumentAdjuster("-fparse-all-comments",
tooling::ArgumentInsertPosition::END),
ArgAdjuster);

// Mapping phase
llvm::outs() << "Mapping decls...\n";
auto Err = Exec->get()->execute(doc::newMapperActionFactory(
Exec->get()->getExecutionContext()),
ArgAdjuster);
if (Err)
llvm::errs() << toString(std::move(Err)) << "\n";

if (DumpMapperResult) {
Exec->get()->getToolResults()->forEachResult([&](StringRef Key,
StringRef Value) {
SmallString<128> IRRootPath;
llvm::sys::path::native(OutDirectory, IRRootPath);
llvm::sys::path::append(IRRootPath, "bc");
std::error_code DirectoryStatus =
llvm::sys::fs::create_directories(IRRootPath);
if (DirectoryStatus != OK) {
llvm::errs() << "Unable to create documentation directories.\n";
return;
}
llvm::sys::path::append(IRRootPath, Key + ".bc");
std::error_code OutErrorInfo;
llvm::raw_fd_ostream OS(IRRootPath, OutErrorInfo, llvm::sys::fs::F_None);
if (OutErrorInfo != OK) {
llvm::errs() << "Error opening documentation file.\n";
return;
}
OS << Value;
OS.close();
});
}

return 0;
}
4 changes: 4 additions & 0 deletions clang-tools-extra/test/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -37,10 +37,14 @@ set(CLANG_TOOLS_TEST_DEPS
# For the clang-apply-replacements test that uses clang-rename.
clang-rename

# For the clang-doc tests that emit bitcode files.
llvm-bcanalyzer

# Individual tools we test.
clang-apply-replacements
clang-change-namespace
clangd
clang-doc
clang-include-fixer
clang-move
clang-query
Expand Down
35 changes: 35 additions & 0 deletions clang-tools-extra/test/clang-doc/mapper-class-in-class.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
// RUN: rm -rf %t
// RUN: mkdir %t
// RUN: echo "" > %t/compile_flags.txt
// RUN: cp "%s" "%t/test.cpp"
// RUN: clang-doc --dump-mapper -doxygen -p %t %t/test.cpp -output=%t/docs
// RUN: llvm-bcanalyzer %t/docs/bc/641AB4A3D36399954ACDE29C7A8833032BF40472.bc --dump | FileCheck %s --check-prefix CHECK-X-Y
// RUN: llvm-bcanalyzer %t/docs/bc/CA7C7935730B5EACD25F080E9C83FA087CCDC75E.bc --dump | FileCheck %s --check-prefix CHECK-X

class X {
class Y {};
};

// CHECK-X: <BLOCKINFO_BLOCK/>
// CHECK-X-NEXT: <VersionBlock NumWords=1 BlockCodeSize=4>
// CHECK-X-NEXT: <Version abbrevid=4 op0=1/>
// CHECK-X-NEXT: </VersionBlock>
// CHECK-X-NEXT: <RecordBlock NumWords={{[0-9]*}} BlockCodeSize=4>
// CHECK-X-NEXT: <USR abbrevid=4 op0=20 op1=202 op2=124 op3=121 op4=53 op5=115 op6=11 op7=94 op8=172 op9=210 op10=95 op11=8 op12=14 op13=156 op14=131 op15=250 op16=8 op17=124 op18=205 op19=199 op20=94/>
// CHECK-X-NEXT: <Name abbrevid=5 op0=1/> blob data = 'X'
// CHECK-X-NEXT: <DefLocation abbrevid=7 op0=9 op1={{[0-9]*}}/> blob data = '{{.*}}'
// CHECK-X-NEXT: <TagType abbrevid=9 op0=3/>
// CHECK-X-NEXT: </RecordBlock>


// CHECK-X-Y: <BLOCKINFO_BLOCK/>
// CHECK-X-Y-NEXT: <VersionBlock NumWords=1 BlockCodeSize=4>
// CHECK-X-Y-NEXT: <Version abbrevid=4 op0=1/>
// CHECK-X-Y-NEXT: </VersionBlock>
// CHECK-X-Y-NEXT: <RecordBlock NumWords={{[0-9]*}} BlockCodeSize=4>
// CHECK-X-Y-NEXT: <USR abbrevid=4 op0=20 op1=100 op2=26 op3=180 op4=163 op5=211 op6=99 op7=153 op8=149 op9=74 op10=205 op11=226 op12=156 op13=122 op14=136 op15=51 op16=3 op17=43 op18=244 op19=4 op20=114/>
// CHECK-X-Y-NEXT: <Name abbrevid=5 op0=1/> blob data = 'Y'
// CHECK-X-Y-NEXT: <Namespace abbrevid=6 op0=1 op1=40/> blob data = 'CA7C7935730B5EACD25F080E9C83FA087CCDC75E'
// CHECK-X-Y-NEXT: <DefLocation abbrevid=7 op0=10 op1={{[0-9]*}}/> blob data = '{{.*}}'
// CHECK-X-Y-NEXT: <TagType abbrevid=9 op0=3/>
// CHECK-X-Y-NEXT: </RecordBlock>
41 changes: 41 additions & 0 deletions clang-tools-extra/test/clang-doc/mapper-class-in-function.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
// This test requires Linux due to the system-dependent USR for the
// inner class.
// REQUIRES: system-linux
// RUN: rm -rf %t
// RUN: mkdir %t
// RUN: echo "" > %t/compile_flags.txt
// RUN: cp "%s" "%t/test.cpp"
// RUN: clang-doc --dump-mapper -doxygen -p %t %t/test.cpp -output=%t/docs
// RUN: llvm-bcanalyzer %t/docs/bc/B6AC4C5C9F2EA3F2B3ECE1A33D349F4EE502B24E.bc --dump | FileCheck %s --check-prefix CHECK-H
// RUN: llvm-bcanalyzer %t/docs/bc/01A95F3F73F53281B3E50109A577FD2493159365.bc --dump | FileCheck %s --check-prefix CHECK-H-I

void H() {
class I {};
}

// CHECK-H: <BLOCKINFO_BLOCK/>
// CHECK-H-NEXT: <VersionBlock NumWords=1 BlockCodeSize=4>
// CHECK-H-NEXT: <Version abbrevid=4 op0=1/>
// CHECK-H-NEXT: </VersionBlock>
// CHECK-H-NEXT: <FunctionBlock NumWords={{[0-9]*}} BlockCodeSize=4>
// CHECK-H-NEXT: <USR abbrevid=4 op0=20 op1=182 op2=172 op3=76 op4=92 op5=159 op6=46 op7=163 op8=242 op9=179 op10=236 op11=225 op12=163 op13=61 op14=52 op15=159 op16=78 op17=229 op18=2 op19=178 op20=78/>
// CHECK-H-NEXT: <Name abbrevid=5 op0=1/> blob data = 'H'
// CHECK-H-NEXT: <DefLocation abbrevid=7 op0=12 op1={{[0-9]*}}/> blob data = '{{.*}}'
// CHECK-H-NEXT: <TypeBlock NumWords=4 BlockCodeSize=4>
// CHECK-H-NEXT: <Type abbrevid=4 op0=4 op1=4/> blob data = 'void'
// CHECK-H-NEXT: </TypeBlock>
// CHECK-H-NEXT: </FunctionBlock>

// CHECK-H-I: <BLOCKINFO_BLOCK/>
// CHECK-H-I-NEXT: <VersionBlock NumWords=1 BlockCodeSize=4>
// CHECK-H-I-NEXT: <Version abbrevid=4 op0=1/>
// CHECK-H-I-NEXT: </VersionBlock>
// CHECK-H-I-NEXT: <RecordBlock NumWords={{[0-9]*}} BlockCodeSize=4>
// CHECK-H-I-NEXT: <USR abbrevid=4 op0=20 op1=1 op2=169 op3=95 op4=63 op5=115 op6=245 op7=50 op8=129 op9=179 op10=229 op11=1 op12=9 op13=165 op14=119 op15=253 op16=36 op17=147 op18=21 op19=147 op20=101/>
// CHECK-H-I-NEXT: <Name abbrevid=5 op0=1/> blob data = 'I'
// CHECK-H-I-NEXT: <Namespace abbrevid=6 op0=2 op1=40/> blob data = 'B6AC4C5C9F2EA3F2B3ECE1A33D349F4EE502B24E'
// CHECK-H-I-NEXT: <DefLocation abbrevid=7 op0=13 op1={{[0-9]*}}/> blob data = '{{.*}}'
// CHECK-H-I-NEXT: <TagType abbrevid=9 op0=3/>
// CHECK-H-I-NEXT: </RecordBlock>


19 changes: 19 additions & 0 deletions clang-tools-extra/test/clang-doc/mapper-class.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
// RUN: rm -rf %t
// RUN: mkdir %t
// RUN: echo "" > %t/compile_flags.txt
// RUN: cp "%s" "%t/test.cpp"
// RUN: clang-doc --dump-mapper -doxygen -p %t %t/test.cpp -output=%t/docs
// RUN: llvm-bcanalyzer %t/docs/bc/289584A8E0FF4178A794622A547AA622503967A1.bc --dump | FileCheck %s

class E {};

// CHECK: <BLOCKINFO_BLOCK/>
// CHECK-NEXT: <VersionBlock NumWords=1 BlockCodeSize=4>
// CHECK-NEXT: <Version abbrevid=4 op0=1/>
// CHECK-NEXT: </VersionBlock>
// CHECK-NEXT: <RecordBlock NumWords={{[0-9]*}} BlockCodeSize=4>
// CHECK-NEXT: <USR abbrevid=4 op0=20 op1=40 op2=149 op3=132 op4=168 op5=224 op6=255 op7=65 op8=120 op9=167 op10=148 op11=98 op12=42 op13=84 op14=122 op15=166 op16=34 op17=80 op18=57 op19=103 op20=161/>
// CHECK-NEXT: <Name abbrevid=5 op0=1/> blob data = 'E'
// CHECK-NEXT: <DefLocation abbrevid=7 op0=8 op1={{[0-9]*}}/> blob data = '{{.*}}'
// CHECK-NEXT: <TagType abbrevid=9 op0=3/>
// CHECK-NEXT: </RecordBlock>
172 changes: 172 additions & 0 deletions clang-tools-extra/test/clang-doc/mapper-comments.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,172 @@
// RUN: rm -rf %t
// RUN: mkdir %t
// RUN: echo "" > %t/compile_flags.txt
// RUN: cp "%s" "%t/test.cpp"
// RUN: clang-doc --dump-mapper -doxygen -p %t %t/test.cpp -output=%t/docs
// RUN: llvm-bcanalyzer %t/docs/bc/7574630614A535710E5A6ABCFFF98BCA2D06A4CA.bc --dump | FileCheck %s

/// \brief Brief description.
///
/// Extended description that
/// continues onto the next line.
///
/// <ul> class="test">
/// <li> Testing.
/// </ul>
///
/// \verbatim
/// The description continues.
/// \endverbatim
///
/// \param [out] I is a parameter.
/// \param J is a parameter.
/// \return int
int F(int I, int J);

// CHECK: <BLOCKINFO_BLOCK/>
// CHECK-NEXT: <VersionBlock NumWords=1 BlockCodeSize=4>
// CHECK-NEXT: <Version abbrevid=4 op0=1/>
// CHECK-NEXT: </VersionBlock>
// CHECK-NEXT: <FunctionBlock NumWords={{[0-9]*}} BlockCodeSize=4>
// CHECK-NEXT: <USR abbrevid=4 op0=20 op1=117 op2=116 op3=99 op4=6 op5=20 op6=165 op7=53 op8=113 op9=14 op10=90 op11=106 op12=188 op13=255 op14=249 op15=139 op16=202 op17=45 op18=6 op19=164 op20=202/>
// CHECK-NEXT: <Name abbrevid=5 op0=1/> blob data = 'F'
// CHECK-NEXT: <CommentBlock NumWords=351 BlockCodeSize=4>
// CHECK-NEXT: <Kind abbrevid=4 op0=11/> blob data = 'FullComment'
// CHECK-NEXT: <CommentBlock NumWords=13 BlockCodeSize=4>
// CHECK-NEXT: <Kind abbrevid=4 op0=16/> blob data = 'ParagraphComment'
// CHECK-NEXT: <CommentBlock NumWords=5 BlockCodeSize=4>
// CHECK-NEXT: <Kind abbrevid=4 op0=11/> blob data = 'TextComment'
// CHECK-NEXT: </CommentBlock>
// CHECK-NEXT: </CommentBlock>
// CHECK-NEXT: <CommentBlock NumWords=31 BlockCodeSize=4>
// CHECK-NEXT: <Kind abbrevid=4 op0=19/> blob data = 'BlockCommandComment'
// CHECK-NEXT: <Name abbrevid=6 op0=5/> blob data = 'brief'
// CHECK-NEXT: <CommentBlock NumWords=19 BlockCodeSize=4>
// CHECK-NEXT: <Kind abbrevid=4 op0=16/> blob data = 'ParagraphComment'
// CHECK-NEXT: <CommentBlock NumWords=11 BlockCodeSize=4>
// CHECK-NEXT: <Kind abbrevid=4 op0=11/> blob data = 'TextComment'
// CHECK-NEXT: <Text abbrevid=5 op0=19/> blob data = ' Brief description.'
// CHECK-NEXT: </CommentBlock>
// CHECK-NEXT: </CommentBlock>
// CHECK-NEXT: </CommentBlock>
// CHECK-NEXT: <CommentBlock NumWords=37 BlockCodeSize=4>
// CHECK-NEXT: <Kind abbrevid=4 op0=16/> blob data = 'ParagraphComment'
// CHECK-NEXT: <CommentBlock NumWords=13 BlockCodeSize=4>
// CHECK-NEXT: <Kind abbrevid=4 op0=11/> blob data = 'TextComment'
// CHECK-NEXT: <Text abbrevid=5 op0=26/> blob data = ' Extended description that'
// CHECK-NEXT: </CommentBlock>
// CHECK-NEXT: <CommentBlock NumWords=14 BlockCodeSize=4>
// CHECK-NEXT: <Kind abbrevid=4 op0=11/> blob data = 'TextComment'
// CHECK-NEXT: <Text abbrevid=5 op0=30/> blob data = ' continues onto the next line.'
// CHECK-NEXT: </CommentBlock>
// CHECK-NEXT: </CommentBlock>
// CHECK-NEXT: <CommentBlock NumWords=83 BlockCodeSize=4>
// CHECK-NEXT: <Kind abbrevid=4 op0=16/> blob data = 'ParagraphComment'
// CHECK-NEXT: <CommentBlock NumWords=5 BlockCodeSize=4>
// CHECK-NEXT: <Kind abbrevid=4 op0=11/> blob data = 'TextComment'
// CHECK-NEXT: </CommentBlock>
// CHECK-NEXT: <CommentBlock NumWords=9 BlockCodeSize=4>
// CHECK-NEXT: <Kind abbrevid=4 op0=19/> blob data = 'HTMLStartTagComment'
// CHECK-NEXT: <Name abbrevid=6 op0=2/> blob data = 'ul'
// CHECK-NEXT: </CommentBlock>
// CHECK-NEXT: <CommentBlock NumWords=10 BlockCodeSize=4>
// CHECK-NEXT: <Kind abbrevid=4 op0=11/> blob data = 'TextComment'
// CHECK-NEXT: <Text abbrevid=5 op0=14/> blob data = ' class="test">'
// CHECK-NEXT: </CommentBlock>
// CHECK-NEXT: <CommentBlock NumWords=5 BlockCodeSize=4>
// CHECK-NEXT: <Kind abbrevid=4 op0=11/> blob data = 'TextComment'
// CHECK-NEXT: </CommentBlock>
// CHECK-NEXT: <CommentBlock NumWords=9 BlockCodeSize=4>
// CHECK-NEXT: <Kind abbrevid=4 op0=19/> blob data = 'HTMLStartTagComment'
// CHECK-NEXT: <Name abbrevid=6 op0=2/> blob data = 'li'
// CHECK-NEXT: </CommentBlock>
// CHECK-NEXT: <CommentBlock NumWords=9 BlockCodeSize=4>
// CHECK-NEXT: <Kind abbrevid=4 op0=11/> blob data = 'TextComment'
// CHECK-NEXT: <Text abbrevid=5 op0=9/> blob data = ' Testing.'
// CHECK-NEXT: </CommentBlock>
// CHECK-NEXT: <CommentBlock NumWords=5 BlockCodeSize=4>
// CHECK-NEXT: <Kind abbrevid=4 op0=11/> blob data = 'TextComment'
// CHECK-NEXT: </CommentBlock>
// CHECK-NEXT: <CommentBlock NumWords=9 BlockCodeSize=4>
// CHECK-NEXT: <Kind abbrevid=4 op0=17/> blob data = 'HTMLEndTagComment'
// CHECK-NEXT: <Name abbrevid=6 op0=2/> blob data = 'ul'
// CHECK-NEXT: <SelfClosing abbrevid=10 op0=1/>
// CHECK-NEXT: </CommentBlock>
// CHECK-NEXT: </CommentBlock>
// CHECK-NEXT: <CommentBlock NumWords=13 BlockCodeSize=4>
// CHECK-NEXT: <Kind abbrevid=4 op0=16/> blob data = 'ParagraphComment'
// CHECK-NEXT: <CommentBlock NumWords=5 BlockCodeSize=4>
// CHECK-NEXT: <Kind abbrevid=4 op0=11/> blob data = 'TextComment'
// CHECK-NEXT: </CommentBlock>
// CHECK-NEXT: </CommentBlock>
// CHECK-NEXT: <CommentBlock NumWords=32 BlockCodeSize=4>
// CHECK-NEXT: <Kind abbrevid=4 op0=20/> blob data = 'VerbatimBlockComment'
// CHECK-NEXT: <Name abbrevid=6 op0=8/> blob data = 'verbatim'
// CHECK-NEXT: <CloseName abbrevid=9 op0=11/> blob data = 'endverbatim'
// CHECK-NEXT: <CommentBlock NumWords=16 BlockCodeSize=4>
// CHECK-NEXT: <Kind abbrevid=4 op0=24/> blob data = 'VerbatimBlockLineComment'
// CHECK-NEXT: <Text abbrevid=5 op0=27/> blob data = ' The description continues.'
// CHECK-NEXT: </CommentBlock>
// CHECK-NEXT: </CommentBlock>
// CHECK-NEXT: <CommentBlock NumWords=13 BlockCodeSize=4>
// CHECK-NEXT: <Kind abbrevid=4 op0=16/> blob data = 'ParagraphComment'
// CHECK-NEXT: <CommentBlock NumWords=5 BlockCodeSize=4>
// CHECK-NEXT: <Kind abbrevid=4 op0=11/> blob data = 'TextComment'
// CHECK-NEXT: </CommentBlock>
// CHECK-NEXT: </CommentBlock>
// CHECK-NEXT: <CommentBlock NumWords=39 BlockCodeSize=4>
// CHECK-NEXT: <Kind abbrevid=4 op0=19/> blob data = 'ParamCommandComment'
// CHECK-NEXT: <Direction abbrevid=7 op0=5/> blob data = '[out]'
// CHECK-NEXT: <ParamName abbrevid=8 op0=1/> blob data = 'I'
// CHECK-NEXT: <Explicit abbrevid=11 op0=1/>
// CHECK-NEXT: <CommentBlock NumWords=25 BlockCodeSize=4>
// CHECK-NEXT: <Kind abbrevid=4 op0=16/> blob data = 'ParagraphComment'
// CHECK-NEXT: <CommentBlock NumWords=10 BlockCodeSize=4>
// CHECK-NEXT: <Kind abbrevid=4 op0=11/> blob data = 'TextComment'
// CHECK-NEXT: <Text abbrevid=5 op0=16/> blob data = ' is a parameter.'
// CHECK-NEXT: </CommentBlock>
// CHECK-NEXT: <CommentBlock NumWords=5 BlockCodeSize=4>
// CHECK-NEXT: <Kind abbrevid=4 op0=11/> blob data = 'TextComment'
// CHECK-NEXT: </CommentBlock>
// CHECK-NEXT: </CommentBlock>
// CHECK-NEXT: </CommentBlock>
// CHECK-NEXT: <CommentBlock NumWords=38 BlockCodeSize=4>
// CHECK-NEXT: <Kind abbrevid=4 op0=19/> blob data = 'ParamCommandComment'
// CHECK-NEXT: <Direction abbrevid=7 op0=4/> blob data = '[in]'
// CHECK-NEXT: <ParamName abbrevid=8 op0=1/> blob data = 'J'
// CHECK-NEXT: <CommentBlock NumWords=25 BlockCodeSize=4>
// CHECK-NEXT: <Kind abbrevid=4 op0=16/> blob data = 'ParagraphComment'
// CHECK-NEXT: <CommentBlock NumWords=10 BlockCodeSize=4>
// CHECK-NEXT: <Kind abbrevid=4 op0=11/> blob data = 'TextComment'
// CHECK-NEXT: <Text abbrevid=5 op0=16/> blob data = ' is a parameter.'
// CHECK-NEXT: </CommentBlock>
// CHECK-NEXT: <CommentBlock NumWords=5 BlockCodeSize=4>
// CHECK-NEXT: <Kind abbrevid=4 op0=11/> blob data = 'TextComment'
// CHECK-NEXT: </CommentBlock>
// CHECK-NEXT: </CommentBlock>
// CHECK-NEXT: </CommentBlock>
// CHECK-NEXT: <CommentBlock NumWords=27 BlockCodeSize=4>
// CHECK-NEXT: <Kind abbrevid=4 op0=19/> blob data = 'BlockCommandComment'
// CHECK-NEXT: <Name abbrevid=6 op0=6/> blob data = 'return'
// CHECK-NEXT: <CommentBlock NumWords=15 BlockCodeSize=4>
// CHECK-NEXT: <Kind abbrevid=4 op0=16/> blob data = 'ParagraphComment'
// CHECK-NEXT: <CommentBlock NumWords=7 BlockCodeSize=4>
// CHECK-NEXT: <Kind abbrevid=4 op0=11/> blob data = 'TextComment'
// CHECK-NEXT: <Text abbrevid=5 op0=4/> blob data = ' int'
// CHECK-NEXT: </CommentBlock>
// CHECK-NEXT: </CommentBlock>
// CHECK-NEXT: </CommentBlock>
// CHECK-NEXT: </CommentBlock>
// CHECK-NEXT: <Location abbrevid=8 op0=24 op1={{[0-9]*}}/> blob data = '{{.*}}'
// CHECK-NEXT: <TypeBlock NumWords=4 BlockCodeSize=4>
// CHECK-NEXT: <Type abbrevid=4 op0=4 op1=3/> blob data = 'int'
// CHECK-NEXT: </TypeBlock>
// CHECK-NEXT: <FieldTypeBlock NumWords=6 BlockCodeSize=4>
// CHECK-NEXT: <Type abbrevid=4 op0=4 op1=3/> blob data = 'int'
// CHECK-NEXT: <Name abbrevid=5 op0=1/> blob data = 'I'
// CHECK-NEXT: </FieldTypeBlock>
// CHECK-NEXT: <FieldTypeBlock NumWords=6 BlockCodeSize=4>
// CHECK-NEXT: <Type abbrevid=4 op0=4 op1=3/> blob data = 'int'
// CHECK-NEXT: <Name abbrevid=5 op0=1/> blob data = 'J'
// CHECK-NEXT: </FieldTypeBlock>
// CHECK-NEXT: </FunctionBlock>
36 changes: 36 additions & 0 deletions clang-tools-extra/test/clang-doc/mapper-enum.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
// RUN: rm -rf %t
// RUN: mkdir %t
// RUN: echo "" > %t/compile_flags.txt
// RUN: cp "%s" "%t/test.cpp"
// RUN: clang-doc --dump-mapper -doxygen -p %t %t/test.cpp -output=%t/docs
// RUN: llvm-bcanalyzer %t/docs/bc/FC07BD34D5E77782C263FA944447929EA8753740.bc --dump | FileCheck %s --check-prefix CHECK-B
// RUN: llvm-bcanalyzer %t/docs/bc/020E6C32A700C3170C009FCCD41671EDDBEAF575.bc --dump | FileCheck %s --check-prefix CHECK-C

enum B { X, Y };

// CHECK-B: <BLOCKINFO_BLOCK/>
// CHECK-B-NEXT: <VersionBlock NumWords=1 BlockCodeSize=4>
// CHECK-B-NEXT: <Version abbrevid=4 op0=1/>
// CHECK-B-NEXT: </VersionBlock>
// CHECK-B-NEXT: <EnumBlock NumWords={{[0-9]*}} BlockCodeSize=4>
// CHECK-B-NEXT: <USR abbrevid=4 op0=20 op1=252 op2=7 op3=189 op4=52 op5=213 op6=231 op7=119 op8=130 op9=194 op10=99 op11=250 op12=148 op13=68 op14=71 op15=146 op16=158 op17=168 op18=117 op19=55 op20=64/>
// CHECK-B-NEXT: <Name abbrevid=5 op0=1/> blob data = 'B'
// CHECK-B-NEXT: <DefLocation abbrevid=7 op0=9 op1={{[0-9]*}}/> blob data = '{{.*}}'
// CHECK-B-NEXT: <Member abbrevid=9 op0=1/> blob data = 'X'
// CHECK-B-NEXT: <Member abbrevid=9 op0=1/> blob data = 'Y'
// CHECK-B-NEXT: </EnumBlock>

enum class C { A, B };

// CHECK-C: <BLOCKINFO_BLOCK/>
// CHECK-C-NEXT: <VersionBlock NumWords=1 BlockCodeSize=4>
// CHECK-C-NEXT: <Version abbrevid=4 op0=1/>
// CHECK-C-NEXT: </VersionBlock>
// CHECK-C-NEXT: <EnumBlock NumWords={{[0-9]*}} BlockCodeSize=4>
// CHECK-C-NEXT: <USR abbrevid=4 op0=20 op1=2 op2=14 op3=108 op4=50 op5=167 op6=0 op7=195 op8=23 op9=12 op10=0 op11=159 op12=204 op13=212 op14=22 op15=113 op16=237 op17=219 op18=234 op19=245 op20=117/>
// CHECK-C-NEXT: <Name abbrevid=5 op0=1/> blob data = 'C'
// CHECK-C-NEXT: <DefLocation abbrevid=7 op0=23 op1={{[0-9]*}}/> blob data = '{{.*}}'
// CHECK-C-NEXT: <Scoped abbrevid=10 op0=1/>
// CHECK-C-NEXT: <Member abbrevid=9 op0=1/> blob data = 'A'
// CHECK-C-NEXT: <Member abbrevid=9 op0=1/> blob data = 'B'
// CHECK-C-NEXT: </EnumBlock>
25 changes: 25 additions & 0 deletions clang-tools-extra/test/clang-doc/mapper-function.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
// RUN: rm -rf %t
// RUN: mkdir %t
// RUN: echo "" > %t/compile_flags.txt
// RUN: cp "%s" "%t/test.cpp"
// RUN: clang-doc --dump-mapper -doxygen -p %t %t/test.cpp -output=%t/docs
// RUN: llvm-bcanalyzer %t/docs/bc/A44B32CC3C087C9AF75DAF50DE193E85E7B2C16B.bc --dump | FileCheck %s

int F(int param) { return param; }

// CHECK: <BLOCKINFO_BLOCK/>
// CHECK-NEXT: <VersionBlock NumWords=1 BlockCodeSize=4>
// CHECK-NEXT: <Version abbrevid=4 op0=1/>
// CHECK-NEXT: </VersionBlock>
// CHECK-NEXT: <FunctionBlock NumWords={{[0-9]*}} BlockCodeSize=4>
// CHECK-NEXT: <USR abbrevid=4 op0=20 op1=164 op2=75 op3=50 op4=204 op5=60 op6=8 op7=124 op8=154 op9=247 op10=93 op11=175 op12=80 op13=222 op14=25 op15=62 op16=133 op17=231 op18=178 op19=193 op20=107/>
// CHECK-NEXT: <Name abbrevid=5 op0=1/> blob data = 'F'
// CHECK-NEXT: <DefLocation abbrevid=7 op0=8 op1={{[0-9]*}}/> blob data = '{{.*}}'
// CHECK-NEXT: <TypeBlock NumWords=4 BlockCodeSize=4>
// CHECK-NEXT: <Type abbrevid=4 op0=4 op1=3/> blob data = 'int'
// CHECK-NEXT: </TypeBlock>
// CHECK-NEXT: <FieldTypeBlock NumWords=7 BlockCodeSize=4>
// CHECK-NEXT: <Type abbrevid=4 op0=4 op1=3/> blob data = 'int'
// CHECK-NEXT: <Name abbrevid=5 op0=5/> blob data = 'param'
// CHECK-NEXT: </FieldTypeBlock>
// CHECK-NEXT: </FunctionBlock>
43 changes: 43 additions & 0 deletions clang-tools-extra/test/clang-doc/mapper-method.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
// RUN: rm -rf %t
// RUN: mkdir %t
// RUN: echo "" > %t/compile_flags.txt
// RUN: cp "%s" "%t/test.cpp"
// RUN: clang-doc --dump-mapper -doxygen -p %t %t/test.cpp -output=%t/docs
// RUN: llvm-bcanalyzer %t/docs/bc/F0F9FC65FC90F54F690144A7AFB15DFC3D69B6E6.bc --dump | FileCheck %s --check-prefix CHECK-G-F
// RUN: llvm-bcanalyzer %t/docs/bc/4202E8BF0ECB12AE354C8499C52725B0EE30AED5.bc --dump | FileCheck %s --check-prefix CHECK-G

class G {
public:
int Method(int param) { return param; }
};

// CHECK-G: <BLOCKINFO_BLOCK/>
// CHECK-G-NEXT: <VersionBlock NumWords=1 BlockCodeSize=4>
// CHECK-G-NEXT: <Version abbrevid=4 op0=1/>
// CHECK-G-NEXT: </VersionBlock>
// CHECK-G-NEXT: <RecordBlock NumWords={{[0-9]*}} BlockCodeSize=4>
// CHECK-G-NEXT: <USR abbrevid=4 op0=20 op1=66 op2=2 op3=232 op4=191 op5=14 op6=203 op7=18 op8=174 op9=53 op10=76 op11=132 op12=153 op13=197 op14=39 op15=37 op16=176 op17=238 op18=48 op19=174 op20=213/>
// CHECK-G-NEXT: <Name abbrevid=5 op0=1/> blob data = 'G'
// CHECK-G-NEXT: <DefLocation abbrevid=7 op0=9 op1={{[0-9]*}}/> blob data = '{{.*}}'
// CHECK-G-NEXT: <TagType abbrevid=9 op0=3/>
// CHECK-G-NEXT: </RecordBlock>

// CHECK-G-F: <BLOCKINFO_BLOCK/>
// CHECK-G-F-NEXT: <VersionBlock NumWords=1 BlockCodeSize=4>
// CHECK-G-F-NEXT: <Version abbrevid=4 op0=1/>
// CHECK-G-F-NEXT: </VersionBlock>
// CHECK-G-F-NEXT: <FunctionBlock NumWords={{[0-9]*}} BlockCodeSize=4>
// CHECK-G-F-NEXT: <USR abbrevid=4 op0=20 op1=240 op2=249 op3=252 op4=101 op5=252 op6=144 op7=245 op8=79 op9=105 op10=1 op11=68 op12=167 op13=175 op14=177 op15=93 op16=252 op17=61 op18=105 op19=182 op20=230/>
// CHECK-G-F-NEXT: <Name abbrevid=5 op0=6/> blob data = 'Method'
// CHECK-G-F-NEXT: <Namespace abbrevid=6 op0=1 op1=40/> blob data = '4202E8BF0ECB12AE354C8499C52725B0EE30AED5'
// CHECK-G-F-NEXT: <IsMethod abbrevid=11 op0=1/>
// CHECK-G-F-NEXT: <DefLocation abbrevid=7 op0=11 op1={{[0-9]*}}/> blob data = '{{.*}}'
// CHECK-G-F-NEXT: <Parent abbrevid=9 op0=1 op1=40/> blob data = '4202E8BF0ECB12AE354C8499C52725B0EE30AED5'
// CHECK-G-F-NEXT: <TypeBlock NumWords=4 BlockCodeSize=4>
// CHECK-G-F-NEXT: <Type abbrevid=4 op0=4 op1=3/> blob data = 'int'
// CHECK-G-F-NEXT: </TypeBlock>
// CHECK-G-F-NEXT: <FieldTypeBlock NumWords=7 BlockCodeSize=4>
// CHECK-G-F-NEXT: <Type abbrevid=4 op0=4 op1=3/> blob data = 'int'
// CHECK-G-F-NEXT: <Name abbrevid=5 op0=5/> blob data = 'param'
// CHECK-G-F-NEXT: </FieldTypeBlock>
// CHECK-G-F-NEXT: </FunctionBlock>
17 changes: 17 additions & 0 deletions clang-tools-extra/test/clang-doc/mapper-namespace.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
// RUN: rm -rf %t
// RUN: mkdir %t
// RUN: echo "" > %t/compile_flags.txt
// RUN: cp "%s" "%t/test.cpp"
// RUN: clang-doc --dump-mapper -doxygen -p %t %t/test.cpp -output=%t/docs
// RUN: llvm-bcanalyzer %t/docs/bc/8D042EFFC98B373450BC6B5B90A330C25A150E9C.bc --dump | FileCheck %s

namespace A {}

// CHECK: <BLOCKINFO_BLOCK/>
// CHECK-NEXT: <VersionBlock NumWords=1 BlockCodeSize=4>
// CHECK-NEXT: <Version abbrevid=4 op0=1/>
// CHECK-NEXT: </VersionBlock>
// CHECK-NEXT: <NamespaceBlock NumWords={{[0-9]*}} BlockCodeSize=4>
// CHECK-NEXT: <USR abbrevid=4 op0=20 op1=141 op2=4 op3=46 op4=255 op5=201 op6=139 op7=55 op8=52 op9=80 op10=188 op11=107 op12=91 op13=144 op14=163 op15=48 op16=194 op17=90 op18=21 op19=14 op20=156/>
// CHECK-NEXT: <Name abbrevid=5 op0=1/> blob data = 'A'
// CHECK-NEXT: </NamespaceBlock>
23 changes: 23 additions & 0 deletions clang-tools-extra/test/clang-doc/mapper-struct.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
// RUN: rm -rf %t
// RUN: mkdir %t
// RUN: echo "" > %t/compile_flags.txt
// RUN: cp "%s" "%t/test.cpp"
// RUN: clang-doc --dump-mapper -doxygen -p %t %t/test.cpp -output=%t/docs
// RUN: llvm-bcanalyzer %t/docs/bc/06B5F6A19BA9F6A832E127C9968282B94619B210.bc --dump | FileCheck %s

struct C { int i; };

// CHECK: <BLOCKINFO_BLOCK/>
// CHECK-NEXT: <VersionBlock NumWords=1 BlockCodeSize=4>
// CHECK-NEXT: <Version abbrevid=4 op0=1/>
// CHECK-NEXT: </VersionBlock>
// CHECK-NEXT: <RecordBlock NumWords={{[0-9]*}} BlockCodeSize=4>
// CHECK-NEXT: <USR abbrevid=4 op0=20 op1=6 op2=181 op3=246 op4=161 op5=155 op6=169 op7=246 op8=168 op9=50 op10=225 op11=39 op12=201 op13=150 op14=130 op15=130 op16=185 op17=70 op18=25 op19=178 op20=16/>
// CHECK-NEXT: <Name abbrevid=5 op0=1/> blob data = 'C'
// CHECK-NEXT: <DefLocation abbrevid=7 op0=8 op1={{[0-9]*}}/> blob data = '{{.*}}'
// CHECK-NEXT: <MemberTypeBlock NumWords=6 BlockCodeSize=4>
// CHECK-NEXT: <Type abbrevid=4 op0=4 op1=3/> blob data = 'int'
// CHECK-NEXT: <Name abbrevid=5 op0=4/> blob data = 'C::i'
// CHECK-NEXT: <Access abbrevid=6 op0=3/>
// CHECK-NEXT: </MemberTypeBlock>
// CHECK-NEXT: </RecordBlock>
29 changes: 29 additions & 0 deletions clang-tools-extra/test/clang-doc/mapper-union.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
// RUN: rm -rf %t
// RUN: mkdir %t
// RUN: echo "" > %t/compile_flags.txt
// RUN: cp "%s" "%t/test.cpp"
// RUN: clang-doc --dump-mapper -doxygen -p %t %t/test.cpp -output=%t/docs
// RUN: llvm-bcanalyzer %t/docs/bc/0B8A6B938B939B77C6325CCCC8AA3E938BF9E2E8.bc --dump | FileCheck %s

union D { int X; int Y; };

// CHECK: <BLOCKINFO_BLOCK/>
// CHECK-NEXT: <VersionBlock NumWords=1 BlockCodeSize=4>
// CHECK-NEXT: <Version abbrevid=4 op0=1/>
// CHECK-NEXT: </VersionBlock>
// CHECK-NEXT: <RecordBlock NumWords={{[0-9]*}} BlockCodeSize=4>
// CHECK-NEXT: <USR abbrevid=4 op0=20 op1=11 op2=138 op3=107 op4=147 op5=139 op6=147 op7=155 op8=119 op9=198 op10=50 op11=92 op12=204 op13=200 op14=170 op15=62 op16=147 op17=139 op18=249 op19=226 op20=232/>
// CHECK-NEXT: <Name abbrevid=5 op0=1/> blob data = 'D'
// CHECK-NEXT: <DefLocation abbrevid=7 op0=8 op1={{[0-9]*}}/> blob data = '{{.*}}'
// CHECK-NEXT: <TagType abbrevid=9 op0=2/>
// CHECK-NEXT: <MemberTypeBlock NumWords=6 BlockCodeSize=4>
// CHECK-NEXT: <Type abbrevid=4 op0=4 op1=3/> blob data = 'int'
// CHECK-NEXT: <Name abbrevid=5 op0=4/> blob data = 'D::X'
// CHECK-NEXT: <Access abbrevid=6 op0=3/>
// CHECK-NEXT: </MemberTypeBlock>
// CHECK-NEXT: <MemberTypeBlock NumWords=6 BlockCodeSize=4>
// CHECK-NEXT: <Type abbrevid=4 op0=4 op1=3/> blob data = 'int'
// CHECK-NEXT: <Name abbrevid=5 op0=4/> blob data = 'D::Y'
// CHECK-NEXT: <Access abbrevid=6 op0=3/>
// CHECK-NEXT: </MemberTypeBlock>
// CHECK-NEXT: </RecordBlock>