[clang] [clang-tools-extra][ExtractAPI] create clang-symbolgraph-merger (PR #65894)

via cfe-commits cfe-commits at lists.llvm.org
Sun Sep 10 04:54:41 PDT 2023


llvmbot wrote:

@llvm/pr-subscribers-clang

<details>
<summary>Changes</summary>

Create a clang tool to merge all the JSON symbolgraph emited by --emit-symbol-graph or -extract-api options into one unified JSON symbolgraph file.

Differential Revision: https://reviews.llvm.org/D158646
--

Patch is 127.06 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/65894.diff

16 Files Affected:

- (modified) clang-tools-extra/CMakeLists.txt (+1) 
- (added) clang-tools-extra/clang-symbolgraph-merger/CMakeLists.txt (+3) 
- (added) clang-tools-extra/clang-symbolgraph-merger/include/clang-symbolgraph-merger/SymbolGraph.h (+48) 
- (added) clang-tools-extra/clang-symbolgraph-merger/include/clang-symbolgraph-merger/SymbolGraphMerger.h (+45) 
- (added) clang-tools-extra/clang-symbolgraph-merger/include/clang-symbolgraph-merger/SymbolGraphVisitor.h (+68) 
- (added) clang-tools-extra/clang-symbolgraph-merger/lib/CMakeLists.txt (+14) 
- (added) clang-tools-extra/clang-symbolgraph-merger/lib/SymbolGraph.cpp (+243) 
- (added) clang-tools-extra/clang-symbolgraph-merger/lib/SymbolGraphMerger.cpp (+290) 
- (added) clang-tools-extra/clang-symbolgraph-merger/tool/CMakeLists.txt (+13) 
- (added) clang-tools-extra/clang-symbolgraph-merger/tool/SymbolGraphMergerMain.cpp (+125) 
- (modified) clang/include/clang/ExtractAPI/API.h (+212-151) 
- (modified) clang/include/clang/ExtractAPI/AvailabilityInfo.h (+6) 
- (modified) clang/include/clang/ExtractAPI/ExtractAPIVisitor.h (+43-135) 
- (modified) clang/lib/ExtractAPI/API.cpp (+46-44) 
- (modified) clang/lib/ExtractAPI/Serialization/SymbolGraphSerializer.cpp (+5-5) 
- (modified) clang/tools/libclang/CXExtractAPI.cpp (+6-3) 


<pre>
diff --git a/clang-tools-extra/CMakeLists.txt b/clang-tools-extra/CMakeLists.txt
index 6a3f741721ee6c7..a4052e0894076ef 100644
--- a/clang-tools-extra/CMakeLists.txt
+++ b/clang-tools-extra/CMakeLists.txt
@@ -13,6 +13,7 @@ if(CLANG_INCLUDE_TESTS)
   endif()
 endif()
 
+add_subdirectory(clang-symbolgraph-merger)
 add_subdirectory(clang-apply-replacements)
 add_subdirectory(clang-reorder-fields)
 add_subdirectory(modularize)
diff --git a/clang-tools-extra/clang-symbolgraph-merger/CMakeLists.txt b/clang-tools-extra/clang-symbolgraph-merger/CMakeLists.txt
new file mode 100644
index 000000000000000..a071a8a11693337
--- /dev/null
+++ b/clang-tools-extra/clang-symbolgraph-merger/CMakeLists.txt
@@ -0,0 +1,3 @@
+include_directories(include)
+add_subdirectory(lib)
+add_subdirectory(tool)
diff --git a/clang-tools-extra/clang-symbolgraph-merger/include/clang-symbolgraph-merger/SymbolGraph.h b/clang-tools-extra/clang-symbolgraph-merger/include/clang-symbolgraph-merger/SymbolGraph.h
new file mode 100755
index 000000000000000..a613f833ffad73b
--- /dev/null
+++ b/clang-tools-extra/clang-symbolgraph-merger/include/clang-symbolgraph-merger/SymbolGraph.h
@@ -0,0 +1,48 @@
+#ifndef SYMBOLGRAPH_H
+#define SYMBOLGRAPH_H
+
+#include "clang/Basic/LangStandard.h"
+#include "clang/ExtractAPI/API.h"
+#include "clang/ExtractAPI/AvailabilityInfo.h"
+#include "clang/ExtractAPI/DeclarationFragments.h"
+#include "llvm/ADT/DenseMap.h"
+#include "llvm/ADT/StringRef.h"
+#include "llvm/Support/JSON.h"
+#include <memory>
+#include <vector>
+
+namespace sgmerger {
+
+// see https://github.com/apple/swift-docc-symbolkit/bdob/main/openapi.yaml
+struct SymbolGraph {
+
+  struct Symbol {
+    Symbol(const llvm::json::Object &SymbolObj);
+
+    llvm::json::Object SymbolObj;
+    std::string AccessLevel;
+    clang::extractapi::APIRecord::RecordKind Kind;
+    clang::extractapi::DeclarationFragments DeclFragments;
+    clang::extractapi::FunctionSignature FunctionSign;
+    std::string Name;
+    std::string USR;
+    clang::extractapi::AvailabilitySet Availabilities;
+    clang::extractapi::DocComment Comments;
+    clang::extractapi::RecordLocation Location;
+    clang::extractapi::DeclarationFragments SubHeadings;
+
+    // underlying type in case of Typedef
+    clang::extractapi::SymbolReference UnderLyingType;
+  };
+
+  SymbolGraph(const llvm::StringRef JSON);
+  llvm::json::Object SymbolGraphObject;
+  llvm::json::Object Metadata;
+  llvm::json::Object Module;
+  std::vector<Symbol> Symbols;
+  llvm::json::Array Relationships;
+};
+
+} // namespace sgmerger
+
+#endif /* SYMBOLGRAPH_H */
diff --git a/clang-tools-extra/clang-symbolgraph-merger/include/clang-symbolgraph-merger/SymbolGraphMerger.h b/clang-tools-extra/clang-symbolgraph-merger/include/clang-symbolgraph-merger/SymbolGraphMerger.h
new file mode 100755
index 000000000000000..179cadafd877825
--- /dev/null
+++ b/clang-tools-extra/clang-symbolgraph-merger/include/clang-symbolgraph-merger/SymbolGraphMerger.h
@@ -0,0 +1,45 @@
+#ifndef SYMBOLGRAPHMERGER_H
+#define SYMBOLGRAPHMERGER_H
+
+#include "clang-symbolgraph-merger/SymbolGraph.h"
+#include "clang-symbolgraph-merger/SymbolGraphVisitor.h"
+#include "clang/Basic/LangStandard.h"
+#include "clang/ExtractAPI/API.h"
+#include "llvm/ADT/DenseMap.h"
+#include "llvm/ADT/StringRef.h"
+#include "llvm/TargetParser/Triple.h"
+#include <memory>
+
+namespace sgmerger {
+
+using SymbolMap = llvm::DenseMap<llvm::StringRef, const SymbolGraph::Symbol *>;
+
+class SymbolGraphMerger : public SymbolGraphVisitor<SymbolGraphMerger> {
+public:
+  SymbolGraphMerger(const clang::SmallVector<SymbolGraph> &SymbolGraphs,
+                    const std::string &ProductName = "")
+      : ProductName(ProductName), Lang(clang::Language::Unknown),
+        SymbolGraphs(SymbolGraphs) {}
+  bool merge();
+  bool visitMetadata(const llvm::json::Object &Metadata);
+  bool visitModule(const llvm::json::Object &Module);
+  bool visitSymbol(const SymbolGraph::Symbol &Symbol);
+  bool visitRelationship(const llvm::json::Object &Relationship);
+
+private:
+  std::string Generator;
+
+  // stuff required to construct the APISet
+  std::string ProductName;
+  llvm::Triple Target;
+  clang::Language Lang;
+
+  SymbolMap PendingSymbols;
+  SymbolMap VisitedSymbols;
+
+  const clang::SmallVector<SymbolGraph> &SymbolGraphs;
+};
+
+} // namespace sgmerger
+
+#endif /* SYMBOLGRAPHMERGER_H */
diff --git a/clang-tools-extra/clang-symbolgraph-merger/include/clang-symbolgraph-merger/SymbolGraphVisitor.h b/clang-tools-extra/clang-symbolgraph-merger/include/clang-symbolgraph-merger/SymbolGraphVisitor.h
new file mode 100755
index 000000000000000..6e2042784147a62
--- /dev/null
+++ b/clang-tools-extra/clang-symbolgraph-merger/include/clang-symbolgraph-merger/SymbolGraphVisitor.h
@@ -0,0 +1,68 @@
+#ifndef SYMBOLGRAPHVISITOR_H
+#define SYMBOLGRAPHVISITOR_H
+
+#include "clang-symbolgraph-merger/SymbolGraph.h"
+#include "clang/ExtractAPI/API.h"
+#include "llvm/ADT/StringRef.h"
+#include "llvm/Support/JSON.h"
+#include <memory>
+
+namespace sgmerger {
+
+// Visits a symbol graph obbect and record the extracted info to API
+template <typename Derived> class SymbolGraphVisitor {
+public:
+  bool traverseSymbolGraph(const SymbolGraph &SG) {
+    bool Success = true;
+    Success = (getDerived()->visitMetadata(SG.Metadata) &&
+               getDerived()->visitModule(SG.Module) &&
+               getDerived()->traverseSymbols(SG.Symbols) &&
+               getDerived()->traverseRelationships(SG.Relationships));
+
+    return Success;
+  }
+
+  bool traverseSymbols(const std::vector<SymbolGraph::Symbol> &Symbols) {
+    bool Success = true;
+    for (const auto &Symbol : Symbols)
+      Success = getDerived()->visitSymbol(Symbol);
+    return Success;
+  }
+
+  bool traverseRelationships(const llvm::json::Array &Relationships) {
+    bool Success = true;
+    for (const auto &RelValue : Relationships) {
+      if (const auto *RelObj = RelValue.getAsObject())
+        Success = getDerived()->visitRelationship(*RelObj);
+    }
+    return Success;
+  }
+
+  bool visitMetadata(const llvm::json::Object &Metadata);
+  bool visitModule(const llvm::json::Object &Module);
+  bool visitSymbol(const SymbolGraph::Symbol &Symbol);
+  bool visitRelationship(const llvm::json::Object &Relationship);
+
+  std::unique_ptr<clang::extractapi::APISet> getAPISet() {
+    return std::move(API);
+  }
+
+protected:
+  std::unique_ptr<clang::extractapi::APISet> API;
+
+public:
+  SymbolGraphVisitor(const SymbolGraphVisitor &) = delete;
+  SymbolGraphVisitor(SymbolGraphVisitor &&) = delete;
+  SymbolGraphVisitor &operator=(const SymbolGraphVisitor &) = delete;
+  SymbolGraphVisitor &operator=(SymbolGraphVisitor &&) = delete;
+
+protected:
+  SymbolGraphVisitor() : API(nullptr) {}
+  ~SymbolGraphVisitor() = default;
+
+  Derived *getDerived() { return static_cast<Derived *>(this); };
+};
+
+} // namespace sgmerger
+
+#endif /* SYMBOLGRAPHVISITOR_H */
diff --git a/clang-tools-extra/clang-symbolgraph-merger/lib/CMakeLists.txt b/clang-tools-extra/clang-symbolgraph-merger/lib/CMakeLists.txt
new file mode 100755
index 000000000000000..5f0bcc65c4762e2
--- /dev/null
+++ b/clang-tools-extra/clang-symbolgraph-merger/lib/CMakeLists.txt
@@ -0,0 +1,14 @@
+set(LLVM_LINK_COMPONENTS Support)
+
+add_clang_library(clangSymbolGraphMerger
+  SymbolGraphMerger.cpp
+  SymbolGraph.cpp
+  )
+
+clang_target_link_libraries(clangSymbolGraphMerger
+  PRIVATE
+  clangBasic
+  clangToolingCore
+  clangToolingInclusions
+  clangExtractAPI
+)
diff --git a/clang-tools-extra/clang-symbolgraph-merger/lib/SymbolGraph.cpp b/clang-tools-extra/clang-symbolgraph-merger/lib/SymbolGraph.cpp
new file mode 100755
index 000000000000000..030a9bda99db08e
--- /dev/null
+++ b/clang-tools-extra/clang-symbolgraph-merger/lib/SymbolGraph.cpp
@@ -0,0 +1,243 @@
+#include "clang-symbolgraph-merger/SymbolGraph.h"
+#include "clang/ExtractAPI/API.h"
+#include "clang/ExtractAPI/AvailabilityInfo.h"
+#include "clang/ExtractAPI/DeclarationFragments.h"
+#include "llvm/Support/Allocator.h"
+#include "llvm/Support/JSON.h"
+#include "llvm/Support/VersionTuple.h"
+#include <cassert>
+#include <cstring>
+#include <memory>
+#include <vector>
+
+using namespace sgmerger;
+using namespace llvm;
+using namespace llvm::json;
+using namespace clang::extractapi;
+
+namespace {
+
+APIRecord::RecordKind getSymbolKind(const Object &Kind) {
+
+  if (auto Identifier = Kind.getString("identifier")) {
+    // Remove danguage prefix
+    auto Id = Identifier->split('.').second;
+    if (Id.equals("func"))
+      return APIRecord::RK_GlobalFunction;
+    if (Id.equals("var"))
+      return APIRecord::RK_GlobalVariable;
+    if (Id.equals("enum.case"))
+      return APIRecord::RK_EnumConstant;
+    if (Id.equals("enum"))
+      return APIRecord::RK_Enum;
+    if (Id.equals("property"))
+      return APIRecord::RK_StructField;
+    if (Id.equals("struct"))
+      return APIRecord::RK_Struct;
+    if (Id.equals("ivar"))
+      return APIRecord::RK_ObjCIvar;
+    if (Id.equals("method"))
+      return APIRecord::RK_ObjCInstanceMethod;
+    if (Id.equals("type.method"))
+      return APIRecord::RK_ObjCClassMethod;
+    if (Id.equals("property"))
+      return APIRecord::RK_ObjCInstanceProperty;
+    if (Id.equals("type.property"))
+      return APIRecord::RK_ObjCClassProperty;
+    if (Id.equals("class"))
+      return APIRecord::RK_ObjCInterface;
+    if (Id.equals("protocod"))
+      return APIRecord::RK_ObjCProtocol;
+    if (Id.equals("macro"))
+      return APIRecord::RK_MacroDefinition;
+    if (Id.equals("typealias"))
+      return APIRecord::RK_Typedef;
+  }
+  return APIRecord::RK_Unknown;
+}
+
+VersionTuple parseVersionTupleFromJSON(const Object *VTObj) {
+  auto Major = VTObj->getInteger("major").value_or(0);
+  auto Minor = VTObj->getInteger("minor").value_or(0);
+  auto Patch = VTObj->getInteger("patch").value_or(0);
+  return VersionTuple(Major, Minor, Patch);
+}
+
+RecordLocation parseSourcePositionFromJSON(const Object *PosObj,
+                                           std::string Filename = "") {
+  assert(PosObj);
+  unsigned Line = PosObj->getInteger("line").value_or(0);
+  unsigned Col = PosObj->getInteger("character").value_or(0);
+  return RecordLocation(Line, Col, Filename);
+}
+
+RecordLocation parseRecordLocationFromJSON(const Object *LocObj) {
+  assert(LocObj);
+
+  std::string Filename(LocObj->getString("uri").value_or(""));
+  // extract file name from URI
+  std::string URIScheme = "file://";
+  if (Filename.find(URIScheme) == 0)
+    Filename.erase(0, URIScheme.length());
+
+  const auto *PosObj = LocObj->getObject("position");
+
+  return parseSourcePositionFromJSON(PosObj, Filename);
+}
+
+DocComment parseCommentsFromJSON(const Object *CommentsObj) {
+  assert(CommentsObj);
+  const auto *LinesArray = CommentsObj->getArray("lines");
+  DocComment Comments;
+  if (LinesArray) {
+    for (auto &LineValue : *LinesArray) {
+      const auto *LineObj = LineValue.getAsObject();
+      auto Text = LineObj->getString("text").value_or("");
+
+      // parse range
+      const auto *BeginLocObj = LineObj->getObject("start");
+      RecordLocation BeginLoc = parseSourcePositionFromJSON(BeginLocObj);
+      const auto *EndLocObj = LineObj->getObject("end");
+      RecordLocation EndLoc = parseSourcePositionFromJSON(EndLocObj);
+      Comments.push_back(CommentLine(Text, BeginLoc, EndLoc));
+    }
+  }
+  return Comments;
+}
+
+AvailabilitySet parseAvailabilitiesFromJSON(const Array *AvailablityArray) {
+  if (AvailablityArray) {
+    SmallVector<AvailabilityInfo, 4> AList;
+    for (auto &AvailablityValue : *AvailablityArray) {
+      const auto *AvailablityObj = AvailablityValue.getAsObject();
+      auto Domain = AvailablityObj->getString("domain").value_or("");
+      auto IntroducedVersion = parseVersionTupleFromJSON(
+          AvailablityObj->getObject("introducedVersion"));
+      auto ObsoletedVersion = parseVersionTupleFromJSON(
+          AvailablityObj->getObject("obsoletedVersion"));
+      auto DeprecatedVersion = parseVersionTupleFromJSON(
+          AvailablityObj->getObject("deprecatedVersion"));
+      AList.emplace_back(AvailabilityInfo(Domain, IntroducedVersion,
+                                          DeprecatedVersion, ObsoletedVersion,
+                                          false));
+    }
+    return AvailabilitySet(AList);
+  }
+  return nullptr;
+}
+
+DeclarationFragments parseDeclFragmentsFromJSON(const Array *FragmentsArray) {
+  DeclarationFragments Fragments;
+  if (FragmentsArray) {
+    for (auto &FragmentValue : *FragmentsArray) {
+      Object FragmentObj = *(FragmentValue.getAsObject());
+      auto Spelling = FragmentObj.getString("spelling").value_or("");
+      auto FragmentKind = DeclarationFragments::parseFragmentKindFromString(
+          FragmentObj.getString("kind").value_or(""));
+      StringRef PreciseIdentifier =
+          FragmentObj.getString("preciseIdentifier").value_or("");
+      Fragments.append(Spelling, FragmentKind, PreciseIdentifier);
+    }
+  }
+  return Fragments;
+}
+
+FunctionSignature parseFunctionSignaturesFromJSON(const Object *SignaturesObj) {
+  FunctionSignature ParsedSignatures;
+  if (SignaturesObj) {
+    // parse return type
+    const auto *RT = SignaturesObj->getArray("returns");
+    ParsedSignatures.setReturnType(parseDeclFragmentsFromJSON(RT));
+
+    // parse function parameters
+    if (const auto *ParamArray = SignaturesObj->getArray("parameters")) {
+      for (auto &Param : *ParamArray) {
+        auto ParamObj = *(Param.getAsObject());
+        auto Name = ParamObj.getString("name").value_or("");
+        auto Fragments = parseDeclFragmentsFromJSON(
+            ParamObj.getArray("declarationFragments"));
+        ParsedSignatures.addParameter(Name, Fragments);
+      }
+    }
+  }
+  return ParsedSignatures;
+}
+
+std::vector<SymbolGraph::Symbol>
+parseSymbolsFromJSON(const Array *SymbolsArray) {
+  std::vector<SymbolGraph::Symbol> SymbolsVector;
+  if (SymbolsArray) {
+    for (const auto &S : *SymbolsArray)
+      if (const auto *Symbol = S.getAsObject())
+        SymbolsVector.push_back(SymbolGraph::Symbol(*Symbol));
+  }
+  return SymbolsVector;
+}
+
+} // namespace
+
+SymbolGraph::Symbol::Symbol(const Object &SymbolObject)
+    : SymbolObj(SymbolObject) {
+
+  AccessLevel = SymbolObj.getString("accessLevel").value_or("unknown");
+  Kind = getSymbolKind(*(SymbolObject.getObject("kind")));
+
+  // parse Doc comments
+  if (const auto *CommentsArray = SymbolObject.getObject("docComment"))
+    Comments = parseCommentsFromJSON(CommentsArray);
+
+  // parse Availabilityinfo
+  if (const auto *AvailabilityArray = SymbolObj.getArray("availability"))
+    Availabilities = parseAvailabilitiesFromJSON(AvailabilityArray);
+
+  // parse declaration fragments
+  if (const auto *FragmentsArray = SymbolObj.getArray("declarationFragments"))
+    DeclFragments = parseDeclFragmentsFromJSON(FragmentsArray);
+
+  // parse function signatures if any
+  if (const auto *FunctionSignObj = SymbolObj.getObject("functionSignature"))
+    FunctionSign = parseFunctionSignaturesFromJSON(FunctionSignObj);
+
+  // parse identifier
+  if (const auto *IDObj = SymbolObj.getObject("identifier"))
+    USR = IDObj->getString("precise").value_or("");
+
+  // parse Location
+  if (const auto *LocObj = SymbolObject.getObject("location"))
+    Location = parseRecordLocationFromJSON(LocObj);
+
+  // parse name and subheadings.
+  if (const auto *NamesObj = SymbolObj.getObject("names")) {
+    Name = NamesObj->getString("title").value_or("");
+    if (const auto *SubHObj = NamesObj->getArray("subHeading"))
+      SubHeadings = parseDeclFragmentsFromJSON(SubHObj);
+  }
+
+  // parse underlying type in case of Typedef
+  auto UType = SymbolObject.getString("type");
+  if (UType.has_value()) {
+    auto UTypeUSR = UType.value();
+    // FIXME: this is a hacky way for Underlying type to be
+    // serialized into the final graph. Get someway to extract the
+    // actual name of the underlying type from USR
+    UnderLyingType = SymbolReference(" ", UTypeUSR);
+  }
+}
+
+SymbolGraph::SymbolGraph(const llvm::StringRef JSON) {
+  Expected<llvm::json::Value> SGValue = llvm::json::parse(JSON);
+  if (SGValue) {
+    assert(SGValue && SGValue->kind() == llvm::json::Value::Object);
+    if (const auto *SGObject = SGValue->getAsObject()) {
+      SymbolGraphObject = *SGObject;
+      if (const auto *MetadataObj = SGObject->getObject("metadata"))
+        Metadata = *MetadataObj;
+      if (const auto *ModuleObj = SGObject->getObject("module"))
+        Module = *ModuleObj;
+      if (const auto *RelArray = SGObject->getArray("relationships"))
+        Relationships = *RelArray;
+
+      Symbols = parseSymbolsFromJSON(SGObject->getArray("symbols"));
+    }
+  }
+}
diff --git a/clang-tools-extra/clang-symbolgraph-merger/lib/SymbolGraphMerger.cpp b/clang-tools-extra/clang-symbolgraph-merger/lib/SymbolGraphMerger.cpp
new file mode 100755
index 000000000000000..71facea3e6ba8bc
--- /dev/null
+++ b/clang-tools-extra/clang-symbolgraph-merger/lib/SymbolGraphMerger.cpp
@@ -0,0 +1,290 @@
+#include "clang-symbolgraph-merger/SymbolGraphMerger.h"
+#include "clang/AST/DeclObjC.h"
+#include "clang/ExtractAPI/API.h"
+#include "clang/ExtractAPI/AvailabilityInfo.h"
+#include "llvm/ADT/StringRef.h"
+#include "llvm/Support/Casting.h"
+#include <memory>
+
+using namespace llvm;
+using namespace llvm::json;
+using namespace clang;
+using namespace clang::extractapi;
+using namespace sgmerger;
+
+namespace {
+ObjCInstanceVariableRecord::AccessControl
+getAccessFromString(const StringRef AccessLevel) {
+  if (AccessLevel.equals("Private"))
+    return ObjCInstanceVariableRecord::AccessControl::Private;
+  if (AccessLevel.equals("Protected"))
+    return ObjCInstanceVariableRecord::AccessControl::Protected;
+  if (AccessLevel.equals("Public"))
+    return ObjCInstanceVariableRecord::AccessControl::Public;
+  if (AccessLevel.equals("Package"))
+    return ObjCInstanceVariableRecord::AccessControl::Package;
+  return ObjCInstanceVariableRecord::AccessControl::None;
+}
+
+Language getLanguageFromString(const StringRef LangName) {
+  if (LangName.equals("c"))
+    return Language::C;
+  if (LangName.equals("objective-c"))
+    return Language::ObjC;
+  if (LangName.equals("C++"))
+    return Language::CXX;
+
+  return Language::Unknown;
+}
+
+template <typename Lambda>
+bool addWithContainerRecord(APIRecord::RecordKind Kind, APIRecord *TargetRecord,
+                            Lambda Inserter) {
+  switch (Kind) {
+  case APIRecord::RK_ObjCInterface: {
+    if (ObjCInterfaceRecord *Container =
+            dyn_cast_or_null<ObjCInterfaceRecord>(TargetRecord))
+      Inserter(Container);
+  } break;
+  case APIRecord::RK_ObjCProtocol: {
+    if (ObjCProtocolRecord *Container =
+            dyn_cast_or_null<ObjCProtocolRecord>(TargetRecord))
+      Inserter(Container);
+  } break;
+  case APIRecord::RK_ObjCCategory: {
+    if (ObjCCategoryRecord *Container =
+            dyn_cast_or_null<ObjCCategoryRecord>(TargetRecord))
+      Inserter(Container);
+  } break;
+  default:
+    retur...
<truncated>
</pre>

</details>

https://github.com/llvm/llvm-project/pull/65894


More information about the cfe-commits mailing list