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