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 26using namespace clang; 27 28//===----------------------------------------------------------------------===// 29// Extend CXDiagnosticSetImpl which contains strings for diagnostics. 30//===----------------------------------------------------------------------===// 31 32typedef llvm::DenseMap<unsigned, const char *> Strings; 33 34namespace { 35class CXLoadedDiagnosticSetImpl : public CXDiagnosticSetImpl { 36public: 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 /// Copy the string into our own allocator. 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} // end anonymous namespace 58 59//===----------------------------------------------------------------------===// 60// Cleanup. 61//===----------------------------------------------------------------------===// 62 63CXLoadedDiagnostic::~CXLoadedDiagnostic() {} 64 65//===----------------------------------------------------------------------===// 66// Public CXLoadedDiagnostic methods. 67//===----------------------------------------------------------------------===// 68 69CXDiagnosticSeverity CXLoadedDiagnostic::getSeverity() const { 70 // FIXME: Fail more softly if the diagnostic level is unknown? 71 auto severityAsLevel = static_cast<serialized_diags::Level>(severity); 72 assert(severity == static_cast<unsigned>(severityAsLevel) && 73 "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 // The 'Remark' level isn't represented in the stable API. 84 case serialized_diags::Remark: return CXDiagnostic_Warning; 85 } 86 87 llvm_unreachable("Invalid diagnostic level"); 88} 89 90static 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 uintptr_t V = (uintptr_t) DLoc; 94 V |= 0x1; 95 CXSourceLocation Loc = { { (void*) V, nullptr }, 0 }; 96 return Loc; 97} 98 99CXSourceLocation 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 return makeLocation(&DiagLoc); 103} 104 105CXString CXLoadedDiagnostic::getSpelling() const { 106 return cxstring::createRef(Spelling); 107} 108 109CXString CXLoadedDiagnostic::getDiagnosticOption(CXString *Disable) const { 110 if (DiagOption.empty()) 111 return cxstring::createEmpty(); 112 113 // FIXME: possibly refactor with logic in CXStoredDiagnostic. 114 if (Disable) 115 *Disable = cxstring::createDup((Twine("-Wno-") + DiagOption).str()); 116 return cxstring::createDup((Twine("-W") + DiagOption).str()); 117} 118 119unsigned CXLoadedDiagnostic::getCategory() const { 120 return category; 121} 122 123CXString CXLoadedDiagnostic::getCategoryText() const { 124 return cxstring::createDup(CategoryText); 125} 126 127unsigned CXLoadedDiagnostic::getNumRanges() const { 128 return Ranges.size(); 129} 130 131CXSourceRange CXLoadedDiagnostic::getRange(unsigned Range) const { 132 assert(Range < Ranges.size()); 133 return Ranges[Range]; 134} 135 136unsigned CXLoadedDiagnostic::getNumFixIts() const { 137 return FixIts.size(); 138} 139 140CXString 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 148void CXLoadedDiagnostic::decodeLocation(CXSourceLocation location, 149 CXFile *file, 150 unsigned int *line, 151 unsigned int *column, 152 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 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// Deserialize diagnostics. 184//===----------------------------------------------------------------------===// 185 186namespace { 187class 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 212protected: 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 236public: 237 DiagLoader(enum CXLoadDiag_Error *e, CXString *es) 238 : 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} // end anonymous namespace 248 249CXDiagnosticSet DiagLoader::load(const char *file) { 250 TopDiags = std::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 // We've already reported the problem. 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 271std::error_code 272DiagLoader::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 288std::error_code 289DiagLoader::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 308std::error_code DiagLoader::visitStartOfDiagnostic() { 309 CurrentDiags.push_back(std::make_unique<CXLoadedDiagnostic>()); 310 return std::error_code(); 311} 312 313std::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 322std::error_code DiagLoader::visitCategoryRecord(unsigned ID, StringRef Name) { 323 // FIXME: Why do we care about long strings? 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 330std::error_code DiagLoader::visitDiagFlagRecord(unsigned ID, StringRef Name) { 331 // FIXME: Why do we care about long strings? 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 338std::error_code DiagLoader::visitFilenameRecord(unsigned ID, unsigned Size, 339 unsigned Timestamp, 340 StringRef Name) { 341 // FIXME: Why do we care about long strings? 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 350std::error_code 351DiagLoader::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 360std::error_code 361DiagLoader::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 // FIXME: Why do we care about long strings? 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 375std::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 389CXDiagnosticSet 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