Paul Kirth 1deab59f6f
[clang-doc] Migrate Namespaces to arena allocation (#190048)
This patch allocates the NamespaceInfo types in the local arenas, and
adapts the merging logic for the new list type and its children.
Memory use and performance improve slightly. Micro-benchmarks show a
regression in merge operations due to the more complex list operations.

 ## Build Clang-Doc Documentation
| Metric | Baseline | Prev | This | Culm% | Seq% |
| :--- | :--- | :--- | :--- | :--- | :--- |
| Time | 920.5s | 1009.2s | 1002.4s | +8.9% | -0.7% |
| Memory | 86.0G | 43.2G | 43.9G | -49.0% | +1.6% |

 ## Microbenchmarks (Filtered for >1% Delta)
| Benchmark | Baseline | Prev | This | Culm% | Seq% |
| :--- | :--- | :--- | :--- | :--- | :--- |
| BM_BitcodeReader_Scale/10 | 67.9us | 69.7us | 69.3us | +1.9% | -0.7% |
| BM_BitcodeReader_Scale/10000 | 70.5ms | 22.3ms | 24.8ms | -64.8% |
+11.4% |
| BM_BitcodeReader_Scale/4096 | 23.2ms | 4.7ms | 4.4ms | -80.9% | -5.7%
|
| BM_BitcodeReader_Scale/512 | 509.4us | 558.7us | 540.2us | +6.0% |
-3.3% |
| BM_BitcodeReader_Scale/64 | 114.8us | 119.2us | 117.0us | +1.9% |
-1.8% |
| BM_EmitInfoFunction | 1.6us | 1.6us | 1.6us | -1.1% | +0.8% |
| BM_Index_Insertion/10 | 2.3us | 4.2us | 3.7us | +61.7% | -11.3% |
| BM_Index_Insertion/10000 | 3.1ms | 5.4ms | 4.9ms | +56.8% | -9.1% |
| BM_Index_Insertion/4096 | 1.3ms | 2.2ms | 2.0ms | +54.5% | -8.2% |
| BM_Index_Insertion/512 | 153.6us | 259.6us | 236.7us | +54.1% | -8.8%
|
| BM_Index_Insertion/64 | 18.1us | 30.7us | 27.9us | +54.5% | -9.0% |
| BM_JSONGenerator_Scale/10 | 36.8us | 37.6us | 36.6us | -0.7% | -2.7% |
| BM_JSONGenerator_Scale/4096 | 33.7ms | 34.3ms | 34.2ms | +1.4% | -0.3%
|
| BM_JSONGenerator_Scale/64 | 222.4us | 225.6us | 220.2us | -1.0% |
-2.4% |
| BM_Mapper_Scale/10 | 2.5ms | 2.5ms | 2.5ms | -1.4% | -1.7% |
| BM_Mapper_Scale/10000 | 104.3ms | 109.3ms | 105.9ms | +1.6% | -3.1% |
| BM_Mapper_Scale/4096 | 44.3ms | 43.9ms | 44.7ms | +0.8% | +1.8% |
| BM_Mapper_Scale/512 | 7.6ms | 7.6ms | 7.6ms | +0.8% | +1.2% |
| BM_MergeInfos_Scale/10000 | 12.2ms | 1.6ms | 2.0ms | -83.6% | +28.7% |
| BM_MergeInfos_Scale/2 | 1.9us | 1.7us | 1.7us | -8.1% | +0.9% |
| BM_MergeInfos_Scale/4096 | 2.8ms | 520.5us | 557.4us | -79.9% | +7.1%
|
| BM_MergeInfos_Scale/512 | 68.9us | 37.9us | 39.9us | -42.0% | +5.3% |
| BM_MergeInfos_Scale/64 | 10.3us | 6.3us | 6.5us | -36.7% | +2.7% |
| BM_MergeInfos_Scale/8 | 2.8us | 2.2us | 2.2us | -20.7% | +1.2% |
| BM_SerializeFunctionInfo | 25.5us | 25.8us | 26.1us | +2.1% | +1.1% |
2026-04-03 20:33:56 +00:00

1281 lines
43 KiB
C++

//===-- BitcodeReader.cpp - ClangDoc Bitcode Reader ------------*- C++ -*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
#include "BitcodeReader.h"
#include "llvm/Support/Error.h"
#include "llvm/Support/ErrorHandling.h"
#include "llvm/Support/TimeProfiler.h"
#include "llvm/Support/raw_ostream.h"
#include <optional>
namespace clang {
namespace doc {
static llvm::ExitOnError ExitOnErr("clang-doc error: ");
using Record = llvm::SmallVector<uint64_t, 1024>;
// This implements decode for SmallString.
static llvm::Error decodeRecord(const Record &R,
llvm::SmallVectorImpl<char> &Field,
llvm::StringRef Blob) {
Field.assign(Blob.begin(), Blob.end());
return llvm::Error::success();
}
static llvm::Error decodeRecord(const Record &R, llvm::StringRef &Field,
llvm::StringRef Blob) {
Field = internString(Blob);
return llvm::Error::success();
}
static llvm::Error decodeRecord(const Record &R, SymbolID &Field,
llvm::StringRef Blob) {
if (R[0] != BitCodeConstants::USRHashSize)
return llvm::createStringError(llvm::inconvertibleErrorCode(),
"incorrect USR size");
// First position in the record is the length of the following array, so we
// copy the following elements to the field.
for (int I = 0, E = R[0]; I < E; ++I)
Field[I] = R[I + 1];
return llvm::Error::success();
}
static llvm::Error decodeRecord(const Record &R, bool &Field,
llvm::StringRef Blob) {
Field = R[0] != 0;
return llvm::Error::success();
}
static llvm::Error decodeRecord(const Record &R, AccessSpecifier &Field,
llvm::StringRef Blob) {
switch (R[0]) {
case AS_public:
case AS_private:
case AS_protected:
case AS_none:
Field = (AccessSpecifier)R[0];
return llvm::Error::success();
}
llvm_unreachable("invalid value for AccessSpecifier");
}
static llvm::Error decodeRecord(const Record &R, TagTypeKind &Field,
llvm::StringRef Blob) {
switch (static_cast<TagTypeKind>(R[0])) {
case TagTypeKind::Struct:
case TagTypeKind::Interface:
case TagTypeKind::Union:
case TagTypeKind::Class:
case TagTypeKind::Enum:
Field = static_cast<TagTypeKind>(R[0]);
return llvm::Error::success();
}
return llvm::createStringError(llvm::inconvertibleErrorCode(),
"invalid value for TagTypeKind");
}
static llvm::Error decodeRecord(const Record &R, std::optional<Location> &Field,
llvm::StringRef Blob) {
if (R[0] > INT_MAX)
return llvm::createStringError(llvm::inconvertibleErrorCode(),
"integer too large to parse");
Field.emplace(static_cast<int>(R[0]), static_cast<int>(R[1]), Blob,
static_cast<bool>(R[2]));
return llvm::Error::success();
}
static llvm::Error decodeRecord(const Record &R, InfoType &Field,
llvm::StringRef Blob) {
switch (auto IT = static_cast<InfoType>(R[0])) {
case InfoType::IT_namespace:
case InfoType::IT_record:
case InfoType::IT_function:
case InfoType::IT_default:
case InfoType::IT_enum:
case InfoType::IT_typedef:
case InfoType::IT_concept:
case InfoType::IT_variable:
case InfoType::IT_friend:
Field = IT;
return llvm::Error::success();
}
return llvm::createStringError(llvm::inconvertibleErrorCode(),
"invalid value for InfoType");
}
static llvm::Error decodeRecord(const Record &R, FieldId &Field,
llvm::StringRef Blob) {
switch (auto F = static_cast<FieldId>(R[0])) {
case FieldId::F_namespace:
case FieldId::F_parent:
case FieldId::F_vparent:
case FieldId::F_type:
case FieldId::F_child_namespace:
case FieldId::F_child_record:
case FieldId::F_concept:
case FieldId::F_friend:
case FieldId::F_default:
Field = F;
return llvm::Error::success();
}
return llvm::createStringError(llvm::inconvertibleErrorCode(),
"invalid value for FieldId");
}
static llvm::Error decodeRecord(const Record &R,
llvm::SmallVectorImpl<Location> &Field,
llvm::StringRef Blob) {
if (R[0] > INT_MAX)
return llvm::createStringError(llvm::inconvertibleErrorCode(),
"integer too large to parse");
Field.emplace_back(static_cast<int>(R[0]), static_cast<int>(R[1]), Blob,
static_cast<bool>(R[2]));
return llvm::Error::success();
}
static llvm::Error parseRecord(const Record &R, unsigned ID,
llvm::StringRef Blob, const unsigned VersionNo) {
if (ID == VERSION && R[0] == VersionNo)
return llvm::Error::success();
return llvm::createStringError(llvm::inconvertibleErrorCode(),
"mismatched bitcode version number");
}
static llvm::Error parseRecord(const Record &R, unsigned ID,
llvm::StringRef Blob, NamespaceInfo *I) {
switch (ID) {
case NAMESPACE_USR:
return decodeRecord(R, I->USR, Blob);
case NAMESPACE_NAME:
return decodeRecord(R, I->Name, Blob);
case NAMESPACE_PATH:
return decodeRecord(R, I->Path, Blob);
case NAMESPACE_PARENT_USR:
return decodeRecord(R, I->ParentUSR, Blob);
default:
return llvm::createStringError(llvm::inconvertibleErrorCode(),
"invalid field for NamespaceInfo");
}
}
static llvm::Error parseRecord(const Record &R, unsigned ID,
llvm::StringRef Blob, RecordInfo *I) {
switch (ID) {
case RECORD_USR:
return decodeRecord(R, I->USR, Blob);
case RECORD_NAME:
return decodeRecord(R, I->Name, Blob);
case RECORD_PATH:
return decodeRecord(R, I->Path, Blob);
case RECORD_DEFLOCATION:
return decodeRecord(R, I->DefLoc, Blob);
case RECORD_LOCATION:
return decodeRecord(R, I->Loc, Blob);
case RECORD_TAG_TYPE:
return decodeRecord(R, I->TagType, Blob);
case RECORD_IS_TYPE_DEF:
return decodeRecord(R, I->IsTypeDef, Blob);
case RECORD_MANGLED_NAME:
return decodeRecord(R, I->MangledName, Blob);
case RECORD_PARENT_USR:
return decodeRecord(R, I->ParentUSR, Blob);
default:
return llvm::createStringError(llvm::inconvertibleErrorCode(),
"invalid field for RecordInfo");
}
}
static llvm::Error parseRecord(const Record &R, unsigned ID,
llvm::StringRef Blob, BaseRecordInfo *I) {
switch (ID) {
case BASE_RECORD_USR:
return decodeRecord(R, I->USR, Blob);
case BASE_RECORD_NAME:
return decodeRecord(R, I->Name, Blob);
case BASE_RECORD_PATH:
return decodeRecord(R, I->Path, Blob);
case BASE_RECORD_TAG_TYPE:
return decodeRecord(R, I->TagType, Blob);
case BASE_RECORD_IS_VIRTUAL:
return decodeRecord(R, I->IsVirtual, Blob);
case BASE_RECORD_ACCESS:
return decodeRecord(R, I->Access, Blob);
case BASE_RECORD_IS_PARENT:
return decodeRecord(R, I->IsParent, Blob);
default:
return llvm::createStringError(llvm::inconvertibleErrorCode(),
"invalid field for BaseRecordInfo");
}
}
static llvm::Error parseRecord(const Record &R, unsigned ID,
llvm::StringRef Blob, EnumInfo *I) {
switch (ID) {
case ENUM_USR:
return decodeRecord(R, I->USR, Blob);
case ENUM_NAME:
return decodeRecord(R, I->Name, Blob);
case ENUM_DEFLOCATION:
return decodeRecord(R, I->DefLoc, Blob);
case ENUM_LOCATION:
return decodeRecord(R, I->Loc, Blob);
case ENUM_SCOPED:
return decodeRecord(R, I->Scoped, Blob);
default:
return llvm::createStringError(llvm::inconvertibleErrorCode(),
"invalid field for EnumInfo");
}
}
static llvm::Error parseRecord(const Record &R, unsigned ID,
llvm::StringRef Blob, TypedefInfo *I) {
switch (ID) {
case TYPEDEF_USR:
return decodeRecord(R, I->USR, Blob);
case TYPEDEF_NAME:
return decodeRecord(R, I->Name, Blob);
case TYPEDEF_DEFLOCATION:
return decodeRecord(R, I->DefLoc, Blob);
case TYPEDEF_IS_USING:
return decodeRecord(R, I->IsUsing, Blob);
default:
return llvm::createStringError(llvm::inconvertibleErrorCode(),
"invalid field for TypedefInfo");
}
}
static 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");
}
}
static llvm::Error parseRecord(const Record &R, unsigned ID,
llvm::StringRef Blob, FunctionInfo *I) {
switch (ID) {
case FUNCTION_USR:
return decodeRecord(R, I->USR, Blob);
case FUNCTION_NAME:
return decodeRecord(R, I->Name, Blob);
case FUNCTION_DEFLOCATION:
return decodeRecord(R, I->DefLoc, Blob);
case FUNCTION_LOCATION:
return decodeRecord(R, I->Loc, Blob);
case FUNCTION_ACCESS:
return decodeRecord(R, I->Access, Blob);
case FUNCTION_IS_METHOD:
return decodeRecord(R, I->IsMethod, Blob);
case FUNCTION_IS_STATIC:
return decodeRecord(R, I->IsStatic, Blob);
default:
return llvm::createStringError(llvm::inconvertibleErrorCode(),
"invalid field for FunctionInfo");
}
}
static llvm::Error parseRecord(const Record &R, unsigned ID,
llvm::StringRef Blob, TypeInfo *I) {
switch (ID) {
case TYPE_IS_BUILTIN:
return decodeRecord(R, I->IsBuiltIn, Blob);
case TYPE_IS_TEMPLATE:
return decodeRecord(R, I->IsTemplate, Blob);
default:
return llvm::createStringError(llvm::inconvertibleErrorCode(),
"invalid field for TypeInfo");
}
}
static llvm::Error parseRecord(const Record &R, unsigned ID,
llvm::StringRef Blob, FieldTypeInfo *I) {
switch (ID) {
case FIELD_TYPE_NAME:
return decodeRecord(R, I->Name, Blob);
case FIELD_DEFAULT_VALUE:
return decodeRecord(R, I->DefaultValue, Blob);
case FIELD_TYPE_IS_BUILTIN:
return decodeRecord(R, I->IsBuiltIn, Blob);
case FIELD_TYPE_IS_TEMPLATE:
return decodeRecord(R, I->IsTemplate, Blob);
default:
return llvm::createStringError(llvm::inconvertibleErrorCode(),
"invalid field for TypeInfo");
}
}
static llvm::Error parseRecord(const Record &R, unsigned ID,
llvm::StringRef Blob, MemberTypeInfo *I) {
switch (ID) {
case MEMBER_TYPE_NAME:
return decodeRecord(R, I->Name, Blob);
case MEMBER_TYPE_ACCESS:
return decodeRecord(R, I->Access, Blob);
case MEMBER_TYPE_IS_STATIC:
return decodeRecord(R, I->IsStatic, Blob);
case MEMBER_TYPE_IS_BUILTIN:
return decodeRecord(R, I->IsBuiltIn, Blob);
case MEMBER_TYPE_IS_TEMPLATE:
return decodeRecord(R, I->IsTemplate, Blob);
default:
return llvm::createStringError(llvm::inconvertibleErrorCode(),
"invalid field for MemberTypeInfo");
}
}
static llvm::Error parseRecord(const Record &R, unsigned ID,
llvm::StringRef Blob, CommentInfo *I,
llvm::SmallVectorImpl<StringRef> &AttrKeys,
llvm::SmallVectorImpl<StringRef> &AttrValues,
llvm::SmallVectorImpl<StringRef> &Args) {
llvm::SmallString<16> KindStr;
switch (ID) {
case COMMENT_KIND:
if (llvm::Error Err = decodeRecord(R, KindStr, Blob))
return Err;
I->Kind = stringToCommentKind(KindStr);
return llvm::Error::success();
case COMMENT_TEXT:
return decodeRecord(R, I->Text, Blob);
case COMMENT_NAME:
return decodeRecord(R, I->Name, Blob);
case COMMENT_DIRECTION:
return decodeRecord(R, I->Direction, Blob);
case COMMENT_PARAMNAME:
return decodeRecord(R, I->ParamName, Blob);
case COMMENT_CLOSENAME:
return decodeRecord(R, I->CloseName, Blob);
case COMMENT_ATTRKEY:
AttrKeys.push_back(internString(Blob));
return llvm::Error::success();
case COMMENT_ATTRVAL:
AttrValues.push_back(internString(Blob));
return llvm::Error::success();
case COMMENT_ARG:
Args.push_back(internString(Blob));
return llvm::Error::success();
case COMMENT_SELFCLOSING:
return decodeRecord(R, I->SelfClosing, Blob);
case COMMENT_EXPLICIT:
return decodeRecord(R, I->Explicit, Blob);
default:
return llvm::createStringError(llvm::inconvertibleErrorCode(),
"invalid field for CommentInfo");
}
}
template <>
llvm::Error ClangDocBitcodeReader::readBlock(unsigned ID, CommentInfo *I) {
llvm::TimeTraceScope("Reducing infos", "readBlock");
if (llvm::Error Err = Stream.EnterSubBlock(ID))
return Err;
llvm::SmallVector<StringRef> AttrKeys;
llvm::SmallVector<StringRef> AttrValues;
llvm::SmallVector<StringRef> Args;
while (true) {
unsigned BlockOrCode = 0;
llvm::Expected<Cursor> C = skipUntilRecordOrBlock(BlockOrCode);
if (!C)
return C.takeError();
switch (*C) {
case Cursor::BadBlock:
return llvm::createStringError(llvm::inconvertibleErrorCode(),
"bad block found");
case Cursor::BlockEnd: {
if (!AttrKeys.empty()) {
StringRef *KeysMem =
TransientArena.Allocate<StringRef>(AttrKeys.size());
std::uninitialized_copy(AttrKeys.begin(), AttrKeys.end(), KeysMem);
I->AttrKeys = llvm::ArrayRef<StringRef>(KeysMem, AttrKeys.size());
}
if (!AttrValues.empty()) {
StringRef *ValuesMem =
TransientArena.Allocate<StringRef>(AttrValues.size());
std::uninitialized_copy(AttrValues.begin(), AttrValues.end(),
ValuesMem);
I->AttrValues = llvm::ArrayRef<StringRef>(ValuesMem, AttrValues.size());
}
if (!Args.empty()) {
StringRef *ArgsMem = TransientArena.Allocate<StringRef>(Args.size());
std::uninitialized_copy(Args.begin(), Args.end(), ArgsMem);
I->Args = llvm::ArrayRef<StringRef>(ArgsMem, Args.size());
}
return llvm::Error::success();
}
case Cursor::BlockBegin:
if (llvm::Error Err = readSubBlock(BlockOrCode, I)) {
if (llvm::Error Skipped = Stream.SkipBlock())
return joinErrors(std::move(Err), std::move(Skipped));
return Err;
}
continue;
case Cursor::Record:
break;
}
Record R;
llvm::StringRef Blob;
llvm::Expected<unsigned> MaybeRecID =
Stream.readRecord(BlockOrCode, R, &Blob);
if (!MaybeRecID)
return MaybeRecID.takeError();
if (llvm::Error Err = parseRecord(R, MaybeRecID.get(), Blob, I, AttrKeys,
AttrValues, Args))
return Err;
}
}
static llvm::Error parseRecord(const Record &R, unsigned ID,
llvm::StringRef Blob, Reference *I, FieldId &F) {
switch (ID) {
case REFERENCE_USR:
return decodeRecord(R, I->USR, Blob);
case REFERENCE_NAME:
return decodeRecord(R, I->Name, Blob);
case REFERENCE_QUAL_NAME:
return decodeRecord(R, I->QualName, Blob);
case REFERENCE_TYPE:
return decodeRecord(R, I->RefType, Blob);
case REFERENCE_PATH:
return decodeRecord(R, I->Path, Blob);
case REFERENCE_FIELD:
return decodeRecord(R, F, Blob);
case REFERENCE_FILE:
return decodeRecord(R, I->DocumentationFileName, Blob);
default:
return llvm::createStringError(llvm::inconvertibleErrorCode(),
"invalid field for Reference");
}
}
static llvm::Error parseRecord(const Record &R, unsigned ID,
llvm::StringRef Blob, TemplateInfo *I) {
// Currently there are no child records of TemplateInfo (only child blocks).
return llvm::createStringError(llvm::inconvertibleErrorCode(),
"invalid field for TemplateParamInfo");
}
static llvm::Error parseRecord(const Record &R, unsigned ID,
llvm::StringRef Blob,
TemplateSpecializationInfo *I) {
if (ID == TEMPLATE_SPECIALIZATION_OF)
return decodeRecord(R, I->SpecializationOf, Blob);
return llvm::createStringError(llvm::inconvertibleErrorCode(),
"invalid field for TemplateParamInfo");
}
static llvm::Error parseRecord(const Record &R, unsigned ID,
llvm::StringRef Blob, TemplateParamInfo *I) {
if (ID == TEMPLATE_PARAM_CONTENTS)
return decodeRecord(R, I->Contents, Blob);
return llvm::createStringError(llvm::inconvertibleErrorCode(),
"invalid field for TemplateParamInfo");
}
static llvm::Error parseRecord(const Record &R, unsigned ID,
llvm::StringRef Blob, ConceptInfo *I) {
switch (ID) {
case CONCEPT_USR:
return decodeRecord(R, I->USR, Blob);
case CONCEPT_NAME:
return decodeRecord(R, I->Name, Blob);
case CONCEPT_IS_TYPE:
return decodeRecord(R, I->IsType, Blob);
case CONCEPT_CONSTRAINT_EXPRESSION:
return decodeRecord(R, I->ConstraintExpression, Blob);
case CONCEPT_DEFLOCATION:
return decodeRecord(R, I->DefLoc, Blob);
}
llvm_unreachable("invalid field for ConceptInfo");
}
static llvm::Error parseRecord(const Record &R, unsigned ID,
llvm::StringRef Blob, ConstraintInfo *I) {
if (ID == CONSTRAINT_EXPRESSION)
return decodeRecord(R, I->ConstraintExpr, Blob);
return llvm::createStringError(llvm::inconvertibleErrorCode(),
"invalid field for ConstraintInfo");
}
static llvm::Error parseRecord(const Record &R, unsigned ID,
llvm::StringRef Blob, VarInfo *I) {
switch (ID) {
case VAR_USR:
return decodeRecord(R, I->USR, Blob);
case VAR_NAME:
return decodeRecord(R, I->Name, Blob);
case VAR_DEFLOCATION:
return decodeRecord(R, I->DefLoc, Blob);
case VAR_IS_STATIC:
return decodeRecord(R, I->IsStatic, Blob);
default:
return llvm::createStringError(llvm::inconvertibleErrorCode(),
"invalid field for VarInfo");
}
}
static llvm::Error parseRecord(const Record &R, unsigned ID, StringRef Blob,
FriendInfo *F) {
if (ID == FRIEND_IS_CLASS) {
return decodeRecord(R, F->IsClass, Blob);
}
return llvm::createStringError(llvm::inconvertibleErrorCode(),
"invalid field for Friend");
}
template <typename T> static llvm::Expected<CommentInfo *> getCommentInfo(T I) {
return llvm::createStringError(llvm::inconvertibleErrorCode(),
"invalid type cannot contain CommentInfo");
}
template <> llvm::Expected<CommentInfo *> getCommentInfo(FunctionInfo *I) {
return &I->Description.emplace_back();
}
template <> llvm::Expected<CommentInfo *> getCommentInfo(NamespaceInfo *I) {
return &I->Description.emplace_back();
}
template <> llvm::Expected<CommentInfo *> getCommentInfo(RecordInfo *I) {
return &I->Description.emplace_back();
}
template <> llvm::Expected<CommentInfo *> getCommentInfo(MemberTypeInfo *I) {
return &I->Description.emplace_back();
}
template <> llvm::Expected<CommentInfo *> getCommentInfo(EnumInfo *I) {
return &I->Description.emplace_back();
}
template <> llvm::Expected<CommentInfo *> getCommentInfo(TypedefInfo *I) {
return &I->Description.emplace_back();
}
template <> llvm::Expected<CommentInfo *> getCommentInfo(EnumValueInfo *I) {
return &I->Description.emplace_back();
}
template <> llvm::Expected<CommentInfo *> getCommentInfo(CommentInfo *I) {
I->Children.emplace_back(allocatePtr<CommentInfo>());
return getPtr(I->Children.back());
}
template <> llvm::Expected<CommentInfo *> getCommentInfo(ConceptInfo *I) {
return &I->Description.emplace_back();
}
template <> Expected<CommentInfo *> getCommentInfo(VarInfo *I) {
return &I->Description.emplace_back();
}
template <> Expected<CommentInfo *> getCommentInfo(FriendInfo *I) {
return &I->Description.emplace_back();
}
// 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>
static llvm::Error addTypeInfo(T I, TTypeInfo &&TI) {
return llvm::createStringError(llvm::inconvertibleErrorCode(),
"invalid type cannot contain TypeInfo");
}
template <> llvm::Error addTypeInfo(RecordInfo *I, MemberTypeInfo &&T) {
I->Members.emplace_back(std::move(T));
return llvm::Error::success();
}
template <> llvm::Error addTypeInfo(BaseRecordInfo *I, MemberTypeInfo &&T) {
I->Members.emplace_back(std::move(T));
return llvm::Error::success();
}
template <> llvm::Error addTypeInfo(FunctionInfo *I, TypeInfo &&T) {
I->ReturnType = std::move(T);
return llvm::Error::success();
}
template <> llvm::Error addTypeInfo(FunctionInfo *I, FieldTypeInfo &&T) {
I->Params.emplace_back(std::move(T));
return llvm::Error::success();
}
template <> llvm::Error addTypeInfo(FriendInfo *I, TypeInfo &&T) {
I->ReturnType.emplace(std::move(T));
return llvm::Error::success();
}
template <> llvm::Error addTypeInfo(EnumInfo *I, TypeInfo &&T) {
I->BaseType = std::move(T);
return llvm::Error::success();
}
template <> llvm::Error addTypeInfo(TypedefInfo *I, TypeInfo &&T) {
I->Underlying = std::move(T);
return llvm::Error::success();
}
template <> llvm::Error addTypeInfo(VarInfo *I, TypeInfo &&T) {
I->Type = std::move(T);
return llvm::Error::success();
}
template <typename T>
static llvm::Error addReference(T I, Reference &&R, FieldId F) {
return llvm::createStringError(llvm::inconvertibleErrorCode(),
"invalid type cannot contain Reference");
}
template <> llvm::Error addReference(VarInfo *I, Reference &&R, FieldId F) {
switch (F) {
case FieldId::F_namespace:
I->Namespace.emplace_back(std::move(R));
return llvm::Error::success();
default:
return llvm::createStringError(llvm::inconvertibleErrorCode(),
"VarInfo cannot contain this Reference");
}
}
template <> llvm::Error addReference(TypeInfo *I, Reference &&R, FieldId F) {
switch (F) {
case FieldId::F_type:
I->Type = std::move(R);
return llvm::Error::success();
default:
return llvm::createStringError(llvm::inconvertibleErrorCode(),
"invalid type cannot contain Reference");
}
}
template <>
llvm::Error addReference(FieldTypeInfo *I, Reference &&R, FieldId F) {
switch (F) {
case FieldId::F_type:
I->Type = std::move(R);
return llvm::Error::success();
default:
return llvm::createStringError(llvm::inconvertibleErrorCode(),
"invalid type cannot contain Reference");
}
}
template <>
llvm::Error addReference(MemberTypeInfo *I, Reference &&R, FieldId F) {
switch (F) {
case FieldId::F_type:
I->Type = std::move(R);
return llvm::Error::success();
default:
return llvm::createStringError(llvm::inconvertibleErrorCode(),
"invalid type cannot contain Reference");
}
}
template <> llvm::Error addReference(EnumInfo *I, Reference &&R, FieldId F) {
switch (F) {
case FieldId::F_namespace:
I->Namespace.emplace_back(std::move(R));
return llvm::Error::success();
default:
return llvm::createStringError(llvm::inconvertibleErrorCode(),
"invalid type cannot contain Reference");
}
}
template <> llvm::Error addReference(TypedefInfo *I, Reference &&R, FieldId F) {
switch (F) {
case FieldId::F_namespace:
I->Namespace.emplace_back(std::move(R));
return llvm::Error::success();
default:
return llvm::createStringError(llvm::inconvertibleErrorCode(),
"invalid type cannot contain Reference");
}
}
template <>
llvm::Error addReference(NamespaceInfo *I, Reference &&R, FieldId F) {
switch (F) {
case FieldId::F_namespace:
I->Namespace.emplace_back(std::move(R));
return llvm::Error::success();
case FieldId::F_child_namespace: {
Reference *NewR = allocatePtr<Reference>(TransientArena, std::move(R));
I->Children.Namespaces.push_back(*NewR);
return llvm::Error::success();
}
case FieldId::F_child_record:
I->Children.Records.emplace_back(std::move(R));
return llvm::Error::success();
default:
return llvm::createStringError(llvm::inconvertibleErrorCode(),
"invalid type cannot contain Reference");
}
}
template <>
llvm::Error addReference(FunctionInfo *I, Reference &&R, FieldId F) {
switch (F) {
case FieldId::F_namespace:
I->Namespace.emplace_back(std::move(R));
return llvm::Error::success();
case FieldId::F_parent:
I->Parent = std::move(R);
return llvm::Error::success();
default:
return llvm::createStringError(llvm::inconvertibleErrorCode(),
"invalid type cannot contain Reference");
}
}
template <> llvm::Error addReference(RecordInfo *I, Reference &&R, FieldId F) {
switch (F) {
case FieldId::F_namespace:
I->Namespace.emplace_back(std::move(R));
return llvm::Error::success();
case FieldId::F_parent:
I->Parents.emplace_back(std::move(R));
return llvm::Error::success();
case FieldId::F_vparent:
I->VirtualParents.emplace_back(std::move(R));
return llvm::Error::success();
case FieldId::F_child_record:
I->Children.Records.emplace_back(std::move(R));
return llvm::Error::success();
default:
return llvm::createStringError(llvm::inconvertibleErrorCode(),
"invalid type cannot contain Reference");
}
}
template <>
llvm::Error addReference(ConstraintInfo *I, Reference &&R, FieldId F) {
if (F == FieldId::F_concept) {
I->ConceptRef = std::move(R);
return llvm::Error::success();
}
return llvm::createStringError(
llvm::inconvertibleErrorCode(),
"ConstraintInfo cannot contain this Reference");
}
template <>
llvm::Error addReference(FriendInfo *Friend, Reference &&R, FieldId F) {
if (F == FieldId::F_friend) {
Friend->Ref = std::move(R);
return llvm::Error::success();
}
return llvm::createStringError(llvm::inconvertibleErrorCode(),
"Friend cannot contain this Reference");
}
template <typename T, typename ChildInfoType>
static void addChild(T I, ChildInfoType &&R) {
ExitOnErr(llvm::createStringError(llvm::inconvertibleErrorCode(),
"invalid child type for info"));
}
// Namespace children:
template <> void addChild(NamespaceInfo *I, FunctionInfo &&R) {
I->Children.Functions.emplace_back(std::move(R));
}
template <> void addChild(NamespaceInfo *I, EnumInfo &&R) {
I->Children.Enums.emplace_back(std::move(R));
}
template <> void addChild(NamespaceInfo *I, TypedefInfo &&R) {
I->Children.Typedefs.emplace_back(std::move(R));
}
template <> void addChild(NamespaceInfo *I, ConceptInfo &&R) {
I->Children.Concepts.emplace_back(std::move(R));
}
template <> void addChild(NamespaceInfo *I, VarInfo &&R) {
I->Children.Variables.emplace_back(std::move(R));
}
// Record children:
template <> void addChild(RecordInfo *I, FunctionInfo &&R) {
I->Children.Functions.emplace_back(std::move(R));
}
template <> void addChild(RecordInfo *I, EnumInfo &&R) {
I->Children.Enums.emplace_back(std::move(R));
}
template <> void addChild(RecordInfo *I, TypedefInfo &&R) {
I->Children.Typedefs.emplace_back(std::move(R));
}
template <> void addChild(RecordInfo *I, FriendInfo &&R) {
I->Friends.emplace_back(std::move(R));
}
// Other types of children:
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));
}
template <> void addChild(BaseRecordInfo *I, FunctionInfo &&R) {
I->Children.Functions.emplace_back(std::move(R));
}
// TemplateParam children. These go into either a TemplateInfo (for template
// parameters) or TemplateSpecializationInfo (for the specialization's
// parameters).
template <typename T> static void addTemplateParam(T I, TemplateParamInfo &&P) {
ExitOnErr(
llvm::createStringError(llvm::inconvertibleErrorCode(),
"invalid container for template parameter"));
}
template <> void addTemplateParam(TemplateInfo *I, TemplateParamInfo &&P) {
I->Params.emplace_back(std::move(P));
}
template <>
void addTemplateParam(TemplateSpecializationInfo *I, TemplateParamInfo &&P) {
I->Params.emplace_back(std::move(P));
}
// Template info. These apply to either records or functions.
template <typename T> static void addTemplate(T I, TemplateInfo &&P) {
ExitOnErr(llvm::createStringError(llvm::inconvertibleErrorCode(),
"invalid container for template info"));
}
template <> void addTemplate(RecordInfo *I, TemplateInfo &&P) {
I->Template.emplace(std::move(P));
}
template <> void addTemplate(FunctionInfo *I, TemplateInfo &&P) {
I->Template.emplace(std::move(P));
}
template <> void addTemplate(ConceptInfo *I, TemplateInfo &&P) {
I->Template = std::move(P);
}
template <> void addTemplate(FriendInfo *I, TemplateInfo &&P) {
I->Template.emplace(std::move(P));
}
template <> void addTemplate(TypedefInfo *I, TemplateInfo &&P) {
I->Template.emplace(std::move(P));
}
// Template specializations go only into template records.
template <typename T>
static void addTemplateSpecialization(T I, TemplateSpecializationInfo &&TSI) {
ExitOnErr(llvm::createStringError(
llvm::inconvertibleErrorCode(),
"invalid container for template specialization info"));
}
template <>
void addTemplateSpecialization(TemplateInfo *I,
TemplateSpecializationInfo &&TSI) {
I->Specialization.emplace(std::move(TSI));
}
template <typename T> static void addConstraint(T I, ConstraintInfo &&C) {
ExitOnErr(llvm::createStringError(llvm::inconvertibleErrorCode(),
"invalid container for constraint info"));
}
template <> void addConstraint(TemplateInfo *I, ConstraintInfo &&C) {
I->Constraints.emplace_back(std::move(C));
}
// Read records from bitcode into a given info.
template <typename T>
llvm::Error ClangDocBitcodeReader::readRecord(unsigned ID, T I) {
Record R;
llvm::StringRef Blob;
llvm::Expected<unsigned> MaybeRecID = Stream.readRecord(ID, R, &Blob);
if (!MaybeRecID)
return MaybeRecID.takeError();
return parseRecord(R, MaybeRecID.get(), Blob, I);
}
template <>
llvm::Error ClangDocBitcodeReader::readRecord(unsigned ID, Reference *I) {
llvm::TimeTraceScope("Reducing infos", "readRecord");
Record R;
llvm::StringRef Blob;
llvm::Expected<unsigned> MaybeRecID = Stream.readRecord(ID, R, &Blob);
if (!MaybeRecID)
return MaybeRecID.takeError();
return parseRecord(R, MaybeRecID.get(), Blob, I, CurrentReferenceField);
}
// Read a block of records into a single info.
template <typename T>
llvm::Error ClangDocBitcodeReader::readBlock(unsigned ID, T I) {
llvm::TimeTraceScope("Reducing infos", "readBlock");
if (llvm::Error Err = Stream.EnterSubBlock(ID))
return Err;
while (true) {
unsigned BlockOrCode = 0;
llvm::Expected<Cursor> C = skipUntilRecordOrBlock(BlockOrCode);
if (!C)
return C.takeError();
switch (*C) {
case Cursor::BadBlock:
return llvm::createStringError(llvm::inconvertibleErrorCode(),
"bad block found");
case Cursor::BlockEnd:
return llvm::Error::success();
case Cursor::BlockBegin:
if (llvm::Error Err = readSubBlock(BlockOrCode, I)) {
if (llvm::Error Skipped = Stream.SkipBlock())
return joinErrors(std::move(Err), std::move(Skipped));
return Err;
}
continue;
case Cursor::Record:
break;
}
if (auto Err = readRecord(BlockOrCode, I))
return Err;
}
}
template <>
llvm::Error ClangDocBitcodeReader::readBlock(unsigned ID, FriendInfo *I) {
llvm::TimeTraceScope("Reducing infos", "readBlock");
if (llvm::Error Err = Stream.EnterSubBlock(ID))
return Err;
llvm::SmallVector<FieldTypeInfo, 4> LocalParams;
while (true) {
unsigned BlockOrCode = 0;
llvm::Expected<Cursor> C = skipUntilRecordOrBlock(BlockOrCode);
if (!C)
return C.takeError();
switch (*C) {
case Cursor::BadBlock:
return llvm::createStringError(llvm::inconvertibleErrorCode(),
"bad block found");
case Cursor::BlockEnd: {
if (!LocalParams.empty())
I->Params = allocateArray<FieldTypeInfo>(LocalParams, TransientArena);
return llvm::Error::success();
}
case Cursor::BlockBegin:
if (BlockOrCode == BI_FIELD_TYPE_BLOCK_ID) {
FieldTypeInfo FI;
if (auto Err = readBlock(BlockOrCode, &FI))
return Err;
LocalParams.push_back(std::move(FI));
continue;
}
if (llvm::Error Err = readSubBlock(BlockOrCode, I)) {
if (llvm::Error Skipped = Stream.SkipBlock())
return joinErrors(std::move(Err), std::move(Skipped));
return Err;
}
continue;
case Cursor::Record:
break;
}
if (auto Err = readRecord(BlockOrCode, I))
return Err;
}
}
// TODO: fix inconsistentent returning of errors in add callbacks.
// Once that's fixed, we only need one handleSubBlock.
template <typename InfoType, typename T, typename Callback>
llvm::Error ClangDocBitcodeReader::handleSubBlock(unsigned ID, T Parent,
Callback Function) {
InfoType Info;
if (auto Err = readBlock(ID, &Info))
return Err;
Function(Parent, std::move(Info));
return llvm::Error::success();
}
template <typename InfoType, typename T, typename Callback>
llvm::Error ClangDocBitcodeReader::handleTypeSubBlock(unsigned ID, T Parent,
Callback Function) {
InfoType Info;
if (auto Err = readBlock(ID, &Info))
return Err;
if (auto Err = Function(Parent, std::move(Info)))
return Err;
return llvm::Error::success();
}
template <typename T>
llvm::Error ClangDocBitcodeReader::readSubBlock(unsigned ID, T I) {
llvm::TimeTraceScope("Reducing infos", "readSubBlock");
static auto CreateAddFunc = [](auto AddFunc) {
return [AddFunc](auto Parent, auto Child) {
return AddFunc(Parent, std::move(Child));
};
};
switch (ID) {
// Blocks can only have certain types of sub blocks.
case BI_COMMENT_BLOCK_ID: {
auto Comment = getCommentInfo(I);
if (!Comment)
return Comment.takeError();
if (auto Err = readBlock(ID, Comment.get()))
return Err;
return llvm::Error::success();
}
case BI_TYPE_BLOCK_ID: {
return handleTypeSubBlock<TypeInfo>(
ID, I, CreateAddFunc(addTypeInfo<T, TypeInfo>));
}
case BI_FIELD_TYPE_BLOCK_ID: {
return handleTypeSubBlock<FieldTypeInfo>(
ID, I, CreateAddFunc(addTypeInfo<T, FieldTypeInfo>));
}
case BI_MEMBER_TYPE_BLOCK_ID: {
return handleTypeSubBlock<MemberTypeInfo>(
ID, I, CreateAddFunc(addTypeInfo<T, MemberTypeInfo>));
}
case BI_REFERENCE_BLOCK_ID: {
Reference R;
if (auto Err = readBlock(ID, &R))
return Err;
if (auto Err = addReference(I, std::move(R), CurrentReferenceField))
return Err;
return llvm::Error::success();
}
case BI_FUNCTION_BLOCK_ID: {
return handleSubBlock<FunctionInfo>(
ID, I, CreateAddFunc(addChild<T, FunctionInfo>));
}
case BI_BASE_RECORD_BLOCK_ID: {
return handleSubBlock<BaseRecordInfo>(
ID, I, CreateAddFunc(addChild<T, BaseRecordInfo>));
}
case BI_ENUM_BLOCK_ID: {
return handleSubBlock<EnumInfo>(ID, I,
CreateAddFunc(addChild<T, EnumInfo>));
}
case BI_ENUM_VALUE_BLOCK_ID: {
return handleSubBlock<EnumValueInfo>(
ID, I, CreateAddFunc(addChild<T, EnumValueInfo>));
}
case BI_TEMPLATE_BLOCK_ID: {
return handleSubBlock<TemplateInfo>(ID, I, CreateAddFunc(addTemplate<T>));
}
case BI_TEMPLATE_SPECIALIZATION_BLOCK_ID: {
return handleSubBlock<TemplateSpecializationInfo>(
ID, I, CreateAddFunc(addTemplateSpecialization<T>));
}
case BI_TEMPLATE_PARAM_BLOCK_ID: {
return handleSubBlock<TemplateParamInfo>(
ID, I, CreateAddFunc(addTemplateParam<T>));
}
case BI_TYPEDEF_BLOCK_ID: {
return handleSubBlock<TypedefInfo>(ID, I,
CreateAddFunc(addChild<T, TypedefInfo>));
}
case BI_CONSTRAINT_BLOCK_ID: {
return handleSubBlock<ConstraintInfo>(ID, I,
CreateAddFunc(addConstraint<T>));
}
case BI_CONCEPT_BLOCK_ID: {
return handleSubBlock<ConceptInfo>(ID, I,
CreateAddFunc(addChild<T, ConceptInfo>));
}
case BI_VAR_BLOCK_ID: {
return handleSubBlock<VarInfo>(ID, I, CreateAddFunc(addChild<T, VarInfo>));
}
case BI_FRIEND_BLOCK_ID: {
return handleSubBlock<FriendInfo>(ID, I,
CreateAddFunc(addChild<T, FriendInfo>));
}
default:
return llvm::createStringError(llvm::inconvertibleErrorCode(),
"invalid subblock type");
}
}
llvm::Expected<ClangDocBitcodeReader::Cursor>
ClangDocBitcodeReader::skipUntilRecordOrBlock(unsigned &BlockOrRecordID) {
llvm::TimeTraceScope("Reducing infos", "skipUntilRecordOrBlock");
BlockOrRecordID = 0;
while (!Stream.AtEndOfStream()) {
Expected<unsigned> Code = Stream.ReadCode();
if (!Code)
return Code.takeError();
if (*Code >= static_cast<unsigned>(llvm::bitc::FIRST_APPLICATION_ABBREV)) {
BlockOrRecordID = *Code;
return Cursor::Record;
}
switch (static_cast<llvm::bitc::FixedAbbrevIDs>(*Code)) {
case llvm::bitc::ENTER_SUBBLOCK:
if (Expected<unsigned> MaybeID = Stream.ReadSubBlockID())
BlockOrRecordID = MaybeID.get();
else
return MaybeID.takeError();
return Cursor::BlockBegin;
case llvm::bitc::END_BLOCK:
if (Stream.ReadBlockEnd())
return llvm::createStringError(llvm::inconvertibleErrorCode(),
"error at end of block");
return Cursor::BlockEnd;
case llvm::bitc::DEFINE_ABBREV:
if (llvm::Error Err = Stream.ReadAbbrevRecord())
return std::move(Err);
continue;
case llvm::bitc::UNABBREV_RECORD:
return llvm::createStringError(llvm::inconvertibleErrorCode(),
"found unabbreviated record");
case llvm::bitc::FIRST_APPLICATION_ABBREV:
llvm_unreachable("Unexpected abbrev id.");
}
}
llvm_unreachable("Premature stream end.");
}
llvm::Error ClangDocBitcodeReader::validateStream() {
if (Stream.AtEndOfStream())
return llvm::createStringError(llvm::inconvertibleErrorCode(),
"premature end of stream");
// Sniff for the signature.
for (int Idx = 0; Idx != 4; ++Idx) {
Expected<llvm::SimpleBitstreamCursor::word_t> MaybeRead = Stream.Read(8);
if (!MaybeRead)
return MaybeRead.takeError();
if (MaybeRead.get() != BitCodeConstants::Signature[Idx])
return llvm::createStringError(llvm::inconvertibleErrorCode(),
"invalid bitcode signature");
}
return llvm::Error::success();
}
llvm::Error ClangDocBitcodeReader::readBlockInfoBlock() {
llvm::TimeTraceScope("Reducing infos", "readBlockInfoBlock");
Expected<std::optional<llvm::BitstreamBlockInfo>> MaybeBlockInfo =
Stream.ReadBlockInfoBlock();
if (!MaybeBlockInfo)
return MaybeBlockInfo.takeError();
BlockInfo = MaybeBlockInfo.get();
if (!BlockInfo)
return llvm::createStringError(llvm::inconvertibleErrorCode(),
"unable to parse BlockInfoBlock");
Stream.setBlockInfo(&*BlockInfo);
return llvm::Error::success();
}
template <typename T>
llvm::Expected<OwnedPtr<Info>> ClangDocBitcodeReader::createInfo(unsigned ID) {
llvm::TimeTraceScope("Reducing infos", "createInfo");
OwnedPtr<Info> I = doc::allocatePtr<T>();
if (auto Err = readBlock(ID, static_cast<T *>(getPtr(I))))
return std::move(Err);
return OwnedPtr<Info>{std::move(I)};
}
llvm::Expected<OwnedPtr<Info>>
ClangDocBitcodeReader::readBlockToInfo(unsigned ID) {
llvm::TimeTraceScope("Reducing infos", "readBlockToInfo");
switch (ID) {
case BI_NAMESPACE_BLOCK_ID:
return createInfo<NamespaceInfo>(ID);
case BI_RECORD_BLOCK_ID:
return createInfo<RecordInfo>(ID);
case BI_ENUM_BLOCK_ID:
return createInfo<EnumInfo>(ID);
case BI_TYPEDEF_BLOCK_ID:
return createInfo<TypedefInfo>(ID);
case BI_CONCEPT_BLOCK_ID:
return createInfo<ConceptInfo>(ID);
case BI_FUNCTION_BLOCK_ID:
return createInfo<FunctionInfo>(ID);
case BI_VAR_BLOCK_ID:
return createInfo<VarInfo>(ID);
case BI_FRIEND_BLOCK_ID:
return createInfo<FriendInfo>(ID);
default:
return llvm::createStringError(llvm::inconvertibleErrorCode(),
"cannot create info");
}
}
// Entry point
llvm::Expected<OwningPtrArray<Info>> ClangDocBitcodeReader::readBitcode() {
OwningPtrArray<Info> Infos;
if (auto Err = validateStream())
return std::move(Err);
// Read the top level blocks.
while (!Stream.AtEndOfStream()) {
Expected<unsigned> MaybeCode = Stream.ReadCode();
if (!MaybeCode)
return MaybeCode.takeError();
if (MaybeCode.get() != llvm::bitc::ENTER_SUBBLOCK)
return llvm::createStringError(llvm::inconvertibleErrorCode(),
"no blocks in input");
Expected<unsigned> MaybeID = Stream.ReadSubBlockID();
if (!MaybeID)
return MaybeID.takeError();
unsigned ID = MaybeID.get();
switch (ID) {
// NamedType and Comment blocks should not appear at the top level
case BI_TYPE_BLOCK_ID:
case BI_FIELD_TYPE_BLOCK_ID:
case BI_MEMBER_TYPE_BLOCK_ID:
case BI_COMMENT_BLOCK_ID:
case BI_REFERENCE_BLOCK_ID:
return llvm::createStringError(llvm::inconvertibleErrorCode(),
"invalid top level block");
case BI_NAMESPACE_BLOCK_ID:
case BI_RECORD_BLOCK_ID:
case BI_ENUM_BLOCK_ID:
case BI_TYPEDEF_BLOCK_ID:
case BI_CONCEPT_BLOCK_ID:
case BI_VAR_BLOCK_ID:
case BI_FRIEND_BLOCK_ID:
case BI_FUNCTION_BLOCK_ID: {
auto InfoOrErr = readBlockToInfo(ID);
if (!InfoOrErr)
return InfoOrErr.takeError();
Infos.emplace_back(std::move(InfoOrErr.get()));
continue;
}
case BI_VERSION_BLOCK_ID:
if (auto Err = readBlock(ID, VersionNumber))
return std::move(Err);
continue;
case llvm::bitc::BLOCKINFO_BLOCK_ID:
if (auto Err = readBlockInfoBlock())
return std::move(Err);
continue;
default:
if (llvm::Error Err = Stream.SkipBlock())
return std::move(Err);
continue;
}
}
return std::move(Infos);
}
} // namespace doc
} // namespace clang