1 //===-- CXLoadedDiagnostic.cpp - Handling of persisent diags ----*- C++ -*-===//
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
7 //===----------------------------------------------------------------------===//
9 // Implements handling of persisent diagnostics.
11 //===----------------------------------------------------------------------===//
13 #include "CXLoadedDiagnostic.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/STLExtras.h"
22 #include "llvm/ADT/StringRef.h"
23 #include "llvm/ADT/Twine.h"
24 #include "llvm/Bitstream/BitstreamReader.h"
25 #include "llvm/Support/ErrorHandling.h"
27 using namespace clang
;
29 //===----------------------------------------------------------------------===//
30 // Extend CXDiagnosticSetImpl which contains strings for diagnostics.
31 //===----------------------------------------------------------------------===//
33 typedef llvm::DenseMap
<unsigned, const char *> Strings
;
36 class CXLoadedDiagnosticSetImpl
: public CXDiagnosticSetImpl
{
38 CXLoadedDiagnosticSetImpl() : CXDiagnosticSetImpl(true), FakeFiles(FO
) {}
39 ~CXLoadedDiagnosticSetImpl() override
{}
41 llvm::BumpPtrAllocator Alloc
;
47 FileManager FakeFiles
;
48 llvm::DenseMap
<unsigned, FileEntryRef
> Files
;
50 /// Copy the string into our own allocator.
51 const char *copyString(StringRef Blob
) {
52 char *mem
= Alloc
.Allocate
<char>(Blob
.size() + 1);
53 memcpy(mem
, Blob
.data(), Blob
.size());
54 mem
[Blob
.size()] = '\0';
58 } // end anonymous namespace
60 //===----------------------------------------------------------------------===//
62 //===----------------------------------------------------------------------===//
64 CXLoadedDiagnostic::~CXLoadedDiagnostic() {}
66 //===----------------------------------------------------------------------===//
67 // Public CXLoadedDiagnostic methods.
68 //===----------------------------------------------------------------------===//
70 CXDiagnosticSeverity
CXLoadedDiagnostic::getSeverity() const {
71 // FIXME: Fail more softly if the diagnostic level is unknown?
72 auto severityAsLevel
= static_cast<serialized_diags::Level
>(severity
);
73 assert(severity
== static_cast<unsigned>(severityAsLevel
) &&
74 "unknown serialized diagnostic level");
76 switch (severityAsLevel
) {
77 #define CASE(X) case serialized_diags::X: return CXDiagnostic_##X;
84 // The 'Remark' level isn't represented in the stable API.
85 case serialized_diags::Remark
: return CXDiagnostic_Warning
;
88 llvm_unreachable("Invalid diagnostic level");
91 static CXSourceLocation
makeLocation(const CXLoadedDiagnostic::Location
*DLoc
) {
92 // The lowest bit of ptr_data[0] is always set to 1 to indicate this
93 // is a persistent diagnostic.
94 uintptr_t V
= (uintptr_t) DLoc
;
96 CXSourceLocation Loc
= { { (void*) V
, nullptr }, 0 };
100 CXSourceLocation
CXLoadedDiagnostic::getLocation() const {
101 // The lowest bit of ptr_data[0] is always set to 1 to indicate this
102 // is a persistent diagnostic.
103 return makeLocation(&DiagLoc
);
106 CXString
CXLoadedDiagnostic::getSpelling() const {
107 return cxstring::createRef(Spelling
);
110 CXString
CXLoadedDiagnostic::getDiagnosticOption(CXString
*Disable
) const {
111 if (DiagOption
.empty())
112 return cxstring::createEmpty();
114 // FIXME: possibly refactor with logic in CXStoredDiagnostic.
116 *Disable
= cxstring::createDup((Twine("-Wno-") + DiagOption
).str());
117 return cxstring::createDup((Twine("-W") + DiagOption
).str());
120 unsigned CXLoadedDiagnostic::getCategory() const {
124 CXString
CXLoadedDiagnostic::getCategoryText() const {
125 return cxstring::createDup(CategoryText
);
128 unsigned CXLoadedDiagnostic::getNumRanges() const {
129 return Ranges
.size();
132 CXSourceRange
CXLoadedDiagnostic::getRange(unsigned Range
) const {
133 assert(Range
< Ranges
.size());
134 return Ranges
[Range
];
137 unsigned CXLoadedDiagnostic::getNumFixIts() const {
138 return FixIts
.size();
141 CXString
CXLoadedDiagnostic::getFixIt(unsigned FixIt
,
142 CXSourceRange
*ReplacementRange
) const {
143 assert(FixIt
< FixIts
.size());
144 if (ReplacementRange
)
145 *ReplacementRange
= FixIts
[FixIt
].first
;
146 return cxstring::createRef(FixIts
[FixIt
].second
);
149 void CXLoadedDiagnostic::decodeLocation(CXSourceLocation location
,
152 unsigned int *column
,
153 unsigned int *offset
) {
156 // CXSourceLocation consists of the following fields:
158 // void *ptr_data[2];
159 // unsigned int_data;
161 // The lowest bit of ptr_data[0] is always set to 1 to indicate this
162 // is a persistent diagnostic.
164 // For now, do the unoptimized approach and store the data in a side
165 // data structure. We can optimize this case later.
167 uintptr_t V
= (uintptr_t) location
.ptr_data
[0];
168 assert((V
& 0x1) == 1);
171 const Location
&Loc
= *((Location
*)V
);
178 *column
= Loc
.column
;
180 *offset
= Loc
.offset
;
183 //===----------------------------------------------------------------------===//
184 // Deserialize diagnostics.
185 //===----------------------------------------------------------------------===//
188 class DiagLoader
: serialized_diags::SerializedDiagnosticReader
{
189 enum CXLoadDiag_Error
*error
;
190 CXString
*errorString
;
191 std::unique_ptr
<CXLoadedDiagnosticSetImpl
> TopDiags
;
192 SmallVector
<std::unique_ptr
<CXLoadedDiagnostic
>, 8> CurrentDiags
;
194 std::error_code
reportBad(enum CXLoadDiag_Error code
, llvm::StringRef err
) {
198 *errorString
= cxstring::createDup(err
);
199 return serialized_diags::SDError::HandlerFailed
;
202 std::error_code
reportInvalidFile(llvm::StringRef err
) {
203 return reportBad(CXLoadDiag_InvalidFile
, err
);
206 std::error_code
readRange(const serialized_diags::Location
&SDStart
,
207 const serialized_diags::Location
&SDEnd
,
210 std::error_code
readLocation(const serialized_diags::Location
&SDLoc
,
211 CXLoadedDiagnostic::Location
&LoadedLoc
);
214 std::error_code
visitStartOfDiagnostic() override
;
215 std::error_code
visitEndOfDiagnostic() override
;
217 std::error_code
visitCategoryRecord(unsigned ID
, StringRef Name
) override
;
219 std::error_code
visitDiagFlagRecord(unsigned ID
, StringRef Name
) override
;
221 std::error_code
visitDiagnosticRecord(
222 unsigned Severity
, const serialized_diags::Location
&Location
,
223 unsigned Category
, unsigned Flag
, StringRef Message
) override
;
225 std::error_code
visitFilenameRecord(unsigned ID
, unsigned Size
,
227 StringRef Name
) override
;
229 std::error_code
visitFixitRecord(const serialized_diags::Location
&Start
,
230 const serialized_diags::Location
&End
,
231 StringRef CodeToInsert
) override
;
234 visitSourceRangeRecord(const serialized_diags::Location
&Start
,
235 const serialized_diags::Location
&End
) override
;
238 DiagLoader(enum CXLoadDiag_Error
*e
, CXString
*es
)
239 : error(e
), errorString(es
) {
241 *error
= CXLoadDiag_None
;
243 *errorString
= cxstring::createEmpty();
246 CXDiagnosticSet
load(const char *file
);
248 } // end anonymous namespace
250 CXDiagnosticSet
DiagLoader::load(const char *file
) {
251 TopDiags
= std::make_unique
<CXLoadedDiagnosticSetImpl
>();
253 std::error_code EC
= readDiagnostics(file
);
255 switch (EC
.value()) {
256 case static_cast<int>(serialized_diags::SDError::HandlerFailed
):
257 // We've already reported the problem.
259 case static_cast<int>(serialized_diags::SDError::CouldNotLoad
):
260 reportBad(CXLoadDiag_CannotLoad
, EC
.message());
263 reportInvalidFile(EC
.message());
269 return (CXDiagnosticSet
)TopDiags
.release();
273 DiagLoader::readLocation(const serialized_diags::Location
&SDLoc
,
274 CXLoadedDiagnostic::Location
&LoadedLoc
) {
275 unsigned FileID
= SDLoc
.FileID
;
277 LoadedLoc
.file
= nullptr;
279 auto It
= TopDiags
->Files
.find(FileID
);
280 if (It
== TopDiags
->Files
.end())
281 return reportInvalidFile("Corrupted file entry in source location");
282 LoadedLoc
.file
= cxfile::makeCXFile(It
->second
);
284 LoadedLoc
.line
= SDLoc
.Line
;
285 LoadedLoc
.column
= SDLoc
.Col
;
286 LoadedLoc
.offset
= SDLoc
.Offset
;
287 return std::error_code();
291 DiagLoader::readRange(const serialized_diags::Location
&SDStart
,
292 const serialized_diags::Location
&SDEnd
,
294 CXLoadedDiagnostic::Location
*Start
, *End
;
295 Start
= TopDiags
->Alloc
.Allocate
<CXLoadedDiagnostic::Location
>();
296 End
= TopDiags
->Alloc
.Allocate
<CXLoadedDiagnostic::Location
>();
299 if ((EC
= readLocation(SDStart
, *Start
)))
301 if ((EC
= readLocation(SDEnd
, *End
)))
304 CXSourceLocation startLoc
= makeLocation(Start
);
305 CXSourceLocation endLoc
= makeLocation(End
);
306 SR
= clang_getRange(startLoc
, endLoc
);
307 return std::error_code();
310 std::error_code
DiagLoader::visitStartOfDiagnostic() {
311 CurrentDiags
.push_back(std::make_unique
<CXLoadedDiagnostic
>());
312 return std::error_code();
315 std::error_code
DiagLoader::visitEndOfDiagnostic() {
316 auto D
= CurrentDiags
.pop_back_val();
317 if (CurrentDiags
.empty())
318 TopDiags
->appendDiagnostic(std::move(D
));
320 CurrentDiags
.back()->getChildDiagnostics().appendDiagnostic(std::move(D
));
321 return std::error_code();
324 std::error_code
DiagLoader::visitCategoryRecord(unsigned ID
, StringRef Name
) {
325 // FIXME: Why do we care about long strings?
326 if (Name
.size() > 65536)
327 return reportInvalidFile("Out-of-bounds string in category");
328 TopDiags
->Categories
[ID
] = TopDiags
->copyString(Name
);
329 return std::error_code();
332 std::error_code
DiagLoader::visitDiagFlagRecord(unsigned ID
, StringRef Name
) {
333 // FIXME: Why do we care about long strings?
334 if (Name
.size() > 65536)
335 return reportInvalidFile("Out-of-bounds string in warning flag");
336 TopDiags
->WarningFlags
[ID
] = TopDiags
->copyString(Name
);
337 return std::error_code();
340 std::error_code
DiagLoader::visitFilenameRecord(unsigned ID
, unsigned Size
,
343 // FIXME: Why do we care about long strings?
344 if (Name
.size() > 65536)
345 return reportInvalidFile("Out-of-bounds string in filename");
346 TopDiags
->FileNames
[ID
] = TopDiags
->copyString(Name
);
347 TopDiags
->Files
.insert(
348 {ID
, TopDiags
->FakeFiles
.getVirtualFileRef(Name
, Size
, Timestamp
)});
349 return std::error_code();
353 DiagLoader::visitSourceRangeRecord(const serialized_diags::Location
&Start
,
354 const serialized_diags::Location
&End
) {
356 if (std::error_code EC
= readRange(Start
, End
, SR
))
358 CurrentDiags
.back()->Ranges
.push_back(SR
);
359 return std::error_code();
363 DiagLoader::visitFixitRecord(const serialized_diags::Location
&Start
,
364 const serialized_diags::Location
&End
,
365 StringRef CodeToInsert
) {
367 if (std::error_code EC
= readRange(Start
, End
, SR
))
369 // FIXME: Why do we care about long strings?
370 if (CodeToInsert
.size() > 65536)
371 return reportInvalidFile("Out-of-bounds string in FIXIT");
372 CurrentDiags
.back()->FixIts
.push_back(
373 std::make_pair(SR
, TopDiags
->copyString(CodeToInsert
)));
374 return std::error_code();
377 std::error_code
DiagLoader::visitDiagnosticRecord(
378 unsigned Severity
, const serialized_diags::Location
&Location
,
379 unsigned Category
, unsigned Flag
, StringRef Message
) {
380 CXLoadedDiagnostic
&D
= *CurrentDiags
.back();
381 D
.severity
= Severity
;
382 if (std::error_code EC
= readLocation(Location
, D
.DiagLoc
))
384 D
.category
= Category
;
385 D
.DiagOption
= Flag
? TopDiags
->WarningFlags
[Flag
] : "";
386 D
.CategoryText
= Category
? TopDiags
->Categories
[Category
] : "";
387 D
.Spelling
= TopDiags
->copyString(Message
);
388 return std::error_code();
391 CXDiagnosticSet
clang_loadDiagnostics(const char *file
,
392 enum CXLoadDiag_Error
*error
,
393 CXString
*errorString
) {
394 DiagLoader
L(error
, errorString
);