1146 lines · cpp
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-exception6//7//===----------------------------------------------------------------------===//8///9/// \file10/// This file implements the SymbolGraphSerializer.11///12//===----------------------------------------------------------------------===//13 14#include "clang/ExtractAPI/Serialization/SymbolGraphSerializer.h"15#include "clang/Basic/SourceLocation.h"16#include "clang/Basic/Version.h"17#include "clang/ExtractAPI/API.h"18#include "clang/ExtractAPI/DeclarationFragments.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 Paren36/// 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 at44/// 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 into52/// 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 version59/// format.60///61/// A semantic version object contains three numeric fields, representing the62/// \c major, \c minor, and \c patch parts of the version tuple.63/// For example version tuple 1.0.3 is serialized as:64/// \code65/// {66/// "major" : 1,67/// "minor" : 0,68/// "patch" : 369/// }70/// \endcode71///72/// \returns \c std::nullopt if the version \p V is empty, or an \c Object73/// 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 an88/// 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 corresponding100/// 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) {131 Object SourceLocation;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) {147 Object SourceRange;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 obsoleted156/// versions of the symbol as semantic versions, if not default.157/// Availability information also contains flags to indicate if the symbol is158/// 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 availability163/// 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 currently211 case Language::OpenCL:212 case Language::OpenCLCXX:213 case Language::CUDA:214 case Language::HIP:215 case Language::HLSL:216 217 // Languages that the frontend cannot parse and compile218 case Language::Unknown:219 case Language::Asm:220 case Language::LLVM_IR: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 unique231/// 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 by241/// the Symbol Graph format.242///243/// The Symbol Graph \c docComment object contains an array of lines. Each line244/// represents one line of striped documentation comment, with source range245/// information.246/// e.g.247/// \code248/// /// This is a documentation comment249/// ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~' First line.250/// /// with multiple lines.251/// ^~~~~~~~~~~~~~~~~~~~~~~' Second line.252/// \endcode253///254/// \returns \c std::nullopt if \p Comment is empty, or an \c Object containing255/// the formatted lines.256std::optional<Object> serializeDocComment(const DocComment &Comment) {257 if (Comment.empty())258 return std::nullopt;259 260 Object DocComment;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 important279/// parts of a symbol's declaration. The fragments sequence can be joined to280/// form spans of declaration text, with attached information useful for281/// purposes like syntax-highlighting etc. For example:282/// \code283/// 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/// \endcode307///308/// \returns \c std::nullopt if \p DF is empty, or an \c Array containing the309/// 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 Graph329/// format.330///331/// The Symbol Graph names field contains multiple representations of a symbol332/// 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 for336/// 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, and345 // should be included in the navigator fragment. The entire subheading is346 // not included as it can contain too much information for other records.347 switch (Record->getKind()) {348 case APIRecord::RK_ObjCClassMethod:349 NavigatorFragments.append("+ ", DeclarationFragments::FragmentKind::Text,350 /*PreciseIdentifier*/ "");351 break;352 case APIRecord::RK_ObjCInstanceMethod:353 NavigatorFragments.append("- ", DeclarationFragments::FragmentKind::Text,354 /*PreciseIdentifier*/ "");355 break;356 default:357 break;358 }359 360 NavigatorFragments.append(Record->Name,361 DeclarationFragments::FragmentKind::Identifier,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) {376 case APIRecord::RK_Unknown:377 Kind["identifier"] = AddLangPrefix("unknown");378 Kind["displayName"] = "Unknown";379 break;380 case APIRecord::RK_Namespace:381 Kind["identifier"] = AddLangPrefix("namespace");382 Kind["displayName"] = "Namespace";383 break;384 case APIRecord::RK_GlobalFunction:385 Kind["identifier"] = AddLangPrefix("func");386 Kind["displayName"] = "Function";387 break;388 case APIRecord::RK_GlobalFunctionTemplate:389 Kind["identifier"] = AddLangPrefix("func");390 Kind["displayName"] = "Function Template";391 break;392 case APIRecord::RK_GlobalFunctionTemplateSpecialization:393 Kind["identifier"] = AddLangPrefix("func");394 Kind["displayName"] = "Function Template Specialization";395 break;396 case APIRecord::RK_GlobalVariableTemplate:397 Kind["identifier"] = AddLangPrefix("var");398 Kind["displayName"] = "Global Variable Template";399 break;400 case APIRecord::RK_GlobalVariableTemplateSpecialization:401 Kind["identifier"] = AddLangPrefix("var");402 Kind["displayName"] = "Global Variable Template Specialization";403 break;404 case APIRecord::RK_GlobalVariableTemplatePartialSpecialization:405 Kind["identifier"] = AddLangPrefix("var");406 Kind["displayName"] = "Global Variable Template Partial Specialization";407 break;408 case APIRecord::RK_GlobalVariable:409 Kind["identifier"] = AddLangPrefix("var");410 Kind["displayName"] = "Global Variable";411 break;412 case APIRecord::RK_EnumConstant:413 Kind["identifier"] = AddLangPrefix("enum.case");414 Kind["displayName"] = "Enumeration Case";415 break;416 case APIRecord::RK_Enum:417 Kind["identifier"] = AddLangPrefix("enum");418 Kind["displayName"] = "Enumeration";419 break;420 case APIRecord::RK_StructField:421 Kind["identifier"] = AddLangPrefix("property");422 Kind["displayName"] = "Instance Property";423 break;424 case APIRecord::RK_Struct:425 Kind["identifier"] = AddLangPrefix("struct");426 Kind["displayName"] = "Structure";427 break;428 case APIRecord::RK_UnionField:429 Kind["identifier"] = AddLangPrefix("property");430 Kind["displayName"] = "Instance Property";431 break;432 case APIRecord::RK_Union:433 Kind["identifier"] = AddLangPrefix("union");434 Kind["displayName"] = "Union";435 break;436 case APIRecord::RK_CXXField:437 Kind["identifier"] = AddLangPrefix("property");438 Kind["displayName"] = "Instance Property";439 break;440 case APIRecord::RK_StaticField:441 Kind["identifier"] = AddLangPrefix("type.property");442 Kind["displayName"] = "Type Property";443 break;444 case APIRecord::RK_ClassTemplate:445 case APIRecord::RK_ClassTemplateSpecialization:446 case APIRecord::RK_ClassTemplatePartialSpecialization:447 case APIRecord::RK_CXXClass:448 Kind["identifier"] = AddLangPrefix("class");449 Kind["displayName"] = "Class";450 break;451 case APIRecord::RK_CXXMethodTemplate:452 Kind["identifier"] = AddLangPrefix("method");453 Kind["displayName"] = "Method Template";454 break;455 case APIRecord::RK_CXXMethodTemplateSpecialization:456 Kind["identifier"] = AddLangPrefix("method");457 Kind["displayName"] = "Method Template Specialization";458 break;459 case APIRecord::RK_CXXFieldTemplate:460 Kind["identifier"] = AddLangPrefix("property");461 Kind["displayName"] = "Template Property";462 break;463 case APIRecord::RK_Concept:464 Kind["identifier"] = AddLangPrefix("concept");465 Kind["displayName"] = "Concept";466 break;467 case APIRecord::RK_CXXStaticMethod:468 Kind["identifier"] = AddLangPrefix("type.method");469 Kind["displayName"] = "Static Method";470 break;471 case APIRecord::RK_CXXInstanceMethod:472 Kind["identifier"] = AddLangPrefix("method");473 Kind["displayName"] = "Instance Method";474 break;475 case APIRecord::RK_CXXConstructorMethod:476 Kind["identifier"] = AddLangPrefix("method");477 Kind["displayName"] = "Constructor";478 break;479 case APIRecord::RK_CXXDestructorMethod:480 Kind["identifier"] = AddLangPrefix("method");481 Kind["displayName"] = "Destructor";482 break;483 case APIRecord::RK_ObjCIvar:484 Kind["identifier"] = AddLangPrefix("ivar");485 Kind["displayName"] = "Instance Variable";486 break;487 case APIRecord::RK_ObjCInstanceMethod:488 Kind["identifier"] = AddLangPrefix("method");489 Kind["displayName"] = "Instance Method";490 break;491 case APIRecord::RK_ObjCClassMethod:492 Kind["identifier"] = AddLangPrefix("type.method");493 Kind["displayName"] = "Type Method";494 break;495 case APIRecord::RK_ObjCInstanceProperty:496 Kind["identifier"] = AddLangPrefix("property");497 Kind["displayName"] = "Instance Property";498 break;499 case APIRecord::RK_ObjCClassProperty:500 Kind["identifier"] = AddLangPrefix("type.property");501 Kind["displayName"] = "Type Property";502 break;503 case APIRecord::RK_ObjCInterface:504 Kind["identifier"] = AddLangPrefix("class");505 Kind["displayName"] = "Class";506 break;507 case APIRecord::RK_ObjCCategory:508 Kind["identifier"] = AddLangPrefix("class.extension");509 Kind["displayName"] = "Class Extension";510 break;511 case APIRecord::RK_ObjCProtocol:512 Kind["identifier"] = AddLangPrefix("protocol");513 Kind["displayName"] = "Protocol";514 break;515 case APIRecord::RK_MacroDefinition:516 Kind["identifier"] = AddLangPrefix("macro");517 Kind["displayName"] = "Macro";518 break;519 case APIRecord::RK_Typedef: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 identifier533/// which is prefixed by the source language name, useful for tooling to parse534/// 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 the540/// 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 the545/// 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()) {558 Object Parameter;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()) {580 Object Parameter;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 else616 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 hierarchy625/// suitable for serialization.626SmallVector<SymbolReference, 8>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 633 return SmallVector<SymbolReference, 8>(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 to641 // 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 extending645 // an external module.646 }647 648 return SymbolReference(Record);649}650 651} // namespace652 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 673Object674SymbolGraphSerializer::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 symbols686 if (Record->Availability.isUnconditionallyUnavailable())687 return true;688 689 // Filter out symbols prefixed with an underscored as they are understood to690 // 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 else740 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 else751 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 key768 // 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 797bool SymbolGraphSerializer::traverseAPIRecord(const APIRecord *Record) {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 implementation804 auto RetVal = Base::traverseAPIRecord(Record);805 Hierarchy.pop_back();806 return RetVal;807}808 809bool SymbolGraphSerializer::visitAPIRecord(const APIRecord *Record) {810 serializeAPIRecord(Record);811 return true;812}813 814bool SymbolGraphSerializer::visitGlobalFunctionRecord(815 const GlobalFunctionRecord *Record) {816 if (!CurrentSymbol)817 return true;818 819 serializeFunctionSignatureMixin(*CurrentSymbol, *Record);820 return true;821}822 823bool SymbolGraphSerializer::visitCXXClassRecord(const CXXClassRecord *Record) {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 833bool SymbolGraphSerializer::visitClassTemplateRecord(834 const ClassTemplateRecord *Record) {835 if (!CurrentSymbol)836 return true;837 838 serializeTemplateMixin(*CurrentSymbol, *Record);839 return true;840}841 842bool SymbolGraphSerializer::visitClassTemplatePartialSpecializationRecord(843 const ClassTemplatePartialSpecializationRecord *Record) {844 if (!CurrentSymbol)845 return true;846 847 serializeTemplateMixin(*CurrentSymbol, *Record);848 return true;849}850 851bool SymbolGraphSerializer::visitCXXMethodRecord(852 const CXXMethodRecord *Record) {853 if (!CurrentSymbol)854 return true;855 856 serializeFunctionSignatureMixin(*CurrentSymbol, *Record);857 return true;858}859 860bool SymbolGraphSerializer::visitCXXMethodTemplateRecord(861 const CXXMethodTemplateRecord *Record) {862 if (!CurrentSymbol)863 return true;864 865 serializeTemplateMixin(*CurrentSymbol, *Record);866 return true;867}868 869bool SymbolGraphSerializer::visitCXXFieldTemplateRecord(870 const CXXFieldTemplateRecord *Record) {871 if (!CurrentSymbol)872 return true;873 874 serializeTemplateMixin(*CurrentSymbol, *Record);875 return true;876}877 878bool SymbolGraphSerializer::visitConceptRecord(const ConceptRecord *Record) {879 if (!CurrentSymbol)880 return true;881 882 serializeTemplateMixin(*CurrentSymbol, *Record);883 return true;884}885 886bool SymbolGraphSerializer::visitGlobalVariableTemplateRecord(887 const GlobalVariableTemplateRecord *Record) {888 if (!CurrentSymbol)889 return true;890 891 serializeTemplateMixin(*CurrentSymbol, *Record);892 return true;893}894 895bool SymbolGraphSerializer::896 visitGlobalVariableTemplatePartialSpecializationRecord(897 const GlobalVariableTemplatePartialSpecializationRecord *Record) {898 if (!CurrentSymbol)899 return true;900 901 serializeTemplateMixin(*CurrentSymbol, *Record);902 return true;903}904 905bool SymbolGraphSerializer::visitGlobalFunctionTemplateRecord(906 const GlobalFunctionTemplateRecord *Record) {907 if (!CurrentSymbol)908 return true;909 910 serializeTemplateMixin(*CurrentSymbol, *Record);911 return true;912}913 914bool SymbolGraphSerializer::visitObjCContainerRecord(915 const ObjCContainerRecord *Record) {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 926bool SymbolGraphSerializer::visitObjCInterfaceRecord(927 const ObjCInterfaceRecord *Record) {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 937bool SymbolGraphSerializer::traverseObjCCategoryRecord(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 947 if (!walkUpFromObjCCategoryRecord(Record))948 return false;949 950 bool RetVal = traverseRecordContext(Record);951 ModuleForCurrentSymbol = CurrentModule;952 return RetVal;953}954 955bool SymbolGraphSerializer::walkUpFromObjCCategoryRecord(956 const ObjCCategoryRecord *Record) {957 return visitObjCCategoryRecord(Record);958}959 960bool SymbolGraphSerializer::visitObjCCategoryRecord(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 the964 // interface it extends.965 for (const auto &Protocol : Record->Protocols)966 serializeRelationship(ConformsTo, Record->Interface, Protocol,967 getModuleForCurrentSymbol());968 969 return true;970}971 972bool SymbolGraphSerializer::visitObjCMethodRecord(973 const ObjCMethodRecord *Record) {974 if (!CurrentSymbol)975 return true;976 977 serializeFunctionSignatureMixin(*CurrentSymbol, *Record);978 return true;979}980 981bool SymbolGraphSerializer::visitObjCInstanceVariableRecord(982 const ObjCInstanceVariableRecord *Record) {983 // FIXME: serialize ivar access control here.984 return true;985}986 987bool SymbolGraphSerializer::walkUpFromTypedefRecord(988 const TypedefRecord *Record) {989 // Short-circuit walking up the class hierarchy and handle creating typedef990 // symbol objects manually as there are additional symbol dropping rules to991 // respect.992 return visitTypedefRecord(Record);993}994 995bool SymbolGraphSerializer::visitTypedefRecord(const TypedefRecord *Record) {996 // Typedefs of anonymous types have their entries unified with the underlying997 // type.998 bool ShouldDrop = Record->UnderlyingType.Name.empty();999 // enums declared with `NS_OPTION` have a named enum and a named typedef, with1000 // the same name1001 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 walkUpFromMethod1018#define CONCRETE_RECORD(CLASS, BASE, KIND) \1019 case APIRecord::KIND: { \1020 walkUpFrom##CLASS(static_cast<const CLASS *>(Record)); \1021 break; \1022 }1023#include "clang/ExtractAPI/APIRecords.inc"1024 // otherwise fallback on the only behavior we can implement safely.1025 case APIRecord::RK_Unknown:1026 visitAPIRecord(Record);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 else1052 OS << formatv("{0:2}", json::Value(std::move(Root))) << "\n";1053}1054 1055void SymbolGraphSerializer::serializeMainSymbolGraph(1056 raw_ostream &OS, const APISet &API, const APIIgnoresList &IgnoresList,1057 SymbolGraphSerializerOption Options) {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 here1067}1068 1069void SymbolGraphSerializer::serializeWithExtensionGraphs(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,1074 SymbolGraphSerializerOption Options) {1075 SymbolGraphSerializer Serializer(API, IgnoresList,1076 Options.EmitSymbolLabelsForTesting);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>1091SymbolGraphSerializer::serializeSingleSymbolSGF(StringRef USR,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 chain1104 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}1146