diff --git a/clang-tools-extra/clang-doc/BitcodeReader.cpp b/clang-tools-extra/clang-doc/BitcodeReader.cpp --- a/clang-tools-extra/clang-doc/BitcodeReader.cpp +++ b/clang-tools-extra/clang-doc/BitcodeReader.cpp @@ -17,12 +17,19 @@ using Record = llvm::SmallVector; +// This implements decode for SmallString. llvm::Error decodeRecord(const Record &R, llvm::SmallVectorImpl &Field, llvm::StringRef Blob) { Field.assign(Blob.begin(), Blob.end()); return llvm::Error::success(); } +llvm::Error decodeRecord(const Record &R, std::string &Field, + llvm::StringRef Blob) { + Field.assign(Blob.begin(), Blob.end()); + return llvm::Error::success(); +} + llvm::Error decodeRecord(const Record &R, SymbolID &Field, llvm::StringRef Blob) { if (R[0] != BitCodeConstants::USRHashSize) @@ -218,8 +225,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 +233,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 +392,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 llvm::Error addTypeInfo(T I, TTypeInfo &&TI) { return llvm::createStringError(llvm::inconvertibleErrorCode(), @@ -398,6 +421,11 @@ return llvm::Error::success(); } +template <> llvm::Error addTypeInfo(EnumInfo *I, TypeInfo &&T) { + I->BaseType = std::move(T); + return llvm::Error::success(); +} + template llvm::Error addReference(T I, Reference &&R, FieldId F) { return llvm::createStringError(llvm::inconvertibleErrorCode(), "invalid type cannot contain Reference"); @@ -524,6 +552,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 +619,7 @@ template 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 +681,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"); diff --git a/clang-tools-extra/clang-doc/BitcodeWriter.h b/clang-tools-extra/clang-doc/BitcodeWriter.h --- a/clang-tools-extra/clang-doc/BitcodeWriter.h +++ b/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, @@ -98,8 +100,10 @@ ENUM_NAME, ENUM_DEFLOCATION, ENUM_LOCATION, - ENUM_MEMBER, ENUM_SCOPED, + ENUM_VALUE_NAME, + ENUM_VALUE_VALUE, + ENUM_VALUE_EXPR, RECORD_USR, RECORD_NAME, RECORD_PATH, @@ -155,6 +159,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 +210,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. diff --git a/clang-tools-extra/clang-doc/BitcodeWriter.cpp b/clang-tools-extra/clang-doc/BitcodeWriter.cpp --- a/clang-tools-extra/clang-doc/BitcodeWriter.cpp +++ b/clang-tools-extra/clang-doc/BitcodeWriter.cpp @@ -112,6 +112,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"}, @@ -158,8 +159,10 @@ {ENUM_NAME, {"Name", &StringAbbrev}}, {ENUM_DEFLOCATION, {"DefLocation", &LocationAbbrev}}, {ENUM_LOCATION, {"Location", &LocationAbbrev}}, - {ENUM_MEMBER, {"Member", &StringAbbrev}}, {ENUM_SCOPED, {"Scoped", &BoolAbbrev}}, + {ENUM_VALUE_NAME, {"Name", &StringAbbrev}}, + {ENUM_VALUE_VALUE, {"Value", &StringAbbrev}}, + {ENUM_VALUE_EXPR, {"Expr", &StringAbbrev}}, {RECORD_USR, {"USR", &SymbolIDAbbrev}}, {RECORD_NAME, {"Name", &StringAbbrev}}, {RECORD_PATH, {"Path", &StringAbbrev}}, @@ -213,8 +216,10 @@ {BI_MEMBER_TYPE_BLOCK_ID, {MEMBER_TYPE_NAME, MEMBER_TYPE_ACCESS}}, // Enum Block {BI_ENUM_BLOCK_ID, - {ENUM_USR, ENUM_NAME, ENUM_DEFLOCATION, ENUM_LOCATION, ENUM_MEMBER, - ENUM_SCOPED}}, + {ENUM_USR, ENUM_NAME, ENUM_DEFLOCATION, ENUM_LOCATION, 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}}, @@ -486,8 +491,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) { diff --git a/clang-tools-extra/clang-doc/HTMLGenerator.cpp b/clang-tools-extra/clang-doc/HTMLGenerator.cpp --- a/clang-tools-extra/clang-doc/HTMLGenerator.cpp +++ b/clang-tools-extra/clang-doc/HTMLGenerator.cpp @@ -361,13 +361,14 @@ } static std::unique_ptr -genEnumMembersBlock(const llvm::SmallVector, 4> &Members) { +genEnumMembersBlock(const llvm::SmallVector &Members) { if (Members.empty()) return nullptr; auto List = std::make_unique(HTMLTag::TAG_UL); for (const auto &M : Members) - List->Children.emplace_back(std::make_unique(HTMLTag::TAG_LI, M)); + List->Children.emplace_back( + std::make_unique(HTMLTag::TAG_LI, M.Name)); return List; } diff --git a/clang-tools-extra/clang-doc/MDGenerator.cpp b/clang-tools-extra/clang-doc/MDGenerator.cpp --- a/clang-tools-extra/clang-doc/MDGenerator.cpp +++ b/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); diff --git a/clang-tools-extra/clang-doc/Representation.h b/clang-tools-extra/clang-doc/Representation.h --- a/clang-tools-extra/clang-doc/Representation.h +++ b/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,30 @@ 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(), + StringRef Value = StringRef("0"), + StringRef ValueExpr = StringRef()) + : Name(Name), Value(Value), ValueExpr(ValueExpr) {} + + bool operator==(const EnumValueInfo &Other) const { + return std::tie(Name, Value, ValueExpr) == + std::tie(Other.Name, Other.Value, 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. + SmallString<16> 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 +430,15 @@ void merge(EnumInfo &&I); - bool Scoped = - false; // Indicates whether this enum is scoped (e.g. enum class). - llvm::SmallVector, 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 BaseType; + + llvm::SmallVector Members; // List of enum members. }; struct Index : public Reference { diff --git a/clang-tools-extra/clang-doc/Serialize.cpp b/clang-tools-extra/clang-doc/Serialize.cpp --- a/clang-tools-extra/clang-doc/Serialize.cpp +++ b/clang-tools-extra/clang-doc/Serialize.cpp @@ -182,6 +182,13 @@ // Serializing functions. +std::string getSourceCode(const Decl *D, const SourceRange &R) { + return Lexer::getSourceText(CharSourceRange::getTokenRange(R), + D->getASTContext().getSourceManager(), + D->getASTContext().getLangOpts()) + .str(); +} + template static std::string serialize(T &I) { SmallString<2048> Buffer; llvm::BitstreamWriter Stream(Buffer); @@ -305,8 +312,15 @@ } 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()) { + std::string ValueExpr; + if (const Expr *InitExpr = E->getInitExpr()) + ValueExpr = getSourceCode(D, InitExpr->getSourceRange()); + + SmallString<16> ValueStr; + E->getInitVal().toString(ValueStr); + I.Members.emplace_back(E->getNameAsString(), ValueStr, ValueExpr); + } } static void parseParameters(FunctionInfo &I, const FunctionDecl *D) { @@ -331,10 +345,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 +668,8 @@ return {}; Enum.Scoped = D->isScoped(); + if (D->isFixed()) + Enum.BaseType = TypeInfo(D->getIntegerType().getAsString()); parseEnumerators(Enum, D); // Put in global namespace diff --git a/clang-tools-extra/clang-doc/YAMLGenerator.cpp b/clang-tools-extra/clang-doc/YAMLGenerator.cpp --- a/clang-tools-extra/clang-doc/YAMLGenerator.cpp +++ b/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) LLVM_YAML_IS_SEQUENCE_VECTOR(llvm::SmallString<16>) @@ -226,10 +228,19 @@ } }; +template <> struct MappingTraits { + static void mapping(IO &IO, EnumValueInfo &I) { + IO.mapOptional("Name", I.Name); + IO.mapOptional("Value", I.Value); + IO.mapOptional("Expr", I.ValueExpr, SmallString<16>()); + } +}; + template <> struct MappingTraits { 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); } }; diff --git a/clang-tools-extra/unittests/clang-doc/SerializeTest.cpp b/clang-tools-extra/unittests/clang-doc/SerializeTest.cpp --- a/clang-tools-extra/unittests/clang-doc/SerializeTest.cpp +++ b/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", "0"); + E.Members.emplace_back("Y", "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", "0"); + G.Members.emplace_back("B", "1"); ExpectedNamespaceWithScopedEnum.ChildEnums.emplace_back(std::move(G)); CheckNamespaceInfo(&ExpectedNamespaceWithScopedEnum, NamespaceWithScopedEnum); } diff --git a/clang-tools-extra/unittests/clang-doc/YAMLGeneratorTest.cpp b/clang-tools-extra/unittests/clang-doc/YAMLGeneratorTest.cpp --- a/clang-tools-extra/unittests/clang-doc/YAMLGeneratorTest.cpp +++ b/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", "-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());