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/APIIgnoresList.h"
19 #include "clang/ExtractAPI/DeclarationFragments.h"
20 #include "clang/ExtractAPI/Serialization/SerializerBase.h"
21 #include "llvm/ADT/STLExtras.h"
22 #include "llvm/ADT/STLFunctionalExtras.h"
23 #include "llvm/ADT/SmallVector.h"
24 #include "llvm/Support/Casting.h"
25 #include "llvm/Support/Compiler.h"
26 #include "llvm/Support/JSON.h"
27 #include "llvm/Support/Path.h"
28 #include "llvm/Support/VersionTuple.h"
29 #include <optional>
30 #include <type_traits>
31
32 using namespace clang;
33 using namespace clang::extractapi;
34 using namespace llvm;
35 using namespace llvm::json;
36
37 namespace {
38
39 /// Helper function to inject a JSON object \p Obj into another object \p Paren
40 /// at position \p Key.
serializeObject(Object & Paren,StringRef Key,std::optional<Object> Obj)41 void serializeObject(Object &Paren, StringRef Key, 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.
serializeArray(Object & Paren,StringRef Key,std::optional<Array> Array)48 void serializeArray(Object &Paren, StringRef Key, std::optional<Array> Array) {
49 if (Array)
50 Paren[Key] = std::move(*Array);
51 }
52
53 /// Serialize a \c VersionTuple \p V with the Symbol Graph semantic version
54 /// format.
55 ///
56 /// A semantic version object contains three numeric fields, representing the
57 /// \c major, \c minor, and \c patch parts of the version tuple.
58 /// For example version tuple 1.0.3 is serialized as:
59 /// \code
60 /// {
61 /// "major" : 1,
62 /// "minor" : 0,
63 /// "patch" : 3
64 /// }
65 /// \endcode
66 ///
67 /// \returns \c std::nullopt if the version \p V is empty, or an \c Object
68 /// containing the semantic version representation of \p V.
serializeSemanticVersion(const VersionTuple & V)69 std::optional<Object> serializeSemanticVersion(const VersionTuple &V) {
70 if (V.empty())
71 return std::nullopt;
72
73 Object Version;
74 Version["major"] = V.getMajor();
75 Version["minor"] = V.getMinor().value_or(0);
76 Version["patch"] = V.getSubminor().value_or(0);
77 return Version;
78 }
79
80 /// Serialize the OS information in the Symbol Graph platform property.
81 ///
82 /// The OS information in Symbol Graph contains the \c name of the OS, and an
83 /// optional \c minimumVersion semantic version field.
serializeOperatingSystem(const Triple & T)84 Object serializeOperatingSystem(const Triple &T) {
85 Object OS;
86 OS["name"] = T.getOSTypeName(T.getOS());
87 serializeObject(OS, "minimumVersion",
88 serializeSemanticVersion(T.getMinimumSupportedOSVersion()));
89 return OS;
90 }
91
92 /// Serialize the platform information in the Symbol Graph module section.
93 ///
94 /// The platform object describes a target platform triple in corresponding
95 /// three fields: \c architecture, \c vendor, and \c operatingSystem.
serializePlatform(const Triple & T)96 Object serializePlatform(const Triple &T) {
97 Object Platform;
98 Platform["architecture"] = T.getArchName();
99 Platform["vendor"] = T.getVendorName();
100 Platform["operatingSystem"] = serializeOperatingSystem(T);
101 return Platform;
102 }
103
104 /// Serialize a source position.
serializeSourcePosition(const PresumedLoc & Loc)105 Object serializeSourcePosition(const PresumedLoc &Loc) {
106 assert(Loc.isValid() && "invalid source position");
107
108 Object SourcePosition;
109 SourcePosition["line"] = Loc.getLine();
110 SourcePosition["character"] = Loc.getColumn();
111
112 return SourcePosition;
113 }
114
115 /// Serialize a source location in file.
116 ///
117 /// \param Loc The presumed location to serialize.
118 /// \param IncludeFileURI If true, include the file path of \p Loc as a URI.
119 /// Defaults to false.
serializeSourceLocation(const PresumedLoc & Loc,bool IncludeFileURI=false)120 Object serializeSourceLocation(const PresumedLoc &Loc,
121 bool IncludeFileURI = false) {
122 Object SourceLocation;
123 serializeObject(SourceLocation, "position", serializeSourcePosition(Loc));
124
125 if (IncludeFileURI) {
126 std::string FileURI = "file://";
127 // Normalize file path to use forward slashes for the URI.
128 FileURI += sys::path::convert_to_slash(Loc.getFilename());
129 SourceLocation["uri"] = FileURI;
130 }
131
132 return SourceLocation;
133 }
134
135 /// Serialize a source range with begin and end locations.
serializeSourceRange(const PresumedLoc & BeginLoc,const PresumedLoc & EndLoc)136 Object serializeSourceRange(const PresumedLoc &BeginLoc,
137 const PresumedLoc &EndLoc) {
138 Object SourceRange;
139 serializeObject(SourceRange, "start", serializeSourcePosition(BeginLoc));
140 serializeObject(SourceRange, "end", serializeSourcePosition(EndLoc));
141 return SourceRange;
142 }
143
144 /// Serialize the availability attributes of a symbol.
145 ///
146 /// Availability information contains the introduced, deprecated, and obsoleted
147 /// versions of the symbol for a given domain (roughly corresponds to a
148 /// platform) as semantic versions, if not default. Availability information
149 /// also contains flags to indicate if the symbol is unconditionally unavailable
150 /// or deprecated, i.e. \c __attribute__((unavailable)) and \c
151 /// __attribute__((deprecated)).
152 ///
153 /// \returns \c std::nullopt if the symbol has default availability attributes,
154 /// or an \c Array containing the formatted availability information.
155 std::optional<Array>
serializeAvailability(const AvailabilitySet & Availabilities)156 serializeAvailability(const AvailabilitySet &Availabilities) {
157 if (Availabilities.isDefault())
158 return std::nullopt;
159
160 Array AvailabilityArray;
161
162 if (Availabilities.isUnconditionallyDeprecated()) {
163 Object UnconditionallyDeprecated;
164 UnconditionallyDeprecated["domain"] = "*";
165 UnconditionallyDeprecated["isUnconditionallyDeprecated"] = true;
166 AvailabilityArray.emplace_back(std::move(UnconditionallyDeprecated));
167 }
168
169 // Note unconditionally unavailable records are skipped.
170
171 for (const auto &AvailInfo : Availabilities) {
172 Object Availability;
173 Availability["domain"] = AvailInfo.Domain;
174 serializeObject(Availability, "introducedVersion",
175 serializeSemanticVersion(AvailInfo.Introduced));
176 serializeObject(Availability, "deprecatedVersion",
177 serializeSemanticVersion(AvailInfo.Deprecated));
178 serializeObject(Availability, "obsoletedVersion",
179 serializeSemanticVersion(AvailInfo.Obsoleted));
180 AvailabilityArray.emplace_back(std::move(Availability));
181 }
182
183 return AvailabilityArray;
184 }
185
186 /// Get the language name string for interface language references.
getLanguageName(Language Lang)187 StringRef getLanguageName(Language Lang) {
188 switch (Lang) {
189 case Language::C:
190 return "c";
191 case Language::ObjC:
192 return "objective-c";
193
194 // Unsupported language currently
195 case Language::CXX:
196 case Language::ObjCXX:
197 case Language::OpenCL:
198 case Language::OpenCLCXX:
199 case Language::CUDA:
200 case Language::RenderScript:
201 case Language::HIP:
202 case Language::HLSL:
203
204 // Languages that the frontend cannot parse and compile
205 case Language::Unknown:
206 case Language::Asm:
207 case Language::LLVM_IR:
208 llvm_unreachable("Unsupported language kind");
209 }
210
211 llvm_unreachable("Unhandled language kind");
212 }
213
214 /// Serialize the identifier object as specified by the Symbol Graph format.
215 ///
216 /// The identifier property of a symbol contains the USR for precise and unique
217 /// references, and the interface language name.
serializeIdentifier(const APIRecord & Record,Language Lang)218 Object serializeIdentifier(const APIRecord &Record, Language Lang) {
219 Object Identifier;
220 Identifier["precise"] = Record.USR;
221 Identifier["interfaceLanguage"] = getLanguageName(Lang);
222
223 return Identifier;
224 }
225
226 /// Serialize the documentation comments attached to a symbol, as specified by
227 /// the Symbol Graph format.
228 ///
229 /// The Symbol Graph \c docComment object contains an array of lines. Each line
230 /// represents one line of striped documentation comment, with source range
231 /// information.
232 /// e.g.
233 /// \code
234 /// /// This is a documentation comment
235 /// ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~' First line.
236 /// /// with multiple lines.
237 /// ^~~~~~~~~~~~~~~~~~~~~~~' Second line.
238 /// \endcode
239 ///
240 /// \returns \c std::nullopt if \p Comment is empty, or an \c Object containing
241 /// the formatted lines.
serializeDocComment(const DocComment & Comment)242 std::optional<Object> serializeDocComment(const DocComment &Comment) {
243 if (Comment.empty())
244 return std::nullopt;
245
246 Object DocComment;
247 Array LinesArray;
248 for (const auto &CommentLine : Comment) {
249 Object Line;
250 Line["text"] = CommentLine.Text;
251 serializeObject(Line, "range",
252 serializeSourceRange(CommentLine.Begin, CommentLine.End));
253 LinesArray.emplace_back(std::move(Line));
254 }
255 serializeArray(DocComment, "lines", LinesArray);
256
257 return DocComment;
258 }
259
260 /// Serialize the declaration fragments of a symbol.
261 ///
262 /// The Symbol Graph declaration fragments is an array of tagged important
263 /// parts of a symbol's declaration. The fragments sequence can be joined to
264 /// form spans of declaration text, with attached information useful for
265 /// purposes like syntax-highlighting etc. For example:
266 /// \code
267 /// const int pi; -> "declarationFragments" : [
268 /// {
269 /// "kind" : "keyword",
270 /// "spelling" : "const"
271 /// },
272 /// {
273 /// "kind" : "text",
274 /// "spelling" : " "
275 /// },
276 /// {
277 /// "kind" : "typeIdentifier",
278 /// "preciseIdentifier" : "c:I",
279 /// "spelling" : "int"
280 /// },
281 /// {
282 /// "kind" : "text",
283 /// "spelling" : " "
284 /// },
285 /// {
286 /// "kind" : "identifier",
287 /// "spelling" : "pi"
288 /// }
289 /// ]
290 /// \endcode
291 ///
292 /// \returns \c std::nullopt if \p DF is empty, or an \c Array containing the
293 /// formatted declaration fragments array.
294 std::optional<Array>
serializeDeclarationFragments(const DeclarationFragments & DF)295 serializeDeclarationFragments(const DeclarationFragments &DF) {
296 if (DF.getFragments().empty())
297 return std::nullopt;
298
299 Array Fragments;
300 for (const auto &F : DF.getFragments()) {
301 Object Fragment;
302 Fragment["spelling"] = F.Spelling;
303 Fragment["kind"] = DeclarationFragments::getFragmentKindString(F.Kind);
304 if (!F.PreciseIdentifier.empty())
305 Fragment["preciseIdentifier"] = F.PreciseIdentifier;
306 Fragments.emplace_back(std::move(Fragment));
307 }
308
309 return Fragments;
310 }
311
312 /// Serialize the \c names field of a symbol as specified by the Symbol Graph
313 /// format.
314 ///
315 /// The Symbol Graph names field contains multiple representations of a symbol
316 /// that can be used for different applications:
317 /// - \c title : The simple declared name of the symbol;
318 /// - \c subHeading : An array of declaration fragments that provides tags,
319 /// and potentially more tokens (for example the \c +/- symbol for
320 /// Objective-C methods). Can be used as sub-headings for documentation.
serializeNames(const APIRecord & Record)321 Object serializeNames(const APIRecord &Record) {
322 Object Names;
323 Names["title"] = Record.Name;
324 serializeArray(Names, "subHeading",
325 serializeDeclarationFragments(Record.SubHeading));
326 DeclarationFragments NavigatorFragments;
327 NavigatorFragments.append(Record.Name,
328 DeclarationFragments::FragmentKind::Identifier,
329 /*PreciseIdentifier*/ "");
330 serializeArray(Names, "navigator",
331 serializeDeclarationFragments(NavigatorFragments));
332
333 return Names;
334 }
335
serializeSymbolKind(APIRecord::RecordKind RK,Language Lang)336 Object serializeSymbolKind(APIRecord::RecordKind RK, Language Lang) {
337 auto AddLangPrefix = [&Lang](StringRef S) -> std::string {
338 return (getLanguageName(Lang) + "." + S).str();
339 };
340
341 Object Kind;
342 switch (RK) {
343 case APIRecord::RK_Unknown:
344 llvm_unreachable("Records should have an explicit kind");
345 break;
346 case APIRecord::RK_GlobalFunction:
347 Kind["identifier"] = AddLangPrefix("func");
348 Kind["displayName"] = "Function";
349 break;
350 case APIRecord::RK_GlobalVariable:
351 Kind["identifier"] = AddLangPrefix("var");
352 Kind["displayName"] = "Global Variable";
353 break;
354 case APIRecord::RK_EnumConstant:
355 Kind["identifier"] = AddLangPrefix("enum.case");
356 Kind["displayName"] = "Enumeration Case";
357 break;
358 case APIRecord::RK_Enum:
359 Kind["identifier"] = AddLangPrefix("enum");
360 Kind["displayName"] = "Enumeration";
361 break;
362 case APIRecord::RK_StructField:
363 Kind["identifier"] = AddLangPrefix("property");
364 Kind["displayName"] = "Instance Property";
365 break;
366 case APIRecord::RK_Struct:
367 Kind["identifier"] = AddLangPrefix("struct");
368 Kind["displayName"] = "Structure";
369 break;
370 case APIRecord::RK_ObjCIvar:
371 Kind["identifier"] = AddLangPrefix("ivar");
372 Kind["displayName"] = "Instance Variable";
373 break;
374 case APIRecord::RK_ObjCInstanceMethod:
375 Kind["identifier"] = AddLangPrefix("method");
376 Kind["displayName"] = "Instance Method";
377 break;
378 case APIRecord::RK_ObjCClassMethod:
379 Kind["identifier"] = AddLangPrefix("type.method");
380 Kind["displayName"] = "Type Method";
381 break;
382 case APIRecord::RK_ObjCInstanceProperty:
383 Kind["identifier"] = AddLangPrefix("property");
384 Kind["displayName"] = "Instance Property";
385 break;
386 case APIRecord::RK_ObjCClassProperty:
387 Kind["identifier"] = AddLangPrefix("type.property");
388 Kind["displayName"] = "Type Property";
389 break;
390 case APIRecord::RK_ObjCInterface:
391 Kind["identifier"] = AddLangPrefix("class");
392 Kind["displayName"] = "Class";
393 break;
394 case APIRecord::RK_ObjCCategory:
395 // We don't serialize out standalone Objective-C category symbols yet.
396 llvm_unreachable("Serializing standalone Objective-C category symbols is "
397 "not supported.");
398 break;
399 case APIRecord::RK_ObjCProtocol:
400 Kind["identifier"] = AddLangPrefix("protocol");
401 Kind["displayName"] = "Protocol";
402 break;
403 case APIRecord::RK_MacroDefinition:
404 Kind["identifier"] = AddLangPrefix("macro");
405 Kind["displayName"] = "Macro";
406 break;
407 case APIRecord::RK_Typedef:
408 Kind["identifier"] = AddLangPrefix("typealias");
409 Kind["displayName"] = "Type Alias";
410 break;
411 }
412
413 return Kind;
414 }
415
416 /// Serialize the symbol kind information.
417 ///
418 /// The Symbol Graph symbol kind property contains a shorthand \c identifier
419 /// which is prefixed by the source language name, useful for tooling to parse
420 /// the kind, and a \c displayName for rendering human-readable names.
serializeSymbolKind(const APIRecord & Record,Language Lang)421 Object serializeSymbolKind(const APIRecord &Record, Language Lang) {
422 return serializeSymbolKind(Record.getKind(), Lang);
423 }
424
425 template <typename RecordTy>
426 std::optional<Object>
serializeFunctionSignatureMixinImpl(const RecordTy & Record,std::true_type)427 serializeFunctionSignatureMixinImpl(const RecordTy &Record, std::true_type) {
428 const auto &FS = Record.Signature;
429 if (FS.empty())
430 return std::nullopt;
431
432 Object Signature;
433 serializeArray(Signature, "returns",
434 serializeDeclarationFragments(FS.getReturnType()));
435
436 Array Parameters;
437 for (const auto &P : FS.getParameters()) {
438 Object Parameter;
439 Parameter["name"] = P.Name;
440 serializeArray(Parameter, "declarationFragments",
441 serializeDeclarationFragments(P.Fragments));
442 Parameters.emplace_back(std::move(Parameter));
443 }
444
445 if (!Parameters.empty())
446 Signature["parameters"] = std::move(Parameters);
447
448 return Signature;
449 }
450
451 template <typename RecordTy>
452 std::optional<Object>
serializeFunctionSignatureMixinImpl(const RecordTy & Record,std::false_type)453 serializeFunctionSignatureMixinImpl(const RecordTy &Record, std::false_type) {
454 return std::nullopt;
455 }
456
457 /// Serialize the function signature field, as specified by the
458 /// Symbol Graph format.
459 ///
460 /// The Symbol Graph function signature property contains two arrays.
461 /// - The \c returns array is the declaration fragments of the return type;
462 /// - The \c parameters array contains names and declaration fragments of the
463 /// parameters.
464 ///
465 /// \returns \c std::nullopt if \p FS is empty, or an \c Object containing the
466 /// formatted function signature.
467 template <typename RecordTy>
serializeFunctionSignatureMixin(Object & Paren,const RecordTy & Record)468 void serializeFunctionSignatureMixin(Object &Paren, const RecordTy &Record) {
469 serializeObject(Paren, "functionSignature",
470 serializeFunctionSignatureMixinImpl(
471 Record, has_function_signature<RecordTy>()));
472 }
473
474 struct PathComponent {
475 StringRef USR;
476 StringRef Name;
477 APIRecord::RecordKind Kind;
478
PathComponent__anonf8a10d840111::PathComponent479 PathComponent(StringRef USR, StringRef Name, APIRecord::RecordKind Kind)
480 : USR(USR), Name(Name), Kind(Kind) {}
481 };
482
483 template <typename RecordTy>
generatePathComponents(const RecordTy & Record,const APISet & API,function_ref<void (const PathComponent &)> ComponentTransformer)484 bool generatePathComponents(
485 const RecordTy &Record, const APISet &API,
486 function_ref<void(const PathComponent &)> ComponentTransformer) {
487 SmallVector<PathComponent, 4> ReverseComponenents;
488 ReverseComponenents.emplace_back(Record.USR, Record.Name, Record.getKind());
489 const auto *CurrentParent = &Record.ParentInformation;
490 while (CurrentParent && !CurrentParent->empty()) {
491 PathComponent CurrentParentComponent(CurrentParent->ParentUSR,
492 CurrentParent->ParentName,
493 CurrentParent->ParentKind);
494
495 auto *ParentRecord = CurrentParent->ParentRecord;
496 // Slow path if we don't have a direct reference to the ParentRecord
497 if (!ParentRecord)
498 ParentRecord = API.findRecordForUSR(CurrentParent->ParentUSR);
499
500 // If the parent is a category then we need to pretend this belongs to the
501 // associated interface.
502 if (auto *CategoryRecord =
503 dyn_cast_or_null<ObjCCategoryRecord>(ParentRecord)) {
504 ParentRecord = API.findRecordForUSR(CategoryRecord->Interface.USR);
505 CurrentParentComponent = PathComponent(CategoryRecord->Interface.USR,
506 CategoryRecord->Interface.Name,
507 APIRecord::RK_ObjCInterface);
508 }
509
510 // The parent record doesn't exist which means the symbol shouldn't be
511 // treated as part of the current product.
512 if (!ParentRecord)
513 return true;
514
515 ReverseComponenents.push_back(std::move(CurrentParentComponent));
516 CurrentParent = &ParentRecord->ParentInformation;
517 }
518
519 for (const auto &PC : reverse(ReverseComponenents))
520 ComponentTransformer(PC);
521
522 return false;
523 }
serializeParentContext(const PathComponent & PC,Language Lang)524 Object serializeParentContext(const PathComponent &PC, Language Lang) {
525 Object ParentContextElem;
526 ParentContextElem["usr"] = PC.USR;
527 ParentContextElem["name"] = PC.Name;
528 ParentContextElem["kind"] = serializeSymbolKind(PC.Kind, Lang)["identifier"];
529 return ParentContextElem;
530 }
531
532 template <typename RecordTy>
generateParentContexts(const RecordTy & Record,const APISet & API,Language Lang)533 Array generateParentContexts(const RecordTy &Record, const APISet &API,
534 Language Lang) {
535 Array ParentContexts;
536 if (generatePathComponents(
537 Record, API, [Lang, &ParentContexts](const PathComponent &PC) {
538 ParentContexts.push_back(serializeParentContext(PC, Lang));
539 }))
540 ParentContexts.clear();
541 ParentContexts.pop_back();
542
543 return ParentContexts;
544 }
545
546 } // namespace
547
anchor()548 void SymbolGraphSerializer::anchor() {}
549
550 /// Defines the format version emitted by SymbolGraphSerializer.
551 const VersionTuple SymbolGraphSerializer::FormatVersion{0, 5, 3};
552
serializeMetadata() const553 Object SymbolGraphSerializer::serializeMetadata() const {
554 Object Metadata;
555 serializeObject(Metadata, "formatVersion",
556 serializeSemanticVersion(FormatVersion));
557 Metadata["generator"] = clang::getClangFullVersion();
558 return Metadata;
559 }
560
serializeModule() const561 Object SymbolGraphSerializer::serializeModule() const {
562 Object Module;
563 // The user is expected to always pass `--product-name=` on the command line
564 // to populate this field.
565 Module["name"] = API.ProductName;
566 serializeObject(Module, "platform", serializePlatform(API.getTarget()));
567 return Module;
568 }
569
shouldSkip(const APIRecord & Record) const570 bool SymbolGraphSerializer::shouldSkip(const APIRecord &Record) const {
571 // Skip explicitly ignored symbols.
572 if (IgnoresList.shouldIgnore(Record.Name))
573 return true;
574
575 // Skip unconditionally unavailable symbols
576 if (Record.Availabilities.isUnconditionallyUnavailable())
577 return true;
578
579 // Filter out symbols prefixed with an underscored as they are understood to
580 // be symbols clients should not use.
581 if (Record.Name.startswith("_"))
582 return true;
583
584 return false;
585 }
586
587 template <typename RecordTy>
588 std::optional<Object>
serializeAPIRecord(const RecordTy & Record) const589 SymbolGraphSerializer::serializeAPIRecord(const RecordTy &Record) const {
590 if (shouldSkip(Record))
591 return std::nullopt;
592
593 Object Obj;
594 serializeObject(Obj, "identifier",
595 serializeIdentifier(Record, API.getLanguage()));
596 serializeObject(Obj, "kind", serializeSymbolKind(Record, API.getLanguage()));
597 serializeObject(Obj, "names", serializeNames(Record));
598 serializeObject(
599 Obj, "location",
600 serializeSourceLocation(Record.Location, /*IncludeFileURI=*/true));
601 serializeArray(Obj, "availability",
602 serializeAvailability(Record.Availabilities));
603 serializeObject(Obj, "docComment", serializeDocComment(Record.Comment));
604 serializeArray(Obj, "declarationFragments",
605 serializeDeclarationFragments(Record.Declaration));
606 // TODO: Once we keep track of symbol access information serialize it
607 // correctly here.
608 Obj["accessLevel"] = "public";
609 SmallVector<StringRef, 4> PathComponentsNames;
610 // If this returns true it indicates that we couldn't find a symbol in the
611 // hierarchy.
612 if (generatePathComponents(Record, API,
613 [&PathComponentsNames](const PathComponent &PC) {
614 PathComponentsNames.push_back(PC.Name);
615 }))
616 return {};
617
618 serializeArray(Obj, "pathComponents", Array(PathComponentsNames));
619
620 serializeFunctionSignatureMixin(Obj, Record);
621
622 return Obj;
623 }
624
625 template <typename MemberTy>
serializeMembers(const APIRecord & Record,const SmallVector<std::unique_ptr<MemberTy>> & Members)626 void SymbolGraphSerializer::serializeMembers(
627 const APIRecord &Record,
628 const SmallVector<std::unique_ptr<MemberTy>> &Members) {
629 // Members should not be serialized if we aren't recursing.
630 if (!ShouldRecurse)
631 return;
632 for (const auto &Member : Members) {
633 auto MemberRecord = serializeAPIRecord(*Member);
634 if (!MemberRecord)
635 continue;
636
637 Symbols.emplace_back(std::move(*MemberRecord));
638 serializeRelationship(RelationshipKind::MemberOf, *Member, Record);
639 }
640 }
641
getRelationshipString(RelationshipKind Kind)642 StringRef SymbolGraphSerializer::getRelationshipString(RelationshipKind Kind) {
643 switch (Kind) {
644 case RelationshipKind::MemberOf:
645 return "memberOf";
646 case RelationshipKind::InheritsFrom:
647 return "inheritsFrom";
648 case RelationshipKind::ConformsTo:
649 return "conformsTo";
650 }
651 llvm_unreachable("Unhandled relationship kind");
652 }
653
serializeRelationship(RelationshipKind Kind,SymbolReference Source,SymbolReference Target)654 void SymbolGraphSerializer::serializeRelationship(RelationshipKind Kind,
655 SymbolReference Source,
656 SymbolReference Target) {
657 Object Relationship;
658 Relationship["source"] = Source.USR;
659 Relationship["target"] = Target.USR;
660 Relationship["targetFallback"] = Target.Name;
661 Relationship["kind"] = getRelationshipString(Kind);
662
663 Relationships.emplace_back(std::move(Relationship));
664 }
665
serializeGlobalFunctionRecord(const GlobalFunctionRecord & Record)666 void SymbolGraphSerializer::serializeGlobalFunctionRecord(
667 const GlobalFunctionRecord &Record) {
668 auto Obj = serializeAPIRecord(Record);
669 if (!Obj)
670 return;
671
672 Symbols.emplace_back(std::move(*Obj));
673 }
674
serializeGlobalVariableRecord(const GlobalVariableRecord & Record)675 void SymbolGraphSerializer::serializeGlobalVariableRecord(
676 const GlobalVariableRecord &Record) {
677 auto Obj = serializeAPIRecord(Record);
678 if (!Obj)
679 return;
680
681 Symbols.emplace_back(std::move(*Obj));
682 }
683
serializeEnumRecord(const EnumRecord & Record)684 void SymbolGraphSerializer::serializeEnumRecord(const EnumRecord &Record) {
685 auto Enum = serializeAPIRecord(Record);
686 if (!Enum)
687 return;
688
689 Symbols.emplace_back(std::move(*Enum));
690 serializeMembers(Record, Record.Constants);
691 }
692
serializeStructRecord(const StructRecord & Record)693 void SymbolGraphSerializer::serializeStructRecord(const StructRecord &Record) {
694 auto Struct = serializeAPIRecord(Record);
695 if (!Struct)
696 return;
697
698 Symbols.emplace_back(std::move(*Struct));
699 serializeMembers(Record, Record.Fields);
700 }
701
serializeObjCContainerRecord(const ObjCContainerRecord & Record)702 void SymbolGraphSerializer::serializeObjCContainerRecord(
703 const ObjCContainerRecord &Record) {
704 auto ObjCContainer = serializeAPIRecord(Record);
705 if (!ObjCContainer)
706 return;
707
708 Symbols.emplace_back(std::move(*ObjCContainer));
709
710 serializeMembers(Record, Record.Ivars);
711 serializeMembers(Record, Record.Methods);
712 serializeMembers(Record, Record.Properties);
713
714 for (const auto &Protocol : Record.Protocols)
715 // Record that Record conforms to Protocol.
716 serializeRelationship(RelationshipKind::ConformsTo, Record, Protocol);
717
718 if (auto *ObjCInterface = dyn_cast<ObjCInterfaceRecord>(&Record)) {
719 if (!ObjCInterface->SuperClass.empty())
720 // If Record is an Objective-C interface record and it has a super class,
721 // record that Record is inherited from SuperClass.
722 serializeRelationship(RelationshipKind::InheritsFrom, Record,
723 ObjCInterface->SuperClass);
724
725 // Members of categories extending an interface are serialized as members of
726 // the interface.
727 for (const auto *Category : ObjCInterface->Categories) {
728 serializeMembers(Record, Category->Ivars);
729 serializeMembers(Record, Category->Methods);
730 serializeMembers(Record, Category->Properties);
731
732 // Surface the protocols of the category to the interface.
733 for (const auto &Protocol : Category->Protocols)
734 serializeRelationship(RelationshipKind::ConformsTo, Record, Protocol);
735 }
736 }
737 }
738
serializeMacroDefinitionRecord(const MacroDefinitionRecord & Record)739 void SymbolGraphSerializer::serializeMacroDefinitionRecord(
740 const MacroDefinitionRecord &Record) {
741 auto Macro = serializeAPIRecord(Record);
742
743 if (!Macro)
744 return;
745
746 Symbols.emplace_back(std::move(*Macro));
747 }
748
serializeSingleRecord(const APIRecord * Record)749 void SymbolGraphSerializer::serializeSingleRecord(const APIRecord *Record) {
750 switch (Record->getKind()) {
751 case APIRecord::RK_Unknown:
752 llvm_unreachable("Records should have a known kind!");
753 case APIRecord::RK_GlobalFunction:
754 serializeGlobalFunctionRecord(*cast<GlobalFunctionRecord>(Record));
755 break;
756 case APIRecord::RK_GlobalVariable:
757 serializeGlobalVariableRecord(*cast<GlobalVariableRecord>(Record));
758 break;
759 case APIRecord::RK_Enum:
760 serializeEnumRecord(*cast<EnumRecord>(Record));
761 break;
762 case APIRecord::RK_Struct:
763 serializeStructRecord(*cast<StructRecord>(Record));
764 break;
765 case APIRecord::RK_ObjCInterface:
766 serializeObjCContainerRecord(*cast<ObjCInterfaceRecord>(Record));
767 break;
768 case APIRecord::RK_ObjCProtocol:
769 serializeObjCContainerRecord(*cast<ObjCProtocolRecord>(Record));
770 break;
771 case APIRecord::RK_MacroDefinition:
772 serializeMacroDefinitionRecord(*cast<MacroDefinitionRecord>(Record));
773 break;
774 case APIRecord::RK_Typedef:
775 serializeTypedefRecord(*cast<TypedefRecord>(Record));
776 break;
777 default:
778 if (auto Obj = serializeAPIRecord(*Record)) {
779 Symbols.emplace_back(std::move(*Obj));
780 auto &ParentInformation = Record->ParentInformation;
781 if (!ParentInformation.empty())
782 serializeRelationship(RelationshipKind::MemberOf, *Record,
783 *ParentInformation.ParentRecord);
784 }
785 break;
786 }
787 }
788
serializeTypedefRecord(const TypedefRecord & Record)789 void SymbolGraphSerializer::serializeTypedefRecord(
790 const TypedefRecord &Record) {
791 // Typedefs of anonymous types have their entries unified with the underlying
792 // type.
793 bool ShouldDrop = Record.UnderlyingType.Name.empty();
794 // enums declared with `NS_OPTION` have a named enum and a named typedef, with
795 // the same name
796 ShouldDrop |= (Record.UnderlyingType.Name == Record.Name);
797 if (ShouldDrop)
798 return;
799
800 auto Typedef = serializeAPIRecord(Record);
801 if (!Typedef)
802 return;
803
804 (*Typedef)["type"] = Record.UnderlyingType.USR;
805
806 Symbols.emplace_back(std::move(*Typedef));
807 }
808
serialize()809 Object SymbolGraphSerializer::serialize() {
810 // Serialize global variables in the API set.
811 for (const auto &GlobalVar : API.getGlobalVariables())
812 serializeGlobalVariableRecord(*GlobalVar.second);
813
814 for (const auto &GlobalFunction : API.getGlobalFunctions())
815 serializeGlobalFunctionRecord(*GlobalFunction.second);
816
817 // Serialize enum records in the API set.
818 for (const auto &Enum : API.getEnums())
819 serializeEnumRecord(*Enum.second);
820
821 // Serialize struct records in the API set.
822 for (const auto &Struct : API.getStructs())
823 serializeStructRecord(*Struct.second);
824
825 // Serialize Objective-C interface records in the API set.
826 for (const auto &ObjCInterface : API.getObjCInterfaces())
827 serializeObjCContainerRecord(*ObjCInterface.second);
828
829 // Serialize Objective-C protocol records in the API set.
830 for (const auto &ObjCProtocol : API.getObjCProtocols())
831 serializeObjCContainerRecord(*ObjCProtocol.second);
832
833 for (const auto &Macro : API.getMacros())
834 serializeMacroDefinitionRecord(*Macro.second);
835
836 for (const auto &Typedef : API.getTypedefs())
837 serializeTypedefRecord(*Typedef.second);
838
839 return serializeCurrentGraph();
840 }
841
serializeCurrentGraph()842 Object SymbolGraphSerializer::serializeCurrentGraph() {
843 Object Root;
844 serializeObject(Root, "metadata", serializeMetadata());
845 serializeObject(Root, "module", serializeModule());
846
847 Root["symbols"] = std::move(Symbols);
848 Root["relationships"] = std::move(Relationships);
849
850 return Root;
851 }
852
serialize(raw_ostream & os)853 void SymbolGraphSerializer::serialize(raw_ostream &os) {
854 Object root = serialize();
855 if (Options.Compact)
856 os << formatv("{0}", Value(std::move(root))) << "\n";
857 else
858 os << formatv("{0:2}", Value(std::move(root))) << "\n";
859 }
860
861 std::optional<Object>
serializeSingleSymbolSGF(StringRef USR,const APISet & API)862 SymbolGraphSerializer::serializeSingleSymbolSGF(StringRef USR,
863 const APISet &API) {
864 APIRecord *Record = API.findRecordForUSR(USR);
865 if (!Record)
866 return {};
867
868 Object Root;
869 APIIgnoresList EmptyIgnores;
870 SymbolGraphSerializer Serializer(API, EmptyIgnores,
871 /*Options.Compact*/ {true},
872 /*ShouldRecurse*/ false);
873 Serializer.serializeSingleRecord(Record);
874 serializeObject(Root, "symbolGraph", Serializer.serializeCurrentGraph());
875
876 Language Lang = API.getLanguage();
877 serializeArray(Root, "parentContexts",
878 generateParentContexts(*Record, API, Lang));
879
880 Array RelatedSymbols;
881
882 for (const auto &Fragment : Record->Declaration.getFragments()) {
883 // If we don't have a USR there isn't much we can do.
884 if (Fragment.PreciseIdentifier.empty())
885 continue;
886
887 APIRecord *RelatedRecord = API.findRecordForUSR(Fragment.PreciseIdentifier);
888
889 // If we can't find the record let's skip.
890 if (!RelatedRecord)
891 continue;
892
893 Object RelatedSymbol;
894 RelatedSymbol["usr"] = RelatedRecord->USR;
895 RelatedSymbol["declarationLanguage"] = getLanguageName(Lang);
896 // TODO: once we record this properly let's serialize it right.
897 RelatedSymbol["accessLevel"] = "public";
898 RelatedSymbol["filePath"] = RelatedRecord->Location.getFilename();
899 RelatedSymbol["moduleName"] = API.ProductName;
900 RelatedSymbol["isSystem"] = RelatedRecord->IsFromSystemHeader;
901
902 serializeArray(RelatedSymbol, "parentContexts",
903 generateParentContexts(*RelatedRecord, API, Lang));
904 RelatedSymbols.push_back(std::move(RelatedSymbol));
905 }
906
907 serializeArray(Root, "relatedSymbols", RelatedSymbols);
908 return Root;
909 }
910