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