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