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
463// Although namespaces and records both have ScopeChildren, they serialize them
464// differently. Only enums, records, and typedefs are handled here.
465void JSONGenerator::serializeCommonChildren(
466 const ScopeChildren &Children, json::Object &Obj,
467 std::optional<ReferenceFunc> MDReferenceLambda) {
468 if (!Children.Enums.empty()) {
469 serializeArray(Children.Enums, Obj, "Enums", serializeInfoLambda());
470 Obj["HasEnums"] = true;
471 }
472
473 if (!Children.Typedefs.empty()) {
474 serializeArray(Children.Typedefs, Obj, "Typedefs", serializeInfoLambda());
475 Obj["HasTypedefs"] = true;
476 }
477
478 if (!Children.Records.empty()) {
479 ReferenceFunc SerializeReferenceFunc = MDReferenceLambda
480 ? MDReferenceLambda.value()
481 : serializeReferenceLambda();
482 serializeArray(Children.Records, Obj, "Records", SerializeReferenceFunc);
483 Obj["HasRecords"] = true;
484 }
485}
486
487template <typename Container, typename SerializationFunc>
488static void serializeArray(const Container &Records, Object &Obj, StringRef Key,
489 SerializationFunc SerializeInfo, StringRef EndKey,
490 function_ref<void(Object &)> UpdateJson) {
491 json::Value RecordsArray = Array();
492 auto &RecordsArrayRef = *RecordsArray.getAsArray();
493 RecordsArrayRef.reserve(Records.size());
494 size_t Index = 0;
495 size_t Size = Records.size();
496 for (const auto &Item : Records) {
497 json::Value ItemVal = Object();
498 auto &ItemObj = *ItemVal.getAsObject();
499 SerializeInfo(Item, ItemObj);
500 if (Index == Size - 1)
501 ItemObj[EndKey] = true;
502 RecordsArrayRef.push_back(ItemVal);
503 ++Index;
504 }
505 Obj[Key] = RecordsArray;
506 UpdateJson(Obj);
507}
508
509void JSONGenerator::serializeInfo(const ConstraintInfo &I, Object &Obj) {
510 serializeReference(I.ConceptRef, Obj);
511 Obj["Expression"] = I.ConstraintExpr;
512}
513
514void JSONGenerator::serializeInfo(const TemplateInfo &Template, Object &Obj) {
515 json::Value TemplateVal = Object();
516 auto &TemplateObj = *TemplateVal.getAsObject();
517 auto SerializeTemplateParam = [](const TemplateParamInfo &Param,
518 Object &JsonObj) {
519 JsonObj["Param"] = Param.Contents;
520 };
521
522 if (Template.Specialization) {
523 json::Value TemplateSpecializationVal = Object();
524 auto &TemplateSpecializationObj = *TemplateSpecializationVal.getAsObject();
525 TemplateSpecializationObj["SpecializationOf"] =
526 toHex(toStringRef(Template.Specialization->SpecializationOf));
527 if (!Template.Specialization->Params.empty()) {
528 bool VerticalDisplay =
529 Template.Specialization->Params.size() > getMaxParamWrapLimit();
530 serializeArray(Template.Specialization->Params, TemplateSpecializationObj,
531 "Parameters", SerializeTemplateParam, "SpecParamEnd",
532 [VerticalDisplay](Object &JsonObj) {
533 JsonObj["VerticalDisplay"] = VerticalDisplay;
534 });
535 }
536 TemplateObj["Specialization"] = TemplateSpecializationVal;
537 }
538
539 if (!Template.Params.empty()) {
540 bool VerticalDisplay = Template.Params.size() > getMaxParamWrapLimit();
541 serializeArray(Template.Params, TemplateObj, "Parameters",
542 SerializeTemplateParam, "End",
543 [VerticalDisplay](Object &JsonObj) {
544 JsonObj["VerticalDisplay"] = VerticalDisplay;
545 });
546 }
547
548 if (!Template.Constraints.empty())
549 serializeArray(Template.Constraints, TemplateObj, "Constraints",
550 serializeInfoLambda());
551
552 Obj["Template"] = TemplateVal;
553}
554
555void JSONGenerator::serializeInfo(const ConceptInfo &I, Object &Obj) {
556 serializeCommonAttributes(I, Obj);
557 Obj["IsType"] = I.IsType;
558 Obj["ConstraintExpression"] = I.ConstraintExpression;
559 serializeInfo(I.Template, Obj);
560}
561
562void JSONGenerator::serializeInfo(const TypeInfo &I, Object &Obj) {
563 Obj["Name"] = I.Type.Name;
564 Obj["QualName"] = I.Type.QualName;
565 Obj["USR"] = toHex(toStringRef(I.Type.USR));
566 Obj["IsTemplate"] = I.IsTemplate;
567 Obj["IsBuiltIn"] = I.IsBuiltIn;
568}
569
570void JSONGenerator::serializeInfo(const FieldTypeInfo &I, Object &Obj) {
571 Obj["Name"] = I.Name;
572 insertNonEmpty("DefaultValue", I.DefaultValue, Obj);
573 json::Value ReferenceVal = Object();
574 Object &ReferenceObj = *ReferenceVal.getAsObject();
575 serializeReference(I.Type, ReferenceObj);
576 Obj["Type"] = ReferenceVal;
577}
578
579void JSONGenerator::serializeInfo(const FunctionInfo &F, json::Object &Obj) {
580 serializeCommonAttributes(F, Obj);
581 Obj["IsStatic"] = F.IsStatic;
582
583 auto ReturnTypeObj = Object();
584 serializeInfo(F.ReturnType, ReturnTypeObj);
585 Obj["ReturnType"] = std::move(ReturnTypeObj);
586
587 if (!F.Params.empty()) {
588 const bool VerticalDisplay = F.Params.size() > getMaxParamWrapLimit();
589 serializeArray(F.Params, Obj, "Params", serializeInfoLambda(), "ParamEnd",
590 [VerticalDisplay](Object &JsonObj) {
591 JsonObj["VerticalDisplay"] = VerticalDisplay;
592 });
593 }
594
595 if (F.Template)
596 serializeInfo(F.Template.value(), Obj);
597}
598
599void JSONGenerator::serializeInfo(const EnumValueInfo &I, Object &Obj) {
600 Obj["Name"] = I.Name;
601 if (!I.ValueExpr.empty())
602 Obj["ValueExpr"] = I.ValueExpr;
603 else
604 Obj["Value"] = I.Value;
605
606 serializeDescription(I.Description, Obj, "HasEnumMemberComments");
607}
608
609void JSONGenerator::serializeInfo(const EnumInfo &I, json::Object &Obj) {
610 serializeCommonAttributes(I, Obj);
611 Obj["Scoped"] = I.Scoped;
612
613 if (I.BaseType) {
614 json::Value BaseTypeVal = Object();
615 auto &BaseTypeObj = *BaseTypeVal.getAsObject();
616 BaseTypeObj["Name"] = I.BaseType->Type.Name;
617 BaseTypeObj["QualName"] = I.BaseType->Type.QualName;
618 BaseTypeObj["USR"] = toHex(toStringRef(I.BaseType->Type.USR));
619 Obj["BaseType"] = BaseTypeVal;
620 }
621
622 if (!I.Members.empty()) {
623 for (const auto &Member : I.Members) {
624 if (!Member.Description.empty()) {
625 Obj["HasComments"] = true;
626 break;
627 }
628 }
629 serializeArray(I.Members, Obj, "Members", serializeInfoLambda());
630 }
631}
632
633void JSONGenerator::serializeInfo(const TypedefInfo &I, json::Object &Obj) {
634 serializeCommonAttributes(I, Obj);
635 Obj["TypeDeclaration"] = I.TypeDeclaration;
636 Obj["IsUsing"] = I.IsUsing;
637 json::Value TypeVal = Object();
638 auto &TypeObj = *TypeVal.getAsObject();
639 serializeInfo(I.Underlying, TypeObj);
640 Obj["Underlying"] = TypeVal;
641 if (I.Template)
642 serializeInfo(I.Template.value(), Obj);
643}
644
645void JSONGenerator::serializeInfo(const BaseRecordInfo &I, Object &Obj) {
646 serializeInfo(static_cast<const RecordInfo &>(I), Obj);
647 Obj["IsVirtual"] = I.IsVirtual;
648 Obj["Access"] = getAccessSpelling(I.Access);
649 Obj["IsParent"] = I.IsParent;
650}
651
652void JSONGenerator::serializeInfo(const FriendInfo &I, Object &Obj) {
653 auto FriendRef = Object();
654 serializeReference(I.Ref, FriendRef);
655 Obj["Reference"] = std::move(FriendRef);
656 Obj["IsClass"] = I.IsClass;
657 if (I.Template)
658 serializeInfo(I.Template.value(), Obj);
659 if (!I.Params.empty())
660 serializeArray(I.Params, Obj, "Params", serializeInfoLambda());
661 if (I.ReturnType) {
662 auto ReturnTypeObj = Object();
663 serializeInfo(I.ReturnType.value(), ReturnTypeObj);
664 Obj["ReturnType"] = std::move(ReturnTypeObj);
665 }
666 serializeCommonAttributes(I, Obj);
667}
668
669static void insertArray(Object &Obj, json::Value &Array, StringRef Key) {
670 Obj[Key] = Array;
671 Obj["Has" + Key.str()] = true;
672}
673
674void JSONGenerator::serializeInfo(const RecordInfo &I, json::Object &Obj) {
675 serializeCommonAttributes(I, Obj);
676 Obj["TagType"] = getTagType(I.TagType);
677 Obj["IsTypedef"] = I.IsTypeDef;
678 Obj["MangledName"] = I.MangledName;
679
680 if (!I.Children.Functions.empty()) {
681 json::Value PubFunctionsArray = Array();
682 json::Array &PubFunctionsArrayRef = *PubFunctionsArray.getAsArray();
683 json::Value ProtFunctionsArray = Array();
684 json::Array &ProtFunctionsArrayRef = *ProtFunctionsArray.getAsArray();
685
686 for (const auto &Function : I.Children.Functions) {
687 json::Value FunctionVal = Object();
688 auto &FunctionObj = *FunctionVal.getAsObject();
689 serializeInfo(Function, FunctionObj);
690 AccessSpecifier Access = Function.Access;
691 if (Access == AccessSpecifier::AS_public)
692 PubFunctionsArrayRef.push_back(FunctionVal);
693 else if (Access == AccessSpecifier::AS_protected)
694 ProtFunctionsArrayRef.push_back(FunctionVal);
695 }
696
697 if (!PubFunctionsArrayRef.empty())
698 insertArray(Obj, PubFunctionsArray, "PublicMethods");
699 if (!ProtFunctionsArrayRef.empty())
700 insertArray(Obj, ProtFunctionsArray, "ProtectedMethods");
701 }
702
703 if (!I.Members.empty()) {
704 Obj["HasMembers"] = true;
705 json::Value PublicMembersArray = Array();
706 json::Array &PubMembersArrayRef = *PublicMembersArray.getAsArray();
707 json::Value ProtectedMembersArray = Array();
708 json::Array &ProtMembersArrayRef = *ProtectedMembersArray.getAsArray();
709 json::Value PrivateMembersArray = Array();
710 json::Array &PrivateMembersArrayRef = *PrivateMembersArray.getAsArray();
711
712 for (const MemberTypeInfo &Member : I.Members) {
713 json::Value MemberVal = Object();
714 auto &MemberObj = *MemberVal.getAsObject();
715 MemberObj["Name"] = Member.Name;
716 MemberObj["Type"] = Member.Type.Name;
717 MemberObj["IsStatic"] = Member.IsStatic;
718
719 if (Member.Access == AccessSpecifier::AS_public)
720 PubMembersArrayRef.push_back(MemberVal);
721 else if (Member.Access == AccessSpecifier::AS_protected)
722 ProtMembersArrayRef.push_back(MemberVal);
723 else if (Member.Access == AccessSpecifier::AS_private)
724 PrivateMembersArrayRef.push_back(MemberVal);
725 }
726
727 if (!PubMembersArrayRef.empty())
728 insertArray(Obj, PublicMembersArray, "PublicMembers");
729 if (!ProtMembersArrayRef.empty())
730 insertArray(Obj, ProtectedMembersArray, "ProtectedMembers");
731 if (!PrivateMembersArrayRef.empty())
732 insertArray(Obj, PrivateMembersArray, "PrivateMembers");
733 }
734
735 if (!I.Bases.empty())
736 serializeArray(I.Bases, Obj, "Bases", serializeInfoLambda());
737
738 if (!I.Parents.empty()) {
739 serializeArray(I.Parents, Obj, "Parents", serializeReferenceLambda());
740 Obj["HasParents"] = true;
741 }
742
743 if (!I.VirtualParents.empty()) {
744 serializeArray(I.VirtualParents, Obj, "VirtualParents",
745 serializeReferenceLambda());
746 Obj["HasVirtualParents"] = true;
747 }
748
749 if (I.Template)
750 serializeInfo(I.Template.value(), Obj);
751
752 if (!I.Friends.empty()) {
753 serializeArray(I.Friends, Obj, "Friends", serializeInfoLambda());
754 Obj["HasFriends"] = true;
755 }
756
757 serializeCommonChildren(I.Children, Obj);
758}
759
760void JSONGenerator::serializeInfo(const VarInfo &I, json::Object &Obj) {
761 serializeCommonAttributes(I, Obj);
762 Obj["IsStatic"] = I.IsStatic;
763 auto TypeObj = Object();
764 serializeInfo(I.Type, TypeObj);
765 Obj["Type"] = std::move(TypeObj);
766}
767
768void JSONGenerator::serializeInfo(const NamespaceInfo &I, json::Object &Obj) {
769 serializeCommonAttributes(I, Obj);
770 if (I.USR == GlobalNamespaceID)
771 Obj["Name"] = "Global Namespace";
772
773 if (!I.Children.Functions.empty()) {
774 serializeArray(I.Children.Functions, Obj, "Functions",
775 serializeInfoLambda());
776 Obj["HasFunctions"] = true;
777 }
778
779 if (!I.Children.Concepts.empty()) {
780 serializeArray(I.Children.Concepts, Obj, "Concepts", serializeInfoLambda());
781 Obj["HasConcepts"] = true;
782 }
783
784 if (!I.Children.Variables.empty()) {
785 serializeArray(I.Children.Variables, Obj, "Variables",
786 serializeInfoLambda());
787 Obj["HasVariables"] = true;
788 }
789
790 ReferenceFunc SerializeReferenceFunc;
791 if (Markdown) {
792 SmallString<64> BasePath = I.getRelativeFilePath("");
793 // serializeCommonChildren doesn't accept Infos, so this lambda needs to be
794 // created here. To avoid making serializeCommonChildren a template, this
795 // lambda is an std::function
796 SerializeReferenceFunc = [this, BasePath](const Reference &Ref,
797 Object &Object) {
798 serializeMDReference(Ref, Object, BasePath);
799 };
800 serializeCommonChildren(I.Children, Obj, SerializeReferenceFunc);
801 } else {
802 SerializeReferenceFunc = serializeReferenceLambda();
803 serializeCommonChildren(I.Children, Obj);
804 }
805
806 if (!I.Children.Namespaces.empty()) {
807 serializeArray(I.Children.Namespaces, Obj, "Namespaces",
808 SerializeReferenceFunc);
809 Obj["HasNamespaces"] = true;
810 }
811}
812
813SmallString<16> JSONGenerator::determineFileName(Info *I,
814 SmallString<128> &Path) {
815 SmallString<16> FileName;
816 if (I->IT == InfoType::IT_record) {
817 auto *RecordSymbolInfo = static_cast<SymbolInfo *>(I);
818 FileName = RecordSymbolInfo->MangledName;
819 } else if (I->IT == InfoType::IT_namespace) {
820 FileName = "index";
821 } else
822 FileName = I->Name;
823 sys::path::append(Path, FileName + ".json");
824 return FileName;
825}
826
827/// \param CDCtxIndex Passed by copy since clang-doc's context is passed to the
828/// generator as `const`
830 CDCtxIndex.sort();
831 OwningVec<Index> Processed;
832 Processed.reserve(CDCtxIndex.Children.size());
833 for (const auto *Idx : CDCtxIndex.getSortedChildren()) {
834 Index NewIdx = *Idx;
835 SmallString<128> NewPath(NewIdx.getRelativeFilePath(""));
836 sys::path::native(NewPath, sys::path::Style::posix);
837 sys::path::append(NewPath, sys::path::Style::posix,
838 NewIdx.getFileBaseName() + ".md");
839 NewIdx.Path = internString(NewPath);
840 Processed.push_back(NewIdx);
841 }
842
843 return Processed;
844}
845
846/// Serialize ClangDocContext's Index for Markdown output
847Error JSONGenerator::serializeAllFiles(const ClangDocContext &CDCtx,
848 StringRef RootDir) {
849 json::Value ObjVal = Object();
850 Object &Obj = *ObjVal.getAsObject();
852 serializeArray(IndexCopy, Obj, "Index", serializeReferenceLambda());
853 SmallString<128> Path;
854 sys::path::append(Path, RootDir, "json", "all_files.json");
855 std::error_code FileErr;
856 raw_fd_ostream RootOS(Path, FileErr, sys::fs::OF_Text);
857 if (FileErr)
858 return createFileError("cannot open file " + Path, FileErr);
859 RootOS << llvm::formatv("{0:2}", ObjVal);
860 return Error::success();
861}
862
863// Creates a JSON file above the global namespace directory.
864// An index can be used to create the top-level HTML index page or the Markdown
865// index file.
866Error JSONGenerator::serializeIndex(StringRef RootDir) {
867 if (CDCtx->Idx.Children.empty())
868 return Error::success();
869
870 json::Value ObjVal = Object();
871 Object &Obj = *ObjVal.getAsObject();
872 insertNonEmpty("ProjectName", CDCtx->ProjectName, Obj);
873
874 auto IndexCopy = CDCtx->Idx;
875 IndexCopy.sort();
876 json::Value IndexArray = json::Array();
877 auto &IndexArrayRef = *IndexArray.getAsArray();
878
879 if (IndexCopy.Children.empty()) {
880 // If the index is empty, default to displaying the global namespace.
881 IndexCopy.Children.try_emplace(toStringRef(GlobalNamespaceID),
883 InfoType::IT_namespace, "GlobalNamespace");
884 } else {
885 IndexArrayRef.reserve(CDCtx->Idx.Children.size());
886 }
887
888 auto Children = IndexCopy.getSortedChildren();
889
890 for (const auto *Idx : Children) {
891 if (Idx->Children.empty())
892 continue;
893 std::string TypeStr = infoTypeToString(Idx->RefType);
894 json::Value IdxVal = Object();
895 auto &IdxObj = *IdxVal.getAsObject();
896 if (Markdown)
897 TypeStr.at(0) = toUppercase(TypeStr.at(0));
898 IdxObj["Type"] = TypeStr;
899 serializeReference(*Idx, IdxObj);
900 IndexArrayRef.push_back(IdxVal);
901 }
902 Obj["Index"] = IndexArray;
903
904 SmallString<128> IndexFilePath(RootDir);
905 sys::path::append(IndexFilePath, "/json/index.json");
906 std::error_code FileErr;
907 raw_fd_ostream RootOS(IndexFilePath, FileErr, sys::fs::OF_Text);
908 if (FileErr)
909 return createFileError("cannot open file " + IndexFilePath, FileErr);
910 RootOS << llvm::formatv("{0:2}", ObjVal);
911 return Error::success();
912}
913
914static void serializeContexts(Info *I, StringMap<OwnedPtr<Info>> &Infos) {
915 if (I->USR == GlobalNamespaceID)
916 return;
917 auto ParentUSR = I->ParentUSR;
918
919 while (true) {
920 // Infos may not have the ParentUSR, if its been filtered (public or path),
921 // so we can't use at() for the lookup, since it would abort.
922 auto Iter = Infos.find(llvm::toHex(ParentUSR));
923 if (Iter == Infos.end())
924 break;
925 auto &ParentInfo = Iter->second;
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);
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
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.")
std::function< void(const Reference &, Object &)> ReferenceFunc
StringRef internString(const Twine &T)
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
llvm::ArrayRef< StringRef > Args
llvm::ArrayRef< StringRef > AttrValues
llvm::ArrayRef< CommentInfo > Children
llvm::ArrayRef< StringRef > AttrKeys
OwningVec< const Index * > getSortedChildren() const
llvm::StringMap< Index > Children
A base struct for Infos.
StringRef getRelativeFilePath(const StringRef &CurrentPath) const
Returns the file path for this Info relative to CurrentPath.
SmallVector< Context, 4 > Contexts
StringRef DocumentationFileName
StringRef getFileBaseName() const
Returns the basename that should be used for this Reference.
StringRef getRelativeFilePath(const StringRef &CurrentPath) const
Returns the path for this Reference relative to CurrentPath.