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