Skip to content

[clang-doc] Extract Info into JSON values #138063

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: users/ilovepi/clang-doc-mustache-templete-helper
Choose a base branch
from
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
255 changes: 253 additions & 2 deletions clang-tools-extra/clang-doc/HTMLMustacheGenerator.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -141,21 +141,272 @@ Error MustacheHTMLGenerator::generateDocs(
return Error::success();
}

static json::Value
extractValue(const Location &L,
std::optional<StringRef> RepositoryUrl = std::nullopt) {
Object Obj = Object();
// Should there be Start/End line numbers?
Obj.insert({"LineNumber", L.StartLineNumber});
Obj.insert({"Filename", L.Filename});

if (!L.IsFileInRootDir || !RepositoryUrl) {
return Obj;
}
SmallString<128> FileURL(*RepositoryUrl);
sys::path::append(FileURL, sys::path::Style::posix, L.Filename);
FileURL += "#" + std::to_string(L.StartLineNumber);
Obj.insert({"FileURL", FileURL});

return Obj;
}

static json::Value extractValue(const Reference &I,
StringRef CurrentDirectory) {
SmallString<64> Path = I.getRelativeFilePath(CurrentDirectory);
sys::path::append(Path, I.getFileBaseName() + ".html");
sys::path::native(Path, sys::path::Style::posix);
Object Obj = Object();
Obj.insert({"Link", Path});
Obj.insert({"Name", I.Name});
Obj.insert({"QualName", I.QualName});
Obj.insert({"ID", toHex(toStringRef(I.USR))});
return Obj;
}

static json::Value extractValue(const TypedefInfo &I) {
// Not Supported
return nullptr;
}

static json::Value extractValue(const CommentInfo &I) {
assert((I.Kind == "BlockCommandComment" || I.Kind == "FullComment" ||
I.Kind == "ParagraphComment" || I.Kind == "TextComment") &&
"Unknown Comment type in CommentInfo.");

Object Obj = Object();
json::Value Child = Object();

// TextComment has no children, so return it.
if (I.Kind == "TextComment") {
Obj.insert({"TextComment", I.Text});
return Obj;
}

// BlockCommandComment needs to generate a Command key.
if (I.Kind == "BlockCommandComment") {
Child.getAsObject()->insert({"Command", I.Name});
}

// Use the same handling for everything else.
// Only valid for:
// - BlockCommandComment
// - FullComment
// - ParagraphComment
json::Value ChildArr = Array();
auto &CARef = *ChildArr.getAsArray();
CARef.reserve(I.Children.size());
for (const auto &C : I.Children)
CARef.emplace_back(extractValue(*C));
Child.getAsObject()->insert({"Children", ChildArr});
Obj.insert({I.Kind, Child});

return Obj;
}

static void maybeInsertLocation(std::optional<Location> Loc,
const ClangDocContext &CDCtx, Object &Obj) {
if (!Loc)
return;
Location L = *Loc;
Obj.insert({"Location", extractValue(L, CDCtx.RepositoryUrl)});
}

static void extractDescriptionFromInfo(ArrayRef<CommentInfo> Descriptions,
json::Object &EnumValObj) {
if (Descriptions.empty())
return;
json::Value ArrDesc = Array();
json::Array &ADescRef = *ArrDesc.getAsArray();
for (const CommentInfo &Child : Descriptions)
ADescRef.emplace_back(extractValue(Child));
EnumValObj.insert({"EnumValueComments", ArrDesc});
}

static json::Value extractValue(const FunctionInfo &I, StringRef ParentInfoDir,
const ClangDocContext &CDCtx) {
Object Obj = Object();
Obj.insert({"Name", I.Name});
Obj.insert({"ID", toHex(toStringRef(I.USR))});
Obj.insert({"Access", getAccessSpelling(I.Access).str()});
Obj.insert({"ReturnType", extractValue(I.ReturnType.Type, ParentInfoDir)});

json::Value ParamArr = Array();
for (const auto Val : enumerate(I.Params)) {
json::Value V = Object();
auto &VRef = *V.getAsObject();
VRef.insert({"Name", Val.value().Name});
VRef.insert({"Type", Val.value().Type.Name});
VRef.insert({"End", Val.index() + 1 == I.Params.size()});
ParamArr.getAsArray()->emplace_back(V);
}
Obj.insert({"Params", ParamArr});

maybeInsertLocation(I.DefLoc, CDCtx, Obj);
return Obj;
}

static json::Value extractValue(const EnumInfo &I,
const ClangDocContext &CDCtx) {
Object Obj = Object();
std::string EnumType = I.Scoped ? "enum class " : "enum ";
EnumType += I.Name;
bool HasComment = std::any_of(
I.Members.begin(), I.Members.end(),
[](const EnumValueInfo &M) { return !M.Description.empty(); });
Obj.insert({"EnumName", EnumType});
Obj.insert({"HasComment", HasComment});
Obj.insert({"ID", toHex(toStringRef(I.USR))});
json::Value Arr = Array();
json::Array &ARef = *Arr.getAsArray();
for (const EnumValueInfo &M : I.Members) {
json::Value EnumValue = Object();
auto &EnumValObj = *EnumValue.getAsObject();
EnumValObj.insert({"Name", M.Name});
if (!M.ValueExpr.empty())
EnumValObj.insert({"ValueExpr", M.ValueExpr});
else
EnumValObj.insert({"Value", M.Value});

extractDescriptionFromInfo(M.Description, EnumValObj);
ARef.emplace_back(EnumValue);
}
Obj.insert({"EnumValues", Arr});

extractDescriptionFromInfo(I.Description, Obj);
maybeInsertLocation(I.DefLoc, CDCtx, Obj);

return Obj;
}

static void extractScopeChildren(const ScopeChildren &S, Object &Obj,
StringRef ParentInfoDir,
const ClangDocContext &CDCtx) {
json::Value ArrNamespace = Array();
for (const Reference &Child : S.Namespaces)
ArrNamespace.getAsArray()->emplace_back(extractValue(Child, ParentInfoDir));

if (!ArrNamespace.getAsArray()->empty())
Obj.insert({"Namespace", Object{{"Links", ArrNamespace}}});

json::Value ArrRecord = Array();
for (const Reference &Child : S.Records)
ArrRecord.getAsArray()->emplace_back(extractValue(Child, ParentInfoDir));

if (!ArrRecord.getAsArray()->empty())
Obj.insert({"Record", Object{{"Links", ArrRecord}}});

json::Value ArrFunction = Array();
json::Value PublicFunction = Array();
json::Value ProtectedFunction = Array();
json::Value PrivateFunction = Array();

for (const FunctionInfo &Child : S.Functions) {
json::Value F = extractValue(Child, ParentInfoDir, CDCtx);
AccessSpecifier Access = Child.Access;
if (Access == AccessSpecifier::AS_public)
PublicFunction.getAsArray()->emplace_back(F);
else if (Access == AccessSpecifier::AS_protected)
ProtectedFunction.getAsArray()->emplace_back(F);
else
ArrFunction.getAsArray()->emplace_back(F);
}

if (!ArrFunction.getAsArray()->empty())
Obj.insert({"Function", Object{{"Obj", ArrFunction}}});

if (!PublicFunction.getAsArray()->empty())
Obj.insert({"PublicFunction", Object{{"Obj", PublicFunction}}});

if (!ProtectedFunction.getAsArray()->empty())
Obj.insert({"ProtectedFunction", Object{{"Obj", ProtectedFunction}}});

json::Value ArrEnum = Array();
auto &ArrEnumRef = *ArrEnum.getAsArray();
for (const EnumInfo &Child : S.Enums)
ArrEnumRef.emplace_back(extractValue(Child, CDCtx));

if (!ArrEnumRef.empty())
Obj.insert({"Enums", Object{{"Obj", ArrEnum}}});

json::Value ArrTypedefs = Array();
auto &ArrTypedefsRef = *ArrTypedefs.getAsArray();
for (const TypedefInfo &Child : S.Typedefs)
ArrTypedefsRef.emplace_back(extractValue(Child));

if (!ArrTypedefsRef.empty())
Obj.insert({"Typedefs", Object{{"Obj", ArrTypedefs}}});
}

static json::Value extractValue(const NamespaceInfo &I,
const ClangDocContext &CDCtx) {
Object NamespaceValue = Object();
std::string InfoTitle = I.Name.empty() ? "Global Namespace"
: (Twine("namespace ") + I.Name).str();

StringRef BasePath = I.getRelativeFilePath("");
NamespaceValue.insert({"NamespaceTitle", InfoTitle});
NamespaceValue.insert({"NamespacePath", BasePath});

extractDescriptionFromInfo(I.Description, NamespaceValue);
extractScopeChildren(I.Children, NamespaceValue, BasePath, CDCtx);
return NamespaceValue;
}

static json::Value extractValue(const RecordInfo &I,
const ClangDocContext &CDCtx) {
Object RecordValue = Object();
extractDescriptionFromInfo(I.Description, RecordValue);
RecordValue.insert({"Name", I.Name});
RecordValue.insert({"FullName", I.FullName});
RecordValue.insert({"RecordType", getTagType(I.TagType)});

maybeInsertLocation(I.DefLoc, CDCtx, RecordValue);

StringRef BasePath = I.getRelativeFilePath("");
extractScopeChildren(I.Children, RecordValue, BasePath, CDCtx);
json::Value PublicMembers = Array();
json::Array &PubMemberRef = *PublicMembers.getAsArray();
json::Value ProtectedMembers = Array();
json::Array &ProtMemberRef = *ProtectedMembers.getAsArray();
json::Value PrivateMembers = Array();
json::Array &PrivMemberRef = *PrivateMembers.getAsArray();
for (const MemberTypeInfo &Member : I.Members) {
json::Value MemberValue = Object();
auto &MVRef = *MemberValue.getAsObject();
MVRef.insert({"Name", Member.Name});
MVRef.insert({"Type", Member.Type.Name});
extractDescriptionFromInfo(Member.Description, MVRef);

if (Member.Access == AccessSpecifier::AS_public)
PubMemberRef.emplace_back(MemberValue);
else if (Member.Access == AccessSpecifier::AS_protected)
ProtMemberRef.emplace_back(MemberValue);
else if (Member.Access == AccessSpecifier::AS_private)
ProtMemberRef.emplace_back(MemberValue);
}
if (!PubMemberRef.empty())
RecordValue.insert({"PublicMembers", Object{{"Obj", PublicMembers}}});
if (!ProtMemberRef.empty())
RecordValue.insert({"ProtectedMembers", Object{{"Obj", ProtectedMembers}}});
if (!PrivMemberRef.empty())
RecordValue.insert({"PrivateMembers", Object{{"Obj", PrivateMembers}}});

return RecordValue;
}

static void setupTemplateValue(const ClangDocContext &CDCtx, json::Value &V,
Info *I) {}

Info *I) {
}
Error MustacheHTMLGenerator::generateDocForInfo(Info *I, raw_ostream &OS,
const ClangDocContext &CDCtx) {
switch (I->IT) {
Expand Down
Loading