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