xref: /llvm-project/clang/lib/Frontend/SerializedDiagnosticReader.cpp (revision 2946cd701067404b99c39fb29dc9c74bd7193eb3)
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/Bitcode/BitCodes.h"
17 #include "llvm/Bitcode/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 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   Optional<llvm::BitstreamBlockInfo> BlockInfo;
39 
40   if (Stream.AtEndOfStream())
41     return SDError::InvalidSignature;
42 
43   // Sniff for the signature.
44   if (Stream.Read(8) != 'D' ||
45       Stream.Read(8) != 'I' ||
46       Stream.Read(8) != 'A' ||
47       Stream.Read(8) != 'G')
48     return SDError::InvalidSignature;
49 
50   // Read the top level blocks.
51   while (!Stream.AtEndOfStream()) {
52     if (Stream.ReadCode() != llvm::bitc::ENTER_SUBBLOCK)
53       return SDError::InvalidDiagnostics;
54 
55     std::error_code EC;
56     switch (Stream.ReadSubBlockID()) {
57     case llvm::bitc::BLOCKINFO_BLOCK_ID:
58       BlockInfo = Stream.ReadBlockInfoBlock();
59       if (!BlockInfo)
60         return SDError::MalformedBlockInfoBlock;
61       Stream.setBlockInfo(&*BlockInfo);
62       continue;
63     case BLOCK_META:
64       if ((EC = readMetaBlock(Stream)))
65         return EC;
66       continue;
67     case BLOCK_DIAG:
68       if ((EC = readDiagnosticBlock(Stream)))
69         return EC;
70       continue;
71     default:
72       if (!Stream.SkipBlock())
73         return SDError::MalformedTopLevelBlock;
74       continue;
75     }
76   }
77   return {};
78 }
79 
80 enum class SerializedDiagnosticReader::Cursor {
81   Record = 1,
82   BlockEnd,
83   BlockBegin
84 };
85 
86 llvm::ErrorOr<SerializedDiagnosticReader::Cursor>
87 SerializedDiagnosticReader::skipUntilRecordOrBlock(
88     llvm::BitstreamCursor &Stream, unsigned &BlockOrRecordID) {
89   BlockOrRecordID = 0;
90 
91   while (!Stream.AtEndOfStream()) {
92     unsigned Code = Stream.ReadCode();
93 
94     switch ((llvm::bitc::FixedAbbrevIDs)Code) {
95     case llvm::bitc::ENTER_SUBBLOCK:
96       BlockOrRecordID = Stream.ReadSubBlockID();
97       return Cursor::BlockBegin;
98 
99     case llvm::bitc::END_BLOCK:
100       if (Stream.ReadBlockEnd())
101         return SDError::InvalidDiagnostics;
102       return Cursor::BlockEnd;
103 
104     case llvm::bitc::DEFINE_ABBREV:
105       Stream.ReadAbbrevRecord();
106       continue;
107 
108     case llvm::bitc::UNABBREV_RECORD:
109       return SDError::UnsupportedConstruct;
110 
111     default:
112       // We found a record.
113       BlockOrRecordID = Code;
114       return Cursor::Record;
115     }
116   }
117 
118   return SDError::InvalidDiagnostics;
119 }
120 
121 std::error_code
122 SerializedDiagnosticReader::readMetaBlock(llvm::BitstreamCursor &Stream) {
123   if (Stream.EnterSubBlock(clang::serialized_diags::BLOCK_META))
124     return SDError::MalformedMetadataBlock;
125 
126   bool VersionChecked = false;
127 
128   while (true) {
129     unsigned BlockOrCode = 0;
130     llvm::ErrorOr<Cursor> Res = skipUntilRecordOrBlock(Stream, BlockOrCode);
131     if (!Res)
132       Res.getError();
133 
134     switch (Res.get()) {
135     case Cursor::Record:
136       break;
137     case Cursor::BlockBegin:
138       if (Stream.SkipBlock())
139         return SDError::MalformedMetadataBlock;
140       LLVM_FALLTHROUGH;
141     case Cursor::BlockEnd:
142       if (!VersionChecked)
143         return SDError::MissingVersion;
144       return {};
145     }
146 
147     SmallVector<uint64_t, 1> Record;
148     unsigned RecordID = Stream.readRecord(BlockOrCode, Record);
149 
150     if (RecordID == RECORD_VERSION) {
151       if (Record.size() < 1)
152         return SDError::MissingVersion;
153       if (Record[0] > VersionNumber)
154         return SDError::VersionMismatch;
155       VersionChecked = true;
156     }
157   }
158 }
159 
160 std::error_code
161 SerializedDiagnosticReader::readDiagnosticBlock(llvm::BitstreamCursor &Stream) {
162   if (Stream.EnterSubBlock(clang::serialized_diags::BLOCK_DIAG))
163     return SDError::MalformedDiagnosticBlock;
164 
165   std::error_code EC;
166   if ((EC = visitStartOfDiagnostic()))
167     return EC;
168 
169   SmallVector<uint64_t, 16> Record;
170   while (true) {
171     unsigned BlockOrCode = 0;
172     llvm::ErrorOr<Cursor> Res = skipUntilRecordOrBlock(Stream, BlockOrCode);
173     if (!Res)
174       Res.getError();
175 
176     switch (Res.get()) {
177     case Cursor::BlockBegin:
178       // The only blocks we care about are subdiagnostics.
179       if (BlockOrCode == serialized_diags::BLOCK_DIAG) {
180         if ((EC = readDiagnosticBlock(Stream)))
181           return EC;
182       } else if (!Stream.SkipBlock())
183         return SDError::MalformedSubBlock;
184       continue;
185     case Cursor::BlockEnd:
186       if ((EC = visitEndOfDiagnostic()))
187         return EC;
188       return {};
189     case Cursor::Record:
190       break;
191     }
192 
193     // Read the record.
194     Record.clear();
195     StringRef Blob;
196     unsigned RecID = Stream.readRecord(BlockOrCode, Record, &Blob);
197 
198     if (RecID < serialized_diags::RECORD_FIRST ||
199         RecID > serialized_diags::RECORD_LAST)
200       continue;
201 
202     switch ((RecordIDs)RecID) {
203     case RECORD_CATEGORY:
204       // A category has ID and name size.
205       if (Record.size() != 2)
206         return SDError::MalformedDiagnosticRecord;
207       if ((EC = visitCategoryRecord(Record[0], Blob)))
208         return EC;
209       continue;
210     case RECORD_DIAG:
211       // A diagnostic has severity, location (4), category, flag, and message
212       // size.
213       if (Record.size() != 8)
214         return SDError::MalformedDiagnosticRecord;
215       if ((EC = visitDiagnosticRecord(
216                Record[0], Location(Record[1], Record[2], Record[3], Record[4]),
217                Record[5], Record[6], Blob)))
218         return EC;
219       continue;
220     case RECORD_DIAG_FLAG:
221       // A diagnostic flag has ID and name size.
222       if (Record.size() != 2)
223         return SDError::MalformedDiagnosticRecord;
224       if ((EC = visitDiagFlagRecord(Record[0], Blob)))
225         return EC;
226       continue;
227     case RECORD_FILENAME:
228       // A filename has ID, size, timestamp, and name size. The size and
229       // timestamp are legacy fields that are always zero these days.
230       if (Record.size() != 4)
231         return SDError::MalformedDiagnosticRecord;
232       if ((EC = visitFilenameRecord(Record[0], Record[1], Record[2], Blob)))
233         return EC;
234       continue;
235     case RECORD_FIXIT:
236       // A fixit has two locations (4 each) and message size.
237       if (Record.size() != 9)
238         return SDError::MalformedDiagnosticRecord;
239       if ((EC = visitFixitRecord(
240                Location(Record[0], Record[1], Record[2], Record[3]),
241                Location(Record[4], Record[5], Record[6], Record[7]), Blob)))
242         return EC;
243       continue;
244     case RECORD_SOURCE_RANGE:
245       // A source range is two locations (4 each).
246       if (Record.size() != 8)
247         return SDError::MalformedDiagnosticRecord;
248       if ((EC = visitSourceRangeRecord(
249                Location(Record[0], Record[1], Record[2], Record[3]),
250                Location(Record[4], Record[5], Record[6], Record[7]))))
251         return EC;
252       continue;
253     case RECORD_VERSION:
254       // A version is just a number.
255       if (Record.size() != 1)
256         return SDError::MalformedDiagnosticRecord;
257       if ((EC = visitVersionRecord(Record[0])))
258         return EC;
259       continue;
260     }
261   }
262 }
263 
264 namespace {
265 
266 class SDErrorCategoryType final : public std::error_category {
267   const char *name() const noexcept override {
268     return "clang.serialized_diags";
269   }
270 
271   std::string message(int IE) const override {
272     auto E = static_cast<SDError>(IE);
273     switch (E) {
274     case SDError::CouldNotLoad:
275       return "Failed to open diagnostics file";
276     case SDError::InvalidSignature:
277       return "Invalid diagnostics signature";
278     case SDError::InvalidDiagnostics:
279       return "Parse error reading diagnostics";
280     case SDError::MalformedTopLevelBlock:
281       return "Malformed block at top-level of diagnostics";
282     case SDError::MalformedSubBlock:
283       return "Malformed sub-block in a diagnostic";
284     case SDError::MalformedBlockInfoBlock:
285       return "Malformed BlockInfo block";
286     case SDError::MalformedMetadataBlock:
287       return "Malformed Metadata block";
288     case SDError::MalformedDiagnosticBlock:
289       return "Malformed Diagnostic block";
290     case SDError::MalformedDiagnosticRecord:
291       return "Malformed Diagnostic record";
292     case SDError::MissingVersion:
293       return "No version provided in diagnostics";
294     case SDError::VersionMismatch:
295       return "Unsupported diagnostics version";
296     case SDError::UnsupportedConstruct:
297       return "Bitcode constructs that are not supported in diagnostics appear";
298     case SDError::HandlerFailed:
299       return "Generic error occurred while handling a record";
300     }
301     llvm_unreachable("Unknown error type!");
302   }
303 };
304 
305 } // namespace
306 
307 static llvm::ManagedStatic<SDErrorCategoryType> ErrorCategory;
308 const std::error_category &clang::serialized_diags::SDErrorCategory() {
309   return *ErrorCategory;
310 }
311