brintos

brintos / llvm-project-archived public Read only

0
0
Text · 22.8 KiB · 97c599a Raw
675 lines · cpp
1#include "Generators.h"2#include "clang/Basic/Specifiers.h"3#include "llvm/Support/JSON.h"4 5using namespace llvm;6using namespace llvm::json;7 8namespace clang {9namespace doc {10 11class JSONGenerator : public Generator {12public:13  static const char *Format;14 15  Error generateDocumentation(StringRef RootDir,16                              llvm::StringMap<std::unique_ptr<doc::Info>> Infos,17                              const ClangDocContext &CDCtx,18                              std::string DirName) override;19  Error createResources(ClangDocContext &CDCtx) override;20  Error generateDocForInfo(Info *I, llvm::raw_ostream &OS,21                           const ClangDocContext &CDCtx) override;22};23 24const char *JSONGenerator::Format = "json";25 26static void serializeInfo(const ConstraintInfo &I, Object &Obj);27static void serializeInfo(const RecordInfo &I, Object &Obj,28                          const std::optional<StringRef> &RepositoryUrl);29 30static void serializeReference(const Reference &Ref, Object &ReferenceObj);31 32template <typename Container, typename SerializationFunc>33static void serializeArray(const Container &Records, Object &Obj,34                           const std::string &Key,35                           SerializationFunc SerializeInfo);36 37// Convenience lambda to pass to serializeArray.38// If a serializeInfo needs a RepositoryUrl, create a local lambda that captures39// the optional.40static auto SerializeInfoLambda = [](const auto &Info, Object &Object) {41  serializeInfo(Info, Object);42};43static auto SerializeReferenceLambda = [](const auto &Ref, Object &Object) {44  serializeReference(Ref, Object);45};46 47static std::string infoTypeToString(InfoType IT) {48  switch (IT) {49  case InfoType::IT_default:50    return "default";51  case InfoType::IT_namespace:52    return "namespace";53  case InfoType::IT_record:54    return "record";55  case InfoType::IT_function:56    return "function";57  case InfoType::IT_enum:58    return "enum";59  case InfoType::IT_typedef:60    return "typedef";61  case InfoType::IT_concept:62    return "concept";63  case InfoType::IT_variable:64    return "variable";65  case InfoType::IT_friend:66    return "friend";67  }68  llvm_unreachable("Unknown InfoType encountered.");69}70 71static json::Object72serializeLocation(const Location &Loc,73                  const std::optional<StringRef> RepositoryUrl) {74  Object LocationObj = Object();75  LocationObj["LineNumber"] = Loc.StartLineNumber;76  LocationObj["Filename"] = Loc.Filename;77 78  if (!Loc.IsFileInRootDir || !RepositoryUrl)79    return LocationObj;80  SmallString<128> FileURL(*RepositoryUrl);81  sys::path::append(FileURL, sys::path::Style::posix, Loc.Filename);82  FileURL += "#" + std::to_string(Loc.StartLineNumber);83  LocationObj["FileURL"] = FileURL;84  return LocationObj;85}86 87static void insertComment(Object &Description, json::Value &Comment,88                          StringRef Key) {89  auto DescriptionIt = Description.find(Key);90 91  if (DescriptionIt == Description.end()) {92    auto CommentsArray = json::Array();93    CommentsArray.push_back(Comment);94    Description[Key] = std::move(CommentsArray);95    Description["Has" + Key.str()] = true;96  } else {97    DescriptionIt->getSecond().getAsArray()->push_back(Comment);98  }99}100 101static json::Value extractTextComments(Object *ParagraphComment) {102  if (!ParagraphComment)103    return json::Object();104  return *ParagraphComment->get("Children");105}106 107static json::Value extractVerbatimComments(json::Array VerbatimLines) {108  json::Value TextArray = json::Array();109  auto &TextArrayRef = *TextArray.getAsArray();110  for (auto &Line : VerbatimLines)111    TextArrayRef.push_back(*Line.getAsObject()112                                ->get("VerbatimBlockLineComment")113                                ->getAsObject()114                                ->get("Text"));115 116  return TextArray;117}118 119static Object serializeComment(const CommentInfo &I, Object &Description) {120  // taken from PR #142273121  Object Obj = Object();122 123  json::Value ChildVal = Object();124  Object &Child = *ChildVal.getAsObject();125 126  json::Value ChildArr = Array();127  auto &CARef = *ChildArr.getAsArray();128  CARef.reserve(I.Children.size());129  for (const auto &C : I.Children)130    CARef.emplace_back(serializeComment(*C, Description));131 132  switch (I.Kind) {133  case CommentKind::CK_TextComment: {134    Obj.insert({commentKindToString(I.Kind), I.Text});135    return Obj;136  }137 138  case CommentKind::CK_BlockCommandComment: {139    auto TextCommentsArray = extractTextComments(CARef.front().getAsObject());140    if (I.Name == "brief")141      insertComment(Description, TextCommentsArray, "BriefComments");142    else if (I.Name == "return")143      insertComment(Description, TextCommentsArray, "ReturnComments");144    else if (I.Name == "throws" || I.Name == "throw") {145      json::Value ThrowsVal = Object();146      auto &ThrowsObj = *ThrowsVal.getAsObject();147      ThrowsObj["Exception"] = I.Args.front();148      ThrowsObj["Children"] = TextCommentsArray;149      insertComment(Description, ThrowsVal, "ThrowsComments");150    }151    return Obj;152  }153 154  case CommentKind::CK_InlineCommandComment: {155    json::Value ArgsArr = Array();156    auto &ARef = *ArgsArr.getAsArray();157    ARef.reserve(I.Args.size());158    for (const auto &Arg : I.Args)159      ARef.emplace_back(Arg);160    Child.insert({"Command", I.Name});161    Child.insert({"Args", ArgsArr});162    Child.insert({"Children", ChildArr});163    Obj.insert({commentKindToString(I.Kind), ChildVal});164    return Obj;165  }166 167  case CommentKind::CK_ParamCommandComment:168  case CommentKind::CK_TParamCommandComment: {169    Child.insert({"ParamName", I.ParamName});170    Child.insert({"Direction", I.Direction});171    Child.insert({"Explicit", I.Explicit});172    auto TextCommentsArray = extractTextComments(CARef.front().getAsObject());173    Child.insert({"Children", TextCommentsArray});174    if (I.Kind == CommentKind::CK_ParamCommandComment)175      insertComment(Description, ChildVal, "ParamComments");176    return Obj;177  }178 179  case CommentKind::CK_VerbatimBlockComment: {180    if (I.CloseName == "endcode") {181      // We don't support \code language specification182      auto TextCommentsArray = extractVerbatimComments(CARef);183      insertComment(Description, TextCommentsArray, "CodeComments");184    } else if (I.CloseName == "endverbatim")185      insertComment(Description, ChildVal, "VerbatimComments");186    return Obj;187  }188 189  case CommentKind::CK_VerbatimBlockLineComment:190  case CommentKind::CK_VerbatimLineComment: {191    Child.insert({"Text", I.Text});192    Child.insert({"Children", ChildArr});193    Obj.insert({commentKindToString(I.Kind), ChildVal});194    return Obj;195  }196 197  case CommentKind::CK_HTMLStartTagComment: {198    json::Value AttrKeysArray = json::Array();199    json::Value AttrValuesArray = json::Array();200    auto &KeyArr = *AttrKeysArray.getAsArray();201    auto &ValArr = *AttrValuesArray.getAsArray();202    KeyArr.reserve(I.AttrKeys.size());203    ValArr.reserve(I.AttrValues.size());204    for (const auto &K : I.AttrKeys)205      KeyArr.emplace_back(K);206    for (const auto &V : I.AttrValues)207      ValArr.emplace_back(V);208    Child.insert({"Name", I.Name});209    Child.insert({"SelfClosing", I.SelfClosing});210    Child.insert({"AttrKeys", AttrKeysArray});211    Child.insert({"AttrValues", AttrValuesArray});212    Child.insert({"Children", ChildArr});213    Obj.insert({commentKindToString(I.Kind), ChildVal});214    return Obj;215  }216 217  case CommentKind::CK_HTMLEndTagComment: {218    Child.insert({"Name", I.Name});219    Child.insert({"Children", ChildArr});220    Obj.insert({commentKindToString(I.Kind), ChildVal});221    return Obj;222  }223 224  case CommentKind::CK_FullComment:225  case CommentKind::CK_ParagraphComment: {226    Child.insert({"Children", ChildArr});227    Child["ParagraphComment"] = true;228    return Child;229  }230 231  case CommentKind::CK_Unknown: {232    Obj.insert({commentKindToString(I.Kind), I.Text});233    return Obj;234  }235  }236  llvm_unreachable("Unknown comment kind encountered.");237}238 239static void240serializeCommonAttributes(const Info &I, json::Object &Obj,241                          const std::optional<StringRef> RepositoryUrl) {242  Obj["Name"] = I.Name;243  Obj["USR"] = toHex(toStringRef(I.USR));244  Obj["InfoType"] = infoTypeToString(I.IT);245  if (!I.DocumentationFileName.empty())246    Obj["DocumentationFileName"] = I.DocumentationFileName;247 248  if (!I.Path.empty())249    Obj["Path"] = I.Path;250 251  if (!I.Namespace.empty()) {252    Obj["Namespace"] = json::Array();253    for (const auto &NS : I.Namespace)254      Obj["Namespace"].getAsArray()->push_back(NS.Name);255  }256 257  if (!I.Description.empty()) {258    Object Description = Object();259    // Skip straight to the FullComment's children260    auto &Comments = I.Description.at(0).Children;261    for (const auto &CommentInfo : Comments) {262      json::Value Comment = serializeComment(*CommentInfo, Description);263      // if a ParagraphComment is returned, then it is a top-level comment that264      // needs to be inserted manually.265      if (auto *ParagraphComment = Comment.getAsObject();266          ParagraphComment->get("ParagraphComment")) {267        auto TextCommentsArray = extractTextComments(ParagraphComment);268        insertComment(Description, TextCommentsArray, "ParagraphComments");269      }270    }271    Obj["Description"] = std::move(Description);272  }273 274  // Namespaces aren't SymbolInfos, so they dont have a DefLoc275  if (I.IT != InfoType::IT_namespace) {276    const auto *Symbol = static_cast<const SymbolInfo *>(&I);277    if (Symbol->DefLoc)278      Obj["Location"] =279          serializeLocation(Symbol->DefLoc.value(), RepositoryUrl);280  }281}282 283static void serializeReference(const Reference &Ref, Object &ReferenceObj) {284  ReferenceObj["Path"] = Ref.Path;285  ReferenceObj["Name"] = Ref.Name;286  ReferenceObj["QualName"] = Ref.QualName;287  ReferenceObj["USR"] = toHex(toStringRef(Ref.USR));288  if (!Ref.DocumentationFileName.empty())289    ReferenceObj["DocumentationFileName"] = Ref.DocumentationFileName;290}291 292// Although namespaces and records both have ScopeChildren, they serialize them293// differently. Only enums, records, and typedefs are handled here.294static void295serializeCommonChildren(const ScopeChildren &Children, json::Object &Obj,296                        const std::optional<StringRef> RepositoryUrl) {297  static auto SerializeInfo = [RepositoryUrl](const auto &Info,298                                              Object &Object) {299    serializeInfo(Info, Object, RepositoryUrl);300  };301 302  if (!Children.Enums.empty()) {303    serializeArray(Children.Enums, Obj, "Enums", SerializeInfo);304    Obj["HasEnums"] = true;305  }306 307  if (!Children.Typedefs.empty())308    serializeArray(Children.Typedefs, Obj, "Typedefs", SerializeInfo);309 310  if (!Children.Records.empty()) {311    serializeArray(Children.Records, Obj, "Records", SerializeReferenceLambda);312    Obj["HasRecords"] = true;313  }314}315 316template <typename Container, typename SerializationFunc>317static void serializeArray(const Container &Records, Object &Obj,318                           const std::string &Key,319                           SerializationFunc SerializeInfo) {320  json::Value RecordsArray = Array();321  auto &RecordsArrayRef = *RecordsArray.getAsArray();322  RecordsArrayRef.reserve(Records.size());323  for (size_t Index = 0; Index < Records.size(); ++Index) {324    json::Value ItemVal = Object();325    auto &ItemObj = *ItemVal.getAsObject();326    SerializeInfo(Records[Index], ItemObj);327    if (Index == Records.size() - 1)328      ItemObj["End"] = true;329    RecordsArrayRef.push_back(ItemVal);330  }331  Obj[Key] = RecordsArray;332}333 334static void serializeInfo(const ConstraintInfo &I, Object &Obj) {335  serializeReference(I.ConceptRef, Obj);336  Obj["Expression"] = I.ConstraintExpr;337}338 339static void serializeInfo(const ArrayRef<TemplateParamInfo> &Params,340                          Object &Obj) {341  json::Value ParamsArray = Array();342  auto &ParamsArrayRef = *ParamsArray.getAsArray();343  ParamsArrayRef.reserve(Params.size());344  for (const auto &Param : Params)345    ParamsArrayRef.push_back(Param.Contents);346  Obj["Parameters"] = ParamsArray;347}348 349static void serializeInfo(const TemplateInfo &Template, Object &Obj) {350  json::Value TemplateVal = Object();351  auto &TemplateObj = *TemplateVal.getAsObject();352 353  if (Template.Specialization) {354    json::Value TemplateSpecializationVal = Object();355    auto &TemplateSpecializationObj = *TemplateSpecializationVal.getAsObject();356    TemplateSpecializationObj["SpecializationOf"] =357        toHex(toStringRef(Template.Specialization->SpecializationOf));358    if (!Template.Specialization->Params.empty())359      serializeInfo(Template.Specialization->Params, TemplateSpecializationObj);360    TemplateObj["Specialization"] = TemplateSpecializationVal;361  }362 363  if (!Template.Params.empty())364    serializeInfo(Template.Params, TemplateObj);365 366  if (!Template.Constraints.empty())367    serializeArray(Template.Constraints, TemplateObj, "Constraints",368                   SerializeInfoLambda);369 370  Obj["Template"] = TemplateVal;371}372 373static void serializeInfo(const ConceptInfo &I, Object &Obj,374                          const std::optional<StringRef> &RepositoryUrl) {375  serializeCommonAttributes(I, Obj, RepositoryUrl);376  Obj["IsType"] = I.IsType;377  Obj["ConstraintExpression"] = I.ConstraintExpression;378  serializeInfo(I.Template, Obj);379}380 381static void serializeInfo(const TypeInfo &I, Object &Obj) {382  Obj["Name"] = I.Type.Name;383  Obj["QualName"] = I.Type.QualName;384  Obj["USR"] = toHex(toStringRef(I.Type.USR));385  Obj["IsTemplate"] = I.IsTemplate;386  Obj["IsBuiltIn"] = I.IsBuiltIn;387}388 389static void serializeInfo(const FieldTypeInfo &I, Object &Obj) {390  Obj["Name"] = I.Name;391  Obj["Type"] = I.Type.Name;392}393 394static void serializeInfo(const FunctionInfo &F, json::Object &Obj,395                          const std::optional<StringRef> RepositoryURL) {396  serializeCommonAttributes(F, Obj, RepositoryURL);397  Obj["IsStatic"] = F.IsStatic;398 399  auto ReturnTypeObj = Object();400  serializeInfo(F.ReturnType, ReturnTypeObj);401  Obj["ReturnType"] = std::move(ReturnTypeObj);402 403  if (!F.Params.empty())404    serializeArray(F.Params, Obj, "Params", SerializeInfoLambda);405 406  if (F.Template)407    serializeInfo(F.Template.value(), Obj);408}409 410static void serializeInfo(const EnumValueInfo &I, Object &Obj) {411  Obj["Name"] = I.Name;412  if (!I.ValueExpr.empty())413    Obj["ValueExpr"] = I.ValueExpr;414  else415    Obj["Value"] = I.Value;416}417 418static void serializeInfo(const EnumInfo &I, json::Object &Obj,419                          const std::optional<StringRef> &RepositoryUrl) {420  serializeCommonAttributes(I, Obj, RepositoryUrl);421  Obj["Scoped"] = I.Scoped;422 423  if (I.BaseType) {424    json::Value BaseTypeVal = Object();425    auto &BaseTypeObj = *BaseTypeVal.getAsObject();426    BaseTypeObj["Name"] = I.BaseType->Type.Name;427    BaseTypeObj["QualName"] = I.BaseType->Type.QualName;428    BaseTypeObj["USR"] = toHex(toStringRef(I.BaseType->Type.USR));429    Obj["BaseType"] = BaseTypeVal;430  }431 432  if (!I.Members.empty())433    serializeArray(I.Members, Obj, "Members", SerializeInfoLambda);434}435 436static void serializeInfo(const TypedefInfo &I, json::Object &Obj,437                          const std::optional<StringRef> &RepositoryUrl) {438  serializeCommonAttributes(I, Obj, RepositoryUrl);439  Obj["TypeDeclaration"] = I.TypeDeclaration;440  Obj["IsUsing"] = I.IsUsing;441  json::Value TypeVal = Object();442  auto &TypeObj = *TypeVal.getAsObject();443  serializeInfo(I.Underlying, TypeObj);444  Obj["Underlying"] = TypeVal;445}446 447static void serializeInfo(const BaseRecordInfo &I, Object &Obj,448                          const std::optional<StringRef> &RepositoryUrl) {449  serializeInfo(static_cast<const RecordInfo &>(I), Obj, RepositoryUrl);450  Obj["IsVirtual"] = I.IsVirtual;451  Obj["Access"] = getAccessSpelling(I.Access);452  Obj["IsParent"] = I.IsParent;453}454 455static void serializeInfo(const FriendInfo &I, Object &Obj) {456  auto FriendRef = Object();457  serializeReference(I.Ref, FriendRef);458  Obj["Reference"] = std::move(FriendRef);459  Obj["IsClass"] = I.IsClass;460  if (I.Template)461    serializeInfo(I.Template.value(), Obj);462  if (I.Params)463    serializeArray(I.Params.value(), Obj, "Params", SerializeInfoLambda);464  if (I.ReturnType) {465    auto ReturnTypeObj = Object();466    serializeInfo(I.ReturnType.value(), ReturnTypeObj);467    Obj["ReturnType"] = std::move(ReturnTypeObj);468  }469}470 471static void insertArray(Object &Obj, json::Value &Array, StringRef Key) {472  Obj[Key] = Array;473  Obj["Has" + Key.str()] = true;474}475 476static void serializeInfo(const RecordInfo &I, json::Object &Obj,477                          const std::optional<StringRef> &RepositoryUrl) {478  serializeCommonAttributes(I, Obj, RepositoryUrl);479  Obj["TagType"] = getTagType(I.TagType);480  Obj["IsTypedef"] = I.IsTypeDef;481  Obj["MangledName"] = I.MangledName;482 483  if (!I.Children.Functions.empty()) {484    json::Value PubFunctionsArray = Array();485    json::Array &PubFunctionsArrayRef = *PubFunctionsArray.getAsArray();486    json::Value ProtFunctionsArray = Array();487    json::Array &ProtFunctionsArrayRef = *ProtFunctionsArray.getAsArray();488 489    for (const auto &Function : I.Children.Functions) {490      json::Value FunctionVal = Object();491      auto &FunctionObj = *FunctionVal.getAsObject();492      serializeInfo(Function, FunctionObj, RepositoryUrl);493      AccessSpecifier Access = Function.Access;494      if (Access == AccessSpecifier::AS_public)495        PubFunctionsArrayRef.push_back(FunctionVal);496      else if (Access == AccessSpecifier::AS_protected)497        ProtFunctionsArrayRef.push_back(FunctionVal);498    }499 500    if (!PubFunctionsArrayRef.empty())501      insertArray(Obj, PubFunctionsArray, "PublicFunctions");502    if (!ProtFunctionsArrayRef.empty())503      Obj["ProtectedFunctions"] = ProtFunctionsArray;504  }505 506  if (!I.Members.empty()) {507    json::Value PublicMembersArray = Array();508    json::Array &PubMembersArrayRef = *PublicMembersArray.getAsArray();509    json::Value ProtectedMembersArray = Array();510    json::Array &ProtMembersArrayRef = *ProtectedMembersArray.getAsArray();511 512    for (const MemberTypeInfo &Member : I.Members) {513      json::Value MemberVal = Object();514      auto &MemberObj = *MemberVal.getAsObject();515      MemberObj["Name"] = Member.Name;516      MemberObj["Type"] = Member.Type.Name;517 518      if (Member.Access == AccessSpecifier::AS_public)519        PubMembersArrayRef.push_back(MemberVal);520      else if (Member.Access == AccessSpecifier::AS_protected)521        ProtMembersArrayRef.push_back(MemberVal);522    }523 524    if (!PubMembersArrayRef.empty())525      insertArray(Obj, PublicMembersArray, "PublicMembers");526    if (!ProtMembersArrayRef.empty())527      Obj["ProtectedMembers"] = ProtectedMembersArray;528  }529 530  if (!I.Bases.empty())531    serializeArray(532        I.Bases, Obj, "Bases",533        [&RepositoryUrl](const BaseRecordInfo &Base, Object &BaseObj) {534          serializeInfo(Base, BaseObj, RepositoryUrl);535        });536 537  if (!I.Parents.empty())538    serializeArray(I.Parents, Obj, "Parents", SerializeReferenceLambda);539 540  if (!I.VirtualParents.empty())541    serializeArray(I.VirtualParents, Obj, "VirtualParents",542                   SerializeReferenceLambda);543 544  if (I.Template)545    serializeInfo(I.Template.value(), Obj);546 547  if (!I.Friends.empty())548    serializeArray(I.Friends, Obj, "Friends", SerializeInfoLambda);549 550  serializeCommonChildren(I.Children, Obj, RepositoryUrl);551}552 553static void serializeInfo(const VarInfo &I, json::Object &Obj,554                          const std::optional<StringRef> RepositoryUrl) {555  serializeCommonAttributes(I, Obj, RepositoryUrl);556  Obj["IsStatic"] = I.IsStatic;557  auto TypeObj = Object();558  serializeInfo(I.Type, TypeObj);559  Obj["Type"] = std::move(TypeObj);560}561 562static void serializeInfo(const NamespaceInfo &I, json::Object &Obj,563                          const std::optional<StringRef> RepositoryUrl) {564  serializeCommonAttributes(I, Obj, RepositoryUrl);565 566  if (!I.Children.Namespaces.empty())567    serializeArray(I.Children.Namespaces, Obj, "Namespaces",568                   SerializeReferenceLambda);569 570  static auto SerializeInfo = [RepositoryUrl](const auto &Info,571                                              Object &Object) {572    serializeInfo(Info, Object, RepositoryUrl);573  };574 575  if (!I.Children.Functions.empty())576    serializeArray(I.Children.Functions, Obj, "Functions", SerializeInfo);577 578  if (!I.Children.Concepts.empty())579    serializeArray(I.Children.Concepts, Obj, "Concepts", SerializeInfo);580 581  if (!I.Children.Variables.empty())582    serializeArray(I.Children.Variables, Obj, "Variables", SerializeInfo);583 584  serializeCommonChildren(I.Children, Obj, RepositoryUrl);585}586 587static SmallString<16> determineFileName(Info *I, SmallString<128> &Path) {588  SmallString<16> FileName;589  if (I->IT == InfoType::IT_record) {590    auto *RecordSymbolInfo = static_cast<SymbolInfo *>(I);591    FileName = RecordSymbolInfo->MangledName;592  } else if (I->IT == InfoType::IT_namespace) {593    FileName = "index";594  } else595    FileName = I->Name;596  sys::path::append(Path, FileName + ".json");597  return FileName;598}599 600Error JSONGenerator::generateDocumentation(601    StringRef RootDir, llvm::StringMap<std::unique_ptr<doc::Info>> Infos,602    const ClangDocContext &CDCtx, std::string DirName) {603  StringSet<> CreatedDirs;604  StringMap<std::vector<doc::Info *>> FileToInfos;605  for (const auto &Group : Infos) {606    Info *Info = Group.getValue().get();607 608    SmallString<128> Path;609    auto RootDirStr = RootDir.str() + "/json";610    StringRef JSONDir = StringRef(RootDirStr);611    sys::path::native(JSONDir, Path);612    sys::path::append(Path, Info->getRelativeFilePath(""));613    if (!CreatedDirs.contains(Path)) {614      if (std::error_code Err = sys::fs::create_directories(Path);615          Err != std::error_code())616        return createFileError(Twine(Path), Err);617      CreatedDirs.insert(Path);618    }619 620    SmallString<16> FileName = determineFileName(Info, Path);621    if (FileToInfos.contains(Path))622      continue;623    FileToInfos[Path].push_back(Info);624    Info->DocumentationFileName = FileName;625  }626 627  for (const auto &Group : FileToInfos) {628    std::error_code FileErr;629    raw_fd_ostream InfoOS(Group.getKey(), FileErr, sys::fs::OF_Text);630    if (FileErr)631      return createFileError("cannot open file " + Group.getKey(), FileErr);632 633    for (const auto &Info : Group.getValue())634      if (Error Err = generateDocForInfo(Info, InfoOS, CDCtx))635        return Err;636  }637 638  return Error::success();639}640 641Error JSONGenerator::generateDocForInfo(Info *I, raw_ostream &OS,642                                        const ClangDocContext &CDCtx) {643  json::Object Obj = Object();644 645  switch (I->IT) {646  case InfoType::IT_namespace:647    serializeInfo(*static_cast<NamespaceInfo *>(I), Obj, CDCtx.RepositoryUrl);648    break;649  case InfoType::IT_record:650    serializeInfo(*static_cast<RecordInfo *>(I), Obj, CDCtx.RepositoryUrl);651    break;652  case InfoType::IT_concept:653  case InfoType::IT_enum:654  case InfoType::IT_function:655  case InfoType::IT_typedef:656  case InfoType::IT_variable:657  case InfoType::IT_friend:658    break;659  case InfoType::IT_default:660    return createStringError(inconvertibleErrorCode(), "unexpected info type");661  }662  OS << llvm::formatv("{0:2}", llvm::json::Value(std::move(Obj)));663  return Error::success();664}665 666Error JSONGenerator::createResources(ClangDocContext &CDCtx) {667  return Error::success();668}669 670static GeneratorRegistry::Add<JSONGenerator> JSON(JSONGenerator::Format,671                                                  "Generator for JSON output.");672volatile int JSONGeneratorAnchorSource = 0;673} // namespace doc674} // namespace clang675