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