1//===- ExtractAPI/Serialization/SymbolGraphSerializer.cpp -------*- C++ -*-===//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8///
9/// \file
10/// This file implements the SymbolGraphSerializer.
11///
12//===----------------------------------------------------------------------===//
13
14#include "clang/ExtractAPI/Serialization/SymbolGraphSerializer.h"
15#include "clang/Basic/SourceLocation.h"
16#include "clang/Basic/Version.h"
17#include "clang/ExtractAPI/API.h"
18#include "clang/ExtractAPI/DeclarationFragments.h"
19#include "llvm/ADT/STLExtras.h"
20#include "llvm/ADT/STLFunctionalExtras.h"
21#include "llvm/ADT/SmallVector.h"
22#include "llvm/Support/Casting.h"
23#include "llvm/Support/Compiler.h"
24#include "llvm/Support/Path.h"
25#include "llvm/Support/VersionTuple.h"
26#include "llvm/Support/raw_ostream.h"
27#include <iterator>
28#include <optional>
29#include <type_traits>
30
31using namespace clang;
32using namespace clang::extractapi;
33using namespace llvm;
34
35namespace {
36
37/// Helper function to inject a JSON object \p Obj into another object \p Paren
38/// at position \p Key.
39void serializeObject(Object &Paren, StringRef Key,
40 std::optional<Object> &&Obj) {
41 if (Obj)
42 Paren[Key] = std::move(*Obj);
43}
44
45/// Helper function to inject a JSON array \p Array into object \p Paren at
46/// position \p Key.
47void serializeArray(Object &Paren, StringRef Key,
48 std::optional<Array> &&Array) {
49 if (Array)
50 Paren[Key] = std::move(*Array);
51}
52
53/// Helper function to inject a JSON array composed of the values in \p C into
54/// object \p Paren at position \p Key.
55template <typename ContainerTy>
56void serializeArray(Object &Paren, StringRef Key, ContainerTy &&C) {
57 Paren[Key] = Array(C);
58}
59
60/// Serialize a \c VersionTuple \p V with the Symbol Graph semantic version
61/// format.
62///
63/// A semantic version object contains three numeric fields, representing the
64/// \c major, \c minor, and \c patch parts of the version tuple.
65/// For example version tuple 1.0.3 is serialized as:
66/// \code
67/// {
68/// "major" : 1,
69/// "minor" : 0,
70/// "patch" : 3
71/// }
72/// \endcode
73///
74/// \returns \c std::nullopt if the version \p V is empty, or an \c Object
75/// containing the semantic version representation of \p V.
76std::optional<Object> serializeSemanticVersion(const VersionTuple &V) {
77 if (V.empty())
78 return std::nullopt;
79
80 Object Version;
81 Version["major"] = V.getMajor();
82 Version["minor"] = V.getMinor().value_or(u: 0);
83 Version["patch"] = V.getSubminor().value_or(u: 0);
84 return Version;
85}
86
87/// Serialize the OS information in the Symbol Graph platform property.
88///
89/// The OS information in Symbol Graph contains the \c name of the OS, and an
90/// optional \c minimumVersion semantic version field.
91Object serializeOperatingSystem(const Triple &T) {
92 Object OS;
93 OS["name"] = T.getOSTypeName(Kind: T.getOS());
94 serializeObject(Paren&: OS, Key: "minimumVersion",
95 Obj: serializeSemanticVersion(V: T.getMinimumSupportedOSVersion()));
96 return OS;
97}
98
99/// Serialize the platform information in the Symbol Graph module section.
100///
101/// The platform object describes a target platform triple in corresponding
102/// three fields: \c architecture, \c vendor, and \c operatingSystem.
103Object serializePlatform(const Triple &T) {
104 Object Platform;
105 Platform["architecture"] = T.getArchName();
106 Platform["vendor"] = T.getVendorName();
107 Platform["operatingSystem"] = serializeOperatingSystem(T);
108 return Platform;
109}
110
111/// Serialize a source position.
112Object serializeSourcePosition(const PresumedLoc &Loc) {
113 assert(Loc.isValid() && "invalid source position");
114
115 Object SourcePosition;
116 SourcePosition["line"] = Loc.getLine() - 1;
117 SourcePosition["character"] = Loc.getColumn() - 1;
118
119 return SourcePosition;
120}
121
122/// Serialize a source location in file.
123///
124/// \param Loc The presumed location to serialize.
125/// \param IncludeFileURI If true, include the file path of \p Loc as a URI.
126/// Defaults to false.
127Object serializeSourceLocation(const PresumedLoc &Loc,
128 bool IncludeFileURI = false) {
129 Object SourceLocation;
130 serializeObject(Paren&: SourceLocation, Key: "position", Obj: serializeSourcePosition(Loc));
131
132 if (IncludeFileURI) {
133 std::string FileURI = "file://";
134 // Normalize file path to use forward slashes for the URI.
135 FileURI += sys::path::convert_to_slash(path: Loc.getFilename());
136 SourceLocation["uri"] = FileURI;
137 }
138
139 return SourceLocation;
140}
141
142/// Serialize a source range with begin and end locations.
143Object serializeSourceRange(const PresumedLoc &BeginLoc,
144 const PresumedLoc &EndLoc) {
145 Object SourceRange;
146 serializeObject(Paren&: SourceRange, Key: "start", Obj: serializeSourcePosition(Loc: BeginLoc));
147 serializeObject(Paren&: SourceRange, Key: "end", Obj: serializeSourcePosition(Loc: EndLoc));
148 return SourceRange;
149}
150
151/// Serialize the availability attributes of a symbol.
152///
153/// Availability information contains the introduced, deprecated, and obsoleted
154/// versions of the symbol as semantic versions, if not default.
155/// Availability information also contains flags to indicate if the symbol is
156/// unconditionally unavailable or deprecated,
157/// i.e. \c __attribute__((unavailable)) and \c __attribute__((deprecated)).
158///
159/// \returns \c std::nullopt if the symbol has default availability attributes,
160/// or an \c Array containing an object with the formatted availability
161/// information.
162std::optional<Array> serializeAvailability(const AvailabilityInfo &Avail) {
163 if (Avail.isDefault())
164 return std::nullopt;
165
166 Array AvailabilityArray;
167
168 if (Avail.isUnconditionallyDeprecated()) {
169 Object UnconditionallyDeprecated;
170 UnconditionallyDeprecated["domain"] = "*";
171 UnconditionallyDeprecated["isUnconditionallyDeprecated"] = true;
172 AvailabilityArray.emplace_back(A: std::move(UnconditionallyDeprecated));
173 }
174 Object Availability;
175
176 Availability["domain"] = Avail.Domain;
177
178 if (Avail.isUnavailable()) {
179 Availability["isUnconditionallyUnavailable"] = true;
180 } else {
181 serializeObject(Paren&: Availability, Key: "introduced",
182 Obj: serializeSemanticVersion(V: Avail.Introduced));
183 serializeObject(Paren&: Availability, Key: "deprecated",
184 Obj: serializeSemanticVersion(V: Avail.Deprecated));
185 serializeObject(Paren&: Availability, Key: "obsoleted",
186 Obj: serializeSemanticVersion(V: Avail.Obsoleted));
187 }
188
189 AvailabilityArray.emplace_back(A: std::move(Availability));
190 return AvailabilityArray;
191}
192
193/// Get the language name string for interface language references.
194StringRef getLanguageName(Language Lang) {
195 switch (Lang) {
196 case Language::C:
197 return "c";
198 case Language::ObjC:
199 return "objective-c";
200 case Language::CXX:
201 return "c++";
202 case Language::ObjCXX:
203 return "objective-c++";
204
205 // Unsupported language currently
206 case Language::OpenCL:
207 case Language::OpenCLCXX:
208 case Language::CUDA:
209 case Language::RenderScript:
210 case Language::HIP:
211 case Language::HLSL:
212
213 // Languages that the frontend cannot parse and compile
214 case Language::Unknown:
215 case Language::Asm:
216 case Language::LLVM_IR:
217 case Language::CIR:
218 llvm_unreachable("Unsupported language kind");
219 }
220
221 llvm_unreachable("Unhandled language kind");
222}
223
224/// Serialize the identifier object as specified by the Symbol Graph format.
225///
226/// The identifier property of a symbol contains the USR for precise and unique
227/// references, and the interface language name.
228Object serializeIdentifier(const APIRecord &Record, Language Lang) {
229 Object Identifier;
230 Identifier["precise"] = Record.USR;
231 Identifier["interfaceLanguage"] = getLanguageName(Lang);
232
233 return Identifier;
234}
235
236/// Serialize the documentation comments attached to a symbol, as specified by
237/// the Symbol Graph format.
238///
239/// The Symbol Graph \c docComment object contains an array of lines. Each line
240/// represents one line of striped documentation comment, with source range
241/// information.
242/// e.g.
243/// \code
244/// /// This is a documentation comment
245/// ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~' First line.
246/// /// with multiple lines.
247/// ^~~~~~~~~~~~~~~~~~~~~~~' Second line.
248/// \endcode
249///
250/// \returns \c std::nullopt if \p Comment is empty, or an \c Object containing
251/// the formatted lines.
252std::optional<Object> serializeDocComment(const DocComment &Comment) {
253 if (Comment.empty())
254 return std::nullopt;
255
256 Object DocComment;
257
258 Array LinesArray;
259 for (const auto &CommentLine : Comment) {
260 Object Line;
261 Line["text"] = CommentLine.Text;
262 serializeObject(Paren&: Line, Key: "range",
263 Obj: serializeSourceRange(BeginLoc: CommentLine.Begin, EndLoc: CommentLine.End));
264 LinesArray.emplace_back(A: std::move(Line));
265 }
266
267 serializeArray(Paren&: DocComment, Key: "lines", C: std::move(LinesArray));
268
269 return DocComment;
270}
271
272/// Serialize the declaration fragments of a symbol.
273///
274/// The Symbol Graph declaration fragments is an array of tagged important
275/// parts of a symbol's declaration. The fragments sequence can be joined to
276/// form spans of declaration text, with attached information useful for
277/// purposes like syntax-highlighting etc. For example:
278/// \code
279/// const int pi; -> "declarationFragments" : [
280/// {
281/// "kind" : "keyword",
282/// "spelling" : "const"
283/// },
284/// {
285/// "kind" : "text",
286/// "spelling" : " "
287/// },
288/// {
289/// "kind" : "typeIdentifier",
290/// "preciseIdentifier" : "c:I",
291/// "spelling" : "int"
292/// },
293/// {
294/// "kind" : "text",
295/// "spelling" : " "
296/// },
297/// {
298/// "kind" : "identifier",
299/// "spelling" : "pi"
300/// }
301/// ]
302/// \endcode
303///
304/// \returns \c std::nullopt if \p DF is empty, or an \c Array containing the
305/// formatted declaration fragments array.
306std::optional<Array>
307serializeDeclarationFragments(const DeclarationFragments &DF) {
308 if (DF.getFragments().empty())
309 return std::nullopt;
310
311 Array Fragments;
312 for (const auto &F : DF.getFragments()) {
313 Object Fragment;
314 Fragment["spelling"] = F.Spelling;
315 Fragment["kind"] = DeclarationFragments::getFragmentKindString(Kind: F.Kind);
316 if (!F.PreciseIdentifier.empty())
317 Fragment["preciseIdentifier"] = F.PreciseIdentifier;
318 Fragments.emplace_back(A: std::move(Fragment));
319 }
320
321 return Fragments;
322}
323
324/// Serialize the \c names field of a symbol as specified by the Symbol Graph
325/// format.
326///
327/// The Symbol Graph names field contains multiple representations of a symbol
328/// that can be used for different applications:
329/// - \c title : The simple declared name of the symbol;
330/// - \c subHeading : An array of declaration fragments that provides tags,
331/// and potentially more tokens (for example the \c +/- symbol for
332/// Objective-C methods). Can be used as sub-headings for documentation.
333Object serializeNames(const APIRecord *Record) {
334 Object Names;
335 Names["title"] = Record->Name;
336
337 serializeArray(Paren&: Names, Key: "subHeading",
338 Array: serializeDeclarationFragments(DF: Record->SubHeading));
339 DeclarationFragments NavigatorFragments;
340 NavigatorFragments.append(Spelling: Record->Name,
341 Kind: DeclarationFragments::FragmentKind::Identifier,
342 /*PreciseIdentifier*/ "");
343 serializeArray(Paren&: Names, Key: "navigator",
344 Array: serializeDeclarationFragments(DF: NavigatorFragments));
345
346 return Names;
347}
348
349Object serializeSymbolKind(APIRecord::RecordKind RK, Language Lang) {
350 auto AddLangPrefix = [&Lang](StringRef S) -> std::string {
351 return (getLanguageName(Lang) + "." + S).str();
352 };
353
354 Object Kind;
355 switch (RK) {
356 case APIRecord::RK_Unknown:
357 Kind["identifier"] = AddLangPrefix("unknown");
358 Kind["displayName"] = "Unknown";
359 break;
360 case APIRecord::RK_Namespace:
361 Kind["identifier"] = AddLangPrefix("namespace");
362 Kind["displayName"] = "Namespace";
363 break;
364 case APIRecord::RK_GlobalFunction:
365 Kind["identifier"] = AddLangPrefix("func");
366 Kind["displayName"] = "Function";
367 break;
368 case APIRecord::RK_GlobalFunctionTemplate:
369 Kind["identifier"] = AddLangPrefix("func");
370 Kind["displayName"] = "Function Template";
371 break;
372 case APIRecord::RK_GlobalFunctionTemplateSpecialization:
373 Kind["identifier"] = AddLangPrefix("func");
374 Kind["displayName"] = "Function Template Specialization";
375 break;
376 case APIRecord::RK_GlobalVariableTemplate:
377 Kind["identifier"] = AddLangPrefix("var");
378 Kind["displayName"] = "Global Variable Template";
379 break;
380 case APIRecord::RK_GlobalVariableTemplateSpecialization:
381 Kind["identifier"] = AddLangPrefix("var");
382 Kind["displayName"] = "Global Variable Template Specialization";
383 break;
384 case APIRecord::RK_GlobalVariableTemplatePartialSpecialization:
385 Kind["identifier"] = AddLangPrefix("var");
386 Kind["displayName"] = "Global Variable Template Partial Specialization";
387 break;
388 case APIRecord::RK_GlobalVariable:
389 Kind["identifier"] = AddLangPrefix("var");
390 Kind["displayName"] = "Global Variable";
391 break;
392 case APIRecord::RK_EnumConstant:
393 Kind["identifier"] = AddLangPrefix("enum.case");
394 Kind["displayName"] = "Enumeration Case";
395 break;
396 case APIRecord::RK_Enum:
397 Kind["identifier"] = AddLangPrefix("enum");
398 Kind["displayName"] = "Enumeration";
399 break;
400 case APIRecord::RK_StructField:
401 Kind["identifier"] = AddLangPrefix("property");
402 Kind["displayName"] = "Instance Property";
403 break;
404 case APIRecord::RK_Struct:
405 Kind["identifier"] = AddLangPrefix("struct");
406 Kind["displayName"] = "Structure";
407 break;
408 case APIRecord::RK_UnionField:
409 Kind["identifier"] = AddLangPrefix("property");
410 Kind["displayName"] = "Instance Property";
411 break;
412 case APIRecord::RK_Union:
413 Kind["identifier"] = AddLangPrefix("union");
414 Kind["displayName"] = "Union";
415 break;
416 case APIRecord::RK_CXXField:
417 Kind["identifier"] = AddLangPrefix("property");
418 Kind["displayName"] = "Instance Property";
419 break;
420 case APIRecord::RK_StaticField:
421 Kind["identifier"] = AddLangPrefix("type.property");
422 Kind["displayName"] = "Type Property";
423 break;
424 case APIRecord::RK_ClassTemplate:
425 case APIRecord::RK_ClassTemplateSpecialization:
426 case APIRecord::RK_ClassTemplatePartialSpecialization:
427 case APIRecord::RK_CXXClass:
428 Kind["identifier"] = AddLangPrefix("class");
429 Kind["displayName"] = "Class";
430 break;
431 case APIRecord::RK_CXXMethodTemplate:
432 Kind["identifier"] = AddLangPrefix("method");
433 Kind["displayName"] = "Method Template";
434 break;
435 case APIRecord::RK_CXXMethodTemplateSpecialization:
436 Kind["identifier"] = AddLangPrefix("method");
437 Kind["displayName"] = "Method Template Specialization";
438 break;
439 case APIRecord::RK_CXXFieldTemplate:
440 Kind["identifier"] = AddLangPrefix("property");
441 Kind["displayName"] = "Template Property";
442 break;
443 case APIRecord::RK_Concept:
444 Kind["identifier"] = AddLangPrefix("concept");
445 Kind["displayName"] = "Concept";
446 break;
447 case APIRecord::RK_CXXStaticMethod:
448 Kind["identifier"] = AddLangPrefix("type.method");
449 Kind["displayName"] = "Static Method";
450 break;
451 case APIRecord::RK_CXXInstanceMethod:
452 Kind["identifier"] = AddLangPrefix("method");
453 Kind["displayName"] = "Instance Method";
454 break;
455 case APIRecord::RK_CXXConstructorMethod:
456 Kind["identifier"] = AddLangPrefix("method");
457 Kind["displayName"] = "Constructor";
458 break;
459 case APIRecord::RK_CXXDestructorMethod:
460 Kind["identifier"] = AddLangPrefix("method");
461 Kind["displayName"] = "Destructor";
462 break;
463 case APIRecord::RK_ObjCIvar:
464 Kind["identifier"] = AddLangPrefix("ivar");
465 Kind["displayName"] = "Instance Variable";
466 break;
467 case APIRecord::RK_ObjCInstanceMethod:
468 Kind["identifier"] = AddLangPrefix("method");
469 Kind["displayName"] = "Instance Method";
470 break;
471 case APIRecord::RK_ObjCClassMethod:
472 Kind["identifier"] = AddLangPrefix("type.method");
473 Kind["displayName"] = "Type Method";
474 break;
475 case APIRecord::RK_ObjCInstanceProperty:
476 Kind["identifier"] = AddLangPrefix("property");
477 Kind["displayName"] = "Instance Property";
478 break;
479 case APIRecord::RK_ObjCClassProperty:
480 Kind["identifier"] = AddLangPrefix("type.property");
481 Kind["displayName"] = "Type Property";
482 break;
483 case APIRecord::RK_ObjCInterface:
484 Kind["identifier"] = AddLangPrefix("class");
485 Kind["displayName"] = "Class";
486 break;
487 case APIRecord::RK_ObjCCategory:
488 Kind["identifier"] = AddLangPrefix("class.extension");
489 Kind["displayName"] = "Class Extension";
490 break;
491 case APIRecord::RK_ObjCProtocol:
492 Kind["identifier"] = AddLangPrefix("protocol");
493 Kind["displayName"] = "Protocol";
494 break;
495 case APIRecord::RK_MacroDefinition:
496 Kind["identifier"] = AddLangPrefix("macro");
497 Kind["displayName"] = "Macro";
498 break;
499 case APIRecord::RK_Typedef:
500 Kind["identifier"] = AddLangPrefix("typealias");
501 Kind["displayName"] = "Type Alias";
502 break;
503 default:
504 llvm_unreachable("API Record with uninstantiable kind");
505 }
506
507 return Kind;
508}
509
510/// Serialize the symbol kind information.
511///
512/// The Symbol Graph symbol kind property contains a shorthand \c identifier
513/// which is prefixed by the source language name, useful for tooling to parse
514/// the kind, and a \c displayName for rendering human-readable names.
515Object serializeSymbolKind(const APIRecord &Record, Language Lang) {
516 return serializeSymbolKind(RK: Record.KindForDisplay, Lang);
517}
518
519/// Serialize the function signature field, as specified by the
520/// Symbol Graph format.
521///
522/// The Symbol Graph function signature property contains two arrays.
523/// - The \c returns array is the declaration fragments of the return type;
524/// - The \c parameters array contains names and declaration fragments of the
525/// parameters.
526template <typename RecordTy>
527void serializeFunctionSignatureMixin(Object &Paren, const RecordTy &Record) {
528 const auto &FS = Record.Signature;
529 if (FS.empty())
530 return;
531
532 Object Signature;
533 serializeArray(Signature, "returns",
534 serializeDeclarationFragments(FS.getReturnType()));
535
536 Array Parameters;
537 for (const auto &P : FS.getParameters()) {
538 Object Parameter;
539 Parameter["name"] = P.Name;
540 serializeArray(Parameter, "declarationFragments",
541 serializeDeclarationFragments(P.Fragments));
542 Parameters.emplace_back(A: std::move(Parameter));
543 }
544
545 if (!Parameters.empty())
546 Signature["parameters"] = std::move(Parameters);
547
548 serializeObject(Paren, Key: "functionSignature", Obj: std::move(Signature));
549}
550
551template <typename RecordTy>
552void serializeTemplateMixin(Object &Paren, const RecordTy &Record) {
553 const auto &Template = Record.Templ;
554 if (Template.empty())
555 return;
556
557 Object Generics;
558 Array GenericParameters;
559 for (const auto &Param : Template.getParameters()) {
560 Object Parameter;
561 Parameter["name"] = Param.Name;
562 Parameter["index"] = Param.Index;
563 Parameter["depth"] = Param.Depth;
564 GenericParameters.emplace_back(A: std::move(Parameter));
565 }
566 if (!GenericParameters.empty())
567 Generics["parameters"] = std::move(GenericParameters);
568
569 Array GenericConstraints;
570 for (const auto &Constr : Template.getConstraints()) {
571 Object Constraint;
572 Constraint["kind"] = Constr.Kind;
573 Constraint["lhs"] = Constr.LHS;
574 Constraint["rhs"] = Constr.RHS;
575 GenericConstraints.emplace_back(A: std::move(Constraint));
576 }
577
578 if (!GenericConstraints.empty())
579 Generics["constraints"] = std::move(GenericConstraints);
580
581 serializeObject(Paren, Key: "swiftGenerics", Obj: Generics);
582}
583
584Array generateParentContexts(const SmallVectorImpl<SymbolReference> &Parents,
585 Language Lang) {
586 Array ParentContexts;
587
588 for (const auto &Parent : Parents) {
589 Object Elem;
590 Elem["usr"] = Parent.USR;
591 Elem["name"] = Parent.Name;
592 if (Parent.Record)
593 Elem["kind"] = serializeSymbolKind(RK: Parent.Record->KindForDisplay,
594 Lang)["identifier"];
595 else
596 Elem["kind"] =
597 serializeSymbolKind(RK: APIRecord::RK_Unknown, Lang)["identifier"];
598 ParentContexts.emplace_back(A: std::move(Elem));
599 }
600
601 return ParentContexts;
602}
603
604/// Walk the records parent information in reverse to generate a hierarchy
605/// suitable for serialization.
606SmallVector<SymbolReference, 8>
607generateHierarchyFromRecord(const APIRecord *Record) {
608 SmallVector<SymbolReference, 8> ReverseHierarchy;
609 for (const auto *Current = Record; Current != nullptr;
610 Current = Current->Parent.Record)
611 ReverseHierarchy.emplace_back(Args&: Current);
612
613 return SmallVector<SymbolReference, 8>(
614 std::make_move_iterator(i: ReverseHierarchy.rbegin()),
615 std::make_move_iterator(i: ReverseHierarchy.rend()));
616}
617
618SymbolReference getHierarchyReference(const APIRecord *Record,
619 const APISet &API) {
620 // If the parent is a category extended from internal module then we need to
621 // pretend this belongs to the associated interface.
622 if (auto *CategoryRecord = dyn_cast_or_null<ObjCCategoryRecord>(Val: Record)) {
623 return CategoryRecord->Interface;
624 // FIXME: TODO generate path components correctly for categories extending
625 // an external module.
626 }
627
628 return SymbolReference(Record);
629}
630
631} // namespace
632
633Object *ExtendedModule::addSymbol(Object &&Symbol) {
634 Symbols.emplace_back(A: std::move(Symbol));
635 return Symbols.back().getAsObject();
636}
637
638void ExtendedModule::addRelationship(Object &&Relationship) {
639 Relationships.emplace_back(A: std::move(Relationship));
640}
641
642/// Defines the format version emitted by SymbolGraphSerializer.
643const VersionTuple SymbolGraphSerializer::FormatVersion{0, 5, 3};
644
645Object SymbolGraphSerializer::serializeMetadata() const {
646 Object Metadata;
647 serializeObject(Paren&: Metadata, Key: "formatVersion",
648 Obj: serializeSemanticVersion(V: FormatVersion));
649 Metadata["generator"] = clang::getClangFullVersion();
650 return Metadata;
651}
652
653Object
654SymbolGraphSerializer::serializeModuleObject(StringRef ModuleName) const {
655 Object Module;
656 Module["name"] = ModuleName;
657 serializeObject(Paren&: Module, Key: "platform", Obj: serializePlatform(T: API.getTarget()));
658 return Module;
659}
660
661bool SymbolGraphSerializer::shouldSkip(const APIRecord *Record) const {
662 if (!Record)
663 return true;
664
665 // Skip unconditionally unavailable symbols
666 if (Record->Availability.isUnconditionallyUnavailable())
667 return true;
668
669 // Filter out symbols without a name as we can generate correct symbol graphs
670 // for them. In practice these are anonymous record types that aren't attached
671 // to a declaration.
672 if (auto *Tag = dyn_cast<TagRecord>(Val: Record)) {
673 if (Tag->IsEmbeddedInVarDeclarator)
674 return true;
675 }
676
677 // Filter out symbols prefixed with an underscored as they are understood to
678 // be symbols clients should not use.
679 if (Record->Name.starts_with(Prefix: "_"))
680 return true;
681
682 // Skip explicitly ignored symbols.
683 if (IgnoresList.shouldIgnore(SymbolName: Record->Name))
684 return true;
685
686 return false;
687}
688
689ExtendedModule &SymbolGraphSerializer::getModuleForCurrentSymbol() {
690 if (!ForceEmitToMainModule && ModuleForCurrentSymbol)
691 return *ModuleForCurrentSymbol;
692
693 return MainModule;
694}
695
696Array SymbolGraphSerializer::serializePathComponents(
697 const APIRecord *Record) const {
698 return Array(map_range(C: Hierarchy, F: [](auto Elt) { return Elt.Name; }));
699}
700
701StringRef SymbolGraphSerializer::getRelationshipString(RelationshipKind Kind) {
702 switch (Kind) {
703 case RelationshipKind::MemberOf:
704 return "memberOf";
705 case RelationshipKind::InheritsFrom:
706 return "inheritsFrom";
707 case RelationshipKind::ConformsTo:
708 return "conformsTo";
709 case RelationshipKind::ExtensionTo:
710 return "extensionTo";
711 }
712 llvm_unreachable("Unhandled relationship kind");
713}
714
715void SymbolGraphSerializer::serializeRelationship(RelationshipKind Kind,
716 const SymbolReference &Source,
717 const SymbolReference &Target,
718 ExtendedModule &Into) {
719 Object Relationship;
720 SmallString<64> TestRelLabel;
721 if (EmitSymbolLabelsForTesting) {
722 llvm::raw_svector_ostream OS(TestRelLabel);
723 OS << SymbolGraphSerializer::getRelationshipString(Kind) << " $ "
724 << Source.USR << " $ ";
725 if (Target.USR.empty())
726 OS << Target.Name;
727 else
728 OS << Target.USR;
729 Relationship["!testRelLabel"] = TestRelLabel;
730 }
731 Relationship["source"] = Source.USR;
732 Relationship["target"] = Target.USR;
733 Relationship["targetFallback"] = Target.Name;
734 Relationship["kind"] = SymbolGraphSerializer::getRelationshipString(Kind);
735
736 if (ForceEmitToMainModule)
737 MainModule.addRelationship(Relationship: std::move(Relationship));
738 else
739 Into.addRelationship(Relationship: std::move(Relationship));
740}
741
742StringRef SymbolGraphSerializer::getConstraintString(ConstraintKind Kind) {
743 switch (Kind) {
744 case ConstraintKind::Conformance:
745 return "conformance";
746 case ConstraintKind::ConditionalConformance:
747 return "conditionalConformance";
748 }
749 llvm_unreachable("Unhandled constraint kind");
750}
751
752void SymbolGraphSerializer::serializeAPIRecord(const APIRecord *Record) {
753 Object Obj;
754
755 // If we need symbol labels for testing emit the USR as the value and the key
756 // starts with '!'' to ensure it ends up at the top of the object.
757 if (EmitSymbolLabelsForTesting)
758 Obj["!testLabel"] = Record->USR;
759
760 serializeObject(Paren&: Obj, Key: "identifier",
761 Obj: serializeIdentifier(Record: *Record, Lang: API.getLanguage()));
762 serializeObject(Paren&: Obj, Key: "kind", Obj: serializeSymbolKind(Record: *Record, Lang: API.getLanguage()));
763 serializeObject(Paren&: Obj, Key: "names", Obj: serializeNames(Record));
764 serializeObject(
765 Paren&: Obj, Key: "location",
766 Obj: serializeSourceLocation(Loc: Record->Location, /*IncludeFileURI=*/true));
767 serializeArray(Paren&: Obj, Key: "availability",
768 Array: serializeAvailability(Avail: Record->Availability));
769 serializeObject(Paren&: Obj, Key: "docComment", Obj: serializeDocComment(Comment: Record->Comment));
770 serializeArray(Paren&: Obj, Key: "declarationFragments",
771 Array: serializeDeclarationFragments(DF: Record->Declaration));
772
773 Obj["pathComponents"] = serializePathComponents(Record);
774 Obj["accessLevel"] = Record->Access.getAccess();
775
776 ExtendedModule &Module = getModuleForCurrentSymbol();
777 // If the hierarchy has at least one parent and child.
778 if (Hierarchy.size() >= 2)
779 serializeRelationship(Kind: MemberOf, Source: Hierarchy.back(),
780 Target: Hierarchy[Hierarchy.size() - 2], Into&: Module);
781
782 CurrentSymbol = Module.addSymbol(Symbol: std::move(Obj));
783}
784
785bool SymbolGraphSerializer::traverseAPIRecord(const APIRecord *Record) {
786 if (!Record)
787 return true;
788 if (shouldSkip(Record))
789 return true;
790 Hierarchy.push_back(Elt: getHierarchyReference(Record, API));
791 // Defer traversal mechanics to APISetVisitor base implementation
792 auto RetVal = Base::traverseAPIRecord(Record);
793 Hierarchy.pop_back();
794 return RetVal;
795}
796
797bool SymbolGraphSerializer::visitAPIRecord(const APIRecord *Record) {
798 serializeAPIRecord(Record);
799 return true;
800}
801
802bool SymbolGraphSerializer::visitGlobalFunctionRecord(
803 const GlobalFunctionRecord *Record) {
804 if (!CurrentSymbol)
805 return true;
806
807 serializeFunctionSignatureMixin(Paren&: *CurrentSymbol, Record: *Record);
808 return true;
809}
810
811bool SymbolGraphSerializer::visitCXXClassRecord(const CXXClassRecord *Record) {
812 if (!CurrentSymbol)
813 return true;
814
815 for (const auto &Base : Record->Bases)
816 serializeRelationship(Kind: RelationshipKind::InheritsFrom, Source: Record, Target: Base,
817 Into&: getModuleForCurrentSymbol());
818 return true;
819}
820
821bool SymbolGraphSerializer::visitClassTemplateRecord(
822 const ClassTemplateRecord *Record) {
823 if (!CurrentSymbol)
824 return true;
825
826 serializeTemplateMixin(Paren&: *CurrentSymbol, Record: *Record);
827 return true;
828}
829
830bool SymbolGraphSerializer::visitClassTemplatePartialSpecializationRecord(
831 const ClassTemplatePartialSpecializationRecord *Record) {
832 if (!CurrentSymbol)
833 return true;
834
835 serializeTemplateMixin(Paren&: *CurrentSymbol, Record: *Record);
836 return true;
837}
838
839bool SymbolGraphSerializer::visitCXXMethodRecord(
840 const CXXMethodRecord *Record) {
841 if (!CurrentSymbol)
842 return true;
843
844 serializeFunctionSignatureMixin(Paren&: *CurrentSymbol, Record: *Record);
845 return true;
846}
847
848bool SymbolGraphSerializer::visitCXXMethodTemplateRecord(
849 const CXXMethodTemplateRecord *Record) {
850 if (!CurrentSymbol)
851 return true;
852
853 serializeTemplateMixin(Paren&: *CurrentSymbol, Record: *Record);
854 return true;
855}
856
857bool SymbolGraphSerializer::visitCXXFieldTemplateRecord(
858 const CXXFieldTemplateRecord *Record) {
859 if (!CurrentSymbol)
860 return true;
861
862 serializeTemplateMixin(Paren&: *CurrentSymbol, Record: *Record);
863 return true;
864}
865
866bool SymbolGraphSerializer::visitConceptRecord(const ConceptRecord *Record) {
867 if (!CurrentSymbol)
868 return true;
869
870 serializeTemplateMixin(Paren&: *CurrentSymbol, Record: *Record);
871 return true;
872}
873
874bool SymbolGraphSerializer::visitGlobalVariableTemplateRecord(
875 const GlobalVariableTemplateRecord *Record) {
876 if (!CurrentSymbol)
877 return true;
878
879 serializeTemplateMixin(Paren&: *CurrentSymbol, Record: *Record);
880 return true;
881}
882
883bool SymbolGraphSerializer::
884 visitGlobalVariableTemplatePartialSpecializationRecord(
885 const GlobalVariableTemplatePartialSpecializationRecord *Record) {
886 if (!CurrentSymbol)
887 return true;
888
889 serializeTemplateMixin(Paren&: *CurrentSymbol, Record: *Record);
890 return true;
891}
892
893bool SymbolGraphSerializer::visitGlobalFunctionTemplateRecord(
894 const GlobalFunctionTemplateRecord *Record) {
895 if (!CurrentSymbol)
896 return true;
897
898 serializeTemplateMixin(Paren&: *CurrentSymbol, Record: *Record);
899 return true;
900}
901
902bool SymbolGraphSerializer::visitObjCContainerRecord(
903 const ObjCContainerRecord *Record) {
904 if (!CurrentSymbol)
905 return true;
906
907 for (const auto &Protocol : Record->Protocols)
908 serializeRelationship(Kind: ConformsTo, Source: Record, Target: Protocol,
909 Into&: getModuleForCurrentSymbol());
910
911 return true;
912}
913
914bool SymbolGraphSerializer::visitObjCInterfaceRecord(
915 const ObjCInterfaceRecord *Record) {
916 if (!CurrentSymbol)
917 return true;
918
919 if (!Record->SuperClass.empty())
920 serializeRelationship(Kind: InheritsFrom, Source: Record, Target: Record->SuperClass,
921 Into&: getModuleForCurrentSymbol());
922 return true;
923}
924
925bool SymbolGraphSerializer::traverseObjCCategoryRecord(
926 const ObjCCategoryRecord *Record) {
927 if (SkipSymbolsInCategoriesToExternalTypes &&
928 !API.findRecordForUSR(USR: Record->Interface.USR))
929 return true;
930
931 auto *CurrentModule = ModuleForCurrentSymbol;
932 if (Record->isExtendingExternalModule())
933 ModuleForCurrentSymbol = &ExtendedModules[Record->Interface.Source];
934
935 if (!walkUpFromObjCCategoryRecord(Record))
936 return false;
937
938 bool RetVal = traverseRecordContext(Record);
939 ModuleForCurrentSymbol = CurrentModule;
940 return RetVal;
941}
942
943bool SymbolGraphSerializer::walkUpFromObjCCategoryRecord(
944 const ObjCCategoryRecord *Record) {
945 return visitObjCCategoryRecord(Record);
946}
947
948bool SymbolGraphSerializer::visitObjCCategoryRecord(
949 const ObjCCategoryRecord *Record) {
950 // If we need to create a record for the category in the future do so here,
951 // otherwise everything is set up to pretend that the category is in fact the
952 // interface it extends.
953 for (const auto &Protocol : Record->Protocols)
954 serializeRelationship(Kind: ConformsTo, Source: Record->Interface, Target: Protocol,
955 Into&: getModuleForCurrentSymbol());
956
957 return true;
958}
959
960bool SymbolGraphSerializer::visitObjCMethodRecord(
961 const ObjCMethodRecord *Record) {
962 if (!CurrentSymbol)
963 return true;
964
965 serializeFunctionSignatureMixin(Paren&: *CurrentSymbol, Record: *Record);
966 return true;
967}
968
969bool SymbolGraphSerializer::visitObjCInstanceVariableRecord(
970 const ObjCInstanceVariableRecord *Record) {
971 // FIXME: serialize ivar access control here.
972 return true;
973}
974
975bool SymbolGraphSerializer::walkUpFromTypedefRecord(
976 const TypedefRecord *Record) {
977 // Short-circuit walking up the class hierarchy and handle creating typedef
978 // symbol objects manually as there are additional symbol dropping rules to
979 // respect.
980 return visitTypedefRecord(Record);
981}
982
983bool SymbolGraphSerializer::visitTypedefRecord(const TypedefRecord *Record) {
984 // Typedefs of anonymous types have their entries unified with the underlying
985 // type.
986 bool ShouldDrop = Record->UnderlyingType.Name.empty();
987 // enums declared with `NS_OPTION` have a named enum and a named typedef, with
988 // the same name
989 ShouldDrop |= (Record->UnderlyingType.Name == Record->Name);
990 if (ShouldDrop)
991 return true;
992
993 // Create the symbol record if the other symbol droppping rules permit it.
994 serializeAPIRecord(Record);
995 if (!CurrentSymbol)
996 return true;
997
998 (*CurrentSymbol)["type"] = Record->UnderlyingType.USR;
999
1000 return true;
1001}
1002
1003void SymbolGraphSerializer::serializeSingleRecord(const APIRecord *Record) {
1004 switch (Record->getKind()) {
1005 // dispatch to the relevant walkUpFromMethod
1006#define CONCRETE_RECORD(CLASS, BASE, KIND) \
1007 case APIRecord::KIND: { \
1008 walkUpFrom##CLASS(static_cast<const CLASS *>(Record)); \
1009 break; \
1010 }
1011#include "clang/ExtractAPI/APIRecords.inc"
1012 // otherwise fallback on the only behavior we can implement safely.
1013 case APIRecord::RK_Unknown:
1014 visitAPIRecord(Record);
1015 break;
1016 default:
1017 llvm_unreachable("API Record with uninstantiable kind");
1018 }
1019}
1020
1021Object SymbolGraphSerializer::serializeGraph(StringRef ModuleName,
1022 ExtendedModule &&EM) {
1023 Object Root;
1024 serializeObject(Paren&: Root, Key: "metadata", Obj: serializeMetadata());
1025 serializeObject(Paren&: Root, Key: "module", Obj: serializeModuleObject(ModuleName));
1026
1027 Root["symbols"] = std::move(EM.Symbols);
1028 Root["relationships"] = std::move(EM.Relationships);
1029
1030 return Root;
1031}
1032
1033void SymbolGraphSerializer::serializeGraphToStream(
1034 raw_ostream &OS, SymbolGraphSerializerOption Options, StringRef ModuleName,
1035 ExtendedModule &&EM) {
1036 Object Root = serializeGraph(ModuleName, EM: std::move(EM));
1037 if (Options.Compact)
1038 OS << formatv(Fmt: "{0}", Vals: json::Value(std::move(Root))) << "\n";
1039 else
1040 OS << formatv(Fmt: "{0:2}", Vals: json::Value(std::move(Root))) << "\n";
1041}
1042
1043void SymbolGraphSerializer::serializeMainSymbolGraph(
1044 raw_ostream &OS, const APISet &API, const APIIgnoresList &IgnoresList,
1045 SymbolGraphSerializerOption Options) {
1046 SymbolGraphSerializer Serializer(
1047 API, IgnoresList, Options.EmitSymbolLabelsForTesting,
1048 /*ForceEmitToMainModule=*/true,
1049 /*SkipSymbolsInCategoriesToExternalTypes=*/true);
1050
1051 Serializer.traverseAPISet();
1052 Serializer.serializeGraphToStream(OS, Options, ModuleName: API.ProductName,
1053 EM: std::move(Serializer.MainModule));
1054 // FIXME: TODO handle extended modules here
1055}
1056
1057void SymbolGraphSerializer::serializeWithExtensionGraphs(
1058 raw_ostream &MainOutput, const APISet &API,
1059 const APIIgnoresList &IgnoresList,
1060 llvm::function_ref<std::unique_ptr<llvm::raw_pwrite_stream>(Twine BaseName)>
1061 CreateOutputStream,
1062 SymbolGraphSerializerOption Options) {
1063 SymbolGraphSerializer Serializer(API, IgnoresList,
1064 Options.EmitSymbolLabelsForTesting);
1065 Serializer.traverseAPISet();
1066
1067 Serializer.serializeGraphToStream(OS&: MainOutput, Options, ModuleName: API.ProductName,
1068 EM: std::move(Serializer.MainModule));
1069
1070 for (auto &ExtensionSGF : Serializer.ExtendedModules) {
1071 if (auto ExtensionOS =
1072 CreateOutputStream(ExtensionSGF.getKey() + "@" + API.ProductName))
1073 Serializer.serializeGraphToStream(OS&: *ExtensionOS, Options,
1074 ModuleName: ExtensionSGF.getKey(),
1075 EM: std::move(ExtensionSGF.getValue()));
1076 }
1077}
1078
1079std::optional<Object>
1080SymbolGraphSerializer::serializeSingleSymbolSGF(StringRef USR,
1081 const APISet &API) {
1082 APIRecord *Record = API.findRecordForUSR(USR);
1083 if (!Record)
1084 return {};
1085
1086 Object Root;
1087 APIIgnoresList EmptyIgnores;
1088 SymbolGraphSerializer Serializer(API, EmptyIgnores,
1089 /*EmitSymbolLabelsForTesting*/ false,
1090 /*ForceEmitToMainModule*/ true);
1091
1092 // Set up serializer parent chain
1093 Serializer.Hierarchy = generateHierarchyFromRecord(Record);
1094
1095 Serializer.serializeSingleRecord(Record);
1096 serializeObject(Paren&: Root, Key: "symbolGraph",
1097 Obj: Serializer.serializeGraph(ModuleName: API.ProductName,
1098 EM: std::move(Serializer.MainModule)));
1099
1100 Language Lang = API.getLanguage();
1101 serializeArray(Paren&: Root, Key: "parentContexts",
1102 C: generateParentContexts(Parents: Serializer.Hierarchy, Lang));
1103
1104 Array RelatedSymbols;
1105
1106 for (const auto &Fragment : Record->Declaration.getFragments()) {
1107 // If we don't have a USR there isn't much we can do.
1108 if (Fragment.PreciseIdentifier.empty())
1109 continue;
1110
1111 APIRecord *RelatedRecord = API.findRecordForUSR(USR: Fragment.PreciseIdentifier);
1112
1113 // If we can't find the record let's skip.
1114 if (!RelatedRecord)
1115 continue;
1116
1117 Object RelatedSymbol;
1118 RelatedSymbol["usr"] = RelatedRecord->USR;
1119 RelatedSymbol["declarationLanguage"] = getLanguageName(Lang);
1120 RelatedSymbol["accessLevel"] = RelatedRecord->Access.getAccess();
1121 RelatedSymbol["filePath"] = RelatedRecord->Location.getFilename();
1122 RelatedSymbol["moduleName"] = API.ProductName;
1123 RelatedSymbol["isSystem"] = RelatedRecord->IsFromSystemHeader;
1124
1125 serializeArray(Paren&: RelatedSymbol, Key: "parentContexts",
1126 C: generateParentContexts(
1127 Parents: generateHierarchyFromRecord(Record: RelatedRecord), Lang));
1128
1129 RelatedSymbols.push_back(E: std::move(RelatedSymbol));
1130 }
1131
1132 serializeArray(Paren&: Root, Key: "relatedSymbols", C&: RelatedSymbols);
1133 return Root;
1134}
1135