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