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