Arsenic created this revision.
Arsenic added a reviewer: dang.
Arsenic added projects: All, clang-tools-extra.
Herald added a reviewer: ributzka.
Arsenic requested review of this revision.

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.


https://reviews.llvm.org/D158646

Files:
  clang-tools-extra/CMakeLists.txt
  clang-tools-extra/clang-symbolgraph-merger/CMakeLists.txt
  
clang-tools-extra/clang-symbolgraph-merger/include/clang-symbolgraph-merger/SymbolGraph.h
  
clang-tools-extra/clang-symbolgraph-merger/include/clang-symbolgraph-merger/SymbolGraphMerger.h
  
clang-tools-extra/clang-symbolgraph-merger/include/clang-symbolgraph-merger/SymbolGraphVisitor.h
  clang-tools-extra/clang-symbolgraph-merger/lib/CMakeLists.txt
  clang-tools-extra/clang-symbolgraph-merger/lib/SymbolGraph.cpp
  clang-tools-extra/clang-symbolgraph-merger/lib/SymbolGraphMerger.cpp
  clang-tools-extra/clang-symbolgraph-merger/tool/CMakeLists.txt
  clang-tools-extra/clang-symbolgraph-merger/tool/SymbolGraphMergerMain.cpp
  clang/include/clang/ExtractAPI/AvailabilityInfo.h

Index: clang/include/clang/ExtractAPI/AvailabilityInfo.h
===================================================================
--- clang/include/clang/ExtractAPI/AvailabilityInfo.h
+++ clang/include/clang/ExtractAPI/AvailabilityInfo.h
@@ -53,6 +53,12 @@
 
 public:
   AvailabilitySet(const Decl *Decl);
+  AvailabilitySet(AvailabilityList &List,
+                  bool UnconditionallyDeprecated = false,
+                  bool UnconditionallyUnavailable = false)
+      : Availabilities(List),
+        UnconditionallyDeprecated(UnconditionallyDeprecated),
+        UnconditionallyUnavailable(UnconditionallyUnavailable) {}
   AvailabilitySet() = default;
 
   AvailabilityList::const_iterator begin() const {
Index: clang-tools-extra/clang-symbolgraph-merger/tool/SymbolGraphMergerMain.cpp
===================================================================
--- /dev/null
+++ clang-tools-extra/clang-symbolgraph-merger/tool/SymbolGraphMergerMain.cpp
@@ -0,0 +1,125 @@
+#include "clang-symbolgraph-merger/SymbolGraphMerger.h"
+#include "clang-symbolgraph-merger/SymbolGraphVisitor.h"
+#include "clang/ExtractAPI/Serialization/SymbolGraphSerializer.h"
+#include "clang/Tooling/CommonOptionsParser.h"
+#include "clang/Tooling/Execution.h"
+#include "clang/Tooling/Tooling.h"
+#include "llvm/ADT/SmallVector.h"
+#include "llvm/ADT/StringRef.h"
+#include "llvm/Support/CommandLine.h"
+#include "llvm/Support/FileSystem.h"
+#include "llvm/Support/Path.h"
+#include "llvm/Support/Signals.h"
+#include "llvm/Support/raw_ostream.h"
+#include <string>
+#include <system_error>
+
+using namespace clang::tooling;
+using namespace llvm;
+using namespace sgmerger;
+
+namespace {
+
+bool collectSymbolGraphs(StringRef SGDirectory,
+                         SmallVector<SymbolGraph> &SymbolGraphs) {
+  std::error_code Error;
+  for (sys::fs::directory_iterator I(SGDirectory, Error), End; I != End;
+       I.increment(Error)) {
+    if (Error)
+      return false;
+    std::string File(I->path());
+    llvm::ErrorOr<sys::fs::basic_file_status> Status = I->status();
+    if (!Status)
+      return false;
+    sys::fs::file_type Type = Status->type();
+    // If the file is a directory, ignore the name and recurse.
+    if (Type == sys::fs::file_type::directory_file) {
+      if (!collectSymbolGraphs(File, SymbolGraphs))
+        return false;
+      continue;
+    }
+
+    // Ignore all the non json files
+    if (!sys::path::extension(File).equals(".json"))
+      continue;
+
+    // Read the Symbol Graph from the file
+    int FileFD;
+    if (auto OpenError = sys::fs::openFileForRead(File, FileFD))
+      return false;
+
+    llvm::SmallString<256> Payload;
+    if (auto ReadError = sys::fs::readNativeFileToEOF(FileFD, Payload))
+      return false;
+
+    SymbolGraphs.emplace_back(SymbolGraph(Payload));
+    llvm::sys::fs::closeFile(FileFD);
+  }
+  return true;
+}
+
+} // namespace
+
+static cl::extrahelp CommonHelp(CommonOptionsParser::HelpMessage);
+// TODO: add more help text
+// static cl::extrahelp MoreHelp("\nMore help text...\n");
+
+static cl::OptionCategory
+    SymbolGraphMergerCategory("clang-symbolgraph-merger options");
+
+static cl::opt<std::string> ProjectName("project-name",
+                                        cl::desc("Name of project."),
+                                        cl::cat(SymbolGraphMergerCategory),
+                                        cl::init(""));
+
+static cl::opt<std::string>
+    OutFile("o", cl::desc("File for outputing generated Symbol Graph."),
+            cl::cat(SymbolGraphMergerCategory), cl::value_desc("filepath"),
+            cl::init("output"));
+
+static cl::opt<std::string>
+    InputDir(cl::Positional, cl::Required, cl::cat(SymbolGraphMergerCategory),
+             cl::value_desc("filepath"),
+             cl::desc("Input directory containing all the SymbolGraphs"));
+
+int main(int argc, const char **argv) {
+  llvm::sys::PrintStackTraceOnErrorSignal(argv[0]);
+
+  cl::ParseCommandLineOptions(argc, argv);
+
+  // collect symbol graphs from input dir
+  SmallVector<SymbolGraph> SymbolGraphs;
+  if (collectSymbolGraphs(InputDir, SymbolGraphs)) {
+    llvm::outs() << "found " << SymbolGraphs.size() << " Symbol-graphs in "
+                 << InputDir << "\n";
+
+    // merge them together to form unified APIset
+    llvm::outs() << "merging ...\n";
+    SymbolGraphMerger Merger(SymbolGraphs);
+    if (Merger.merge()) {
+      // serialize the unified symbol graph
+      std::error_code Error;
+      if (!sys::path::extension(OutFile).equals(".json"))
+        OutFile.append(".json");
+
+      llvm::outs() << "serializing...\n";
+      auto OS = std::make_unique<raw_fd_ostream>(OutFile, Error);
+      const auto APISet = Merger.getAPISet();
+      if (APISet) {
+        clang::extractapi::SymbolGraphSerializer SGSerializer(
+            *APISet, clang::extractapi::APIIgnoresList());
+        SGSerializer.serialize(*OS);
+        OS.reset();
+        llvm::outs() << "successfully serialized resultant SymbolGraph to "
+                     << OutFile << "\n";
+      }
+    } else {
+      llvm::errs() << "merge faliure\n";
+      return 1;
+    }
+  } else {
+    llvm::errs() << "some error occured while accessing " << InputDir << "\n";
+    return 1;
+  }
+  return 0;
+}
Index: clang-tools-extra/clang-symbolgraph-merger/tool/CMakeLists.txt
===================================================================
--- /dev/null
+++ clang-tools-extra/clang-symbolgraph-merger/tool/CMakeLists.txt
@@ -0,0 +1,13 @@
+set(LLVM_LINK_COMPONENTS Support)
+
+add_clang_tool(clang-symbolgraph-merger SymbolGraphMergerMain.cpp)
+
+target_link_libraries(clang-symbolgraph-merger PRIVATE
+  clangBasic
+  clangFrontend
+  clangSerialization
+  clangTooling
+)
+
+target_link_libraries(clang-symbolgraph-merger PRIVATE
+  clangSymbolGraphMerger)
Index: clang-tools-extra/clang-symbolgraph-merger/lib/SymbolGraphMerger.cpp
===================================================================
--- /dev/null
+++ 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:
+    return false;
+  }
+  return true;
+}
+} // namespace
+
+bool SymbolGraphMerger::merge() {
+  for (const auto &SG : SymbolGraphs)
+    traverseSymbolGraph(SG);
+  return true;
+}
+
+bool SymbolGraphMerger::visitMetadata(const Object &Metadata) {
+  // TODO: check if all the symbol graphs are generated form same
+  // generator or not. Info from metadata is currently not needed to
+  // construct the APISet,
+  return true;
+}
+
+bool SymbolGraphMerger::visitModule(const Object &Module) {
+  if (!API) {
+    // If the product name is not provided via command line then extract product
+    // name from SymbolGraph
+    if (ProductName.empty())
+      if (auto NameStr = Module.getString("name"))
+        ProductName = NameStr->str();
+
+    // extract target triple info
+    if (const auto *Platform = Module.getObject("platform")) {
+      auto Arch = Platform->getString("architecture");
+      const auto *OSObj = Platform->getObject("operatingSystem");
+      auto Vendor = Platform->getString("vendor");
+      if (!(Arch && Vendor && OSObj))
+        return false;
+      if (auto OSStr = OSObj->getString("name")) {
+        Target = llvm::Triple(*Arch, *Vendor, *OSStr);
+        return true;
+      }
+    }
+    return false;
+  }
+  return true;
+}
+
+bool SymbolGraphMerger::visitRelationship(const Object &Relationship) {
+  std::string SourceUSR(Relationship.getString("source").value_or(""));
+  std::string TargetUSR(Relationship.getString("target").value_or(""));
+
+  auto *SourceSymbol = PendingSymbols.lookup(SourceUSR);
+  auto *TargetSymbol = VisitedSymbols.lookup(TargetUSR);
+  auto *TargetRecord = API->findRecordForUSR(TargetUSR);
+
+  switch (SourceSymbol->Kind) {
+  case APIRecord::RK_StructField: {
+    if (StructRecord *ParentStruct =
+            dyn_cast_or_null<StructRecord>(TargetRecord))
+      API->addStructField(ParentStruct, SourceSymbol->Name, SourceSymbol->USR,
+                          SourceSymbol->Location, SourceSymbol->Availabilities,
+                          SourceSymbol->Comments, SourceSymbol->DeclFragments,
+                          SourceSymbol->SubHeadings,
+                          false /*IsFromSystemHeader*/);
+  } break;
+  case APIRecord::RK_ObjCIvar: {
+    auto AddRecord = [&](ObjCContainerRecord *Container) {
+      ObjCInstanceVariableRecord::AccessControl Access =
+          getAccessFromString(SourceSymbol->AccessLevel);
+
+      API->addObjCInstanceVariable(
+          Container, SourceSymbol->Name, SourceSymbol->USR,
+          SourceSymbol->Location, SourceSymbol->Availabilities,
+          SourceSymbol->Comments, SourceSymbol->DeclFragments,
+          SourceSymbol->SubHeadings, Access, false /*IsFromSystemHeader*/);
+    };
+    if (!addWithContainerRecord(TargetSymbol->Kind, TargetRecord, AddRecord))
+      return false;
+  } break;
+  case APIRecord::RK_ObjCInstanceMethod: {
+    auto AddRecord = [&](ObjCContainerRecord *Container) {
+      API->addObjCMethod(Container, SourceSymbol->Name, SourceSymbol->USR,
+                         SourceSymbol->Location, SourceSymbol->Availabilities,
+                         SourceSymbol->Comments, SourceSymbol->DeclFragments,
+                         SourceSymbol->SubHeadings, SourceSymbol->FunctionSign,
+                         true
+                         /*IsInstanceMethod*/,
+                         false /*IsFromSystemHeader*/);
+    };
+    if (!addWithContainerRecord(TargetSymbol->Kind, TargetRecord, AddRecord))
+      return false;
+  } break;
+  case APIRecord::RK_EnumConstant: {
+    if (EnumRecord *Enum = dyn_cast_or_null<EnumRecord>(TargetRecord))
+      API->addEnumConstant(Enum, SourceSymbol->Name, SourceSymbol->USR,
+                           SourceSymbol->Location, SourceSymbol->Availabilities,
+                           SourceSymbol->Comments, SourceSymbol->DeclFragments,
+                           SourceSymbol->SubHeadings,
+                           false /*IsFromSystemHeader*/);
+  } break;
+  case APIRecord::RK_ObjCClassMethod: {
+    auto AddRecord = [&](ObjCContainerRecord *Container) {
+      API->addObjCMethod(Container, SourceSymbol->Name, SourceSymbol->USR,
+                         SourceSymbol->Location, SourceSymbol->Availabilities,
+                         SourceSymbol->Comments, SourceSymbol->DeclFragments,
+                         SourceSymbol->SubHeadings, SourceSymbol->FunctionSign,
+                         false
+                         /*IsInstanceMethod*/,
+                         false /*IsFromSystemHeader*/);
+    };
+    if (!addWithContainerRecord(TargetSymbol->Kind, TargetRecord, AddRecord))
+      return false;
+  } break;
+  case APIRecord::RK_ObjCInstanceProperty: {
+    auto AddRecord = [&](ObjCContainerRecord *Container) {
+      API->addObjCProperty(Container, SourceSymbol->Name, SourceSymbol->USR,
+                           SourceSymbol->Location, SourceSymbol->Availabilities,
+                           SourceSymbol->Comments, SourceSymbol->DeclFragments,
+                           SourceSymbol->SubHeadings,
+                           ObjCPropertyRecord::AttributeKind::NoAttr, "", "",
+                           false /*IsOptional*/, true /*IsInstanceProperty*/,
+                           false /*IsFromSystemHeader*/);
+    };
+    if (!addWithContainerRecord(TargetSymbol->Kind, TargetRecord, AddRecord))
+      return false;
+  } break;
+  case APIRecord::RK_ObjCClassProperty: {
+    auto AddRecord = [&](ObjCContainerRecord *Container) {
+      API->addObjCProperty(Container, SourceSymbol->Name, SourceSymbol->USR,
+                           SourceSymbol->Location, SourceSymbol->Availabilities,
+                           SourceSymbol->Comments, SourceSymbol->DeclFragments,
+                           SourceSymbol->SubHeadings,
+                           ObjCPropertyRecord::AttributeKind::NoAttr, "", "",
+                           false /*IsOptional*/, false /*IsInstanceProperty*/,
+                           false /*IsFromSystemHeader*/);
+    };
+    if (!addWithContainerRecord(TargetSymbol->Kind, TargetRecord, AddRecord))
+      return false;
+  } break;
+  case APIRecord::RK_ObjCInterface: {
+    if (TargetRecord) {
+      SymbolReference SuperClass(TargetRecord);
+
+      API->addObjCInterface(
+          SourceSymbol->Name, SourceSymbol->USR, SourceSymbol->Location,
+          SourceSymbol->Availabilities, LinkageInfo(), SourceSymbol->Comments,
+          SourceSymbol->DeclFragments, SourceSymbol->SubHeadings, SuperClass,
+          false /*IsFromSystemHeader*/);
+    }
+  } break;
+  case APIRecord::RK_ObjCCategory: {
+    if (TargetRecord) {
+      SymbolReference Interface(TargetRecord);
+
+      API->addObjCCategory(
+          SourceSymbol->Name, SourceSymbol->USR, SourceSymbol->Location,
+          SourceSymbol->Availabilities, SourceSymbol->Comments,
+          SourceSymbol->DeclFragments, SourceSymbol->SubHeadings, Interface,
+          false /*IsFromSystemHeader*/, true /*IsFromExternalModule*/);
+    }
+  } break;
+  default:
+    return false;
+  }
+  return true;
+}
+
+bool SymbolGraphMerger::visitSymbol(const SymbolGraph::Symbol &Symbol) {
+  // If the APISet is not yet created, then create it ( it's generally
+  // the Language information that is pending uptill this point )
+  if (!API) {
+    if (const auto *Id = Symbol.SymbolObj.getObject("identifier"))
+      if (const auto LangName = Id->getString("interfaceLanguage"))
+        Lang = getLanguageFromString(LangName.value_or(""));
+    API = std::make_unique<extractapi::APISet>(Target, Lang, ProductName);
+  }
+
+  switch (Symbol.Kind) {
+  // TODO: Handle unknown symbols properly
+  case APIRecord::RK_Unknown:
+    break;
+  case APIRecord::RK_GlobalVariable: {
+    API->addGlobalVar(Symbol.Name, Symbol.USR, Symbol.Location,
+                      Symbol.Availabilities, LinkageInfo(), Symbol.Comments,
+                      Symbol.DeclFragments, Symbol.SubHeadings,
+                      false /*IsFromSystemHeader*/);
+    VisitedSymbols[Symbol.USR] = &Symbol;
+  } break;
+  case APIRecord::RK_GlobalFunction: {
+    API->addGlobalFunction(
+        Symbol.Name, Symbol.USR, Symbol.Location, Symbol.Availabilities,
+        LinkageInfo(), Symbol.Comments, Symbol.DeclFragments,
+        Symbol.SubHeadings, Symbol.FunctionSign, false /*IsFromSystemHeader*/);
+    VisitedSymbols[Symbol.USR] = &Symbol;
+  } break;
+
+  case APIRecord::RK_Enum: {
+    API->addEnum(Symbol.Name, Symbol.USR, Symbol.Location,
+                 Symbol.Availabilities, Symbol.Comments, Symbol.DeclFragments,
+                 Symbol.SubHeadings, false /*IsFromSystemHeader*/);
+    VisitedSymbols[Symbol.USR] = &Symbol;
+  } break;
+  case APIRecord::RK_Struct: {
+    API->addStruct(Symbol.Name, Symbol.USR, Symbol.Location,
+                   Symbol.Availabilities, Symbol.Comments, Symbol.DeclFragments,
+                   Symbol.SubHeadings, false /*IsFromSystemHeader*/);
+    VisitedSymbols[Symbol.USR] = &Symbol;
+  } break;
+  case APIRecord::RK_ObjCProtocol: {
+    API->addObjCProtocol(Symbol.Name, Symbol.USR, Symbol.Location,
+                         Symbol.Availabilities, Symbol.Comments,
+                         Symbol.DeclFragments, Symbol.SubHeadings,
+                         false /*IsFromSystemHeader*/);
+    VisitedSymbols[Symbol.USR] = &Symbol;
+  } break;
+  case APIRecord::RK_MacroDefinition: {
+    API->addMacroDefinition(Symbol.Name, Symbol.USR, Symbol.Location,
+                            Symbol.DeclFragments, Symbol.SubHeadings,
+                            false /*IsFromSystemHeader*/);
+    VisitedSymbols[Symbol.USR] = &Symbol;
+  } break;
+  case APIRecord::RK_Typedef: {
+    API->addTypedef(Symbol.Name, Symbol.USR, Symbol.Location,
+                    Symbol.Availabilities, Symbol.Comments,
+                    Symbol.DeclFragments, Symbol.SubHeadings,
+                    Symbol.UnderLyingType, false /*IsFromSystemHeader*/);
+    VisitedSymbols[Symbol.USR] = &Symbol;
+  } break;
+  default:
+    // Try again when visiting Relationships
+    PendingSymbols[Symbol.USR] = &Symbol;
+  }
+  return true;
+}
Index: clang-tools-extra/clang-symbolgraph-merger/lib/SymbolGraph.cpp
===================================================================
--- /dev/null
+++ 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"));
+    }
+  }
+}
Index: clang-tools-extra/clang-symbolgraph-merger/lib/CMakeLists.txt
===================================================================
--- /dev/null
+++ 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
+)
Index: clang-tools-extra/clang-symbolgraph-merger/include/clang-symbolgraph-merger/SymbolGraphVisitor.h
===================================================================
--- /dev/null
+++ 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 */
Index: clang-tools-extra/clang-symbolgraph-merger/include/clang-symbolgraph-merger/SymbolGraphMerger.h
===================================================================
--- /dev/null
+++ 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 */
Index: clang-tools-extra/clang-symbolgraph-merger/include/clang-symbolgraph-merger/SymbolGraph.h
===================================================================
--- /dev/null
+++ 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 */
Index: clang-tools-extra/clang-symbolgraph-merger/CMakeLists.txt
===================================================================
--- /dev/null
+++ clang-tools-extra/clang-symbolgraph-merger/CMakeLists.txt
@@ -0,0 +1,3 @@
+include_directories(include)
+add_subdirectory(lib)
+add_subdirectory(tool)
Index: clang-tools-extra/CMakeLists.txt
===================================================================
--- clang-tools-extra/CMakeLists.txt
+++ clang-tools-extra/CMakeLists.txt
@@ -13,6 +13,7 @@
   endif()
 endif()
 
+add_subdirectory(clang-symbolgraph-merger)
 add_subdirectory(clang-apply-replacements)
 add_subdirectory(clang-reorder-fields)
 add_subdirectory(modularize)
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
  • [PATCH] D158646: [clang-to... Ankur Saini via Phabricator via cfe-commits

Reply via email to