aboutsummaryrefslogtreecommitdiff
path: root/clang-tools-extra/clang-doc/HTMLMustacheGenerator.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'clang-tools-extra/clang-doc/HTMLMustacheGenerator.cpp')
-rw-r--r--clang-tools-extra/clang-doc/HTMLMustacheGenerator.cpp489
1 files changed, 80 insertions, 409 deletions
diff --git a/clang-tools-extra/clang-doc/HTMLMustacheGenerator.cpp b/clang-tools-extra/clang-doc/HTMLMustacheGenerator.cpp
index 7aeaa1b..a64cb5e 100644
--- a/clang-tools-extra/clang-doc/HTMLMustacheGenerator.cpp
+++ b/clang-tools-extra/clang-doc/HTMLMustacheGenerator.cpp
@@ -27,6 +27,9 @@ using namespace llvm::mustache;
namespace clang {
namespace doc {
+static Error generateDocForJSON(json::Value &JSON, StringRef Filename,
+ StringRef Path, raw_fd_ostream &OS,
+ const ClangDocContext &CDCtx);
static Error createFileOpenError(StringRef FileName, std::error_code EC) {
return createFileError("cannot open file " + FileName, EC);
@@ -132,404 +135,68 @@ Error MustacheHTMLGenerator::generateDocs(
return Err;
}
- // Track which directories we already tried to create.
- StringSet<> CreatedDirs;
- // Collect all output by file name and create the necessary directories.
- StringMap<std::vector<doc::Info *>> FileToInfos;
- for (const auto &Group : Infos) {
- llvm::TimeTraceScope TS("setup directories");
- doc::Info *Info = Group.getValue().get();
-
- SmallString<128> Path;
- sys::path::native(RootDir, Path);
- sys::path::append(Path, Info->getRelativeFilePath(""));
- if (!CreatedDirs.contains(Path)) {
- if (std::error_code EC = sys::fs::create_directories(Path))
- return createStringError(EC, "failed to create directory '%s'.",
- Path.c_str());
- CreatedDirs.insert(Path);
- }
-
- sys::path::append(Path, Info->getFileBaseName() + ".html");
- FileToInfos[Path].push_back(Info);
+ {
+ llvm::TimeTraceScope TS("Generate JSON for Mustache");
+ if (auto JSONGenerator = findGeneratorByName("json")) {
+ if (Error Err = JSONGenerator.get()->generateDocs(
+ RootDir, std::move(Infos), CDCtx))
+ return Err;
+ } else
+ return JSONGenerator.takeError();
}
+ StringMap<json::Value> JSONFileMap;
{
- llvm::TimeTraceScope TS("Generate Docs");
- for (const auto &Group : FileToInfos) {
- llvm::TimeTraceScope TS("Info to Doc");
+ llvm::TimeTraceScope TS("Iterate JSON files");
+ std::error_code EC;
+ sys::fs::directory_iterator JSONIter(RootDir, EC);
+ std::vector<json::Value> JSONFiles;
+ JSONFiles.reserve(Infos.size());
+ if (EC)
+ return createStringError("Failed to create directory iterator.");
+
+ while (JSONIter != sys::fs::directory_iterator()) {
+ if (EC)
+ return createFileError("Failed to iterate: " + JSONIter->path(), EC);
+
+ auto Path = StringRef(JSONIter->path());
+ if (!Path.ends_with(".json")) {
+ JSONIter.increment(EC);
+ continue;
+ }
+
+ auto File = MemoryBuffer::getFile(Path);
+ if (EC = File.getError(); EC)
+ // TODO: Buffer errors to report later, look into using Clang
+ // diagnostics.
+ llvm::errs() << "Failed to open file: " << Path << " " << EC.message()
+ << '\n';
+
+ auto Parsed = json::parse((*File)->getBuffer());
+ if (!Parsed)
+ return Parsed.takeError();
+
std::error_code FileErr;
- raw_fd_ostream InfoOS(Group.getKey(), FileErr, sys::fs::OF_None);
+ SmallString<16> HTMLPath(Path.begin(), Path.end());
+ sys::path::replace_extension(HTMLPath, "html");
+ raw_fd_ostream InfoOS(HTMLPath, FileErr, sys::fs::OF_None);
if (FileErr)
- return createFileOpenError(Group.getKey(), FileErr);
+ return createFileOpenError(Path, FileErr);
- for (const auto &Info : Group.getValue())
- if (Error Err = generateDocForInfo(Info, InfoOS, CDCtx))
- return Err;
+ if (Error Err = generateDocForJSON(*Parsed, sys::path::stem(HTMLPath),
+ HTMLPath, InfoOS, CDCtx))
+ return Err;
+ JSONIter.increment(EC);
}
}
- return Error::success();
-}
-
-static json::Value
-extractValue(const Location &L,
- std::optional<StringRef> RepositoryUrl = std::nullopt) {
- Object Obj = Object();
- // TODO: Consider using both Start/End line numbers to improve location report
- 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) {
- Object Obj = Object();
-
- json::Value ChildVal = Object();
- Object &Child = *ChildVal.getAsObject();
-
- json::Value ChildArr = Array();
- auto &CARef = *ChildArr.getAsArray();
- CARef.reserve(I.Children.size());
- for (const auto &C : I.Children)
- CARef.emplace_back(extractValue(*C));
-
- switch (I.Kind) {
- case CommentKind::CK_TextComment: {
- Obj.insert({commentKindToString(I.Kind), I.Text});
- return Obj;
- }
-
- case CommentKind::CK_BlockCommandComment: {
- Child.insert({"Command", I.Name});
- Child.insert({"Children", ChildArr});
- Obj.insert({commentKindToString(I.Kind), ChildVal});
- return Obj;
- }
-
- case CommentKind::CK_InlineCommandComment: {
- json::Value ArgsArr = Array();
- auto &ARef = *ArgsArr.getAsArray();
- ARef.reserve(I.Args.size());
- for (const auto &Arg : I.Args)
- ARef.emplace_back(Arg);
- Child.insert({"Command", I.Name});
- Child.insert({"Args", ArgsArr});
- Child.insert({"Children", ChildArr});
- Obj.insert({commentKindToString(I.Kind), ChildVal});
- return Obj;
- }
-
- case CommentKind::CK_ParamCommandComment:
- case CommentKind::CK_TParamCommandComment: {
- Child.insert({"ParamName", I.ParamName});
- Child.insert({"Direction", I.Direction});
- Child.insert({"Explicit", I.Explicit});
- Child.insert({"Children", ChildArr});
- Obj.insert({commentKindToString(I.Kind), ChildVal});
- return Obj;
- }
-
- case CommentKind::CK_VerbatimBlockComment: {
- Child.insert({"Text", I.Text});
- if (!I.CloseName.empty())
- Child.insert({"CloseName", I.CloseName});
- Child.insert({"Children", ChildArr});
- Obj.insert({commentKindToString(I.Kind), ChildVal});
- return Obj;
- }
-
- case CommentKind::CK_VerbatimBlockLineComment:
- case CommentKind::CK_VerbatimLineComment: {
- Child.insert({"Text", I.Text});
- Child.insert({"Children", ChildArr});
- Obj.insert({commentKindToString(I.Kind), ChildVal});
- return Obj;
- }
-
- case CommentKind::CK_HTMLStartTagComment: {
- json::Value AttrKeysArray = json::Array();
- json::Value AttrValuesArray = json::Array();
- auto &KeyArr = *AttrKeysArray.getAsArray();
- auto &ValArr = *AttrValuesArray.getAsArray();
- KeyArr.reserve(I.AttrKeys.size());
- ValArr.reserve(I.AttrValues.size());
- for (const auto &K : I.AttrKeys)
- KeyArr.emplace_back(K);
- for (const auto &V : I.AttrValues)
- ValArr.emplace_back(V);
- Child.insert({"Name", I.Name});
- Child.insert({"SelfClosing", I.SelfClosing});
- Child.insert({"AttrKeys", AttrKeysArray});
- Child.insert({"AttrValues", AttrValuesArray});
- Child.insert({"Children", ChildArr});
- Obj.insert({commentKindToString(I.Kind), ChildVal});
- return Obj;
- }
-
- case CommentKind::CK_HTMLEndTagComment: {
- Child.insert({"Name", I.Name});
- Child.insert({"Children", ChildArr});
- Obj.insert({commentKindToString(I.Kind), ChildVal});
- return Obj;
- }
-
- case CommentKind::CK_FullComment:
- case CommentKind::CK_ParagraphComment: {
- Child.insert({"Children", ChildArr});
- Obj.insert({commentKindToString(I.Kind), ChildVal});
- return Obj;
- }
-
- case CommentKind::CK_Unknown: {
- Obj.insert({commentKindToString(I.Kind), I.Text});
- return Obj;
- }
- }
- llvm_unreachable("Unknown comment kind encountered.");
-}
-
-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 DescArr = Array();
- json::Array &DescARef = *DescArr.getAsArray();
- DescARef.reserve(Descriptions.size());
- for (const CommentInfo &Child : Descriptions)
- DescARef.emplace_back(extractValue(Child));
- EnumValObj.insert({"EnumValueComments", DescArr});
-}
-
-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();
- json::Array &ParamARef = *ParamArr.getAsArray();
- ParamARef.reserve(I.Params.size());
- 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()});
- ParamARef.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 = llvm::any_of(
- I.Members, [](const EnumValueInfo &M) { return !M.Description.empty(); });
- Obj.insert({"EnumName", EnumType});
- Obj.insert({"HasComment", HasComment});
- Obj.insert({"ID", toHex(toStringRef(I.USR))});
- json::Value EnumArr = Array();
- json::Array &EnumARef = *EnumArr.getAsArray();
- EnumARef.reserve(I.Members.size());
- 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);
- EnumARef.emplace_back(EnumValue);
- }
- Obj.insert({"EnumValues", EnumArr});
-
- 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 NamespaceArr = Array();
- json::Array &NamespaceARef = *NamespaceArr.getAsArray();
- NamespaceARef.reserve(S.Namespaces.size());
- for (const Reference &Child : S.Namespaces)
- NamespaceARef.emplace_back(extractValue(Child, ParentInfoDir));
-
- if (!NamespaceARef.empty())
- Obj.insert({"Namespace", Object{{"Links", NamespaceArr}}});
-
- json::Value RecordArr = Array();
- json::Array &RecordARef = *RecordArr.getAsArray();
- RecordARef.reserve(S.Records.size());
- for (const Reference &Child : S.Records)
- RecordARef.emplace_back(extractValue(Child, ParentInfoDir));
-
- if (!RecordARef.empty())
- Obj.insert({"Record", Object{{"Links", RecordArr}}});
-
- json::Value FunctionArr = Array();
- json::Array &FunctionARef = *FunctionArr.getAsArray();
- FunctionARef.reserve(S.Functions.size());
-
- json::Value PublicFunctionArr = Array();
- json::Array &PublicFunctionARef = *PublicFunctionArr.getAsArray();
- PublicFunctionARef.reserve(S.Functions.size());
-
- json::Value ProtectedFunctionArr = Array();
- json::Array &ProtectedFunctionARef = *ProtectedFunctionArr.getAsArray();
- ProtectedFunctionARef.reserve(S.Functions.size());
-
- for (const FunctionInfo &Child : S.Functions) {
- json::Value F = extractValue(Child, ParentInfoDir, CDCtx);
- AccessSpecifier Access = Child.Access;
- if (Access == AccessSpecifier::AS_public)
- PublicFunctionARef.emplace_back(F);
- else if (Access == AccessSpecifier::AS_protected)
- ProtectedFunctionARef.emplace_back(F);
- else
- FunctionARef.emplace_back(F);
- }
-
- if (!FunctionARef.empty())
- Obj.insert({"Function", Object{{"Obj", FunctionArr}}});
-
- if (!PublicFunctionARef.empty())
- Obj.insert({"PublicFunction", Object{{"Obj", PublicFunctionArr}}});
-
- if (!ProtectedFunctionARef.empty())
- Obj.insert({"ProtectedFunction", Object{{"Obj", ProtectedFunctionArr}}});
-
- json::Value EnumArr = Array();
- auto &EnumARef = *EnumArr.getAsArray();
- EnumARef.reserve(S.Enums.size());
- for (const EnumInfo &Child : S.Enums)
- EnumARef.emplace_back(extractValue(Child, CDCtx));
-
- if (!EnumARef.empty())
- Obj.insert({"Enums", Object{{"Obj", EnumArr}}});
-
- json::Value TypedefArr = Array();
- auto &TypedefARef = *TypedefArr.getAsArray();
- TypedefARef.reserve(S.Typedefs.size());
- for (const TypedefInfo &Child : S.Typedefs)
- TypedefARef.emplace_back(extractValue(Child));
-
- if (!TypedefARef.empty())
- Obj.insert({"Typedefs", Object{{"Obj", TypedefArr}}});
-}
-
-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();
-
- SmallString<64> 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);
-
- SmallString<64> BasePath = I.getRelativeFilePath("");
- extractScopeChildren(I.Children, RecordValue, BasePath, CDCtx);
- json::Value PublicMembers = Array();
- json::Array &PubMemberRef = *PublicMembers.getAsArray();
- PubMemberRef.reserve(I.Members.size());
- json::Value ProtectedMembers = Array();
- json::Array &ProtMemberRef = *ProtectedMembers.getAsArray();
- ProtMemberRef.reserve(I.Members.size());
- json::Value PrivateMembers = Array();
- json::Array &PrivMemberRef = *PrivateMembers.getAsArray();
- PrivMemberRef.reserve(I.Members.size());
- 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;
+ return Error::success();
}
-static Error setupTemplateValue(const ClangDocContext &CDCtx, json::Value &V,
- Info *I) {
+static Error setupTemplateValue(const ClangDocContext &CDCtx, json::Value &V) {
V.getAsObject()->insert({"ProjectName", CDCtx.ProjectName});
json::Value StylesheetArr = Array();
- auto InfoPath = I->getRelativeFilePath("");
- SmallString<128> RelativePath = computeRelativePath("", InfoPath);
+ SmallString<128> RelativePath("./");
sys::path::native(RelativePath, sys::path::Style::posix);
auto *SSA = StylesheetArr.getAsArray();
@@ -555,38 +222,42 @@ static Error setupTemplateValue(const ClangDocContext &CDCtx, json::Value &V,
return Error::success();
}
-Error MustacheHTMLGenerator::generateDocForInfo(Info *I, raw_ostream &OS,
- const ClangDocContext &CDCtx) {
- switch (I->IT) {
- case InfoType::IT_namespace: {
- json::Value V =
- extractValue(*static_cast<clang::doc::NamespaceInfo *>(I), CDCtx);
- if (auto Err = setupTemplateValue(CDCtx, V, I))
+static Error generateDocForJSON(json::Value &JSON, StringRef Filename,
+ StringRef Path, raw_fd_ostream &OS,
+ const ClangDocContext &CDCtx) {
+ auto StrValue = (*JSON.getAsObject())["InfoType"];
+ if (StrValue.kind() != json::Value::Kind::String)
+ return createStringError("JSON file '%s' does not contain key: 'InfoType'.",
+ Filename.str().c_str());
+ auto ObjTypeStr = StrValue.getAsString();
+ if (!ObjTypeStr.has_value())
+ return createStringError(
+ "JSON file '%s' does not contain 'InfoType' field as a string.",
+ Filename.str().c_str());
+
+ if (ObjTypeStr.value() == "namespace") {
+ if (auto Err = setupTemplateValue(CDCtx, JSON))
return Err;
assert(NamespaceTemplate && "NamespaceTemplate is nullptr.");
- NamespaceTemplate->render(V, OS);
- break;
- }
- case InfoType::IT_record: {
- json::Value V =
- extractValue(*static_cast<clang::doc::RecordInfo *>(I), CDCtx);
- if (auto Err = setupTemplateValue(CDCtx, V, I))
+ NamespaceTemplate->render(JSON, OS);
+ } else if (ObjTypeStr.value() == "record") {
+ if (auto Err = setupTemplateValue(CDCtx, JSON))
return Err;
- // Serialize the JSON value to the output stream in a readable format.
- RecordTemplate->render(V, OS);
- break;
+ assert(RecordTemplate && "RecordTemplate is nullptr.");
+ RecordTemplate->render(JSON, OS);
}
+ return Error::success();
+}
+
+Error MustacheHTMLGenerator::generateDocForInfo(Info *I, raw_ostream &OS,
+ const ClangDocContext &CDCtx) {
+ switch (I->IT) {
case InfoType::IT_enum:
- OS << "IT_enum\n";
- break;
case InfoType::IT_function:
- OS << "IT_Function\n";
- break;
case InfoType::IT_typedef:
- OS << "IT_typedef\n";
- break;
+ case InfoType::IT_namespace:
+ case InfoType::IT_record:
case InfoType::IT_concept:
- break;
case InfoType::IT_variable:
case InfoType::IT_friend:
break;