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/DeclarationFragments.h" 18 #include "llvm/ADT/STLExtras.h" 19 #include "llvm/ADT/STLFunctionalExtras.h" 20 #include "llvm/Support/Casting.h" 21 #include "llvm/Support/Compiler.h" 22 #include "llvm/Support/Path.h" 23 #include "llvm/Support/VersionTuple.h" 24 #include <optional> 25 #include <type_traits> 26 27 using namespace clang; 28 using namespace clang::extractapi; 29 using namespace llvm; 30 using namespace llvm::json; 31 32 namespace { 33 34 /// Helper function to inject a JSON object \p Obj into another object \p Paren 35 /// at position \p Key. 36 void serializeObject(Object &Paren, StringRef Key, std::optional<Object> Obj) { 37 if (Obj) 38 Paren[Key] = std::move(*Obj); 39 } 40 41 /// Helper function to inject a StringRef \p String into an object \p Paren at 42 /// position \p Key 43 void serializeString(Object &Paren, StringRef Key, 44 std::optional<std::string> String) { 45 if (String) 46 Paren[Key] = std::move(*String); 47 } 48 49 /// Helper function to inject a JSON array \p Array into object \p Paren at 50 /// position \p Key. 51 void serializeArray(Object &Paren, StringRef Key, std::optional<Array> Array) { 52 if (Array) 53 Paren[Key] = std::move(*Array); 54 } 55 56 /// Serialize a \c VersionTuple \p V with the Symbol Graph semantic version 57 /// format. 58 /// 59 /// A semantic version object contains three numeric fields, representing the 60 /// \c major, \c minor, and \c patch parts of the version tuple. 61 /// For example version tuple 1.0.3 is serialized as: 62 /// \code 63 /// { 64 /// "major" : 1, 65 /// "minor" : 0, 66 /// "patch" : 3 67 /// } 68 /// \endcode 69 /// 70 /// \returns \c std::nullopt if the version \p V is empty, or an \c Object 71 /// containing the semantic version representation of \p V. 72 std::optional<Object> serializeSemanticVersion(const VersionTuple &V) { 73 if (V.empty()) 74 return std::nullopt; 75 76 Object Version; 77 Version["major"] = V.getMajor(); 78 Version["minor"] = V.getMinor().value_or(0); 79 Version["patch"] = V.getSubminor().value_or(0); 80 return Version; 81 } 82 83 /// Serialize the OS information in the Symbol Graph platform property. 84 /// 85 /// The OS information in Symbol Graph contains the \c name of the OS, and an 86 /// optional \c minimumVersion semantic version field. 87 Object serializeOperatingSystem(const Triple &T) { 88 Object OS; 89 OS["name"] = T.getOSTypeName(T.getOS()); 90 serializeObject(OS, "minimumVersion", 91 serializeSemanticVersion(T.getMinimumSupportedOSVersion())); 92 return OS; 93 } 94 95 /// Serialize the platform information in the Symbol Graph module section. 96 /// 97 /// The platform object describes a target platform triple in corresponding 98 /// three fields: \c architecture, \c vendor, and \c operatingSystem. 99 Object serializePlatform(const Triple &T) { 100 Object Platform; 101 Platform["architecture"] = T.getArchName(); 102 Platform["vendor"] = T.getVendorName(); 103 Platform["operatingSystem"] = serializeOperatingSystem(T); 104 return Platform; 105 } 106 107 /// Serialize a source position. 108 Object serializeSourcePosition(const PresumedLoc &Loc) { 109 assert(Loc.isValid() && "invalid source position"); 110 111 Object SourcePosition; 112 SourcePosition["line"] = Loc.getLine() - 1; 113 SourcePosition["character"] = Loc.getColumn() - 1; 114 115 return SourcePosition; 116 } 117 118 /// Serialize a source location in file. 119 /// 120 /// \param Loc The presumed location to serialize. 121 /// \param IncludeFileURI If true, include the file path of \p Loc as a URI. 122 /// Defaults to false. 123 Object serializeSourceLocation(const PresumedLoc &Loc, 124 bool IncludeFileURI = false) { 125 Object SourceLocation; 126 serializeObject(SourceLocation, "position", serializeSourcePosition(Loc)); 127 128 if (IncludeFileURI) { 129 std::string FileURI = "file://"; 130 // Normalize file path to use forward slashes for the URI. 131 FileURI += sys::path::convert_to_slash(Loc.getFilename()); 132 SourceLocation["uri"] = FileURI; 133 } 134 135 return SourceLocation; 136 } 137 138 /// Serialize a source range with begin and end locations. 139 Object serializeSourceRange(const PresumedLoc &BeginLoc, 140 const PresumedLoc &EndLoc) { 141 Object SourceRange; 142 serializeObject(SourceRange, "start", serializeSourcePosition(BeginLoc)); 143 serializeObject(SourceRange, "end", serializeSourcePosition(EndLoc)); 144 return SourceRange; 145 } 146 147 /// Serialize the availability attributes of a symbol. 148 /// 149 /// Availability information contains the introduced, deprecated, and obsoleted 150 /// versions of the symbol as semantic versions, if not default. 151 /// Availability information also contains flags to indicate if the symbol is 152 /// unconditionally unavailable or deprecated, 153 /// i.e. \c __attribute__((unavailable)) and \c __attribute__((deprecated)). 154 /// 155 /// \returns \c std::nullopt if the symbol has default availability attributes, 156 /// or an \c Array containing an object with the formatted availability 157 /// information. 158 std::optional<Array> serializeAvailability(const AvailabilityInfo &Avail) { 159 if (Avail.isDefault()) 160 return std::nullopt; 161 162 Object Availability; 163 Array AvailabilityArray; 164 Availability["domain"] = Avail.Domain; 165 serializeObject(Availability, "introduced", 166 serializeSemanticVersion(Avail.Introduced)); 167 serializeObject(Availability, "deprecated", 168 serializeSemanticVersion(Avail.Deprecated)); 169 serializeObject(Availability, "obsoleted", 170 serializeSemanticVersion(Avail.Obsoleted)); 171 if (Avail.isUnconditionallyDeprecated()) { 172 Object UnconditionallyDeprecated; 173 UnconditionallyDeprecated["domain"] = "*"; 174 UnconditionallyDeprecated["isUnconditionallyDeprecated"] = true; 175 AvailabilityArray.emplace_back(std::move(UnconditionallyDeprecated)); 176 } 177 if (Avail.isUnconditionallyUnavailable()) { 178 Object UnconditionallyUnavailable; 179 UnconditionallyUnavailable["domain"] = "*"; 180 UnconditionallyUnavailable["isUnconditionallyUnavailable"] = true; 181 AvailabilityArray.emplace_back(std::move(UnconditionallyUnavailable)); 182 } 183 AvailabilityArray.emplace_back(std::move(Availability)); 184 return AvailabilityArray; 185 } 186 187 /// Get the language name string for interface language references. 188 StringRef getLanguageName(Language Lang) { 189 switch (Lang) { 190 case Language::C: 191 return "c"; 192 case Language::ObjC: 193 return "objective-c"; 194 case Language::CXX: 195 return "c++"; 196 case Language::ObjCXX: 197 return "objective-c++"; 198 199 // Unsupported language currently 200 case Language::OpenCL: 201 case Language::OpenCLCXX: 202 case Language::CUDA: 203 case Language::RenderScript: 204 case Language::HIP: 205 case Language::HLSL: 206 207 // Languages that the frontend cannot parse and compile 208 case Language::Unknown: 209 case Language::Asm: 210 case Language::LLVM_IR: 211 llvm_unreachable("Unsupported language kind"); 212 } 213 214 llvm_unreachable("Unhandled language kind"); 215 } 216 217 /// Serialize the identifier object as specified by the Symbol Graph format. 218 /// 219 /// The identifier property of a symbol contains the USR for precise and unique 220 /// references, and the interface language name. 221 Object serializeIdentifier(const APIRecord &Record, Language Lang) { 222 Object Identifier; 223 Identifier["precise"] = Record.USR; 224 Identifier["interfaceLanguage"] = getLanguageName(Lang); 225 226 return Identifier; 227 } 228 229 /// Serialize the documentation comments attached to a symbol, as specified by 230 /// the Symbol Graph format. 231 /// 232 /// The Symbol Graph \c docComment object contains an array of lines. Each line 233 /// represents one line of striped documentation comment, with source range 234 /// information. 235 /// e.g. 236 /// \code 237 /// /// This is a documentation comment 238 /// ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~' First line. 239 /// /// with multiple lines. 240 /// ^~~~~~~~~~~~~~~~~~~~~~~' Second line. 241 /// \endcode 242 /// 243 /// \returns \c std::nullopt if \p Comment is empty, or an \c Object containing 244 /// the formatted lines. 245 std::optional<Object> serializeDocComment(const DocComment &Comment) { 246 if (Comment.empty()) 247 return std::nullopt; 248 249 Object DocComment; 250 Array LinesArray; 251 for (const auto &CommentLine : Comment) { 252 Object Line; 253 Line["text"] = CommentLine.Text; 254 serializeObject(Line, "range", 255 serializeSourceRange(CommentLine.Begin, CommentLine.End)); 256 LinesArray.emplace_back(std::move(Line)); 257 } 258 serializeArray(DocComment, "lines", LinesArray); 259 260 return DocComment; 261 } 262 263 /// Serialize the declaration fragments of a symbol. 264 /// 265 /// The Symbol Graph declaration fragments is an array of tagged important 266 /// parts of a symbol's declaration. The fragments sequence can be joined to 267 /// form spans of declaration text, with attached information useful for 268 /// purposes like syntax-highlighting etc. For example: 269 /// \code 270 /// const int pi; -> "declarationFragments" : [ 271 /// { 272 /// "kind" : "keyword", 273 /// "spelling" : "const" 274 /// }, 275 /// { 276 /// "kind" : "text", 277 /// "spelling" : " " 278 /// }, 279 /// { 280 /// "kind" : "typeIdentifier", 281 /// "preciseIdentifier" : "c:I", 282 /// "spelling" : "int" 283 /// }, 284 /// { 285 /// "kind" : "text", 286 /// "spelling" : " " 287 /// }, 288 /// { 289 /// "kind" : "identifier", 290 /// "spelling" : "pi" 291 /// } 292 /// ] 293 /// \endcode 294 /// 295 /// \returns \c std::nullopt if \p DF is empty, or an \c Array containing the 296 /// formatted declaration fragments array. 297 std::optional<Array> 298 serializeDeclarationFragments(const DeclarationFragments &DF) { 299 if (DF.getFragments().empty()) 300 return std::nullopt; 301 302 Array Fragments; 303 for (const auto &F : DF.getFragments()) { 304 Object Fragment; 305 Fragment["spelling"] = F.Spelling; 306 Fragment["kind"] = DeclarationFragments::getFragmentKindString(F.Kind); 307 if (!F.PreciseIdentifier.empty()) 308 Fragment["preciseIdentifier"] = F.PreciseIdentifier; 309 Fragments.emplace_back(std::move(Fragment)); 310 } 311 312 return Fragments; 313 } 314 315 /// Serialize the \c names field of a symbol as specified by the Symbol Graph 316 /// format. 317 /// 318 /// The Symbol Graph names field contains multiple representations of a symbol 319 /// that can be used for different applications: 320 /// - \c title : The simple declared name of the symbol; 321 /// - \c subHeading : An array of declaration fragments that provides tags, 322 /// and potentially more tokens (for example the \c +/- symbol for 323 /// Objective-C methods). Can be used as sub-headings for documentation. 324 Object serializeNames(const APIRecord &Record) { 325 Object Names; 326 if (auto *CategoryRecord = 327 dyn_cast_or_null<const ObjCCategoryRecord>(&Record)) 328 Names["title"] = 329 (CategoryRecord->Interface.Name + " (" + Record.Name + ")").str(); 330 else 331 Names["title"] = Record.Name; 332 333 serializeArray(Names, "subHeading", 334 serializeDeclarationFragments(Record.SubHeading)); 335 DeclarationFragments NavigatorFragments; 336 NavigatorFragments.append(Record.Name, 337 DeclarationFragments::FragmentKind::Identifier, 338 /*PreciseIdentifier*/ ""); 339 serializeArray(Names, "navigator", 340 serializeDeclarationFragments(NavigatorFragments)); 341 342 return Names; 343 } 344 345 Object serializeSymbolKind(APIRecord::RecordKind RK, Language Lang) { 346 auto AddLangPrefix = [&Lang](StringRef S) -> std::string { 347 return (getLanguageName(Lang) + "." + S).str(); 348 }; 349 350 Object Kind; 351 switch (RK) { 352 case APIRecord::RK_Unknown: 353 llvm_unreachable("Records should have an explicit kind"); 354 break; 355 case APIRecord::RK_Namespace: 356 Kind["identifier"] = AddLangPrefix("namespace"); 357 Kind["displayName"] = "Namespace"; 358 break; 359 case APIRecord::RK_GlobalFunction: 360 Kind["identifier"] = AddLangPrefix("func"); 361 Kind["displayName"] = "Function"; 362 break; 363 case APIRecord::RK_GlobalFunctionTemplate: 364 Kind["identifier"] = AddLangPrefix("func"); 365 Kind["displayName"] = "Function Template"; 366 break; 367 case APIRecord::RK_GlobalFunctionTemplateSpecialization: 368 Kind["identifier"] = AddLangPrefix("func"); 369 Kind["displayName"] = "Function Template Specialization"; 370 break; 371 case APIRecord::RK_GlobalVariableTemplate: 372 Kind["identifier"] = AddLangPrefix("var"); 373 Kind["displayName"] = "Global Variable Template"; 374 break; 375 case APIRecord::RK_GlobalVariableTemplateSpecialization: 376 Kind["identifier"] = AddLangPrefix("var"); 377 Kind["displayName"] = "Global Variable Template Specialization"; 378 break; 379 case APIRecord::RK_GlobalVariableTemplatePartialSpecialization: 380 Kind["identifier"] = AddLangPrefix("var"); 381 Kind["displayName"] = "Global Variable Template Partial Specialization"; 382 break; 383 case APIRecord::RK_GlobalVariable: 384 Kind["identifier"] = AddLangPrefix("var"); 385 Kind["displayName"] = "Global Variable"; 386 break; 387 case APIRecord::RK_EnumConstant: 388 Kind["identifier"] = AddLangPrefix("enum.case"); 389 Kind["displayName"] = "Enumeration Case"; 390 break; 391 case APIRecord::RK_Enum: 392 Kind["identifier"] = AddLangPrefix("enum"); 393 Kind["displayName"] = "Enumeration"; 394 break; 395 case APIRecord::RK_StructField: 396 Kind["identifier"] = AddLangPrefix("property"); 397 Kind["displayName"] = "Instance Property"; 398 break; 399 case APIRecord::RK_Struct: 400 Kind["identifier"] = AddLangPrefix("struct"); 401 Kind["displayName"] = "Structure"; 402 break; 403 case APIRecord::RK_UnionField: 404 Kind["identifier"] = AddLangPrefix("property"); 405 Kind["displayName"] = "Instance Property"; 406 break; 407 case APIRecord::RK_Union: 408 Kind["identifier"] = AddLangPrefix("union"); 409 Kind["displayName"] = "Union"; 410 break; 411 case APIRecord::RK_CXXField: 412 Kind["identifier"] = AddLangPrefix("property"); 413 Kind["displayName"] = "Instance Property"; 414 break; 415 case APIRecord::RK_StaticField: 416 Kind["identifier"] = AddLangPrefix("type.property"); 417 Kind["displayName"] = "Type Property"; 418 break; 419 case APIRecord::RK_ClassTemplate: 420 case APIRecord::RK_ClassTemplateSpecialization: 421 case APIRecord::RK_ClassTemplatePartialSpecialization: 422 case APIRecord::RK_CXXClass: 423 Kind["identifier"] = AddLangPrefix("class"); 424 Kind["displayName"] = "Class"; 425 break; 426 case APIRecord::RK_CXXMethodTemplate: 427 Kind["identifier"] = AddLangPrefix("method"); 428 Kind["displayName"] = "Method Template"; 429 break; 430 case APIRecord::RK_CXXMethodTemplateSpecialization: 431 Kind["identifier"] = AddLangPrefix("method"); 432 Kind["displayName"] = "Method Template Specialization"; 433 break; 434 case APIRecord::RK_CXXFieldTemplate: 435 Kind["identifier"] = AddLangPrefix("property"); 436 Kind["displayName"] = "Template Property"; 437 break; 438 case APIRecord::RK_Concept: 439 Kind["identifier"] = AddLangPrefix("concept"); 440 Kind["displayName"] = "Concept"; 441 break; 442 case APIRecord::RK_CXXStaticMethod: 443 Kind["identifier"] = AddLangPrefix("type.method"); 444 Kind["displayName"] = "Static Method"; 445 break; 446 case APIRecord::RK_CXXInstanceMethod: 447 Kind["identifier"] = AddLangPrefix("method"); 448 Kind["displayName"] = "Instance Method"; 449 break; 450 case APIRecord::RK_CXXConstructorMethod: 451 Kind["identifier"] = AddLangPrefix("method"); 452 Kind["displayName"] = "Constructor"; 453 break; 454 case APIRecord::RK_CXXDestructorMethod: 455 Kind["identifier"] = AddLangPrefix("method"); 456 Kind["displayName"] = "Destructor"; 457 break; 458 case APIRecord::RK_ObjCIvar: 459 Kind["identifier"] = AddLangPrefix("ivar"); 460 Kind["displayName"] = "Instance Variable"; 461 break; 462 case APIRecord::RK_ObjCInstanceMethod: 463 Kind["identifier"] = AddLangPrefix("method"); 464 Kind["displayName"] = "Instance Method"; 465 break; 466 case APIRecord::RK_ObjCClassMethod: 467 Kind["identifier"] = AddLangPrefix("type.method"); 468 Kind["displayName"] = "Type Method"; 469 break; 470 case APIRecord::RK_ObjCInstanceProperty: 471 Kind["identifier"] = AddLangPrefix("property"); 472 Kind["displayName"] = "Instance Property"; 473 break; 474 case APIRecord::RK_ObjCClassProperty: 475 Kind["identifier"] = AddLangPrefix("type.property"); 476 Kind["displayName"] = "Type Property"; 477 break; 478 case APIRecord::RK_ObjCInterface: 479 Kind["identifier"] = AddLangPrefix("class"); 480 Kind["displayName"] = "Class"; 481 break; 482 case APIRecord::RK_ObjCCategory: 483 Kind["identifier"] = AddLangPrefix("class.extension"); 484 Kind["displayName"] = "Class Extension"; 485 break; 486 case APIRecord::RK_ObjCCategoryModule: 487 Kind["identifier"] = AddLangPrefix("module.extension"); 488 Kind["displayName"] = "Module Extension"; 489 break; 490 case APIRecord::RK_ObjCProtocol: 491 Kind["identifier"] = AddLangPrefix("protocol"); 492 Kind["displayName"] = "Protocol"; 493 break; 494 case APIRecord::RK_MacroDefinition: 495 Kind["identifier"] = AddLangPrefix("macro"); 496 Kind["displayName"] = "Macro"; 497 break; 498 case APIRecord::RK_Typedef: 499 Kind["identifier"] = AddLangPrefix("typealias"); 500 Kind["displayName"] = "Type Alias"; 501 break; 502 } 503 504 return Kind; 505 } 506 507 /// Serialize the symbol kind information. 508 /// 509 /// The Symbol Graph symbol kind property contains a shorthand \c identifier 510 /// which is prefixed by the source language name, useful for tooling to parse 511 /// the kind, and a \c displayName for rendering human-readable names. 512 Object serializeSymbolKind(const APIRecord &Record, Language Lang) { 513 return serializeSymbolKind(Record.getKind(), Lang); 514 } 515 516 template <typename RecordTy> 517 std::optional<Object> 518 serializeFunctionSignatureMixinImpl(const RecordTy &Record, std::true_type) { 519 const auto &FS = Record.Signature; 520 if (FS.empty()) 521 return std::nullopt; 522 523 Object Signature; 524 serializeArray(Signature, "returns", 525 serializeDeclarationFragments(FS.getReturnType())); 526 527 Array Parameters; 528 for (const auto &P : FS.getParameters()) { 529 Object Parameter; 530 Parameter["name"] = P.Name; 531 serializeArray(Parameter, "declarationFragments", 532 serializeDeclarationFragments(P.Fragments)); 533 Parameters.emplace_back(std::move(Parameter)); 534 } 535 536 if (!Parameters.empty()) 537 Signature["parameters"] = std::move(Parameters); 538 539 return Signature; 540 } 541 542 template <typename RecordTy> 543 std::optional<Object> 544 serializeFunctionSignatureMixinImpl(const RecordTy &Record, std::false_type) { 545 return std::nullopt; 546 } 547 548 /// Serialize the function signature field, as specified by the 549 /// Symbol Graph format. 550 /// 551 /// The Symbol Graph function signature property contains two arrays. 552 /// - The \c returns array is the declaration fragments of the return type; 553 /// - The \c parameters array contains names and declaration fragments of the 554 /// parameters. 555 /// 556 /// \returns \c std::nullopt if \p FS is empty, or an \c Object containing the 557 /// formatted function signature. 558 template <typename RecordTy> 559 void serializeFunctionSignatureMixin(Object &Paren, const RecordTy &Record) { 560 serializeObject(Paren, "functionSignature", 561 serializeFunctionSignatureMixinImpl( 562 Record, has_function_signature<RecordTy>())); 563 } 564 565 template <typename RecordTy> 566 std::optional<std::string> serializeAccessMixinImpl(const RecordTy &Record, 567 std::true_type) { 568 const auto &AccessControl = Record.Access; 569 std::string Access; 570 if (AccessControl.empty()) 571 return std::nullopt; 572 Access = AccessControl.getAccess(); 573 return Access; 574 } 575 576 template <typename RecordTy> 577 std::optional<std::string> serializeAccessMixinImpl(const RecordTy &Record, 578 std::false_type) { 579 return std::nullopt; 580 } 581 582 template <typename RecordTy> 583 void serializeAccessMixin(Object &Paren, const RecordTy &Record) { 584 auto accessLevel = serializeAccessMixinImpl(Record, has_access<RecordTy>()); 585 if (!accessLevel.has_value()) 586 accessLevel = "public"; 587 serializeString(Paren, "accessLevel", accessLevel); 588 } 589 590 template <typename RecordTy> 591 std::optional<Object> serializeTemplateMixinImpl(const RecordTy &Record, 592 std::true_type) { 593 const auto &Template = Record.Templ; 594 if (Template.empty()) 595 return std::nullopt; 596 597 Object Generics; 598 Array GenericParameters; 599 for (const auto &Param : Template.getParameters()) { 600 Object Parameter; 601 Parameter["name"] = Param.Name; 602 Parameter["index"] = Param.Index; 603 Parameter["depth"] = Param.Depth; 604 GenericParameters.emplace_back(std::move(Parameter)); 605 } 606 if (!GenericParameters.empty()) 607 Generics["parameters"] = std::move(GenericParameters); 608 609 Array GenericConstraints; 610 for (const auto &Constr : Template.getConstraints()) { 611 Object Constraint; 612 Constraint["kind"] = Constr.Kind; 613 Constraint["lhs"] = Constr.LHS; 614 Constraint["rhs"] = Constr.RHS; 615 GenericConstraints.emplace_back(std::move(Constraint)); 616 } 617 618 if (!GenericConstraints.empty()) 619 Generics["constraints"] = std::move(GenericConstraints); 620 621 return Generics; 622 } 623 624 template <typename RecordTy> 625 std::optional<Object> serializeTemplateMixinImpl(const RecordTy &Record, 626 std::false_type) { 627 return std::nullopt; 628 } 629 630 template <typename RecordTy> 631 void serializeTemplateMixin(Object &Paren, const RecordTy &Record) { 632 serializeObject(Paren, "swiftGenerics", 633 serializeTemplateMixinImpl(Record, has_template<RecordTy>())); 634 } 635 636 struct PathComponent { 637 StringRef USR; 638 StringRef Name; 639 APIRecord::RecordKind Kind; 640 641 PathComponent(StringRef USR, StringRef Name, APIRecord::RecordKind Kind) 642 : USR(USR), Name(Name), Kind(Kind) {} 643 }; 644 645 template <typename RecordTy> 646 bool generatePathComponents( 647 const RecordTy &Record, const APISet &API, 648 function_ref<void(const PathComponent &)> ComponentTransformer) { 649 SmallVector<PathComponent, 4> ReverseComponenents; 650 ReverseComponenents.emplace_back(Record.USR, Record.Name, Record.getKind()); 651 const auto *CurrentParent = &Record.ParentInformation; 652 bool FailedToFindParent = false; 653 while (CurrentParent && !CurrentParent->empty()) { 654 PathComponent CurrentParentComponent(CurrentParent->ParentUSR, 655 CurrentParent->ParentName, 656 CurrentParent->ParentKind); 657 658 auto *ParentRecord = CurrentParent->ParentRecord; 659 // Slow path if we don't have a direct reference to the ParentRecord 660 if (!ParentRecord) 661 ParentRecord = API.findRecordForUSR(CurrentParent->ParentUSR); 662 663 // If the parent is a category extended from internal module then we need to 664 // pretend this belongs to the associated interface. 665 if (auto *CategoryRecord = 666 dyn_cast_or_null<ObjCCategoryRecord>(ParentRecord)) { 667 if (!CategoryRecord->IsFromExternalModule) { 668 ParentRecord = API.findRecordForUSR(CategoryRecord->Interface.USR); 669 CurrentParentComponent = PathComponent(CategoryRecord->Interface.USR, 670 CategoryRecord->Interface.Name, 671 APIRecord::RK_ObjCInterface); 672 } 673 } 674 675 // The parent record doesn't exist which means the symbol shouldn't be 676 // treated as part of the current product. 677 if (!ParentRecord) { 678 FailedToFindParent = true; 679 break; 680 } 681 682 ReverseComponenents.push_back(std::move(CurrentParentComponent)); 683 CurrentParent = &ParentRecord->ParentInformation; 684 } 685 686 for (const auto &PC : reverse(ReverseComponenents)) 687 ComponentTransformer(PC); 688 689 return FailedToFindParent; 690 } 691 692 Object serializeParentContext(const PathComponent &PC, Language Lang) { 693 Object ParentContextElem; 694 ParentContextElem["usr"] = PC.USR; 695 ParentContextElem["name"] = PC.Name; 696 ParentContextElem["kind"] = serializeSymbolKind(PC.Kind, Lang)["identifier"]; 697 return ParentContextElem; 698 } 699 700 template <typename RecordTy> 701 Array generateParentContexts(const RecordTy &Record, const APISet &API, 702 Language Lang) { 703 Array ParentContexts; 704 generatePathComponents( 705 Record, API, [Lang, &ParentContexts](const PathComponent &PC) { 706 ParentContexts.push_back(serializeParentContext(PC, Lang)); 707 }); 708 709 return ParentContexts; 710 } 711 } // namespace 712 713 /// Defines the format version emitted by SymbolGraphSerializer. 714 const VersionTuple SymbolGraphSerializer::FormatVersion{0, 5, 3}; 715 716 Object SymbolGraphSerializer::serializeMetadata() const { 717 Object Metadata; 718 serializeObject(Metadata, "formatVersion", 719 serializeSemanticVersion(FormatVersion)); 720 Metadata["generator"] = clang::getClangFullVersion(); 721 return Metadata; 722 } 723 724 Object SymbolGraphSerializer::serializeModule() const { 725 Object Module; 726 // The user is expected to always pass `--product-name=` on the command line 727 // to populate this field. 728 Module["name"] = API.ProductName; 729 serializeObject(Module, "platform", serializePlatform(API.getTarget())); 730 return Module; 731 } 732 733 bool SymbolGraphSerializer::shouldSkip(const APIRecord &Record) const { 734 // Skip explicitly ignored symbols. 735 if (IgnoresList.shouldIgnore(Record.Name)) 736 return true; 737 738 // Skip unconditionally unavailable symbols 739 if (Record.Availability.isUnconditionallyUnavailable()) 740 return true; 741 742 // Filter out symbols prefixed with an underscored as they are understood to 743 // be symbols clients should not use. 744 if (Record.Name.starts_with("_")) 745 return true; 746 747 return false; 748 } 749 750 template <typename RecordTy> 751 std::optional<Object> 752 SymbolGraphSerializer::serializeAPIRecord(const RecordTy &Record) const { 753 if (shouldSkip(Record)) 754 return std::nullopt; 755 756 Object Obj; 757 serializeObject(Obj, "identifier", 758 serializeIdentifier(Record, API.getLanguage())); 759 serializeObject(Obj, "kind", serializeSymbolKind(Record, API.getLanguage())); 760 serializeObject(Obj, "names", serializeNames(Record)); 761 serializeObject( 762 Obj, "location", 763 serializeSourceLocation(Record.Location, /*IncludeFileURI=*/true)); 764 serializeArray(Obj, "availability", 765 serializeAvailability(Record.Availability)); 766 serializeObject(Obj, "docComment", serializeDocComment(Record.Comment)); 767 serializeArray(Obj, "declarationFragments", 768 serializeDeclarationFragments(Record.Declaration)); 769 SmallVector<StringRef, 4> PathComponentsNames; 770 // If this returns true it indicates that we couldn't find a symbol in the 771 // hierarchy. 772 if (generatePathComponents(Record, API, 773 [&PathComponentsNames](const PathComponent &PC) { 774 PathComponentsNames.push_back(PC.Name); 775 })) 776 return {}; 777 778 serializeArray(Obj, "pathComponents", Array(PathComponentsNames)); 779 780 serializeFunctionSignatureMixin(Obj, Record); 781 serializeAccessMixin(Obj, Record); 782 serializeTemplateMixin(Obj, Record); 783 784 return Obj; 785 } 786 787 template <typename MemberTy> 788 void SymbolGraphSerializer::serializeMembers( 789 const APIRecord &Record, 790 const SmallVector<std::unique_ptr<MemberTy>> &Members) { 791 // Members should not be serialized if we aren't recursing. 792 if (!ShouldRecurse) 793 return; 794 for (const auto &Member : Members) { 795 auto MemberRecord = serializeAPIRecord(*Member); 796 if (!MemberRecord) 797 continue; 798 799 Symbols.emplace_back(std::move(*MemberRecord)); 800 serializeRelationship(RelationshipKind::MemberOf, *Member, Record); 801 } 802 } 803 804 StringRef SymbolGraphSerializer::getRelationshipString(RelationshipKind Kind) { 805 switch (Kind) { 806 case RelationshipKind::MemberOf: 807 return "memberOf"; 808 case RelationshipKind::InheritsFrom: 809 return "inheritsFrom"; 810 case RelationshipKind::ConformsTo: 811 return "conformsTo"; 812 case RelationshipKind::ExtensionTo: 813 return "extensionTo"; 814 } 815 llvm_unreachable("Unhandled relationship kind"); 816 } 817 818 StringRef SymbolGraphSerializer::getConstraintString(ConstraintKind Kind) { 819 switch (Kind) { 820 case ConstraintKind::Conformance: 821 return "conformance"; 822 case ConstraintKind::ConditionalConformance: 823 return "conditionalConformance"; 824 } 825 llvm_unreachable("Unhandled constraint kind"); 826 } 827 828 void SymbolGraphSerializer::serializeRelationship(RelationshipKind Kind, 829 SymbolReference Source, 830 SymbolReference Target) { 831 Object Relationship; 832 Relationship["source"] = Source.USR; 833 Relationship["target"] = Target.USR; 834 Relationship["targetFallback"] = Target.Name; 835 Relationship["kind"] = getRelationshipString(Kind); 836 837 Relationships.emplace_back(std::move(Relationship)); 838 } 839 840 void SymbolGraphSerializer::visitNamespaceRecord( 841 const NamespaceRecord &Record) { 842 auto Namespace = serializeAPIRecord(Record); 843 if (!Namespace) 844 return; 845 Symbols.emplace_back(std::move(*Namespace)); 846 if (!Record.ParentInformation.empty()) 847 serializeRelationship(RelationshipKind::MemberOf, Record, 848 Record.ParentInformation.ParentRecord); 849 } 850 851 void SymbolGraphSerializer::visitGlobalFunctionRecord( 852 const GlobalFunctionRecord &Record) { 853 auto Obj = serializeAPIRecord(Record); 854 if (!Obj) 855 return; 856 857 Symbols.emplace_back(std::move(*Obj)); 858 } 859 860 void SymbolGraphSerializer::visitGlobalVariableRecord( 861 const GlobalVariableRecord &Record) { 862 auto Obj = serializeAPIRecord(Record); 863 if (!Obj) 864 return; 865 866 Symbols.emplace_back(std::move(*Obj)); 867 } 868 869 void SymbolGraphSerializer::visitEnumRecord(const EnumRecord &Record) { 870 auto Enum = serializeAPIRecord(Record); 871 if (!Enum) 872 return; 873 874 Symbols.emplace_back(std::move(*Enum)); 875 serializeMembers(Record, Record.Constants); 876 } 877 878 void SymbolGraphSerializer::visitRecordRecord(const RecordRecord &Record) { 879 auto SerializedRecord = serializeAPIRecord(Record); 880 if (!SerializedRecord) 881 return; 882 883 Symbols.emplace_back(std::move(*SerializedRecord)); 884 serializeMembers(Record, Record.Fields); 885 } 886 887 void SymbolGraphSerializer::visitStaticFieldRecord( 888 const StaticFieldRecord &Record) { 889 auto StaticField = serializeAPIRecord(Record); 890 if (!StaticField) 891 return; 892 Symbols.emplace_back(std::move(*StaticField)); 893 serializeRelationship(RelationshipKind::MemberOf, Record, Record.Context); 894 } 895 896 void SymbolGraphSerializer::visitCXXClassRecord(const CXXClassRecord &Record) { 897 auto Class = serializeAPIRecord(Record); 898 if (!Class) 899 return; 900 901 Symbols.emplace_back(std::move(*Class)); 902 for (const auto &Base : Record.Bases) 903 serializeRelationship(RelationshipKind::InheritsFrom, Record, Base); 904 if (!Record.ParentInformation.empty()) 905 serializeRelationship(RelationshipKind::MemberOf, Record, 906 Record.ParentInformation.ParentRecord); 907 } 908 909 void SymbolGraphSerializer::visitClassTemplateRecord( 910 const ClassTemplateRecord &Record) { 911 auto Class = serializeAPIRecord(Record); 912 if (!Class) 913 return; 914 915 Symbols.emplace_back(std::move(*Class)); 916 for (const auto &Base : Record.Bases) 917 serializeRelationship(RelationshipKind::InheritsFrom, Record, Base); 918 if (!Record.ParentInformation.empty()) 919 serializeRelationship(RelationshipKind::MemberOf, Record, 920 Record.ParentInformation.ParentRecord); 921 } 922 923 void SymbolGraphSerializer::visitClassTemplateSpecializationRecord( 924 const ClassTemplateSpecializationRecord &Record) { 925 auto Class = serializeAPIRecord(Record); 926 if (!Class) 927 return; 928 929 Symbols.emplace_back(std::move(*Class)); 930 931 for (const auto &Base : Record.Bases) 932 serializeRelationship(RelationshipKind::InheritsFrom, Record, Base); 933 if (!Record.ParentInformation.empty()) 934 serializeRelationship(RelationshipKind::MemberOf, Record, 935 Record.ParentInformation.ParentRecord); 936 } 937 938 void SymbolGraphSerializer::visitClassTemplatePartialSpecializationRecord( 939 const ClassTemplatePartialSpecializationRecord &Record) { 940 auto Class = serializeAPIRecord(Record); 941 if (!Class) 942 return; 943 944 Symbols.emplace_back(std::move(*Class)); 945 946 for (const auto &Base : Record.Bases) 947 serializeRelationship(RelationshipKind::InheritsFrom, Record, Base); 948 if (!Record.ParentInformation.empty()) 949 serializeRelationship(RelationshipKind::MemberOf, Record, 950 Record.ParentInformation.ParentRecord); 951 } 952 953 void SymbolGraphSerializer::visitCXXInstanceMethodRecord( 954 const CXXInstanceMethodRecord &Record) { 955 auto InstanceMethod = serializeAPIRecord(Record); 956 if (!InstanceMethod) 957 return; 958 959 Symbols.emplace_back(std::move(*InstanceMethod)); 960 serializeRelationship(RelationshipKind::MemberOf, Record, 961 Record.ParentInformation.ParentRecord); 962 } 963 964 void SymbolGraphSerializer::visitCXXStaticMethodRecord( 965 const CXXStaticMethodRecord &Record) { 966 auto StaticMethod = serializeAPIRecord(Record); 967 if (!StaticMethod) 968 return; 969 970 Symbols.emplace_back(std::move(*StaticMethod)); 971 serializeRelationship(RelationshipKind::MemberOf, Record, 972 Record.ParentInformation.ParentRecord); 973 } 974 975 void SymbolGraphSerializer::visitMethodTemplateRecord( 976 const CXXMethodTemplateRecord &Record) { 977 if (!ShouldRecurse) 978 // Ignore child symbols 979 return; 980 auto MethodTemplate = serializeAPIRecord(Record); 981 if (!MethodTemplate) 982 return; 983 Symbols.emplace_back(std::move(*MethodTemplate)); 984 serializeRelationship(RelationshipKind::MemberOf, Record, 985 Record.ParentInformation.ParentRecord); 986 } 987 988 void SymbolGraphSerializer::visitMethodTemplateSpecializationRecord( 989 const CXXMethodTemplateSpecializationRecord &Record) { 990 if (!ShouldRecurse) 991 // Ignore child symbols 992 return; 993 auto MethodTemplateSpecialization = serializeAPIRecord(Record); 994 if (!MethodTemplateSpecialization) 995 return; 996 Symbols.emplace_back(std::move(*MethodTemplateSpecialization)); 997 serializeRelationship(RelationshipKind::MemberOf, Record, 998 Record.ParentInformation.ParentRecord); 999 } 1000 1001 void SymbolGraphSerializer::visitCXXFieldRecord(const CXXFieldRecord &Record) { 1002 if (!ShouldRecurse) 1003 return; 1004 auto CXXField = serializeAPIRecord(Record); 1005 if (!CXXField) 1006 return; 1007 Symbols.emplace_back(std::move(*CXXField)); 1008 serializeRelationship(RelationshipKind::MemberOf, Record, 1009 Record.ParentInformation.ParentRecord); 1010 } 1011 1012 void SymbolGraphSerializer::visitCXXFieldTemplateRecord( 1013 const CXXFieldTemplateRecord &Record) { 1014 if (!ShouldRecurse) 1015 // Ignore child symbols 1016 return; 1017 auto CXXFieldTemplate = serializeAPIRecord(Record); 1018 if (!CXXFieldTemplate) 1019 return; 1020 Symbols.emplace_back(std::move(*CXXFieldTemplate)); 1021 serializeRelationship(RelationshipKind::MemberOf, Record, 1022 Record.ParentInformation.ParentRecord); 1023 } 1024 1025 void SymbolGraphSerializer::visitConceptRecord(const ConceptRecord &Record) { 1026 auto Concept = serializeAPIRecord(Record); 1027 if (!Concept) 1028 return; 1029 1030 Symbols.emplace_back(std::move(*Concept)); 1031 } 1032 1033 void SymbolGraphSerializer::visitGlobalVariableTemplateRecord( 1034 const GlobalVariableTemplateRecord &Record) { 1035 auto GlobalVariableTemplate = serializeAPIRecord(Record); 1036 if (!GlobalVariableTemplate) 1037 return; 1038 Symbols.emplace_back(std::move(*GlobalVariableTemplate)); 1039 } 1040 1041 void SymbolGraphSerializer::visitGlobalVariableTemplateSpecializationRecord( 1042 const GlobalVariableTemplateSpecializationRecord &Record) { 1043 auto GlobalVariableTemplateSpecialization = serializeAPIRecord(Record); 1044 if (!GlobalVariableTemplateSpecialization) 1045 return; 1046 Symbols.emplace_back(std::move(*GlobalVariableTemplateSpecialization)); 1047 } 1048 1049 void SymbolGraphSerializer:: 1050 visitGlobalVariableTemplatePartialSpecializationRecord( 1051 const GlobalVariableTemplatePartialSpecializationRecord &Record) { 1052 auto GlobalVariableTemplatePartialSpecialization = serializeAPIRecord(Record); 1053 if (!GlobalVariableTemplatePartialSpecialization) 1054 return; 1055 Symbols.emplace_back(std::move(*GlobalVariableTemplatePartialSpecialization)); 1056 } 1057 1058 void SymbolGraphSerializer::visitGlobalFunctionTemplateRecord( 1059 const GlobalFunctionTemplateRecord &Record) { 1060 auto GlobalFunctionTemplate = serializeAPIRecord(Record); 1061 if (!GlobalFunctionTemplate) 1062 return; 1063 Symbols.emplace_back(std::move(*GlobalFunctionTemplate)); 1064 } 1065 1066 void SymbolGraphSerializer::visitGlobalFunctionTemplateSpecializationRecord( 1067 const GlobalFunctionTemplateSpecializationRecord &Record) { 1068 auto GlobalFunctionTemplateSpecialization = serializeAPIRecord(Record); 1069 if (!GlobalFunctionTemplateSpecialization) 1070 return; 1071 Symbols.emplace_back(std::move(*GlobalFunctionTemplateSpecialization)); 1072 } 1073 1074 void SymbolGraphSerializer::visitObjCContainerRecord( 1075 const ObjCContainerRecord &Record) { 1076 auto ObjCContainer = serializeAPIRecord(Record); 1077 if (!ObjCContainer) 1078 return; 1079 1080 Symbols.emplace_back(std::move(*ObjCContainer)); 1081 1082 serializeMembers(Record, Record.Ivars); 1083 serializeMembers(Record, Record.Methods); 1084 serializeMembers(Record, Record.Properties); 1085 1086 for (const auto &Protocol : Record.Protocols) 1087 // Record that Record conforms to Protocol. 1088 serializeRelationship(RelationshipKind::ConformsTo, Record, Protocol); 1089 1090 if (auto *ObjCInterface = dyn_cast<ObjCInterfaceRecord>(&Record)) { 1091 if (!ObjCInterface->SuperClass.empty()) 1092 // If Record is an Objective-C interface record and it has a super class, 1093 // record that Record is inherited from SuperClass. 1094 serializeRelationship(RelationshipKind::InheritsFrom, Record, 1095 ObjCInterface->SuperClass); 1096 1097 // Members of categories extending an interface are serialized as members of 1098 // the interface. 1099 for (const auto *Category : ObjCInterface->Categories) { 1100 serializeMembers(Record, Category->Ivars); 1101 serializeMembers(Record, Category->Methods); 1102 serializeMembers(Record, Category->Properties); 1103 1104 // Surface the protocols of the category to the interface. 1105 for (const auto &Protocol : Category->Protocols) 1106 serializeRelationship(RelationshipKind::ConformsTo, Record, Protocol); 1107 } 1108 } 1109 } 1110 1111 void SymbolGraphSerializer::visitObjCCategoryRecord( 1112 const ObjCCategoryRecord &Record) { 1113 if (!Record.IsFromExternalModule) 1114 return; 1115 1116 // Check if the current Category' parent has been visited before, if so skip. 1117 if (!visitedCategories.contains(Record.Interface.Name)) { 1118 visitedCategories.insert(Record.Interface.Name); 1119 Object Obj; 1120 serializeObject(Obj, "identifier", 1121 serializeIdentifier(Record, API.getLanguage())); 1122 serializeObject(Obj, "kind", 1123 serializeSymbolKind(APIRecord::RK_ObjCCategoryModule, 1124 API.getLanguage())); 1125 Obj["accessLevel"] = "public"; 1126 Symbols.emplace_back(std::move(Obj)); 1127 } 1128 1129 Object Relationship; 1130 Relationship["source"] = Record.USR; 1131 Relationship["target"] = Record.Interface.USR; 1132 Relationship["targetFallback"] = Record.Interface.Name; 1133 Relationship["kind"] = getRelationshipString(RelationshipKind::ExtensionTo); 1134 Relationships.emplace_back(std::move(Relationship)); 1135 1136 auto ObjCCategory = serializeAPIRecord(Record); 1137 1138 if (!ObjCCategory) 1139 return; 1140 1141 Symbols.emplace_back(std::move(*ObjCCategory)); 1142 serializeMembers(Record, Record.Methods); 1143 serializeMembers(Record, Record.Properties); 1144 1145 // Surface the protocols of the category to the interface. 1146 for (const auto &Protocol : Record.Protocols) 1147 serializeRelationship(RelationshipKind::ConformsTo, Record, Protocol); 1148 } 1149 1150 void SymbolGraphSerializer::visitMacroDefinitionRecord( 1151 const MacroDefinitionRecord &Record) { 1152 auto Macro = serializeAPIRecord(Record); 1153 1154 if (!Macro) 1155 return; 1156 1157 Symbols.emplace_back(std::move(*Macro)); 1158 } 1159 1160 void SymbolGraphSerializer::serializeSingleRecord(const APIRecord *Record) { 1161 switch (Record->getKind()) { 1162 case APIRecord::RK_Unknown: 1163 llvm_unreachable("Records should have a known kind!"); 1164 case APIRecord::RK_GlobalFunction: 1165 visitGlobalFunctionRecord(*cast<GlobalFunctionRecord>(Record)); 1166 break; 1167 case APIRecord::RK_GlobalVariable: 1168 visitGlobalVariableRecord(*cast<GlobalVariableRecord>(Record)); 1169 break; 1170 case APIRecord::RK_Enum: 1171 visitEnumRecord(*cast<EnumRecord>(Record)); 1172 break; 1173 case APIRecord::RK_Struct: 1174 LLVM_FALLTHROUGH; 1175 case APIRecord::RK_Union: 1176 visitRecordRecord(*cast<RecordRecord>(Record)); 1177 break; 1178 case APIRecord::RK_StaticField: 1179 visitStaticFieldRecord(*cast<StaticFieldRecord>(Record)); 1180 break; 1181 case APIRecord::RK_CXXClass: 1182 visitCXXClassRecord(*cast<CXXClassRecord>(Record)); 1183 break; 1184 case APIRecord::RK_ObjCInterface: 1185 visitObjCContainerRecord(*cast<ObjCInterfaceRecord>(Record)); 1186 break; 1187 case APIRecord::RK_ObjCProtocol: 1188 visitObjCContainerRecord(*cast<ObjCProtocolRecord>(Record)); 1189 break; 1190 case APIRecord::RK_ObjCCategory: 1191 visitObjCCategoryRecord(*cast<ObjCCategoryRecord>(Record)); 1192 break; 1193 case APIRecord::RK_MacroDefinition: 1194 visitMacroDefinitionRecord(*cast<MacroDefinitionRecord>(Record)); 1195 break; 1196 case APIRecord::RK_Typedef: 1197 visitTypedefRecord(*cast<TypedefRecord>(Record)); 1198 break; 1199 default: 1200 if (auto Obj = serializeAPIRecord(*Record)) { 1201 Symbols.emplace_back(std::move(*Obj)); 1202 auto &ParentInformation = Record->ParentInformation; 1203 if (!ParentInformation.empty()) 1204 serializeRelationship(RelationshipKind::MemberOf, *Record, 1205 *ParentInformation.ParentRecord); 1206 } 1207 break; 1208 } 1209 } 1210 1211 void SymbolGraphSerializer::visitTypedefRecord(const TypedefRecord &Record) { 1212 // Typedefs of anonymous types have their entries unified with the underlying 1213 // type. 1214 bool ShouldDrop = Record.UnderlyingType.Name.empty(); 1215 // enums declared with `NS_OPTION` have a named enum and a named typedef, with 1216 // the same name 1217 ShouldDrop |= (Record.UnderlyingType.Name == Record.Name); 1218 if (ShouldDrop) 1219 return; 1220 1221 auto Typedef = serializeAPIRecord(Record); 1222 if (!Typedef) 1223 return; 1224 1225 (*Typedef)["type"] = Record.UnderlyingType.USR; 1226 1227 Symbols.emplace_back(std::move(*Typedef)); 1228 } 1229 1230 Object SymbolGraphSerializer::serialize() { 1231 traverseAPISet(); 1232 return serializeCurrentGraph(); 1233 } 1234 1235 Object SymbolGraphSerializer::serializeCurrentGraph() { 1236 Object Root; 1237 serializeObject(Root, "metadata", serializeMetadata()); 1238 serializeObject(Root, "module", serializeModule()); 1239 1240 Root["symbols"] = std::move(Symbols); 1241 Root["relationships"] = std::move(Relationships); 1242 1243 return Root; 1244 } 1245 1246 void SymbolGraphSerializer::serialize(raw_ostream &os) { 1247 Object root = serialize(); 1248 if (Options.Compact) 1249 os << formatv("{0}", Value(std::move(root))) << "\n"; 1250 else 1251 os << formatv("{0:2}", Value(std::move(root))) << "\n"; 1252 } 1253 1254 std::optional<Object> 1255 SymbolGraphSerializer::serializeSingleSymbolSGF(StringRef USR, 1256 const APISet &API) { 1257 APIRecord *Record = API.findRecordForUSR(USR); 1258 if (!Record) 1259 return {}; 1260 1261 Object Root; 1262 APIIgnoresList EmptyIgnores; 1263 SymbolGraphSerializer Serializer(API, EmptyIgnores, 1264 /*Options.Compact*/ {true}, 1265 /*ShouldRecurse*/ false); 1266 Serializer.serializeSingleRecord(Record); 1267 serializeObject(Root, "symbolGraph", Serializer.serializeCurrentGraph()); 1268 1269 Language Lang = API.getLanguage(); 1270 serializeArray(Root, "parentContexts", 1271 generateParentContexts(*Record, API, Lang)); 1272 1273 Array RelatedSymbols; 1274 1275 for (const auto &Fragment : Record->Declaration.getFragments()) { 1276 // If we don't have a USR there isn't much we can do. 1277 if (Fragment.PreciseIdentifier.empty()) 1278 continue; 1279 1280 APIRecord *RelatedRecord = API.findRecordForUSR(Fragment.PreciseIdentifier); 1281 1282 // If we can't find the record let's skip. 1283 if (!RelatedRecord) 1284 continue; 1285 1286 Object RelatedSymbol; 1287 RelatedSymbol["usr"] = RelatedRecord->USR; 1288 RelatedSymbol["declarationLanguage"] = getLanguageName(Lang); 1289 // TODO: once we record this properly let's serialize it right. 1290 RelatedSymbol["accessLevel"] = "public"; 1291 RelatedSymbol["filePath"] = RelatedRecord->Location.getFilename(); 1292 RelatedSymbol["moduleName"] = API.ProductName; 1293 RelatedSymbol["isSystem"] = RelatedRecord->IsFromSystemHeader; 1294 1295 serializeArray(RelatedSymbol, "parentContexts", 1296 generateParentContexts(*RelatedRecord, API, Lang)); 1297 RelatedSymbols.push_back(std::move(RelatedSymbol)); 1298 } 1299 1300 serializeArray(Root, "relatedSymbols", RelatedSymbols); 1301 return Root; 1302 } 1303