clang-tools 22.0.0git
JSONGenerator.cpp
Go to the documentation of this file.
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 captures
39// 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 void insertNonEmpty(StringRef Key, StringRef Value, Object &Obj) {
48 if (!Value.empty())
49 Obj[Key] = Value;
50}
51
52static std::string infoTypeToString(InfoType IT) {
53 switch (IT) {
55 return "default";
57 return "namespace";
59 return "record";
61 return "function";
63 return "enum";
65 return "typedef";
67 return "concept";
69 return "variable";
71 return "friend";
72 }
73 llvm_unreachable("Unknown InfoType encountered.");
74}
75
76static json::Object
78 const std::optional<StringRef> RepositoryUrl) {
79 Object LocationObj = Object();
80 LocationObj["LineNumber"] = Loc.StartLineNumber;
81 LocationObj["Filename"] = Loc.Filename;
82
83 if (!Loc.IsFileInRootDir || !RepositoryUrl)
84 return LocationObj;
85 SmallString<128> FileURL(*RepositoryUrl);
86 sys::path::append(FileURL, sys::path::Style::posix, Loc.Filename);
87 FileURL += "#" + std::to_string(Loc.StartLineNumber);
88 LocationObj["FileURL"] = FileURL;
89 return LocationObj;
90}
91
92/// Insert comments into a key in the Description object.
93///
94/// \param Comment Either an Object or Array, depending on the comment type
95/// \param Key The type (Brief, Code, etc.) of comment to be inserted
96static void insertComment(Object &Description, json::Value &Comment,
97 StringRef Key) {
98 // The comment has a Children array for the actual text, with meta attributes
99 // alongside it in the Object.
100 if (auto *Obj = Comment.getAsObject()) {
101 if (auto *Children = Obj->getArray("Children");
102 Children && Children->empty())
103 return;
104 }
105 // The comment is just an array of text comments.
106 else if (auto *Array = Comment.getAsArray(); Array && Array->empty()) {
107 return;
108 }
109
110 auto DescriptionIt = Description.find(Key);
111
112 if (DescriptionIt == Description.end()) {
113 auto CommentsArray = json::Array();
114 CommentsArray.push_back(Comment);
115 Description[Key] = std::move(CommentsArray);
116 Description["Has" + Key.str()] = true;
117 } else {
118 DescriptionIt->getSecond().getAsArray()->push_back(Comment);
119 }
120}
121
122/// Takes the nested "Children" array from a comment Object.
123///
124/// \return a json::Array of comments, possible json::Value::Kind::Null
125static json::Value extractTextComments(Object *ParagraphComment) {
126 if (!ParagraphComment)
127 return json::Value(nullptr);
128 json::Value *Children = ParagraphComment->get("Children");
129 if (!Children)
130 return json::Value(nullptr);
131 auto ChildrenArray = *Children->getAsArray();
132 auto ChildrenIt = ChildrenArray.begin();
133 while (ChildrenIt != ChildrenArray.end()) {
134 auto *ChildObj = ChildrenIt->getAsObject();
135 assert(ChildObj && "Invalid JSON object in Comment");
136 auto TextComment = ChildObj->getString("TextComment");
137 if (!TextComment || TextComment->empty()) {
138 ChildrenIt = ChildrenArray.erase(ChildrenIt);
139 continue;
140 }
141 ++ChildrenIt;
142 }
143 return ChildrenArray;
144}
145
146static json::Value extractVerbatimComments(json::Array VerbatimLines) {
147 json::Value TextArray = json::Array();
148 auto &TextArrayRef = *TextArray.getAsArray();
149 for (auto &Line : VerbatimLines)
150 TextArrayRef.push_back(*Line.getAsObject()
151 ->get("VerbatimBlockLineComment")
152 ->getAsObject()
153 ->get("Text"));
154
155 return TextArray;
156}
157
158static Object serializeComment(const CommentInfo &I, Object &Description) {
159 // taken from PR #142273
160 Object Obj = Object();
161
162 json::Value ChildVal = Object();
163 Object &Child = *ChildVal.getAsObject();
164
165 json::Value ChildArr = Array();
166 auto &CARef = *ChildArr.getAsArray();
167 CARef.reserve(I.Children.size());
168 for (const auto &C : I.Children)
169 CARef.emplace_back(serializeComment(*C, Description));
170
171 switch (I.Kind) {
173 if (!I.Text.empty())
174 Obj.insert({commentKindToString(I.Kind), I.Text});
175 return Obj;
176 }
177
179 auto TextCommentsArray = extractTextComments(CARef.front().getAsObject());
180 if (I.Name == "brief")
181 insertComment(Description, TextCommentsArray, "BriefComments");
182 else if (I.Name == "return")
183 insertComment(Description, TextCommentsArray, "ReturnComments");
184 else if (I.Name == "throws" || I.Name == "throw") {
185 json::Value ThrowsVal = Object();
186 auto &ThrowsObj = *ThrowsVal.getAsObject();
187 ThrowsObj["Exception"] = I.Args.front();
188 ThrowsObj["Children"] = TextCommentsArray;
189 insertComment(Description, ThrowsVal, "ThrowsComments");
190 }
191 return Obj;
192 }
193
195 json::Value ArgsArr = Array();
196 auto &ARef = *ArgsArr.getAsArray();
197 ARef.reserve(I.Args.size());
198 for (const auto &Arg : I.Args)
199 ARef.emplace_back(Arg);
200 Child.insert({"Command", I.Name});
201 Child.insert({"Args", ArgsArr});
202 Child.insert({"Children", ChildArr});
203 Obj.insert({commentKindToString(I.Kind), ChildVal});
204 return Obj;
205 }
206
209 Child.insert({"ParamName", I.ParamName});
210 Child.insert({"Direction", I.Direction});
211 Child.insert({"Explicit", I.Explicit});
212 auto TextCommentsArray = extractTextComments(CARef.front().getAsObject());
213 Child.insert({"Children", TextCommentsArray});
215 insertComment(Description, ChildVal, "ParamComments");
217 insertComment(Description, ChildVal, "TParamComments");
218 return Obj;
219 }
220
222 if (I.CloseName == "endcode") {
223 // We don't support \code language specification
224 auto TextCommentsArray = extractVerbatimComments(CARef);
225 insertComment(Description, TextCommentsArray, "CodeComments");
226 } else if (I.CloseName == "endverbatim")
227 insertComment(Description, ChildVal, "VerbatimComments");
228 return Obj;
229 }
230
233 Child.insert({"Text", I.Text});
234 Child.insert({"Children", ChildArr});
235 Obj.insert({commentKindToString(I.Kind), ChildVal});
236 return Obj;
237 }
238
240 json::Value AttrKeysArray = json::Array();
241 json::Value AttrValuesArray = json::Array();
242 auto &KeyArr = *AttrKeysArray.getAsArray();
243 auto &ValArr = *AttrValuesArray.getAsArray();
244 KeyArr.reserve(I.AttrKeys.size());
245 ValArr.reserve(I.AttrValues.size());
246 for (const auto &K : I.AttrKeys)
247 KeyArr.emplace_back(K);
248 for (const auto &V : I.AttrValues)
249 ValArr.emplace_back(V);
250 Child.insert({"Name", I.Name});
251 Child.insert({"SelfClosing", I.SelfClosing});
252 Child.insert({"AttrKeys", AttrKeysArray});
253 Child.insert({"AttrValues", AttrValuesArray});
254 Child.insert({"Children", ChildArr});
255 Obj.insert({commentKindToString(I.Kind), ChildVal});
256 return Obj;
257 }
258
260 Child.insert({"Name", I.Name});
261 Child.insert({"Children", ChildArr});
262 Obj.insert({commentKindToString(I.Kind), ChildVal});
263 return Obj;
264 }
265
268 Child.insert({"Children", ChildArr});
269 Child["ParagraphComment"] = true;
270 return Child;
271 }
272
274 Obj.insert({commentKindToString(I.Kind), I.Text});
275 return Obj;
276 }
277 }
278 llvm_unreachable("Unknown comment kind encountered.");
279}
280
281/// Creates Contexts for namespaces and records to allow for navigation.
282static void generateContext(const Info &I, Object &Obj) {
283 json::Value ContextArray = json::Array();
284 auto &ContextArrayRef = *ContextArray.getAsArray();
285 ContextArrayRef.reserve(I.Contexts.size());
286
287 std::string CurrentRelativePath;
288 bool PreviousRecord = false;
289 for (const auto &Current : I.Contexts) {
290 json::Value ContextVal = Object();
291 Object &Context = *ContextVal.getAsObject();
292 serializeReference(Current, Context);
293
294 if (ContextArrayRef.empty() && I.IT == InfoType::IT_record) {
295 if (Current.DocumentationFileName == "index") {
296 // If the record's immediate context is a namespace, then the
297 // "index.html" is in the same directory.
298 PreviousRecord = false;
299 Context["RelativePath"] = "./";
300 } else {
301 // If the immediate context is a record, then the file is one level
302 // above
303 PreviousRecord = true;
304 CurrentRelativePath += "../";
305 Context["RelativePath"] = CurrentRelativePath;
306 }
307 ContextArrayRef.push_back(ContextVal);
308 continue;
309 }
310
311 if (PreviousRecord && (Current.DocumentationFileName == "index")) {
312 // If the previous Context was a record then we already went up a level,
313 // so the current namespace index is in the same directory.
314 PreviousRecord = false;
315 } else if (Current.DocumentationFileName != "index") {
316 // If the current Context is a record but the previous wasn't a record,
317 // then the namespace index is located one level above.
318 PreviousRecord = true;
319 CurrentRelativePath += "../";
320 } else {
321 // The current Context is a namespace and so was the previous Context.
322 PreviousRecord = false;
323 CurrentRelativePath += "../";
324 // If this namespace is the global namespace, then its documentation
325 // name needs to be changed to link correctly.
326 if (Current.QualName == "GlobalNamespace" && Current.RelativePath != "./")
327 Context["DocumentationFileName"] =
328 SmallString<16>("GlobalNamespace/index");
329 }
330 Context["RelativePath"] = CurrentRelativePath;
331 ContextArrayRef.insert(ContextArrayRef.begin(), ContextVal);
332 }
333
334 ContextArrayRef.back().getAsObject()->insert({"End", true});
335 Obj["Contexts"] = ContextArray;
336 Obj["HasContexts"] = true;
337}
338
339static void
340serializeCommonAttributes(const Info &I, json::Object &Obj,
341 const std::optional<StringRef> RepositoryUrl) {
342 insertNonEmpty("Name", I.Name, Obj);
343 Obj["USR"] = toHex(toStringRef(I.USR));
344 Obj["InfoType"] = infoTypeToString(I.IT);
345 // Conditionally insert fields.
346 // Empty properties are omitted because Mustache templates use existence
347 // to conditionally render content.
348 insertNonEmpty("DocumentationFileName", I.DocumentationFileName, Obj);
349 insertNonEmpty("Path", I.Path, Obj);
350
351 if (!I.Namespace.empty()) {
352 Obj["Namespace"] = json::Array();
353 for (const auto &NS : I.Namespace)
354 Obj["Namespace"].getAsArray()->push_back(NS.Name);
355 }
356
357 if (!I.Description.empty()) {
358 Object Description = Object();
359 // Skip straight to the FullComment's children
360 auto &Comments = I.Description.at(0).Children;
361 for (const auto &CommentInfo : Comments) {
362 json::Value Comment = serializeComment(*CommentInfo, Description);
363 // if a ParagraphComment is returned, then it is a top-level comment that
364 // needs to be inserted manually.
365 if (auto *ParagraphComment = Comment.getAsObject();
366 ParagraphComment->get("ParagraphComment")) {
367 auto TextCommentsArray = extractTextComments(ParagraphComment);
368 if (TextCommentsArray.kind() == json::Value::Null ||
369 TextCommentsArray.getAsArray()->empty())
370 continue;
371 insertComment(Description, TextCommentsArray, "ParagraphComments");
372 }
373 }
374 Obj["Description"] = std::move(Description);
375 }
376
377 // Namespaces aren't SymbolInfos, so they dont have a DefLoc
378 if (I.IT != InfoType::IT_namespace) {
379 const auto *Symbol = static_cast<const SymbolInfo *>(&I);
380 if (Symbol->DefLoc)
381 Obj["Location"] =
382 serializeLocation(Symbol->DefLoc.value(), RepositoryUrl);
383 }
384
385 if (!I.Contexts.empty())
386 generateContext(I, Obj);
387}
388
389static void serializeReference(const Reference &Ref, Object &ReferenceObj) {
390 insertNonEmpty("Path", Ref.Path, ReferenceObj);
391 ReferenceObj["Name"] = Ref.Name;
392 ReferenceObj["QualName"] = Ref.QualName;
393 ReferenceObj["USR"] = toHex(toStringRef(Ref.USR));
394 if (!Ref.DocumentationFileName.empty()) {
395 ReferenceObj["DocumentationFileName"] = Ref.DocumentationFileName;
396
397 // If the reference is a nested class it will be put into a folder named
398 // after the parent class. We can get that name from the path's stem.
399 if (Ref.Path != "GlobalNamespace" && !Ref.Path.empty())
400 ReferenceObj["PathStem"] = sys::path::stem(Ref.Path);
401 }
402}
403
404// Although namespaces and records both have ScopeChildren, they serialize them
405// differently. Only enums, records, and typedefs are handled here.
406static void
407serializeCommonChildren(const ScopeChildren &Children, json::Object &Obj,
408 const std::optional<StringRef> RepositoryUrl) {
409 static auto SerializeInfo = [RepositoryUrl](const auto &Info,
410 Object &Object) {
412 };
413
414 if (!Children.Enums.empty()) {
415 serializeArray(Children.Enums, Obj, "Enums", SerializeInfo);
416 Obj["HasEnums"] = true;
417 }
418
419 if (!Children.Typedefs.empty()) {
420 serializeArray(Children.Typedefs, Obj, "Typedefs", SerializeInfo);
421 Obj["HasTypedefs"] = true;
422 }
423
424 if (!Children.Records.empty()) {
425 serializeArray(Children.Records, Obj, "Records", SerializeReferenceLambda);
426 Obj["HasRecords"] = true;
427 }
428}
429
430template <typename Container, typename SerializationFunc>
431static void serializeArray(const Container &Records, Object &Obj,
432 const std::string &Key,
433 SerializationFunc SerializeInfo) {
434 json::Value RecordsArray = Array();
435 auto &RecordsArrayRef = *RecordsArray.getAsArray();
436 RecordsArrayRef.reserve(Records.size());
437 for (size_t Index = 0; Index < Records.size(); ++Index) {
438 json::Value ItemVal = Object();
439 auto &ItemObj = *ItemVal.getAsObject();
440 SerializeInfo(Records[Index], ItemObj);
441 if (Index == Records.size() - 1)
442 ItemObj["End"] = true;
443 RecordsArrayRef.push_back(ItemVal);
444 }
445 Obj[Key] = RecordsArray;
446}
447
448static void serializeInfo(const ConstraintInfo &I, Object &Obj) {
450 Obj["Expression"] = I.ConstraintExpr;
451}
452
453static void serializeInfo(const ArrayRef<TemplateParamInfo> &Params,
454 Object &Obj) {
455 json::Value ParamsArray = Array();
456 auto &ParamsArrayRef = *ParamsArray.getAsArray();
457 ParamsArrayRef.reserve(Params.size());
458 for (size_t Idx = 0; Idx < Params.size(); ++Idx) {
459 json::Value ParamObjVal = Object();
460 Object &ParamObj = *ParamObjVal.getAsObject();
461
462 ParamObj["Param"] = Params[Idx].Contents;
463 if (Idx == Params.size() - 1)
464 ParamObj["End"] = true;
465 ParamsArrayRef.push_back(ParamObjVal);
466 }
467 Obj["Parameters"] = ParamsArray;
468}
469
470static void serializeInfo(const TemplateInfo &Template, Object &Obj) {
471 json::Value TemplateVal = Object();
472 auto &TemplateObj = *TemplateVal.getAsObject();
473
474 if (Template.Specialization) {
475 json::Value TemplateSpecializationVal = Object();
476 auto &TemplateSpecializationObj = *TemplateSpecializationVal.getAsObject();
477 TemplateSpecializationObj["SpecializationOf"] =
478 toHex(toStringRef(Template.Specialization->SpecializationOf));
479 if (!Template.Specialization->Params.empty())
480 serializeInfo(Template.Specialization->Params, TemplateSpecializationObj);
481 TemplateObj["Specialization"] = TemplateSpecializationVal;
482 }
483
484 if (!Template.Params.empty())
485 serializeInfo(Template.Params, TemplateObj);
486
487 if (!Template.Constraints.empty())
488 serializeArray(Template.Constraints, TemplateObj, "Constraints",
490
491 Obj["Template"] = TemplateVal;
492}
493
494static void serializeInfo(const ConceptInfo &I, Object &Obj,
495 const std::optional<StringRef> &RepositoryUrl) {
497 Obj["IsType"] = I.IsType;
498 Obj["ConstraintExpression"] = I.ConstraintExpression;
499 serializeInfo(I.Template, Obj);
500}
501
502static void serializeInfo(const TypeInfo &I, Object &Obj) {
503 Obj["Name"] = I.Type.Name;
504 Obj["QualName"] = I.Type.QualName;
505 Obj["USR"] = toHex(toStringRef(I.Type.USR));
506 Obj["IsTemplate"] = I.IsTemplate;
507 Obj["IsBuiltIn"] = I.IsBuiltIn;
508}
509
510static void serializeInfo(const FieldTypeInfo &I, Object &Obj) {
511 Obj["Name"] = I.Name;
512 insertNonEmpty("DefaultValue", I.DefaultValue, Obj);
513 json::Value ReferenceVal = Object();
514 Object &ReferenceObj = *ReferenceVal.getAsObject();
515 serializeReference(I.Type, ReferenceObj);
516 Obj["Type"] = ReferenceVal;
517}
518
519static void serializeInfo(const FunctionInfo &F, json::Object &Obj,
520 const std::optional<StringRef> RepositoryURL) {
521 serializeCommonAttributes(F, Obj, RepositoryURL);
522 Obj["IsStatic"] = F.IsStatic;
523
524 auto ReturnTypeObj = Object();
525 serializeInfo(F.ReturnType, ReturnTypeObj);
526 Obj["ReturnType"] = std::move(ReturnTypeObj);
527
528 if (!F.Params.empty())
529 serializeArray(F.Params, Obj, "Params", SerializeInfoLambda);
530
531 if (F.Template)
532 serializeInfo(F.Template.value(), Obj);
533}
534
535static void serializeInfo(const EnumValueInfo &I, Object &Obj) {
536 Obj["Name"] = I.Name;
537 if (!I.ValueExpr.empty())
538 Obj["ValueExpr"] = I.ValueExpr;
539 else
540 Obj["Value"] = I.Value;
541}
542
543static void serializeInfo(const EnumInfo &I, json::Object &Obj,
544 const std::optional<StringRef> &RepositoryUrl) {
546 Obj["Scoped"] = I.Scoped;
547
548 if (I.BaseType) {
549 json::Value BaseTypeVal = Object();
550 auto &BaseTypeObj = *BaseTypeVal.getAsObject();
551 BaseTypeObj["Name"] = I.BaseType->Type.Name;
552 BaseTypeObj["QualName"] = I.BaseType->Type.QualName;
553 BaseTypeObj["USR"] = toHex(toStringRef(I.BaseType->Type.USR));
554 Obj["BaseType"] = BaseTypeVal;
555 }
556
557 if (!I.Members.empty())
558 serializeArray(I.Members, Obj, "Members", SerializeInfoLambda);
559}
560
561static void serializeInfo(const TypedefInfo &I, json::Object &Obj,
562 const std::optional<StringRef> &RepositoryUrl) {
564 Obj["TypeDeclaration"] = I.TypeDeclaration;
565 Obj["IsUsing"] = I.IsUsing;
566 json::Value TypeVal = Object();
567 auto &TypeObj = *TypeVal.getAsObject();
568 serializeInfo(I.Underlying, TypeObj);
569 Obj["Underlying"] = TypeVal;
570 if (I.Template)
571 serializeInfo(I.Template.value(), Obj);
572}
573
574static void serializeInfo(const BaseRecordInfo &I, Object &Obj,
575 const std::optional<StringRef> &RepositoryUrl) {
576 serializeInfo(static_cast<const RecordInfo &>(I), Obj, RepositoryUrl);
577 Obj["IsVirtual"] = I.IsVirtual;
578 Obj["Access"] = getAccessSpelling(I.Access);
579 Obj["IsParent"] = I.IsParent;
580}
581
582static void serializeInfo(const FriendInfo &I, Object &Obj) {
583 auto FriendRef = Object();
584 serializeReference(I.Ref, FriendRef);
585 Obj["Reference"] = std::move(FriendRef);
586 Obj["IsClass"] = I.IsClass;
587 if (I.Template)
588 serializeInfo(I.Template.value(), Obj);
589 if (I.Params)
590 serializeArray(I.Params.value(), Obj, "Params", SerializeInfoLambda);
591 if (I.ReturnType) {
592 auto ReturnTypeObj = Object();
593 serializeInfo(I.ReturnType.value(), ReturnTypeObj);
594 Obj["ReturnType"] = std::move(ReturnTypeObj);
595 }
596 serializeCommonAttributes(I, Obj, std::nullopt);
597}
598
599static void insertArray(Object &Obj, json::Value &Array, StringRef Key) {
600 Obj[Key] = Array;
601 Obj["Has" + Key.str()] = true;
602}
603
604static void serializeInfo(const RecordInfo &I, json::Object &Obj,
605 const std::optional<StringRef> &RepositoryUrl) {
607 Obj["TagType"] = getTagType(I.TagType);
608 Obj["IsTypedef"] = I.IsTypeDef;
609 Obj["MangledName"] = I.MangledName;
610
611 if (!I.Children.Functions.empty()) {
612 json::Value PubFunctionsArray = Array();
613 json::Array &PubFunctionsArrayRef = *PubFunctionsArray.getAsArray();
614 json::Value ProtFunctionsArray = Array();
615 json::Array &ProtFunctionsArrayRef = *ProtFunctionsArray.getAsArray();
616
617 for (const auto &Function : I.Children.Functions) {
618 json::Value FunctionVal = Object();
619 auto &FunctionObj = *FunctionVal.getAsObject();
620 serializeInfo(Function, FunctionObj, RepositoryUrl);
621 AccessSpecifier Access = Function.Access;
622 if (Access == AccessSpecifier::AS_public)
623 PubFunctionsArrayRef.push_back(FunctionVal);
624 else if (Access == AccessSpecifier::AS_protected)
625 ProtFunctionsArrayRef.push_back(FunctionVal);
626 }
627
628 if (!PubFunctionsArrayRef.empty())
629 insertArray(Obj, PubFunctionsArray, "PublicFunctions");
630 if (!ProtFunctionsArrayRef.empty())
631 insertArray(Obj, ProtFunctionsArray, "ProtectedFunctions");
632 }
633
634 if (!I.Members.empty()) {
635 json::Value PublicMembersArray = Array();
636 json::Array &PubMembersArrayRef = *PublicMembersArray.getAsArray();
637 json::Value ProtectedMembersArray = Array();
638 json::Array &ProtMembersArrayRef = *ProtectedMembersArray.getAsArray();
639 json::Value PrivateMembersArray = Array();
640 json::Array &PrivateMembersArrayRef = *PrivateMembersArray.getAsArray();
641
642 for (const MemberTypeInfo &Member : I.Members) {
643 json::Value MemberVal = Object();
644 auto &MemberObj = *MemberVal.getAsObject();
645 MemberObj["Name"] = Member.Name;
646 MemberObj["Type"] = Member.Type.Name;
647 MemberObj["IsStatic"] = Member.IsStatic;
648
649 if (Member.Access == AccessSpecifier::AS_public)
650 PubMembersArrayRef.push_back(MemberVal);
651 else if (Member.Access == AccessSpecifier::AS_protected)
652 ProtMembersArrayRef.push_back(MemberVal);
653 else if (Member.Access == AccessSpecifier::AS_private)
654 PrivateMembersArrayRef.push_back(MemberVal);
655 }
656
657 if (!PubMembersArrayRef.empty())
658 insertArray(Obj, PublicMembersArray, "PublicMembers");
659 if (!ProtMembersArrayRef.empty())
660 insertArray(Obj, ProtectedMembersArray, "ProtectedMembers");
661 if (!PrivateMembersArrayRef.empty())
662 insertArray(Obj, PrivateMembersArray, "PrivateMembers");
663 }
664
665 if (!I.Bases.empty())
667 I.Bases, Obj, "Bases",
668 [&RepositoryUrl](const BaseRecordInfo &Base, Object &BaseObj) {
669 serializeInfo(Base, BaseObj, RepositoryUrl);
670 });
671
672 if (!I.Parents.empty()) {
674 Obj["HasParents"] = true;
675 }
676
677 if (!I.VirtualParents.empty()) {
678 serializeArray(I.VirtualParents, Obj, "VirtualParents",
680 Obj["HasVirtualParents"] = true;
681 }
682
683 if (I.Template)
684 serializeInfo(I.Template.value(), Obj);
685
686 if (!I.Friends.empty()) {
687 serializeArray(I.Friends, Obj, "Friends", SerializeInfoLambda);
688 Obj["HasFriends"] = true;
689 }
690
692}
693
694static void serializeInfo(const VarInfo &I, json::Object &Obj,
695 const std::optional<StringRef> RepositoryUrl) {
697 Obj["IsStatic"] = I.IsStatic;
698 auto TypeObj = Object();
699 serializeInfo(I.Type, TypeObj);
700 Obj["Type"] = std::move(TypeObj);
701}
702
703static void serializeInfo(const NamespaceInfo &I, json::Object &Obj,
704 const std::optional<StringRef> RepositoryUrl) {
706 if (I.USR == GlobalNamespaceID)
707 Obj["Name"] = "Global Namespace";
708
709 if (!I.Children.Namespaces.empty()) {
710 serializeArray(I.Children.Namespaces, Obj, "Namespaces",
712 Obj["HasNamespaces"] = true;
713 }
714
715 static auto SerializeInfo = [RepositoryUrl](const auto &Info,
716 Object &Object) {
718 };
719
720 if (!I.Children.Functions.empty()) {
721 serializeArray(I.Children.Functions, Obj, "Functions", SerializeInfo);
722 Obj["HasFunctions"] = true;
723 }
724
725 if (!I.Children.Concepts.empty()) {
726 serializeArray(I.Children.Concepts, Obj, "Concepts", SerializeInfo);
727 Obj["HasConcepts"] = true;
728 }
729
730 if (!I.Children.Variables.empty())
731 serializeArray(I.Children.Variables, Obj, "Variables", SerializeInfo);
732
734}
735
736static SmallString<16> determineFileName(Info *I, SmallString<128> &Path) {
737 SmallString<16> FileName;
738 if (I->IT == InfoType::IT_record) {
739 auto *RecordSymbolInfo = static_cast<SymbolInfo *>(I);
740 FileName = RecordSymbolInfo->MangledName;
741 } else if (I->IT == InfoType::IT_namespace) {
742 FileName = "index";
743 } else
744 FileName = I->Name;
745 sys::path::append(Path, FileName + ".json");
746 return FileName;
747}
748
749// Creates a JSON file above the global namespace directory.
750// An index can be used to create the top-level HTML index page or the Markdown
751// index file.
752static Error serializeIndex(const ClangDocContext &CDCtx, StringRef RootDir) {
753 if (CDCtx.Idx.Children.empty())
754 return Error::success();
755
756 json::Value ObjVal = Object();
757 Object &Obj = *ObjVal.getAsObject();
758 insertNonEmpty("ProjectName", CDCtx.ProjectName, Obj);
759
760 auto IndexCopy = CDCtx.Idx;
761 IndexCopy.sort();
762 json::Value IndexArray = json::Array();
763 auto &IndexArrayRef = *IndexArray.getAsArray();
764
765 if (IndexCopy.Children.empty()) {
766 // If the index is empty, default to displaying the global namespace.
767 IndexCopy.Children.emplace_back(GlobalNamespaceID, "",
768 InfoType::IT_namespace, "GlobalNamespace");
769 } else {
770 IndexArrayRef.reserve(CDCtx.Idx.Children.size());
771 }
772
773 for (auto &Idx : IndexCopy.Children) {
774 if (Idx.Children.empty())
775 continue;
776 std::string TypeStr = infoTypeToString(Idx.RefType);
777 json::Value IdxVal = Object();
778 auto &IdxObj = *IdxVal.getAsObject();
779 serializeReference(Idx, IdxObj);
780 IndexArrayRef.push_back(IdxVal);
781 }
782 Obj["Index"] = IndexArray;
783
784 SmallString<128> IndexFilePath(RootDir);
785 sys::path::append(IndexFilePath, "/json/index.json");
786 std::error_code FileErr;
787 raw_fd_ostream RootOS(IndexFilePath, FileErr, sys::fs::OF_Text);
788 if (FileErr)
789 return createFileError("cannot open file " + IndexFilePath, FileErr);
790 RootOS << llvm::formatv("{0:2}", ObjVal);
791 return Error::success();
792}
793
794static void serializeContexts(Info *I,
795 StringMap<std::unique_ptr<Info>> &Infos) {
796 if (I->USR == GlobalNamespaceID)
797 return;
798 auto ParentUSR = I->ParentUSR;
799
800 while (true) {
801 auto &ParentInfo = Infos.at(llvm::toHex(ParentUSR));
802
803 if (ParentInfo && ParentInfo->USR == GlobalNamespaceID) {
804 Context GlobalRef(ParentInfo->USR, "Global Namespace",
805 InfoType::IT_namespace, "GlobalNamespace", "",
806 SmallString<16>("index"));
807 I->Contexts.push_back(GlobalRef);
808 return;
809 }
810
811 Context ParentRef(*ParentInfo);
812 I->Contexts.push_back(ParentRef);
813 ParentUSR = ParentInfo->ParentUSR;
814 }
815}
816
818 StringRef RootDir, llvm::StringMap<std::unique_ptr<doc::Info>> Infos,
819 const ClangDocContext &CDCtx, std::string DirName) {
820 StringSet<> CreatedDirs;
821 StringMap<std::vector<doc::Info *>> FileToInfos;
822 for (const auto &Group : Infos) {
823 Info *Info = Group.getValue().get();
824
825 SmallString<128> Path;
826 auto RootDirStr = RootDir.str() + "/json";
827 StringRef JSONDir = StringRef(RootDirStr);
828 sys::path::native(JSONDir, Path);
829 sys::path::append(Path, Info->getRelativeFilePath(""));
830 if (!CreatedDirs.contains(Path)) {
831 if (std::error_code Err = sys::fs::create_directories(Path);
832 Err != std::error_code())
833 return createFileError(Twine(Path), Err);
834 CreatedDirs.insert(Path);
835 }
836
837 SmallString<16> FileName = determineFileName(Info, Path);
838 if (FileToInfos.contains(Path))
839 continue;
840 FileToInfos[Path].push_back(Info);
841 Info->DocumentationFileName = FileName;
842 }
843
844 for (const auto &Group : FileToInfos) {
845 std::error_code FileErr;
846 raw_fd_ostream InfoOS(Group.getKey(), FileErr, sys::fs::OF_Text);
847 if (FileErr)
848 return createFileError("cannot open file " + Group.getKey(), FileErr);
849
850 for (const auto &Info : Group.getValue()) {
852 serializeContexts(Info, Infos);
853 if (Error Err = generateDocForInfo(Info, InfoOS, CDCtx))
854 return Err;
855 }
856 }
857
858 return serializeIndex(CDCtx, RootDir);
859}
860
861Error JSONGenerator::generateDocForInfo(Info *I, raw_ostream &OS,
862 const ClangDocContext &CDCtx) {
863 json::Object Obj = Object();
864
865 switch (I->IT) {
867 serializeInfo(*static_cast<NamespaceInfo *>(I), Obj, CDCtx.RepositoryUrl);
868 break;
870 serializeInfo(*static_cast<RecordInfo *>(I), Obj, CDCtx.RepositoryUrl);
871 break;
878 break;
880 return createStringError(inconvertibleErrorCode(), "unexpected info type");
881 }
882 OS << llvm::formatv("{0:2}", llvm::json::Value(std::move(Obj)));
883 return Error::success();
884}
885
887 return Error::success();
888}
889
890static GeneratorRegistry::Add<JSONGenerator> JSON(JSONGenerator::Format,
891 "Generator for JSON output.");
893} // namespace doc
894} // namespace clang
static llvm::cl::opt< std::string > RepositoryUrl("repository", llvm::cl::desc(R"( URL of repository that hosts code. Used for links to definition locations.)"), llvm::cl::cat(ClangDocCategory))
static const char * Format
Error generateDocForInfo(Info *I, llvm::raw_ostream &OS, const ClangDocContext &CDCtx) override
Error createResources(ClangDocContext &CDCtx) override
Error generateDocumentation(StringRef RootDir, llvm::StringMap< std::unique_ptr< doc::Info > > Infos, const ClangDocContext &CDCtx, std::string DirName) override
static std::string infoTypeToString(InfoType IT)
static json::Value extractTextComments(Object *ParagraphComment)
Takes the nested "Children" array from a comment Object.
static Object serializeComment(const CommentInfo &I, Object &Description)
std::string getTagType(TagTypeKind AS)
static void generateContext(const Info &I, Object &Obj)
Creates Contexts for namespaces and records to allow for navigation.
static void insertComment(Object &Description, json::Value &Comment, StringRef Key)
Insert comments into a key in the Description object.
static void serializeArray(const Container &Records, Object &Obj, const std::string &Key, SerializationFunc SerializeInfo)
static void serializeContexts(Info *I, StringMap< std::unique_ptr< Info > > &Infos)
volatile int JSONGeneratorAnchorSource
static auto SerializeReferenceLambda
static void serializeReference(const Reference &Ref, Object &ReferenceObj)
static void serializeInfo(const ConstraintInfo &I, Object &Obj)
static void serializeCommonAttributes(const Info &I, json::Object &Obj, const std::optional< StringRef > RepositoryUrl)
static GeneratorRegistry::Add< JSONGenerator > JSON(JSONGenerator::Format, "Generator for JSON output.")
static void serializeCommonChildren(const ScopeChildren &Children, json::Object &Obj, const std::optional< StringRef > RepositoryUrl)
static auto SerializeInfoLambda
static SmallString< 16 > determineFileName(Info *I, SmallString< 128 > &Path)
constexpr SymbolID GlobalNamespaceID
static json::Value extractVerbatimComments(json::Array VerbatimLines)
static void insertNonEmpty(StringRef Key, StringRef Value, Object &Obj)
static Error serializeIndex(const ClangDocContext &CDCtx, StringRef RootDir)
static json::Object serializeLocation(const Location &Loc, const std::optional< StringRef > RepositoryUrl)
llvm::StringRef commentKindToString(CommentKind Kind)
static void insertArray(Object &Obj, json::Value &Array, StringRef Key)
===– Representation.cpp - ClangDoc Representation --------—*- C++ -*-===//
Some operations such as code completion produce a set of candidates.
Definition Generators.h:145
std::optional< std::string > RepositoryUrl
SmallString< 8 > Direction
std::vector< std::unique_ptr< CommentInfo > > Children
llvm::SmallVector< SmallString< 16 >, 4 > AttrValues
SmallString< 16 > CloseName
SmallString< 16 > Name
SmallString< 64 > Text
llvm::SmallVector< SmallString< 16 >, 4 > AttrKeys
llvm::SmallVector< SmallString< 16 >, 4 > Args
SmallString< 16 > ParamName
SmallString< 16 > ConstraintExpression
SmallString< 16 > ConstraintExpr
llvm::SmallVector< EnumValueInfo, 4 > Members
std::optional< TypeInfo > BaseType
SmallString< 16 > ValueExpr
SmallString< 16 > DefaultValue
std::optional< TypeInfo > ReturnType
std::optional< SmallVector< FieldTypeInfo, 4 > > Params
std::optional< TemplateInfo > Template
llvm::SmallVector< FieldTypeInfo, 4 > Params
std::optional< TemplateInfo > Template
std::vector< Index > Children
A base struct for Infos.
SmallString< 16 > DocumentationFileName
SmallString< 16 > Name
std::vector< CommentInfo > Description
SmallVector< Context, 4 > Contexts
llvm::SmallString< 128 > Path
llvm::SmallString< 64 > getRelativeFilePath(const StringRef &CurrentPath) const
Returns the file path for this Info relative to CurrentPath.
llvm::SmallVector< Reference, 4 > Namespace
SmallString< 32 > Filename
llvm::SmallVector< MemberTypeInfo, 4 > Members
std::optional< TemplateInfo > Template
std::vector< FriendInfo > Friends
llvm::SmallVector< Reference, 4 > VirtualParents
llvm::SmallVector< Reference, 4 > Parents
std::vector< BaseRecordInfo > Bases
SmallString< 16 > QualName
llvm::SmallString< 128 > Path
SmallString< 16 > DocumentationFileName
SmallString< 16 > Name
std::vector< FunctionInfo > Functions
std::vector< Reference > Namespaces
std::vector< VarInfo > Variables
std::vector< ConceptInfo > Concepts
SmallString< 16 > MangledName
std::optional< TemplateInfo > Template
SmallString< 16 > TypeDeclaration