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