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/ADT/StringRef.h"
15#include "llvm/Bitstream/BitCodes.h"
16#include "llvm/Bitstream/BitstreamReader.h"
17#include "llvm/Support/Compiler.h"
18#include "llvm/Support/ErrorHandling.h"
19#include "llvm/Support/ErrorOr.h"
20#include "llvm/Support/ManagedStatic.h"
21#include <cstdint>
22#include <optional>
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(Filename: File);
34 if (!Buffer)
35 return SDError::CouldNotLoad;
36
37 llvm::BitstreamCursor Stream(**Buffer);
38 std::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(NumBits: 8)) {
46 if (Res.get() == C)
47 continue;
48 } else {
49 // FIXME this drops the error on the floor.
50 consumeError(Err: 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(Err: 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(Err: MaybeSubBlockID.takeError());
71 return SDError::InvalidDiagnostics;
72 }
73
74 switch (MaybeSubBlockID.get()) {
75 case llvm::bitc::BLOCKINFO_BLOCK_ID: {
76 Expected<std::optional<llvm::BitstreamBlockInfo>> MaybeBlockInfo =
77 Stream.ReadBlockInfoBlock();
78 if (!MaybeBlockInfo) {
79 // FIXME this drops the error on the floor.
80 consumeError(Err: 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(Err: 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(Err: 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(Err: 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(Err: 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(BlockID: clang::serialized_diags::BLOCK_META)) {
165 // FIXME this drops the error on the floor.
166 consumeError(Err: 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, BlockOrRecordID&: 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(Err: std::move(Err));
185 return SDError::MalformedMetadataBlock;
186 }
187 [[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(AbbrevID: BlockOrCode, Vals&: Record);
196 if (!MaybeRecordID)
197 return errorToErrorCode(Err: 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(BlockID: clang::serialized_diags::BLOCK_DIAG)) {
214 // FIXME this drops the error on the floor.
215 consumeError(Err: 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, BlockOrRecordID&: 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(Err: 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(AbbrevID: BlockOrCode, Vals&: Record, Blob: &Blob);
255 if (!MaybeRecID)
256 return errorToErrorCode(Err: 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(ID: Record[0], Name: 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 Severity: Record[0], Location: Location(Record[1], Record[2], Record[3], Record[4]),
278 Category: Record[5], Flag: Record[6], Message: 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(ID: Record[0], Name: 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(ID: Record[0], Size: Record[1], Timestamp: Record[2], Name: 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 Start: Location(Record[0], Record[1], Record[2], Record[3]),
302 End: Location(Record[4], Record[5], Record[6], Record[7]), Text: 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 Start: Location(Record[0], Record[1], Record[2], Record[3]),
311 End: 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(Version: 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