clang 20.0.0git
SerializedDiagnosticReader.cpp
Go to the documentation of this file.
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
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
25using namespace clang;
26using namespace serialized_diags;
27
28std::error_code SerializedDiagnosticReader::readDiagnostics(StringRef File) {
29 // Open the diagnostics file.
31 FileManager FileMgr(FO);
32
33 auto Buffer = FileMgr.getBufferForFile(File);
34 if (!Buffer)
36
37 llvm::BitstreamCursor Stream(**Buffer);
38 std::optional<llvm::BitstreamBlockInfo> BlockInfo;
39
40 if (Stream.AtEndOfStream())
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(8)) {
46 if (Res.get() == C)
47 continue;
48 } else {
49 // FIXME this drops the error on the floor.
50 consumeError(Res.takeError());
51 }
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)
60 } else {
61 // FIXME this drops the error on the floor.
62 consumeError(Res.takeError());
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(MaybeSubBlockID.takeError());
72 }
73
74 switch (MaybeSubBlockID.get()) {
75 case llvm::bitc::BLOCKINFO_BLOCK_ID: {
77 Stream.ReadBlockInfoBlock();
78 if (!MaybeBlockInfo) {
79 // FIXME this drops the error on the floor.
80 consumeError(MaybeBlockInfo.takeError());
82 }
83 BlockInfo = std::move(MaybeBlockInfo.get());
84 }
85 if (!BlockInfo)
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(std::move(Err));
102 }
103 continue;
104 }
105 }
106 return {};
107}
108
110 Record = 1,
111 BlockEnd,
113};
114
115llvm::ErrorOr<SerializedDiagnosticReader::Cursor>
116SerializedDiagnosticReader::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(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(Res.takeError());
138 return Cursor::BlockBegin;
139
140 case llvm::bitc::END_BLOCK:
141 if (Stream.ReadBlockEnd())
143 return Cursor::BlockEnd;
144
145 case llvm::bitc::DEFINE_ABBREV:
146 if (llvm::Error Err = Stream.ReadAbbrevRecord())
147 return llvm::errorToErrorCode(std::move(Err));
148 continue;
149
150 case llvm::bitc::UNABBREV_RECORD:
152
153 case llvm::bitc::FIRST_APPLICATION_ABBREV:
154 llvm_unreachable("Unexpected abbrev id.");
155 }
156 }
157
159}
160
161std::error_code
162SerializedDiagnosticReader::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));
168 }
169
170 bool VersionChecked = false;
171
172 while (true) {
173 unsigned BlockOrCode = 0;
174 llvm::ErrorOr<Cursor> Res = skipUntilRecordOrBlock(Stream, 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(std::move(Err));
186 }
187 [[fallthrough]];
188 case Cursor::BlockEnd:
189 if (!VersionChecked)
191 return {};
192 }
193
195 Expected<unsigned> MaybeRecordID = Stream.readRecord(BlockOrCode, Record);
196 if (!MaybeRecordID)
197 return errorToErrorCode(MaybeRecordID.takeError());
198 unsigned RecordID = MaybeRecordID.get();
199
200 if (RecordID == RECORD_VERSION) {
201 if (Record.size() < 1)
203 if (Record[0] > VersionNumber)
205 VersionChecked = true;
206 }
207 }
208}
209
210std::error_code
211SerializedDiagnosticReader::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));
217 }
218
219 std::error_code EC;
220 if ((EC = visitStartOfDiagnostic()))
221 return EC;
222
224 while (true) {
225 unsigned BlockOrCode = 0;
226 llvm::ErrorOr<Cursor> Res = skipUntilRecordOrBlock(Stream, 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(std::move(Err));
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(BlockOrCode, Record, &Blob);
255 if (!MaybeRecID)
256 return errorToErrorCode(MaybeRecID.takeError());
257 unsigned RecID = MaybeRecID.get();
258
259 if (RecID < serialized_diags::RECORD_FIRST ||
261 continue;
262
263 switch ((RecordIDs)RecID) {
264 case RECORD_CATEGORY:
265 // A category has ID and name size.
266 if (Record.size() != 2)
268 if ((EC = visitCategoryRecord(Record[0], 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)
276 if ((EC = visitDiagnosticRecord(
277 Record[0], Location(Record[1], Record[2], Record[3], Record[4]),
278 Record[5], Record[6], 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)
285 if ((EC = visitDiagFlagRecord(Record[0], 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)
293 if ((EC = visitFilenameRecord(Record[0], Record[1], Record[2], 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)
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)))
303 return EC;
304 continue;
306 // A source range is two locations (4 each).
307 if (Record.size() != 8)
309 if ((EC = visitSourceRangeRecord(
310 Location(Record[0], Record[1], Record[2], Record[3]),
311 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)
318 if ((EC = visitVersionRecord(Record[0])))
319 return EC;
320 continue;
321 }
322 }
323}
324
325namespace {
326
327class 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
368static llvm::ManagedStatic<SDErrorCategoryType> ErrorCategory;
369const std::error_category &clang::serialized_diags::SDErrorCategory() {
370 return *ErrorCategory;
371}
Expr * E
Defines the clang::FileManager interface and associated types.
Defines the clang::FileSystemOptions interface.
llvm::MachO::Record Record
Definition: MachO.h:31
static llvm::ManagedStatic< SDErrorCategoryType > ErrorCategory
Implements support for file system lookup, file system caching, and directory search management.
Definition: FileManager.h:53
Keeps track of options that affect how file operations are performed.
virtual std::error_code visitCategoryRecord(unsigned ID, StringRef Name)
Visit a category. This associates the category ID to a Name.
virtual std::error_code visitDiagFlagRecord(unsigned ID, StringRef Name)
Visit a flag. This associates the flag's ID to a Name.
virtual std::error_code visitStartOfDiagnostic()
Visit the start of a diagnostic block.
virtual std::error_code visitDiagnosticRecord(unsigned Severity, const Location &Location, unsigned Category, unsigned Flag, StringRef Message)
Visit a diagnostic.
virtual std::error_code visitSourceRangeRecord(const Location &Start, const Location &End)
Visit a source range.
virtual std::error_code visitVersionRecord(unsigned Version)
Visit the version of the set of diagnostics.
virtual std::error_code visitFixitRecord(const Location &Start, const Location &End, StringRef Text)
Visit a fixit hint.
virtual std::error_code visitFilenameRecord(unsigned ID, unsigned Size, unsigned Timestamp, StringRef Name)
Visit a filename. This associates the file's ID to a Name.
virtual std::error_code visitEndOfDiagnostic()
Visit the end of a diagnostic block.
const std::error_category & SDErrorCategory()
@ BLOCK_DIAG
The this block acts as a container for all the information for a specific diagnostic.
@ BLOCK_META
A top-level block which represents any meta data associated with the diagostics, including versioning...
RangeSelector name(std::string ID)
Given a node with a "name", (like NamedDecl, DeclRefExpr, CxxCtorInitializer, and TypeLoc) selects th...
The JSON file list parser is used to communicate input to InstallAPI.
A location that is represented in the serialized diagnostics.