/Users/buildslave/jenkins/workspace/coverage/llvm-project/clang/lib/Frontend/SerializedDiagnosticReader.cpp
Line | Count | Source (jump to first uncovered line) |
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/Optional.h" |
14 | | #include "llvm/ADT/SmallVector.h" |
15 | | #include "llvm/ADT/StringRef.h" |
16 | | #include "llvm/Bitstream/BitCodes.h" |
17 | | #include "llvm/Bitstream/BitstreamReader.h" |
18 | | #include "llvm/Support/Compiler.h" |
19 | | #include "llvm/Support/ErrorHandling.h" |
20 | | #include "llvm/Support/ErrorOr.h" |
21 | | #include "llvm/Support/ManagedStatic.h" |
22 | | #include <cstdint> |
23 | | #include <system_error> |
24 | | |
25 | | using namespace clang; |
26 | | using namespace serialized_diags; |
27 | | |
28 | 17 | std::error_code SerializedDiagnosticReader::readDiagnostics(StringRef File) { |
29 | | // Open the diagnostics file. |
30 | 17 | FileSystemOptions FO; |
31 | 17 | FileManager FileMgr(FO); |
32 | | |
33 | 17 | auto Buffer = FileMgr.getBufferForFile(File); |
34 | 17 | if (!Buffer) |
35 | 0 | return SDError::CouldNotLoad; |
36 | | |
37 | 17 | llvm::BitstreamCursor Stream(**Buffer); |
38 | 17 | Optional<llvm::BitstreamBlockInfo> BlockInfo; |
39 | | |
40 | 17 | if (Stream.AtEndOfStream()) |
41 | 1 | return SDError::InvalidSignature; |
42 | | |
43 | | // Sniff for the signature. |
44 | 64 | for (unsigned char C : {'D', 'I', 'A', 'G'})16 { |
45 | 64 | if (Expected<llvm::SimpleBitstreamCursor::word_t> Res = Stream.Read(8)) { |
46 | 64 | if (Res.get() == C) |
47 | 64 | continue; |
48 | 64 | } else { |
49 | | // FIXME this drops the error on the floor. |
50 | 0 | consumeError(Res.takeError()); |
51 | 0 | } |
52 | 0 | return SDError::InvalidSignature; |
53 | 64 | } |
54 | | |
55 | | // Read the top level blocks. |
56 | 88 | while (16 !Stream.AtEndOfStream()) { |
57 | 72 | if (Expected<unsigned> Res = Stream.ReadCode()) { |
58 | 72 | if (Res.get() != llvm::bitc::ENTER_SUBBLOCK) |
59 | 0 | return SDError::InvalidDiagnostics; |
60 | 72 | } else { |
61 | | // FIXME this drops the error on the floor. |
62 | 0 | consumeError(Res.takeError()); |
63 | 0 | return SDError::InvalidDiagnostics; |
64 | 0 | } |
65 | | |
66 | 72 | std::error_code EC; |
67 | 72 | Expected<unsigned> MaybeSubBlockID = Stream.ReadSubBlockID(); |
68 | 72 | if (!MaybeSubBlockID) { |
69 | | // FIXME this drops the error on the floor. |
70 | 0 | consumeError(MaybeSubBlockID.takeError()); |
71 | 0 | return SDError::InvalidDiagnostics; |
72 | 0 | } |
73 | | |
74 | 72 | switch (MaybeSubBlockID.get()) { |
75 | 16 | case llvm::bitc::BLOCKINFO_BLOCK_ID: { |
76 | 16 | Expected<Optional<llvm::BitstreamBlockInfo>> MaybeBlockInfo = |
77 | 16 | Stream.ReadBlockInfoBlock(); |
78 | 16 | if (!MaybeBlockInfo) { |
79 | | // FIXME this drops the error on the floor. |
80 | 0 | consumeError(MaybeBlockInfo.takeError()); |
81 | 0 | return SDError::InvalidDiagnostics; |
82 | 0 | } |
83 | 16 | BlockInfo = std::move(MaybeBlockInfo.get()); |
84 | 16 | } |
85 | 16 | if (!BlockInfo) |
86 | 0 | return SDError::MalformedBlockInfoBlock; |
87 | 16 | Stream.setBlockInfo(&*BlockInfo); |
88 | 16 | continue; |
89 | 16 | case BLOCK_META: |
90 | 16 | if ((EC = readMetaBlock(Stream))) |
91 | 0 | return EC; |
92 | 16 | continue; |
93 | 40 | case BLOCK_DIAG: |
94 | 40 | if ((EC = readDiagnosticBlock(Stream))) |
95 | 0 | return EC; |
96 | 40 | continue; |
97 | 40 | default: |
98 | 0 | if (llvm::Error Err = Stream.SkipBlock()) { |
99 | | // FIXME this drops the error on the floor. |
100 | 0 | consumeError(std::move(Err)); |
101 | 0 | return SDError::MalformedTopLevelBlock; |
102 | 0 | } |
103 | 0 | continue; |
104 | 72 | } |
105 | 72 | } |
106 | 16 | return {}; |
107 | 16 | } |
108 | | |
109 | | enum class SerializedDiagnosticReader::Cursor { |
110 | | Record = 1, |
111 | | BlockEnd, |
112 | | BlockBegin |
113 | | }; |
114 | | |
115 | | llvm::ErrorOr<SerializedDiagnosticReader::Cursor> |
116 | | SerializedDiagnosticReader::skipUntilRecordOrBlock( |
117 | 348 | llvm::BitstreamCursor &Stream, unsigned &BlockOrRecordID) { |
118 | 348 | BlockOrRecordID = 0; |
119 | | |
120 | 348 | while (!Stream.AtEndOfStream()) { |
121 | 348 | unsigned Code; |
122 | 348 | if (Expected<unsigned> Res = Stream.ReadCode()) |
123 | 348 | Code = Res.get(); |
124 | 0 | else |
125 | 0 | return llvm::errorToErrorCode(Res.takeError()); |
126 | | |
127 | 348 | if (Code >= static_cast<unsigned>(llvm::bitc::FIRST_APPLICATION_ABBREV)) { |
128 | | // We found a record. |
129 | 216 | BlockOrRecordID = Code; |
130 | 216 | return Cursor::Record; |
131 | 216 | } |
132 | 132 | switch (static_cast<llvm::bitc::FixedAbbrevIDs>(Code)) { |
133 | 38 | case llvm::bitc::ENTER_SUBBLOCK: |
134 | 38 | if (Expected<unsigned> Res = Stream.ReadSubBlockID()) |
135 | 38 | BlockOrRecordID = Res.get(); |
136 | 0 | else |
137 | 0 | return llvm::errorToErrorCode(Res.takeError()); |
138 | 38 | return Cursor::BlockBegin; |
139 | | |
140 | 94 | case llvm::bitc::END_BLOCK: |
141 | 94 | if (Stream.ReadBlockEnd()) |
142 | 0 | return SDError::InvalidDiagnostics; |
143 | 94 | return Cursor::BlockEnd; |
144 | | |
145 | 0 | case llvm::bitc::DEFINE_ABBREV: |
146 | 0 | if (llvm::Error Err = Stream.ReadAbbrevRecord()) |
147 | 0 | return llvm::errorToErrorCode(std::move(Err)); |
148 | 0 | continue; |
149 | | |
150 | 0 | case llvm::bitc::UNABBREV_RECORD: |
151 | 0 | return SDError::UnsupportedConstruct; |
152 | | |
153 | 0 | case llvm::bitc::FIRST_APPLICATION_ABBREV: |
154 | 0 | llvm_unreachable("Unexpected abbrev id."); |
155 | 132 | } |
156 | 132 | } |
157 | | |
158 | 0 | return SDError::InvalidDiagnostics; |
159 | 348 | } |
160 | | |
161 | | std::error_code |
162 | 16 | SerializedDiagnosticReader::readMetaBlock(llvm::BitstreamCursor &Stream) { |
163 | 16 | if (llvm::Error Err = |
164 | 16 | Stream.EnterSubBlock(clang::serialized_diags::BLOCK_META)) { |
165 | | // FIXME this drops the error on the floor. |
166 | 0 | consumeError(std::move(Err)); |
167 | 0 | return SDError::MalformedMetadataBlock; |
168 | 0 | } |
169 | | |
170 | 16 | bool VersionChecked = false; |
171 | | |
172 | 32 | while (true) { |
173 | 32 | unsigned BlockOrCode = 0; |
174 | 32 | llvm::ErrorOr<Cursor> Res = skipUntilRecordOrBlock(Stream, BlockOrCode); |
175 | 32 | if (!Res) |
176 | 0 | Res.getError(); |
177 | | |
178 | 32 | switch (Res.get()) { |
179 | 16 | case Cursor::Record: |
180 | 16 | break; |
181 | 0 | case Cursor::BlockBegin: |
182 | 0 | if (llvm::Error Err = Stream.SkipBlock()) { |
183 | | // FIXME this drops the error on the floor. |
184 | 0 | consumeError(std::move(Err)); |
185 | 0 | return SDError::MalformedMetadataBlock; |
186 | 0 | } |
187 | 0 | LLVM_FALLTHROUGH; |
188 | 16 | case Cursor::BlockEnd: |
189 | 16 | if (!VersionChecked) |
190 | 0 | return SDError::MissingVersion; |
191 | 16 | return {}; |
192 | 32 | } |
193 | | |
194 | 16 | SmallVector<uint64_t, 1> Record; |
195 | 16 | Expected<unsigned> MaybeRecordID = Stream.readRecord(BlockOrCode, Record); |
196 | 16 | if (!MaybeRecordID) |
197 | 0 | return errorToErrorCode(MaybeRecordID.takeError()); |
198 | 16 | unsigned RecordID = MaybeRecordID.get(); |
199 | | |
200 | 16 | if (RecordID == RECORD_VERSION) { |
201 | 16 | if (Record.size() < 1) |
202 | 0 | return SDError::MissingVersion; |
203 | 16 | if (Record[0] > VersionNumber) |
204 | 0 | return SDError::VersionMismatch; |
205 | 16 | VersionChecked = true; |
206 | 16 | } |
207 | 16 | } |
208 | 16 | } |
209 | | |
210 | | std::error_code |
211 | 78 | SerializedDiagnosticReader::readDiagnosticBlock(llvm::BitstreamCursor &Stream) { |
212 | 78 | if (llvm::Error Err = |
213 | 78 | Stream.EnterSubBlock(clang::serialized_diags::BLOCK_DIAG)) { |
214 | | // FIXME this drops the error on the floor. |
215 | 0 | consumeError(std::move(Err)); |
216 | 0 | return SDError::MalformedDiagnosticBlock; |
217 | 0 | } |
218 | | |
219 | 78 | std::error_code EC; |
220 | 78 | if ((EC = visitStartOfDiagnostic())) |
221 | 0 | return EC; |
222 | | |
223 | 78 | SmallVector<uint64_t, 16> Record; |
224 | 316 | while (true) { |
225 | 316 | unsigned BlockOrCode = 0; |
226 | 316 | llvm::ErrorOr<Cursor> Res = skipUntilRecordOrBlock(Stream, BlockOrCode); |
227 | 316 | if (!Res) |
228 | 0 | Res.getError(); |
229 | | |
230 | 316 | switch (Res.get()) { |
231 | 38 | case Cursor::BlockBegin: |
232 | | // The only blocks we care about are subdiagnostics. |
233 | 38 | if (BlockOrCode == serialized_diags::BLOCK_DIAG) { |
234 | 38 | if ((EC = readDiagnosticBlock(Stream))) |
235 | 0 | return EC; |
236 | 38 | } else if (llvm::Error 0 Err0 = Stream.SkipBlock()) { |
237 | | // FIXME this drops the error on the floor. |
238 | 0 | consumeError(std::move(Err)); |
239 | 0 | return SDError::MalformedSubBlock; |
240 | 0 | } |
241 | 38 | continue; |
242 | 78 | case Cursor::BlockEnd: |
243 | 78 | if ((EC = visitEndOfDiagnostic())) |
244 | 0 | return EC; |
245 | 78 | return {}; |
246 | 200 | case Cursor::Record: |
247 | 200 | break; |
248 | 316 | } |
249 | | |
250 | | // Read the record. |
251 | 200 | Record.clear(); |
252 | 200 | StringRef Blob; |
253 | 200 | Expected<unsigned> MaybeRecID = |
254 | 200 | Stream.readRecord(BlockOrCode, Record, &Blob); |
255 | 200 | if (!MaybeRecID) |
256 | 0 | return errorToErrorCode(MaybeRecID.takeError()); |
257 | 200 | unsigned RecID = MaybeRecID.get(); |
258 | | |
259 | 200 | if (RecID < serialized_diags::RECORD_FIRST || |
260 | 200 | RecID > serialized_diags::RECORD_LAST) |
261 | 0 | continue; |
262 | | |
263 | 200 | switch ((RecordIDs)RecID) { |
264 | 30 | case RECORD_CATEGORY: |
265 | | // A category has ID and name size. |
266 | 30 | if (Record.size() != 2) |
267 | 0 | return SDError::MalformedDiagnosticRecord; |
268 | 30 | if ((EC = visitCategoryRecord(Record[0], Blob))) |
269 | 0 | return EC; |
270 | 30 | continue; |
271 | 78 | case RECORD_DIAG: |
272 | | // A diagnostic has severity, location (4), category, flag, and message |
273 | | // size. |
274 | 78 | if (Record.size() != 8) |
275 | 0 | return SDError::MalformedDiagnosticRecord; |
276 | 78 | if ((EC = visitDiagnosticRecord( |
277 | 78 | Record[0], Location(Record[1], Record[2], Record[3], Record[4]), |
278 | 78 | Record[5], Record[6], Blob))) |
279 | 0 | return EC; |
280 | 78 | continue; |
281 | 78 | case RECORD_DIAG_FLAG: |
282 | | // A diagnostic flag has ID and name size. |
283 | 16 | if (Record.size() != 2) |
284 | 0 | return SDError::MalformedDiagnosticRecord; |
285 | 16 | if ((EC = visitDiagFlagRecord(Record[0], Blob))) |
286 | 0 | return EC; |
287 | 16 | continue; |
288 | 28 | 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 | 28 | if (Record.size() != 4) |
292 | 0 | return SDError::MalformedDiagnosticRecord; |
293 | 28 | if ((EC = visitFilenameRecord(Record[0], Record[1], Record[2], Blob))) |
294 | 0 | return EC; |
295 | 28 | continue; |
296 | 28 | case RECORD_FIXIT: |
297 | | // A fixit has two locations (4 each) and message size. |
298 | 12 | if (Record.size() != 9) |
299 | 0 | return SDError::MalformedDiagnosticRecord; |
300 | 12 | if ((EC = visitFixitRecord( |
301 | 12 | Location(Record[0], Record[1], Record[2], Record[3]), |
302 | 12 | Location(Record[4], Record[5], Record[6], Record[7]), Blob))) |
303 | 0 | return EC; |
304 | 12 | continue; |
305 | 36 | case RECORD_SOURCE_RANGE: |
306 | | // A source range is two locations (4 each). |
307 | 36 | if (Record.size() != 8) |
308 | 0 | return SDError::MalformedDiagnosticRecord; |
309 | 36 | if ((EC = visitSourceRangeRecord( |
310 | 36 | Location(Record[0], Record[1], Record[2], Record[3]), |
311 | 36 | Location(Record[4], Record[5], Record[6], Record[7])))) |
312 | 0 | return EC; |
313 | 36 | continue; |
314 | 36 | case RECORD_VERSION: |
315 | | // A version is just a number. |
316 | 0 | if (Record.size() != 1) |
317 | 0 | return SDError::MalformedDiagnosticRecord; |
318 | 0 | if ((EC = visitVersionRecord(Record[0]))) |
319 | 0 | return EC; |
320 | 0 | continue; |
321 | 200 | } |
322 | 200 | } |
323 | 78 | } |
324 | | |
325 | | namespace { |
326 | | |
327 | | class SDErrorCategoryType final : public std::error_category { |
328 | 0 | const char *name() const noexcept override { |
329 | 0 | return "clang.serialized_diags"; |
330 | 0 | } |
331 | | |
332 | 1 | std::string message(int IE) const override { |
333 | 1 | auto E = static_cast<SDError>(IE); |
334 | 1 | switch (E) { |
335 | 0 | case SDError::CouldNotLoad: |
336 | 0 | return "Failed to open diagnostics file"; |
337 | 1 | case SDError::InvalidSignature: |
338 | 1 | return "Invalid diagnostics signature"; |
339 | 0 | case SDError::InvalidDiagnostics: |
340 | 0 | return "Parse error reading diagnostics"; |
341 | 0 | case SDError::MalformedTopLevelBlock: |
342 | 0 | return "Malformed block at top-level of diagnostics"; |
343 | 0 | case SDError::MalformedSubBlock: |
344 | 0 | return "Malformed sub-block in a diagnostic"; |
345 | 0 | case SDError::MalformedBlockInfoBlock: |
346 | 0 | return "Malformed BlockInfo block"; |
347 | 0 | case SDError::MalformedMetadataBlock: |
348 | 0 | return "Malformed Metadata block"; |
349 | 0 | case SDError::MalformedDiagnosticBlock: |
350 | 0 | return "Malformed Diagnostic block"; |
351 | 0 | case SDError::MalformedDiagnosticRecord: |
352 | 0 | return "Malformed Diagnostic record"; |
353 | 0 | case SDError::MissingVersion: |
354 | 0 | return "No version provided in diagnostics"; |
355 | 0 | case SDError::VersionMismatch: |
356 | 0 | return "Unsupported diagnostics version"; |
357 | 0 | case SDError::UnsupportedConstruct: |
358 | 0 | return "Bitcode constructs that are not supported in diagnostics appear"; |
359 | 0 | case SDError::HandlerFailed: |
360 | 0 | return "Generic error occurred while handling a record"; |
361 | 1 | } |
362 | 0 | llvm_unreachable("Unknown error type!"); |
363 | 0 | } |
364 | | }; |
365 | | |
366 | | } // namespace |
367 | | |
368 | | static llvm::ManagedStatic<SDErrorCategoryType> ErrorCategory; |
369 | 2 | const std::error_category &clang::serialized_diags::SDErrorCategory() { |
370 | 2 | return *ErrorCategory; |
371 | 2 | } |