clang  19.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 
25 using namespace clang;
26 using namespace serialized_diags;
27 
28 std::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)
35  return SDError::CouldNotLoad;
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,
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())
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 
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));
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 
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));
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;
305  case RECORD_SOURCE_RANGE:
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 
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) {
336  return "Failed to open diagnostics file";
338  return "Invalid diagnostics signature";
340  return "Parse error reading diagnostics";
342  return "Malformed block at top-level of diagnostics";
344  return "Malformed sub-block in a diagnostic";
346  return "Malformed BlockInfo block";
348  return "Malformed Metadata block";
350  return "Malformed Diagnostic block";
352  return "Malformed Diagnostic record";
354  return "No version provided in diagnostics";
356  return "Unsupported diagnostics version";
358  return "Bitcode constructs that are not supported in diagnostics appear";
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 }
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
llvm::ErrorOr< std::unique_ptr< llvm::MemoryBuffer > > getBufferForFile(FileEntryRef Entry, bool isVolatile=false, bool RequiresNullTerminator=true)
Open the specified file as a MemoryBuffer, returning a new MemoryBuffer if successful,...
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.
std::error_code readDiagnostics(StringRef File)
Read the diagnostics in File.
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...
@ HandlerFailed
A generic error for subclass handlers that don't want or need to define their own error_category.
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.