1 | |
2 | |
3 | |
4 | |
5 | |
6 | |
7 | |
8 | |
9 | |
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/Bitcode/BitstreamReader.h" |
24 | #include "llvm/Support/ErrorHandling.h" |
25 | |
26 | using namespace clang; |
27 | |
28 | |
29 | |
30 | |
31 | |
32 | typedef llvm::DenseMap<unsigned, const char *> Strings; |
33 | |
34 | namespace { |
35 | class CXLoadedDiagnosticSetImpl : public CXDiagnosticSetImpl { |
36 | public: |
37 | CXLoadedDiagnosticSetImpl() : CXDiagnosticSetImpl(true), FakeFiles(FO) {} |
38 | ~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 | |
50 | const char *copyString(StringRef Blob) { |
51 | char *mem = Alloc.Allocate<char>(Blob.size() + 1); |
52 | memcpy(mem, Blob.data(), Blob.size()); |
53 | mem[Blob.size()] = '\0'; |
54 | return mem; |
55 | } |
56 | }; |
57 | } |
58 | |
59 | |
60 | |
61 | |
62 | |
63 | CXLoadedDiagnostic::~CXLoadedDiagnostic() {} |
64 | |
65 | |
66 | |
67 | |
68 | |
69 | CXDiagnosticSeverity CXLoadedDiagnostic::getSeverity() const { |
70 | |
71 | auto severityAsLevel = static_cast<serialized_diags::Level>(severity); |
72 | (0) . __assert_fail ("severity == static_cast(severityAsLevel) && \"unknown serialized diagnostic level\"", "/home/seafit/code_projects/clang_source/clang/tools/libclang/CXLoadedDiagnostic.cpp", 73, __PRETTY_FUNCTION__))" file_link="../../../include/assert.h.html#88" macro="true">assert(severity == static_cast<unsigned>(severityAsLevel) && |
73 | (0) . __assert_fail ("severity == static_cast(severityAsLevel) && \"unknown serialized diagnostic level\"", "/home/seafit/code_projects/clang_source/clang/tools/libclang/CXLoadedDiagnostic.cpp", 73, __PRETTY_FUNCTION__))" file_link="../../../include/assert.h.html#88" macro="true"> "unknown serialized diagnostic level"); |
74 | |
75 | switch (severityAsLevel) { |
76 | #define CASE(X) case serialized_diags::X: return CXDiagnostic_##X; |
77 | CASE(Ignored) |
78 | CASE(Note) |
79 | CASE(Warning) |
80 | CASE(Error) |
81 | CASE(Fatal) |
82 | #undef CASE |
83 | |
84 | case serialized_diags::Remark: return CXDiagnostic_Warning; |
85 | } |
86 | |
87 | llvm_unreachable("Invalid diagnostic level"); |
88 | } |
89 | |
90 | static CXSourceLocation makeLocation(const CXLoadedDiagnostic::Location *DLoc) { |
91 | |
92 | |
93 | uintptr_t V = (uintptr_t) DLoc; |
94 | V |= 0x1; |
95 | CXSourceLocation Loc = { { (void*) V, nullptr }, 0 }; |
96 | return Loc; |
97 | } |
98 | |
99 | CXSourceLocation CXLoadedDiagnostic::getLocation() const { |
100 | |
101 | |
102 | return makeLocation(&DiagLoc); |
103 | } |
104 | |
105 | CXString CXLoadedDiagnostic::getSpelling() const { |
106 | return cxstring::createRef(Spelling); |
107 | } |
108 | |
109 | CXString CXLoadedDiagnostic::getDiagnosticOption(CXString *Disable) const { |
110 | if (DiagOption.empty()) |
111 | return cxstring::createEmpty(); |
112 | |
113 | |
114 | if (Disable) |
115 | *Disable = cxstring::createDup((Twine("-Wno-") + DiagOption).str()); |
116 | return cxstring::createDup((Twine("-W") + DiagOption).str()); |
117 | } |
118 | |
119 | unsigned CXLoadedDiagnostic::getCategory() const { |
120 | return category; |
121 | } |
122 | |
123 | CXString CXLoadedDiagnostic::getCategoryText() const { |
124 | return cxstring::createDup(CategoryText); |
125 | } |
126 | |
127 | unsigned CXLoadedDiagnostic::getNumRanges() const { |
128 | return Ranges.size(); |
129 | } |
130 | |
131 | CXSourceRange CXLoadedDiagnostic::getRange(unsigned Range) const { |
132 | assert(Range < Ranges.size()); |
133 | return Ranges[Range]; |
134 | } |
135 | |
136 | unsigned CXLoadedDiagnostic::getNumFixIts() const { |
137 | return FixIts.size(); |
138 | } |
139 | |
140 | CXString CXLoadedDiagnostic::getFixIt(unsigned FixIt, |
141 | CXSourceRange *ReplacementRange) const { |
142 | assert(FixIt < FixIts.size()); |
143 | if (ReplacementRange) |
144 | *ReplacementRange = FixIts[FixIt].first; |
145 | return cxstring::createRef(FixIts[FixIt].second); |
146 | } |
147 | |
148 | void CXLoadedDiagnostic::decodeLocation(CXSourceLocation location, |
149 | CXFile *file, |
150 | unsigned int *line, |
151 | unsigned int *column, |
152 | unsigned int *offset) { |
153 | |
154 | |
155 | |
156 | |
157 | |
158 | |
159 | |
160 | |
161 | |
162 | |
163 | |
164 | |
165 | |
166 | uintptr_t V = (uintptr_t) location.ptr_data[0]; |
167 | assert((V & 0x1) == 1); |
168 | V &= ~(uintptr_t)1; |
169 | |
170 | const Location &Loc = *((Location*)V); |
171 | |
172 | if (file) |
173 | *file = Loc.file; |
174 | if (line) |
175 | *line = Loc.line; |
176 | if (column) |
177 | *column = Loc.column; |
178 | if (offset) |
179 | *offset = Loc.offset; |
180 | } |
181 | |
182 | |
183 | |
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 | std::error_code reportBad(enum CXLoadDiag_Error code, llvm::StringRef err) { |
194 | if (error) |
195 | *error = code; |
196 | if (errorString) |
197 | *errorString = cxstring::createDup(err); |
198 | return serialized_diags::SDError::HandlerFailed; |
199 | } |
200 | |
201 | std::error_code reportInvalidFile(llvm::StringRef err) { |
202 | return reportBad(CXLoadDiag_InvalidFile, err); |
203 | } |
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 | : SerializedDiagnosticReader(), error(e), errorString(es) { |
239 | if (error) |
240 | *error = CXLoadDiag_None; |
241 | if (errorString) |
242 | *errorString = cxstring::createEmpty(); |
243 | } |
244 | |
245 | CXDiagnosticSet load(const char *file); |
246 | }; |
247 | } |
248 | |
249 | CXDiagnosticSet DiagLoader::load(const char *file) { |
250 | TopDiags = llvm::make_unique<CXLoadedDiagnosticSetImpl>(); |
251 | |
252 | std::error_code EC = readDiagnostics(file); |
253 | if (EC) { |
254 | switch (EC.value()) { |
255 | case static_cast<int>(serialized_diags::SDError::HandlerFailed): |
256 | |
257 | break; |
258 | case static_cast<int>(serialized_diags::SDError::CouldNotLoad): |
259 | reportBad(CXLoadDiag_CannotLoad, EC.message()); |
260 | break; |
261 | default: |
262 | reportInvalidFile(EC.message()); |
263 | break; |
264 | } |
265 | return nullptr; |
266 | } |
267 | |
268 | return (CXDiagnosticSet)TopDiags.release(); |
269 | } |
270 | |
271 | std::error_code |
272 | DiagLoader::readLocation(const serialized_diags::Location &SDLoc, |
273 | CXLoadedDiagnostic::Location &LoadedLoc) { |
274 | unsigned FileID = SDLoc.FileID; |
275 | if (FileID == 0) |
276 | LoadedLoc.file = nullptr; |
277 | else { |
278 | LoadedLoc.file = const_cast<FileEntry *>(TopDiags->Files[FileID]); |
279 | if (!LoadedLoc.file) |
280 | return reportInvalidFile("Corrupted file entry in source location"); |
281 | } |
282 | LoadedLoc.line = SDLoc.Line; |
283 | LoadedLoc.column = SDLoc.Col; |
284 | LoadedLoc.offset = SDLoc.Offset; |
285 | return std::error_code(); |
286 | } |
287 | |
288 | std::error_code |
289 | DiagLoader::readRange(const serialized_diags::Location &SDStart, |
290 | const serialized_diags::Location &SDEnd, |
291 | CXSourceRange &SR) { |
292 | CXLoadedDiagnostic::Location *Start, *End; |
293 | Start = TopDiags->Alloc.Allocate<CXLoadedDiagnostic::Location>(); |
294 | End = TopDiags->Alloc.Allocate<CXLoadedDiagnostic::Location>(); |
295 | |
296 | std::error_code EC; |
297 | if ((EC = readLocation(SDStart, *Start))) |
298 | return EC; |
299 | if ((EC = readLocation(SDEnd, *End))) |
300 | return EC; |
301 | |
302 | CXSourceLocation startLoc = makeLocation(Start); |
303 | CXSourceLocation endLoc = makeLocation(End); |
304 | SR = clang_getRange(startLoc, endLoc); |
305 | return std::error_code(); |
306 | } |
307 | |
308 | std::error_code DiagLoader::visitStartOfDiagnostic() { |
309 | CurrentDiags.push_back(llvm::make_unique<CXLoadedDiagnostic>()); |
310 | return std::error_code(); |
311 | } |
312 | |
313 | std::error_code DiagLoader::visitEndOfDiagnostic() { |
314 | auto D = CurrentDiags.pop_back_val(); |
315 | if (CurrentDiags.empty()) |
316 | TopDiags->appendDiagnostic(std::move(D)); |
317 | else |
318 | CurrentDiags.back()->getChildDiagnostics().appendDiagnostic(std::move(D)); |
319 | return std::error_code(); |
320 | } |
321 | |
322 | std::error_code DiagLoader::visitCategoryRecord(unsigned ID, StringRef Name) { |
323 | |
324 | if (Name.size() > 65536) |
325 | return reportInvalidFile("Out-of-bounds string in category"); |
326 | TopDiags->Categories[ID] = TopDiags->copyString(Name); |
327 | return std::error_code(); |
328 | } |
329 | |
330 | std::error_code DiagLoader::visitDiagFlagRecord(unsigned ID, StringRef Name) { |
331 | |
332 | if (Name.size() > 65536) |
333 | return reportInvalidFile("Out-of-bounds string in warning flag"); |
334 | TopDiags->WarningFlags[ID] = TopDiags->copyString(Name); |
335 | return std::error_code(); |
336 | } |
337 | |
338 | std::error_code DiagLoader::visitFilenameRecord(unsigned ID, unsigned Size, |
339 | unsigned Timestamp, |
340 | StringRef Name) { |
341 | |
342 | if (Name.size() > 65536) |
343 | return reportInvalidFile("Out-of-bounds string in filename"); |
344 | TopDiags->FileNames[ID] = TopDiags->copyString(Name); |
345 | TopDiags->Files[ID] = |
346 | TopDiags->FakeFiles.getVirtualFile(Name, Size, Timestamp); |
347 | return std::error_code(); |
348 | } |
349 | |
350 | std::error_code |
351 | DiagLoader::visitSourceRangeRecord(const serialized_diags::Location &Start, |
352 | const serialized_diags::Location &End) { |
353 | CXSourceRange SR; |
354 | if (std::error_code EC = readRange(Start, End, SR)) |
355 | return EC; |
356 | CurrentDiags.back()->Ranges.push_back(SR); |
357 | return std::error_code(); |
358 | } |
359 | |
360 | std::error_code |
361 | DiagLoader::visitFixitRecord(const serialized_diags::Location &Start, |
362 | const serialized_diags::Location &End, |
363 | StringRef CodeToInsert) { |
364 | CXSourceRange SR; |
365 | if (std::error_code EC = readRange(Start, End, SR)) |
366 | return EC; |
367 | |
368 | if (CodeToInsert.size() > 65536) |
369 | return reportInvalidFile("Out-of-bounds string in FIXIT"); |
370 | CurrentDiags.back()->FixIts.push_back( |
371 | std::make_pair(SR, TopDiags->copyString(CodeToInsert))); |
372 | return std::error_code(); |
373 | } |
374 | |
375 | std::error_code DiagLoader::visitDiagnosticRecord( |
376 | unsigned Severity, const serialized_diags::Location &Location, |
377 | unsigned Category, unsigned Flag, StringRef Message) { |
378 | CXLoadedDiagnostic &D = *CurrentDiags.back(); |
379 | D.severity = Severity; |
380 | if (std::error_code EC = readLocation(Location, D.DiagLoc)) |
381 | return EC; |
382 | D.category = Category; |
383 | D.DiagOption = Flag ? TopDiags->WarningFlags[Flag] : ""; |
384 | D.CategoryText = Category ? TopDiags->Categories[Category] : ""; |
385 | D.Spelling = TopDiags->copyString(Message); |
386 | return std::error_code(); |
387 | } |
388 | |
389 | CXDiagnosticSet clang_loadDiagnostics(const char *file, |
390 | enum CXLoadDiag_Error *error, |
391 | CXString *errorString) { |
392 | DiagLoader L(error, errorString); |
393 | return L.load(file); |
394 | } |
395 | |