[clang] 71b4c22 - [clang][extract-api] Add enum support

Zixu Wang via cfe-commits cfe-commits at lists.llvm.org
Wed Mar 23 09:42:45 PDT 2022


Author: Zixu Wang
Date: 2022-03-23T09:41:21-07:00
New Revision: 71b4c22612a06c950d31db83a45dee7412a64c64

URL: https://github.com/llvm/llvm-project/commit/71b4c22612a06c950d31db83a45dee7412a64c64
DIFF: https://github.com/llvm/llvm-project/commit/71b4c22612a06c950d31db83a45dee7412a64c64.diff

LOG: [clang][extract-api] Add enum support

Add support for enum records
- Add `EnumConstantRecord` and `EnumRecord` to store API information for
  enums
- Implement `VisitEnumDecl` in `ExtractAPIVisitor`
- Implement serializatin for enum records and `MemberOf` relationship
- Add test case for enum records
- Few other improvements

Depends on D122160

Differential Revision: https://reviews.llvm.org/D121873

Added: 
    clang/test/ExtractAPI/enum.c

Modified: 
    clang/include/clang/ExtractAPI/API.h
    clang/include/clang/ExtractAPI/DeclarationFragments.h
    clang/include/clang/ExtractAPI/Serialization/SymbolGraphSerializer.h
    clang/lib/ExtractAPI/API.cpp
    clang/lib/ExtractAPI/DeclarationFragments.cpp
    clang/lib/ExtractAPI/ExtractAPIConsumer.cpp
    clang/lib/ExtractAPI/Serialization/SymbolGraphSerializer.cpp

Removed: 
    


################################################################################
diff  --git a/clang/include/clang/ExtractAPI/API.h b/clang/include/clang/ExtractAPI/API.h
index 5ba3cf7268d88..52b78d9188a1c 100644
--- a/clang/include/clang/ExtractAPI/API.h
+++ b/clang/include/clang/ExtractAPI/API.h
@@ -30,6 +30,25 @@
 #include "llvm/Support/Casting.h"
 #include <memory>
 
+namespace {
+
+/// \brief A custom deleter used for ``std::unique_ptr`` to APIRecords stored
+/// in the BumpPtrAllocator.
+///
+/// \tparam T the exact type of the APIRecord subclass.
+template <typename T> struct UniquePtrBumpPtrAllocatorDeleter {
+  void operator()(T *Instance) { Instance->~T(); }
+};
+
+/// A unique pointer to an APIRecord stored in the BumpPtrAllocator.
+///
+/// \tparam T the exact type of the APIRecord subclass.
+template <typename T>
+using APIRecordUniquePtr =
+    std::unique_ptr<T, UniquePtrBumpPtrAllocatorDeleter<T>>;
+
+} // anonymous namespace
+
 namespace clang {
 namespace extractapi {
 
@@ -73,6 +92,8 @@ struct APIRecord {
   /// Discriminator for LLVM-style RTTI (dyn_cast<> et al.)
   enum RecordKind {
     RK_Global,
+    RK_EnumConstant,
+    RK_Enum,
   };
 
 private:
@@ -125,6 +146,36 @@ struct GlobalRecord : APIRecord {
   virtual void anchor();
 };
 
+/// This holds information associated with enum constants.
+struct EnumConstantRecord : APIRecord {
+  EnumConstantRecord(StringRef Name, StringRef USR, PresumedLoc Loc,
+                     const AvailabilityInfo &Availability,
+                     const DocComment &Comment,
+                     DeclarationFragments Declaration,
+                     DeclarationFragments SubHeading)
+      : APIRecord(RK_EnumConstant, Name, USR, Loc, Availability,
+                  LinkageInfo::none(), Comment, Declaration, SubHeading) {}
+
+  static bool classof(const APIRecord *Record) {
+    return Record->getKind() == RK_EnumConstant;
+  }
+};
+
+/// This holds information associated with enums.
+struct EnumRecord : APIRecord {
+  SmallVector<APIRecordUniquePtr<EnumConstantRecord>> Constants;
+
+  EnumRecord(StringRef Name, StringRef USR, PresumedLoc Loc,
+             const AvailabilityInfo &Availability, const DocComment &Comment,
+             DeclarationFragments Declaration, DeclarationFragments SubHeading)
+      : APIRecord(RK_Enum, Name, USR, Loc, Availability, LinkageInfo::none(),
+                  Comment, Declaration, SubHeading) {}
+
+  static bool classof(const APIRecord *Record) {
+    return Record->getKind() == RK_Enum;
+  }
+};
+
 /// APISet holds the set of API records collected from given inputs.
 class APISet {
 public:
@@ -166,28 +217,41 @@ class APISet {
                             DeclarationFragments SubHeading,
                             FunctionSignature Signature);
 
-private:
-  /// \brief A custom deleter used for ``std::unique_ptr`` to APIRecords stored
-  /// in the BumpPtrAllocator.
+  /// Create and add an enum constant record into the API set.
   ///
-  /// \tparam T the exact type of the APIRecord subclass.
-  template <typename T> struct UniquePtrBumpPtrAllocatorDeleter {
-    void operator()(T *Instance) { Instance->~T(); }
-  };
-
-public:
-  /// A unique pointer to an APIRecord stored in the BumpPtrAllocator.
+  /// Note: the caller is responsible for keeping the StringRef \p Name and
+  /// \p USR alive. APISet::copyString provides a way to copy strings into
+  /// APISet itself, and APISet::recordUSR(const Decl *D) is a helper method
+  /// to generate the USR for \c D and keep it alive in APISet.
+  EnumConstantRecord *addEnumConstant(EnumRecord *Enum, StringRef Name,
+                                      StringRef USR, PresumedLoc Loc,
+                                      const AvailabilityInfo &Availability,
+                                      const DocComment &Comment,
+                                      DeclarationFragments Declaration,
+                                      DeclarationFragments SubHeading);
+
+  /// Create and add an enum record into the API set.
   ///
-  /// \tparam T the exact type of the APIRecord subclass.
-  template <typename T>
-  using APIRecordUniquePtr =
-      std::unique_ptr<T, UniquePtrBumpPtrAllocatorDeleter<T>>;
+  /// Note: the caller is responsible for keeping the StringRef \p Name and
+  /// \p USR alive. APISet::copyString provides a way to copy strings into
+  /// APISet itself, and APISet::recordUSR(const Decl *D) is a helper method
+  /// to generate the USR for \c D and keep it alive in APISet.
+  EnumRecord *addEnum(StringRef Name, StringRef USR, PresumedLoc Loc,
+                      const AvailabilityInfo &Availability,
+                      const DocComment &Comment,
+                      DeclarationFragments Declaration,
+                      DeclarationFragments SubHeading);
 
   /// A map to store the set of GlobalRecord%s with the declaration name as the
   /// key.
   using GlobalRecordMap =
       llvm::MapVector<StringRef, APIRecordUniquePtr<GlobalRecord>>;
 
+  /// A map to store the set of EnumRecord%s with the declaration name as the
+  /// key.
+  using EnumRecordMap =
+      llvm::MapVector<StringRef, APIRecordUniquePtr<EnumRecord>>;
+
   /// Get the target triple for the ExtractAPI invocation.
   const llvm::Triple &getTarget() const { return Target; }
 
@@ -195,6 +259,7 @@ class APISet {
   const LangOptions &getLangOpts() const { return LangOpts; }
 
   const GlobalRecordMap &getGlobals() const { return Globals; }
+  const EnumRecordMap &getEnums() const { return Enums; }
 
   /// Generate and store the USR of declaration \p D.
   ///
@@ -219,6 +284,7 @@ class APISet {
   const LangOptions LangOpts;
 
   GlobalRecordMap Globals;
+  EnumRecordMap Enums;
 };
 
 } // namespace extractapi

diff  --git a/clang/include/clang/ExtractAPI/DeclarationFragments.h b/clang/include/clang/ExtractAPI/DeclarationFragments.h
index 17375efd12c77..7e76a57a48189 100644
--- a/clang/include/clang/ExtractAPI/DeclarationFragments.h
+++ b/clang/include/clang/ExtractAPI/DeclarationFragments.h
@@ -188,6 +188,14 @@ class DeclarationFragmentsBuilder {
   /// Build DeclarationFragments for a function declaration FunctionDecl.
   static DeclarationFragments getFragmentsForFunction(const FunctionDecl *);
 
+  /// Build DeclarationFragments for an enum constant declaration
+  /// EnumConstantDecl.
+  static DeclarationFragments
+  getFragmentsForEnumConstant(const EnumConstantDecl *);
+
+  /// Build DeclarationFragments for an enum declaration EnumDecl.
+  static DeclarationFragments getFragmentsForEnum(const EnumDecl *);
+
   /// Build sub-heading fragments for a NamedDecl.
   static DeclarationFragments getSubHeading(const NamedDecl *);
 

diff  --git a/clang/include/clang/ExtractAPI/Serialization/SymbolGraphSerializer.h b/clang/include/clang/ExtractAPI/Serialization/SymbolGraphSerializer.h
index 11c84c1e205d6..2423f511480d4 100644
--- a/clang/include/clang/ExtractAPI/Serialization/SymbolGraphSerializer.h
+++ b/clang/include/clang/ExtractAPI/Serialization/SymbolGraphSerializer.h
@@ -56,6 +56,17 @@ class SymbolGraphSerializer : public APISerializer {
   /// write out the serialized JSON object to \p os.
   void serialize(raw_ostream &os) override;
 
+  /// The kind of a relationship between two symbols.
+  enum RelationshipKind {
+    /// The source symbol is a member of the target symbol.
+    /// For example enum constants are members of the enum, class/instance
+    /// methods are members of the class, etc.
+    MemberOf,
+  };
+
+  /// Get the string representation of the relationship kind.
+  static StringRef getRelationshipString(RelationshipKind Kind);
+
 private:
   /// Synthesize the metadata section of the Symbol Graph format.
   ///
@@ -86,9 +97,19 @@ class SymbolGraphSerializer : public APISerializer {
   /// containing common symbol information of \p Record.
   Optional<Object> serializeAPIRecord(const APIRecord &Record) const;
 
+  /// Serialize the \p Kind relationship between \p Source and \p Target.
+  ///
+  /// Record the relationship between the two symbols in
+  /// SymbolGraphSerializer::Relationships.
+  void serializeRelationship(RelationshipKind Kind, const APIRecord &Source,
+                             const APIRecord &Target);
+
   /// Serialize a global record.
   void serializeGlobalRecord(const GlobalRecord &Record);
 
+  /// Serialize an enum record.
+  void serializeEnumRecord(const EnumRecord &Record);
+
 public:
   SymbolGraphSerializer(const APISet &API, StringRef ProductName,
                         APISerializerOption Options = {})

diff  --git a/clang/lib/ExtractAPI/API.cpp b/clang/lib/ExtractAPI/API.cpp
index 45eef3d1e9529..fd9e92cc7b2bf 100644
--- a/clang/lib/ExtractAPI/API.cpp
+++ b/clang/lib/ExtractAPI/API.cpp
@@ -59,6 +59,31 @@ APISet::addFunction(StringRef Name, StringRef USR, PresumedLoc Loc,
                    Comment, Fragments, SubHeading, Signature);
 }
 
+EnumConstantRecord *APISet::addEnumConstant(
+    EnumRecord *Enum, StringRef Name, StringRef USR, PresumedLoc Loc,
+    const AvailabilityInfo &Availability, const DocComment &Comment,
+    DeclarationFragments Declaration, DeclarationFragments SubHeading) {
+  auto Record =
+      APIRecordUniquePtr<EnumConstantRecord>(new (Allocator) EnumConstantRecord{
+          Name, USR, Loc, Availability, Comment, Declaration, SubHeading});
+  return Enum->Constants.emplace_back(std::move(Record)).get();
+}
+
+EnumRecord *APISet::addEnum(StringRef Name, StringRef USR, PresumedLoc Loc,
+                            const AvailabilityInfo &Availability,
+                            const DocComment &Comment,
+                            DeclarationFragments Declaration,
+                            DeclarationFragments SubHeading) {
+  auto Result = Enums.insert({Name, nullptr});
+  if (Result.second) {
+    // Create the record if it does not already exist.
+    auto Record = APIRecordUniquePtr<EnumRecord>(new (Allocator) EnumRecord{
+        Name, USR, Loc, Availability, Comment, Declaration, SubHeading});
+    Result.first->second = std::move(Record);
+  }
+  return Result.first->second.get();
+}
+
 StringRef APISet::recordUSR(const Decl *D) {
   SmallString<128> USR;
   index::generateUSRForDecl(D, USR);

diff  --git a/clang/lib/ExtractAPI/DeclarationFragments.cpp b/clang/lib/ExtractAPI/DeclarationFragments.cpp
index d5e03832f80e1..daa01c7dcd13b 100644
--- a/clang/lib/ExtractAPI/DeclarationFragments.cpp
+++ b/clang/lib/ExtractAPI/DeclarationFragments.cpp
@@ -401,6 +401,35 @@ DeclarationFragmentsBuilder::getFragmentsForFunction(const FunctionDecl *Func) {
   return Fragments;
 }
 
+DeclarationFragments DeclarationFragmentsBuilder::getFragmentsForEnumConstant(
+    const EnumConstantDecl *EnumConstDecl) {
+  DeclarationFragments Fragments;
+  return Fragments.append(EnumConstDecl->getName(),
+                          DeclarationFragments::FragmentKind::Identifier);
+}
+
+DeclarationFragments
+DeclarationFragmentsBuilder::getFragmentsForEnum(const EnumDecl *EnumDecl) {
+  // TODO: After we support typedef records, if there's a typedef for this enum
+  // just use the declaration fragments of the typedef decl.
+
+  DeclarationFragments Fragments, After;
+  Fragments.append("enum", DeclarationFragments::FragmentKind::Keyword);
+
+  if (!EnumDecl->getName().empty())
+    Fragments.appendSpace().append(
+        EnumDecl->getName(), DeclarationFragments::FragmentKind::Identifier);
+
+  QualType IntegerType = EnumDecl->getIntegerType();
+  if (!IntegerType.isNull())
+    Fragments.append(": ", DeclarationFragments::FragmentKind::Text)
+        .append(
+            getFragmentsForType(IntegerType, EnumDecl->getASTContext(), After))
+        .append(std::move(After));
+
+  return Fragments;
+}
+
 FunctionSignature
 DeclarationFragmentsBuilder::getFunctionSignature(const FunctionDecl *Func) {
   FunctionSignature Signature;

diff  --git a/clang/lib/ExtractAPI/ExtractAPIConsumer.cpp b/clang/lib/ExtractAPI/ExtractAPIConsumer.cpp
index 754c3c1089b62..9c8adcf9347ba 100644
--- a/clang/lib/ExtractAPI/ExtractAPIConsumer.cpp
+++ b/clang/lib/ExtractAPI/ExtractAPIConsumer.cpp
@@ -145,6 +145,40 @@ class ExtractAPIVisitor : public RecursiveASTVisitor<ExtractAPIVisitor> {
     return true;
   }
 
+  bool VisitEnumDecl(const EnumDecl *Decl) {
+    if (!Decl->isComplete())
+      return true;
+
+    // Skip forward declaration.
+    if (!Decl->isThisDeclarationADefinition())
+      return true;
+
+    // Collect symbol information.
+    StringRef Name = Decl->getName();
+    StringRef USR = API.recordUSR(Decl);
+    PresumedLoc Loc =
+        Context.getSourceManager().getPresumedLoc(Decl->getLocation());
+    AvailabilityInfo Availability = getAvailability(Decl);
+    DocComment Comment;
+    if (auto *RawComment = Context.getRawCommentForDeclNoCache(Decl))
+      Comment = RawComment->getFormattedLines(Context.getSourceManager(),
+                                              Context.getDiagnostics());
+
+    // Build declaration fragments and sub-heading for the enum.
+    DeclarationFragments Declaration =
+        DeclarationFragmentsBuilder::getFragmentsForEnum(Decl);
+    DeclarationFragments SubHeading =
+        DeclarationFragmentsBuilder::getSubHeading(Decl);
+
+    EnumRecord *EnumRecord = API.addEnum(Name, USR, Loc, Availability, Comment,
+                                         Declaration, SubHeading);
+
+    // Now collect information about the enumerators in this enum.
+    recordEnumConstants(EnumRecord, Decl->enumerators());
+
+    return true;
+  }
+
 private:
   /// Get availability information of the declaration \p D.
   AvailabilityInfo getAvailability(const Decl *D) const {
@@ -177,6 +211,33 @@ class ExtractAPIVisitor : public RecursiveASTVisitor<ExtractAPIVisitor> {
     return Availability;
   }
 
+  /// Collect API information for the enum constants and associate with the
+  /// parent enum.
+  void recordEnumConstants(EnumRecord *EnumRecord,
+                           const EnumDecl::enumerator_range Constants) {
+    for (const auto *Constant : Constants) {
+      // Collect symbol information.
+      StringRef Name = Constant->getName();
+      StringRef USR = API.recordUSR(Constant);
+      PresumedLoc Loc =
+          Context.getSourceManager().getPresumedLoc(Constant->getLocation());
+      AvailabilityInfo Availability = getAvailability(Constant);
+      DocComment Comment;
+      if (auto *RawComment = Context.getRawCommentForDeclNoCache(Constant))
+        Comment = RawComment->getFormattedLines(Context.getSourceManager(),
+                                                Context.getDiagnostics());
+
+      // Build declaration fragments and sub-heading for the enum constant.
+      DeclarationFragments Declaration =
+          DeclarationFragmentsBuilder::getFragmentsForEnumConstant(Constant);
+      DeclarationFragments SubHeading =
+          DeclarationFragmentsBuilder::getSubHeading(Constant);
+
+      API.addEnumConstant(EnumRecord, Name, USR, Loc, Availability, Comment,
+                          Declaration, SubHeading);
+    }
+  }
+
   ASTContext &Context;
   APISet API;
 };

diff  --git a/clang/lib/ExtractAPI/Serialization/SymbolGraphSerializer.cpp b/clang/lib/ExtractAPI/Serialization/SymbolGraphSerializer.cpp
index f262aab418d33..574ea451e276c 100644
--- a/clang/lib/ExtractAPI/Serialization/SymbolGraphSerializer.cpp
+++ b/clang/lib/ExtractAPI/Serialization/SymbolGraphSerializer.cpp
@@ -336,17 +336,21 @@ Object serializeNames(const APIRecord &Record) {
 /// the kind, and a \c displayName for rendering human-readable names.
 Object serializeSymbolKind(const APIRecord &Record,
                            const LangOptions &LangOpts) {
+  auto AddLangPrefix = [&LangOpts](StringRef S) -> std::string {
+    return (getLanguageName(LangOpts) + "." + S).str();
+  };
+
   Object Kind;
   switch (Record.getKind()) {
-  case APIRecord::RK_Global:
+  case APIRecord::RK_Global: {
     auto *GR = dyn_cast<GlobalRecord>(&Record);
     switch (GR->GlobalKind) {
     case GVKind::Function:
-      Kind["identifier"] = (getLanguageName(LangOpts) + ".func").str();
+      Kind["identifier"] = AddLangPrefix("func");
       Kind["displayName"] = "Function";
       break;
     case GVKind::Variable:
-      Kind["identifier"] = (getLanguageName(LangOpts) + ".var").str();
+      Kind["identifier"] = AddLangPrefix("var");
       Kind["displayName"] = "Global Variable";
       break;
     case GVKind::Unknown:
@@ -355,6 +359,15 @@ Object serializeSymbolKind(const APIRecord &Record,
     }
     break;
   }
+  case APIRecord::RK_EnumConstant:
+    Kind["identifier"] = AddLangPrefix("enum.case");
+    Kind["displayName"] = "Enumeration Case";
+    break;
+  case APIRecord::RK_Enum:
+    Kind["identifier"] = AddLangPrefix("enum");
+    Kind["displayName"] = "Enumeration";
+    break;
+  }
 
   return Kind;
 }
@@ -413,6 +426,25 @@ SymbolGraphSerializer::serializeAPIRecord(const APIRecord &Record) const {
   return Obj;
 }
 
+StringRef SymbolGraphSerializer::getRelationshipString(RelationshipKind Kind) {
+  switch (Kind) {
+  case RelationshipKind::MemberOf:
+    return "memberOf";
+  }
+  llvm_unreachable("Unhandled relationship kind");
+}
+
+void SymbolGraphSerializer::serializeRelationship(RelationshipKind Kind,
+                                                  const APIRecord &Source,
+                                                  const APIRecord &Target) {
+  Object Relationship;
+  Relationship["source"] = Source.USR;
+  Relationship["target"] = Target.USR;
+  Relationship["kind"] = getRelationshipString(Kind);
+
+  Relationships.emplace_back(std::move(Relationship));
+}
+
 void SymbolGraphSerializer::serializeGlobalRecord(const GlobalRecord &Record) {
   auto Obj = serializeAPIRecord(Record);
   if (!Obj)
@@ -425,6 +457,23 @@ void SymbolGraphSerializer::serializeGlobalRecord(const GlobalRecord &Record) {
   Symbols.emplace_back(std::move(*Obj));
 }
 
+void SymbolGraphSerializer::serializeEnumRecord(const EnumRecord &Record) {
+  auto Enum = serializeAPIRecord(Record);
+  if (!Enum)
+    return;
+
+  Symbols.emplace_back(std::move(*Enum));
+
+  for (const auto &Constant : Record.Constants) {
+    auto EnumConstant = serializeAPIRecord(*Constant);
+    if (!EnumConstant)
+      continue;
+
+    Symbols.emplace_back(std::move(*EnumConstant));
+    serializeRelationship(RelationshipKind::MemberOf, *Constant, Record);
+  }
+}
+
 Object SymbolGraphSerializer::serialize() {
   Object Root;
   serializeObject(Root, "metadata", serializeMetadata());
@@ -434,6 +483,10 @@ Object SymbolGraphSerializer::serialize() {
   for (const auto &Global : API.getGlobals())
     serializeGlobalRecord(*Global.second);
 
+  // Serialize enum records in the API set.
+  for (const auto &Enum : API.getEnums())
+    serializeEnumRecord(*Enum.second);
+
   Root["symbols"] = std::move(Symbols);
   Root["relationhips"] = std::move(Relationships);
 

diff  --git a/clang/test/ExtractAPI/enum.c b/clang/test/ExtractAPI/enum.c
new file mode 100644
index 0000000000000..f3b5cc8e08c74
--- /dev/null
+++ b/clang/test/ExtractAPI/enum.c
@@ -0,0 +1,505 @@
+// RUN: rm -rf %t
+// RUN: split-file %s %t
+// RUN: sed -e "s at INPUT_DIR@%/t at g" %t/reference.output.json.in >> \
+// RUN: %t/reference.output.json
+// RUN: %clang -extract-api -target arm64-apple-macosx \
+// RUN: %t/input.h -o %t/output.json | FileCheck -allow-empty %s
+
+// Generator version is not consistent across test runs, normalize it.
+// RUN: sed -e "s@\"generator\": \".*\"@\"generator\": \"?\"@g" \
+// RUN: %t/output.json >> %t/output-normalized.json
+// RUN: 
diff  %t/reference.output.json %t/output-normalized.json
+
+// CHECK-NOT: error:
+// CHECK-NOT: warning:
+
+//--- input.h
+/// Kinds of vehicles
+enum Vehicle {
+  Bicycle,
+  Car,
+  Train, ///< Move this to the top! -Sheldon
+  Ship,
+  Airplane,
+};
+
+enum Direction : unsigned char {
+  North = 0,
+  East,
+  South,
+  West
+};
+
+//--- reference.output.json.in
+{
+  "metadata": {
+    "formatVersion": {
+      "major": 0,
+      "minor": 5,
+      "patch": 3
+    },
+    "generator": "?"
+  },
+  "module": {
+    "name": "",
+    "platform": {
+      "architecture": "arm64",
+      "operatingSystem": {
+        "minimumVersion": {
+          "major": 11,
+          "minor": 0,
+          "patch": 0
+        },
+        "name": "macosx"
+      },
+      "vendor": "apple"
+    }
+  },
+  "relationhips": [
+    {
+      "kind": "memberOf",
+      "source": "c:@E at Vehicle@Bicycle",
+      "target": "c:@E at Vehicle"
+    },
+    {
+      "kind": "memberOf",
+      "source": "c:@E at Vehicle@Car",
+      "target": "c:@E at Vehicle"
+    },
+    {
+      "kind": "memberOf",
+      "source": "c:@E at Vehicle@Train",
+      "target": "c:@E at Vehicle"
+    },
+    {
+      "kind": "memberOf",
+      "source": "c:@E at Vehicle@Ship",
+      "target": "c:@E at Vehicle"
+    },
+    {
+      "kind": "memberOf",
+      "source": "c:@E at Vehicle@Airplane",
+      "target": "c:@E at Vehicle"
+    },
+    {
+      "kind": "memberOf",
+      "source": "c:@E at Direction@North",
+      "target": "c:@E at Direction"
+    },
+    {
+      "kind": "memberOf",
+      "source": "c:@E at Direction@East",
+      "target": "c:@E at Direction"
+    },
+    {
+      "kind": "memberOf",
+      "source": "c:@E at Direction@South",
+      "target": "c:@E at Direction"
+    },
+    {
+      "kind": "memberOf",
+      "source": "c:@E at Direction@West",
+      "target": "c:@E at Direction"
+    }
+  ],
+  "symbols": [
+    {
+      "declarationFragments": [
+        {
+          "kind": "keyword",
+          "spelling": "enum"
+        },
+        {
+          "kind": "text",
+          "spelling": " "
+        },
+        {
+          "kind": "identifier",
+          "spelling": "Vehicle"
+        },
+        {
+          "kind": "text",
+          "spelling": ": "
+        },
+        {
+          "kind": "typeIdentifier",
+          "preciseIdentifier": "c:i",
+          "spelling": "unsigned int"
+        }
+      ],
+      "docComment": {
+        "lines": [
+          {
+            "range": {
+              "end": {
+                "character": 22,
+                "line": 1
+              },
+              "start": {
+                "character": 5,
+                "line": 1
+              }
+            },
+            "text": "Kinds of vehicles"
+          }
+        ]
+      },
+      "identifier": {
+        "interfaceLanguage": "c",
+        "precise": "c:@E at Vehicle"
+      },
+      "kind": {
+        "displayName": "Enumeration",
+        "identifier": "c.enum"
+      },
+      "location": {
+        "character": 6,
+        "line": 2,
+        "uri": "file://INPUT_DIR/input.h"
+      },
+      "names": {
+        "subHeading": [
+          {
+            "kind": "identifier",
+            "spelling": "Vehicle"
+          }
+        ],
+        "title": "Vehicle"
+      }
+    },
+    {
+      "declarationFragments": [
+        {
+          "kind": "identifier",
+          "spelling": "Bicycle"
+        }
+      ],
+      "identifier": {
+        "interfaceLanguage": "c",
+        "precise": "c:@E at Vehicle@Bicycle"
+      },
+      "kind": {
+        "displayName": "Enumeration Case",
+        "identifier": "c.enum.case"
+      },
+      "location": {
+        "character": 3,
+        "line": 3,
+        "uri": "file://INPUT_DIR/input.h"
+      },
+      "names": {
+        "subHeading": [
+          {
+            "kind": "identifier",
+            "spelling": "Bicycle"
+          }
+        ],
+        "title": "Bicycle"
+      }
+    },
+    {
+      "declarationFragments": [
+        {
+          "kind": "identifier",
+          "spelling": "Car"
+        }
+      ],
+      "identifier": {
+        "interfaceLanguage": "c",
+        "precise": "c:@E at Vehicle@Car"
+      },
+      "kind": {
+        "displayName": "Enumeration Case",
+        "identifier": "c.enum.case"
+      },
+      "location": {
+        "character": 3,
+        "line": 4,
+        "uri": "file://INPUT_DIR/input.h"
+      },
+      "names": {
+        "subHeading": [
+          {
+            "kind": "identifier",
+            "spelling": "Car"
+          }
+        ],
+        "title": "Car"
+      }
+    },
+    {
+      "declarationFragments": [
+        {
+          "kind": "identifier",
+          "spelling": "Train"
+        }
+      ],
+      "docComment": {
+        "lines": [
+          {
+            "range": {
+              "end": {
+                "character": 45,
+                "line": 5
+              },
+              "start": {
+                "character": 15,
+                "line": 5
+              }
+            },
+            "text": "Move this to the top! -Sheldon"
+          }
+        ]
+      },
+      "identifier": {
+        "interfaceLanguage": "c",
+        "precise": "c:@E at Vehicle@Train"
+      },
+      "kind": {
+        "displayName": "Enumeration Case",
+        "identifier": "c.enum.case"
+      },
+      "location": {
+        "character": 3,
+        "line": 5,
+        "uri": "file://INPUT_DIR/input.h"
+      },
+      "names": {
+        "subHeading": [
+          {
+            "kind": "identifier",
+            "spelling": "Train"
+          }
+        ],
+        "title": "Train"
+      }
+    },
+    {
+      "declarationFragments": [
+        {
+          "kind": "identifier",
+          "spelling": "Ship"
+        }
+      ],
+      "identifier": {
+        "interfaceLanguage": "c",
+        "precise": "c:@E at Vehicle@Ship"
+      },
+      "kind": {
+        "displayName": "Enumeration Case",
+        "identifier": "c.enum.case"
+      },
+      "location": {
+        "character": 3,
+        "line": 6,
+        "uri": "file://INPUT_DIR/input.h"
+      },
+      "names": {
+        "subHeading": [
+          {
+            "kind": "identifier",
+            "spelling": "Ship"
+          }
+        ],
+        "title": "Ship"
+      }
+    },
+    {
+      "declarationFragments": [
+        {
+          "kind": "identifier",
+          "spelling": "Airplane"
+        }
+      ],
+      "identifier": {
+        "interfaceLanguage": "c",
+        "precise": "c:@E at Vehicle@Airplane"
+      },
+      "kind": {
+        "displayName": "Enumeration Case",
+        "identifier": "c.enum.case"
+      },
+      "location": {
+        "character": 3,
+        "line": 7,
+        "uri": "file://INPUT_DIR/input.h"
+      },
+      "names": {
+        "subHeading": [
+          {
+            "kind": "identifier",
+            "spelling": "Airplane"
+          }
+        ],
+        "title": "Airplane"
+      }
+    },
+    {
+      "declarationFragments": [
+        {
+          "kind": "keyword",
+          "spelling": "enum"
+        },
+        {
+          "kind": "text",
+          "spelling": " "
+        },
+        {
+          "kind": "identifier",
+          "spelling": "Direction"
+        },
+        {
+          "kind": "text",
+          "spelling": ": "
+        },
+        {
+          "kind": "typeIdentifier",
+          "preciseIdentifier": "c:c",
+          "spelling": "unsigned char"
+        }
+      ],
+      "identifier": {
+        "interfaceLanguage": "c",
+        "precise": "c:@E at Direction"
+      },
+      "kind": {
+        "displayName": "Enumeration",
+        "identifier": "c.enum"
+      },
+      "location": {
+        "character": 6,
+        "line": 10,
+        "uri": "file://INPUT_DIR/input.h"
+      },
+      "names": {
+        "subHeading": [
+          {
+            "kind": "identifier",
+            "spelling": "Direction"
+          }
+        ],
+        "title": "Direction"
+      }
+    },
+    {
+      "declarationFragments": [
+        {
+          "kind": "identifier",
+          "spelling": "North"
+        }
+      ],
+      "identifier": {
+        "interfaceLanguage": "c",
+        "precise": "c:@E at Direction@North"
+      },
+      "kind": {
+        "displayName": "Enumeration Case",
+        "identifier": "c.enum.case"
+      },
+      "location": {
+        "character": 3,
+        "line": 11,
+        "uri": "file://INPUT_DIR/input.h"
+      },
+      "names": {
+        "subHeading": [
+          {
+            "kind": "identifier",
+            "spelling": "North"
+          }
+        ],
+        "title": "North"
+      }
+    },
+    {
+      "declarationFragments": [
+        {
+          "kind": "identifier",
+          "spelling": "East"
+        }
+      ],
+      "identifier": {
+        "interfaceLanguage": "c",
+        "precise": "c:@E at Direction@East"
+      },
+      "kind": {
+        "displayName": "Enumeration Case",
+        "identifier": "c.enum.case"
+      },
+      "location": {
+        "character": 3,
+        "line": 12,
+        "uri": "file://INPUT_DIR/input.h"
+      },
+      "names": {
+        "subHeading": [
+          {
+            "kind": "identifier",
+            "spelling": "East"
+          }
+        ],
+        "title": "East"
+      }
+    },
+    {
+      "declarationFragments": [
+        {
+          "kind": "identifier",
+          "spelling": "South"
+        }
+      ],
+      "identifier": {
+        "interfaceLanguage": "c",
+        "precise": "c:@E at Direction@South"
+      },
+      "kind": {
+        "displayName": "Enumeration Case",
+        "identifier": "c.enum.case"
+      },
+      "location": {
+        "character": 3,
+        "line": 13,
+        "uri": "file://INPUT_DIR/input.h"
+      },
+      "names": {
+        "subHeading": [
+          {
+            "kind": "identifier",
+            "spelling": "South"
+          }
+        ],
+        "title": "South"
+      }
+    },
+    {
+      "declarationFragments": [
+        {
+          "kind": "identifier",
+          "spelling": "West"
+        }
+      ],
+      "identifier": {
+        "interfaceLanguage": "c",
+        "precise": "c:@E at Direction@West"
+      },
+      "kind": {
+        "displayName": "Enumeration Case",
+        "identifier": "c.enum.case"
+      },
+      "location": {
+        "character": 3,
+        "line": 14,
+        "uri": "file://INPUT_DIR/input.h"
+      },
+      "names": {
+        "subHeading": [
+          {
+            "kind": "identifier",
+            "spelling": "West"
+          }
+        ],
+        "title": "West"
+      }
+    }
+  ]
+}


        


More information about the cfe-commits mailing list