]> CyberLeo.Net >> Repos - FreeBSD/stable/9.git/blob - contrib/llvm/tools/clang/include/clang/Basic/PartialDiagnostic.h
MFC r244628:
[FreeBSD/stable/9.git] / contrib / llvm / tools / clang / include / clang / Basic / PartialDiagnostic.h
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