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