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