• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 {
StorageStorage39     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.
Allocate()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.
Deallocate(Storage * S)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.
getStorage()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 
freeStorage()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 
freeStorageSlow()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 
AddSourceRange(const CharSourceRange & R)160   void AddSourceRange(const CharSourceRange &R) const {
161     if (!DiagStorage)
162       DiagStorage = getStorage();
163 
164     DiagStorage->DiagRanges.push_back(R);
165   }
166 
AddFixItHint(const FixItHint & Hint)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.
PartialDiagnostic(NullDiagnostic)181   PartialDiagnostic(NullDiagnostic)
182     : DiagID(0), DiagStorage(nullptr), Allocator(nullptr) { }
183 
PartialDiagnostic(unsigned DiagID,StorageAllocator & Allocator)184   PartialDiagnostic(unsigned DiagID, StorageAllocator &Allocator)
185     : DiagID(DiagID), DiagStorage(nullptr), Allocator(&Allocator) { }
186 
PartialDiagnostic(const PartialDiagnostic & Other)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 
PartialDiagnostic(PartialDiagnostic && Other)196   PartialDiagnostic(PartialDiagnostic &&Other)
197     : DiagID(Other.DiagID), DiagStorage(Other.DiagStorage),
198       Allocator(Other.Allocator) {
199     Other.DiagStorage = nullptr;
200   }
201 
PartialDiagnostic(const PartialDiagnostic & Other,Storage * DiagStorage)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 
PartialDiagnostic(const Diagnostic & Other,StorageAllocator & Allocator)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 
~PartialDiagnostic()255   ~PartialDiagnostic() {
256     freeStorage();
257   }
258 
swap(PartialDiagnostic & PD)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 
getDiagID()265   unsigned getDiagID() const { return DiagID; }
266 
AddTaggedVal(intptr_t V,DiagnosticsEngine::ArgumentKind Kind)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 
AddString(StringRef V)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 
Emit(const DiagnosticBuilder & DB)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 
EmitToString(DiagnosticsEngine & Diags,SmallVectorImpl<char> & Buf)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 
hasStorage()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