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