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