brettw created this revision.
brettw added a reviewer: paulkirth.
brettw added a project: clang-tools-extra.
Herald added a project: All.
brettw requested review of this revision.
Herald added a subscriber: cfe-commits.

Add support for explicitly typed enums:

  enum Foo : unsigned { ... };

to the internal representation and to the YAML output.

      

Add support for getting the value of an enum constant, as well as accessing the 
original expression that produced it. This changes the YAML output of enums 
from an array of strings for the enum members to an array of dictionaries. 
These dictionaries now report the name, value, and original expression.

      

The markdown and HTML outputs are unchanged, they still output the name from 
the new enhanced internal schema.


Repository:
  rG LLVM Github Monorepo

https://reviews.llvm.org/D134055

Files:
  clang-tools-extra/clang-doc/BitcodeReader.cpp
  clang-tools-extra/clang-doc/BitcodeWriter.cpp
  clang-tools-extra/clang-doc/BitcodeWriter.h
  clang-tools-extra/clang-doc/HTMLGenerator.cpp
  clang-tools-extra/clang-doc/MDGenerator.cpp
  clang-tools-extra/clang-doc/Representation.h
  clang-tools-extra/clang-doc/Serialize.cpp
  clang-tools-extra/clang-doc/YAMLGenerator.cpp
  clang-tools-extra/unittests/clang-doc/SerializeTest.cpp
  clang-tools-extra/unittests/clang-doc/YAMLGeneratorTest.cpp

Index: clang-tools-extra/unittests/clang-doc/YAMLGeneratorTest.cpp
===================================================================
--- clang-tools-extra/unittests/clang-doc/YAMLGeneratorTest.cpp
+++ clang-tools-extra/unittests/clang-doc/YAMLGeneratorTest.cpp
@@ -256,7 +256,11 @@
   EXPECT_EQ(Expected, Actual.str());
 }
 
-TEST(YAMLGeneratorTest, emitEnumYAML) {
+// Tests the equivalent of:
+// namespace A {
+// enum e { X };
+// }
+TEST(YAMLGeneratorTest, emitSimpleEnumYAML) {
   EnumInfo I;
   I.Name = "e";
   I.Namespace.emplace_back(EmptySID, "A", InfoType::IT_namespace);
@@ -265,7 +269,7 @@
   I.Loc.emplace_back(12, llvm::SmallString<16>{"test.cpp"});
 
   I.Members.emplace_back("X");
-  I.Scoped = true;
+  I.Scoped = false;
 
   auto G = getYAMLGenerator();
   assert(G);
@@ -286,9 +290,42 @@
 Location:
   - LineNumber:      12
     Filename:        'test.cpp'
+Members:
+  - Name:            'X'
+    Value:           '0'
+...
+)raw";
+  EXPECT_EQ(Expected, Actual.str());
+}
+
+// Tests the equivalent of:
+// enum class e : short { X = FOO_BAR + 2 };
+TEST(YAMLGeneratorTest, enumTypedScopedEnumYAML) {
+  EnumInfo I;
+  I.Name = "e";
+
+  I.Members.emplace_back("X", llvm::APSInt::get(-9876), "FOO_BAR + 2");
+  I.Scoped = true;
+  I.BaseType = TypeInfo("short");
+
+  auto G = getYAMLGenerator();
+  assert(G);
+  std::string Buffer;
+  llvm::raw_string_ostream Actual(Buffer);
+  auto Err = G->generateDocForInfo(&I, Actual, ClangDocContext());
+  assert(!Err);
+  std::string Expected =
+      R"raw(---
+USR:             '0000000000000000000000000000000000000000'
+Name:            'e'
 Scoped:          true
+BaseType:
+  Type:
+    Name:            'short'
 Members:
-  - 'X'
+  - Name:            'X'
+    Value:           '-9876'
+    Expr:            'FOO_BAR + 2'
 ...
 )raw";
   EXPECT_EQ(Expected, Actual.str());
Index: clang-tools-extra/unittests/clang-doc/SerializeTest.cpp
===================================================================
--- clang-tools-extra/unittests/clang-doc/SerializeTest.cpp
+++ clang-tools-extra/unittests/clang-doc/SerializeTest.cpp
@@ -266,8 +266,8 @@
   EnumInfo E;
   E.Name = "E";
   E.DefLoc = Location(0, llvm::SmallString<16>{"test.cpp"});
-  E.Members.emplace_back("X");
-  E.Members.emplace_back("Y");
+  E.Members.emplace_back("X", llvm::APSInt::get(0));
+  E.Members.emplace_back("Y", llvm::APSInt::get(1));
   ExpectedNamespaceWithEnum.ChildEnums.emplace_back(std::move(E));
   CheckNamespaceInfo(&ExpectedNamespaceWithEnum, NamespaceWithEnum);
 
@@ -277,8 +277,8 @@
   G.Name = "G";
   G.Scoped = true;
   G.DefLoc = Location(0, llvm::SmallString<16>{"test.cpp"});
-  G.Members.emplace_back("A");
-  G.Members.emplace_back("B");
+  G.Members.emplace_back("A", llvm::APSInt::get(0));
+  G.Members.emplace_back("B", llvm::APSInt::get(1));
   ExpectedNamespaceWithScopedEnum.ChildEnums.emplace_back(std::move(G));
   CheckNamespaceInfo(&ExpectedNamespaceWithScopedEnum, NamespaceWithScopedEnum);
 }
Index: clang-tools-extra/clang-doc/YAMLGenerator.cpp
===================================================================
--- clang-tools-extra/clang-doc/YAMLGenerator.cpp
+++ clang-tools-extra/clang-doc/YAMLGenerator.cpp
@@ -14,6 +14,7 @@
 
 using namespace clang::doc;
 
+// These define YAML traits for decoding the listed values within a vector.
 LLVM_YAML_IS_SEQUENCE_VECTOR(FieldTypeInfo)
 LLVM_YAML_IS_SEQUENCE_VECTOR(MemberTypeInfo)
 LLVM_YAML_IS_SEQUENCE_VECTOR(Reference)
@@ -21,6 +22,7 @@
 LLVM_YAML_IS_SEQUENCE_VECTOR(CommentInfo)
 LLVM_YAML_IS_SEQUENCE_VECTOR(FunctionInfo)
 LLVM_YAML_IS_SEQUENCE_VECTOR(EnumInfo)
+LLVM_YAML_IS_SEQUENCE_VECTOR(EnumValueInfo)
 LLVM_YAML_IS_SEQUENCE_VECTOR(BaseRecordInfo)
 LLVM_YAML_IS_SEQUENCE_VECTOR(std::unique_ptr<CommentInfo>)
 LLVM_YAML_IS_SEQUENCE_VECTOR(llvm::SmallString<16>)
@@ -226,10 +228,22 @@
   }
 };
 
+template <> struct MappingTraits<EnumValueInfo> {
+  static void mapping(IO &IO, EnumValueInfo &I) {
+    IO.mapOptional("Name", I.Name);
+
+    SmallString<16> ValueStr;
+    I.Value.toString(ValueStr);
+    IO.mapOptional("Value", ValueStr);
+    IO.mapOptional("Expr", I.ValueExpr, SmallString<16>());
+  }
+};
+
 template <> struct MappingTraits<EnumInfo> {
   static void mapping(IO &IO, EnumInfo &I) {
     SymbolInfoMapping(IO, I);
     IO.mapOptional("Scoped", I.Scoped, false);
+    IO.mapOptional("BaseType", I.BaseType);
     IO.mapOptional("Members", I.Members);
   }
 };
Index: clang-tools-extra/clang-doc/Serialize.cpp
===================================================================
--- clang-tools-extra/clang-doc/Serialize.cpp
+++ clang-tools-extra/clang-doc/Serialize.cpp
@@ -182,6 +182,13 @@
 
 // Serializing functions.
 
+SmallString<128> getSourceCode(const Decl* D, const SourceRange& R) {
+  return Lexer::getSourceText(
+      CharSourceRange::getTokenRange(R),
+      D->getASTContext().getSourceManager(),
+      D->getASTContext().getLangOpts());
+}
+
 template <typename T> static std::string serialize(T &I) {
   SmallString<2048> Buffer;
   llvm::BitstreamWriter Stream(Buffer);
@@ -305,8 +312,12 @@
 }
 
 static void parseEnumerators(EnumInfo &I, const EnumDecl *D) {
-  for (const EnumConstantDecl *E : D->enumerators())
-    I.Members.emplace_back(E->getNameAsString());
+  for (const EnumConstantDecl *E : D->enumerators()) {
+    SmallString<128> ValueExpr;
+    if (const Expr* InitExpr = E->getInitExpr())
+      ValueExpr = getSourceCode(D, InitExpr->getSourceRange());
+    I.Members.emplace_back(E->getNameAsString(), E->getInitVal(), ValueExpr);
+  }
 }
 
 static void parseParameters(FunctionInfo &I, const FunctionDecl *D) {
@@ -331,10 +342,7 @@
     }
 
     if (const Expr *DefaultArg = P->getDefaultArg()) {
-      FieldInfo->DefaultValue = Lexer::getSourceText(
-          CharSourceRange::getTokenRange(DefaultArg->getSourceRange()),
-          D->getASTContext().getSourceManager(),
-          D->getASTContext().getLangOpts());
+      FieldInfo->DefaultValue = getSourceCode(D, DefaultArg->getSourceRange());
     }
   }
 }
@@ -657,6 +665,8 @@
     return {};
 
   Enum.Scoped = D->isScoped();
+  if (D->isFixed())
+    Enum.BaseType = TypeInfo(D->getIntegerType().getAsString());
   parseEnumerators(Enum, D);
 
   // Put in global namespace
Index: clang-tools-extra/clang-doc/Representation.h
===================================================================
--- clang-tools-extra/clang-doc/Representation.h
+++ clang-tools-extra/clang-doc/Representation.h
@@ -17,6 +17,7 @@
 #include "clang/AST/Type.h"
 #include "clang/Basic/Specifiers.h"
 #include "clang/Tooling/StandaloneExecution.h"
+#include "llvm/ADT/APSInt.h"
 #include "llvm/ADT/Optional.h"
 #include "llvm/ADT/SmallVector.h"
 #include "llvm/ADT/StringExtras.h"
@@ -397,6 +398,33 @@
   bool IsParent = false; // Indicates if this base is a direct parent
 };
 
+// Information for a single possible value of an enumeration.
+struct EnumValueInfo {
+  explicit EnumValueInfo(StringRef Name = StringRef(),
+                         llvm::APSInt Value = llvm::APSInt(),
+                         StringRef ValueExpr = StringRef())
+      : Name(Name), Value(Value), ValueExpr(ValueExpr) {}
+
+  bool operator==(const EnumValueInfo &Other) const {
+    // Be permissive in comparison for tests so compare the values rather than
+    // their exact representation (which is what operator== does).
+    if (llvm::APSInt::compareValues(Value, Other.Value) != 0)
+      return false;
+    return std::tie(Name, ValueExpr) == std::tie(Other.Name, Other.ValueExpr);
+  }
+
+  SmallString<16> Name;
+
+  // The computed value of the enumeration constant. This could be the result of
+  // evaluating the ValueExpr, or it could be automatically generated according
+  // to C rules.
+  llvm::APSInt Value;
+
+  // Stores the user-supplied initialization expression for this enumeration
+  // constant. This will be empty for implicit enumeration values.
+  SmallString<16> ValueExpr;
+};
+
 // TODO: Expand to allow for documenting templating.
 // Info for types.
 struct EnumInfo : public SymbolInfo {
@@ -405,9 +433,15 @@
 
   void merge(EnumInfo &&I);
 
-  bool Scoped =
-      false; // Indicates whether this enum is scoped (e.g. enum class).
-  llvm::SmallVector<SmallString<16>, 4> Members; // List of enum members.
+  // Indicates whether this enum is scoped (e.g. enum class).
+  bool Scoped = false;
+
+  // Set to nonempty to the type when this is an explicitly typed enum. For
+  //   enum Foo : short { ... };
+  // this will be "short".
+  llvm::Optional<TypeInfo> BaseType;
+
+  llvm::SmallVector<EnumValueInfo, 4> Members; // List of enum members.
 };
 
 struct Index : public Reference {
Index: clang-tools-extra/clang-doc/MDGenerator.cpp
===================================================================
--- clang-tools-extra/clang-doc/MDGenerator.cpp
+++ clang-tools-extra/clang-doc/MDGenerator.cpp
@@ -136,7 +136,7 @@
   llvm::raw_string_ostream Members(Buffer);
   if (!I.Members.empty())
     for (const auto &N : I.Members)
-      Members << "| " << N << " |\n";
+      Members << "| " << N.Name << " |\n";
   writeLine(Members.str(), OS);
   if (I.DefLoc)
     writeFileDefinition(CDCtx, *I.DefLoc, OS);
Index: clang-tools-extra/clang-doc/HTMLGenerator.cpp
===================================================================
--- clang-tools-extra/clang-doc/HTMLGenerator.cpp
+++ clang-tools-extra/clang-doc/HTMLGenerator.cpp
@@ -361,13 +361,13 @@
 }
 
 static std::unique_ptr<TagNode>
-genEnumMembersBlock(const llvm::SmallVector<SmallString<16>, 4> &Members) {
+genEnumMembersBlock(const llvm::SmallVector<EnumValueInfo, 4> &Members) {
   if (Members.empty())
     return nullptr;
 
   auto List = std::make_unique<TagNode>(HTMLTag::TAG_UL);
   for (const auto &M : Members)
-    List->Children.emplace_back(std::make_unique<TagNode>(HTMLTag::TAG_LI, M));
+    List->Children.emplace_back(std::make_unique<TagNode>(HTMLTag::TAG_LI, M.Name));
   return List;
 }
 
Index: clang-tools-extra/clang-doc/BitcodeWriter.h
===================================================================
--- clang-tools-extra/clang-doc/BitcodeWriter.h
+++ clang-tools-extra/clang-doc/BitcodeWriter.h
@@ -17,6 +17,7 @@
 
 #include "Representation.h"
 #include "clang/AST/AST.h"
+#include "llvm/ADT/APSInt.h"
 #include "llvm/ADT/DenseMap.h"
 #include "llvm/ADT/SmallVector.h"
 #include "llvm/ADT/StringRef.h"
@@ -54,6 +55,7 @@
   BI_VERSION_BLOCK_ID = llvm::bitc::FIRST_APPLICATION_BLOCKID,
   BI_NAMESPACE_BLOCK_ID,
   BI_ENUM_BLOCK_ID,
+  BI_ENUM_VALUE_BLOCK_ID,
   BI_TYPE_BLOCK_ID,
   BI_FIELD_TYPE_BLOCK_ID,
   BI_MEMBER_TYPE_BLOCK_ID,
@@ -100,6 +102,9 @@
   ENUM_LOCATION,
   ENUM_MEMBER,
   ENUM_SCOPED,
+  ENUM_VALUE_NAME,
+  ENUM_VALUE_VALUE,
+  ENUM_VALUE_EXPR,
   RECORD_USR,
   RECORD_NAME,
   RECORD_PATH,
@@ -155,6 +160,7 @@
   void emitBlock(const BaseRecordInfo &I);
   void emitBlock(const FunctionInfo &I);
   void emitBlock(const EnumInfo &I);
+  void emitBlock(const EnumValueInfo &I);
   void emitBlock(const TypeInfo &B);
   void emitBlock(const FieldTypeInfo &B);
   void emitBlock(const MemberTypeInfo &B);
@@ -205,6 +211,7 @@
   void emitRecord(bool Value, RecordId ID);
   void emitRecord(int Value, RecordId ID);
   void emitRecord(unsigned Value, RecordId ID);
+  void emitRecord(llvm::APSInt Value, RecordId ID);
   bool prepRecordData(RecordId ID, bool ShouldEmit = true);
 
   // Emission of appropriate abbreviation type.
Index: clang-tools-extra/clang-doc/BitcodeWriter.cpp
===================================================================
--- clang-tools-extra/clang-doc/BitcodeWriter.cpp
+++ clang-tools-extra/clang-doc/BitcodeWriter.cpp
@@ -36,6 +36,18 @@
     Abbrev->Add(Op);
 }
 
+static void ApsIntAbbrev(std::shared_ptr<llvm::BitCodeAbbrev> &Abbrev) {
+  AbbrevGen(Abbrev,
+            {// 0. Fixed-size integer (length of the following bytes)
+             llvm::BitCodeAbbrevOp(llvm::BitCodeAbbrevOp::Fixed,
+                                   BitCodeConstants::StringLengthSize),
+             // 1. Type unsigned flag.
+             llvm::BitCodeAbbrevOp(llvm::BitCodeAbbrevOp::Fixed,
+                                   BitCodeConstants::BoolSize),
+             // 2. The APSInt value blob.
+             llvm::BitCodeAbbrevOp(llvm::BitCodeAbbrevOp::Blob)});
+}
+
 static void BoolAbbrev(std::shared_ptr<llvm::BitCodeAbbrev> &Abbrev) {
   AbbrevGen(Abbrev,
             {// 0. Boolean
@@ -112,6 +124,7 @@
           {BI_VERSION_BLOCK_ID, "VersionBlock"},
           {BI_NAMESPACE_BLOCK_ID, "NamespaceBlock"},
           {BI_ENUM_BLOCK_ID, "EnumBlock"},
+          {BI_ENUM_VALUE_BLOCK_ID, "EnumValueBlock"},
           {BI_TYPE_BLOCK_ID, "TypeBlock"},
           {BI_FIELD_TYPE_BLOCK_ID, "FieldTypeBlock"},
           {BI_MEMBER_TYPE_BLOCK_ID, "MemberTypeBlock"},
@@ -160,6 +173,9 @@
           {ENUM_LOCATION, {"Location", &LocationAbbrev}},
           {ENUM_MEMBER, {"Member", &StringAbbrev}},
           {ENUM_SCOPED, {"Scoped", &BoolAbbrev}},
+          {ENUM_VALUE_NAME, {"Name", &StringAbbrev}},
+          {ENUM_VALUE_VALUE, {"Value", &ApsIntAbbrev}},
+          {ENUM_VALUE_EXPR, {"Expr", &StringAbbrev}},
           {RECORD_USR, {"USR", &SymbolIDAbbrev}},
           {RECORD_NAME, {"Name", &StringAbbrev}},
           {RECORD_PATH, {"Path", &StringAbbrev}},
@@ -215,6 +231,9 @@
         {BI_ENUM_BLOCK_ID,
          {ENUM_USR, ENUM_NAME, ENUM_DEFLOCATION, ENUM_LOCATION, ENUM_MEMBER,
           ENUM_SCOPED}},
+        // Enum Value Block
+        {BI_ENUM_VALUE_BLOCK_ID,
+         {ENUM_VALUE_NAME, ENUM_VALUE_VALUE, ENUM_VALUE_EXPR}},
         // Namespace Block
         {BI_NAMESPACE_BLOCK_ID,
          {NAMESPACE_USR, NAMESPACE_NAME, NAMESPACE_PATH}},
@@ -367,6 +386,19 @@
   Stream.EmitRecordWithAbbrev(Abbrevs.get(ID), Record);
 }
 
+void ClangDocBitcodeWriter::emitRecord(llvm::APSInt Value, RecordId ID) {
+  assert(RecordIdNameMap[ID] && "Unknown RecordId.");
+  assert(RecordIdNameMap[ID].Abbrev == &ApsIntAbbrev && "Abbrev type mismatch.");
+  if (!prepRecordData(ID, true))
+    return;
+
+  auto ByteSize = Value.getNumWords() * llvm::APSInt::APINT_WORD_SIZE;
+  assert(ByteSize < (1U << BitCodeConstants::StringLengthSize));
+  Record.push_back(ByteSize);
+  Record.push_back(Value.isUnsigned());
+  Stream.EmitRecordWithBlob(Abbrevs.get(ID), Record, reinterpret_cast<const char*>(Value.getRawData()), ByteSize);
+}
+
 bool ClangDocBitcodeWriter::prepRecordData(RecordId ID, bool ShouldEmit) {
   assert(RecordIdNameMap[ID] && "Unknown RecordId.");
   if (!ShouldEmit)
@@ -486,8 +518,17 @@
   for (const auto &L : I.Loc)
     emitRecord(L, ENUM_LOCATION);
   emitRecord(I.Scoped, ENUM_SCOPED);
+  if (I.BaseType)
+    emitBlock(*I.BaseType);
   for (const auto &N : I.Members)
-    emitRecord(N, ENUM_MEMBER);
+    emitBlock(N);
+}
+
+void ClangDocBitcodeWriter::emitBlock(const EnumValueInfo &I) {
+  StreamSubBlockGuard Block(Stream, BI_ENUM_VALUE_BLOCK_ID);
+  emitRecord(I.Name, ENUM_VALUE_NAME);
+  emitRecord(I.Value, ENUM_VALUE_VALUE);
+  emitRecord(I.ValueExpr, ENUM_VALUE_EXPR);
 }
 
 void ClangDocBitcodeWriter::emitBlock(const RecordInfo &I) {
Index: clang-tools-extra/clang-doc/BitcodeReader.cpp
===================================================================
--- clang-tools-extra/clang-doc/BitcodeReader.cpp
+++ clang-tools-extra/clang-doc/BitcodeReader.cpp
@@ -17,6 +17,7 @@
 
 using Record = llvm::SmallVector<uint64_t, 1024>;
 
+// This implements decode for strings.
 llvm::Error decodeRecord(const Record &R, llvm::SmallVectorImpl<char> &Field,
                          llvm::StringRef Blob) {
   Field.assign(Blob.begin(), Blob.end());
@@ -49,6 +50,27 @@
   return llvm::Error::success();
 }
 
+llvm::Error decodeRecord(const Record &R, llvm::APSInt &Field, llvm::StringRef Blob) {
+  auto ByteWidth = R[0];
+  // The writer only ever writes out the number of bytes in the whole words stored by APSInt.
+  assert(ByteWidth % llvm::APSInt::APINT_WORD_SIZE == 0);
+  assert(Blob.size() == ByteWidth);
+
+  auto WordWidth = ByteWidth / llvm::APSInt::APINT_WORD_SIZE;
+  assert(WordWidth > 0);
+  auto BitWidth = ByteWidth * 8;
+
+  auto IsUnsigned = R[1];
+
+  llvm::SmallVector<uint64_t, 1024> AsWords;
+  AsWords.resize(WordWidth);
+  memcpy(AsWords.data(), Blob.data(), ByteWidth);
+
+  llvm::APInt IntValue(static_cast<unsigned>(BitWidth), makeArrayRef(AsWords));
+  Field = llvm::APSInt(IntValue, IsUnsigned);
+  return llvm::Error::success();
+}
+
 llvm::Error decodeRecord(const Record &R, AccessSpecifier &Field,
                          llvm::StringRef Blob) {
   switch (R[0]) {
@@ -218,8 +240,6 @@
     return decodeRecord(R, I->DefLoc, Blob);
   case ENUM_LOCATION:
     return decodeRecord(R, I->Loc, Blob);
-  case ENUM_MEMBER:
-    return decodeRecord(R, I->Members, Blob);
   case ENUM_SCOPED:
     return decodeRecord(R, I->Scoped, Blob);
   default:
@@ -228,6 +248,21 @@
   }
 }
 
+llvm::Error parseRecord(const Record &R, unsigned ID, llvm::StringRef Blob,
+                        EnumValueInfo *I) {
+  switch (ID) {
+  case ENUM_VALUE_NAME:
+    return decodeRecord(R, I->Name, Blob);
+  case ENUM_VALUE_VALUE:
+    return decodeRecord(R, I->Value, Blob);
+  case ENUM_VALUE_EXPR:
+    return decodeRecord(R, I->ValueExpr, Blob);
+  default:
+    return llvm::createStringError(llvm::inconvertibleErrorCode(),
+                                   "invalid field for EnumValueInfo");
+  }
+}
+
 llvm::Error parseRecord(const Record &R, unsigned ID, llvm::StringRef Blob,
                         FunctionInfo *I) {
   switch (ID) {
@@ -372,6 +407,9 @@
   return getCommentInfo(I.get());
 }
 
+// When readSubBlock encounters a TypeInfo sub-block, it calls addTypeInfo on
+// the parent block to set it. The template specializations define what to do
+// for each supported parent block.
 template <typename T, typename TTypeInfo>
 llvm::Error addTypeInfo(T I, TTypeInfo &&TI) {
   return llvm::createStringError(llvm::inconvertibleErrorCode(),
@@ -398,6 +436,11 @@
   return llvm::Error::success();
 }
 
+template <> llvm::Error addTypeInfo(EnumInfo *I, TypeInfo &&T) {
+  I->BaseType = std::move(T);
+  return llvm::Error::success();
+}
+
 template <typename T> llvm::Error addReference(T I, Reference &&R, FieldId F) {
   return llvm::createStringError(llvm::inconvertibleErrorCode(),
                                  "invalid type cannot contain Reference");
@@ -524,6 +567,10 @@
   I->ChildEnums.emplace_back(std::move(R));
 }
 
+template <> void addChild(EnumInfo *I, EnumValueInfo &&R) {
+  I->Members.emplace_back(std::move(R));
+}
+
 template <> void addChild(RecordInfo *I, BaseRecordInfo &&R) {
   I->Bases.emplace_back(std::move(R));
 }
@@ -587,8 +634,7 @@
 template <typename T>
 llvm::Error ClangDocBitcodeReader::readSubBlock(unsigned ID, T I) {
   switch (ID) {
-  // Blocks can only have Comment, Reference, TypeInfo, FunctionInfo, or
-  // EnumInfo subblocks
+  // Blocks can only have certain types of sub blocks.
   case BI_COMMENT_BLOCK_ID: {
     auto Comment = getCommentInfo(I);
     if (!Comment)
@@ -650,6 +696,13 @@
     addChild(I, std::move(E));
     return llvm::Error::success();
   }
+  case BI_ENUM_VALUE_BLOCK_ID: {
+    EnumValueInfo EV;
+    if (auto Err = readBlock(ID, &EV))
+      return Err;
+    addChild(I, std::move(EV));
+    return llvm::Error::success();
+  }
   default:
     return llvm::createStringError(llvm::inconvertibleErrorCode(),
                                    "invalid subblock type");
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
  • [PATCH] D134055: [clang-doc] ... Brett Wilson via Phabricator via cfe-commits

Reply via email to