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