clang 24.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 // Comments in source files may contain invalid UTF-8. JSON values must be
266 // valid UTF-8, so replace any invalid sequences before serializing.
267 Line["text"] = json::isUTF8(CommentLine.Text)
268 ? CommentLine.Text
269 : json::fixUTF8(CommentLine.Text);
270 serializeObject(Line, "range",
271 serializeSourceRange(CommentLine.Begin, CommentLine.End));
272 LinesArray.emplace_back(std::move(Line));
273 }
274
275 serializeArray(DocComment, "lines", std::move(LinesArray));
276
277 return DocComment;
278}
279
280/// Serialize the declaration fragments of a symbol.
281///
282/// The Symbol Graph declaration fragments is an array of tagged important
283/// parts of a symbol's declaration. The fragments sequence can be joined to
284/// form spans of declaration text, with attached information useful for
285/// purposes like syntax-highlighting etc. For example:
286/// \code
287/// const int pi; -> "declarationFragments" : [
288/// {
289/// "kind" : "keyword",
290/// "spelling" : "const"
291/// },
292/// {
293/// "kind" : "text",
294/// "spelling" : " "
295/// },
296/// {
297/// "kind" : "typeIdentifier",
298/// "preciseIdentifier" : "c:I",
299/// "spelling" : "int"
300/// },
301/// {
302/// "kind" : "text",
303/// "spelling" : " "
304/// },
305/// {
306/// "kind" : "identifier",
307/// "spelling" : "pi"
308/// }
309/// ]
310/// \endcode
311///
312/// \returns \c std::nullopt if \p DF is empty, or an \c Array containing the
313/// formatted declaration fragments array.
314std::optional<Array>
315serializeDeclarationFragments(const DeclarationFragments &DF) {
316 if (DF.getFragments().empty())
317 return std::nullopt;
318
319 Array Fragments;
320 for (const auto &F : DF.getFragments()) {
321 Object Fragment;
322 Fragment["spelling"] = F.Spelling;
323 Fragment["kind"] = DeclarationFragments::getFragmentKindString(F.Kind);
324 if (!F.PreciseIdentifier.empty())
325 Fragment["preciseIdentifier"] = F.PreciseIdentifier;
326 Fragments.emplace_back(std::move(Fragment));
327 }
328
329 return Fragments;
330}
331
332/// Serialize the \c names field of a symbol as specified by the Symbol Graph
333/// format.
334///
335/// The Symbol Graph names field contains multiple representations of a symbol
336/// that can be used for different applications:
337/// - \c title : The simple declared name of the symbol;
338/// - \c subHeading : An array of declaration fragments that provides tags,
339/// and potentially more tokens (for example the \c +/- symbol for
340/// Objective-C methods). Can be used as sub-headings for documentation.
341Object serializeNames(const APIRecord *Record) {
342 Object Names;
343 Names["title"] = Record->Name;
344
345 serializeArray(Names, "subHeading",
346 serializeDeclarationFragments(Record->SubHeading));
347 DeclarationFragments NavigatorFragments;
348 // The +/- prefix for Objective-C methods is important information, and
349 // should be included in the navigator fragment. The entire subheading is
350 // not included as it can contain too much information for other records.
351 switch (Record->getKind()) {
353 NavigatorFragments.append("+ ", DeclarationFragments::FragmentKind::Text,
354 /*PreciseIdentifier*/ "");
355 break;
357 NavigatorFragments.append("- ", DeclarationFragments::FragmentKind::Text,
358 /*PreciseIdentifier*/ "");
359 break;
360 default:
361 break;
362 }
363
364 NavigatorFragments.append(Record->Name,
366 /*PreciseIdentifier*/ "");
367 serializeArray(Names, "navigator",
368 serializeDeclarationFragments(NavigatorFragments));
369
370 return Names;
371}
372
373Object serializeSymbolKind(APIRecord::RecordKind RK, Language Lang) {
374 auto AddLangPrefix = [&Lang](StringRef S) -> std::string {
375 return (getLanguageName(Lang) + "." + S).str();
376 };
377
378 Object Kind;
379 switch (RK) {
381 Kind["identifier"] = AddLangPrefix("unknown");
382 Kind["displayName"] = "Unknown";
383 break;
385 Kind["identifier"] = AddLangPrefix("namespace");
386 Kind["displayName"] = "Namespace";
387 break;
389 Kind["identifier"] = AddLangPrefix("func");
390 Kind["displayName"] = "Function";
391 break;
393 Kind["identifier"] = AddLangPrefix("func");
394 Kind["displayName"] = "Function Template";
395 break;
397 Kind["identifier"] = AddLangPrefix("func");
398 Kind["displayName"] = "Function Template Specialization";
399 break;
401 Kind["identifier"] = AddLangPrefix("var");
402 Kind["displayName"] = "Global Variable Template";
403 break;
405 Kind["identifier"] = AddLangPrefix("var");
406 Kind["displayName"] = "Global Variable Template Specialization";
407 break;
409 Kind["identifier"] = AddLangPrefix("var");
410 Kind["displayName"] = "Global Variable Template Partial Specialization";
411 break;
413 Kind["identifier"] = AddLangPrefix("var");
414 Kind["displayName"] = "Global Variable";
415 break;
417 Kind["identifier"] = AddLangPrefix("enum.case");
418 Kind["displayName"] = "Enumeration Case";
419 break;
421 Kind["identifier"] = AddLangPrefix("enum");
422 Kind["displayName"] = "Enumeration";
423 break;
425 Kind["identifier"] = AddLangPrefix("property");
426 Kind["displayName"] = "Instance Property";
427 break;
429 Kind["identifier"] = AddLangPrefix("struct");
430 Kind["displayName"] = "Structure";
431 break;
433 Kind["identifier"] = AddLangPrefix("property");
434 Kind["displayName"] = "Instance Property";
435 break;
437 Kind["identifier"] = AddLangPrefix("union");
438 Kind["displayName"] = "Union";
439 break;
441 Kind["identifier"] = AddLangPrefix("property");
442 Kind["displayName"] = "Instance Property";
443 break;
445 Kind["identifier"] = AddLangPrefix("type.property");
446 Kind["displayName"] = "Type Property";
447 break;
452 Kind["identifier"] = AddLangPrefix("class");
453 Kind["displayName"] = "Class";
454 break;
456 Kind["identifier"] = AddLangPrefix("method");
457 Kind["displayName"] = "Method Template";
458 break;
460 Kind["identifier"] = AddLangPrefix("method");
461 Kind["displayName"] = "Method Template Specialization";
462 break;
464 Kind["identifier"] = AddLangPrefix("property");
465 Kind["displayName"] = "Template Property";
466 break;
468 Kind["identifier"] = AddLangPrefix("concept");
469 Kind["displayName"] = "Concept";
470 break;
472 Kind["identifier"] = AddLangPrefix("type.method");
473 Kind["displayName"] = "Static Method";
474 break;
476 Kind["identifier"] = AddLangPrefix("method");
477 Kind["displayName"] = "Instance Method";
478 break;
480 Kind["identifier"] = AddLangPrefix("method");
481 Kind["displayName"] = "Constructor";
482 break;
484 Kind["identifier"] = AddLangPrefix("method");
485 Kind["displayName"] = "Destructor";
486 break;
488 Kind["identifier"] = AddLangPrefix("ivar");
489 Kind["displayName"] = "Instance Variable";
490 break;
492 Kind["identifier"] = AddLangPrefix("method");
493 Kind["displayName"] = "Instance Method";
494 break;
496 Kind["identifier"] = AddLangPrefix("type.method");
497 Kind["displayName"] = "Type Method";
498 break;
500 Kind["identifier"] = AddLangPrefix("property");
501 Kind["displayName"] = "Instance Property";
502 break;
504 Kind["identifier"] = AddLangPrefix("type.property");
505 Kind["displayName"] = "Type Property";
506 break;
508 Kind["identifier"] = AddLangPrefix("class");
509 Kind["displayName"] = "Class";
510 break;
512 Kind["identifier"] = AddLangPrefix("class.extension");
513 Kind["displayName"] = "Class Extension";
514 break;
516 Kind["identifier"] = AddLangPrefix("protocol");
517 Kind["displayName"] = "Protocol";
518 break;
520 Kind["identifier"] = AddLangPrefix("macro");
521 Kind["displayName"] = "Macro";
522 break;
524 Kind["identifier"] = AddLangPrefix("typealias");
525 Kind["displayName"] = "Type Alias";
526 break;
527 default:
528 llvm_unreachable("API Record with uninstantiable kind");
529 }
530
531 return Kind;
532}
533
534/// Serialize the symbol kind information.
535///
536/// The Symbol Graph symbol kind property contains a shorthand \c identifier
537/// which is prefixed by the source language name, useful for tooling to parse
538/// the kind, and a \c displayName for rendering human-readable names.
539Object serializeSymbolKind(const APIRecord &Record, Language Lang) {
540 return serializeSymbolKind(Record.KindForDisplay, Lang);
541}
542
543/// Serialize the function signature field, as specified by the
544/// Symbol Graph format.
545///
546/// The Symbol Graph function signature property contains two arrays.
547/// - The \c returns array is the declaration fragments of the return type;
548/// - The \c parameters array contains names and declaration fragments of the
549/// parameters.
550template <typename RecordTy>
551void serializeFunctionSignatureMixin(Object &Paren, const RecordTy &Record) {
552 const auto &FS = Record.Signature;
553 if (FS.empty())
554 return;
555
556 Object Signature;
557 serializeArray(Signature, "returns",
558 serializeDeclarationFragments(FS.getReturnType()));
559
561 for (const auto &P : FS.getParameters()) {
563 Parameter["name"] = P.Name;
564 serializeArray(Parameter, "declarationFragments",
565 serializeDeclarationFragments(P.Fragments));
566 Parameters.emplace_back(std::move(Parameter));
567 }
568
569 if (!Parameters.empty())
570 Signature["parameters"] = std::move(Parameters);
571
572 serializeObject(Paren, "functionSignature", std::move(Signature));
573}
574
575template <typename RecordTy>
576void serializeTemplateMixin(Object &Paren, const RecordTy &Record) {
577 const auto &Template = Record.Templ;
578 if (Template.empty())
579 return;
580
581 Object Generics;
582 Array GenericParameters;
583 for (const auto &Param : Template.getParameters()) {
585 Parameter["name"] = Param.Name;
586 Parameter["index"] = Param.Index;
587 Parameter["depth"] = Param.Depth;
588 GenericParameters.emplace_back(std::move(Parameter));
589 }
590 if (!GenericParameters.empty())
591 Generics["parameters"] = std::move(GenericParameters);
592
593 Array GenericConstraints;
594 for (const auto &Constr : Template.getConstraints()) {
595 Object Constraint;
596 Constraint["kind"] = Constr.Kind;
597 Constraint["lhs"] = Constr.LHS;
598 Constraint["rhs"] = Constr.RHS;
599 GenericConstraints.emplace_back(std::move(Constraint));
600 }
601
602 if (!GenericConstraints.empty())
603 Generics["constraints"] = std::move(GenericConstraints);
604
605 serializeObject(Paren, "swiftGenerics", Generics);
606}
607
608Array generateParentContexts(const SmallVectorImpl<SymbolReference> &Parents,
609 Language Lang) {
610 Array ParentContexts;
611
612 for (const auto &Parent : Parents) {
613 Object Elem;
614 Elem["usr"] = Parent.USR;
615 Elem["name"] = Parent.Name;
616 if (Parent.Record)
617 Elem["kind"] = serializeSymbolKind(Parent.Record->KindForDisplay,
618 Lang)["identifier"];
619 else
620 Elem["kind"] =
621 serializeSymbolKind(APIRecord::RK_Unknown, Lang)["identifier"];
622 ParentContexts.emplace_back(std::move(Elem));
623 }
624
625 return ParentContexts;
626}
627
628/// Walk the records parent information in reverse to generate a hierarchy
629/// suitable for serialization.
631generateHierarchyFromRecord(const APIRecord *Record) {
632 SmallVector<SymbolReference, 8> ReverseHierarchy;
633 for (const auto *Current = Record; Current != nullptr;
634 Current = Current->Parent.Record)
635 ReverseHierarchy.emplace_back(Current);
636
638 std::make_move_iterator(ReverseHierarchy.rbegin()),
639 std::make_move_iterator(ReverseHierarchy.rend()));
640}
641
642SymbolReference getHierarchyReference(const APIRecord *Record,
643 const APISet &API) {
644 // If the parent is a category extended from internal module then we need to
645 // pretend this belongs to the associated interface.
646 if (auto *CategoryRecord = dyn_cast_or_null<ObjCCategoryRecord>(Record)) {
647 return CategoryRecord->Interface;
648 // FIXME: TODO generate path components correctly for categories extending
649 // an external module.
650 }
651
652 return SymbolReference(Record);
653}
654
655} // namespace
656
658 Symbols.emplace_back(std::move(Symbol));
659 return Symbols.back().getAsObject();
660}
661
663 Relationships.emplace_back(std::move(Relationship));
664}
665
666/// Defines the format version emitted by SymbolGraphSerializer.
667const VersionTuple SymbolGraphSerializer::FormatVersion{0, 5, 3};
668
669Object SymbolGraphSerializer::serializeMetadata() const {
670 Object Metadata;
671 serializeObject(Metadata, "formatVersion",
672 serializeSemanticVersion(FormatVersion));
673 Metadata["generator"] = clang::getClangFullVersion();
674 return Metadata;
675}
676
677Object
678SymbolGraphSerializer::serializeModuleObject(StringRef ModuleName) const {
680 Module["name"] = ModuleName;
681 serializeObject(Module, "platform", serializePlatform(API.getTarget()));
682 return Module;
683}
684
685bool SymbolGraphSerializer::shouldSkip(const APIRecord *Record) const {
686 if (!Record)
687 return true;
688
689 // Skip unconditionally unavailable symbols
690 if (Record->Availability.isUnconditionallyUnavailable())
691 return true;
692
693 // Filter out symbols prefixed with an underscored as they are understood to
694 // be symbols clients should not use.
695 if (Record->Name.starts_with("_"))
696 return true;
697
698 // Skip explicitly ignored symbols.
699 if (IgnoresList.shouldIgnore(Record->Name))
700 return true;
701
702 return false;
703}
704
705ExtendedModule &SymbolGraphSerializer::getModuleForCurrentSymbol() {
706 if (!ForceEmitToMainModule && ModuleForCurrentSymbol)
707 return *ModuleForCurrentSymbol;
708
709 return MainModule;
710}
711
712Array SymbolGraphSerializer::serializePathComponents(
713 const APIRecord *Record) const {
714 return Array(map_range(Hierarchy, [](auto Elt) { return Elt.Name; }));
715}
716
717StringRef SymbolGraphSerializer::getRelationshipString(RelationshipKind Kind) {
718 switch (Kind) {
719 case RelationshipKind::MemberOf:
720 return "memberOf";
721 case RelationshipKind::InheritsFrom:
722 return "inheritsFrom";
723 case RelationshipKind::ConformsTo:
724 return "conformsTo";
725 case RelationshipKind::ExtensionTo:
726 return "extensionTo";
727 }
728 llvm_unreachable("Unhandled relationship kind");
729}
730
731void SymbolGraphSerializer::serializeRelationship(RelationshipKind Kind,
732 const SymbolReference &Source,
733 const SymbolReference &Target,
734 ExtendedModule &Into) {
735 Object Relationship;
736 SmallString<64> TestRelLabel;
737 if (EmitSymbolLabelsForTesting) {
738 llvm::raw_svector_ostream OS(TestRelLabel);
739 OS << SymbolGraphSerializer::getRelationshipString(Kind) << " $ "
740 << Source.USR << " $ ";
741 if (Target.USR.empty())
742 OS << Target.Name;
743 else
744 OS << Target.USR;
745 Relationship["!testRelLabel"] = TestRelLabel;
746 }
747 Relationship["source"] = Source.USR;
748 Relationship["target"] = Target.USR;
749 Relationship["targetFallback"] = Target.Name;
750 Relationship["kind"] = SymbolGraphSerializer::getRelationshipString(Kind);
751
752 if (ForceEmitToMainModule)
753 MainModule.addRelationship(std::move(Relationship));
754 else
755 Into.addRelationship(std::move(Relationship));
756}
757
758StringRef SymbolGraphSerializer::getConstraintString(ConstraintKind Kind) {
759 switch (Kind) {
760 case ConstraintKind::Conformance:
761 return "conformance";
762 case ConstraintKind::ConditionalConformance:
763 return "conditionalConformance";
764 }
765 llvm_unreachable("Unhandled constraint kind");
766}
767
768void SymbolGraphSerializer::serializeAPIRecord(const APIRecord *Record) {
769 Object Obj;
770
771 // If we need symbol labels for testing emit the USR as the value and the key
772 // starts with '!'' to ensure it ends up at the top of the object.
773 if (EmitSymbolLabelsForTesting)
774 Obj["!testLabel"] = Record->USR;
775
776 serializeObject(Obj, "identifier",
777 serializeIdentifier(*Record, API.getLanguage()));
778 serializeObject(Obj, "kind", serializeSymbolKind(*Record, API.getLanguage()));
779 serializeObject(Obj, "names", serializeNames(Record));
780 serializeObject(
781 Obj, "location",
782 serializeSourceLocation(Record->Location, /*IncludeFileURI=*/true));
783 serializeArray(Obj, "availability",
784 serializeAvailability(Record->Availability));
785 serializeObject(Obj, "docComment", serializeDocComment(Record->Comment));
786 serializeArray(Obj, "declarationFragments",
787 serializeDeclarationFragments(Record->Declaration));
788
789 Obj["pathComponents"] = serializePathComponents(Record);
790 Obj["accessLevel"] = Record->Access.getAccess();
791
792 ExtendedModule &Module = getModuleForCurrentSymbol();
793 // If the hierarchy has at least one parent and child.
794 if (Hierarchy.size() >= 2)
795 serializeRelationship(MemberOf, Hierarchy.back(),
796 Hierarchy[Hierarchy.size() - 2], Module);
797
798 CurrentSymbol = Module.addSymbol(std::move(Obj));
799}
800
802 if (!Record)
803 return true;
804 if (shouldSkip(Record))
805 return true;
806 Hierarchy.push_back(getHierarchyReference(Record, API));
807 // Defer traversal mechanics to APISetVisitor base implementation
808 auto RetVal = Base::traverseAPIRecord(Record);
809 Hierarchy.pop_back();
810 return RetVal;
811}
812
814 serializeAPIRecord(Record);
815 return true;
816}
817
820 if (!CurrentSymbol)
821 return true;
822
823 serializeFunctionSignatureMixin(*CurrentSymbol, *Record);
824 return true;
825}
826
828 if (!CurrentSymbol)
829 return true;
830
831 for (const auto &Base : Record->Bases)
832 serializeRelationship(RelationshipKind::InheritsFrom, Record, Base,
833 getModuleForCurrentSymbol());
834 return true;
835}
836
839 if (!CurrentSymbol)
840 return true;
841
842 serializeTemplateMixin(*CurrentSymbol, *Record);
843 return true;
844}
845
848 if (!CurrentSymbol)
849 return true;
850
851 serializeTemplateMixin(*CurrentSymbol, *Record);
852 return true;
853}
854
856 const CXXMethodRecord *Record) {
857 if (!CurrentSymbol)
858 return true;
859
860 serializeFunctionSignatureMixin(*CurrentSymbol, *Record);
861 return true;
862}
863
866 if (!CurrentSymbol)
867 return true;
868
869 serializeTemplateMixin(*CurrentSymbol, *Record);
870 return true;
871}
872
875 if (!CurrentSymbol)
876 return true;
877
878 serializeTemplateMixin(*CurrentSymbol, *Record);
879 return true;
880}
881
883 if (!CurrentSymbol)
884 return true;
885
886 serializeTemplateMixin(*CurrentSymbol, *Record);
887 return true;
888}
889
892 if (!CurrentSymbol)
893 return true;
894
895 serializeTemplateMixin(*CurrentSymbol, *Record);
896 return true;
897}
898
902 if (!CurrentSymbol)
903 return true;
904
905 serializeTemplateMixin(*CurrentSymbol, *Record);
906 return true;
907}
908
911 if (!CurrentSymbol)
912 return true;
913
914 serializeTemplateMixin(*CurrentSymbol, *Record);
915 return true;
916}
917
920 if (!CurrentSymbol)
921 return true;
922
923 for (const auto &Protocol : Record->Protocols)
924 serializeRelationship(ConformsTo, Record, Protocol,
925 getModuleForCurrentSymbol());
926
927 return true;
928}
929
932 if (!CurrentSymbol)
933 return true;
934
935 if (!Record->SuperClass.empty())
936 serializeRelationship(InheritsFrom, Record, Record->SuperClass,
937 getModuleForCurrentSymbol());
938 return true;
939}
940
942 const ObjCCategoryRecord *Record) {
943 if (SkipSymbolsInCategoriesToExternalTypes &&
944 !API.findRecordForUSR(Record->Interface.USR))
945 return true;
946
947 auto *CurrentModule = ModuleForCurrentSymbol;
948 if (auto ModuleExtendedByRecord = Record->getExtendedExternalModule())
949 ModuleForCurrentSymbol = &ExtendedModules[*ModuleExtendedByRecord];
950
952 return false;
953
954 bool RetVal = traverseRecordContext(Record);
955 ModuleForCurrentSymbol = CurrentModule;
956 return RetVal;
957}
958
963
965 const ObjCCategoryRecord *Record) {
966 // If we need to create a record for the category in the future do so here,
967 // otherwise everything is set up to pretend that the category is in fact the
968 // interface it extends.
969 for (const auto &Protocol : Record->Protocols)
970 serializeRelationship(ConformsTo, Record->Interface, Protocol,
971 getModuleForCurrentSymbol());
972
973 return true;
974}
975
977 const ObjCMethodRecord *Record) {
978 if (!CurrentSymbol)
979 return true;
980
981 serializeFunctionSignatureMixin(*CurrentSymbol, *Record);
982 return true;
983}
984
987 // FIXME: serialize ivar access control here.
988 return true;
989}
990
992 const TypedefRecord *Record) {
993 // Short-circuit walking up the class hierarchy and handle creating typedef
994 // symbol objects manually as there are additional symbol dropping rules to
995 // respect.
997}
998
1000 // Typedefs of anonymous types have their entries unified with the underlying
1001 // type.
1002 bool ShouldDrop = Record->UnderlyingType.Name.empty();
1003 // enums declared with `NS_OPTION` have a named enum and a named typedef, with
1004 // the same name
1005 ShouldDrop |= (Record->UnderlyingType.Name == Record->Name);
1006 if (ShouldDrop)
1007 return true;
1008
1009 // Create the symbol record if the other symbol droppping rules permit it.
1010 serializeAPIRecord(Record);
1011 if (!CurrentSymbol)
1012 return true;
1013
1014 (*CurrentSymbol)["type"] = Record->UnderlyingType.USR;
1015
1016 return true;
1017}
1018
1019void SymbolGraphSerializer::serializeSingleRecord(const APIRecord *Record) {
1020 switch (Record->getKind()) {
1021 // dispatch to the relevant walkUpFromMethod
1022#define CONCRETE_RECORD(CLASS, BASE, KIND) \
1023 case APIRecord::KIND: { \
1024 walkUpFrom##CLASS(static_cast<const CLASS *>(Record)); \
1025 break; \
1026 }
1028 // otherwise fallback on the only behavior we can implement safely.
1031 break;
1032 default:
1033 llvm_unreachable("API Record with uninstantiable kind");
1034 }
1035}
1036
1037Object SymbolGraphSerializer::serializeGraph(StringRef ModuleName,
1038 ExtendedModule &&EM) {
1039 Object Root;
1040 serializeObject(Root, "metadata", serializeMetadata());
1041 serializeObject(Root, "module", serializeModuleObject(ModuleName));
1042
1043 Root["symbols"] = std::move(EM.Symbols);
1044 Root["relationships"] = std::move(EM.Relationships);
1045
1046 return Root;
1047}
1048
1049void SymbolGraphSerializer::serializeGraphToStream(
1050 raw_ostream &OS, SymbolGraphSerializerOption Options, StringRef ModuleName,
1051 ExtendedModule &&EM) {
1052 Object Root = serializeGraph(ModuleName, std::move(EM));
1053 if (Options.Compact)
1054 OS << formatv("{0}", json::Value(std::move(Root))) << "\n";
1055 else
1056 OS << formatv("{0:2}", json::Value(std::move(Root))) << "\n";
1057}
1058
1060 raw_ostream &OS, const APISet &API, const APIIgnoresList &IgnoresList,
1062 SymbolGraphSerializer Serializer(
1063 API, IgnoresList, Options.EmitSymbolLabelsForTesting,
1064 /*ForceEmitToMainModule=*/true,
1065 /*SkipSymbolsInCategoriesToExternalTypes=*/true);
1066
1067 Serializer.traverseAPISet();
1068 Serializer.serializeGraphToStream(OS, Options, API.ProductName,
1069 std::move(Serializer.MainModule));
1070 // FIXME: TODO handle extended modules here
1071}
1072
1074 raw_ostream &MainOutput, const APISet &API,
1075 const APIIgnoresList &IgnoresList,
1076 llvm::function_ref<std::unique_ptr<llvm::raw_pwrite_stream>(Twine BaseName)>
1077 CreateOutputStream,
1079 SymbolGraphSerializer Serializer(API, IgnoresList,
1081 Serializer.traverseAPISet();
1082
1083 Serializer.serializeGraphToStream(MainOutput, Options, API.ProductName,
1084 std::move(Serializer.MainModule));
1085
1086 for (auto &ExtensionSGF : Serializer.ExtendedModules) {
1087 if (auto ExtensionOS =
1088 CreateOutputStream(API.ProductName + "@" + ExtensionSGF.getKey()))
1089 Serializer.serializeGraphToStream(*ExtensionOS, Options, API.ProductName,
1090 std::move(ExtensionSGF.getValue()));
1091 }
1092}
1093
1094std::optional<Object>
1096 const APISet &API) {
1097 APIRecord *Record = API.findRecordForUSR(USR);
1098 if (!Record)
1099 return {};
1100
1101 Object Root;
1102 APIIgnoresList EmptyIgnores;
1103 SymbolGraphSerializer Serializer(API, EmptyIgnores,
1104 /*EmitSymbolLabelsForTesting*/ false,
1105 /*ForceEmitToMainModule*/ true);
1106
1107 // Set up serializer parent chain
1108 Serializer.Hierarchy = generateHierarchyFromRecord(Record);
1109
1110 Serializer.serializeSingleRecord(Record);
1111 serializeObject(Root, "symbolGraph",
1112 Serializer.serializeGraph(API.ProductName,
1113 std::move(Serializer.MainModule)));
1114
1115 Language Lang = API.getLanguage();
1116 serializeArray(Root, "parentContexts",
1117 generateParentContexts(Serializer.Hierarchy, Lang));
1118
1119 Array RelatedSymbols;
1120
1121 for (const auto &Fragment : Record->Declaration.getFragments()) {
1122 // If we don't have a USR there isn't much we can do.
1123 if (Fragment.PreciseIdentifier.empty())
1124 continue;
1125
1126 APIRecord *RelatedRecord = API.findRecordForUSR(Fragment.PreciseIdentifier);
1127
1128 // If we can't find the record let's skip.
1129 if (!RelatedRecord)
1130 continue;
1131
1132 Object RelatedSymbol;
1133 RelatedSymbol["usr"] = RelatedRecord->USR;
1134 RelatedSymbol["declarationLanguage"] = getLanguageName(Lang);
1135 RelatedSymbol["accessLevel"] = RelatedRecord->Access.getAccess();
1136 RelatedSymbol["filePath"] = RelatedRecord->Location.getFilename();
1137 RelatedSymbol["moduleName"] = API.ProductName;
1138 RelatedSymbol["isSystem"] = RelatedRecord->IsFromSystemHeader;
1139
1140 serializeArray(RelatedSymbol, "parentContexts",
1141 generateParentContexts(
1142 generateHierarchyFromRecord(RelatedRecord), Lang));
1143
1144 RelatedSymbols.push_back(std::move(RelatedSymbol));
1145 }
1146
1147 serializeArray(Root, "relatedSymbols", RelatedSymbols);
1148 return Root;
1149}
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.
llvm::MachO::Record Record
Definition MachO.h:31
llvm::json::Object Object
llvm::json::Array Array
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:340
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:1428
const llvm::Triple & getTarget() const
Get the target triple for the ExtractAPI invocation.
Definition API.h:1431
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
StringRef getLanguageName(FormatStyle::LanguageKind Language)
Definition Format.h:6596
Top level wrappers for InstallAPI frontend operations.
@ 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:909
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:1405