1 //===- SerializedDiagnosticReader.cpp - Reads diagnostics -----------------===//
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
7 //===----------------------------------------------------------------------===//
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"
23 #include <system_error>
25 using namespace clang
;
26 using namespace serialized_diags
;
28 std::error_code
SerializedDiagnosticReader::readDiagnostics(StringRef File
) {
29 // Open the diagnostics file.
31 FileManager
FileMgr(FO
);
33 auto Buffer
= FileMgr
.getBufferForFile(File
);
35 return SDError::CouldNotLoad
;
37 llvm::BitstreamCursor
Stream(**Buffer
);
38 std::optional
<llvm::BitstreamBlockInfo
> BlockInfo
;
40 if (Stream
.AtEndOfStream())
41 return SDError::InvalidSignature
;
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)) {
49 // FIXME this drops the error on the floor.
50 consumeError(Res
.takeError());
52 return SDError::InvalidSignature
;
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
;
61 // FIXME this drops the error on the floor.
62 consumeError(Res
.takeError());
63 return SDError::InvalidDiagnostics
;
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
;
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(MaybeBlockInfo
.takeError());
81 return SDError::InvalidDiagnostics
;
83 BlockInfo
= std::move(MaybeBlockInfo
.get());
86 return SDError::MalformedBlockInfoBlock
;
87 Stream
.setBlockInfo(&*BlockInfo
);
90 if ((EC
= readMetaBlock(Stream
)))
94 if ((EC
= readDiagnosticBlock(Stream
)))
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
;
109 enum class SerializedDiagnosticReader::Cursor
{
115 llvm::ErrorOr
<SerializedDiagnosticReader::Cursor
>
116 SerializedDiagnosticReader::skipUntilRecordOrBlock(
117 llvm::BitstreamCursor
&Stream
, unsigned &BlockOrRecordID
) {
120 while (!Stream
.AtEndOfStream()) {
122 if (Expected
<unsigned> Res
= Stream
.ReadCode())
125 return llvm::errorToErrorCode(Res
.takeError());
127 if (Code
>= static_cast<unsigned>(llvm::bitc::FIRST_APPLICATION_ABBREV
)) {
128 // We found a record.
129 BlockOrRecordID
= Code
;
130 return Cursor::Record
;
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();
137 return llvm::errorToErrorCode(Res
.takeError());
138 return Cursor::BlockBegin
;
140 case llvm::bitc::END_BLOCK
:
141 if (Stream
.ReadBlockEnd())
142 return SDError::InvalidDiagnostics
;
143 return Cursor::BlockEnd
;
145 case llvm::bitc::DEFINE_ABBREV
:
146 if (llvm::Error Err
= Stream
.ReadAbbrevRecord())
147 return llvm::errorToErrorCode(std::move(Err
));
150 case llvm::bitc::UNABBREV_RECORD
:
151 return SDError::UnsupportedConstruct
;
153 case llvm::bitc::FIRST_APPLICATION_ABBREV
:
154 llvm_unreachable("Unexpected abbrev id.");
158 return SDError::InvalidDiagnostics
;
162 SerializedDiagnosticReader::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
;
170 bool VersionChecked
= false;
173 unsigned BlockOrCode
= 0;
174 llvm::ErrorOr
<Cursor
> Res
= skipUntilRecordOrBlock(Stream
, BlockOrCode
);
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
;
188 case Cursor::BlockEnd
:
190 return SDError::MissingVersion
;
194 SmallVector
<uint64_t, 1> Record
;
195 Expected
<unsigned> MaybeRecordID
= Stream
.readRecord(BlockOrCode
, Record
);
197 return errorToErrorCode(MaybeRecordID
.takeError());
198 unsigned RecordID
= MaybeRecordID
.get();
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;
211 SerializedDiagnosticReader::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
;
220 if ((EC
= visitStartOfDiagnostic()))
223 SmallVector
<uint64_t, 16> Record
;
225 unsigned BlockOrCode
= 0;
226 llvm::ErrorOr
<Cursor
> Res
= skipUntilRecordOrBlock(Stream
, BlockOrCode
);
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
)))
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
;
242 case Cursor::BlockEnd
:
243 if ((EC
= visitEndOfDiagnostic()))
253 Expected
<unsigned> MaybeRecID
=
254 Stream
.readRecord(BlockOrCode
, Record
, &Blob
);
256 return errorToErrorCode(MaybeRecID
.takeError());
257 unsigned RecID
= MaybeRecID
.get();
259 if (RecID
< serialized_diags::RECORD_FIRST
||
260 RecID
> serialized_diags::RECORD_LAST
)
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
)))
272 // A diagnostic has severity, location (4), category, flag, and message
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
)))
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
)))
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
)))
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
)))
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]))))
315 // A version is just a number.
316 if (Record
.size() != 1)
317 return SDError::MalformedDiagnosticRecord
;
318 if ((EC
= visitVersionRecord(Record
[0])))
327 class SDErrorCategoryType final
: public std::error_category
{
328 const char *name() const noexcept override
{
329 return "clang.serialized_diags";
332 std::string
message(int IE
) const override
{
333 auto E
= static_cast<SDError
>(IE
);
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";
362 llvm_unreachable("Unknown error type!");
368 static llvm::ManagedStatic
<SDErrorCategoryType
> ErrorCategory
;
369 const std::error_category
&clang::serialized_diags::SDErrorCategory() {
370 return *ErrorCategory
;