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