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 | |
25 | using namespace clang; |
26 | using namespace serialized_diags; |
27 | |
28 | std::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 | |
109 | enum class SerializedDiagnosticReader::Cursor { |
110 | Record = 1, |
111 | BlockEnd, |
112 | BlockBegin |
113 | }; |
114 | |
115 | llvm::ErrorOr<SerializedDiagnosticReader::Cursor> |
116 | SerializedDiagnosticReader::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 | |
161 | std::error_code |
162 | SerializedDiagnosticReader::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 | |
210 | std::error_code |
211 | SerializedDiagnosticReader::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 | |
325 | namespace { |
326 | |
327 | class 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 | |
368 | static llvm::ManagedStatic<SDErrorCategoryType> ErrorCategory; |
369 | const std::error_category &clang::serialized_diags::SDErrorCategory() { |
370 | return *ErrorCategory; |
371 | } |
372 | |