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) { }
     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 Specifies for each argument whether it is in DiagArgumentsStr
     54     /// or in DiagArguments.
     55     unsigned char DiagArgumentsKind[MaxArguments];
     56 
     57     /// \brief The values for the various substitution positions.
     58     ///
     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     /// \brief The list of ranges added to this diagnostic.
     69     SmallVector<CharSourceRange, 8> DiagRanges;
     70 
     71     /// \brief If valid, provides a hint with some code to insert, remove, or
     72     /// modify at a particular position.
     73     SmallVector<FixItHint, 6>  FixItHints;
     74   };
     75 
     76   /// \brief An allocator for Storage objects, which uses a small cache to
     77   /// objects, used to reduce malloc()/free() traffic for partial diagnostics.
     78   class StorageAllocator {
     79     static const unsigned NumCached = 16;
     80     Storage Cached[NumCached];
     81     Storage *FreeList[NumCached];
     82     unsigned NumFreeListEntries;
     83 
     84   public:
     85     StorageAllocator();
     86     ~StorageAllocator();
     87 
     88     /// \brief Allocate new storage.
     89     Storage *Allocate() {
     90       if (NumFreeListEntries == 0)
     91         return new Storage;
     92 
     93       Storage *Result = FreeList[--NumFreeListEntries];
     94       Result->NumDiagArgs = 0;
     95       Result->DiagRanges.clear();
     96       Result->FixItHints.clear();
     97       return Result;
     98     }
     99 
    100     /// \brief Free the given storage object.
    101     void Deallocate(Storage *S) {
    102       if (S >= Cached && S <= Cached + NumCached) {
    103         FreeList[NumFreeListEntries++] = S;
    104         return;
    105       }
    106 
    107       delete S;
    108     }
    109   };
    110 
    111 private:
    112   // NOTE: Sema assumes that PartialDiagnostic is location-invariant
    113   // in the sense that its bits can be safely memcpy'ed and destructed
    114   // in the new location.
    115 
    116   /// \brief The diagnostic ID.
    117   mutable unsigned DiagID;
    118 
    119   /// \brief Storage for args and ranges.
    120   mutable Storage *DiagStorage;
    121 
    122   /// \brief Allocator used to allocate storage for this diagnostic.
    123   StorageAllocator *Allocator;
    124 
    125   /// \brief Retrieve storage for this particular diagnostic.
    126   Storage *getStorage() const {
    127     if (DiagStorage)
    128       return DiagStorage;
    129 
    130     if (Allocator)
    131       DiagStorage = Allocator->Allocate();
    132     else {
    133       assert(Allocator != reinterpret_cast<StorageAllocator *>(~uintptr_t(0)));
    134       DiagStorage = new Storage;
    135     }
    136     return DiagStorage;
    137   }
    138 
    139   void freeStorage() {
    140     if (!DiagStorage)
    141       return;
    142 
    143     // The hot path for PartialDiagnostic is when we just used it to wrap an ID
    144     // (typically so we have the flexibility of passing a more complex
    145     // diagnostic into the callee, but that does not commonly occur).
    146     //
    147     // Split this out into a slow function for silly compilers (*cough*) which
    148     // can't do decent partial inlining.
    149     freeStorageSlow();
    150   }
    151 
    152   void freeStorageSlow() {
    153     if (Allocator)
    154       Allocator->Deallocate(DiagStorage);
    155     else if (Allocator != reinterpret_cast<StorageAllocator *>(~uintptr_t(0)))
    156       delete DiagStorage;
    157     DiagStorage = nullptr;
    158   }
    159 
    160   void AddSourceRange(const CharSourceRange &R) const {
    161     if (!DiagStorage)
    162       DiagStorage = getStorage();
    163 
    164     DiagStorage->DiagRanges.push_back(R);
    165   }
    166 
    167   void AddFixItHint(const FixItHint &Hint) const {
    168     if (Hint.isNull())
    169       return;
    170 
    171     if (!DiagStorage)
    172       DiagStorage = getStorage();
    173 
    174     DiagStorage->FixItHints.push_back(Hint);
    175   }
    176 
    177 public:
    178   struct NullDiagnostic {};
    179   /// \brief Create a null partial diagnostic, which cannot carry a payload,
    180   /// and only exists to be swapped with a real partial diagnostic.
    181   PartialDiagnostic(NullDiagnostic)
    182     : DiagID(0), DiagStorage(nullptr), Allocator(nullptr) { }
    183 
    184   PartialDiagnostic(unsigned DiagID, StorageAllocator &Allocator)
    185     : DiagID(DiagID), DiagStorage(nullptr), Allocator(&Allocator) { }
    186 
    187   PartialDiagnostic(const PartialDiagnostic &Other)
    188     : DiagID(Other.DiagID), DiagStorage(nullptr), Allocator(Other.Allocator)
    189   {
    190     if (Other.DiagStorage) {
    191       DiagStorage = getStorage();
    192       *DiagStorage = *Other.DiagStorage;
    193     }
    194   }
    195 
    196   PartialDiagnostic(PartialDiagnostic &&Other)
    197     : DiagID(Other.DiagID), DiagStorage(Other.DiagStorage),
    198       Allocator(Other.Allocator) {
    199     Other.DiagStorage = nullptr;
    200   }
    201 
    202   PartialDiagnostic(const PartialDiagnostic &Other, Storage *DiagStorage)
    203     : DiagID(Other.DiagID), DiagStorage(DiagStorage),
    204       Allocator(reinterpret_cast<StorageAllocator *>(~uintptr_t(0)))
    205   {
    206     if (Other.DiagStorage)
    207       *this->DiagStorage = *Other.DiagStorage;
    208   }
    209 
    210   PartialDiagnostic(const Diagnostic &Other, StorageAllocator &Allocator)
    211     : DiagID(Other.getID()), DiagStorage(nullptr), Allocator(&Allocator)
    212   {
    213     // Copy arguments.
    214     for (unsigned I = 0, N = Other.getNumArgs(); I != N; ++I) {
    215       if (Other.getArgKind(I) == DiagnosticsEngine::ak_std_string)
    216         AddString(Other.getArgStdStr(I));
    217       else
    218         AddTaggedVal(Other.getRawArg(I), Other.getArgKind(I));
    219     }
    220 
    221     // Copy source ranges.
    222     for (unsigned I = 0, N = Other.getNumRanges(); I != N; ++I)
    223       AddSourceRange(Other.getRange(I));
    224 
    225     // Copy fix-its.
    226     for (unsigned I = 0, N = Other.getNumFixItHints(); I != N; ++I)
    227       AddFixItHint(Other.getFixItHint(I));
    228   }
    229 
    230   PartialDiagnostic &operator=(const PartialDiagnostic &Other) {
    231     DiagID = Other.DiagID;
    232     if (Other.DiagStorage) {
    233       if (!DiagStorage)
    234         DiagStorage = getStorage();
    235 
    236       *DiagStorage = *Other.DiagStorage;
    237     } else {
    238       freeStorage();
    239     }
    240 
    241     return *this;
    242   }
    243 
    244   PartialDiagnostic &operator=(PartialDiagnostic &&Other) {
    245     freeStorage();
    246 
    247     DiagID = Other.DiagID;
    248     DiagStorage = Other.DiagStorage;
    249     Allocator = Other.Allocator;
    250 
    251     Other.DiagStorage = nullptr;
    252     return *this;
    253   }
    254 
    255   ~PartialDiagnostic() {
    256     freeStorage();
    257   }
    258 
    259   void swap(PartialDiagnostic &PD) {
    260     std::swap(DiagID, PD.DiagID);
    261     std::swap(DiagStorage, PD.DiagStorage);
    262     std::swap(Allocator, PD.Allocator);
    263   }
    264 
    265   unsigned getDiagID() const { return DiagID; }
    266 
    267   void AddTaggedVal(intptr_t V, DiagnosticsEngine::ArgumentKind Kind) 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] = Kind;
    274     DiagStorage->DiagArgumentsVal[DiagStorage->NumDiagArgs++] = V;
    275   }
    276 
    277   void AddString(StringRef V) const {
    278     if (!DiagStorage)
    279       DiagStorage = getStorage();
    280 
    281     assert(DiagStorage->NumDiagArgs < Storage::MaxArguments &&
    282            "Too many arguments to diagnostic!");
    283     DiagStorage->DiagArgumentsKind[DiagStorage->NumDiagArgs]
    284       = DiagnosticsEngine::ak_std_string;
    285     DiagStorage->DiagArgumentsStr[DiagStorage->NumDiagArgs++] = V;
    286   }
    287 
    288   void Emit(const DiagnosticBuilder &DB) const {
    289     if (!DiagStorage)
    290       return;
    291 
    292     // Add all arguments.
    293     for (unsigned i = 0, e = DiagStorage->NumDiagArgs; i != e; ++i) {
    294       if ((DiagnosticsEngine::ArgumentKind)DiagStorage->DiagArgumentsKind[i]
    295             == DiagnosticsEngine::ak_std_string)
    296         DB.AddString(DiagStorage->DiagArgumentsStr[i]);
    297       else
    298         DB.AddTaggedVal(DiagStorage->DiagArgumentsVal[i],
    299             (DiagnosticsEngine::ArgumentKind)DiagStorage->DiagArgumentsKind[i]);
    300     }
    301 
    302     // Add all ranges.
    303     for (const CharSourceRange &Range : DiagStorage->DiagRanges)
    304       DB.AddSourceRange(Range);
    305 
    306     // Add all fix-its.
    307     for (const FixItHint &Fix : DiagStorage->FixItHints)
    308       DB.AddFixItHint(Fix);
    309   }
    310 
    311   void EmitToString(DiagnosticsEngine &Diags,
    312                     SmallVectorImpl<char> &Buf) const {
    313     // FIXME: It should be possible to render a diagnostic to a string without
    314     //        messing with the state of the diagnostics engine.
    315     DiagnosticBuilder DB(Diags.Report(getDiagID()));
    316     Emit(DB);
    317     DB.FlushCounts();
    318     Diagnostic(&Diags).FormatDiagnostic(Buf);
    319     DB.Clear();
    320     Diags.Clear();
    321   }
    322 
    323   /// \brief Clear out this partial diagnostic, giving it a new diagnostic ID
    324   /// and removing all of its arguments, ranges, and fix-it hints.
    325   void Reset(unsigned DiagID = 0) {
    326     this->DiagID = DiagID;
    327     freeStorage();
    328   }
    329 
    330   bool hasStorage() const { return DiagStorage != nullptr; }
    331 
    332   friend const PartialDiagnostic &operator<<(const PartialDiagnostic &PD,
    333                                              unsigned I) {
    334     PD.AddTaggedVal(I, DiagnosticsEngine::ak_uint);
    335     return PD;
    336   }
    337 
    338   friend const PartialDiagnostic &operator<<(const PartialDiagnostic &PD,
    339                                              int I) {
    340     PD.AddTaggedVal(I, DiagnosticsEngine::ak_sint);
    341     return PD;
    342   }
    343 
    344   friend inline const PartialDiagnostic &operator<<(const PartialDiagnostic &PD,
    345                                                     const char *S) {
    346     PD.AddTaggedVal(reinterpret_cast<intptr_t>(S),
    347                     DiagnosticsEngine::ak_c_string);
    348     return PD;
    349   }
    350 
    351   friend inline const PartialDiagnostic &operator<<(const PartialDiagnostic &PD,
    352                                                     StringRef S) {
    353 
    354     PD.AddString(S);
    355     return PD;
    356   }
    357 
    358   friend inline const PartialDiagnostic &operator<<(const PartialDiagnostic &PD,
    359                                                     const IdentifierInfo *II) {
    360     PD.AddTaggedVal(reinterpret_cast<intptr_t>(II),
    361                     DiagnosticsEngine::ak_identifierinfo);
    362     return PD;
    363   }
    364 
    365   // Adds a DeclContext to the diagnostic. The enable_if template magic is here
    366   // so that we only match those arguments that are (statically) DeclContexts;
    367   // other arguments that derive from DeclContext (e.g., RecordDecls) will not
    368   // match.
    369   template<typename T>
    370   friend inline
    371   typename std::enable_if<std::is_same<T, DeclContext>::value,
    372                           const PartialDiagnostic &>::type
    373   operator<<(const PartialDiagnostic &PD, T *DC) {
    374     PD.AddTaggedVal(reinterpret_cast<intptr_t>(DC),
    375                     DiagnosticsEngine::ak_declcontext);
    376     return PD;
    377   }
    378 
    379   friend inline const PartialDiagnostic &operator<<(const PartialDiagnostic &PD,
    380                                                     const SourceRange &R) {
    381     PD.AddSourceRange(CharSourceRange::getTokenRange(R));
    382     return PD;
    383   }
    384 
    385   friend inline const PartialDiagnostic &operator<<(const PartialDiagnostic &PD,
    386                                                     const CharSourceRange &R) {
    387     PD.AddSourceRange(R);
    388     return PD;
    389   }
    390 
    391   friend const PartialDiagnostic &operator<<(const PartialDiagnostic &PD,
    392                                              const FixItHint &Hint) {
    393     PD.AddFixItHint(Hint);
    394     return PD;
    395   }
    396 
    397 };
    398 
    399 inline const DiagnosticBuilder &operator<<(const DiagnosticBuilder &DB,
    400                                            const PartialDiagnostic &PD) {
    401   PD.Emit(DB);
    402   return DB;
    403 }
    404 
    405 /// \brief A partial diagnostic along with the source location where this
    406 /// diagnostic occurs.
    407 typedef std::pair<SourceLocation, PartialDiagnostic> PartialDiagnosticAt;
    408 
    409 }  // end namespace clang
    410 #endif
    411