================ @@ -0,0 +1,352 @@ +#include "Generators.h" +#include "llvm/Support/JSON.h" + +using namespace llvm; +using namespace llvm::json; + +static llvm::ExitOnError ExitOnErr; + +namespace clang { +namespace doc { + +class JSONGenerator : public Generator { +public: + static const char *Format; + + Error generateDocs(StringRef RootDir, + llvm::StringMap<std::unique_ptr<doc::Info>> Infos, + const ClangDocContext &CDCtx) override; + Error createResources(ClangDocContext &CDCtx) override; + Error generateDocForInfo(Info *I, llvm::raw_ostream &OS, + const ClangDocContext &CDCtx) override; +}; + +const char *JSONGenerator::Format = "json"; + +static void serializeInfo(const TypedefInfo &I, json::Object &Obj, + std::optional<StringRef> RepositoryUrl); +static void serializeInfo(const EnumInfo &I, json::Object &Obj, + std::optional<StringRef> RepositoryUrl); + +static json::Object serializeLocation(const Location &Loc, + std::optional<StringRef> RepositoryUrl) { + Object LocationObj = Object(); + LocationObj["LineNumber"] = Loc.StartLineNumber; + LocationObj["Filename"] = Loc.Filename; + + if (!Loc.IsFileInRootDir || !RepositoryUrl) + return LocationObj; + SmallString<128> FileURL(*RepositoryUrl); + sys::path::append(FileURL, sys::path::Style::posix, Loc.Filename); + FileURL += "#" + std::to_string(Loc.StartLineNumber); + LocationObj["FileURL"] = FileURL; + return LocationObj; +} + +static json::Value serializeComment(const CommentInfo &Comment) { + assert((Comment.Kind == "BlockCommandComment" || + Comment.Kind == "FullComment" || Comment.Kind == "ParagraphComment" || + Comment.Kind == "TextComment") && + "Unknown Comment type in CommentInfo."); + + Object Obj = Object(); + json::Value Child = Object(); + + // TextComment has no children, so return it. + if (Comment.Kind == "TextComment") { + Obj["TextComment"] = Comment.Text; + return Obj; + } + + // BlockCommandComment needs to generate a Command key. + if (Comment.Kind == "BlockCommandComment") + Child.getAsObject()->insert({"Command", Comment.Name}); + + // Use the same handling for everything else. + // Only valid for: + // - BlockCommandComment + // - FullComment + // - ParagraphComment + json::Value ChildArr = Array(); + auto &CARef = *ChildArr.getAsArray(); + CARef.reserve(Comment.Children.size()); + for (const auto &C : Comment.Children) + CARef.emplace_back(serializeComment(*C)); + Child.getAsObject()->insert({"Children", ChildArr}); + Obj.insert({Comment.Kind, Child}); + return Obj; +} + +static void serializeCommonAttributes(const Info &I, json::Object &Obj, + std::optional<StringRef> RepositoryUrl) { + Obj["Name"] = I.Name.str(); + Obj["USR"] = toHex(toStringRef(I.USR)); + + if (!I.Path.empty()) + Obj["Path"] = I.Path.str(); + + if (!I.Namespace.empty()) { + Obj["Namespace"] = json::Array(); + for (const auto &NS : I.Namespace) + Obj["Namespace"].getAsArray()->push_back(NS.Name.str()); + } + + if (!I.Description.empty()) { + json::Value DescArray = json::Array(); + auto &DescArrayRef = *DescArray.getAsArray(); + for (const auto &Comment : I.Description) + DescArrayRef.push_back(serializeComment(Comment)); + Obj["Description"] = std::move(DescArray); + } + + // Namespaces aren't SymbolInfos, so they dont have a DefLoc + if (I.IT != InfoType::IT_namespace) { + const auto *Symbol = static_cast<const SymbolInfo *>(&I); + if (Symbol->DefLoc) + Obj["Location"] = + serializeLocation(Symbol->DefLoc.value(), RepositoryUrl); + } +} + +static void serializeReference(const Reference &Ref, Object &ReferenceObj, + SmallString<64> CurrentDirectory) { + SmallString<64> Path = Ref.getRelativeFilePath(CurrentDirectory); + sys::path::append(Path, Ref.getFileBaseName() + ".json"); + sys::path::native(Path, sys::path::Style::posix); + ReferenceObj["Link"] = Path; + ReferenceObj["Name"] = Ref.Name; + ReferenceObj["QualName"] = Ref.QualName; + ReferenceObj["ID"] = toHex(toStringRef(Ref.USR)); +} + +// Although namespaces and records both have ScopeChildren, they serialize them +// differently. Only enums, records, and typedefs are handled here. +static void serializeCommonChildren(const ScopeChildren &Children, + json::Object &Obj, + std::optional<StringRef> RepositoryUrl) { + if (!Children.Enums.empty()) { + json::Value EnumsArray = Array(); + auto &EnumsArrayRef = *EnumsArray.getAsArray(); + for (const auto &Enum : Children.Enums) { + json::Object EnumObj; + serializeInfo(Enum, EnumObj, RepositoryUrl); + EnumsArrayRef.push_back(std::move(EnumObj)); + } + Obj["Enums"] = std::move(EnumsArray); + } + + if (!Children.Typedefs.empty()) { + json::Value TypedefsArray = Array(); + auto &TypedefsArrayRef = *TypedefsArray.getAsArray(); + for (const auto &Typedef : Children.Typedefs) { + json::Object TypedefObj; + serializeInfo(Typedef, TypedefObj, RepositoryUrl); + TypedefsArrayRef.push_back(std::move(TypedefObj)); + } + Obj["Typedefs"] = std::move(TypedefsArray); + } + + if (!Children.Records.empty()) { + json::Value RecordsArray = Array(); + auto &RecordsArrayRef = *RecordsArray.getAsArray(); + for (const auto &Record : Children.Records) { + json::Object RecordObj; + SmallString<64> BasePath = Record.getRelativeFilePath(""); + serializeReference(Record, RecordObj, BasePath); + RecordsArrayRef.push_back(std::move(RecordObj)); + } + Obj["Records"] = std::move(RecordsArray); + } +} + +static void serializeInfo(const TypeInfo &I, Object &Obj) { + Obj["Name"] = I.Type.Name; + Obj["QualName"] = I.Type.QualName; + Obj["ID"] = toHex(toStringRef(I.Type.USR)); + Obj["IsTemplate"] = I.IsTemplate; + Obj["IsBuiltIn"] = I.IsBuiltIn; +} + +static void serializeInfo(const FunctionInfo &F, json::Object &Obj, + std::optional<StringRef> RepositoryURL) { + serializeCommonAttributes(F, Obj, RepositoryURL); + Obj["IsStatic"] = F.IsStatic; + + auto ReturnTypeObj = Object(); + serializeInfo(F.ReturnType, ReturnTypeObj); + Obj["ReturnType"] = std::move(ReturnTypeObj); + + if (!F.Params.empty()) { + json::Value ParamsArray = json::Array(); + auto &ParamsArrayRef = *ParamsArray.getAsArray(); + for (const auto &Param : F.Params) { + json::Object ParamObj; + ParamObj["Name"] = Param.Name; + ParamObj["Type"] = Param.Type.Name; + ParamsArrayRef.push_back(std::move(ParamObj)); + } + Obj["Params"] = std::move(ParamsArray); + } +} + +static void serializeInfo(const EnumInfo &I, json::Object &Obj, + std::optional<StringRef> RepositoryUrl) { + serializeCommonAttributes(I, Obj, RepositoryUrl); + Obj["Scoped"] = I.Scoped; + + if (I.BaseType) { + json::Object BaseTypeObj; + BaseTypeObj["Name"] = I.BaseType->Type.Name; + BaseTypeObj["QualName"] = I.BaseType->Type.QualName; + BaseTypeObj["ID"] = toHex(toStringRef(I.BaseType->Type.USR)); + Obj["BaseType"] = std::move(BaseTypeObj); + } + + if (!I.Members.empty()) { + json::Value MembersArray = Array(); + auto &MembersArrayRef = *MembersArray.getAsArray(); + for (const auto &Member : I.Members) { + json::Object MemberObj; + MemberObj["Name"] = Member.Name; + if (!Member.ValueExpr.empty()) + MemberObj["ValueExpr"] = Member.ValueExpr; + else + MemberObj["Value"] = Member.Value; + MembersArrayRef.push_back(std::move(MemberObj)); + } + Obj["Members"] = std::move(MembersArray); + } +} + +static void serializeInfo(const TypedefInfo &I, json::Object &Obj, + std::optional<StringRef> RepositoryUrl) { + serializeCommonAttributes(I, Obj, RepositoryUrl); + Obj["TypeDeclaration"] = I.TypeDeclaration; + Obj["IsUsing"] = I.IsUsing; + Object TypeObj = Object(); + serializeInfo(I.Underlying, TypeObj); + Obj["Underlying"] = std::move(TypeObj); +} + +static void serializeInfo(const RecordInfo &I, json::Object &Obj, + std::optional<StringRef> RepositoryUrl) { + serializeCommonAttributes(I, Obj, RepositoryUrl); + Obj["FullName"] = I.Name.str(); + Obj["TagType"] = getTagType(I.TagType); + Obj["IsTypedef"] = I.IsTypeDef; + + if (!I.Children.Functions.empty()) { + json::Value PublicFunctionArr = Array(); + json::Array &PublicFunctionARef = *PublicFunctionArr.getAsArray(); + json::Value ProtectedFunctionArr = Array(); + json::Array &ProtectedFunctionARef = *ProtectedFunctionArr.getAsArray(); + + for (const auto &Function : I.Children.Functions) { + json::Object FunctionObj; + serializeInfo(Function, FunctionObj, RepositoryUrl); + AccessSpecifier Access = Function.Access; + if (Access == AccessSpecifier::AS_public) + PublicFunctionARef.push_back(std::move(FunctionObj)); + else if (Access == AccessSpecifier::AS_protected) + ProtectedFunctionARef.push_back(std::move(FunctionObj)); + } + + if (!PublicFunctionARef.empty()) + Obj["PublicFunctions"] = std::move(PublicFunctionArr); + if (!ProtectedFunctionARef.empty()) + Obj["ProtectedFunctions"] = std::move(ProtectedFunctionArr); + } + + if (!I.Members.empty()) { + json::Value PublicMembers = Array(); + json::Array &PubMemberRef = *PublicMembers.getAsArray(); + json::Value ProtectedMembers = Array(); + json::Array &ProtMemberRef = *ProtectedMembers.getAsArray(); + + for (const MemberTypeInfo &Member : I.Members) { + json::Object MemberObj = Object(); + MemberObj["Name"] = Member.Name; + MemberObj["Type"] = Member.Type.Name; + + if (Member.Access == AccessSpecifier::AS_public) + PubMemberRef.push_back(std::move(MemberObj)); + else if (Member.Access == AccessSpecifier::AS_protected) + ProtMemberRef.push_back(std::move(MemberObj)); + } + + if (!PubMemberRef.empty()) + Obj["PublicMembers"] = std::move(PublicMembers); + if (!ProtMemberRef.empty()) + Obj["ProtectedMembers"] = std::move(ProtectedMembers); + } + + serializeCommonChildren(I.Children, Obj, RepositoryUrl); +} + +Error JSONGenerator::generateDocs( + StringRef RootDir, llvm::StringMap<std::unique_ptr<doc::Info>> Infos, + const ClangDocContext &CDCtx) { + StringSet<> CreatedDirs; + StringMap<std::vector<doc::Info *>> FileToInfos; + for (const auto &Group : Infos) { + 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 Err = sys::fs::create_directories(Path); + Err != std::error_code()) + ExitOnErr(createFileError(Twine(Path), Err)); + CreatedDirs.insert(Path); + } + + sys::path::append(Path, Info->getFileBaseName() + ".json"); + FileToInfos[Path].push_back(Info); + } + + for (const auto &Group : FileToInfos) { + std::error_code FileErr; + raw_fd_ostream InfoOS(Group.getKey(), FileErr, sys::fs::OF_Text); + if (FileErr) + ExitOnErr(createFileError("cannot open file " + Group.getKey(), FileErr)); ---------------- ilovepi wrote:
Same as above, IDK if we should make these exit directly over handling the error in a single place in the tool. https://github.com/llvm/llvm-project/pull/142483 _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits