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

source code of clang/lib/ExtractAPI/Serialization/SymbolGraphSerializer.cpp