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_CXXField: 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_StaticField: 412 Kind["identifier"] = AddLangPrefix("type.property"); 413 Kind["displayName"] = "Type Property"; 414 break; 415 case APIRecord::RK_ClassTemplate: 416 case APIRecord::RK_ClassTemplateSpecialization: 417 case APIRecord::RK_ClassTemplatePartialSpecialization: 418 case APIRecord::RK_CXXClass: 419 Kind["identifier"] = AddLangPrefix("class"); 420 Kind["displayName"] = "Class"; 421 break; 422 case APIRecord::RK_CXXMethodTemplate: 423 Kind["identifier"] = AddLangPrefix("method"); 424 Kind["displayName"] = "Method Template"; 425 break; 426 case APIRecord::RK_CXXMethodTemplateSpecialization: 427 Kind["identifier"] = AddLangPrefix("method"); 428 Kind["displayName"] = "Method Template Specialization"; 429 break; 430 case APIRecord::RK_CXXFieldTemplate: 431 Kind["identifier"] = AddLangPrefix("property"); 432 Kind["displayName"] = "Template Property"; 433 break; 434 case APIRecord::RK_Concept: 435 Kind["identifier"] = AddLangPrefix("concept"); 436 Kind["displayName"] = "Concept"; 437 break; 438 case APIRecord::RK_CXXStaticMethod: 439 Kind["identifier"] = AddLangPrefix("type.method"); 440 Kind["displayName"] = "Static Method"; 441 break; 442 case APIRecord::RK_CXXInstanceMethod: 443 Kind["identifier"] = AddLangPrefix("method"); 444 Kind["displayName"] = "Instance Method"; 445 break; 446 case APIRecord::RK_CXXConstructorMethod: 447 Kind["identifier"] = AddLangPrefix("method"); 448 Kind["displayName"] = "Constructor"; 449 break; 450 case APIRecord::RK_CXXDestructorMethod: 451 Kind["identifier"] = AddLangPrefix("method"); 452 Kind["displayName"] = "Destructor"; 453 break; 454 case APIRecord::RK_ObjCIvar: 455 Kind["identifier"] = AddLangPrefix("ivar"); 456 Kind["displayName"] = "Instance Variable"; 457 break; 458 case APIRecord::RK_ObjCInstanceMethod: 459 Kind["identifier"] = AddLangPrefix("method"); 460 Kind["displayName"] = "Instance Method"; 461 break; 462 case APIRecord::RK_ObjCClassMethod: 463 Kind["identifier"] = AddLangPrefix("type.method"); 464 Kind["displayName"] = "Type Method"; 465 break; 466 case APIRecord::RK_ObjCInstanceProperty: 467 Kind["identifier"] = AddLangPrefix("property"); 468 Kind["displayName"] = "Instance Property"; 469 break; 470 case APIRecord::RK_ObjCClassProperty: 471 Kind["identifier"] = AddLangPrefix("type.property"); 472 Kind["displayName"] = "Type Property"; 473 break; 474 case APIRecord::RK_ObjCInterface: 475 Kind["identifier"] = AddLangPrefix("class"); 476 Kind["displayName"] = "Class"; 477 break; 478 case APIRecord::RK_ObjCCategory: 479 Kind["identifier"] = AddLangPrefix("class.extension"); 480 Kind["displayName"] = "Class Extension"; 481 break; 482 case APIRecord::RK_ObjCCategoryModule: 483 Kind["identifier"] = AddLangPrefix("module.extension"); 484 Kind["displayName"] = "Module Extension"; 485 break; 486 case APIRecord::RK_ObjCProtocol: 487 Kind["identifier"] = AddLangPrefix("protocol"); 488 Kind["displayName"] = "Protocol"; 489 break; 490 case APIRecord::RK_MacroDefinition: 491 Kind["identifier"] = AddLangPrefix("macro"); 492 Kind["displayName"] = "Macro"; 493 break; 494 case APIRecord::RK_Typedef: 495 Kind["identifier"] = AddLangPrefix("typealias"); 496 Kind["displayName"] = "Type Alias"; 497 break; 498 } 499 500 return Kind; 501 } 502 503 /// Serialize the symbol kind information. 504 /// 505 /// The Symbol Graph symbol kind property contains a shorthand \c identifier 506 /// which is prefixed by the source language name, useful for tooling to parse 507 /// the kind, and a \c displayName for rendering human-readable names. 508 Object serializeSymbolKind(const APIRecord &Record, Language Lang) { 509 return serializeSymbolKind(Record.getKind(), Lang); 510 } 511 512 template <typename RecordTy> 513 std::optional<Object> 514 serializeFunctionSignatureMixinImpl(const RecordTy &Record, std::true_type) { 515 const auto &FS = Record.Signature; 516 if (FS.empty()) 517 return std::nullopt; 518 519 Object Signature; 520 serializeArray(Signature, "returns", 521 serializeDeclarationFragments(FS.getReturnType())); 522 523 Array Parameters; 524 for (const auto &P : FS.getParameters()) { 525 Object Parameter; 526 Parameter["name"] = P.Name; 527 serializeArray(Parameter, "declarationFragments", 528 serializeDeclarationFragments(P.Fragments)); 529 Parameters.emplace_back(std::move(Parameter)); 530 } 531 532 if (!Parameters.empty()) 533 Signature["parameters"] = std::move(Parameters); 534 535 return Signature; 536 } 537 538 template <typename RecordTy> 539 std::optional<Object> 540 serializeFunctionSignatureMixinImpl(const RecordTy &Record, std::false_type) { 541 return std::nullopt; 542 } 543 544 /// Serialize the function signature field, as specified by the 545 /// Symbol Graph format. 546 /// 547 /// The Symbol Graph function signature property contains two arrays. 548 /// - The \c returns array is the declaration fragments of the return type; 549 /// - The \c parameters array contains names and declaration fragments of the 550 /// parameters. 551 /// 552 /// \returns \c std::nullopt if \p FS is empty, or an \c Object containing the 553 /// formatted function signature. 554 template <typename RecordTy> 555 void serializeFunctionSignatureMixin(Object &Paren, const RecordTy &Record) { 556 serializeObject(Paren, "functionSignature", 557 serializeFunctionSignatureMixinImpl( 558 Record, has_function_signature<RecordTy>())); 559 } 560 561 template <typename RecordTy> 562 std::optional<std::string> serializeAccessMixinImpl(const RecordTy &Record, 563 std::true_type) { 564 const auto &AccessControl = Record.Access; 565 std::string Access; 566 if (AccessControl.empty()) 567 return std::nullopt; 568 Access = AccessControl.getAccess(); 569 return Access; 570 } 571 572 template <typename RecordTy> 573 std::optional<std::string> serializeAccessMixinImpl(const RecordTy &Record, 574 std::false_type) { 575 return std::nullopt; 576 } 577 578 template <typename RecordTy> 579 void serializeAccessMixin(Object &Paren, const RecordTy &Record) { 580 auto accessLevel = serializeAccessMixinImpl(Record, has_access<RecordTy>()); 581 if (!accessLevel.has_value()) 582 accessLevel = "public"; 583 serializeString(Paren, "accessLevel", accessLevel); 584 } 585 586 template <typename RecordTy> 587 std::optional<Object> serializeTemplateMixinImpl(const RecordTy &Record, 588 std::true_type) { 589 const auto &Template = Record.Templ; 590 if (Template.empty()) 591 return std::nullopt; 592 593 Object Generics; 594 Array GenericParameters; 595 for (const auto &Param : Template.getParameters()) { 596 Object Parameter; 597 Parameter["name"] = Param.Name; 598 Parameter["index"] = Param.Index; 599 Parameter["depth"] = Param.Depth; 600 GenericParameters.emplace_back(std::move(Parameter)); 601 } 602 if (!GenericParameters.empty()) 603 Generics["parameters"] = std::move(GenericParameters); 604 605 Array GenericConstraints; 606 for (const auto &Constr : Template.getConstraints()) { 607 Object Constraint; 608 Constraint["kind"] = Constr.Kind; 609 Constraint["lhs"] = Constr.LHS; 610 Constraint["rhs"] = Constr.RHS; 611 GenericConstraints.emplace_back(std::move(Constraint)); 612 } 613 614 if (!GenericConstraints.empty()) 615 Generics["constraints"] = std::move(GenericConstraints); 616 617 return Generics; 618 } 619 620 template <typename RecordTy> 621 std::optional<Object> serializeTemplateMixinImpl(const RecordTy &Record, 622 std::false_type) { 623 return std::nullopt; 624 } 625 626 template <typename RecordTy> 627 void serializeTemplateMixin(Object &Paren, const RecordTy &Record) { 628 serializeObject(Paren, "swiftGenerics", 629 serializeTemplateMixinImpl(Record, has_template<RecordTy>())); 630 } 631 632 struct PathComponent { 633 StringRef USR; 634 StringRef Name; 635 APIRecord::RecordKind Kind; 636 637 PathComponent(StringRef USR, StringRef Name, APIRecord::RecordKind Kind) 638 : USR(USR), Name(Name), Kind(Kind) {} 639 }; 640 641 template <typename RecordTy> 642 bool generatePathComponents( 643 const RecordTy &Record, const APISet &API, 644 function_ref<void(const PathComponent &)> ComponentTransformer) { 645 SmallVector<PathComponent, 4> ReverseComponenents; 646 ReverseComponenents.emplace_back(Record.USR, Record.Name, Record.getKind()); 647 const auto *CurrentParent = &Record.ParentInformation; 648 bool FailedToFindParent = false; 649 while (CurrentParent && !CurrentParent->empty()) { 650 PathComponent CurrentParentComponent(CurrentParent->ParentUSR, 651 CurrentParent->ParentName, 652 CurrentParent->ParentKind); 653 654 auto *ParentRecord = CurrentParent->ParentRecord; 655 // Slow path if we don't have a direct reference to the ParentRecord 656 if (!ParentRecord) 657 ParentRecord = API.findRecordForUSR(CurrentParent->ParentUSR); 658 659 // If the parent is a category extended from internal module then we need to 660 // pretend this belongs to the associated interface. 661 if (auto *CategoryRecord = 662 dyn_cast_or_null<ObjCCategoryRecord>(ParentRecord)) { 663 if (!CategoryRecord->IsFromExternalModule) { 664 ParentRecord = API.findRecordForUSR(CategoryRecord->Interface.USR); 665 CurrentParentComponent = PathComponent(CategoryRecord->Interface.USR, 666 CategoryRecord->Interface.Name, 667 APIRecord::RK_ObjCInterface); 668 } 669 } 670 671 // The parent record doesn't exist which means the symbol shouldn't be 672 // treated as part of the current product. 673 if (!ParentRecord) { 674 FailedToFindParent = true; 675 break; 676 } 677 678 ReverseComponenents.push_back(std::move(CurrentParentComponent)); 679 CurrentParent = &ParentRecord->ParentInformation; 680 } 681 682 for (const auto &PC : reverse(ReverseComponenents)) 683 ComponentTransformer(PC); 684 685 return FailedToFindParent; 686 } 687 688 Object serializeParentContext(const PathComponent &PC, Language Lang) { 689 Object ParentContextElem; 690 ParentContextElem["usr"] = PC.USR; 691 ParentContextElem["name"] = PC.Name; 692 ParentContextElem["kind"] = serializeSymbolKind(PC.Kind, Lang)["identifier"]; 693 return ParentContextElem; 694 } 695 696 template <typename RecordTy> 697 Array generateParentContexts(const RecordTy &Record, const APISet &API, 698 Language Lang) { 699 Array ParentContexts; 700 generatePathComponents( 701 Record, API, [Lang, &ParentContexts](const PathComponent &PC) { 702 ParentContexts.push_back(serializeParentContext(PC, Lang)); 703 }); 704 705 return ParentContexts; 706 } 707 } // namespace 708 709 /// Defines the format version emitted by SymbolGraphSerializer. 710 const VersionTuple SymbolGraphSerializer::FormatVersion{0, 5, 3}; 711 712 Object SymbolGraphSerializer::serializeMetadata() const { 713 Object Metadata; 714 serializeObject(Metadata, "formatVersion", 715 serializeSemanticVersion(FormatVersion)); 716 Metadata["generator"] = clang::getClangFullVersion(); 717 return Metadata; 718 } 719 720 Object SymbolGraphSerializer::serializeModule() const { 721 Object Module; 722 // The user is expected to always pass `--product-name=` on the command line 723 // to populate this field. 724 Module["name"] = API.ProductName; 725 serializeObject(Module, "platform", serializePlatform(API.getTarget())); 726 return Module; 727 } 728 729 bool SymbolGraphSerializer::shouldSkip(const APIRecord &Record) const { 730 // Skip explicitly ignored symbols. 731 if (IgnoresList.shouldIgnore(Record.Name)) 732 return true; 733 734 // Skip unconditionally unavailable symbols 735 if (Record.Availability.isUnconditionallyUnavailable()) 736 return true; 737 738 // Filter out symbols prefixed with an underscored as they are understood to 739 // be symbols clients should not use. 740 if (Record.Name.starts_with("_")) 741 return true; 742 743 return false; 744 } 745 746 template <typename RecordTy> 747 std::optional<Object> 748 SymbolGraphSerializer::serializeAPIRecord(const RecordTy &Record) const { 749 if (shouldSkip(Record)) 750 return std::nullopt; 751 752 Object Obj; 753 serializeObject(Obj, "identifier", 754 serializeIdentifier(Record, API.getLanguage())); 755 serializeObject(Obj, "kind", serializeSymbolKind(Record, API.getLanguage())); 756 serializeObject(Obj, "names", serializeNames(Record)); 757 serializeObject( 758 Obj, "location", 759 serializeSourceLocation(Record.Location, /*IncludeFileURI=*/true)); 760 serializeArray(Obj, "availability", 761 serializeAvailability(Record.Availability)); 762 serializeObject(Obj, "docComment", serializeDocComment(Record.Comment)); 763 serializeArray(Obj, "declarationFragments", 764 serializeDeclarationFragments(Record.Declaration)); 765 SmallVector<StringRef, 4> PathComponentsNames; 766 // If this returns true it indicates that we couldn't find a symbol in the 767 // hierarchy. 768 if (generatePathComponents(Record, API, 769 [&PathComponentsNames](const PathComponent &PC) { 770 PathComponentsNames.push_back(PC.Name); 771 })) 772 return {}; 773 774 serializeArray(Obj, "pathComponents", Array(PathComponentsNames)); 775 776 serializeFunctionSignatureMixin(Obj, Record); 777 serializeAccessMixin(Obj, Record); 778 serializeTemplateMixin(Obj, Record); 779 780 return Obj; 781 } 782 783 template <typename MemberTy> 784 void SymbolGraphSerializer::serializeMembers( 785 const APIRecord &Record, 786 const SmallVector<std::unique_ptr<MemberTy>> &Members) { 787 // Members should not be serialized if we aren't recursing. 788 if (!ShouldRecurse) 789 return; 790 for (const auto &Member : Members) { 791 auto MemberRecord = serializeAPIRecord(*Member); 792 if (!MemberRecord) 793 continue; 794 795 Symbols.emplace_back(std::move(*MemberRecord)); 796 serializeRelationship(RelationshipKind::MemberOf, *Member, Record); 797 } 798 } 799 800 StringRef SymbolGraphSerializer::getRelationshipString(RelationshipKind Kind) { 801 switch (Kind) { 802 case RelationshipKind::MemberOf: 803 return "memberOf"; 804 case RelationshipKind::InheritsFrom: 805 return "inheritsFrom"; 806 case RelationshipKind::ConformsTo: 807 return "conformsTo"; 808 case RelationshipKind::ExtensionTo: 809 return "extensionTo"; 810 } 811 llvm_unreachable("Unhandled relationship kind"); 812 } 813 814 StringRef SymbolGraphSerializer::getConstraintString(ConstraintKind Kind) { 815 switch (Kind) { 816 case ConstraintKind::Conformance: 817 return "conformance"; 818 case ConstraintKind::ConditionalConformance: 819 return "conditionalConformance"; 820 } 821 llvm_unreachable("Unhandled constraint kind"); 822 } 823 824 void SymbolGraphSerializer::serializeRelationship(RelationshipKind Kind, 825 SymbolReference Source, 826 SymbolReference Target) { 827 Object Relationship; 828 Relationship["source"] = Source.USR; 829 Relationship["target"] = Target.USR; 830 Relationship["targetFallback"] = Target.Name; 831 Relationship["kind"] = getRelationshipString(Kind); 832 833 Relationships.emplace_back(std::move(Relationship)); 834 } 835 836 void SymbolGraphSerializer::visitNamespaceRecord( 837 const NamespaceRecord &Record) { 838 auto Namespace = serializeAPIRecord(Record); 839 if (!Namespace) 840 return; 841 Symbols.emplace_back(std::move(*Namespace)); 842 if (!Record.ParentInformation.empty()) 843 serializeRelationship(RelationshipKind::MemberOf, Record, 844 Record.ParentInformation.ParentRecord); 845 } 846 847 void SymbolGraphSerializer::visitGlobalFunctionRecord( 848 const GlobalFunctionRecord &Record) { 849 auto Obj = serializeAPIRecord(Record); 850 if (!Obj) 851 return; 852 853 Symbols.emplace_back(std::move(*Obj)); 854 } 855 856 void SymbolGraphSerializer::visitGlobalVariableRecord( 857 const GlobalVariableRecord &Record) { 858 auto Obj = serializeAPIRecord(Record); 859 if (!Obj) 860 return; 861 862 Symbols.emplace_back(std::move(*Obj)); 863 } 864 865 void SymbolGraphSerializer::visitEnumRecord(const EnumRecord &Record) { 866 auto Enum = serializeAPIRecord(Record); 867 if (!Enum) 868 return; 869 870 Symbols.emplace_back(std::move(*Enum)); 871 serializeMembers(Record, Record.Constants); 872 } 873 874 void SymbolGraphSerializer::visitStructRecord(const StructRecord &Record) { 875 auto Struct = serializeAPIRecord(Record); 876 if (!Struct) 877 return; 878 879 Symbols.emplace_back(std::move(*Struct)); 880 serializeMembers(Record, Record.Fields); 881 } 882 883 void SymbolGraphSerializer::visitStaticFieldRecord( 884 const StaticFieldRecord &Record) { 885 auto StaticField = serializeAPIRecord(Record); 886 if (!StaticField) 887 return; 888 Symbols.emplace_back(std::move(*StaticField)); 889 serializeRelationship(RelationshipKind::MemberOf, Record, Record.Context); 890 } 891 892 void SymbolGraphSerializer::visitCXXClassRecord(const CXXClassRecord &Record) { 893 auto Class = serializeAPIRecord(Record); 894 if (!Class) 895 return; 896 897 Symbols.emplace_back(std::move(*Class)); 898 for (const auto &Base : Record.Bases) 899 serializeRelationship(RelationshipKind::InheritsFrom, Record, Base); 900 if (!Record.ParentInformation.empty()) 901 serializeRelationship(RelationshipKind::MemberOf, Record, 902 Record.ParentInformation.ParentRecord); 903 } 904 905 void SymbolGraphSerializer::visitClassTemplateRecord( 906 const ClassTemplateRecord &Record) { 907 auto Class = serializeAPIRecord(Record); 908 if (!Class) 909 return; 910 911 Symbols.emplace_back(std::move(*Class)); 912 for (const auto &Base : Record.Bases) 913 serializeRelationship(RelationshipKind::InheritsFrom, Record, Base); 914 if (!Record.ParentInformation.empty()) 915 serializeRelationship(RelationshipKind::MemberOf, Record, 916 Record.ParentInformation.ParentRecord); 917 } 918 919 void SymbolGraphSerializer::visitClassTemplateSpecializationRecord( 920 const ClassTemplateSpecializationRecord &Record) { 921 auto Class = serializeAPIRecord(Record); 922 if (!Class) 923 return; 924 925 Symbols.emplace_back(std::move(*Class)); 926 927 for (const auto &Base : Record.Bases) 928 serializeRelationship(RelationshipKind::InheritsFrom, Record, Base); 929 if (!Record.ParentInformation.empty()) 930 serializeRelationship(RelationshipKind::MemberOf, Record, 931 Record.ParentInformation.ParentRecord); 932 } 933 934 void SymbolGraphSerializer::visitClassTemplatePartialSpecializationRecord( 935 const ClassTemplatePartialSpecializationRecord &Record) { 936 auto Class = serializeAPIRecord(Record); 937 if (!Class) 938 return; 939 940 Symbols.emplace_back(std::move(*Class)); 941 942 for (const auto &Base : Record.Bases) 943 serializeRelationship(RelationshipKind::InheritsFrom, Record, Base); 944 if (!Record.ParentInformation.empty()) 945 serializeRelationship(RelationshipKind::MemberOf, Record, 946 Record.ParentInformation.ParentRecord); 947 } 948 949 void SymbolGraphSerializer::visitCXXInstanceMethodRecord( 950 const CXXInstanceMethodRecord &Record) { 951 auto InstanceMethod = serializeAPIRecord(Record); 952 if (!InstanceMethod) 953 return; 954 955 Symbols.emplace_back(std::move(*InstanceMethod)); 956 serializeRelationship(RelationshipKind::MemberOf, Record, 957 Record.ParentInformation.ParentRecord); 958 } 959 960 void SymbolGraphSerializer::visitCXXStaticMethodRecord( 961 const CXXStaticMethodRecord &Record) { 962 auto StaticMethod = serializeAPIRecord(Record); 963 if (!StaticMethod) 964 return; 965 966 Symbols.emplace_back(std::move(*StaticMethod)); 967 serializeRelationship(RelationshipKind::MemberOf, Record, 968 Record.ParentInformation.ParentRecord); 969 } 970 971 void SymbolGraphSerializer::visitMethodTemplateRecord( 972 const CXXMethodTemplateRecord &Record) { 973 if (!ShouldRecurse) 974 // Ignore child symbols 975 return; 976 auto MethodTemplate = serializeAPIRecord(Record); 977 if (!MethodTemplate) 978 return; 979 Symbols.emplace_back(std::move(*MethodTemplate)); 980 serializeRelationship(RelationshipKind::MemberOf, Record, 981 Record.ParentInformation.ParentRecord); 982 } 983 984 void SymbolGraphSerializer::visitMethodTemplateSpecializationRecord( 985 const CXXMethodTemplateSpecializationRecord &Record) { 986 if (!ShouldRecurse) 987 // Ignore child symbols 988 return; 989 auto MethodTemplateSpecialization = serializeAPIRecord(Record); 990 if (!MethodTemplateSpecialization) 991 return; 992 Symbols.emplace_back(std::move(*MethodTemplateSpecialization)); 993 serializeRelationship(RelationshipKind::MemberOf, Record, 994 Record.ParentInformation.ParentRecord); 995 } 996 997 void SymbolGraphSerializer::visitCXXFieldRecord(const CXXFieldRecord &Record) { 998 if (!ShouldRecurse) 999 return; 1000 auto CXXField = serializeAPIRecord(Record); 1001 if (!CXXField) 1002 return; 1003 Symbols.emplace_back(std::move(*CXXField)); 1004 serializeRelationship(RelationshipKind::MemberOf, Record, 1005 Record.ParentInformation.ParentRecord); 1006 } 1007 1008 void SymbolGraphSerializer::visitCXXFieldTemplateRecord( 1009 const CXXFieldTemplateRecord &Record) { 1010 if (!ShouldRecurse) 1011 // Ignore child symbols 1012 return; 1013 auto CXXFieldTemplate = serializeAPIRecord(Record); 1014 if (!CXXFieldTemplate) 1015 return; 1016 Symbols.emplace_back(std::move(*CXXFieldTemplate)); 1017 serializeRelationship(RelationshipKind::MemberOf, Record, 1018 Record.ParentInformation.ParentRecord); 1019 } 1020 1021 void SymbolGraphSerializer::visitConceptRecord(const ConceptRecord &Record) { 1022 auto Concept = serializeAPIRecord(Record); 1023 if (!Concept) 1024 return; 1025 1026 Symbols.emplace_back(std::move(*Concept)); 1027 } 1028 1029 void SymbolGraphSerializer::visitGlobalVariableTemplateRecord( 1030 const GlobalVariableTemplateRecord &Record) { 1031 auto GlobalVariableTemplate = serializeAPIRecord(Record); 1032 if (!GlobalVariableTemplate) 1033 return; 1034 Symbols.emplace_back(std::move(*GlobalVariableTemplate)); 1035 } 1036 1037 void SymbolGraphSerializer::visitGlobalVariableTemplateSpecializationRecord( 1038 const GlobalVariableTemplateSpecializationRecord &Record) { 1039 auto GlobalVariableTemplateSpecialization = serializeAPIRecord(Record); 1040 if (!GlobalVariableTemplateSpecialization) 1041 return; 1042 Symbols.emplace_back(std::move(*GlobalVariableTemplateSpecialization)); 1043 } 1044 1045 void SymbolGraphSerializer:: 1046 visitGlobalVariableTemplatePartialSpecializationRecord( 1047 const GlobalVariableTemplatePartialSpecializationRecord &Record) { 1048 auto GlobalVariableTemplatePartialSpecialization = serializeAPIRecord(Record); 1049 if (!GlobalVariableTemplatePartialSpecialization) 1050 return; 1051 Symbols.emplace_back(std::move(*GlobalVariableTemplatePartialSpecialization)); 1052 } 1053 1054 void SymbolGraphSerializer::visitGlobalFunctionTemplateRecord( 1055 const GlobalFunctionTemplateRecord &Record) { 1056 auto GlobalFunctionTemplate = serializeAPIRecord(Record); 1057 if (!GlobalFunctionTemplate) 1058 return; 1059 Symbols.emplace_back(std::move(*GlobalFunctionTemplate)); 1060 } 1061 1062 void SymbolGraphSerializer::visitGlobalFunctionTemplateSpecializationRecord( 1063 const GlobalFunctionTemplateSpecializationRecord &Record) { 1064 auto GlobalFunctionTemplateSpecialization = serializeAPIRecord(Record); 1065 if (!GlobalFunctionTemplateSpecialization) 1066 return; 1067 Symbols.emplace_back(std::move(*GlobalFunctionTemplateSpecialization)); 1068 } 1069 1070 void SymbolGraphSerializer::visitObjCContainerRecord( 1071 const ObjCContainerRecord &Record) { 1072 auto ObjCContainer = serializeAPIRecord(Record); 1073 if (!ObjCContainer) 1074 return; 1075 1076 Symbols.emplace_back(std::move(*ObjCContainer)); 1077 1078 serializeMembers(Record, Record.Ivars); 1079 serializeMembers(Record, Record.Methods); 1080 serializeMembers(Record, Record.Properties); 1081 1082 for (const auto &Protocol : Record.Protocols) 1083 // Record that Record conforms to Protocol. 1084 serializeRelationship(RelationshipKind::ConformsTo, Record, Protocol); 1085 1086 if (auto *ObjCInterface = dyn_cast<ObjCInterfaceRecord>(&Record)) { 1087 if (!ObjCInterface->SuperClass.empty()) 1088 // If Record is an Objective-C interface record and it has a super class, 1089 // record that Record is inherited from SuperClass. 1090 serializeRelationship(RelationshipKind::InheritsFrom, Record, 1091 ObjCInterface->SuperClass); 1092 1093 // Members of categories extending an interface are serialized as members of 1094 // the interface. 1095 for (const auto *Category : ObjCInterface->Categories) { 1096 serializeMembers(Record, Category->Ivars); 1097 serializeMembers(Record, Category->Methods); 1098 serializeMembers(Record, Category->Properties); 1099 1100 // Surface the protocols of the category to the interface. 1101 for (const auto &Protocol : Category->Protocols) 1102 serializeRelationship(RelationshipKind::ConformsTo, Record, Protocol); 1103 } 1104 } 1105 } 1106 1107 void SymbolGraphSerializer::visitObjCCategoryRecord( 1108 const ObjCCategoryRecord &Record) { 1109 if (!Record.IsFromExternalModule) 1110 return; 1111 1112 // Check if the current Category' parent has been visited before, if so skip. 1113 if (!visitedCategories.contains(Record.Interface.Name)) { 1114 visitedCategories.insert(Record.Interface.Name); 1115 Object Obj; 1116 serializeObject(Obj, "identifier", 1117 serializeIdentifier(Record, API.getLanguage())); 1118 serializeObject(Obj, "kind", 1119 serializeSymbolKind(APIRecord::RK_ObjCCategoryModule, 1120 API.getLanguage())); 1121 Obj["accessLevel"] = "public"; 1122 Symbols.emplace_back(std::move(Obj)); 1123 } 1124 1125 Object Relationship; 1126 Relationship["source"] = Record.USR; 1127 Relationship["target"] = Record.Interface.USR; 1128 Relationship["targetFallback"] = Record.Interface.Name; 1129 Relationship["kind"] = getRelationshipString(RelationshipKind::ExtensionTo); 1130 Relationships.emplace_back(std::move(Relationship)); 1131 1132 auto ObjCCategory = serializeAPIRecord(Record); 1133 1134 if (!ObjCCategory) 1135 return; 1136 1137 Symbols.emplace_back(std::move(*ObjCCategory)); 1138 serializeMembers(Record, Record.Methods); 1139 serializeMembers(Record, Record.Properties); 1140 1141 // Surface the protocols of the category to the interface. 1142 for (const auto &Protocol : Record.Protocols) 1143 serializeRelationship(RelationshipKind::ConformsTo, Record, Protocol); 1144 } 1145 1146 void SymbolGraphSerializer::visitMacroDefinitionRecord( 1147 const MacroDefinitionRecord &Record) { 1148 auto Macro = serializeAPIRecord(Record); 1149 1150 if (!Macro) 1151 return; 1152 1153 Symbols.emplace_back(std::move(*Macro)); 1154 } 1155 1156 void SymbolGraphSerializer::serializeSingleRecord(const APIRecord *Record) { 1157 switch (Record->getKind()) { 1158 case APIRecord::RK_Unknown: 1159 llvm_unreachable("Records should have a known kind!"); 1160 case APIRecord::RK_GlobalFunction: 1161 visitGlobalFunctionRecord(*cast<GlobalFunctionRecord>(Record)); 1162 break; 1163 case APIRecord::RK_GlobalVariable: 1164 visitGlobalVariableRecord(*cast<GlobalVariableRecord>(Record)); 1165 break; 1166 case APIRecord::RK_Enum: 1167 visitEnumRecord(*cast<EnumRecord>(Record)); 1168 break; 1169 case APIRecord::RK_Struct: 1170 visitStructRecord(*cast<StructRecord>(Record)); 1171 break; 1172 case APIRecord::RK_StaticField: 1173 visitStaticFieldRecord(*cast<StaticFieldRecord>(Record)); 1174 break; 1175 case APIRecord::RK_CXXClass: 1176 visitCXXClassRecord(*cast<CXXClassRecord>(Record)); 1177 break; 1178 case APIRecord::RK_ObjCInterface: 1179 visitObjCContainerRecord(*cast<ObjCInterfaceRecord>(Record)); 1180 break; 1181 case APIRecord::RK_ObjCProtocol: 1182 visitObjCContainerRecord(*cast<ObjCProtocolRecord>(Record)); 1183 break; 1184 case APIRecord::RK_ObjCCategory: 1185 visitObjCCategoryRecord(*cast<ObjCCategoryRecord>(Record)); 1186 break; 1187 case APIRecord::RK_MacroDefinition: 1188 visitMacroDefinitionRecord(*cast<MacroDefinitionRecord>(Record)); 1189 break; 1190 case APIRecord::RK_Typedef: 1191 visitTypedefRecord(*cast<TypedefRecord>(Record)); 1192 break; 1193 default: 1194 if (auto Obj = serializeAPIRecord(*Record)) { 1195 Symbols.emplace_back(std::move(*Obj)); 1196 auto &ParentInformation = Record->ParentInformation; 1197 if (!ParentInformation.empty()) 1198 serializeRelationship(RelationshipKind::MemberOf, *Record, 1199 *ParentInformation.ParentRecord); 1200 } 1201 break; 1202 } 1203 } 1204 1205 void SymbolGraphSerializer::visitTypedefRecord(const TypedefRecord &Record) { 1206 // Typedefs of anonymous types have their entries unified with the underlying 1207 // type. 1208 bool ShouldDrop = Record.UnderlyingType.Name.empty(); 1209 // enums declared with `NS_OPTION` have a named enum and a named typedef, with 1210 // the same name 1211 ShouldDrop |= (Record.UnderlyingType.Name == Record.Name); 1212 if (ShouldDrop) 1213 return; 1214 1215 auto Typedef = serializeAPIRecord(Record); 1216 if (!Typedef) 1217 return; 1218 1219 (*Typedef)["type"] = Record.UnderlyingType.USR; 1220 1221 Symbols.emplace_back(std::move(*Typedef)); 1222 } 1223 1224 Object SymbolGraphSerializer::serialize() { 1225 traverseAPISet(); 1226 return serializeCurrentGraph(); 1227 } 1228 1229 Object SymbolGraphSerializer::serializeCurrentGraph() { 1230 Object Root; 1231 serializeObject(Root, "metadata", serializeMetadata()); 1232 serializeObject(Root, "module", serializeModule()); 1233 1234 Root["symbols"] = std::move(Symbols); 1235 Root["relationships"] = std::move(Relationships); 1236 1237 return Root; 1238 } 1239 1240 void SymbolGraphSerializer::serialize(raw_ostream &os) { 1241 Object root = serialize(); 1242 if (Options.Compact) 1243 os << formatv("{0}", Value(std::move(root))) << "\n"; 1244 else 1245 os << formatv("{0:2}", Value(std::move(root))) << "\n"; 1246 } 1247 1248 std::optional<Object> 1249 SymbolGraphSerializer::serializeSingleSymbolSGF(StringRef USR, 1250 const APISet &API) { 1251 APIRecord *Record = API.findRecordForUSR(USR); 1252 if (!Record) 1253 return {}; 1254 1255 Object Root; 1256 APIIgnoresList EmptyIgnores; 1257 SymbolGraphSerializer Serializer(API, EmptyIgnores, 1258 /*Options.Compact*/ {true}, 1259 /*ShouldRecurse*/ false); 1260 Serializer.serializeSingleRecord(Record); 1261 serializeObject(Root, "symbolGraph", Serializer.serializeCurrentGraph()); 1262 1263 Language Lang = API.getLanguage(); 1264 serializeArray(Root, "parentContexts", 1265 generateParentContexts(*Record, API, Lang)); 1266 1267 Array RelatedSymbols; 1268 1269 for (const auto &Fragment : Record->Declaration.getFragments()) { 1270 // If we don't have a USR there isn't much we can do. 1271 if (Fragment.PreciseIdentifier.empty()) 1272 continue; 1273 1274 APIRecord *RelatedRecord = API.findRecordForUSR(Fragment.PreciseIdentifier); 1275 1276 // If we can't find the record let's skip. 1277 if (!RelatedRecord) 1278 continue; 1279 1280 Object RelatedSymbol; 1281 RelatedSymbol["usr"] = RelatedRecord->USR; 1282 RelatedSymbol["declarationLanguage"] = getLanguageName(Lang); 1283 // TODO: once we record this properly let's serialize it right. 1284 RelatedSymbol["accessLevel"] = "public"; 1285 RelatedSymbol["filePath"] = RelatedRecord->Location.getFilename(); 1286 RelatedSymbol["moduleName"] = API.ProductName; 1287 RelatedSymbol["isSystem"] = RelatedRecord->IsFromSystemHeader; 1288 1289 serializeArray(RelatedSymbol, "parentContexts", 1290 generateParentContexts(*RelatedRecord, API, Lang)); 1291 RelatedSymbols.push_back(std::move(RelatedSymbol)); 1292 } 1293 1294 serializeArray(Root, "relatedSymbols", RelatedSymbols); 1295 return Root; 1296 } 1297