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 | |
23 | using namespace clang; |
24 | using namespace serialized_diags; |
25 | |
26 | std::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 | |
107 | enum class SerializedDiagnosticReader::Cursor { |
108 | Record = 1, |
109 | BlockEnd, |
110 | BlockBegin |
111 | }; |
112 | |
113 | llvm::ErrorOr<SerializedDiagnosticReader::Cursor> |
114 | SerializedDiagnosticReader::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 | |
159 | std::error_code |
160 | SerializedDiagnosticReader::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 | |
208 | std::error_code |
209 | SerializedDiagnosticReader::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 | |
323 | namespace { |
324 | |
325 | class 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 | |
366 | static llvm::ManagedStatic<SDErrorCategoryType> ErrorCategory; |
367 | const std::error_category &clang::serialized_diags::SDErrorCategory() { |
368 | return *ErrorCategory; |
369 | } |
370 | |