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