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