xref: /freebsd/contrib/llvm-project/clang/lib/Frontend/SerializedDiagnosticReader.cpp (revision 401ab69cff8fa2320a9f8ea4baa114a6da6c952b)
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(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(8)) {
46       if (Res.get() == C)
47         continue;
48     } else {
49       // FIXME this drops the error on the floor.
50       consumeError(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(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(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(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(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(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())
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(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(clang::serialized_diags::BLOCK_META)) {
165     // FIXME this drops the error on the floor.
166     consumeError(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, 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));
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(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)
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(clang::serialized_diags::BLOCK_DIAG)) {
214     // FIXME this drops the error on the floor.
215     consumeError(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, 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));
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(BlockOrCode, Record, &Blob);
255     if (!MaybeRecID)
256       return errorToErrorCode(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(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)
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)))
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(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)
292         return SDError::MalformedDiagnosticRecord;
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)
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)))
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                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)
317         return SDError::MalformedDiagnosticRecord;
318       if ((EC = visitVersionRecord(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