1341825Sdim//===- SerializedDiagnosticReader.cpp - Reads diagnostics -----------------===//
2277325Sdim//
3353358Sdim// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4353358Sdim// See https://llvm.org/LICENSE.txt for license information.
5353358Sdim// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6277325Sdim//
7277325Sdim//===----------------------------------------------------------------------===//
8277325Sdim
9277325Sdim#include "clang/Frontend/SerializedDiagnosticReader.h"
10277325Sdim#include "clang/Basic/FileManager.h"
11341825Sdim#include "clang/Basic/FileSystemOptions.h"
12277325Sdim#include "clang/Frontend/SerializedDiagnostics.h"
13341825Sdim#include "llvm/ADT/Optional.h"
14341825Sdim#include "llvm/ADT/SmallVector.h"
15341825Sdim#include "llvm/ADT/StringRef.h"
16353358Sdim#include "llvm/Bitstream/BitCodes.h"
17353358Sdim#include "llvm/Bitstream/BitstreamReader.h"
18341825Sdim#include "llvm/Support/Compiler.h"
19341825Sdim#include "llvm/Support/ErrorHandling.h"
20341825Sdim#include "llvm/Support/ErrorOr.h"
21277325Sdim#include "llvm/Support/ManagedStatic.h"
22341825Sdim#include <cstdint>
23341825Sdim#include <system_error>
24277325Sdim
25277325Sdimusing namespace clang;
26341825Sdimusing namespace serialized_diags;
27277325Sdim
28277325Sdimstd::error_code SerializedDiagnosticReader::readDiagnostics(StringRef File) {
29277325Sdim  // Open the diagnostics file.
30277325Sdim  FileSystemOptions FO;
31277325Sdim  FileManager FileMgr(FO);
32277325Sdim
33277325Sdim  auto Buffer = FileMgr.getBufferForFile(File);
34277325Sdim  if (!Buffer)
35277325Sdim    return SDError::CouldNotLoad;
36277325Sdim
37314564Sdim  llvm::BitstreamCursor Stream(**Buffer);
38314564Sdim  Optional<llvm::BitstreamBlockInfo> BlockInfo;
39277325Sdim
40321369Sdim  if (Stream.AtEndOfStream())
41321369Sdim    return SDError::InvalidSignature;
42321369Sdim
43277325Sdim  // Sniff for the signature.
44353358Sdim  for (unsigned char C : {'D', 'I', 'A', 'G'}) {
45353358Sdim    if (Expected<llvm::SimpleBitstreamCursor::word_t> Res = Stream.Read(8)) {
46353358Sdim      if (Res.get() == C)
47353358Sdim        continue;
48353358Sdim    } else {
49353358Sdim      // FIXME this drops the error on the floor.
50353358Sdim      consumeError(Res.takeError());
51353358Sdim    }
52277325Sdim    return SDError::InvalidSignature;
53353358Sdim  }
54277325Sdim
55277325Sdim  // Read the top level blocks.
56277325Sdim  while (!Stream.AtEndOfStream()) {
57353358Sdim    if (Expected<unsigned> Res = Stream.ReadCode()) {
58353358Sdim      if (Res.get() != llvm::bitc::ENTER_SUBBLOCK)
59353358Sdim        return SDError::InvalidDiagnostics;
60353358Sdim    } else {
61353358Sdim      // FIXME this drops the error on the floor.
62353358Sdim      consumeError(Res.takeError());
63277325Sdim      return SDError::InvalidDiagnostics;
64353358Sdim    }
65277325Sdim
66277325Sdim    std::error_code EC;
67353358Sdim    Expected<unsigned> MaybeSubBlockID = Stream.ReadSubBlockID();
68353358Sdim    if (!MaybeSubBlockID) {
69353358Sdim      // FIXME this drops the error on the floor.
70353358Sdim      consumeError(MaybeSubBlockID.takeError());
71353358Sdim      return SDError::InvalidDiagnostics;
72353358Sdim    }
73353358Sdim
74353358Sdim    switch (MaybeSubBlockID.get()) {
75353358Sdim    case llvm::bitc::BLOCKINFO_BLOCK_ID: {
76353358Sdim      Expected<Optional<llvm::BitstreamBlockInfo>> MaybeBlockInfo =
77353358Sdim          Stream.ReadBlockInfoBlock();
78353358Sdim      if (!MaybeBlockInfo) {
79353358Sdim        // FIXME this drops the error on the floor.
80353358Sdim        consumeError(MaybeBlockInfo.takeError());
81353358Sdim        return SDError::InvalidDiagnostics;
82353358Sdim      }
83353358Sdim      BlockInfo = std::move(MaybeBlockInfo.get());
84353358Sdim    }
85314564Sdim      if (!BlockInfo)
86277325Sdim        return SDError::MalformedBlockInfoBlock;
87314564Sdim      Stream.setBlockInfo(&*BlockInfo);
88277325Sdim      continue;
89277325Sdim    case BLOCK_META:
90277325Sdim      if ((EC = readMetaBlock(Stream)))
91277325Sdim        return EC;
92277325Sdim      continue;
93277325Sdim    case BLOCK_DIAG:
94277325Sdim      if ((EC = readDiagnosticBlock(Stream)))
95277325Sdim        return EC;
96277325Sdim      continue;
97277325Sdim    default:
98353358Sdim      if (llvm::Error Err = Stream.SkipBlock()) {
99353358Sdim        // FIXME this drops the error on the floor.
100353358Sdim        consumeError(std::move(Err));
101277325Sdim        return SDError::MalformedTopLevelBlock;
102353358Sdim      }
103277325Sdim      continue;
104277325Sdim    }
105277325Sdim  }
106341825Sdim  return {};
107277325Sdim}
108277325Sdim
109277325Sdimenum class SerializedDiagnosticReader::Cursor {
110277325Sdim  Record = 1,
111277325Sdim  BlockEnd,
112277325Sdim  BlockBegin
113277325Sdim};
114277325Sdim
115277325Sdimllvm::ErrorOr<SerializedDiagnosticReader::Cursor>
116277325SdimSerializedDiagnosticReader::skipUntilRecordOrBlock(
117277325Sdim    llvm::BitstreamCursor &Stream, unsigned &BlockOrRecordID) {
118277325Sdim  BlockOrRecordID = 0;
119277325Sdim
120277325Sdim  while (!Stream.AtEndOfStream()) {
121353358Sdim    unsigned Code;
122353358Sdim    if (Expected<unsigned> Res = Stream.ReadCode())
123353358Sdim      Code = Res.get();
124353358Sdim    else
125353358Sdim      return llvm::errorToErrorCode(Res.takeError());
126277325Sdim
127353358Sdim    if (Code >= static_cast<unsigned>(llvm::bitc::FIRST_APPLICATION_ABBREV)) {
128353358Sdim      // We found a record.
129353358Sdim      BlockOrRecordID = Code;
130353358Sdim      return Cursor::Record;
131353358Sdim    }
132353358Sdim    switch (static_cast<llvm::bitc::FixedAbbrevIDs>(Code)) {
133277325Sdim    case llvm::bitc::ENTER_SUBBLOCK:
134353358Sdim      if (Expected<unsigned> Res = Stream.ReadSubBlockID())
135353358Sdim        BlockOrRecordID = Res.get();
136353358Sdim      else
137353358Sdim        return llvm::errorToErrorCode(Res.takeError());
138277325Sdim      return Cursor::BlockBegin;
139277325Sdim
140277325Sdim    case llvm::bitc::END_BLOCK:
141277325Sdim      if (Stream.ReadBlockEnd())
142277325Sdim        return SDError::InvalidDiagnostics;
143277325Sdim      return Cursor::BlockEnd;
144277325Sdim
145277325Sdim    case llvm::bitc::DEFINE_ABBREV:
146353358Sdim      if (llvm::Error Err = Stream.ReadAbbrevRecord())
147353358Sdim        return llvm::errorToErrorCode(std::move(Err));
148277325Sdim      continue;
149277325Sdim
150277325Sdim    case llvm::bitc::UNABBREV_RECORD:
151277325Sdim      return SDError::UnsupportedConstruct;
152277325Sdim
153353358Sdim    case llvm::bitc::FIRST_APPLICATION_ABBREV:
154353358Sdim      llvm_unreachable("Unexpected abbrev id.");
155277325Sdim    }
156277325Sdim  }
157277325Sdim
158277325Sdim  return SDError::InvalidDiagnostics;
159277325Sdim}
160277325Sdim
161277325Sdimstd::error_code
162277325SdimSerializedDiagnosticReader::readMetaBlock(llvm::BitstreamCursor &Stream) {
163353358Sdim  if (llvm::Error Err =
164353358Sdim          Stream.EnterSubBlock(clang::serialized_diags::BLOCK_META)) {
165353358Sdim    // FIXME this drops the error on the floor.
166353358Sdim    consumeError(std::move(Err));
167277325Sdim    return SDError::MalformedMetadataBlock;
168353358Sdim  }
169277325Sdim
170277325Sdim  bool VersionChecked = false;
171277325Sdim
172277325Sdim  while (true) {
173277325Sdim    unsigned BlockOrCode = 0;
174277325Sdim    llvm::ErrorOr<Cursor> Res = skipUntilRecordOrBlock(Stream, BlockOrCode);
175277325Sdim    if (!Res)
176277325Sdim      Res.getError();
177277325Sdim
178277325Sdim    switch (Res.get()) {
179277325Sdim    case Cursor::Record:
180277325Sdim      break;
181277325Sdim    case Cursor::BlockBegin:
182353358Sdim      if (llvm::Error Err = Stream.SkipBlock()) {
183353358Sdim        // FIXME this drops the error on the floor.
184353358Sdim        consumeError(std::move(Err));
185277325Sdim        return SDError::MalformedMetadataBlock;
186353358Sdim      }
187321369Sdim      LLVM_FALLTHROUGH;
188277325Sdim    case Cursor::BlockEnd:
189277325Sdim      if (!VersionChecked)
190277325Sdim        return SDError::MissingVersion;
191341825Sdim      return {};
192277325Sdim    }
193277325Sdim
194277325Sdim    SmallVector<uint64_t, 1> Record;
195353358Sdim    Expected<unsigned> MaybeRecordID = Stream.readRecord(BlockOrCode, Record);
196353358Sdim    if (!MaybeRecordID)
197353358Sdim      return errorToErrorCode(MaybeRecordID.takeError());
198353358Sdim    unsigned RecordID = MaybeRecordID.get();
199277325Sdim
200277325Sdim    if (RecordID == RECORD_VERSION) {
201277325Sdim      if (Record.size() < 1)
202277325Sdim        return SDError::MissingVersion;
203277325Sdim      if (Record[0] > VersionNumber)
204277325Sdim        return SDError::VersionMismatch;
205277325Sdim      VersionChecked = true;
206277325Sdim    }
207277325Sdim  }
208277325Sdim}
209277325Sdim
210277325Sdimstd::error_code
211277325SdimSerializedDiagnosticReader::readDiagnosticBlock(llvm::BitstreamCursor &Stream) {
212353358Sdim  if (llvm::Error Err =
213353358Sdim          Stream.EnterSubBlock(clang::serialized_diags::BLOCK_DIAG)) {
214353358Sdim    // FIXME this drops the error on the floor.
215353358Sdim    consumeError(std::move(Err));
216277325Sdim    return SDError::MalformedDiagnosticBlock;
217353358Sdim  }
218277325Sdim
219277325Sdim  std::error_code EC;
220277325Sdim  if ((EC = visitStartOfDiagnostic()))
221277325Sdim    return EC;
222277325Sdim
223277325Sdim  SmallVector<uint64_t, 16> Record;
224277325Sdim  while (true) {
225277325Sdim    unsigned BlockOrCode = 0;
226277325Sdim    llvm::ErrorOr<Cursor> Res = skipUntilRecordOrBlock(Stream, BlockOrCode);
227277325Sdim    if (!Res)
228277325Sdim      Res.getError();
229277325Sdim
230277325Sdim    switch (Res.get()) {
231277325Sdim    case Cursor::BlockBegin:
232277325Sdim      // The only blocks we care about are subdiagnostics.
233277325Sdim      if (BlockOrCode == serialized_diags::BLOCK_DIAG) {
234277325Sdim        if ((EC = readDiagnosticBlock(Stream)))
235277325Sdim          return EC;
236353358Sdim      } else if (llvm::Error Err = Stream.SkipBlock()) {
237353358Sdim        // FIXME this drops the error on the floor.
238353358Sdim        consumeError(std::move(Err));
239277325Sdim        return SDError::MalformedSubBlock;
240353358Sdim      }
241277325Sdim      continue;
242277325Sdim    case Cursor::BlockEnd:
243277325Sdim      if ((EC = visitEndOfDiagnostic()))
244277325Sdim        return EC;
245341825Sdim      return {};
246277325Sdim    case Cursor::Record:
247277325Sdim      break;
248277325Sdim    }
249277325Sdim
250277325Sdim    // Read the record.
251277325Sdim    Record.clear();
252277325Sdim    StringRef Blob;
253353358Sdim    Expected<unsigned> MaybeRecID =
254353358Sdim        Stream.readRecord(BlockOrCode, Record, &Blob);
255353358Sdim    if (!MaybeRecID)
256353358Sdim      return errorToErrorCode(MaybeRecID.takeError());
257353358Sdim    unsigned RecID = MaybeRecID.get();
258277325Sdim
259277325Sdim    if (RecID < serialized_diags::RECORD_FIRST ||
260277325Sdim        RecID > serialized_diags::RECORD_LAST)
261277325Sdim      continue;
262277325Sdim
263277325Sdim    switch ((RecordIDs)RecID) {
264277325Sdim    case RECORD_CATEGORY:
265277325Sdim      // A category has ID and name size.
266277325Sdim      if (Record.size() != 2)
267277325Sdim        return SDError::MalformedDiagnosticRecord;
268277325Sdim      if ((EC = visitCategoryRecord(Record[0], Blob)))
269277325Sdim        return EC;
270277325Sdim      continue;
271277325Sdim    case RECORD_DIAG:
272277325Sdim      // A diagnostic has severity, location (4), category, flag, and message
273277325Sdim      // size.
274277325Sdim      if (Record.size() != 8)
275277325Sdim        return SDError::MalformedDiagnosticRecord;
276277325Sdim      if ((EC = visitDiagnosticRecord(
277277325Sdim               Record[0], Location(Record[1], Record[2], Record[3], Record[4]),
278277325Sdim               Record[5], Record[6], Blob)))
279277325Sdim        return EC;
280277325Sdim      continue;
281277325Sdim    case RECORD_DIAG_FLAG:
282277325Sdim      // A diagnostic flag has ID and name size.
283277325Sdim      if (Record.size() != 2)
284277325Sdim        return SDError::MalformedDiagnosticRecord;
285277325Sdim      if ((EC = visitDiagFlagRecord(Record[0], Blob)))
286277325Sdim        return EC;
287277325Sdim      continue;
288277325Sdim    case RECORD_FILENAME:
289277325Sdim      // A filename has ID, size, timestamp, and name size. The size and
290277325Sdim      // timestamp are legacy fields that are always zero these days.
291277325Sdim      if (Record.size() != 4)
292277325Sdim        return SDError::MalformedDiagnosticRecord;
293277325Sdim      if ((EC = visitFilenameRecord(Record[0], Record[1], Record[2], Blob)))
294277325Sdim        return EC;
295277325Sdim      continue;
296277325Sdim    case RECORD_FIXIT:
297277325Sdim      // A fixit has two locations (4 each) and message size.
298277325Sdim      if (Record.size() != 9)
299277325Sdim        return SDError::MalformedDiagnosticRecord;
300277325Sdim      if ((EC = visitFixitRecord(
301277325Sdim               Location(Record[0], Record[1], Record[2], Record[3]),
302277325Sdim               Location(Record[4], Record[5], Record[6], Record[7]), Blob)))
303277325Sdim        return EC;
304277325Sdim      continue;
305277325Sdim    case RECORD_SOURCE_RANGE:
306277325Sdim      // A source range is two locations (4 each).
307277325Sdim      if (Record.size() != 8)
308277325Sdim        return SDError::MalformedDiagnosticRecord;
309277325Sdim      if ((EC = visitSourceRangeRecord(
310277325Sdim               Location(Record[0], Record[1], Record[2], Record[3]),
311277325Sdim               Location(Record[4], Record[5], Record[6], Record[7]))))
312277325Sdim        return EC;
313277325Sdim      continue;
314277325Sdim    case RECORD_VERSION:
315277325Sdim      // A version is just a number.
316277325Sdim      if (Record.size() != 1)
317277325Sdim        return SDError::MalformedDiagnosticRecord;
318277325Sdim      if ((EC = visitVersionRecord(Record[0])))
319277325Sdim        return EC;
320277325Sdim      continue;
321277325Sdim    }
322277325Sdim  }
323277325Sdim}
324277325Sdim
325277325Sdimnamespace {
326341825Sdim
327277325Sdimclass SDErrorCategoryType final : public std::error_category {
328314564Sdim  const char *name() const noexcept override {
329277325Sdim    return "clang.serialized_diags";
330277325Sdim  }
331341825Sdim
332277325Sdim  std::string message(int IE) const override {
333341825Sdim    auto E = static_cast<SDError>(IE);
334277325Sdim    switch (E) {
335277325Sdim    case SDError::CouldNotLoad:
336277325Sdim      return "Failed to open diagnostics file";
337277325Sdim    case SDError::InvalidSignature:
338277325Sdim      return "Invalid diagnostics signature";
339277325Sdim    case SDError::InvalidDiagnostics:
340277325Sdim      return "Parse error reading diagnostics";
341277325Sdim    case SDError::MalformedTopLevelBlock:
342277325Sdim      return "Malformed block at top-level of diagnostics";
343277325Sdim    case SDError::MalformedSubBlock:
344277325Sdim      return "Malformed sub-block in a diagnostic";
345277325Sdim    case SDError::MalformedBlockInfoBlock:
346277325Sdim      return "Malformed BlockInfo block";
347277325Sdim    case SDError::MalformedMetadataBlock:
348277325Sdim      return "Malformed Metadata block";
349277325Sdim    case SDError::MalformedDiagnosticBlock:
350277325Sdim      return "Malformed Diagnostic block";
351277325Sdim    case SDError::MalformedDiagnosticRecord:
352277325Sdim      return "Malformed Diagnostic record";
353277325Sdim    case SDError::MissingVersion:
354277325Sdim      return "No version provided in diagnostics";
355277325Sdim    case SDError::VersionMismatch:
356277325Sdim      return "Unsupported diagnostics version";
357277325Sdim    case SDError::UnsupportedConstruct:
358277325Sdim      return "Bitcode constructs that are not supported in diagnostics appear";
359277325Sdim    case SDError::HandlerFailed:
360277325Sdim      return "Generic error occurred while handling a record";
361277325Sdim    }
362277325Sdim    llvm_unreachable("Unknown error type!");
363277325Sdim  }
364277325Sdim};
365277325Sdim
366341825Sdim} // namespace
367341825Sdim
368277325Sdimstatic llvm::ManagedStatic<SDErrorCategoryType> ErrorCategory;
369277325Sdimconst std::error_category &clang::serialized_diags::SDErrorCategory() {
370277325Sdim  return *ErrorCategory;
371277325Sdim}
372