aboutsummaryrefslogtreecommitdiff
path: root/clang/lib/ExtractAPI/Serialization/SymbolGraphSerializer.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'clang/lib/ExtractAPI/Serialization/SymbolGraphSerializer.cpp')
-rw-r--r--clang/lib/ExtractAPI/Serialization/SymbolGraphSerializer.cpp379
1 files changed, 290 insertions, 89 deletions
diff --git a/clang/lib/ExtractAPI/Serialization/SymbolGraphSerializer.cpp b/clang/lib/ExtractAPI/Serialization/SymbolGraphSerializer.cpp
index 709b781968bf..01e9b37d2680 100644
--- a/clang/lib/ExtractAPI/Serialization/SymbolGraphSerializer.cpp
+++ b/clang/lib/ExtractAPI/Serialization/SymbolGraphSerializer.cpp
@@ -12,12 +12,21 @@
//===----------------------------------------------------------------------===//
#include "clang/ExtractAPI/Serialization/SymbolGraphSerializer.h"
+#include "clang/Basic/SourceLocation.h"
#include "clang/Basic/Version.h"
#include "clang/ExtractAPI/API.h"
+#include "clang/ExtractAPI/APIIgnoresList.h"
#include "clang/ExtractAPI/DeclarationFragments.h"
+#include "clang/ExtractAPI/Serialization/SerializerBase.h"
+#include "llvm/ADT/STLExtras.h"
+#include "llvm/ADT/STLFunctionalExtras.h"
+#include "llvm/ADT/SmallVector.h"
+#include "llvm/Support/Casting.h"
+#include "llvm/Support/Compiler.h"
#include "llvm/Support/JSON.h"
#include "llvm/Support/Path.h"
#include "llvm/Support/VersionTuple.h"
+#include <optional>
#include <type_traits>
using namespace clang;
@@ -29,16 +38,16 @@ namespace {
/// Helper function to inject a JSON object \p Obj into another object \p Paren
/// at position \p Key.
-void serializeObject(Object &Paren, StringRef Key, Optional<Object> Obj) {
+void serializeObject(Object &Paren, StringRef Key, std::optional<Object> Obj) {
if (Obj)
- Paren[Key] = std::move(Obj.value());
+ Paren[Key] = std::move(*Obj);
}
/// Helper function to inject a JSON array \p Array into object \p Paren at
/// position \p Key.
-void serializeArray(Object &Paren, StringRef Key, Optional<Array> Array) {
+void serializeArray(Object &Paren, StringRef Key, std::optional<Array> Array) {
if (Array)
- Paren[Key] = std::move(Array.value());
+ Paren[Key] = std::move(*Array);
}
/// Serialize a \c VersionTuple \p V with the Symbol Graph semantic version
@@ -55,11 +64,11 @@ void serializeArray(Object &Paren, StringRef Key, Optional<Array> Array) {
/// }
/// \endcode
///
-/// \returns \c None if the version \p V is empty, or an \c Object containing
-/// the semantic version representation of \p V.
-Optional<Object> serializeSemanticVersion(const VersionTuple &V) {
+/// \returns \c std::nullopt if the version \p V is empty, or an \c Object
+/// containing the semantic version representation of \p V.
+std::optional<Object> serializeSemanticVersion(const VersionTuple &V) {
if (V.empty())
- return None;
+ return std::nullopt;
Object Version;
Version["major"] = V.getMajor();
@@ -135,30 +144,43 @@ Object serializeSourceRange(const PresumedLoc &BeginLoc,
/// Serialize the availability attributes of a symbol.
///
/// Availability information contains the introduced, deprecated, and obsoleted
-/// versions of the symbol as semantic versions, if not default.
-/// Availability information also contains flags to indicate if the symbol is
-/// unconditionally unavailable or deprecated,
-/// i.e. \c __attribute__((unavailable)) and \c __attribute__((deprecated)).
+/// versions of the symbol for a given domain (roughly corresponds to a
+/// platform) as semantic versions, if not default. Availability information
+/// also contains flags to indicate if the symbol is unconditionally unavailable
+/// or deprecated, i.e. \c __attribute__((unavailable)) and \c
+/// __attribute__((deprecated)).
///
-/// \returns \c None if the symbol has default availability attributes, or
-/// an \c Object containing the formatted availability information.
-Optional<Object> serializeAvailability(const AvailabilityInfo &Avail) {
- if (Avail.isDefault())
- return None;
-
- Object Availbility;
- serializeObject(Availbility, "introducedVersion",
- serializeSemanticVersion(Avail.Introduced));
- serializeObject(Availbility, "deprecatedVersion",
- serializeSemanticVersion(Avail.Deprecated));
- serializeObject(Availbility, "obsoletedVersion",
- serializeSemanticVersion(Avail.Obsoleted));
- if (Avail.isUnavailable())
- Availbility["isUnconditionallyUnavailable"] = true;
- if (Avail.isUnconditionallyDeprecated())
- Availbility["isUnconditionallyDeprecated"] = true;
-
- return Availbility;
+/// \returns \c std::nullopt if the symbol has default availability attributes,
+/// or an \c Array containing the formatted availability information.
+std::optional<Array>
+serializeAvailability(const AvailabilitySet &Availabilities) {
+ if (Availabilities.isDefault())
+ return std::nullopt;
+
+ Array AvailabilityArray;
+
+ if (Availabilities.isUnconditionallyDeprecated()) {
+ Object UnconditionallyDeprecated;
+ UnconditionallyDeprecated["domain"] = "*";
+ UnconditionallyDeprecated["isUnconditionallyDeprecated"] = true;
+ AvailabilityArray.emplace_back(std::move(UnconditionallyDeprecated));
+ }
+
+ // Note unconditionally unavailable records are skipped.
+
+ for (const auto &AvailInfo : Availabilities) {
+ Object Availability;
+ Availability["domain"] = AvailInfo.Domain;
+ serializeObject(Availability, "introducedVersion",
+ serializeSemanticVersion(AvailInfo.Introduced));
+ serializeObject(Availability, "deprecatedVersion",
+ serializeSemanticVersion(AvailInfo.Deprecated));
+ serializeObject(Availability, "obsoletedVersion",
+ serializeSemanticVersion(AvailInfo.Obsoleted));
+ AvailabilityArray.emplace_back(std::move(Availability));
+ }
+
+ return AvailabilityArray;
}
/// Get the language name string for interface language references.
@@ -215,11 +237,11 @@ Object serializeIdentifier(const APIRecord &Record, Language Lang) {
/// ^~~~~~~~~~~~~~~~~~~~~~~' Second line.
/// \endcode
///
-/// \returns \c None if \p Comment is empty, or an \c Object containing the
-/// formatted lines.
-Optional<Object> serializeDocComment(const DocComment &Comment) {
+/// \returns \c std::nullopt if \p Comment is empty, or an \c Object containing
+/// the formatted lines.
+std::optional<Object> serializeDocComment(const DocComment &Comment) {
if (Comment.empty())
- return None;
+ return std::nullopt;
Object DocComment;
Array LinesArray;
@@ -267,11 +289,12 @@ Optional<Object> serializeDocComment(const DocComment &Comment) {
/// ]
/// \endcode
///
-/// \returns \c None if \p DF is empty, or an \c Array containing the formatted
-/// declaration fragments array.
-Optional<Array> serializeDeclarationFragments(const DeclarationFragments &DF) {
+/// \returns \c std::nullopt if \p DF is empty, or an \c Array containing the
+/// formatted declaration fragments array.
+std::optional<Array>
+serializeDeclarationFragments(const DeclarationFragments &DF) {
if (DF.getFragments().empty())
- return None;
+ return std::nullopt;
Array Fragments;
for (const auto &F : DF.getFragments()) {
@@ -310,18 +333,16 @@ Object serializeNames(const APIRecord &Record) {
return Names;
}
-/// Serialize the symbol kind information.
-///
-/// The Symbol Graph symbol kind property contains a shorthand \c identifier
-/// which is prefixed by the source language name, useful for tooling to parse
-/// the kind, and a \c displayName for rendering human-readable names.
-Object serializeSymbolKind(const APIRecord &Record, Language Lang) {
+Object serializeSymbolKind(APIRecord::RecordKind RK, Language Lang) {
auto AddLangPrefix = [&Lang](StringRef S) -> std::string {
return (getLanguageName(Lang) + "." + S).str();
};
Object Kind;
- switch (Record.getKind()) {
+ switch (RK) {
+ case APIRecord::RK_Unknown:
+ llvm_unreachable("Records should have an explicit kind");
+ break;
case APIRecord::RK_GlobalFunction:
Kind["identifier"] = AddLangPrefix("func");
Kind["displayName"] = "Function";
@@ -350,19 +371,22 @@ Object serializeSymbolKind(const APIRecord &Record, Language Lang) {
Kind["identifier"] = AddLangPrefix("ivar");
Kind["displayName"] = "Instance Variable";
break;
- case APIRecord::RK_ObjCMethod:
- if (dyn_cast<ObjCMethodRecord>(&Record)->IsInstanceMethod) {
- Kind["identifier"] = AddLangPrefix("method");
- Kind["displayName"] = "Instance Method";
- } else {
- Kind["identifier"] = AddLangPrefix("type.method");
- Kind["displayName"] = "Type Method";
- }
+ case APIRecord::RK_ObjCInstanceMethod:
+ Kind["identifier"] = AddLangPrefix("method");
+ Kind["displayName"] = "Instance Method";
break;
- case APIRecord::RK_ObjCProperty:
+ case APIRecord::RK_ObjCClassMethod:
+ Kind["identifier"] = AddLangPrefix("type.method");
+ Kind["displayName"] = "Type Method";
+ break;
+ case APIRecord::RK_ObjCInstanceProperty:
Kind["identifier"] = AddLangPrefix("property");
Kind["displayName"] = "Instance Property";
break;
+ case APIRecord::RK_ObjCClassProperty:
+ Kind["identifier"] = AddLangPrefix("type.property");
+ Kind["displayName"] = "Type Property";
+ break;
case APIRecord::RK_ObjCInterface:
Kind["identifier"] = AddLangPrefix("class");
Kind["displayName"] = "Class";
@@ -389,12 +413,21 @@ Object serializeSymbolKind(const APIRecord &Record, Language Lang) {
return Kind;
}
+/// Serialize the symbol kind information.
+///
+/// The Symbol Graph symbol kind property contains a shorthand \c identifier
+/// which is prefixed by the source language name, useful for tooling to parse
+/// the kind, and a \c displayName for rendering human-readable names.
+Object serializeSymbolKind(const APIRecord &Record, Language Lang) {
+ return serializeSymbolKind(Record.getKind(), Lang);
+}
+
template <typename RecordTy>
-Optional<Object> serializeFunctionSignatureMixinImpl(const RecordTy &Record,
- std::true_type) {
+std::optional<Object>
+serializeFunctionSignatureMixinImpl(const RecordTy &Record, std::true_type) {
const auto &FS = Record.Signature;
if (FS.empty())
- return None;
+ return std::nullopt;
Object Signature;
serializeArray(Signature, "returns",
@@ -416,9 +449,9 @@ Optional<Object> serializeFunctionSignatureMixinImpl(const RecordTy &Record,
}
template <typename RecordTy>
-Optional<Object> serializeFunctionSignatureMixinImpl(const RecordTy &Record,
- std::false_type) {
- return None;
+std::optional<Object>
+serializeFunctionSignatureMixinImpl(const RecordTy &Record, std::false_type) {
+ return std::nullopt;
}
/// Serialize the function signature field, as specified by the
@@ -429,7 +462,7 @@ Optional<Object> serializeFunctionSignatureMixinImpl(const RecordTy &Record,
/// - The \c parameters array contains names and declaration fragments of the
/// parameters.
///
-/// \returns \c None if \p FS is empty, or an \c Object containing the
+/// \returns \c std::nullopt if \p FS is empty, or an \c Object containing the
/// formatted function signature.
template <typename RecordTy>
void serializeFunctionSignatureMixin(Object &Paren, const RecordTy &Record) {
@@ -438,6 +471,78 @@ void serializeFunctionSignatureMixin(Object &Paren, const RecordTy &Record) {
Record, has_function_signature<RecordTy>()));
}
+struct PathComponent {
+ StringRef USR;
+ StringRef Name;
+ APIRecord::RecordKind Kind;
+
+ PathComponent(StringRef USR, StringRef Name, APIRecord::RecordKind Kind)
+ : USR(USR), Name(Name), Kind(Kind) {}
+};
+
+template <typename RecordTy>
+bool generatePathComponents(
+ const RecordTy &Record, const APISet &API,
+ function_ref<void(const PathComponent &)> ComponentTransformer) {
+ SmallVector<PathComponent, 4> ReverseComponenents;
+ ReverseComponenents.emplace_back(Record.USR, Record.Name, Record.getKind());
+ const auto *CurrentParent = &Record.ParentInformation;
+ while (CurrentParent && !CurrentParent->empty()) {
+ PathComponent CurrentParentComponent(CurrentParent->ParentUSR,
+ CurrentParent->ParentName,
+ CurrentParent->ParentKind);
+
+ auto *ParentRecord = CurrentParent->ParentRecord;
+ // Slow path if we don't have a direct reference to the ParentRecord
+ if (!ParentRecord)
+ ParentRecord = API.findRecordForUSR(CurrentParent->ParentUSR);
+
+ // If the parent is a category then we need to pretend this belongs to the
+ // associated interface.
+ if (auto *CategoryRecord =
+ dyn_cast_or_null<ObjCCategoryRecord>(ParentRecord)) {
+ ParentRecord = API.findRecordForUSR(CategoryRecord->Interface.USR);
+ CurrentParentComponent = PathComponent(CategoryRecord->Interface.USR,
+ CategoryRecord->Interface.Name,
+ APIRecord::RK_ObjCInterface);
+ }
+
+ // The parent record doesn't exist which means the symbol shouldn't be
+ // treated as part of the current product.
+ if (!ParentRecord)
+ return true;
+
+ ReverseComponenents.push_back(std::move(CurrentParentComponent));
+ CurrentParent = &ParentRecord->ParentInformation;
+ }
+
+ for (const auto &PC : reverse(ReverseComponenents))
+ ComponentTransformer(PC);
+
+ return false;
+}
+Object serializeParentContext(const PathComponent &PC, Language Lang) {
+ Object ParentContextElem;
+ ParentContextElem["usr"] = PC.USR;
+ ParentContextElem["name"] = PC.Name;
+ ParentContextElem["kind"] = serializeSymbolKind(PC.Kind, Lang)["identifier"];
+ return ParentContextElem;
+}
+
+template <typename RecordTy>
+Array generateParentContexts(const RecordTy &Record, const APISet &API,
+ Language Lang) {
+ Array ParentContexts;
+ if (generatePathComponents(
+ Record, API, [Lang, &ParentContexts](const PathComponent &PC) {
+ ParentContexts.push_back(serializeParentContext(PC, Lang));
+ }))
+ ParentContexts.clear();
+ ParentContexts.pop_back();
+
+ return ParentContexts;
+}
+
} // namespace
void SymbolGraphSerializer::anchor() {}
@@ -457,14 +562,18 @@ Object SymbolGraphSerializer::serializeModule() const {
Object Module;
// The user is expected to always pass `--product-name=` on the command line
// to populate this field.
- Module["name"] = ProductName;
+ Module["name"] = API.ProductName;
serializeObject(Module, "platform", serializePlatform(API.getTarget()));
return Module;
}
bool SymbolGraphSerializer::shouldSkip(const APIRecord &Record) const {
+ // Skip explicitly ignored symbols.
+ if (IgnoresList.shouldIgnore(Record.Name))
+ return true;
+
// Skip unconditionally unavailable symbols
- if (Record.Availability.isUnconditionallyUnavailable())
+ if (Record.Availabilities.isUnconditionallyUnavailable())
return true;
// Filter out symbols prefixed with an underscored as they are understood to
@@ -476,10 +585,10 @@ bool SymbolGraphSerializer::shouldSkip(const APIRecord &Record) const {
}
template <typename RecordTy>
-Optional<Object>
+std::optional<Object>
SymbolGraphSerializer::serializeAPIRecord(const RecordTy &Record) const {
if (shouldSkip(Record))
- return None;
+ return std::nullopt;
Object Obj;
serializeObject(Obj, "identifier",
@@ -489,15 +598,24 @@ SymbolGraphSerializer::serializeAPIRecord(const RecordTy &Record) const {
serializeObject(
Obj, "location",
serializeSourceLocation(Record.Location, /*IncludeFileURI=*/true));
- serializeObject(Obj, "availbility",
- serializeAvailability(Record.Availability));
+ serializeArray(Obj, "availability",
+ serializeAvailability(Record.Availabilities));
serializeObject(Obj, "docComment", serializeDocComment(Record.Comment));
serializeArray(Obj, "declarationFragments",
serializeDeclarationFragments(Record.Declaration));
// TODO: Once we keep track of symbol access information serialize it
// correctly here.
Obj["accessLevel"] = "public";
- serializeArray(Obj, "pathComponents", Array(PathComponents));
+ SmallVector<StringRef, 4> PathComponentsNames;
+ // If this returns true it indicates that we couldn't find a symbol in the
+ // hierarchy.
+ if (generatePathComponents(Record, API,
+ [&PathComponentsNames](const PathComponent &PC) {
+ PathComponentsNames.push_back(PC.Name);
+ }))
+ return {};
+
+ serializeArray(Obj, "pathComponents", Array(PathComponentsNames));
serializeFunctionSignatureMixin(Obj, Record);
@@ -508,8 +626,10 @@ template <typename MemberTy>
void SymbolGraphSerializer::serializeMembers(
const APIRecord &Record,
const SmallVector<std::unique_ptr<MemberTy>> &Members) {
+ // Members should not be serialized if we aren't recursing.
+ if (!ShouldRecurse)
+ return;
for (const auto &Member : Members) {
- auto MemberPathComponentGuard = makePathComponentGuard(Member->Name);
auto MemberRecord = serializeAPIRecord(*Member);
if (!MemberRecord)
continue;
@@ -537,6 +657,7 @@ void SymbolGraphSerializer::serializeRelationship(RelationshipKind Kind,
Object Relationship;
Relationship["source"] = Source.USR;
Relationship["target"] = Target.USR;
+ Relationship["targetFallback"] = Target.Name;
Relationship["kind"] = getRelationshipString(Kind);
Relationships.emplace_back(std::move(Relationship));
@@ -544,8 +665,6 @@ void SymbolGraphSerializer::serializeRelationship(RelationshipKind Kind,
void SymbolGraphSerializer::serializeGlobalFunctionRecord(
const GlobalFunctionRecord &Record) {
- auto GlobalPathComponentGuard = makePathComponentGuard(Record.Name);
-
auto Obj = serializeAPIRecord(Record);
if (!Obj)
return;
@@ -555,8 +674,6 @@ void SymbolGraphSerializer::serializeGlobalFunctionRecord(
void SymbolGraphSerializer::serializeGlobalVariableRecord(
const GlobalVariableRecord &Record) {
- auto GlobalPathComponentGuard = makePathComponentGuard(Record.Name);
-
auto Obj = serializeAPIRecord(Record);
if (!Obj)
return;
@@ -565,7 +682,6 @@ void SymbolGraphSerializer::serializeGlobalVariableRecord(
}
void SymbolGraphSerializer::serializeEnumRecord(const EnumRecord &Record) {
- auto EnumPathComponentGuard = makePathComponentGuard(Record.Name);
auto Enum = serializeAPIRecord(Record);
if (!Enum)
return;
@@ -575,7 +691,6 @@ void SymbolGraphSerializer::serializeEnumRecord(const EnumRecord &Record) {
}
void SymbolGraphSerializer::serializeStructRecord(const StructRecord &Record) {
- auto StructPathComponentGuard = makePathComponentGuard(Record.Name);
auto Struct = serializeAPIRecord(Record);
if (!Struct)
return;
@@ -586,7 +701,6 @@ void SymbolGraphSerializer::serializeStructRecord(const StructRecord &Record) {
void SymbolGraphSerializer::serializeObjCContainerRecord(
const ObjCContainerRecord &Record) {
- auto ObjCContainerPathComponentGuard = makePathComponentGuard(Record.Name);
auto ObjCContainer = serializeAPIRecord(Record);
if (!ObjCContainer)
return;
@@ -615,7 +729,7 @@ void SymbolGraphSerializer::serializeObjCContainerRecord(
serializeMembers(Record, Category->Methods);
serializeMembers(Record, Category->Properties);
- // Surface the protocols of the the category to the interface.
+ // Surface the protocols of the category to the interface.
for (const auto &Protocol : Category->Protocols)
serializeRelationship(RelationshipKind::ConformsTo, Record, Protocol);
}
@@ -624,7 +738,6 @@ void SymbolGraphSerializer::serializeObjCContainerRecord(
void SymbolGraphSerializer::serializeMacroDefinitionRecord(
const MacroDefinitionRecord &Record) {
- auto MacroPathComponentGuard = makePathComponentGuard(Record.Name);
auto Macro = serializeAPIRecord(Record);
if (!Macro)
@@ -633,6 +746,46 @@ void SymbolGraphSerializer::serializeMacroDefinitionRecord(
Symbols.emplace_back(std::move(*Macro));
}
+void SymbolGraphSerializer::serializeSingleRecord(const APIRecord *Record) {
+ switch (Record->getKind()) {
+ case APIRecord::RK_Unknown:
+ llvm_unreachable("Records should have a known kind!");
+ case APIRecord::RK_GlobalFunction:
+ serializeGlobalFunctionRecord(*cast<GlobalFunctionRecord>(Record));
+ break;
+ case APIRecord::RK_GlobalVariable:
+ serializeGlobalVariableRecord(*cast<GlobalVariableRecord>(Record));
+ break;
+ case APIRecord::RK_Enum:
+ serializeEnumRecord(*cast<EnumRecord>(Record));
+ break;
+ case APIRecord::RK_Struct:
+ serializeStructRecord(*cast<StructRecord>(Record));
+ break;
+ case APIRecord::RK_ObjCInterface:
+ serializeObjCContainerRecord(*cast<ObjCInterfaceRecord>(Record));
+ break;
+ case APIRecord::RK_ObjCProtocol:
+ serializeObjCContainerRecord(*cast<ObjCProtocolRecord>(Record));
+ break;
+ case APIRecord::RK_MacroDefinition:
+ serializeMacroDefinitionRecord(*cast<MacroDefinitionRecord>(Record));
+ break;
+ case APIRecord::RK_Typedef:
+ serializeTypedefRecord(*cast<TypedefRecord>(Record));
+ break;
+ default:
+ if (auto Obj = serializeAPIRecord(*Record)) {
+ Symbols.emplace_back(std::move(*Obj));
+ auto &ParentInformation = Record->ParentInformation;
+ if (!ParentInformation.empty())
+ serializeRelationship(RelationshipKind::MemberOf, *Record,
+ *ParentInformation.ParentRecord);
+ }
+ break;
+ }
+}
+
void SymbolGraphSerializer::serializeTypedefRecord(
const TypedefRecord &Record) {
// Typedefs of anonymous types have their entries unified with the underlying
@@ -644,7 +797,6 @@ void SymbolGraphSerializer::serializeTypedefRecord(
if (ShouldDrop)
return;
- auto TypedefPathComponentGuard = makePathComponentGuard(Record.Name);
auto Typedef = serializeAPIRecord(Record);
if (!Typedef)
return;
@@ -654,16 +806,7 @@ void SymbolGraphSerializer::serializeTypedefRecord(
Symbols.emplace_back(std::move(*Typedef));
}
-SymbolGraphSerializer::PathComponentGuard
-SymbolGraphSerializer::makePathComponentGuard(StringRef Component) {
- return PathComponentGuard(PathComponents, Component);
-}
-
Object SymbolGraphSerializer::serialize() {
- Object Root;
- serializeObject(Root, "metadata", serializeMetadata());
- serializeObject(Root, "module", serializeModule());
-
// Serialize global variables in the API set.
for (const auto &GlobalVar : API.getGlobalVariables())
serializeGlobalVariableRecord(*GlobalVar.second);
@@ -693,6 +836,14 @@ Object SymbolGraphSerializer::serialize() {
for (const auto &Typedef : API.getTypedefs())
serializeTypedefRecord(*Typedef.second);
+ return serializeCurrentGraph();
+}
+
+Object SymbolGraphSerializer::serializeCurrentGraph() {
+ Object Root;
+ serializeObject(Root, "metadata", serializeMetadata());
+ serializeObject(Root, "module", serializeModule());
+
Root["symbols"] = std::move(Symbols);
Root["relationships"] = std::move(Relationships);
@@ -706,3 +857,53 @@ void SymbolGraphSerializer::serialize(raw_ostream &os) {
else
os << formatv("{0:2}", Value(std::move(root))) << "\n";
}
+
+std::optional<Object>
+SymbolGraphSerializer::serializeSingleSymbolSGF(StringRef USR,
+ const APISet &API) {
+ APIRecord *Record = API.findRecordForUSR(USR);
+ if (!Record)
+ return {};
+
+ Object Root;
+ APIIgnoresList EmptyIgnores;
+ SymbolGraphSerializer Serializer(API, EmptyIgnores,
+ /*Options.Compact*/ {true},
+ /*ShouldRecurse*/ false);
+ Serializer.serializeSingleRecord(Record);
+ serializeObject(Root, "symbolGraph", Serializer.serializeCurrentGraph());
+
+ Language Lang = API.getLanguage();
+ serializeArray(Root, "parentContexts",
+ generateParentContexts(*Record, API, Lang));
+
+ Array RelatedSymbols;
+
+ for (const auto &Fragment : Record->Declaration.getFragments()) {
+ // If we don't have a USR there isn't much we can do.
+ if (Fragment.PreciseIdentifier.empty())
+ continue;
+
+ APIRecord *RelatedRecord = API.findRecordForUSR(Fragment.PreciseIdentifier);
+
+ // If we can't find the record let's skip.
+ if (!RelatedRecord)
+ continue;
+
+ Object RelatedSymbol;
+ RelatedSymbol["usr"] = RelatedRecord->USR;
+ RelatedSymbol["declarationLanguage"] = getLanguageName(Lang);
+ // TODO: once we record this properly let's serialize it right.
+ RelatedSymbol["accessLevel"] = "public";
+ RelatedSymbol["filePath"] = RelatedRecord->Location.getFilename();
+ RelatedSymbol["moduleName"] = API.ProductName;
+ RelatedSymbol["isSystem"] = RelatedRecord->IsFromSystemHeader;
+
+ serializeArray(RelatedSymbol, "parentContexts",
+ generateParentContexts(*RelatedRecord, API, Lang));
+ RelatedSymbols.push_back(std::move(RelatedSymbol));
+ }
+
+ serializeArray(Root, "relatedSymbols", RelatedSymbols);
+ return Root;
+}