clang 17.0.0git
SymbolGraphSerializer.cpp
Go to the documentation of this file.
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
16#include "clang/Basic/Version.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
32using namespace clang;
33using namespace clang::extractapi;
34using namespace llvm;
35using namespace llvm::json;
36
37namespace {
38
39/// Helper function to inject a JSON object \p Obj into another object \p Paren
40/// at position \p Key.
41void 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.
48void 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.
69std::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.
84Object 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.
96Object 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.
105Object 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.
120Object serializeSourceLocation(const PresumedLoc &Loc,
121 bool IncludeFileURI = false) {
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.
136Object serializeSourceRange(const PresumedLoc &BeginLoc,
137 const PresumedLoc &EndLoc) {
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.
155std::optional<Array>
156serializeAvailability(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 if (AvailInfo.Unavailable)
175 Availability["isUnconditionallyUnavailable"] = true;
176 else {
177 serializeObject(Availability, "introducedVersion",
178 serializeSemanticVersion(AvailInfo.Introduced));
179 serializeObject(Availability, "deprecatedVersion",
180 serializeSemanticVersion(AvailInfo.Deprecated));
181 serializeObject(Availability, "obsoletedVersion",
182 serializeSemanticVersion(AvailInfo.Obsoleted));
183 }
184 AvailabilityArray.emplace_back(std::move(Availability));
185 }
186
187 return AvailabilityArray;
188}
189
190/// Get the language name string for interface language references.
191StringRef getLanguageName(Language Lang) {
192 switch (Lang) {
193 case Language::C:
194 return "c";
195 case Language::ObjC:
196 return "objective-c";
197
198 // Unsupported language currently
199 case Language::CXX:
200 case Language::ObjCXX:
201 case Language::OpenCL:
202 case Language::OpenCLCXX:
203 case Language::CUDA:
204 case Language::RenderScript:
205 case Language::HIP:
206 case Language::HLSL:
207
208 // Languages that the frontend cannot parse and compile
209 case Language::Unknown:
210 case Language::Asm:
211 case Language::LLVM_IR:
212 llvm_unreachable("Unsupported language kind");
213 }
214
215 llvm_unreachable("Unhandled language kind");
216}
217
218/// Serialize the identifier object as specified by the Symbol Graph format.
219///
220/// The identifier property of a symbol contains the USR for precise and unique
221/// references, and the interface language name.
222Object serializeIdentifier(const APIRecord &Record, Language Lang) {
224 Identifier["precise"] = Record.USR;
225 Identifier["interfaceLanguage"] = getLanguageName(Lang);
226
227 return Identifier;
228}
229
230/// Serialize the documentation comments attached to a symbol, as specified by
231/// the Symbol Graph format.
232///
233/// The Symbol Graph \c docComment object contains an array of lines. Each line
234/// represents one line of striped documentation comment, with source range
235/// information.
236/// e.g.
237/// \code
238/// /// This is a documentation comment
239/// ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~' First line.
240/// /// with multiple lines.
241/// ^~~~~~~~~~~~~~~~~~~~~~~' Second line.
242/// \endcode
243///
244/// \returns \c std::nullopt if \p Comment is empty, or an \c Object containing
245/// the formatted lines.
246std::optional<Object> serializeDocComment(const DocComment &Comment) {
247 if (Comment.empty())
248 return std::nullopt;
249
251 Array LinesArray;
252 for (const auto &CommentLine : Comment) {
253 Object Line;
254 Line["text"] = CommentLine.Text;
255 serializeObject(Line, "range",
256 serializeSourceRange(CommentLine.Begin, CommentLine.End));
257 LinesArray.emplace_back(std::move(Line));
258 }
259 serializeArray(DocComment, "lines", LinesArray);
260
261 return DocComment;
262}
263
264/// Serialize the declaration fragments of a symbol.
265///
266/// The Symbol Graph declaration fragments is an array of tagged important
267/// parts of a symbol's declaration. The fragments sequence can be joined to
268/// form spans of declaration text, with attached information useful for
269/// purposes like syntax-highlighting etc. For example:
270/// \code
271/// const int pi; -> "declarationFragments" : [
272/// {
273/// "kind" : "keyword",
274/// "spelling" : "const"
275/// },
276/// {
277/// "kind" : "text",
278/// "spelling" : " "
279/// },
280/// {
281/// "kind" : "typeIdentifier",
282/// "preciseIdentifier" : "c:I",
283/// "spelling" : "int"
284/// },
285/// {
286/// "kind" : "text",
287/// "spelling" : " "
288/// },
289/// {
290/// "kind" : "identifier",
291/// "spelling" : "pi"
292/// }
293/// ]
294/// \endcode
295///
296/// \returns \c std::nullopt if \p DF is empty, or an \c Array containing the
297/// formatted declaration fragments array.
298std::optional<Array>
299serializeDeclarationFragments(const DeclarationFragments &DF) {
300 if (DF.getFragments().empty())
301 return std::nullopt;
302
303 Array Fragments;
304 for (const auto &F : DF.getFragments()) {
305 Object Fragment;
306 Fragment["spelling"] = F.Spelling;
307 Fragment["kind"] = DeclarationFragments::getFragmentKindString(F.Kind);
308 if (!F.PreciseIdentifier.empty())
309 Fragment["preciseIdentifier"] = F.PreciseIdentifier;
310 Fragments.emplace_back(std::move(Fragment));
311 }
312
313 return Fragments;
314}
315
316/// Serialize the \c names field of a symbol as specified by the Symbol Graph
317/// format.
318///
319/// The Symbol Graph names field contains multiple representations of a symbol
320/// that can be used for different applications:
321/// - \c title : The simple declared name of the symbol;
322/// - \c subHeading : An array of declaration fragments that provides tags,
323/// and potentially more tokens (for example the \c +/- symbol for
324/// Objective-C methods). Can be used as sub-headings for documentation.
325Object serializeNames(const APIRecord &Record) {
326 Object Names;
327 Names["title"] = Record.Name;
328 serializeArray(Names, "subHeading",
329 serializeDeclarationFragments(Record.SubHeading));
330 DeclarationFragments NavigatorFragments;
331 NavigatorFragments.append(Record.Name,
332 DeclarationFragments::FragmentKind::Identifier,
333 /*PreciseIdentifier*/ "");
334 serializeArray(Names, "navigator",
335 serializeDeclarationFragments(NavigatorFragments));
336
337 return Names;
338}
339
340Object serializeSymbolKind(APIRecord::RecordKind RK, Language Lang) {
341 auto AddLangPrefix = [&Lang](StringRef S) -> std::string {
342 return (getLanguageName(Lang) + "." + S).str();
343 };
344
345 Object Kind;
346 switch (RK) {
348 llvm_unreachable("Records should have an explicit kind");
349 break;
351 Kind["identifier"] = AddLangPrefix("func");
352 Kind["displayName"] = "Function";
353 break;
355 Kind["identifier"] = AddLangPrefix("var");
356 Kind["displayName"] = "Global Variable";
357 break;
359 Kind["identifier"] = AddLangPrefix("enum.case");
360 Kind["displayName"] = "Enumeration Case";
361 break;
363 Kind["identifier"] = AddLangPrefix("enum");
364 Kind["displayName"] = "Enumeration";
365 break;
367 Kind["identifier"] = AddLangPrefix("property");
368 Kind["displayName"] = "Instance Property";
369 break;
371 Kind["identifier"] = AddLangPrefix("struct");
372 Kind["displayName"] = "Structure";
373 break;
375 Kind["identifier"] = AddLangPrefix("ivar");
376 Kind["displayName"] = "Instance Variable";
377 break;
379 Kind["identifier"] = AddLangPrefix("method");
380 Kind["displayName"] = "Instance Method";
381 break;
383 Kind["identifier"] = AddLangPrefix("type.method");
384 Kind["displayName"] = "Type Method";
385 break;
387 Kind["identifier"] = AddLangPrefix("property");
388 Kind["displayName"] = "Instance Property";
389 break;
391 Kind["identifier"] = AddLangPrefix("type.property");
392 Kind["displayName"] = "Type Property";
393 break;
395 Kind["identifier"] = AddLangPrefix("class");
396 Kind["displayName"] = "Class";
397 break;
399 // We don't serialize out standalone Objective-C category symbols yet.
400 llvm_unreachable("Serializing standalone Objective-C category symbols is "
401 "not supported.");
402 break;
404 Kind["identifier"] = AddLangPrefix("protocol");
405 Kind["displayName"] = "Protocol";
406 break;
408 Kind["identifier"] = AddLangPrefix("macro");
409 Kind["displayName"] = "Macro";
410 break;
412 Kind["identifier"] = AddLangPrefix("typealias");
413 Kind["displayName"] = "Type Alias";
414 break;
415 }
416
417 return Kind;
418}
419
420/// Serialize the symbol kind information.
421///
422/// The Symbol Graph symbol kind property contains a shorthand \c identifier
423/// which is prefixed by the source language name, useful for tooling to parse
424/// the kind, and a \c displayName for rendering human-readable names.
425Object serializeSymbolKind(const APIRecord &Record, Language Lang) {
426 return serializeSymbolKind(Record.getKind(), Lang);
427}
428
429template <typename RecordTy>
430std::optional<Object>
431serializeFunctionSignatureMixinImpl(const RecordTy &Record, std::true_type) {
432 const auto &FS = Record.Signature;
433 if (FS.empty())
434 return std::nullopt;
435
436 Object Signature;
437 serializeArray(Signature, "returns",
438 serializeDeclarationFragments(FS.getReturnType()));
439
440 Array Parameters;
441 for (const auto &P : FS.getParameters()) {
443 Parameter["name"] = P.Name;
444 serializeArray(Parameter, "declarationFragments",
445 serializeDeclarationFragments(P.Fragments));
446 Parameters.emplace_back(std::move(Parameter));
447 }
448
449 if (!Parameters.empty())
450 Signature["parameters"] = std::move(Parameters);
451
452 return Signature;
453}
454
455template <typename RecordTy>
456std::optional<Object>
457serializeFunctionSignatureMixinImpl(const RecordTy &Record, std::false_type) {
458 return std::nullopt;
459}
460
461/// Serialize the function signature field, as specified by the
462/// Symbol Graph format.
463///
464/// The Symbol Graph function signature property contains two arrays.
465/// - The \c returns array is the declaration fragments of the return type;
466/// - The \c parameters array contains names and declaration fragments of the
467/// parameters.
468///
469/// \returns \c std::nullopt if \p FS is empty, or an \c Object containing the
470/// formatted function signature.
471template <typename RecordTy>
472void serializeFunctionSignatureMixin(Object &Paren, const RecordTy &Record) {
473 serializeObject(Paren, "functionSignature",
474 serializeFunctionSignatureMixinImpl(
476}
477
478struct PathComponent {
479 StringRef USR;
480 StringRef Name;
482
483 PathComponent(StringRef USR, StringRef Name, APIRecord::RecordKind Kind)
484 : USR(USR), Name(Name), Kind(Kind) {}
485};
486
487template <typename RecordTy>
488bool generatePathComponents(
489 const RecordTy &Record, const APISet &API,
490 function_ref<void(const PathComponent &)> ComponentTransformer) {
491 SmallVector<PathComponent, 4> ReverseComponenents;
492 ReverseComponenents.emplace_back(Record.USR, Record.Name, Record.getKind());
493 const auto *CurrentParent = &Record.ParentInformation;
494 bool FailedToFindParent = false;
495 while (CurrentParent && !CurrentParent->empty()) {
496 PathComponent CurrentParentComponent(CurrentParent->ParentUSR,
497 CurrentParent->ParentName,
498 CurrentParent->ParentKind);
499
500 auto *ParentRecord = CurrentParent->ParentRecord;
501 // Slow path if we don't have a direct reference to the ParentRecord
502 if (!ParentRecord)
503 ParentRecord = API.findRecordForUSR(CurrentParent->ParentUSR);
504
505 // If the parent is a category then we need to pretend this belongs to the
506 // associated interface.
507 if (auto *CategoryRecord =
508 dyn_cast_or_null<ObjCCategoryRecord>(ParentRecord)) {
509 ParentRecord = API.findRecordForUSR(CategoryRecord->Interface.USR);
510 CurrentParentComponent = PathComponent(CategoryRecord->Interface.USR,
511 CategoryRecord->Interface.Name,
513 }
514
515 // The parent record doesn't exist which means the symbol shouldn't be
516 // treated as part of the current product.
517 if (!ParentRecord) {
518 FailedToFindParent = true;
519 break;
520 }
521
522 ReverseComponenents.push_back(std::move(CurrentParentComponent));
523 CurrentParent = &ParentRecord->ParentInformation;
524 }
525
526 for (const auto &PC : reverse(ReverseComponenents))
527 ComponentTransformer(PC);
528
529 return FailedToFindParent;
530}
531
532Object serializeParentContext(const PathComponent &PC, Language Lang) {
533 Object ParentContextElem;
534 ParentContextElem["usr"] = PC.USR;
535 ParentContextElem["name"] = PC.Name;
536 ParentContextElem["kind"] = serializeSymbolKind(PC.Kind, Lang)["identifier"];
537 return ParentContextElem;
538}
539
540template <typename RecordTy>
541Array generateParentContexts(const RecordTy &Record, const APISet &API,
542 Language Lang) {
543 Array ParentContexts;
544 generatePathComponents(Record, API,
545 [Lang, &ParentContexts](const PathComponent &PC) {
546 ParentContexts.push_back(
547 serializeParentContext(PC, Lang));
548 });
549
550 return ParentContexts;
551}
552
553} // namespace
554
555void SymbolGraphSerializer::anchor() {}
556
557/// Defines the format version emitted by SymbolGraphSerializer.
558const VersionTuple SymbolGraphSerializer::FormatVersion{0, 5, 3};
559
560Object SymbolGraphSerializer::serializeMetadata() const {
561 Object Metadata;
562 serializeObject(Metadata, "formatVersion",
563 serializeSemanticVersion(FormatVersion));
564 Metadata["generator"] = clang::getClangFullVersion();
565 return Metadata;
566}
567
568Object SymbolGraphSerializer::serializeModule() const {
570 // The user is expected to always pass `--product-name=` on the command line
571 // to populate this field.
572 Module["name"] = API.ProductName;
573 serializeObject(Module, "platform", serializePlatform(API.getTarget()));
574 return Module;
575}
576
577bool SymbolGraphSerializer::shouldSkip(const APIRecord &Record) const {
578 // Skip explicitly ignored symbols.
580 return true;
581
582 // Skip unconditionally unavailable symbols
583 if (Record.Availabilities.isUnconditionallyUnavailable())
584 return true;
585
586 // Filter out symbols prefixed with an underscored as they are understood to
587 // be symbols clients should not use.
588 if (Record.Name.startswith("_"))
589 return true;
590
591 return false;
592}
593
594template <typename RecordTy>
595std::optional<Object>
596SymbolGraphSerializer::serializeAPIRecord(const RecordTy &Record) const {
597 if (shouldSkip(Record))
598 return std::nullopt;
599
600 Object Obj;
601 serializeObject(Obj, "identifier",
602 serializeIdentifier(Record, API.getLanguage()));
603 serializeObject(Obj, "kind", serializeSymbolKind(Record, API.getLanguage()));
604 serializeObject(Obj, "names", serializeNames(Record));
605 serializeObject(
606 Obj, "location",
607 serializeSourceLocation(Record.Location, /*IncludeFileURI=*/true));
608 serializeArray(Obj, "availability",
609 serializeAvailability(Record.Availabilities));
610 serializeObject(Obj, "docComment", serializeDocComment(Record.Comment));
611 serializeArray(Obj, "declarationFragments",
612 serializeDeclarationFragments(Record.Declaration));
613 // TODO: Once we keep track of symbol access information serialize it
614 // correctly here.
615 Obj["accessLevel"] = "public";
616 SmallVector<StringRef, 4> PathComponentsNames;
617 // If this returns true it indicates that we couldn't find a symbol in the
618 // hierarchy.
619 if (generatePathComponents(Record, API,
620 [&PathComponentsNames](const PathComponent &PC) {
621 PathComponentsNames.push_back(PC.Name);
622 }))
623 return {};
624
625 serializeArray(Obj, "pathComponents", Array(PathComponentsNames));
626
627 serializeFunctionSignatureMixin(Obj, Record);
628
629 return Obj;
630}
631
632template <typename MemberTy>
633void SymbolGraphSerializer::serializeMembers(
634 const APIRecord &Record,
635 const SmallVector<std::unique_ptr<MemberTy>> &Members) {
636 // Members should not be serialized if we aren't recursing.
637 if (!ShouldRecurse)
638 return;
639 for (const auto &Member : Members) {
640 auto MemberRecord = serializeAPIRecord(*Member);
641 if (!MemberRecord)
642 continue;
643
644 Symbols.emplace_back(std::move(*MemberRecord));
645 serializeRelationship(RelationshipKind::MemberOf, *Member, Record);
646 }
647}
648
650 switch (Kind) {
652 return "memberOf";
654 return "inheritsFrom";
656 return "conformsTo";
657 }
658 llvm_unreachable("Unhandled relationship kind");
659}
660
661void SymbolGraphSerializer::serializeRelationship(RelationshipKind Kind,
662 SymbolReference Source,
664 Object Relationship;
665 Relationship["source"] = Source.USR;
666 Relationship["target"] = Target.USR;
667 Relationship["targetFallback"] = Target.Name;
668 Relationship["kind"] = getRelationshipString(Kind);
669
670 Relationships.emplace_back(std::move(Relationship));
671}
672
673void SymbolGraphSerializer::serializeGlobalFunctionRecord(
674 const GlobalFunctionRecord &Record) {
675 auto Obj = serializeAPIRecord(Record);
676 if (!Obj)
677 return;
678
679 Symbols.emplace_back(std::move(*Obj));
680}
681
682void SymbolGraphSerializer::serializeGlobalVariableRecord(
683 const GlobalVariableRecord &Record) {
684 auto Obj = serializeAPIRecord(Record);
685 if (!Obj)
686 return;
687
688 Symbols.emplace_back(std::move(*Obj));
689}
690
691void SymbolGraphSerializer::serializeEnumRecord(const EnumRecord &Record) {
692 auto Enum = serializeAPIRecord(Record);
693 if (!Enum)
694 return;
695
696 Symbols.emplace_back(std::move(*Enum));
697 serializeMembers(Record, Record.Constants);
698}
699
700void SymbolGraphSerializer::serializeStructRecord(const StructRecord &Record) {
701 auto Struct = serializeAPIRecord(Record);
702 if (!Struct)
703 return;
704
705 Symbols.emplace_back(std::move(*Struct));
706 serializeMembers(Record, Record.Fields);
707}
708
709void SymbolGraphSerializer::serializeObjCContainerRecord(
710 const ObjCContainerRecord &Record) {
711 auto ObjCContainer = serializeAPIRecord(Record);
712 if (!ObjCContainer)
713 return;
714
715 Symbols.emplace_back(std::move(*ObjCContainer));
716
717 serializeMembers(Record, Record.Ivars);
718 serializeMembers(Record, Record.Methods);
719 serializeMembers(Record, Record.Properties);
720
721 for (const auto &Protocol : Record.Protocols)
722 // Record that Record conforms to Protocol.
723 serializeRelationship(RelationshipKind::ConformsTo, Record, Protocol);
724
725 if (auto *ObjCInterface = dyn_cast<ObjCInterfaceRecord>(&Record)) {
726 if (!ObjCInterface->SuperClass.empty())
727 // If Record is an Objective-C interface record and it has a super class,
728 // record that Record is inherited from SuperClass.
729 serializeRelationship(RelationshipKind::InheritsFrom, Record,
730 ObjCInterface->SuperClass);
731
732 // Members of categories extending an interface are serialized as members of
733 // the interface.
734 for (const auto *Category : ObjCInterface->Categories) {
735 serializeMembers(Record, Category->Ivars);
736 serializeMembers(Record, Category->Methods);
737 serializeMembers(Record, Category->Properties);
738
739 // Surface the protocols of the category to the interface.
740 for (const auto &Protocol : Category->Protocols)
741 serializeRelationship(RelationshipKind::ConformsTo, Record, Protocol);
742 }
743 }
744}
745
746void SymbolGraphSerializer::serializeMacroDefinitionRecord(
747 const MacroDefinitionRecord &Record) {
748 auto Macro = serializeAPIRecord(Record);
749
750 if (!Macro)
751 return;
752
753 Symbols.emplace_back(std::move(*Macro));
754}
755
756void SymbolGraphSerializer::serializeSingleRecord(const APIRecord *Record) {
757 switch (Record->getKind()) {
759 llvm_unreachable("Records should have a known kind!");
761 serializeGlobalFunctionRecord(*cast<GlobalFunctionRecord>(Record));
762 break;
764 serializeGlobalVariableRecord(*cast<GlobalVariableRecord>(Record));
765 break;
767 serializeEnumRecord(*cast<EnumRecord>(Record));
768 break;
770 serializeStructRecord(*cast<StructRecord>(Record));
771 break;
773 serializeObjCContainerRecord(*cast<ObjCInterfaceRecord>(Record));
774 break;
776 serializeObjCContainerRecord(*cast<ObjCProtocolRecord>(Record));
777 break;
779 serializeMacroDefinitionRecord(*cast<MacroDefinitionRecord>(Record));
780 break;
782 serializeTypedefRecord(*cast<TypedefRecord>(Record));
783 break;
784 default:
785 if (auto Obj = serializeAPIRecord(*Record)) {
786 Symbols.emplace_back(std::move(*Obj));
787 auto &ParentInformation = Record->ParentInformation;
788 if (!ParentInformation.empty())
789 serializeRelationship(RelationshipKind::MemberOf, *Record,
790 *ParentInformation.ParentRecord);
791 }
792 break;
793 }
794}
795
796void SymbolGraphSerializer::serializeTypedefRecord(
797 const TypedefRecord &Record) {
798 // Typedefs of anonymous types have their entries unified with the underlying
799 // type.
800 bool ShouldDrop = Record.UnderlyingType.Name.empty();
801 // enums declared with `NS_OPTION` have a named enum and a named typedef, with
802 // the same name
803 ShouldDrop |= (Record.UnderlyingType.Name == Record.Name);
804 if (ShouldDrop)
805 return;
806
807 auto Typedef = serializeAPIRecord(Record);
808 if (!Typedef)
809 return;
810
811 (*Typedef)["type"] = Record.UnderlyingType.USR;
812
813 Symbols.emplace_back(std::move(*Typedef));
814}
815
817 // Serialize global variables in the API set.
818 for (const auto &GlobalVar : API.getGlobalVariables())
819 serializeGlobalVariableRecord(*GlobalVar.second);
820
821 for (const auto &GlobalFunction : API.getGlobalFunctions())
822 serializeGlobalFunctionRecord(*GlobalFunction.second);
823
824 // Serialize enum records in the API set.
825 for (const auto &Enum : API.getEnums())
826 serializeEnumRecord(*Enum.second);
827
828 // Serialize struct records in the API set.
829 for (const auto &Struct : API.getStructs())
830 serializeStructRecord(*Struct.second);
831
832 // Serialize Objective-C interface records in the API set.
833 for (const auto &ObjCInterface : API.getObjCInterfaces())
834 serializeObjCContainerRecord(*ObjCInterface.second);
835
836 // Serialize Objective-C protocol records in the API set.
837 for (const auto &ObjCProtocol : API.getObjCProtocols())
838 serializeObjCContainerRecord(*ObjCProtocol.second);
839
840 for (const auto &Macro : API.getMacros())
841 serializeMacroDefinitionRecord(*Macro.second);
842
843 for (const auto &Typedef : API.getTypedefs())
844 serializeTypedefRecord(*Typedef.second);
845
846 return serializeCurrentGraph();
847}
848
849Object SymbolGraphSerializer::serializeCurrentGraph() {
850 Object Root;
851 serializeObject(Root, "metadata", serializeMetadata());
852 serializeObject(Root, "module", serializeModule());
853
854 Root["symbols"] = std::move(Symbols);
855 Root["relationships"] = std::move(Relationships);
856
857 return Root;
858}
859
860void SymbolGraphSerializer::serialize(raw_ostream &os) {
861 Object root = serialize();
862 if (Options.Compact)
863 os << formatv("{0}", Value(std::move(root))) << "\n";
864 else
865 os << formatv("{0:2}", Value(std::move(root))) << "\n";
866}
867
868std::optional<Object>
870 const APISet &API) {
871 APIRecord *Record = API.findRecordForUSR(USR);
872 if (!Record)
873 return {};
874
875 if (isa<ObjCCategoryRecord>(Record))
876 return {};
877
878 Object Root;
879 APIIgnoresList EmptyIgnores;
880 SymbolGraphSerializer Serializer(API, EmptyIgnores,
881 /*Options.Compact*/ {true},
882 /*ShouldRecurse*/ false);
883 Serializer.serializeSingleRecord(Record);
884 serializeObject(Root, "symbolGraph", Serializer.serializeCurrentGraph());
885
886 Language Lang = API.getLanguage();
887 serializeArray(Root, "parentContexts",
888 generateParentContexts(*Record, API, Lang));
889
890 Array RelatedSymbols;
891
892 for (const auto &Fragment : Record->Declaration.getFragments()) {
893 // If we don't have a USR there isn't much we can do.
894 if (Fragment.PreciseIdentifier.empty())
895 continue;
896
897 APIRecord *RelatedRecord = API.findRecordForUSR(Fragment.PreciseIdentifier);
898
899 // If we can't find the record let's skip.
900 if (!RelatedRecord)
901 continue;
902
903 Object RelatedSymbol;
904 RelatedSymbol["usr"] = RelatedRecord->USR;
905 RelatedSymbol["declarationLanguage"] = getLanguageName(Lang);
906 // TODO: once we record this properly let's serialize it right.
907 RelatedSymbol["accessLevel"] = "public";
908 RelatedSymbol["filePath"] = RelatedRecord->Location.getFilename();
909 RelatedSymbol["moduleName"] = API.ProductName;
910 RelatedSymbol["isSystem"] = RelatedRecord->IsFromSystemHeader;
911
912 serializeArray(RelatedSymbol, "parentContexts",
913 generateParentContexts(*RelatedRecord, API, Lang));
914 RelatedSymbols.push_back(std::move(RelatedSymbol));
915 }
916
917 serializeArray(Root, "relatedSymbols", RelatedSymbols);
918 return Root;
919}
This file defines the APIRecord-based structs and the APISet class.
#define V(N, I)
Definition: ASTContext.h:3230
StringRef P
This file defines the Declaration Fragments related classes.
int Category
Definition: Format.cpp:2795
StringRef Identifier
Definition: Format.cpp:2800
This file defines the ExtractAPI APISerializer interface.
Defines the clang::SourceLocation class and associated facilities.
This file defines the SymbolGraphSerializer class.
Defines version macros and version-related utility functions for Clang.
Describes a module or submodule.
Definition: Module.h:98
Represents an unpacked "presumed" location which can be presented to the user.
unsigned getColumn() const
Return the presumed column number of this location.
const char * getFilename() const
Return the presumed filename of this location.
bool isValid() const
unsigned getLine() const
Return the presumed line number of this location.
Encodes a location in the source.
A trivial tuple used to represent a source range.
const APIIgnoresList & IgnoresList
The list of symbols to ignore.
APISet holds the set of API records collected from given inputs.
Definition: API.h:596
Language getLanguage() const
Get the language used by the APIs.
Definition: API.h:785
const std::string ProductName
Definition: API.h:857
const RecordMap< EnumRecord > & getEnums() const
Definition: API.h:793
const llvm::Triple & getTarget() const
Get the target triple for the ExtractAPI invocation.
Definition: API.h:782
const RecordMap< TypedefRecord > & getTypedefs() const
Definition: API.h:805
const RecordMap< GlobalVariableRecord > & getGlobalVariables() const
Definition: API.h:790
const RecordMap< MacroDefinitionRecord > & getMacros() const
Definition: API.h:804
const RecordMap< ObjCProtocolRecord > & getObjCProtocols() const
Definition: API.h:801
const RecordMap< ObjCInterfaceRecord > & getObjCInterfaces() const
Definition: API.h:798
const RecordMap< GlobalFunctionRecord > & getGlobalFunctions() const
Definition: API.h:787
APIRecord * findRecordForUSR(StringRef USR) const
Finds the APIRecord for a given USR.
Definition: API.cpp:248
const RecordMap< StructRecord > & getStructs() const
Definition: API.h:794
bool isUnconditionallyDeprecated() const
Check if the symbol is unconditionally deprecated.
bool isDefault() const
Determine if this AvailabilitySet represents default availability.
DeclarationFragments is a vector of tagged important parts of a symbol's declaration.
const std::vector< Fragment > & getFragments() const
static StringRef getFragmentKindString(FragmentKind Kind)
Get the string description of a FragmentKind Kind.
DeclarationFragments & append(StringRef Spelling, FragmentKind Kind, StringRef PreciseIdentifier="", const Decl *Declaration=nullptr)
Append a new Fragment to the end of the Fragments.
The serializer that organizes API information in the Symbol Graph format.
static StringRef getRelationshipString(RelationshipKind Kind)
Get the string representation of the relationship kind.
Object serialize()
Serialize the APIs in APISet in the Symbol Graph format.
RelationshipKind
The kind of a relationship between two symbols.
@ InheritsFrom
The source symbol is inherited from the target symbol.
@ MemberOf
The source symbol is a member of the target symbol.
@ ConformsTo
The source symbol conforms to the target symbol.
static std::optional< Object > serializeSingleSymbolSGF(StringRef USR, const APISet &API)
Serialize a single symbol SGF.
std::vector< RawComment::CommentLine > DocComment
DocComment is a vector of RawComment::CommentLine.
Definition: API.h:51
StringRef getLanguageName(FormatStyle::LanguageKind Language)
Definition: Format.h:4723
Language
The language for the input, used to select and validate the language standard and possible actions.
Definition: LangStandard.h:23
@ Parameter
The parameter type of a method or function.
std::string getClangFullVersion()
Retrieves a string representing the complete clang version, which includes the clang version number,...
Definition: Version.cpp:88
YAML serialization mapping.
Definition: Dominators.h:30
A type that provides access to a new line separated list of symbol names to ignore when extracting AP...
bool shouldIgnore(llvm::StringRef SymbolName) const
Check if SymbolName is specified in the APIIgnoresList and if it should therefore be ignored.
The base representation of an API record. Holds common symbol information.
Definition: API.h:57
PresumedLoc Location
Definition: API.h:105
RecordKind
Discriminator for LLVM-style RTTI (dyn_cast<> et al.)
Definition: API.h:59
bool IsFromSystemHeader
Whether the symbol was defined in a system header.
Definition: API.h:127
bool Compact
Do not include unnecessary whitespaces to save space.
This holds information associated with enums.
Definition: API.h:212
This holds information associated with global functions.
Definition: API.h:152
This holds information associated with global functions.
Definition: API.h:175
This holds information associated with macro definitions.
Definition: API.h:536
The base representation of an Objective-C container record.
Definition: API.h:449
This holds information associated with structs.
Definition: API.h:250
This represents a reference to another symbol that might come from external sources.
Definition: API.h:428
This holds information associated with typedefs.
Definition: API.h:558
Check if a record type has a function signature mixin.
Definition: API.h:584