1 //===-- CXLoadedDiagnostic.cpp - Handling of persisent diags ----*- C++ -*-===//
2 //
3 // The LLVM Compiler Infrastructure
4 //
5 // This file is distributed under the University of Illinois Open Source
6 // License. See LICENSE.TXT for details.
7 //
8 //===----------------------------------------------------------------------===//
9 //
10 // Implements handling of persisent diagnostics.
11 //
12 //===----------------------------------------------------------------------===//
13
14 #include "CXLoadedDiagnostic.h"
15 #include "CXString.h"
16 #include "clang/Basic/Diagnostic.h"
17 #include "clang/Basic/FileManager.h"
18 #include "clang/Basic/LLVM.h"
19 #include "clang/Frontend/SerializedDiagnosticReader.h"
20 #include "clang/Frontend/SerializedDiagnostics.h"
21 #include "llvm/ADT/Optional.h"
22 #include "llvm/ADT/STLExtras.h"
23 #include "llvm/ADT/StringRef.h"
24 #include "llvm/ADT/Twine.h"
25 #include "llvm/Bitcode/BitstreamReader.h"
26 #include "llvm/Support/ErrorHandling.h"
27 #include "llvm/Support/MemoryBuffer.h"
28
29 using namespace clang;
30
31 //===----------------------------------------------------------------------===//
32 // Extend CXDiagnosticSetImpl which contains strings for diagnostics.
33 //===----------------------------------------------------------------------===//
34
35 typedef llvm::DenseMap<unsigned, const char *> Strings;
36
37 namespace {
38 class CXLoadedDiagnosticSetImpl : public CXDiagnosticSetImpl {
39 public:
CXLoadedDiagnosticSetImpl()40 CXLoadedDiagnosticSetImpl() : CXDiagnosticSetImpl(true), FakeFiles(FO) {}
~CXLoadedDiagnosticSetImpl()41 ~CXLoadedDiagnosticSetImpl() override {}
42
43 llvm::BumpPtrAllocator Alloc;
44 Strings Categories;
45 Strings WarningFlags;
46 Strings FileNames;
47
48 FileSystemOptions FO;
49 FileManager FakeFiles;
50 llvm::DenseMap<unsigned, const FileEntry *> Files;
51
52 /// \brief Copy the string into our own allocator.
copyString(StringRef Blob)53 const char *copyString(StringRef Blob) {
54 char *mem = Alloc.Allocate<char>(Blob.size() + 1);
55 memcpy(mem, Blob.data(), Blob.size());
56 mem[Blob.size()] = '\0';
57 return mem;
58 }
59 };
60 } // end anonymous namespace
61
62 //===----------------------------------------------------------------------===//
63 // Cleanup.
64 //===----------------------------------------------------------------------===//
65
~CXLoadedDiagnostic()66 CXLoadedDiagnostic::~CXLoadedDiagnostic() {}
67
68 //===----------------------------------------------------------------------===//
69 // Public CXLoadedDiagnostic methods.
70 //===----------------------------------------------------------------------===//
71
getSeverity() const72 CXDiagnosticSeverity CXLoadedDiagnostic::getSeverity() const {
73 // FIXME: Fail more softly if the diagnostic level is unknown?
74 auto severityAsLevel = static_cast<serialized_diags::Level>(severity);
75 assert(severity == static_cast<unsigned>(severityAsLevel) &&
76 "unknown serialized diagnostic level");
77
78 switch (severityAsLevel) {
79 #define CASE(X) case serialized_diags::X: return CXDiagnostic_##X;
80 CASE(Ignored)
81 CASE(Note)
82 CASE(Warning)
83 CASE(Error)
84 CASE(Fatal)
85 #undef CASE
86 // The 'Remark' level isn't represented in the stable API.
87 case serialized_diags::Remark: return CXDiagnostic_Warning;
88 }
89
90 llvm_unreachable("Invalid diagnostic level");
91 }
92
makeLocation(const CXLoadedDiagnostic::Location * DLoc)93 static CXSourceLocation makeLocation(const CXLoadedDiagnostic::Location *DLoc) {
94 // The lowest bit of ptr_data[0] is always set to 1 to indicate this
95 // is a persistent diagnostic.
96 uintptr_t V = (uintptr_t) DLoc;
97 V |= 0x1;
98 CXSourceLocation Loc = { { (void*) V, nullptr }, 0 };
99 return Loc;
100 }
101
getLocation() const102 CXSourceLocation CXLoadedDiagnostic::getLocation() const {
103 // The lowest bit of ptr_data[0] is always set to 1 to indicate this
104 // is a persistent diagnostic.
105 return makeLocation(&DiagLoc);
106 }
107
getSpelling() const108 CXString CXLoadedDiagnostic::getSpelling() const {
109 return cxstring::createRef(Spelling);
110 }
111
getDiagnosticOption(CXString * Disable) const112 CXString CXLoadedDiagnostic::getDiagnosticOption(CXString *Disable) const {
113 if (DiagOption.empty())
114 return cxstring::createEmpty();
115
116 // FIXME: possibly refactor with logic in CXStoredDiagnostic.
117 if (Disable)
118 *Disable = cxstring::createDup((Twine("-Wno-") + DiagOption).str());
119 return cxstring::createDup((Twine("-W") + DiagOption).str());
120 }
121
getCategory() const122 unsigned CXLoadedDiagnostic::getCategory() const {
123 return category;
124 }
125
getCategoryText() const126 CXString CXLoadedDiagnostic::getCategoryText() const {
127 return cxstring::createDup(CategoryText);
128 }
129
getNumRanges() const130 unsigned CXLoadedDiagnostic::getNumRanges() const {
131 return Ranges.size();
132 }
133
getRange(unsigned Range) const134 CXSourceRange CXLoadedDiagnostic::getRange(unsigned Range) const {
135 assert(Range < Ranges.size());
136 return Ranges[Range];
137 }
138
getNumFixIts() const139 unsigned CXLoadedDiagnostic::getNumFixIts() const {
140 return FixIts.size();
141 }
142
getFixIt(unsigned FixIt,CXSourceRange * ReplacementRange) const143 CXString CXLoadedDiagnostic::getFixIt(unsigned FixIt,
144 CXSourceRange *ReplacementRange) const {
145 assert(FixIt < FixIts.size());
146 if (ReplacementRange)
147 *ReplacementRange = FixIts[FixIt].first;
148 return cxstring::createRef(FixIts[FixIt].second);
149 }
150
decodeLocation(CXSourceLocation location,CXFile * file,unsigned int * line,unsigned int * column,unsigned int * offset)151 void CXLoadedDiagnostic::decodeLocation(CXSourceLocation location,
152 CXFile *file,
153 unsigned int *line,
154 unsigned int *column,
155 unsigned int *offset) {
156
157
158 // CXSourceLocation consists of the following fields:
159 //
160 // void *ptr_data[2];
161 // unsigned int_data;
162 //
163 // The lowest bit of ptr_data[0] is always set to 1 to indicate this
164 // is a persistent diagnostic.
165 //
166 // For now, do the unoptimized approach and store the data in a side
167 // data structure. We can optimize this case later.
168
169 uintptr_t V = (uintptr_t) location.ptr_data[0];
170 assert((V & 0x1) == 1);
171 V &= ~(uintptr_t)1;
172
173 const Location &Loc = *((Location*)V);
174
175 if (file)
176 *file = Loc.file;
177 if (line)
178 *line = Loc.line;
179 if (column)
180 *column = Loc.column;
181 if (offset)
182 *offset = Loc.offset;
183 }
184
185 //===----------------------------------------------------------------------===//
186 // Deserialize diagnostics.
187 //===----------------------------------------------------------------------===//
188
189 namespace {
190 class DiagLoader : serialized_diags::SerializedDiagnosticReader {
191 enum CXLoadDiag_Error *error;
192 CXString *errorString;
193 std::unique_ptr<CXLoadedDiagnosticSetImpl> TopDiags;
194 SmallVector<std::unique_ptr<CXLoadedDiagnostic>, 8> CurrentDiags;
195
reportBad(enum CXLoadDiag_Error code,llvm::StringRef err)196 std::error_code reportBad(enum CXLoadDiag_Error code, llvm::StringRef err) {
197 if (error)
198 *error = code;
199 if (errorString)
200 *errorString = cxstring::createDup(err);
201 return serialized_diags::SDError::HandlerFailed;
202 }
203
reportInvalidFile(llvm::StringRef err)204 std::error_code reportInvalidFile(llvm::StringRef err) {
205 return reportBad(CXLoadDiag_InvalidFile, err);
206 }
207
208 std::error_code readRange(const serialized_diags::Location &SDStart,
209 const serialized_diags::Location &SDEnd,
210 CXSourceRange &SR);
211
212 std::error_code readLocation(const serialized_diags::Location &SDLoc,
213 CXLoadedDiagnostic::Location &LoadedLoc);
214
215 protected:
216 std::error_code visitStartOfDiagnostic() override;
217 std::error_code visitEndOfDiagnostic() override;
218
219 std::error_code visitCategoryRecord(unsigned ID, StringRef Name) override;
220
221 std::error_code visitDiagFlagRecord(unsigned ID, StringRef Name) override;
222
223 std::error_code visitDiagnosticRecord(
224 unsigned Severity, const serialized_diags::Location &Location,
225 unsigned Category, unsigned Flag, StringRef Message) override;
226
227 std::error_code visitFilenameRecord(unsigned ID, unsigned Size,
228 unsigned Timestamp,
229 StringRef Name) override;
230
231 std::error_code visitFixitRecord(const serialized_diags::Location &Start,
232 const serialized_diags::Location &End,
233 StringRef CodeToInsert) override;
234
235 std::error_code
236 visitSourceRangeRecord(const serialized_diags::Location &Start,
237 const serialized_diags::Location &End) override;
238
239 public:
DiagLoader(enum CXLoadDiag_Error * e,CXString * es)240 DiagLoader(enum CXLoadDiag_Error *e, CXString *es)
241 : SerializedDiagnosticReader(), error(e), errorString(es) {
242 if (error)
243 *error = CXLoadDiag_None;
244 if (errorString)
245 *errorString = cxstring::createEmpty();
246 }
247
248 CXDiagnosticSet load(const char *file);
249 };
250 } // end anonymous namespace
251
load(const char * file)252 CXDiagnosticSet DiagLoader::load(const char *file) {
253 TopDiags = llvm::make_unique<CXLoadedDiagnosticSetImpl>();
254
255 std::error_code EC = readDiagnostics(file);
256 if (EC) {
257 switch (EC.value()) {
258 case static_cast<int>(serialized_diags::SDError::HandlerFailed):
259 // We've already reported the problem.
260 break;
261 case static_cast<int>(serialized_diags::SDError::CouldNotLoad):
262 reportBad(CXLoadDiag_CannotLoad, EC.message());
263 break;
264 default:
265 reportInvalidFile(EC.message());
266 break;
267 }
268 return nullptr;
269 }
270
271 return (CXDiagnosticSet)TopDiags.release();
272 }
273
274 std::error_code
readLocation(const serialized_diags::Location & SDLoc,CXLoadedDiagnostic::Location & LoadedLoc)275 DiagLoader::readLocation(const serialized_diags::Location &SDLoc,
276 CXLoadedDiagnostic::Location &LoadedLoc) {
277 unsigned FileID = SDLoc.FileID;
278 if (FileID == 0)
279 LoadedLoc.file = nullptr;
280 else {
281 LoadedLoc.file = const_cast<FileEntry *>(TopDiags->Files[FileID]);
282 if (!LoadedLoc.file)
283 return reportInvalidFile("Corrupted file entry in source location");
284 }
285 LoadedLoc.line = SDLoc.Line;
286 LoadedLoc.column = SDLoc.Col;
287 LoadedLoc.offset = SDLoc.Offset;
288 return std::error_code();
289 }
290
291 std::error_code
readRange(const serialized_diags::Location & SDStart,const serialized_diags::Location & SDEnd,CXSourceRange & SR)292 DiagLoader::readRange(const serialized_diags::Location &SDStart,
293 const serialized_diags::Location &SDEnd,
294 CXSourceRange &SR) {
295 CXLoadedDiagnostic::Location *Start, *End;
296 Start = TopDiags->Alloc.Allocate<CXLoadedDiagnostic::Location>();
297 End = TopDiags->Alloc.Allocate<CXLoadedDiagnostic::Location>();
298
299 std::error_code EC;
300 if ((EC = readLocation(SDStart, *Start)))
301 return EC;
302 if ((EC = readLocation(SDEnd, *End)))
303 return EC;
304
305 CXSourceLocation startLoc = makeLocation(Start);
306 CXSourceLocation endLoc = makeLocation(End);
307 SR = clang_getRange(startLoc, endLoc);
308 return std::error_code();
309 }
310
visitStartOfDiagnostic()311 std::error_code DiagLoader::visitStartOfDiagnostic() {
312 CurrentDiags.push_back(llvm::make_unique<CXLoadedDiagnostic>());
313 return std::error_code();
314 }
315
visitEndOfDiagnostic()316 std::error_code DiagLoader::visitEndOfDiagnostic() {
317 auto D = CurrentDiags.pop_back_val();
318 if (CurrentDiags.empty())
319 TopDiags->appendDiagnostic(std::move(D));
320 else
321 CurrentDiags.back()->getChildDiagnostics().appendDiagnostic(std::move(D));
322 return std::error_code();
323 }
324
visitCategoryRecord(unsigned ID,StringRef Name)325 std::error_code DiagLoader::visitCategoryRecord(unsigned ID, StringRef Name) {
326 // FIXME: Why do we care about long strings?
327 if (Name.size() > 65536)
328 return reportInvalidFile("Out-of-bounds string in category");
329 TopDiags->Categories[ID] = TopDiags->copyString(Name);
330 return std::error_code();
331 }
332
visitDiagFlagRecord(unsigned ID,StringRef Name)333 std::error_code DiagLoader::visitDiagFlagRecord(unsigned ID, StringRef Name) {
334 // FIXME: Why do we care about long strings?
335 if (Name.size() > 65536)
336 return reportInvalidFile("Out-of-bounds string in warning flag");
337 TopDiags->WarningFlags[ID] = TopDiags->copyString(Name);
338 return std::error_code();
339 }
340
visitFilenameRecord(unsigned ID,unsigned Size,unsigned Timestamp,StringRef Name)341 std::error_code DiagLoader::visitFilenameRecord(unsigned ID, unsigned Size,
342 unsigned Timestamp,
343 StringRef Name) {
344 // FIXME: Why do we care about long strings?
345 if (Name.size() > 65536)
346 return reportInvalidFile("Out-of-bounds string in filename");
347 TopDiags->FileNames[ID] = TopDiags->copyString(Name);
348 TopDiags->Files[ID] =
349 TopDiags->FakeFiles.getVirtualFile(Name, Size, Timestamp);
350 return std::error_code();
351 }
352
353 std::error_code
visitSourceRangeRecord(const serialized_diags::Location & Start,const serialized_diags::Location & End)354 DiagLoader::visitSourceRangeRecord(const serialized_diags::Location &Start,
355 const serialized_diags::Location &End) {
356 CXSourceRange SR;
357 if (std::error_code EC = readRange(Start, End, SR))
358 return EC;
359 CurrentDiags.back()->Ranges.push_back(SR);
360 return std::error_code();
361 }
362
363 std::error_code
visitFixitRecord(const serialized_diags::Location & Start,const serialized_diags::Location & End,StringRef CodeToInsert)364 DiagLoader::visitFixitRecord(const serialized_diags::Location &Start,
365 const serialized_diags::Location &End,
366 StringRef CodeToInsert) {
367 CXSourceRange SR;
368 if (std::error_code EC = readRange(Start, End, SR))
369 return EC;
370 // FIXME: Why do we care about long strings?
371 if (CodeToInsert.size() > 65536)
372 return reportInvalidFile("Out-of-bounds string in FIXIT");
373 CurrentDiags.back()->FixIts.push_back(
374 std::make_pair(SR, TopDiags->copyString(CodeToInsert)));
375 return std::error_code();
376 }
377
visitDiagnosticRecord(unsigned Severity,const serialized_diags::Location & Location,unsigned Category,unsigned Flag,StringRef Message)378 std::error_code DiagLoader::visitDiagnosticRecord(
379 unsigned Severity, const serialized_diags::Location &Location,
380 unsigned Category, unsigned Flag, StringRef Message) {
381 CXLoadedDiagnostic &D = *CurrentDiags.back();
382 D.severity = Severity;
383 if (std::error_code EC = readLocation(Location, D.DiagLoc))
384 return EC;
385 D.category = Category;
386 D.DiagOption = Flag ? TopDiags->WarningFlags[Flag] : "";
387 D.CategoryText = Category ? TopDiags->Categories[Category] : "";
388 D.Spelling = TopDiags->copyString(Message);
389 return std::error_code();
390 }
391
392 extern "C" {
clang_loadDiagnostics(const char * file,enum CXLoadDiag_Error * error,CXString * errorString)393 CXDiagnosticSet clang_loadDiagnostics(const char *file,
394 enum CXLoadDiag_Error *error,
395 CXString *errorString) {
396 DiagLoader L(error, errorString);
397 return L.load(file);
398 }
399 } // end extern 'C'.
400