1 //===- SerializedDiagnosticReader.cpp - Reads diagnostics -----------------===// 2 // 3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4 // See https://llvm.org/LICENSE.txt for license information. 5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6 // 7 //===----------------------------------------------------------------------===// 8 9 #include "clang/Frontend/SerializedDiagnosticReader.h" 10 #include "clang/Basic/FileManager.h" 11 #include "clang/Basic/FileSystemOptions.h" 12 #include "clang/Frontend/SerializedDiagnostics.h" 13 #include "llvm/ADT/SmallVector.h" 14 #include "llvm/Bitstream/BitstreamReader.h" 15 #include "llvm/Support/Compiler.h" 16 #include "llvm/Support/ErrorHandling.h" 17 #include "llvm/Support/ErrorOr.h" 18 #include "llvm/Support/ManagedStatic.h" 19 #include <cstdint> 20 #include <optional> 21 #include <system_error> 22 23 using namespace clang; 24 using namespace serialized_diags; 25 26 std::error_code SerializedDiagnosticReader::readDiagnostics(StringRef File) { 27 // Open the diagnostics file. 28 FileSystemOptions FO; 29 FileManager FileMgr(FO); 30 31 auto Buffer = FileMgr.getBufferForFile(File); 32 if (!Buffer) 33 return SDError::CouldNotLoad; 34 35 llvm::BitstreamCursor Stream(**Buffer); 36 std::optional<llvm::BitstreamBlockInfo> BlockInfo; 37 38 if (Stream.AtEndOfStream()) 39 return SDError::InvalidSignature; 40 41 // Sniff for the signature. 42 for (unsigned char C : {'D', 'I', 'A', 'G'}) { 43 if (Expected<llvm::SimpleBitstreamCursor::word_t> Res = Stream.Read(8)) { 44 if (Res.get() == C) 45 continue; 46 } else { 47 // FIXME this drops the error on the floor. 48 consumeError(Res.takeError()); 49 } 50 return SDError::InvalidSignature; 51 } 52 53 // Read the top level blocks. 54 while (!Stream.AtEndOfStream()) { 55 if (Expected<unsigned> Res = Stream.ReadCode()) { 56 if (Res.get() != llvm::bitc::ENTER_SUBBLOCK) 57 return SDError::InvalidDiagnostics; 58 } else { 59 // FIXME this drops the error on the floor. 60 consumeError(Res.takeError()); 61 return SDError::InvalidDiagnostics; 62 } 63 64 std::error_code EC; 65 Expected<unsigned> MaybeSubBlockID = Stream.ReadSubBlockID(); 66 if (!MaybeSubBlockID) { 67 // FIXME this drops the error on the floor. 68 consumeError(MaybeSubBlockID.takeError()); 69 return SDError::InvalidDiagnostics; 70 } 71 72 switch (MaybeSubBlockID.get()) { 73 case llvm::bitc::BLOCKINFO_BLOCK_ID: { 74 Expected<std::optional<llvm::BitstreamBlockInfo>> MaybeBlockInfo = 75 Stream.ReadBlockInfoBlock(); 76 if (!MaybeBlockInfo) { 77 // FIXME this drops the error on the floor. 78 consumeError(MaybeBlockInfo.takeError()); 79 return SDError::InvalidDiagnostics; 80 } 81 BlockInfo = std::move(MaybeBlockInfo.get()); 82 } 83 if (!BlockInfo) 84 return SDError::MalformedBlockInfoBlock; 85 Stream.setBlockInfo(&*BlockInfo); 86 continue; 87 case BLOCK_META: 88 if ((EC = readMetaBlock(Stream))) 89 return EC; 90 continue; 91 case BLOCK_DIAG: 92 if ((EC = readDiagnosticBlock(Stream))) 93 return EC; 94 continue; 95 default: 96 if (llvm::Error Err = Stream.SkipBlock()) { 97 // FIXME this drops the error on the floor. 98 consumeError(std::move(Err)); 99 return SDError::MalformedTopLevelBlock; 100 } 101 continue; 102 } 103 } 104 return {}; 105 } 106 107 enum class SerializedDiagnosticReader::Cursor { 108 Record = 1, 109 BlockEnd, 110 BlockBegin 111 }; 112 113 llvm::ErrorOr<SerializedDiagnosticReader::Cursor> 114 SerializedDiagnosticReader::skipUntilRecordOrBlock( 115 llvm::BitstreamCursor &Stream, unsigned &BlockOrRecordID) { 116 BlockOrRecordID = 0; 117 118 while (!Stream.AtEndOfStream()) { 119 unsigned Code; 120 if (Expected<unsigned> Res = Stream.ReadCode()) 121 Code = Res.get(); 122 else 123 return llvm::errorToErrorCode(Res.takeError()); 124 125 if (Code >= static_cast<unsigned>(llvm::bitc::FIRST_APPLICATION_ABBREV)) { 126 // We found a record. 127 BlockOrRecordID = Code; 128 return Cursor::Record; 129 } 130 switch (static_cast<llvm::bitc::FixedAbbrevIDs>(Code)) { 131 case llvm::bitc::ENTER_SUBBLOCK: 132 if (Expected<unsigned> Res = Stream.ReadSubBlockID()) 133 BlockOrRecordID = Res.get(); 134 else 135 return llvm::errorToErrorCode(Res.takeError()); 136 return Cursor::BlockBegin; 137 138 case llvm::bitc::END_BLOCK: 139 if (Stream.ReadBlockEnd()) 140 return SDError::InvalidDiagnostics; 141 return Cursor::BlockEnd; 142 143 case llvm::bitc::DEFINE_ABBREV: 144 if (llvm::Error Err = Stream.ReadAbbrevRecord()) 145 return llvm::errorToErrorCode(std::move(Err)); 146 continue; 147 148 case llvm::bitc::UNABBREV_RECORD: 149 return SDError::UnsupportedConstruct; 150 151 case llvm::bitc::FIRST_APPLICATION_ABBREV: 152 llvm_unreachable("Unexpected abbrev id."); 153 } 154 } 155 156 return SDError::InvalidDiagnostics; 157 } 158 159 std::error_code 160 SerializedDiagnosticReader::readMetaBlock(llvm::BitstreamCursor &Stream) { 161 if (llvm::Error Err = 162 Stream.EnterSubBlock(clang::serialized_diags::BLOCK_META)) { 163 // FIXME this drops the error on the floor. 164 consumeError(std::move(Err)); 165 return SDError::MalformedMetadataBlock; 166 } 167 168 bool VersionChecked = false; 169 170 while (true) { 171 unsigned BlockOrCode = 0; 172 llvm::ErrorOr<Cursor> Res = skipUntilRecordOrBlock(Stream, BlockOrCode); 173 if (!Res) 174 Res.getError(); 175 176 switch (Res.get()) { 177 case Cursor::Record: 178 break; 179 case Cursor::BlockBegin: 180 if (llvm::Error Err = Stream.SkipBlock()) { 181 // FIXME this drops the error on the floor. 182 consumeError(std::move(Err)); 183 return SDError::MalformedMetadataBlock; 184 } 185 [[fallthrough]]; 186 case Cursor::BlockEnd: 187 if (!VersionChecked) 188 return SDError::MissingVersion; 189 return {}; 190 } 191 192 SmallVector<uint64_t, 1> Record; 193 Expected<unsigned> MaybeRecordID = Stream.readRecord(BlockOrCode, Record); 194 if (!MaybeRecordID) 195 return errorToErrorCode(MaybeRecordID.takeError()); 196 unsigned RecordID = MaybeRecordID.get(); 197 198 if (RecordID == RECORD_VERSION) { 199 if (Record.size() < 1) 200 return SDError::MissingVersion; 201 if (Record[0] > VersionNumber) 202 return SDError::VersionMismatch; 203 VersionChecked = true; 204 } 205 } 206 } 207 208 std::error_code 209 SerializedDiagnosticReader::readDiagnosticBlock(llvm::BitstreamCursor &Stream) { 210 if (llvm::Error Err = 211 Stream.EnterSubBlock(clang::serialized_diags::BLOCK_DIAG)) { 212 // FIXME this drops the error on the floor. 213 consumeError(std::move(Err)); 214 return SDError::MalformedDiagnosticBlock; 215 } 216 217 std::error_code EC; 218 if ((EC = visitStartOfDiagnostic())) 219 return EC; 220 221 SmallVector<uint64_t, 16> Record; 222 while (true) { 223 unsigned BlockOrCode = 0; 224 llvm::ErrorOr<Cursor> Res = skipUntilRecordOrBlock(Stream, BlockOrCode); 225 if (!Res) 226 Res.getError(); 227 228 switch (Res.get()) { 229 case Cursor::BlockBegin: 230 // The only blocks we care about are subdiagnostics. 231 if (BlockOrCode == serialized_diags::BLOCK_DIAG) { 232 if ((EC = readDiagnosticBlock(Stream))) 233 return EC; 234 } else if (llvm::Error Err = Stream.SkipBlock()) { 235 // FIXME this drops the error on the floor. 236 consumeError(std::move(Err)); 237 return SDError::MalformedSubBlock; 238 } 239 continue; 240 case Cursor::BlockEnd: 241 if ((EC = visitEndOfDiagnostic())) 242 return EC; 243 return {}; 244 case Cursor::Record: 245 break; 246 } 247 248 // Read the record. 249 Record.clear(); 250 StringRef Blob; 251 Expected<unsigned> MaybeRecID = 252 Stream.readRecord(BlockOrCode, Record, &Blob); 253 if (!MaybeRecID) 254 return errorToErrorCode(MaybeRecID.takeError()); 255 unsigned RecID = MaybeRecID.get(); 256 257 if (RecID < serialized_diags::RECORD_FIRST || 258 RecID > serialized_diags::RECORD_LAST) 259 continue; 260 261 switch ((RecordIDs)RecID) { 262 case RECORD_CATEGORY: 263 // A category has ID and name size. 264 if (Record.size() != 2) 265 return SDError::MalformedDiagnosticRecord; 266 if ((EC = visitCategoryRecord(Record[0], Blob))) 267 return EC; 268 continue; 269 case RECORD_DIAG: 270 // A diagnostic has severity, location (4), category, flag, and message 271 // size. 272 if (Record.size() != 8) 273 return SDError::MalformedDiagnosticRecord; 274 if ((EC = visitDiagnosticRecord( 275 Record[0], Location(Record[1], Record[2], Record[3], Record[4]), 276 Record[5], Record[6], Blob))) 277 return EC; 278 continue; 279 case RECORD_DIAG_FLAG: 280 // A diagnostic flag has ID and name size. 281 if (Record.size() != 2) 282 return SDError::MalformedDiagnosticRecord; 283 if ((EC = visitDiagFlagRecord(Record[0], Blob))) 284 return EC; 285 continue; 286 case RECORD_FILENAME: 287 // A filename has ID, size, timestamp, and name size. The size and 288 // timestamp are legacy fields that are always zero these days. 289 if (Record.size() != 4) 290 return SDError::MalformedDiagnosticRecord; 291 if ((EC = visitFilenameRecord(Record[0], Record[1], Record[2], Blob))) 292 return EC; 293 continue; 294 case RECORD_FIXIT: 295 // A fixit has two locations (4 each) and message size. 296 if (Record.size() != 9) 297 return SDError::MalformedDiagnosticRecord; 298 if ((EC = visitFixitRecord( 299 Location(Record[0], Record[1], Record[2], Record[3]), 300 Location(Record[4], Record[5], Record[6], Record[7]), Blob))) 301 return EC; 302 continue; 303 case RECORD_SOURCE_RANGE: 304 // A source range is two locations (4 each). 305 if (Record.size() != 8) 306 return SDError::MalformedDiagnosticRecord; 307 if ((EC = visitSourceRangeRecord( 308 Location(Record[0], Record[1], Record[2], Record[3]), 309 Location(Record[4], Record[5], Record[6], Record[7])))) 310 return EC; 311 continue; 312 case RECORD_VERSION: 313 // A version is just a number. 314 if (Record.size() != 1) 315 return SDError::MalformedDiagnosticRecord; 316 if ((EC = visitVersionRecord(Record[0]))) 317 return EC; 318 continue; 319 } 320 } 321 } 322 323 namespace { 324 325 class SDErrorCategoryType final : public std::error_category { 326 const char *name() const noexcept override { 327 return "clang.serialized_diags"; 328 } 329 330 std::string message(int IE) const override { 331 auto E = static_cast<SDError>(IE); 332 switch (E) { 333 case SDError::CouldNotLoad: 334 return "Failed to open diagnostics file"; 335 case SDError::InvalidSignature: 336 return "Invalid diagnostics signature"; 337 case SDError::InvalidDiagnostics: 338 return "Parse error reading diagnostics"; 339 case SDError::MalformedTopLevelBlock: 340 return "Malformed block at top-level of diagnostics"; 341 case SDError::MalformedSubBlock: 342 return "Malformed sub-block in a diagnostic"; 343 case SDError::MalformedBlockInfoBlock: 344 return "Malformed BlockInfo block"; 345 case SDError::MalformedMetadataBlock: 346 return "Malformed Metadata block"; 347 case SDError::MalformedDiagnosticBlock: 348 return "Malformed Diagnostic block"; 349 case SDError::MalformedDiagnosticRecord: 350 return "Malformed Diagnostic record"; 351 case SDError::MissingVersion: 352 return "No version provided in diagnostics"; 353 case SDError::VersionMismatch: 354 return "Unsupported diagnostics version"; 355 case SDError::UnsupportedConstruct: 356 return "Bitcode constructs that are not supported in diagnostics appear"; 357 case SDError::HandlerFailed: 358 return "Generic error occurred while handling a record"; 359 } 360 llvm_unreachable("Unknown error type!"); 361 } 362 }; 363 364 } // namespace 365 366 static llvm::ManagedStatic<SDErrorCategoryType> ErrorCategory; 367 const std::error_category &clang::serialized_diags::SDErrorCategory() { 368 return *ErrorCategory; 369 } 370