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 14 #include "clang/ExtractAPI/Serialization/SymbolGraphSerializer.h" 15 #include "clang/Basic/Version.h" 16 #include "clang/ExtractAPI/API.h" 17 #include "clang/ExtractAPI/DeclarationFragments.h" 18 #include "llvm/Support/JSON.h" 19 #include "llvm/Support/Path.h" 20 #include "llvm/Support/VersionTuple.h" 21 #include <type_traits> 22 23 using namespace clang; 24 using namespace clang::extractapi; 25 using namespace llvm; 26 using namespace llvm::json; 27 28 namespace { 29 30 /// Helper function to inject a JSON object \p Obj into another object \p Paren 31 /// at position \p Key. 32 void serializeObject(Object &Paren, StringRef Key, Optional<Object> Obj) { 33 if (Obj) 34 Paren[Key] = std::move(Obj.value()); 35 } 36 37 /// Helper function to inject a JSON array \p Array into object \p Paren at 38 /// position \p Key. 39 void serializeArray(Object &Paren, StringRef Key, Optional<Array> Array) { 40 if (Array) 41 Paren[Key] = std::move(Array.value()); 42 } 43 44 /// Serialize a \c VersionTuple \p V with the Symbol Graph semantic version 45 /// format. 46 /// 47 /// A semantic version object contains three numeric fields, representing the 48 /// \c major, \c minor, and \c patch parts of the version tuple. 49 /// For example version tuple 1.0.3 is serialized as: 50 /// \code 51 /// { 52 /// "major" : 1, 53 /// "minor" : 0, 54 /// "patch" : 3 55 /// } 56 /// \endcode 57 /// 58 /// \returns \c None if the version \p V is empty, or an \c Object containing 59 /// the semantic version representation of \p V. 60 Optional<Object> serializeSemanticVersion(const VersionTuple &V) { 61 if (V.empty()) 62 return None; 63 64 Object Version; 65 Version["major"] = V.getMajor(); 66 Version["minor"] = V.getMinor().value_or(0); 67 Version["patch"] = V.getSubminor().value_or(0); 68 return Version; 69 } 70 71 /// Serialize the OS information in the Symbol Graph platform property. 72 /// 73 /// The OS information in Symbol Graph contains the \c name of the OS, and an 74 /// optional \c minimumVersion semantic version field. 75 Object serializeOperatingSystem(const Triple &T) { 76 Object OS; 77 OS["name"] = T.getOSTypeName(T.getOS()); 78 serializeObject(OS, "minimumVersion", 79 serializeSemanticVersion(T.getMinimumSupportedOSVersion())); 80 return OS; 81 } 82 83 /// Serialize the platform information in the Symbol Graph module section. 84 /// 85 /// The platform object describes a target platform triple in corresponding 86 /// three fields: \c architecture, \c vendor, and \c operatingSystem. 87 Object serializePlatform(const Triple &T) { 88 Object Platform; 89 Platform["architecture"] = T.getArchName(); 90 Platform["vendor"] = T.getVendorName(); 91 Platform["operatingSystem"] = serializeOperatingSystem(T); 92 return Platform; 93 } 94 95 /// Serialize a source position. 96 Object serializeSourcePosition(const PresumedLoc &Loc) { 97 assert(Loc.isValid() && "invalid source position"); 98 99 Object SourcePosition; 100 SourcePosition["line"] = Loc.getLine(); 101 SourcePosition["character"] = Loc.getColumn(); 102 103 return SourcePosition; 104 } 105 106 /// Serialize a source location in file. 107 /// 108 /// \param Loc The presumed location to serialize. 109 /// \param IncludeFileURI If true, include the file path of \p Loc as a URI. 110 /// Defaults to false. 111 Object serializeSourceLocation(const PresumedLoc &Loc, 112 bool IncludeFileURI = false) { 113 Object SourceLocation; 114 serializeObject(SourceLocation, "position", serializeSourcePosition(Loc)); 115 116 if (IncludeFileURI) { 117 std::string FileURI = "file://"; 118 // Normalize file path to use forward slashes for the URI. 119 FileURI += sys::path::convert_to_slash(Loc.getFilename()); 120 SourceLocation["uri"] = FileURI; 121 } 122 123 return SourceLocation; 124 } 125 126 /// Serialize a source range with begin and end locations. 127 Object serializeSourceRange(const PresumedLoc &BeginLoc, 128 const PresumedLoc &EndLoc) { 129 Object SourceRange; 130 serializeObject(SourceRange, "start", serializeSourcePosition(BeginLoc)); 131 serializeObject(SourceRange, "end", serializeSourcePosition(EndLoc)); 132 return SourceRange; 133 } 134 135 /// Serialize the availability attributes of a symbol. 136 /// 137 /// Availability information contains the introduced, deprecated, and obsoleted 138 /// versions of the symbol as semantic versions, if not default. 139 /// Availability information also contains flags to indicate if the symbol is 140 /// unconditionally unavailable or deprecated, 141 /// i.e. \c __attribute__((unavailable)) and \c __attribute__((deprecated)). 142 /// 143 /// \returns \c None if the symbol has default availability attributes, or 144 /// an \c Object containing the formatted availability information. 145 Optional<Object> serializeAvailability(const AvailabilityInfo &Avail) { 146 if (Avail.isDefault()) 147 return None; 148 149 Object Availbility; 150 serializeObject(Availbility, "introducedVersion", 151 serializeSemanticVersion(Avail.Introduced)); 152 serializeObject(Availbility, "deprecatedVersion", 153 serializeSemanticVersion(Avail.Deprecated)); 154 serializeObject(Availbility, "obsoletedVersion", 155 serializeSemanticVersion(Avail.Obsoleted)); 156 if (Avail.isUnavailable()) 157 Availbility["isUnconditionallyUnavailable"] = true; 158 if (Avail.isUnconditionallyDeprecated()) 159 Availbility["isUnconditionallyDeprecated"] = true; 160 161 return Availbility; 162 } 163 164 /// Get the language name string for interface language references. 165 StringRef getLanguageName(Language Lang) { 166 switch (Lang) { 167 case Language::C: 168 return "c"; 169 case Language::ObjC: 170 return "objective-c"; 171 172 // Unsupported language currently 173 case Language::CXX: 174 case Language::ObjCXX: 175 case Language::OpenCL: 176 case Language::OpenCLCXX: 177 case Language::CUDA: 178 case Language::RenderScript: 179 case Language::HIP: 180 case Language::HLSL: 181 182 // Languages that the frontend cannot parse and compile 183 case Language::Unknown: 184 case Language::Asm: 185 case Language::LLVM_IR: 186 llvm_unreachable("Unsupported language kind"); 187 } 188 189 llvm_unreachable("Unhandled language kind"); 190 } 191 192 /// Serialize the identifier object as specified by the Symbol Graph format. 193 /// 194 /// The identifier property of a symbol contains the USR for precise and unique 195 /// references, and the interface language name. 196 Object serializeIdentifier(const APIRecord &Record, Language Lang) { 197 Object Identifier; 198 Identifier["precise"] = Record.USR; 199 Identifier["interfaceLanguage"] = getLanguageName(Lang); 200 201 return Identifier; 202 } 203 204 /// Serialize the documentation comments attached to a symbol, as specified by 205 /// the Symbol Graph format. 206 /// 207 /// The Symbol Graph \c docComment object contains an array of lines. Each line 208 /// represents one line of striped documentation comment, with source range 209 /// information. 210 /// e.g. 211 /// \code 212 /// /// This is a documentation comment 213 /// ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~' First line. 214 /// /// with multiple lines. 215 /// ^~~~~~~~~~~~~~~~~~~~~~~' Second line. 216 /// \endcode 217 /// 218 /// \returns \c None if \p Comment is empty, or an \c Object containing the 219 /// formatted lines. 220 Optional<Object> serializeDocComment(const DocComment &Comment) { 221 if (Comment.empty()) 222 return None; 223 224 Object DocComment; 225 Array LinesArray; 226 for (const auto &CommentLine : Comment) { 227 Object Line; 228 Line["text"] = CommentLine.Text; 229 serializeObject(Line, "range", 230 serializeSourceRange(CommentLine.Begin, CommentLine.End)); 231 LinesArray.emplace_back(std::move(Line)); 232 } 233 serializeArray(DocComment, "lines", LinesArray); 234 235 return DocComment; 236 } 237 238 /// Serialize the declaration fragments of a symbol. 239 /// 240 /// The Symbol Graph declaration fragments is an array of tagged important 241 /// parts of a symbol's declaration. The fragments sequence can be joined to 242 /// form spans of declaration text, with attached information useful for 243 /// purposes like syntax-highlighting etc. For example: 244 /// \code 245 /// const int pi; -> "declarationFragments" : [ 246 /// { 247 /// "kind" : "keyword", 248 /// "spelling" : "const" 249 /// }, 250 /// { 251 /// "kind" : "text", 252 /// "spelling" : " " 253 /// }, 254 /// { 255 /// "kind" : "typeIdentifier", 256 /// "preciseIdentifier" : "c:I", 257 /// "spelling" : "int" 258 /// }, 259 /// { 260 /// "kind" : "text", 261 /// "spelling" : " " 262 /// }, 263 /// { 264 /// "kind" : "identifier", 265 /// "spelling" : "pi" 266 /// } 267 /// ] 268 /// \endcode 269 /// 270 /// \returns \c None if \p DF is empty, or an \c Array containing the formatted 271 /// declaration fragments array. 272 Optional<Array> serializeDeclarationFragments(const DeclarationFragments &DF) { 273 if (DF.getFragments().empty()) 274 return None; 275 276 Array Fragments; 277 for (const auto &F : DF.getFragments()) { 278 Object Fragment; 279 Fragment["spelling"] = F.Spelling; 280 Fragment["kind"] = DeclarationFragments::getFragmentKindString(F.Kind); 281 if (!F.PreciseIdentifier.empty()) 282 Fragment["preciseIdentifier"] = F.PreciseIdentifier; 283 Fragments.emplace_back(std::move(Fragment)); 284 } 285 286 return Fragments; 287 } 288 289 /// Serialize the \c names field of a symbol as specified by the Symbol Graph 290 /// format. 291 /// 292 /// The Symbol Graph names field contains multiple representations of a symbol 293 /// that can be used for different applications: 294 /// - \c title : The simple declared name of the symbol; 295 /// - \c subHeading : An array of declaration fragments that provides tags, 296 /// and potentially more tokens (for example the \c +/- symbol for 297 /// Objective-C methods). Can be used as sub-headings for documentation. 298 Object serializeNames(const APIRecord &Record) { 299 Object Names; 300 Names["title"] = Record.Name; 301 serializeArray(Names, "subHeading", 302 serializeDeclarationFragments(Record.SubHeading)); 303 DeclarationFragments NavigatorFragments; 304 NavigatorFragments.append(Record.Name, 305 DeclarationFragments::FragmentKind::Identifier, 306 /*PreciseIdentifier*/ ""); 307 serializeArray(Names, "navigator", 308 serializeDeclarationFragments(NavigatorFragments)); 309 310 return Names; 311 } 312 313 /// Serialize the symbol kind information. 314 /// 315 /// The Symbol Graph symbol kind property contains a shorthand \c identifier 316 /// which is prefixed by the source language name, useful for tooling to parse 317 /// the kind, and a \c displayName for rendering human-readable names. 318 Object serializeSymbolKind(const APIRecord &Record, Language Lang) { 319 auto AddLangPrefix = [&Lang](StringRef S) -> std::string { 320 return (getLanguageName(Lang) + "." + S).str(); 321 }; 322 323 Object Kind; 324 switch (Record.getKind()) { 325 case APIRecord::RK_GlobalFunction: 326 Kind["identifier"] = AddLangPrefix("func"); 327 Kind["displayName"] = "Function"; 328 break; 329 case APIRecord::RK_GlobalVariable: 330 Kind["identifier"] = AddLangPrefix("var"); 331 Kind["displayName"] = "Global Variable"; 332 break; 333 case APIRecord::RK_EnumConstant: 334 Kind["identifier"] = AddLangPrefix("enum.case"); 335 Kind["displayName"] = "Enumeration Case"; 336 break; 337 case APIRecord::RK_Enum: 338 Kind["identifier"] = AddLangPrefix("enum"); 339 Kind["displayName"] = "Enumeration"; 340 break; 341 case APIRecord::RK_StructField: 342 Kind["identifier"] = AddLangPrefix("property"); 343 Kind["displayName"] = "Instance Property"; 344 break; 345 case APIRecord::RK_Struct: 346 Kind["identifier"] = AddLangPrefix("struct"); 347 Kind["displayName"] = "Structure"; 348 break; 349 case APIRecord::RK_ObjCIvar: 350 Kind["identifier"] = AddLangPrefix("ivar"); 351 Kind["displayName"] = "Instance Variable"; 352 break; 353 case APIRecord::RK_ObjCMethod: 354 if (dyn_cast<ObjCMethodRecord>(&Record)->IsInstanceMethod) { 355 Kind["identifier"] = AddLangPrefix("method"); 356 Kind["displayName"] = "Instance Method"; 357 } else { 358 Kind["identifier"] = AddLangPrefix("type.method"); 359 Kind["displayName"] = "Type Method"; 360 } 361 break; 362 case APIRecord::RK_ObjCProperty: 363 Kind["identifier"] = AddLangPrefix("property"); 364 Kind["displayName"] = "Instance Property"; 365 break; 366 case APIRecord::RK_ObjCInterface: 367 Kind["identifier"] = AddLangPrefix("class"); 368 Kind["displayName"] = "Class"; 369 break; 370 case APIRecord::RK_ObjCCategory: 371 // We don't serialize out standalone Objective-C category symbols yet. 372 llvm_unreachable("Serializing standalone Objective-C category symbols is " 373 "not supported."); 374 break; 375 case APIRecord::RK_ObjCProtocol: 376 Kind["identifier"] = AddLangPrefix("protocol"); 377 Kind["displayName"] = "Protocol"; 378 break; 379 case APIRecord::RK_MacroDefinition: 380 Kind["identifier"] = AddLangPrefix("macro"); 381 Kind["displayName"] = "Macro"; 382 break; 383 case APIRecord::RK_Typedef: 384 Kind["identifier"] = AddLangPrefix("typealias"); 385 Kind["displayName"] = "Type Alias"; 386 break; 387 } 388 389 return Kind; 390 } 391 392 template <typename RecordTy> 393 Optional<Object> serializeFunctionSignatureMixinImpl(const RecordTy &Record, 394 std::true_type) { 395 const auto &FS = Record.Signature; 396 if (FS.empty()) 397 return None; 398 399 Object Signature; 400 serializeArray(Signature, "returns", 401 serializeDeclarationFragments(FS.getReturnType())); 402 403 Array Parameters; 404 for (const auto &P : FS.getParameters()) { 405 Object Parameter; 406 Parameter["name"] = P.Name; 407 serializeArray(Parameter, "declarationFragments", 408 serializeDeclarationFragments(P.Fragments)); 409 Parameters.emplace_back(std::move(Parameter)); 410 } 411 412 if (!Parameters.empty()) 413 Signature["parameters"] = std::move(Parameters); 414 415 return Signature; 416 } 417 418 template <typename RecordTy> 419 Optional<Object> serializeFunctionSignatureMixinImpl(const RecordTy &Record, 420 std::false_type) { 421 return None; 422 } 423 424 /// Serialize the function signature field, as specified by the 425 /// Symbol Graph format. 426 /// 427 /// The Symbol Graph function signature property contains two arrays. 428 /// - The \c returns array is the declaration fragments of the return type; 429 /// - The \c parameters array contains names and declaration fragments of the 430 /// parameters. 431 /// 432 /// \returns \c None if \p FS is empty, or an \c Object containing the 433 /// formatted function signature. 434 template <typename RecordTy> 435 void serializeFunctionSignatureMixin(Object &Paren, const RecordTy &Record) { 436 serializeObject(Paren, "functionSignature", 437 serializeFunctionSignatureMixinImpl( 438 Record, has_function_signature<RecordTy>())); 439 } 440 441 } // namespace 442 443 void SymbolGraphSerializer::anchor() {} 444 445 /// Defines the format version emitted by SymbolGraphSerializer. 446 const VersionTuple SymbolGraphSerializer::FormatVersion{0, 5, 3}; 447 448 Object SymbolGraphSerializer::serializeMetadata() const { 449 Object Metadata; 450 serializeObject(Metadata, "formatVersion", 451 serializeSemanticVersion(FormatVersion)); 452 Metadata["generator"] = clang::getClangFullVersion(); 453 return Metadata; 454 } 455 456 Object SymbolGraphSerializer::serializeModule() const { 457 Object Module; 458 // The user is expected to always pass `--product-name=` on the command line 459 // to populate this field. 460 Module["name"] = ProductName; 461 serializeObject(Module, "platform", serializePlatform(API.getTarget())); 462 return Module; 463 } 464 465 bool SymbolGraphSerializer::shouldSkip(const APIRecord &Record) const { 466 // Skip unconditionally unavailable symbols 467 if (Record.Availability.isUnconditionallyUnavailable()) 468 return true; 469 470 // Filter out symbols prefixed with an underscored as they are understood to 471 // be symbols clients should not use. 472 if (Record.Name.startswith("_")) 473 return true; 474 475 return false; 476 } 477 478 template <typename RecordTy> 479 Optional<Object> 480 SymbolGraphSerializer::serializeAPIRecord(const RecordTy &Record) const { 481 if (shouldSkip(Record)) 482 return None; 483 484 Object Obj; 485 serializeObject(Obj, "identifier", 486 serializeIdentifier(Record, API.getLanguage())); 487 serializeObject(Obj, "kind", serializeSymbolKind(Record, API.getLanguage())); 488 serializeObject(Obj, "names", serializeNames(Record)); 489 serializeObject( 490 Obj, "location", 491 serializeSourceLocation(Record.Location, /*IncludeFileURI=*/true)); 492 serializeObject(Obj, "availbility", 493 serializeAvailability(Record.Availability)); 494 serializeObject(Obj, "docComment", serializeDocComment(Record.Comment)); 495 serializeArray(Obj, "declarationFragments", 496 serializeDeclarationFragments(Record.Declaration)); 497 // TODO: Once we keep track of symbol access information serialize it 498 // correctly here. 499 Obj["accessLevel"] = "public"; 500 serializeArray(Obj, "pathComponents", Array(PathComponents)); 501 502 serializeFunctionSignatureMixin(Obj, Record); 503 504 return Obj; 505 } 506 507 template <typename MemberTy> 508 void SymbolGraphSerializer::serializeMembers( 509 const APIRecord &Record, 510 const SmallVector<std::unique_ptr<MemberTy>> &Members) { 511 for (const auto &Member : Members) { 512 auto MemberPathComponentGuard = makePathComponentGuard(Member->Name); 513 auto MemberRecord = serializeAPIRecord(*Member); 514 if (!MemberRecord) 515 continue; 516 517 Symbols.emplace_back(std::move(*MemberRecord)); 518 serializeRelationship(RelationshipKind::MemberOf, *Member, Record); 519 } 520 } 521 522 StringRef SymbolGraphSerializer::getRelationshipString(RelationshipKind Kind) { 523 switch (Kind) { 524 case RelationshipKind::MemberOf: 525 return "memberOf"; 526 case RelationshipKind::InheritsFrom: 527 return "inheritsFrom"; 528 case RelationshipKind::ConformsTo: 529 return "conformsTo"; 530 } 531 llvm_unreachable("Unhandled relationship kind"); 532 } 533 534 void SymbolGraphSerializer::serializeRelationship(RelationshipKind Kind, 535 SymbolReference Source, 536 SymbolReference Target) { 537 Object Relationship; 538 Relationship["source"] = Source.USR; 539 Relationship["target"] = Target.USR; 540 Relationship["kind"] = getRelationshipString(Kind); 541 542 Relationships.emplace_back(std::move(Relationship)); 543 } 544 545 void SymbolGraphSerializer::serializeGlobalFunctionRecord( 546 const GlobalFunctionRecord &Record) { 547 auto GlobalPathComponentGuard = makePathComponentGuard(Record.Name); 548 549 auto Obj = serializeAPIRecord(Record); 550 if (!Obj) 551 return; 552 553 Symbols.emplace_back(std::move(*Obj)); 554 } 555 556 void SymbolGraphSerializer::serializeGlobalVariableRecord( 557 const GlobalVariableRecord &Record) { 558 auto GlobalPathComponentGuard = makePathComponentGuard(Record.Name); 559 560 auto Obj = serializeAPIRecord(Record); 561 if (!Obj) 562 return; 563 564 Symbols.emplace_back(std::move(*Obj)); 565 } 566 567 void SymbolGraphSerializer::serializeEnumRecord(const EnumRecord &Record) { 568 auto EnumPathComponentGuard = makePathComponentGuard(Record.Name); 569 auto Enum = serializeAPIRecord(Record); 570 if (!Enum) 571 return; 572 573 Symbols.emplace_back(std::move(*Enum)); 574 serializeMembers(Record, Record.Constants); 575 } 576 577 void SymbolGraphSerializer::serializeStructRecord(const StructRecord &Record) { 578 auto StructPathComponentGuard = makePathComponentGuard(Record.Name); 579 auto Struct = serializeAPIRecord(Record); 580 if (!Struct) 581 return; 582 583 Symbols.emplace_back(std::move(*Struct)); 584 serializeMembers(Record, Record.Fields); 585 } 586 587 void SymbolGraphSerializer::serializeObjCContainerRecord( 588 const ObjCContainerRecord &Record) { 589 auto ObjCContainerPathComponentGuard = makePathComponentGuard(Record.Name); 590 auto ObjCContainer = serializeAPIRecord(Record); 591 if (!ObjCContainer) 592 return; 593 594 Symbols.emplace_back(std::move(*ObjCContainer)); 595 596 serializeMembers(Record, Record.Ivars); 597 serializeMembers(Record, Record.Methods); 598 serializeMembers(Record, Record.Properties); 599 600 for (const auto &Protocol : Record.Protocols) 601 // Record that Record conforms to Protocol. 602 serializeRelationship(RelationshipKind::ConformsTo, Record, Protocol); 603 604 if (auto *ObjCInterface = dyn_cast<ObjCInterfaceRecord>(&Record)) { 605 if (!ObjCInterface->SuperClass.empty()) 606 // If Record is an Objective-C interface record and it has a super class, 607 // record that Record is inherited from SuperClass. 608 serializeRelationship(RelationshipKind::InheritsFrom, Record, 609 ObjCInterface->SuperClass); 610 611 // Members of categories extending an interface are serialized as members of 612 // the interface. 613 for (const auto *Category : ObjCInterface->Categories) { 614 serializeMembers(Record, Category->Ivars); 615 serializeMembers(Record, Category->Methods); 616 serializeMembers(Record, Category->Properties); 617 618 // Surface the protocols of the the category to the interface. 619 for (const auto &Protocol : Category->Protocols) 620 serializeRelationship(RelationshipKind::ConformsTo, Record, Protocol); 621 } 622 } 623 } 624 625 void SymbolGraphSerializer::serializeMacroDefinitionRecord( 626 const MacroDefinitionRecord &Record) { 627 auto MacroPathComponentGuard = makePathComponentGuard(Record.Name); 628 auto Macro = serializeAPIRecord(Record); 629 630 if (!Macro) 631 return; 632 633 Symbols.emplace_back(std::move(*Macro)); 634 } 635 636 void SymbolGraphSerializer::serializeTypedefRecord( 637 const TypedefRecord &Record) { 638 // Typedefs of anonymous types have their entries unified with the underlying 639 // type. 640 bool ShouldDrop = Record.UnderlyingType.Name.empty(); 641 // enums declared with `NS_OPTION` have a named enum and a named typedef, with 642 // the same name 643 ShouldDrop |= (Record.UnderlyingType.Name == Record.Name); 644 if (ShouldDrop) 645 return; 646 647 auto TypedefPathComponentGuard = makePathComponentGuard(Record.Name); 648 auto Typedef = serializeAPIRecord(Record); 649 if (!Typedef) 650 return; 651 652 (*Typedef)["type"] = Record.UnderlyingType.USR; 653 654 Symbols.emplace_back(std::move(*Typedef)); 655 } 656 657 SymbolGraphSerializer::PathComponentGuard 658 SymbolGraphSerializer::makePathComponentGuard(StringRef Component) { 659 return PathComponentGuard(PathComponents, Component); 660 } 661 662 Object SymbolGraphSerializer::serialize() { 663 Object Root; 664 serializeObject(Root, "metadata", serializeMetadata()); 665 serializeObject(Root, "module", serializeModule()); 666 667 // Serialize global variables in the API set. 668 for (const auto &GlobalVar : API.getGlobalVariables()) 669 serializeGlobalVariableRecord(*GlobalVar.second); 670 671 for (const auto &GlobalFunction : API.getGlobalFunctions()) 672 serializeGlobalFunctionRecord(*GlobalFunction.second); 673 674 // Serialize enum records in the API set. 675 for (const auto &Enum : API.getEnums()) 676 serializeEnumRecord(*Enum.second); 677 678 // Serialize struct records in the API set. 679 for (const auto &Struct : API.getStructs()) 680 serializeStructRecord(*Struct.second); 681 682 // Serialize Objective-C interface records in the API set. 683 for (const auto &ObjCInterface : API.getObjCInterfaces()) 684 serializeObjCContainerRecord(*ObjCInterface.second); 685 686 // Serialize Objective-C protocol records in the API set. 687 for (const auto &ObjCProtocol : API.getObjCProtocols()) 688 serializeObjCContainerRecord(*ObjCProtocol.second); 689 690 for (const auto &Macro : API.getMacros()) 691 serializeMacroDefinitionRecord(*Macro.second); 692 693 for (const auto &Typedef : API.getTypedefs()) 694 serializeTypedefRecord(*Typedef.second); 695 696 Root["symbols"] = std::move(Symbols); 697 Root["relationships"] = std::move(Relationships); 698 699 return Root; 700 } 701 702 void SymbolGraphSerializer::serialize(raw_ostream &os) { 703 Object root = serialize(); 704 if (Options.Compact) 705 os << formatv("{0}", Value(std::move(root))) << "\n"; 706 else 707 os << formatv("{0:2}", Value(std::move(root))) << "\n"; 708 } 709