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