Home | History | Annotate | Download | only in Basic
      1 //===--- PartialDiagnostic.h - Diagnostic "closures" ------------*- 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 //  This file implements a partial diagnostic that can be emitted anwyhere
     11 //  in a DiagnosticBuilder stream.
     12 //
     13 //===----------------------------------------------------------------------===//
     14 
     15 #ifndef LLVM_CLANG_PARTIALDIAGNOSTIC_H
     16 #define LLVM_CLANG_PARTIALDIAGNOSTIC_H
     17 
     18 #include "clang/Basic/Diagnostic.h"
     19 #include "clang/Basic/SourceLocation.h"
     20 #include "llvm/ADT/STLExtras.h"
     21 #include "llvm/Support/DataTypes.h"
     22 #include <cassert>
     23 
     24 namespace clang {
     25 
     26 class PartialDiagnostic {
     27 public:
     28   enum {
     29       // The MaxArguments and MaxFixItHints member enum values from
     30       // DiagnosticsEngine are private but DiagnosticsEngine declares
     31       // PartialDiagnostic a friend.  These enum values are redeclared
     32       // here so that the nested Storage class below can access them.
     33       MaxArguments = DiagnosticsEngine::MaxArguments
     34   };
     35 
     36   struct Storage {
     37     Storage() : NumDiagArgs(0), NumDiagRanges(0) { }
     38 
     39     enum {
     40         /// MaxArguments - The maximum number of arguments we can hold. We
     41         /// currently only support up to 10 arguments (%0-%9).
     42         /// A single diagnostic with more than that almost certainly has to
     43         /// be simplified anyway.
     44         MaxArguments = PartialDiagnostic::MaxArguments
     45     };
     46 
     47     /// NumDiagArgs - This contains the number of entries in Arguments.
     48     unsigned char NumDiagArgs;
     49 
     50     /// NumDiagRanges - This is the number of ranges in the DiagRanges array.
     51     unsigned char NumDiagRanges;
     52 
     53     /// DiagArgumentsKind - This is an array of ArgumentKind::ArgumentKind enum
     54     /// values, with one for each argument.  This specifies whether the argument
     55     /// is in DiagArgumentsStr or in DiagArguments.
     56     unsigned char DiagArgumentsKind[MaxArguments];
     57 
     58     /// DiagArgumentsVal - The values for the various substitution positions.
     59     /// This is used when the argument is not an std::string. The specific value
     60     /// is mangled into an intptr_t and the interpretation depends on exactly
     61     /// what sort of argument kind it is.
     62     intptr_t DiagArgumentsVal[MaxArguments];
     63 
     64     /// \brief The values for the various substitution positions that have
     65     /// string arguments.
     66     std::string DiagArgumentsStr[MaxArguments];
     67 
     68     /// DiagRanges - The list of ranges added to this diagnostic.  It currently
     69     /// only support 10 ranges, could easily be extended if needed.
     70     CharSourceRange DiagRanges[10];
     71 
     72     /// FixItHints - If valid, provides a hint with some code
     73     /// to insert, remove, or modify at a particular position.
     74     SmallVector<FixItHint, 6>  FixItHints;
     75   };
     76 
     77   /// \brief An allocator for Storage objects, which uses a small cache to
     78   /// objects, used to reduce malloc()/free() traffic for partial diagnostics.
     79   class StorageAllocator {
     80     static const unsigned NumCached = 16;
     81     Storage Cached[NumCached];
     82     Storage *FreeList[NumCached];
     83     unsigned NumFreeListEntries;
     84 
     85   public:
     86     StorageAllocator();
     87     ~StorageAllocator();
     88 
     89     /// \brief Allocate new storage.
     90     Storage *Allocate() {
     91       if (NumFreeListEntries == 0)
     92         return new Storage;
     93 
     94       Storage *Result = FreeList[--NumFreeListEntries];
     95       Result->NumDiagArgs = 0;
     96       Result->NumDiagRanges = 0;
     97       Result->FixItHints.clear();
     98       return Result;
     99     }
    100 
    101     /// \brief Free the given storage object.
    102     void Deallocate(Storage *S) {
    103       if (S >= Cached && S <= Cached + NumCached) {
    104         FreeList[NumFreeListEntries++] = S;
    105         return;
    106       }
    107 
    108       delete S;
    109     }
    110   };
    111 
    112 private:
    113   // NOTE: Sema assumes that PartialDiagnostic is location-invariant
    114   // in the sense that its bits can be safely memcpy'ed and destructed
    115   // in the new location.
    116 
    117   /// DiagID - The diagnostic ID.
    118   mutable unsigned DiagID;
    119 
    120   /// DiagStorage - Storage for args and ranges.
    121   mutable Storage *DiagStorage;
    122 
    123   /// \brief Allocator used to allocate storage for this diagnostic.
    124   StorageAllocator *Allocator;
    125 
    126   /// \brief Retrieve storage for this particular diagnostic.
    127   Storage *getStorage() const {
    128     if (DiagStorage)
    129       return DiagStorage;
    130 
    131     if (Allocator)
    132       DiagStorage = Allocator->Allocate();
    133     else {
    134       assert(Allocator != reinterpret_cast<StorageAllocator *>(~uintptr_t(0)));
    135       DiagStorage = new Storage;
    136     }
    137     return DiagStorage;
    138   }
    139 
    140   void freeStorage() {
    141     if (!DiagStorage)
    142       return;
    143 
    144     // The hot path for PartialDiagnostic is when we just used it to wrap an ID
    145     // (typically so we have the flexibility of passing a more complex
    146     // diagnostic into the callee, but that does not commonly occur).
    147     //
    148     // Split this out into a slow function for silly compilers (*cough*) which
    149     // can't do decent partial inlining.
    150     freeStorageSlow();
    151   }
    152 
    153   void freeStorageSlow() {
    154     if (Allocator)
    155       Allocator->Deallocate(DiagStorage);
    156     else if (Allocator != reinterpret_cast<StorageAllocator *>(~uintptr_t(0)))
    157       delete DiagStorage;
    158     DiagStorage = 0;
    159   }
    160 
    161   void AddSourceRange(const CharSourceRange &R) const {
    162     if (!DiagStorage)
    163       DiagStorage = getStorage();
    164 
    165     assert(DiagStorage->NumDiagRanges <
    166            llvm::array_lengthof(DiagStorage->DiagRanges) &&
    167            "Too many arguments to diagnostic!");
    168     DiagStorage->DiagRanges[DiagStorage->NumDiagRanges++] = R;
    169   }
    170 
    171   void AddFixItHint(const FixItHint &Hint) const {
    172     if (Hint.isNull())
    173       return;
    174 
    175     if (!DiagStorage)
    176       DiagStorage = getStorage();
    177 
    178     DiagStorage->FixItHints.push_back(Hint);
    179   }
    180 
    181 public:
    182   PartialDiagnostic(unsigned DiagID, StorageAllocator &Allocator)
    183     : DiagID(DiagID), DiagStorage(0), Allocator(&Allocator) { }
    184 
    185   PartialDiagnostic(const PartialDiagnostic &Other)
    186     : DiagID(Other.DiagID), DiagStorage(0), Allocator(Other.Allocator)
    187   {
    188     if (Other.DiagStorage) {
    189       DiagStorage = getStorage();
    190       *DiagStorage = *Other.DiagStorage;
    191     }
    192   }
    193 
    194   PartialDiagnostic(const PartialDiagnostic &Other, Storage *DiagStorage)
    195     : DiagID(Other.DiagID), DiagStorage(DiagStorage),
    196       Allocator(reinterpret_cast<StorageAllocator *>(~uintptr_t(0)))
    197   {
    198     if (Other.DiagStorage)
    199       *this->DiagStorage = *Other.DiagStorage;
    200   }
    201 
    202   PartialDiagnostic(const Diagnostic &Other, StorageAllocator &Allocator)
    203     : DiagID(Other.getID()), DiagStorage(0), Allocator(&Allocator)
    204   {
    205     // Copy arguments.
    206     for (unsigned I = 0, N = Other.getNumArgs(); I != N; ++I) {
    207       if (Other.getArgKind(I) == DiagnosticsEngine::ak_std_string)
    208         AddString(Other.getArgStdStr(I));
    209       else
    210         AddTaggedVal(Other.getRawArg(I), Other.getArgKind(I));
    211     }
    212 
    213     // Copy source ranges.
    214     for (unsigned I = 0, N = Other.getNumRanges(); I != N; ++I)
    215       AddSourceRange(Other.getRange(I));
    216 
    217     // Copy fix-its.
    218     for (unsigned I = 0, N = Other.getNumFixItHints(); I != N; ++I)
    219       AddFixItHint(Other.getFixItHint(I));
    220   }
    221 
    222   PartialDiagnostic &operator=(const PartialDiagnostic &Other) {
    223     DiagID = Other.DiagID;
    224     if (Other.DiagStorage) {
    225       if (!DiagStorage)
    226         DiagStorage = getStorage();
    227 
    228       *DiagStorage = *Other.DiagStorage;
    229     } else {
    230       freeStorage();
    231     }
    232 
    233     return *this;
    234   }
    235 
    236   ~PartialDiagnostic() {
    237     freeStorage();
    238   }
    239 
    240   unsigned getDiagID() const { return DiagID; }
    241 
    242   void AddTaggedVal(intptr_t V, DiagnosticsEngine::ArgumentKind Kind) const {
    243     if (!DiagStorage)
    244       DiagStorage = getStorage();
    245 
    246     assert(DiagStorage->NumDiagArgs < Storage::MaxArguments &&
    247            "Too many arguments to diagnostic!");
    248     DiagStorage->DiagArgumentsKind[DiagStorage->NumDiagArgs] = Kind;
    249     DiagStorage->DiagArgumentsVal[DiagStorage->NumDiagArgs++] = V;
    250   }
    251 
    252   void AddString(StringRef V) const {
    253     if (!DiagStorage)
    254       DiagStorage = getStorage();
    255 
    256     assert(DiagStorage->NumDiagArgs < Storage::MaxArguments &&
    257            "Too many arguments to diagnostic!");
    258     DiagStorage->DiagArgumentsKind[DiagStorage->NumDiagArgs]
    259       = DiagnosticsEngine::ak_std_string;
    260     DiagStorage->DiagArgumentsStr[DiagStorage->NumDiagArgs++] = V;
    261   }
    262 
    263   void Emit(const DiagnosticBuilder &DB) const {
    264     if (!DiagStorage)
    265       return;
    266 
    267     // Add all arguments.
    268     for (unsigned i = 0, e = DiagStorage->NumDiagArgs; i != e; ++i) {
    269       if ((DiagnosticsEngine::ArgumentKind)DiagStorage->DiagArgumentsKind[i]
    270             == DiagnosticsEngine::ak_std_string)
    271         DB.AddString(DiagStorage->DiagArgumentsStr[i]);
    272       else
    273         DB.AddTaggedVal(DiagStorage->DiagArgumentsVal[i],
    274             (DiagnosticsEngine::ArgumentKind)DiagStorage->DiagArgumentsKind[i]);
    275     }
    276 
    277     // Add all ranges.
    278     for (unsigned i = 0, e = DiagStorage->NumDiagRanges; i != e; ++i)
    279       DB.AddSourceRange(DiagStorage->DiagRanges[i]);
    280 
    281     // Add all fix-its.
    282     for (unsigned i = 0, e = DiagStorage->FixItHints.size(); i != e; ++i)
    283       DB.AddFixItHint(DiagStorage->FixItHints[i]);
    284   }
    285 
    286   /// \brief Clear out this partial diagnostic, giving it a new diagnostic ID
    287   /// and removing all of its arguments, ranges, and fix-it hints.
    288   void Reset(unsigned DiagID = 0) {
    289     this->DiagID = DiagID;
    290     freeStorage();
    291   }
    292 
    293   bool hasStorage() const { return DiagStorage != 0; }
    294 
    295   friend const PartialDiagnostic &operator<<(const PartialDiagnostic &PD,
    296                                              unsigned I) {
    297     PD.AddTaggedVal(I, DiagnosticsEngine::ak_uint);
    298     return PD;
    299   }
    300 
    301   friend const PartialDiagnostic &operator<<(const PartialDiagnostic &PD,
    302                                              int I) {
    303     PD.AddTaggedVal(I, DiagnosticsEngine::ak_sint);
    304     return PD;
    305   }
    306 
    307   friend inline const PartialDiagnostic &operator<<(const PartialDiagnostic &PD,
    308                                                     const char *S) {
    309     PD.AddTaggedVal(reinterpret_cast<intptr_t>(S),
    310                     DiagnosticsEngine::ak_c_string);
    311     return PD;
    312   }
    313 
    314   friend inline const PartialDiagnostic &operator<<(const PartialDiagnostic &PD,
    315                                                     StringRef S) {
    316 
    317     PD.AddString(S);
    318     return PD;
    319   }
    320 
    321   friend inline const PartialDiagnostic &operator<<(const PartialDiagnostic &PD,
    322                                                     const SourceRange &R) {
    323     PD.AddSourceRange(CharSourceRange::getTokenRange(R));
    324     return PD;
    325   }
    326 
    327   friend inline const PartialDiagnostic &operator<<(const PartialDiagnostic &PD,
    328                                                     const CharSourceRange &R) {
    329     PD.AddSourceRange(R);
    330     return PD;
    331   }
    332 
    333   friend const PartialDiagnostic &operator<<(const PartialDiagnostic &PD,
    334                                              const FixItHint &Hint) {
    335     PD.AddFixItHint(Hint);
    336     return PD;
    337   }
    338 
    339 };
    340 
    341 inline const DiagnosticBuilder &operator<<(const DiagnosticBuilder &DB,
    342                                            const PartialDiagnostic &PD) {
    343   PD.Emit(DB);
    344   return DB;
    345 }
    346 
    347 /// \brief A partial diagnostic along with the source location where this
    348 /// diagnostic occurs.
    349 typedef std::pair<SourceLocation, PartialDiagnostic> PartialDiagnosticAt;
    350 
    351 }  // end namespace clang
    352 #endif
    353