1#include "Generators.h"
2#include "clang/Basic/Specifiers.h"
3#include "llvm/Support/JSON.h"
4
5using namespace llvm;
6using namespace llvm::json;
7
8namespace clang {
9namespace doc {
10
11class JSONGenerator : public Generator {
12public:
13 static const char *Format;
14
15 Error generateDocs(StringRef RootDir,
16 llvm::StringMap<std::unique_ptr<doc::Info>> Infos,
17 const ClangDocContext &CDCtx) override;
18 Error createResources(ClangDocContext &CDCtx) override;
19 Error generateDocForInfo(Info *I, llvm::raw_ostream &OS,
20 const ClangDocContext &CDCtx) override;
21};
22
23const char *JSONGenerator::Format = "json";
24
25static void serializeInfo(const TypedefInfo &I, json::Object &Obj,
26 std::optional<StringRef> RepositoryUrl);
27static void serializeInfo(const EnumInfo &I, json::Object &Obj,
28 std::optional<StringRef> RepositoryUrl);
29
30static json::Object serializeLocation(const Location &Loc,
31 std::optional<StringRef> RepositoryUrl) {
32 Object LocationObj = Object();
33 LocationObj["LineNumber"] = Loc.StartLineNumber;
34 LocationObj["Filename"] = Loc.Filename;
35
36 if (!Loc.IsFileInRootDir || !RepositoryUrl)
37 return LocationObj;
38 SmallString<128> FileURL(*RepositoryUrl);
39 sys::path::append(path&: FileURL, style: sys::path::Style::posix, a: Loc.Filename);
40 FileURL += "#" + std::to_string(val: Loc.StartLineNumber);
41 LocationObj["FileURL"] = FileURL;
42 return LocationObj;
43}
44
45static json::Value serializeComment(const CommentInfo &I) {
46 // taken from PR #142273
47 Object Obj = Object();
48
49 json::Value ChildVal = Object();
50 Object &Child = *ChildVal.getAsObject();
51
52 json::Value ChildArr = Array();
53 auto &CARef = *ChildArr.getAsArray();
54 CARef.reserve(S: I.Children.size());
55 for (const auto &C : I.Children)
56 CARef.emplace_back(A: serializeComment(I: *C));
57
58 switch (I.Kind) {
59 case CommentKind::CK_TextComment: {
60 Obj.insert(E: {.K: commentKindToString(Kind: I.Kind), .V: I.Text});
61 return Obj;
62 }
63
64 case CommentKind::CK_BlockCommandComment: {
65 Child.insert(E: {.K: "Command", .V: I.Name});
66 Child.insert(E: {.K: "Children", .V: ChildArr});
67 Obj.insert(E: {.K: commentKindToString(Kind: I.Kind), .V: ChildVal});
68 return Obj;
69 }
70
71 case CommentKind::CK_InlineCommandComment: {
72 json::Value ArgsArr = Array();
73 auto &ARef = *ArgsArr.getAsArray();
74 ARef.reserve(S: I.Args.size());
75 for (const auto &Arg : I.Args)
76 ARef.emplace_back(A: Arg);
77 Child.insert(E: {.K: "Command", .V: I.Name});
78 Child.insert(E: {.K: "Args", .V: ArgsArr});
79 Child.insert(E: {.K: "Children", .V: ChildArr});
80 Obj.insert(E: {.K: commentKindToString(Kind: I.Kind), .V: ChildVal});
81 return Obj;
82 }
83
84 case CommentKind::CK_ParamCommandComment:
85 case CommentKind::CK_TParamCommandComment: {
86 Child.insert(E: {.K: "ParamName", .V: I.ParamName});
87 Child.insert(E: {.K: "Direction", .V: I.Direction});
88 Child.insert(E: {.K: "Explicit", .V: I.Explicit});
89 Child.insert(E: {.K: "Children", .V: ChildArr});
90 Obj.insert(E: {.K: commentKindToString(Kind: I.Kind), .V: ChildVal});
91 return Obj;
92 }
93
94 case CommentKind::CK_VerbatimBlockComment: {
95 Child.insert(E: {.K: "Text", .V: I.Text});
96 if (!I.CloseName.empty())
97 Child.insert(E: {.K: "CloseName", .V: I.CloseName});
98 Child.insert(E: {.K: "Children", .V: ChildArr});
99 Obj.insert(E: {.K: commentKindToString(Kind: I.Kind), .V: ChildVal});
100 return Obj;
101 }
102
103 case CommentKind::CK_VerbatimBlockLineComment:
104 case CommentKind::CK_VerbatimLineComment: {
105 Child.insert(E: {.K: "Text", .V: I.Text});
106 Child.insert(E: {.K: "Children", .V: ChildArr});
107 Obj.insert(E: {.K: commentKindToString(Kind: I.Kind), .V: ChildVal});
108 return Obj;
109 }
110
111 case CommentKind::CK_HTMLStartTagComment: {
112 json::Value AttrKeysArray = json::Array();
113 json::Value AttrValuesArray = json::Array();
114 auto &KeyArr = *AttrKeysArray.getAsArray();
115 auto &ValArr = *AttrValuesArray.getAsArray();
116 KeyArr.reserve(S: I.AttrKeys.size());
117 ValArr.reserve(S: I.AttrValues.size());
118 for (const auto &K : I.AttrKeys)
119 KeyArr.emplace_back(A: K);
120 for (const auto &V : I.AttrValues)
121 ValArr.emplace_back(A: V);
122 Child.insert(E: {.K: "Name", .V: I.Name});
123 Child.insert(E: {.K: "SelfClosing", .V: I.SelfClosing});
124 Child.insert(E: {.K: "AttrKeys", .V: AttrKeysArray});
125 Child.insert(E: {.K: "AttrValues", .V: AttrValuesArray});
126 Child.insert(E: {.K: "Children", .V: ChildArr});
127 Obj.insert(E: {.K: commentKindToString(Kind: I.Kind), .V: ChildVal});
128 return Obj;
129 }
130
131 case CommentKind::CK_HTMLEndTagComment: {
132 Child.insert(E: {.K: "Name", .V: I.Name});
133 Child.insert(E: {.K: "Children", .V: ChildArr});
134 Obj.insert(E: {.K: commentKindToString(Kind: I.Kind), .V: ChildVal});
135 return Obj;
136 }
137
138 case CommentKind::CK_FullComment:
139 case CommentKind::CK_ParagraphComment: {
140 Child.insert(E: {.K: "Children", .V: ChildArr});
141 Obj.insert(E: {.K: commentKindToString(Kind: I.Kind), .V: ChildVal});
142 return Obj;
143 }
144
145 case CommentKind::CK_Unknown: {
146 Obj.insert(E: {.K: commentKindToString(Kind: I.Kind), .V: I.Text});
147 return Obj;
148 }
149 }
150 llvm_unreachable("Unknown comment kind encountered.");
151}
152
153static void serializeCommonAttributes(const Info &I, json::Object &Obj,
154 std::optional<StringRef> RepositoryUrl) {
155 Obj["Name"] = I.Name;
156 Obj["USR"] = toHex(Input: toStringRef(Input: I.USR));
157
158 if (!I.Path.empty())
159 Obj["Path"] = I.Path;
160
161 if (!I.Namespace.empty()) {
162 Obj["Namespace"] = json::Array();
163 for (const auto &NS : I.Namespace)
164 Obj["Namespace"].getAsArray()->push_back(E: NS.Name);
165 }
166
167 if (!I.Description.empty()) {
168 json::Value DescArray = json::Array();
169 auto &DescArrayRef = *DescArray.getAsArray();
170 DescArrayRef.reserve(S: I.Description.size());
171 for (const auto &Comment : I.Description)
172 DescArrayRef.push_back(E: serializeComment(I: Comment));
173 Obj["Description"] = DescArray;
174 }
175
176 // Namespaces aren't SymbolInfos, so they dont have a DefLoc
177 if (I.IT != InfoType::IT_namespace) {
178 const auto *Symbol = static_cast<const SymbolInfo *>(&I);
179 if (Symbol->DefLoc)
180 Obj["Location"] =
181 serializeLocation(Loc: Symbol->DefLoc.value(), RepositoryUrl);
182 }
183}
184
185static void serializeReference(const Reference &Ref, Object &ReferenceObj) {
186 ReferenceObj["Path"] = Ref.Path;
187 ReferenceObj["Name"] = Ref.Name;
188 ReferenceObj["QualName"] = Ref.QualName;
189 ReferenceObj["USR"] = toHex(Input: toStringRef(Input: Ref.USR));
190}
191
192static void serializeReference(const SmallVector<Reference, 4> &References,
193 Object &Obj, std::string Key) {
194 json::Value ReferencesArray = Array();
195 json::Array &ReferencesArrayRef = *ReferencesArray.getAsArray();
196 ReferencesArrayRef.reserve(S: References.size());
197 for (const auto &Reference : References) {
198 json::Value ReferenceVal = Object();
199 auto &ReferenceObj = *ReferenceVal.getAsObject();
200 serializeReference(Ref: Reference, ReferenceObj);
201 ReferencesArrayRef.push_back(E: ReferenceVal);
202 }
203 Obj[Key] = ReferencesArray;
204}
205
206// Although namespaces and records both have ScopeChildren, they serialize them
207// differently. Only enums, records, and typedefs are handled here.
208static void serializeCommonChildren(const ScopeChildren &Children,
209 json::Object &Obj,
210 std::optional<StringRef> RepositoryUrl) {
211 if (!Children.Enums.empty()) {
212 json::Value EnumsArray = Array();
213 auto &EnumsArrayRef = *EnumsArray.getAsArray();
214 EnumsArrayRef.reserve(S: Children.Enums.size());
215 for (const auto &Enum : Children.Enums) {
216 json::Value EnumVal = Object();
217 auto &EnumObj = *EnumVal.getAsObject();
218 serializeInfo(I: Enum, Obj&: EnumObj, RepositoryUrl);
219 EnumsArrayRef.push_back(E: EnumVal);
220 }
221 Obj["Enums"] = EnumsArray;
222 }
223
224 if (!Children.Typedefs.empty()) {
225 json::Value TypedefsArray = Array();
226 auto &TypedefsArrayRef = *TypedefsArray.getAsArray();
227 TypedefsArrayRef.reserve(S: Children.Typedefs.size());
228 for (const auto &Typedef : Children.Typedefs) {
229 json::Value TypedefVal = Object();
230 auto &TypedefObj = *TypedefVal.getAsObject();
231 serializeInfo(I: Typedef, Obj&: TypedefObj, RepositoryUrl);
232 TypedefsArrayRef.push_back(E: TypedefVal);
233 }
234 Obj["Typedefs"] = TypedefsArray;
235 }
236
237 if (!Children.Records.empty()) {
238 json::Value RecordsArray = Array();
239 auto &RecordsArrayRef = *RecordsArray.getAsArray();
240 RecordsArrayRef.reserve(S: Children.Records.size());
241 for (const auto &Record : Children.Records) {
242 json::Value RecordVal = Object();
243 auto &RecordObj = *RecordVal.getAsObject();
244 serializeReference(Ref: Record, ReferenceObj&: RecordObj);
245 RecordsArrayRef.push_back(E: RecordVal);
246 }
247 Obj["Records"] = RecordsArray;
248 }
249}
250
251static void serializeInfo(const TemplateInfo &Template, Object &Obj) {
252 json::Value TemplateVal = Object();
253 auto &TemplateObj = *TemplateVal.getAsObject();
254
255 if (Template.Specialization) {
256 json::Value TemplateSpecializationVal = Object();
257 auto &TemplateSpecializationObj = *TemplateSpecializationVal.getAsObject();
258 TemplateSpecializationObj["SpecializationOf"] =
259 toHex(Input: toStringRef(Input: Template.Specialization->SpecializationOf));
260 if (!Template.Specialization->Params.empty()) {
261 json::Value ParamsArray = Array();
262 auto &ParamsArrayRef = *ParamsArray.getAsArray();
263 ParamsArrayRef.reserve(S: Template.Specialization->Params.size());
264 for (const auto &Param : Template.Specialization->Params)
265 ParamsArrayRef.push_back(E: Param.Contents);
266 TemplateSpecializationObj["Parameters"] = ParamsArray;
267 }
268 TemplateObj["Specialization"] = TemplateSpecializationVal;
269 }
270
271 if (!Template.Params.empty()) {
272 json::Value ParamsArray = Array();
273 auto &ParamsArrayRef = *ParamsArray.getAsArray();
274 ParamsArrayRef.reserve(S: Template.Params.size());
275 for (const auto &Param : Template.Params)
276 ParamsArrayRef.push_back(E: Param.Contents);
277 TemplateObj["Parameters"] = ParamsArray;
278 }
279
280 Obj["Template"] = TemplateVal;
281}
282
283static void serializeInfo(const TypeInfo &I, Object &Obj) {
284 Obj["Name"] = I.Type.Name;
285 Obj["QualName"] = I.Type.QualName;
286 Obj["USR"] = toHex(Input: toStringRef(Input: I.Type.USR));
287 Obj["IsTemplate"] = I.IsTemplate;
288 Obj["IsBuiltIn"] = I.IsBuiltIn;
289}
290
291static void serializeInfo(const FunctionInfo &F, json::Object &Obj,
292 std::optional<StringRef> RepositoryURL) {
293 serializeCommonAttributes(I: F, Obj, RepositoryUrl: RepositoryURL);
294 Obj["IsStatic"] = F.IsStatic;
295
296 auto ReturnTypeObj = Object();
297 serializeInfo(I: F.ReturnType, Obj&: ReturnTypeObj);
298 Obj["ReturnType"] = std::move(ReturnTypeObj);
299
300 if (!F.Params.empty()) {
301 json::Value ParamsArray = json::Array();
302 auto &ParamsArrayRef = *ParamsArray.getAsArray();
303 ParamsArrayRef.reserve(S: F.Params.size());
304 for (const auto &Param : F.Params) {
305 json::Value ParamVal = Object();
306 auto &ParamObj = *ParamVal.getAsObject();
307 ParamObj["Name"] = Param.Name;
308 ParamObj["Type"] = Param.Type.Name;
309 ParamsArrayRef.push_back(E: ParamVal);
310 }
311 Obj["Params"] = ParamsArray;
312 }
313
314 if (F.Template)
315 serializeInfo(Template: F.Template.value(), Obj);
316}
317
318static void serializeInfo(const EnumInfo &I, json::Object &Obj,
319 std::optional<StringRef> RepositoryUrl) {
320 serializeCommonAttributes(I, Obj, RepositoryUrl);
321 Obj["Scoped"] = I.Scoped;
322
323 if (I.BaseType) {
324 json::Value BaseTypeVal = Object();
325 auto &BaseTypeObj = *BaseTypeVal.getAsObject();
326 BaseTypeObj["Name"] = I.BaseType->Type.Name;
327 BaseTypeObj["QualName"] = I.BaseType->Type.QualName;
328 BaseTypeObj["USR"] = toHex(Input: toStringRef(Input: I.BaseType->Type.USR));
329 Obj["BaseType"] = BaseTypeVal;
330 }
331
332 if (!I.Members.empty()) {
333 json::Value MembersArray = Array();
334 auto &MembersArrayRef = *MembersArray.getAsArray();
335 MembersArrayRef.reserve(S: I.Members.size());
336 for (const auto &Member : I.Members) {
337 json::Value MemberVal = Object();
338 auto &MemberObj = *MemberVal.getAsObject();
339 MemberObj["Name"] = Member.Name;
340 if (!Member.ValueExpr.empty())
341 MemberObj["ValueExpr"] = Member.ValueExpr;
342 else
343 MemberObj["Value"] = Member.Value;
344 MembersArrayRef.push_back(E: MemberVal);
345 }
346 Obj["Members"] = MembersArray;
347 }
348}
349
350static void serializeInfo(const TypedefInfo &I, json::Object &Obj,
351 std::optional<StringRef> RepositoryUrl) {
352 serializeCommonAttributes(I, Obj, RepositoryUrl);
353 Obj["TypeDeclaration"] = I.TypeDeclaration;
354 Obj["IsUsing"] = I.IsUsing;
355 json::Value TypeVal = Object();
356 auto &TypeObj = *TypeVal.getAsObject();
357 serializeInfo(I: I.Underlying, Obj&: TypeObj);
358 Obj["Underlying"] = TypeVal;
359}
360
361static void serializeInfo(const RecordInfo &I, json::Object &Obj,
362 std::optional<StringRef> RepositoryUrl) {
363 serializeCommonAttributes(I, Obj, RepositoryUrl);
364 Obj["FullName"] = I.FullName;
365 Obj["TagType"] = getTagType(AS: I.TagType);
366 Obj["IsTypedef"] = I.IsTypeDef;
367
368 if (!I.Children.Functions.empty()) {
369 json::Value PubFunctionsArray = Array();
370 json::Array &PubFunctionsArrayRef = *PubFunctionsArray.getAsArray();
371 json::Value ProtFunctionsArray = Array();
372 json::Array &ProtFunctionsArrayRef = *ProtFunctionsArray.getAsArray();
373
374 for (const auto &Function : I.Children.Functions) {
375 json::Value FunctionVal = Object();
376 auto &FunctionObj = *FunctionVal.getAsObject();
377 serializeInfo(F: Function, Obj&: FunctionObj, RepositoryURL: RepositoryUrl);
378 AccessSpecifier Access = Function.Access;
379 if (Access == AccessSpecifier::AS_public)
380 PubFunctionsArrayRef.push_back(E: FunctionVal);
381 else if (Access == AccessSpecifier::AS_protected)
382 ProtFunctionsArrayRef.push_back(E: FunctionVal);
383 }
384
385 if (!PubFunctionsArrayRef.empty())
386 Obj["PublicFunctions"] = PubFunctionsArray;
387 if (!ProtFunctionsArrayRef.empty())
388 Obj["ProtectedFunctions"] = ProtFunctionsArray;
389 }
390
391 if (!I.Members.empty()) {
392 json::Value PublicMembersArray = Array();
393 json::Array &PubMembersArrayRef = *PublicMembersArray.getAsArray();
394 json::Value ProtectedMembersArray = Array();
395 json::Array &ProtMembersArrayRef = *ProtectedMembersArray.getAsArray();
396
397 for (const MemberTypeInfo &Member : I.Members) {
398 json::Value MemberVal = Object();
399 auto &MemberObj = *MemberVal.getAsObject();
400 MemberObj["Name"] = Member.Name;
401 MemberObj["Type"] = Member.Type.Name;
402
403 if (Member.Access == AccessSpecifier::AS_public)
404 PubMembersArrayRef.push_back(E: MemberVal);
405 else if (Member.Access == AccessSpecifier::AS_protected)
406 ProtMembersArrayRef.push_back(E: MemberVal);
407 }
408
409 if (!PubMembersArrayRef.empty())
410 Obj["PublicMembers"] = PublicMembersArray;
411 if (!ProtMembersArrayRef.empty())
412 Obj["ProtectedMembers"] = ProtectedMembersArray;
413 }
414
415 if (!I.Bases.empty()) {
416 json::Value BasesArray = Array();
417 json::Array &BasesArrayRef = *BasesArray.getAsArray();
418 BasesArrayRef.reserve(S: I.Bases.size());
419 for (const auto &BaseInfo : I.Bases) {
420 json::Value BaseInfoVal = Object();
421 auto &BaseInfoObj = *BaseInfoVal.getAsObject();
422 serializeInfo(I: BaseInfo, Obj&: BaseInfoObj, RepositoryUrl);
423 BaseInfoObj["IsVirtual"] = BaseInfo.IsVirtual;
424 BaseInfoObj["Access"] = getAccessSpelling(AS: BaseInfo.Access);
425 BaseInfoObj["IsParent"] = BaseInfo.IsParent;
426 BasesArrayRef.push_back(E: BaseInfoVal);
427 }
428 Obj["Bases"] = BasesArray;
429 }
430
431 if (!I.Parents.empty())
432 serializeReference(References: I.Parents, Obj, Key: "Parents");
433
434 if (!I.VirtualParents.empty())
435 serializeReference(References: I.VirtualParents, Obj, Key: "VirtualParents");
436
437 if (I.Template)
438 serializeInfo(Template: I.Template.value(), Obj);
439
440 serializeCommonChildren(Children: I.Children, Obj, RepositoryUrl);
441}
442
443static void serializeInfo(const NamespaceInfo &I, json::Object &Obj,
444 std::optional<StringRef> RepositoryUrl) {
445 serializeCommonAttributes(I, Obj, RepositoryUrl);
446
447 if (!I.Children.Namespaces.empty()) {
448 json::Value NamespacesArray = Array();
449 auto &NamespacesArrayRef = *NamespacesArray.getAsArray();
450 NamespacesArrayRef.reserve(S: I.Children.Namespaces.size());
451 for (auto &Namespace : I.Children.Namespaces) {
452 json::Value NamespaceVal = Object();
453 auto &NamespaceObj = *NamespaceVal.getAsObject();
454 serializeReference(Ref: Namespace, ReferenceObj&: NamespaceObj);
455 NamespacesArrayRef.push_back(E: NamespaceVal);
456 }
457 Obj["Namespaces"] = NamespacesArray;
458 }
459
460 if (!I.Children.Functions.empty()) {
461 json::Value FunctionsArray = Array();
462 auto &FunctionsArrayRef = *FunctionsArray.getAsArray();
463 FunctionsArrayRef.reserve(S: I.Children.Functions.size());
464 for (const auto &Function : I.Children.Functions) {
465 json::Value FunctionVal = Object();
466 auto &FunctionObj = *FunctionVal.getAsObject();
467 serializeInfo(F: Function, Obj&: FunctionObj, RepositoryURL: RepositoryUrl);
468 FunctionsArrayRef.push_back(E: FunctionVal);
469 }
470 Obj["Functions"] = FunctionsArray;
471 }
472
473 serializeCommonChildren(Children: I.Children, Obj, RepositoryUrl);
474}
475
476Error JSONGenerator::generateDocs(
477 StringRef RootDir, llvm::StringMap<std::unique_ptr<doc::Info>> Infos,
478 const ClangDocContext &CDCtx) {
479 StringSet<> CreatedDirs;
480 StringMap<std::vector<doc::Info *>> FileToInfos;
481 for (const auto &Group : Infos) {
482 Info *Info = Group.getValue().get();
483
484 SmallString<128> Path;
485 sys::path::native(path: RootDir, result&: Path);
486 sys::path::append(path&: Path, a: Info->getRelativeFilePath(CurrentPath: ""));
487 if (!CreatedDirs.contains(key: Path)) {
488 if (std::error_code Err = sys::fs::create_directories(path: Path);
489 Err != std::error_code())
490 return createFileError(F: Twine(Path), EC: Err);
491 CreatedDirs.insert(key: Path);
492 }
493
494 sys::path::append(path&: Path, a: Info->getFileBaseName() + ".json");
495 FileToInfos[Path].push_back(x: Info);
496 }
497
498 for (const auto &Group : FileToInfos) {
499 std::error_code FileErr;
500 raw_fd_ostream InfoOS(Group.getKey(), FileErr, sys::fs::OF_Text);
501 if (FileErr)
502 return createFileError(F: "cannot open file " + Group.getKey(), EC: FileErr);
503
504 for (const auto &Info : Group.getValue())
505 if (Error Err = generateDocForInfo(I: Info, OS&: InfoOS, CDCtx))
506 return Err;
507 }
508
509 return Error::success();
510}
511
512Error JSONGenerator::generateDocForInfo(Info *I, raw_ostream &OS,
513 const ClangDocContext &CDCtx) {
514 json::Object Obj = Object();
515
516 switch (I->IT) {
517 case InfoType::IT_namespace:
518 serializeInfo(I: *static_cast<NamespaceInfo *>(I), Obj, RepositoryUrl: CDCtx.RepositoryUrl);
519 break;
520 case InfoType::IT_record:
521 serializeInfo(I: *static_cast<RecordInfo *>(I), Obj, RepositoryUrl: CDCtx.RepositoryUrl);
522 break;
523 case InfoType::IT_enum:
524 case InfoType::IT_function:
525 case InfoType::IT_typedef:
526 break;
527 case InfoType::IT_default:
528 return createStringError(EC: inconvertibleErrorCode(), S: "unexpected info type");
529 }
530 OS << llvm::formatv(Fmt: "{0:2}", Vals: llvm::json::Value(std::move(Obj)));
531 return Error::success();
532}
533
534Error JSONGenerator::createResources(ClangDocContext &CDCtx) {
535 return Error::success();
536}
537
538static GeneratorRegistry::Add<JSONGenerator> JSON(JSONGenerator::Format,
539 "Generator for JSON output.");
540volatile int JSONGeneratorAnchorSource = 0;
541} // namespace doc
542} // namespace clang
543

source code of clang-tools-extra/clang-doc/JSONGenerator.cpp