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_BASIC_PARTIALDIAGNOSTIC_H
17#define LLVM_CLANG_BASIC_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) { }
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.
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.
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
111private:
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.
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
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
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
160  void AddSourceRange(const CharSourceRange &R) const {
161    if (!DiagStorage)
162      DiagStorage = getStorage();
163
164    DiagStorage->DiagRanges.push_back(R);
165  }
166
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
177public:
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.
181  PartialDiagnostic(NullDiagnostic)
182    : DiagID(0), DiagStorage(nullptr), Allocator(nullptr) { }
183
184  PartialDiagnostic(unsigned DiagID, StorageAllocator &Allocator)
185    : DiagID(DiagID), DiagStorage(nullptr), Allocator(&Allocator) { }
186
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
196  PartialDiagnostic(PartialDiagnostic &&Other)
197    : DiagID(Other.DiagID), DiagStorage(Other.DiagStorage),
198      Allocator(Other.Allocator) {
199    Other.DiagStorage = nullptr;
200  }
201
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
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
255  ~PartialDiagnostic() {
256    freeStorage();
257  }
258
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
265  unsigned getDiagID() const { return DiagID; }
266
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
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
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
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
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                                                    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
399inline 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.
407typedef std::pair<SourceLocation, PartialDiagnostic> PartialDiagnosticAt;
408
409}  // end namespace clang
410#endif
411