xref: /freebsd/contrib/llvm-project/clang/lib/Frontend/SerializedDiagnosticReader.cpp (revision bdd1243df58e60e85101c09001d9812a789b6bc4)
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  
readDiagnostics(StringRef File)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>
skipUntilRecordOrBlock(llvm::BitstreamCursor & Stream,unsigned & BlockOrRecordID)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
readMetaBlock(llvm::BitstreamCursor & Stream)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
readDiagnosticBlock(llvm::BitstreamCursor & Stream)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 {
name() const328    const char *name() const noexcept override {
329      return "clang.serialized_diags";
330    }
331  
message(int IE) const332    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;
SDErrorCategory()369  const std::error_category &clang::serialized_diags::SDErrorCategory() {
370    return *ErrorCategory;
371  }
372