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