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