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
23using namespace clang;
24using namespace serialized_diags;
25
26std::error_code SerializedDiagnosticReader::readDiagnostics(StringRef File) {
27 // Open the diagnostics file.
28 FileSystemOptions FO;
29 FileManager FileMgr(FO);
30
31 auto Buffer = FileMgr.getBufferForFile(Filename: 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(NumBits: 8)) {
44 if (Res.get() == C)
45 continue;
46 } else {
47 // FIXME this drops the error on the floor.
48 consumeError(Err: 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(Err: 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(Err: 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(Err: 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(Err: std::move(Err));
99 return SDError::MalformedTopLevelBlock;
100 }
101 continue;
102 }
103 }
104 return {};
105}
106
107enum class SerializedDiagnosticReader::Cursor {
108 Record = 1,
109 BlockEnd,
110 BlockBegin
111};
112
113llvm::ErrorOr<SerializedDiagnosticReader::Cursor>
114SerializedDiagnosticReader::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(Err: 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(Err: 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(Err: 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
159std::error_code
160SerializedDiagnosticReader::readMetaBlock(llvm::BitstreamCursor &Stream) {
161 if (llvm::Error Err =
162 Stream.EnterSubBlock(BlockID: clang::serialized_diags::BLOCK_META)) {
163 // FIXME this drops the error on the floor.
164 consumeError(Err: 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, BlockOrRecordID&: 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(Err: 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(AbbrevID: BlockOrCode, Vals&: Record);
194 if (!MaybeRecordID)
195 return errorToErrorCode(Err: 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
208std::error_code
209SerializedDiagnosticReader::readDiagnosticBlock(llvm::BitstreamCursor &Stream) {
210 if (llvm::Error Err =
211 Stream.EnterSubBlock(BlockID: clang::serialized_diags::BLOCK_DIAG)) {
212 // FIXME this drops the error on the floor.
213 consumeError(Err: 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, BlockOrRecordID&: 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(Err: 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(AbbrevID: BlockOrCode, Vals&: Record, Blob: &Blob);
253 if (!MaybeRecID)
254 return errorToErrorCode(Err: 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(ID: Record[0], Name: 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 Severity: Record[0], Location: Location(Record[1], Record[2], Record[3], Record[4]),
276 Category: Record[5], Flag: Record[6], Message: 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(ID: Record[0], Name: 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(ID: Record[0], Size: Record[1], Timestamp: Record[2], Name: 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 Start: Location(Record[0], Record[1], Record[2], Record[3]),
300 End: Location(Record[4], Record[5], Record[6], Record[7]), Text: 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 Start: Location(Record[0], Record[1], Record[2], Record[3]),
309 End: 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(Version: Record[0])))
317 return EC;
318 continue;
319 }
320 }
321}
322
323namespace {
324
325class 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
366static llvm::ManagedStatic<SDErrorCategoryType> ErrorCategory;
367const std::error_category &clang::serialized_diags::SDErrorCategory() {
368 return *ErrorCategory;
369}
370

Provided by KDAB

Privacy Policy
Improve your Profiling and Debugging skills
Find out more

source code of clang/lib/Frontend/SerializedDiagnosticReader.cpp