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