//===-- 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/ADT/IndexedMap.h" #include "llvm/ADT/Optional.h" #include "llvm/Support/Error.h" #include "llvm/Support/raw_ostream.h" namespace clang { namespace doc { using Record = llvm::SmallVector; llvm::Error decodeRecord(Record R, llvm::SmallVectorImpl &Field, llvm::StringRef Blob) { Field.assign(Blob.begin(), Blob.end()); return llvm::Error::success(); } llvm::Error decodeRecord(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(); } llvm::Error decodeRecord(Record R, bool &Field, llvm::StringRef Blob) { Field = R[0] != 0; return llvm::Error::success(); } llvm::Error decodeRecord(Record R, int &Field, llvm::StringRef Blob) { if (R[0] > INT_MAX) return llvm::createStringError(llvm::inconvertibleErrorCode(), "integer too large to parse"); Field = (int)R[0]; return llvm::Error::success(); } llvm::Error decodeRecord(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(); default: return llvm::createStringError(llvm::inconvertibleErrorCode(), "invalid value for AccessSpecifier"); } } llvm::Error decodeRecord(Record R, TagTypeKind &Field, llvm::StringRef Blob) { switch (R[0]) { case TTK_Struct: case TTK_Interface: case TTK_Union: case TTK_Class: case TTK_Enum: Field = (TagTypeKind)R[0]; return llvm::Error::success(); default: return llvm::createStringError(llvm::inconvertibleErrorCode(), "invalid value for TagTypeKind"); } } llvm::Error decodeRecord(Record R, llvm::Optional &Field, llvm::StringRef Blob) { if (R[0] > INT_MAX) return llvm::createStringError(llvm::inconvertibleErrorCode(), "integer too large to parse"); Field.emplace((int)R[0], Blob, (bool)R[1]); return llvm::Error::success(); } llvm::Error decodeRecord(Record R, InfoType &Field, llvm::StringRef Blob) { switch (auto IT = static_cast(R[0])) { case InfoType::IT_namespace: case InfoType::IT_record: case InfoType::IT_function: case InfoType::IT_default: case InfoType::IT_enum: Field = IT; return llvm::Error::success(); } return llvm::createStringError(llvm::inconvertibleErrorCode(), "invalid value for InfoType"); } llvm::Error decodeRecord(Record R, FieldId &Field, llvm::StringRef Blob) { switch (auto F = static_cast(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_default: Field = F; return llvm::Error::success(); } return llvm::createStringError(llvm::inconvertibleErrorCode(), "invalid value for FieldId"); } llvm::Error decodeRecord(Record R, llvm::SmallVectorImpl> &Field, llvm::StringRef Blob) { Field.push_back(Blob); return llvm::Error::success(); } llvm::Error decodeRecord(Record R, llvm::SmallVectorImpl &Field, llvm::StringRef Blob) { if (R[0] > INT_MAX) return llvm::createStringError(llvm::inconvertibleErrorCode(), "integer too large to parse"); Field.emplace_back((int)R[0], Blob, (bool)R[1]); return llvm::Error::success(); } llvm::Error parseRecord(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"); } llvm::Error parseRecord(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); default: return llvm::createStringError(llvm::inconvertibleErrorCode(), "invalid field for NamespaceInfo"); } } llvm::Error parseRecord(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); default: return llvm::createStringError(llvm::inconvertibleErrorCode(), "invalid field for RecordInfo"); } } llvm::Error parseRecord(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"); } } llvm::Error parseRecord(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_MEMBER: return decodeRecord(R, I->Members, Blob); case ENUM_SCOPED: return decodeRecord(R, I->Scoped, Blob); default: return llvm::createStringError(llvm::inconvertibleErrorCode(), "invalid field for EnumInfo"); } } llvm::Error parseRecord(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); default: return llvm::createStringError(llvm::inconvertibleErrorCode(), "invalid field for FunctionInfo"); } } llvm::Error parseRecord(Record R, unsigned ID, llvm::StringRef Blob, TypeInfo *I) { return llvm::Error::success(); } llvm::Error parseRecord(Record R, unsigned ID, llvm::StringRef Blob, FieldTypeInfo *I) { switch (ID) { case FIELD_TYPE_NAME: return decodeRecord(R, I->Name, Blob); default: return llvm::createStringError(llvm::inconvertibleErrorCode(), "invalid field for TypeInfo"); } } llvm::Error parseRecord(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); default: return llvm::createStringError(llvm::inconvertibleErrorCode(), "invalid field for MemberTypeInfo"); } } llvm::Error parseRecord(Record R, unsigned ID, llvm::StringRef Blob, CommentInfo *I) { switch (ID) { case COMMENT_KIND: return decodeRecord(R, I->Kind, Blob); 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: return decodeRecord(R, I->AttrKeys, Blob); case COMMENT_ATTRVAL: return decodeRecord(R, I->AttrValues, Blob); case COMMENT_ARG: return decodeRecord(R, I->Args, Blob); 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"); } } llvm::Error parseRecord(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_TYPE: return decodeRecord(R, I->RefType, Blob); case REFERENCE_PATH: return decodeRecord(R, I->Path, Blob); case REFERENCE_IS_IN_GLOBAL_NAMESPACE: return decodeRecord(R, I->IsInGlobalNamespace, Blob); case REFERENCE_FIELD: return decodeRecord(R, F, Blob); default: return llvm::createStringError(llvm::inconvertibleErrorCode(), "invalid field for Reference"); } } template llvm::Expected getCommentInfo(T I) { return llvm::createStringError(llvm::inconvertibleErrorCode(), "invalid type cannot contain CommentInfo"); } template <> llvm::Expected getCommentInfo(FunctionInfo *I) { I->Description.emplace_back(); return &I->Description.back(); } template <> llvm::Expected getCommentInfo(NamespaceInfo *I) { I->Description.emplace_back(); return &I->Description.back(); } template <> llvm::Expected getCommentInfo(RecordInfo *I) { I->Description.emplace_back(); return &I->Description.back(); } template <> llvm::Expected getCommentInfo(EnumInfo *I) { I->Description.emplace_back(); return &I->Description.back(); } template <> llvm::Expected getCommentInfo(CommentInfo *I) { I->Children.emplace_back(std::make_unique()); return I->Children.back().get(); } template <> llvm::Expected getCommentInfo(std::unique_ptr &I) { return getCommentInfo(I.get()); } template 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 addReference(T I, Reference &&R, FieldId F) { return llvm::createStringError(llvm::inconvertibleErrorCode(), "invalid type cannot contain 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(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: I->ChildNamespaces.emplace_back(std::move(R)); return llvm::Error::success(); case FieldId::F_child_record: I->ChildRecords.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->ChildRecords.emplace_back(std::move(R)); return llvm::Error::success(); default: return llvm::createStringError(llvm::inconvertibleErrorCode(), "invalid type cannot contain Reference"); } } template void addChild(T I, ChildInfoType &&R) { llvm::errs() << "invalid child type for info"; exit(1); } template <> void addChild(NamespaceInfo *I, FunctionInfo &&R) { I->ChildFunctions.emplace_back(std::move(R)); } template <> void addChild(NamespaceInfo *I, EnumInfo &&R) { I->ChildEnums.emplace_back(std::move(R)); } template <> void addChild(RecordInfo *I, FunctionInfo &&R) { I->ChildFunctions.emplace_back(std::move(R)); } template <> void addChild(RecordInfo *I, EnumInfo &&R) { I->ChildEnums.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->ChildFunctions.emplace_back(std::move(R)); } // Read records from bitcode into a given info. template llvm::Error ClangDocBitcodeReader::readRecord(unsigned ID, T I) { Record R; llvm::StringRef Blob; llvm::Expected 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) { Record R; llvm::StringRef Blob; llvm::Expected 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 llvm::Error ClangDocBitcodeReader::readBlock(unsigned ID, T I) { if (llvm::Error Err = Stream.EnterSubBlock(ID)) return Err; while (true) { unsigned BlockOrCode = 0; Cursor Res = skipUntilRecordOrBlock(BlockOrCode); switch (Res) { 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::readSubBlock(unsigned ID, T I) { switch (ID) { // Blocks can only have Comment, Reference, TypeInfo, FunctionInfo, or // EnumInfo subblocks 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: { TypeInfo TI; if (auto Err = readBlock(ID, &TI)) return Err; if (auto Err = addTypeInfo(I, std::move(TI))) return Err; return llvm::Error::success(); } case BI_FIELD_TYPE_BLOCK_ID: { FieldTypeInfo TI; if (auto Err = readBlock(ID, &TI)) return Err; if (auto Err = addTypeInfo(I, std::move(TI))) return Err; return llvm::Error::success(); } case BI_MEMBER_TYPE_BLOCK_ID: { MemberTypeInfo TI; if (auto Err = readBlock(ID, &TI)) return Err; if (auto Err = addTypeInfo(I, std::move(TI))) return Err; return llvm::Error::success(); } 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: { FunctionInfo F; if (auto Err = readBlock(ID, &F)) return Err; addChild(I, std::move(F)); return llvm::Error::success(); } case BI_BASE_RECORD_BLOCK_ID: { BaseRecordInfo BR; if (auto Err = readBlock(ID, &BR)) return Err; addChild(I, std::move(BR)); return llvm::Error::success(); } case BI_ENUM_BLOCK_ID: { EnumInfo E; if (auto Err = readBlock(ID, &E)) return Err; addChild(I, std::move(E)); return llvm::Error::success(); } default: return llvm::createStringError(llvm::inconvertibleErrorCode(), "invalid subblock type"); } } ClangDocBitcodeReader::Cursor ClangDocBitcodeReader::skipUntilRecordOrBlock(unsigned &BlockOrRecordID) { BlockOrRecordID = 0; while (!Stream.AtEndOfStream()) { Expected MaybeCode = Stream.ReadCode(); if (!MaybeCode) { // FIXME this drops the error on the floor. consumeError(MaybeCode.takeError()); return Cursor::BadBlock; } unsigned Code = MaybeCode.get(); if (Code >= static_cast(llvm::bitc::FIRST_APPLICATION_ABBREV)) { BlockOrRecordID = Code; return Cursor::Record; } switch (static_cast(Code)) { case llvm::bitc::ENTER_SUBBLOCK: if (Expected MaybeID = Stream.ReadSubBlockID()) BlockOrRecordID = MaybeID.get(); else { // FIXME this drops the error on the floor. consumeError(MaybeID.takeError()); } return Cursor::BlockBegin; case llvm::bitc::END_BLOCK: if (Stream.ReadBlockEnd()) return Cursor::BadBlock; return Cursor::BlockEnd; case llvm::bitc::DEFINE_ABBREV: if (llvm::Error Err = Stream.ReadAbbrevRecord()) { // FIXME this drops the error on the floor. consumeError(std::move(Err)); } continue; case llvm::bitc::UNABBREV_RECORD: return Cursor::BadBlock; 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 MaybeRead = Stream.Read(8); if (!MaybeRead) return MaybeRead.takeError(); else if (MaybeRead.get() != BitCodeConstants::Signature[Idx]) return llvm::createStringError(llvm::inconvertibleErrorCode(), "invalid bitcode signature"); } return llvm::Error::success(); } llvm::Error ClangDocBitcodeReader::readBlockInfoBlock() { Expected> MaybeBlockInfo = Stream.ReadBlockInfoBlock(); if (!MaybeBlockInfo) return MaybeBlockInfo.takeError(); else BlockInfo = MaybeBlockInfo.get(); if (!BlockInfo) return llvm::createStringError(llvm::inconvertibleErrorCode(), "unable to parse BlockInfoBlock"); Stream.setBlockInfo(&*BlockInfo); return llvm::Error::success(); } template llvm::Expected> ClangDocBitcodeReader::createInfo(unsigned ID) { std::unique_ptr I = std::make_unique(); if (auto Err = readBlock(ID, static_cast(I.get()))) return std::move(Err); return std::unique_ptr{std::move(I)}; } llvm::Expected> ClangDocBitcodeReader::readBlockToInfo(unsigned ID) { switch (ID) { case BI_NAMESPACE_BLOCK_ID: return createInfo(ID); case BI_RECORD_BLOCK_ID: return createInfo(ID); case BI_ENUM_BLOCK_ID: return createInfo(ID); case BI_FUNCTION_BLOCK_ID: return createInfo(ID); default: return llvm::createStringError(llvm::inconvertibleErrorCode(), "cannot create info"); } } // Entry point llvm::Expected>> ClangDocBitcodeReader::readBitcode() { std::vector> Infos; if (auto Err = validateStream()) return std::move(Err); // Read the top level blocks. while (!Stream.AtEndOfStream()) { Expected 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 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_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()) { // FIXME this drops the error on the floor. consumeError(std::move(Err)); } continue; } } return std::move(Infos); } } // namespace doc } // namespace clang