1//===--- SerializedDiagnosticReader.cpp - Reads diagnostics ---------------===//
2//
3//                     The LLVM Compiler Infrastructure
4//
5// This file is distributed under the University of Illinois Open Source
6// License. See LICENSE.TXT for details.
7//
8//===----------------------------------------------------------------------===//
9
10#include "clang/Frontend/SerializedDiagnosticReader.h"
11#include "clang/Basic/FileManager.h"
12#include "clang/Frontend/SerializedDiagnostics.h"
13#include "llvm/Support/ManagedStatic.h"
14#include "llvm/Support/MemoryBuffer.h"
15
16using namespace clang;
17using namespace clang::serialized_diags;
18
19std::error_code SerializedDiagnosticReader::readDiagnostics(StringRef File) {
20  // Open the diagnostics file.
21  FileSystemOptions FO;
22  FileManager FileMgr(FO);
23
24  auto Buffer = FileMgr.getBufferForFile(File);
25  if (!Buffer)
26    return SDError::CouldNotLoad;
27
28  llvm::BitstreamReader StreamFile;
29  StreamFile.init((const unsigned char *)(*Buffer)->getBufferStart(),
30                  (const unsigned char *)(*Buffer)->getBufferEnd());
31
32  llvm::BitstreamCursor Stream(StreamFile);
33
34  // Sniff for the signature.
35  if (Stream.Read(8) != 'D' ||
36      Stream.Read(8) != 'I' ||
37      Stream.Read(8) != 'A' ||
38      Stream.Read(8) != 'G')
39    return SDError::InvalidSignature;
40
41  // Read the top level blocks.
42  while (!Stream.AtEndOfStream()) {
43    if (Stream.ReadCode() != llvm::bitc::ENTER_SUBBLOCK)
44      return SDError::InvalidDiagnostics;
45
46    std::error_code EC;
47    switch (Stream.ReadSubBlockID()) {
48    case llvm::bitc::BLOCKINFO_BLOCK_ID:
49      if (Stream.ReadBlockInfoBlock())
50        return SDError::MalformedBlockInfoBlock;
51      continue;
52    case BLOCK_META:
53      if ((EC = readMetaBlock(Stream)))
54        return EC;
55      continue;
56    case BLOCK_DIAG:
57      if ((EC = readDiagnosticBlock(Stream)))
58        return EC;
59      continue;
60    default:
61      if (!Stream.SkipBlock())
62        return SDError::MalformedTopLevelBlock;
63      continue;
64    }
65  }
66  return std::error_code();
67}
68
69enum class SerializedDiagnosticReader::Cursor {
70  Record = 1,
71  BlockEnd,
72  BlockBegin
73};
74
75llvm::ErrorOr<SerializedDiagnosticReader::Cursor>
76SerializedDiagnosticReader::skipUntilRecordOrBlock(
77    llvm::BitstreamCursor &Stream, unsigned &BlockOrRecordID) {
78  BlockOrRecordID = 0;
79
80  while (!Stream.AtEndOfStream()) {
81    unsigned Code = Stream.ReadCode();
82
83    switch ((llvm::bitc::FixedAbbrevIDs)Code) {
84    case llvm::bitc::ENTER_SUBBLOCK:
85      BlockOrRecordID = Stream.ReadSubBlockID();
86      return Cursor::BlockBegin;
87
88    case llvm::bitc::END_BLOCK:
89      if (Stream.ReadBlockEnd())
90        return SDError::InvalidDiagnostics;
91      return Cursor::BlockEnd;
92
93    case llvm::bitc::DEFINE_ABBREV:
94      Stream.ReadAbbrevRecord();
95      continue;
96
97    case llvm::bitc::UNABBREV_RECORD:
98      return SDError::UnsupportedConstruct;
99
100    default:
101      // We found a record.
102      BlockOrRecordID = Code;
103      return Cursor::Record;
104    }
105  }
106
107  return SDError::InvalidDiagnostics;
108}
109
110std::error_code
111SerializedDiagnosticReader::readMetaBlock(llvm::BitstreamCursor &Stream) {
112  if (Stream.EnterSubBlock(clang::serialized_diags::BLOCK_META))
113    return SDError::MalformedMetadataBlock;
114
115  bool VersionChecked = false;
116
117  while (true) {
118    unsigned BlockOrCode = 0;
119    llvm::ErrorOr<Cursor> Res = skipUntilRecordOrBlock(Stream, BlockOrCode);
120    if (!Res)
121      Res.getError();
122
123    switch (Res.get()) {
124    case Cursor::Record:
125      break;
126    case Cursor::BlockBegin:
127      if (Stream.SkipBlock())
128        return SDError::MalformedMetadataBlock;
129    case Cursor::BlockEnd:
130      if (!VersionChecked)
131        return SDError::MissingVersion;
132      return std::error_code();
133    }
134
135    SmallVector<uint64_t, 1> Record;
136    unsigned RecordID = Stream.readRecord(BlockOrCode, Record);
137
138    if (RecordID == RECORD_VERSION) {
139      if (Record.size() < 1)
140        return SDError::MissingVersion;
141      if (Record[0] > VersionNumber)
142        return SDError::VersionMismatch;
143      VersionChecked = true;
144    }
145  }
146}
147
148std::error_code
149SerializedDiagnosticReader::readDiagnosticBlock(llvm::BitstreamCursor &Stream) {
150  if (Stream.EnterSubBlock(clang::serialized_diags::BLOCK_DIAG))
151    return SDError::MalformedDiagnosticBlock;
152
153  std::error_code EC;
154  if ((EC = visitStartOfDiagnostic()))
155    return EC;
156
157  SmallVector<uint64_t, 16> Record;
158  while (true) {
159    unsigned BlockOrCode = 0;
160    llvm::ErrorOr<Cursor> Res = skipUntilRecordOrBlock(Stream, BlockOrCode);
161    if (!Res)
162      Res.getError();
163
164    switch (Res.get()) {
165    case Cursor::BlockBegin:
166      // The only blocks we care about are subdiagnostics.
167      if (BlockOrCode == serialized_diags::BLOCK_DIAG) {
168        if ((EC = readDiagnosticBlock(Stream)))
169          return EC;
170      } else if (!Stream.SkipBlock())
171        return SDError::MalformedSubBlock;
172      continue;
173    case Cursor::BlockEnd:
174      if ((EC = visitEndOfDiagnostic()))
175        return EC;
176      return std::error_code();
177    case Cursor::Record:
178      break;
179    }
180
181    // Read the record.
182    Record.clear();
183    StringRef Blob;
184    unsigned RecID = Stream.readRecord(BlockOrCode, Record, &Blob);
185
186    if (RecID < serialized_diags::RECORD_FIRST ||
187        RecID > serialized_diags::RECORD_LAST)
188      continue;
189
190    switch ((RecordIDs)RecID) {
191    case RECORD_CATEGORY:
192      // A category has ID and name size.
193      if (Record.size() != 2)
194        return SDError::MalformedDiagnosticRecord;
195      if ((EC = visitCategoryRecord(Record[0], Blob)))
196        return EC;
197      continue;
198    case RECORD_DIAG:
199      // A diagnostic has severity, location (4), category, flag, and message
200      // size.
201      if (Record.size() != 8)
202        return SDError::MalformedDiagnosticRecord;
203      if ((EC = visitDiagnosticRecord(
204               Record[0], Location(Record[1], Record[2], Record[3], Record[4]),
205               Record[5], Record[6], Blob)))
206        return EC;
207      continue;
208    case RECORD_DIAG_FLAG:
209      // A diagnostic flag has ID and name size.
210      if (Record.size() != 2)
211        return SDError::MalformedDiagnosticRecord;
212      if ((EC = visitDiagFlagRecord(Record[0], Blob)))
213        return EC;
214      continue;
215    case RECORD_FILENAME:
216      // A filename has ID, size, timestamp, and name size. The size and
217      // timestamp are legacy fields that are always zero these days.
218      if (Record.size() != 4)
219        return SDError::MalformedDiagnosticRecord;
220      if ((EC = visitFilenameRecord(Record[0], Record[1], Record[2], Blob)))
221        return EC;
222      continue;
223    case RECORD_FIXIT:
224      // A fixit has two locations (4 each) and message size.
225      if (Record.size() != 9)
226        return SDError::MalformedDiagnosticRecord;
227      if ((EC = visitFixitRecord(
228               Location(Record[0], Record[1], Record[2], Record[3]),
229               Location(Record[4], Record[5], Record[6], Record[7]), Blob)))
230        return EC;
231      continue;
232    case RECORD_SOURCE_RANGE:
233      // A source range is two locations (4 each).
234      if (Record.size() != 8)
235        return SDError::MalformedDiagnosticRecord;
236      if ((EC = visitSourceRangeRecord(
237               Location(Record[0], Record[1], Record[2], Record[3]),
238               Location(Record[4], Record[5], Record[6], Record[7]))))
239        return EC;
240      continue;
241    case RECORD_VERSION:
242      // A version is just a number.
243      if (Record.size() != 1)
244        return SDError::MalformedDiagnosticRecord;
245      if ((EC = visitVersionRecord(Record[0])))
246        return EC;
247      continue;
248    }
249  }
250}
251
252namespace {
253class SDErrorCategoryType final : public std::error_category {
254  const char *name() const LLVM_NOEXCEPT override {
255    return "clang.serialized_diags";
256  }
257  std::string message(int IE) const override {
258    SDError E = static_cast<SDError>(IE);
259    switch (E) {
260    case SDError::CouldNotLoad:
261      return "Failed to open diagnostics file";
262    case SDError::InvalidSignature:
263      return "Invalid diagnostics signature";
264    case SDError::InvalidDiagnostics:
265      return "Parse error reading diagnostics";
266    case SDError::MalformedTopLevelBlock:
267      return "Malformed block at top-level of diagnostics";
268    case SDError::MalformedSubBlock:
269      return "Malformed sub-block in a diagnostic";
270    case SDError::MalformedBlockInfoBlock:
271      return "Malformed BlockInfo block";
272    case SDError::MalformedMetadataBlock:
273      return "Malformed Metadata block";
274    case SDError::MalformedDiagnosticBlock:
275      return "Malformed Diagnostic block";
276    case SDError::MalformedDiagnosticRecord:
277      return "Malformed Diagnostic record";
278    case SDError::MissingVersion:
279      return "No version provided in diagnostics";
280    case SDError::VersionMismatch:
281      return "Unsupported diagnostics version";
282    case SDError::UnsupportedConstruct:
283      return "Bitcode constructs that are not supported in diagnostics appear";
284    case SDError::HandlerFailed:
285      return "Generic error occurred while handling a record";
286    }
287    llvm_unreachable("Unknown error type!");
288  }
289};
290}
291
292static llvm::ManagedStatic<SDErrorCategoryType> ErrorCategory;
293const std::error_category &clang::serialized_diags::SDErrorCategory() {
294  return *ErrorCategory;
295}
296