1//===- PartialDiagnostic.h - Diagnostic "closures" --------------*- C++ -*-===//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8//
9/// \file
10/// Implements a partial diagnostic that can be emitted anwyhere
11/// in a DiagnosticBuilder stream.
12//
13//===----------------------------------------------------------------------===//
14
15#ifndef LLVM_CLANG_BASIC_PARTIALDIAGNOSTIC_H
16#define LLVM_CLANG_BASIC_PARTIALDIAGNOSTIC_H
17
18#include "clang/Basic/Diagnostic.h"
19#include "clang/Basic/LLVM.h"
20#include "clang/Basic/SourceLocation.h"
21#include "llvm/ADT/SmallVector.h"
22#include "llvm/ADT/StringRef.h"
23#include <cassert>
24#include <cstdint>
25#include <string>
26#include <type_traits>
27#include <utility>
28
29namespace clang {
30
31class DeclContext;
32class IdentifierInfo;
33
34class PartialDiagnostic {
35public:
36  enum {
37      // The MaxArguments and MaxFixItHints member enum values from
38      // DiagnosticsEngine are private but DiagnosticsEngine declares
39      // PartialDiagnostic a friend.  These enum values are redeclared
40      // here so that the nested Storage class below can access them.
41      MaxArguments = DiagnosticsEngine::MaxArguments
42  };
43
44  struct Storage {
45    enum {
46        /// The maximum number of arguments we can hold. We
47        /// currently only support up to 10 arguments (%0-%9).
48        ///
49        /// A single diagnostic with more than that almost certainly has to
50        /// be simplified anyway.
51        MaxArguments = PartialDiagnostic::MaxArguments
52    };
53
54    /// The number of entries in Arguments.
55    unsigned char NumDiagArgs = 0;
56
57    /// Specifies for each argument whether it is in DiagArgumentsStr
58    /// or in DiagArguments.
59    unsigned char DiagArgumentsKind[MaxArguments];
60
61    /// The values for the various substitution positions.
62    ///
63    /// This is used when the argument is not an std::string. The specific value
64    /// is mangled into an intptr_t and the interpretation depends on exactly
65    /// what sort of argument kind it is.
66    intptr_t DiagArgumentsVal[MaxArguments];
67
68    /// The values for the various substitution positions that have
69    /// string arguments.
70    std::string DiagArgumentsStr[MaxArguments];
71
72    /// The list of ranges added to this diagnostic.
73    SmallVector<CharSourceRange, 8> DiagRanges;
74
75    /// 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    Storage() = default;
80  };
81
82  /// An allocator for Storage objects, which uses a small cache to
83  /// objects, used to reduce malloc()/free() traffic for partial diagnostics.
84  class StorageAllocator {
85    static const unsigned NumCached = 16;
86    Storage Cached[NumCached];
87    Storage *FreeList[NumCached];
88    unsigned NumFreeListEntries;
89
90  public:
91    StorageAllocator();
92    ~StorageAllocator();
93
94    /// Allocate new storage.
95    Storage *Allocate() {
96      if (NumFreeListEntries == 0)
97        return new Storage;
98
99      Storage *Result = FreeList[--NumFreeListEntries];
100      Result->NumDiagArgs = 0;
101      Result->DiagRanges.clear();
102      Result->FixItHints.clear();
103      return Result;
104    }
105
106    /// Free the given storage object.
107    void Deallocate(Storage *S) {
108      if (S >= Cached && S <= Cached + NumCached) {
109        FreeList[NumFreeListEntries++] = S;
110        return;
111      }
112
113      delete S;
114    }
115  };
116
117private:
118  // NOTE: Sema assumes that PartialDiagnostic is location-invariant
119  // in the sense that its bits can be safely memcpy'ed and destructed
120  // in the new location.
121
122  /// The diagnostic ID.
123  mutable unsigned DiagID = 0;
124
125  /// Storage for args and ranges.
126  mutable Storage *DiagStorage = nullptr;
127
128  /// Allocator used to allocate storage for this diagnostic.
129  StorageAllocator *Allocator = nullptr;
130
131  /// Retrieve storage for this particular diagnostic.
132  Storage *getStorage() const {
133    if (DiagStorage)
134      return DiagStorage;
135
136    if (Allocator)
137      DiagStorage = Allocator->Allocate();
138    else {
139      assert(Allocator != reinterpret_cast<StorageAllocator *>(~uintptr_t(0)));
140      DiagStorage = new Storage;
141    }
142    return DiagStorage;
143  }
144
145  void freeStorage() {
146    if (!DiagStorage)
147      return;
148
149    // The hot path for PartialDiagnostic is when we just used it to wrap an ID
150    // (typically so we have the flexibility of passing a more complex
151    // diagnostic into the callee, but that does not commonly occur).
152    //
153    // Split this out into a slow function for silly compilers (*cough*) which
154    // can't do decent partial inlining.
155    freeStorageSlow();
156  }
157
158  void freeStorageSlow() {
159    if (Allocator)
160      Allocator->Deallocate(DiagStorage);
161    else if (Allocator != reinterpret_cast<StorageAllocator *>(~uintptr_t(0)))
162      delete DiagStorage;
163    DiagStorage = nullptr;
164  }
165
166  void AddSourceRange(const CharSourceRange &R) const {
167    if (!DiagStorage)
168      DiagStorage = getStorage();
169
170    DiagStorage->DiagRanges.push_back(R);
171  }
172
173  void AddFixItHint(const FixItHint &Hint) const {
174    if (Hint.isNull())
175      return;
176
177    if (!DiagStorage)
178      DiagStorage = getStorage();
179
180    DiagStorage->FixItHints.push_back(Hint);
181  }
182
183public:
184  struct NullDiagnostic {};
185
186  /// 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
190  PartialDiagnostic(unsigned DiagID, StorageAllocator &Allocator)
191      : DiagID(DiagID), Allocator(&Allocator) {}
192
193  PartialDiagnostic(const PartialDiagnostic &Other)
194      : DiagID(Other.DiagID), Allocator(Other.Allocator) {
195    if (Other.DiagStorage) {
196      DiagStorage = getStorage();
197      *DiagStorage = *Other.DiagStorage;
198    }
199  }
200
201  PartialDiagnostic(PartialDiagnostic &&Other)
202      : DiagID(Other.DiagID), DiagStorage(Other.DiagStorage),
203        Allocator(Other.Allocator) {
204    Other.DiagStorage = nullptr;
205  }
206
207  PartialDiagnostic(const PartialDiagnostic &Other, Storage *DiagStorage)
208      : DiagID(Other.DiagID), DiagStorage(DiagStorage),
209        Allocator(reinterpret_cast<StorageAllocator *>(~uintptr_t(0))) {
210    if (Other.DiagStorage)
211      *this->DiagStorage = *Other.DiagStorage;
212  }
213
214  PartialDiagnostic(const Diagnostic &Other, StorageAllocator &Allocator)
215      : DiagID(Other.getID()), Allocator(&Allocator) {
216    // Copy arguments.
217    for (unsigned I = 0, N = Other.getNumArgs(); I != N; ++I) {
218      if (Other.getArgKind(I) == DiagnosticsEngine::ak_std_string)
219        AddString(Other.getArgStdStr(I));
220      else
221        AddTaggedVal(Other.getRawArg(I), Other.getArgKind(I));
222    }
223
224    // Copy source ranges.
225    for (unsigned I = 0, N = Other.getNumRanges(); I != N; ++I)
226      AddSourceRange(Other.getRange(I));
227
228    // Copy fix-its.
229    for (unsigned I = 0, N = Other.getNumFixItHints(); I != N; ++I)
230      AddFixItHint(Other.getFixItHint(I));
231  }
232
233  PartialDiagnostic &operator=(const PartialDiagnostic &Other) {
234    DiagID = Other.DiagID;
235    if (Other.DiagStorage) {
236      if (!DiagStorage)
237        DiagStorage = getStorage();
238
239      *DiagStorage = *Other.DiagStorage;
240    } else {
241      freeStorage();
242    }
243
244    return *this;
245  }
246
247  PartialDiagnostic &operator=(PartialDiagnostic &&Other) {
248    freeStorage();
249
250    DiagID = Other.DiagID;
251    DiagStorage = Other.DiagStorage;
252    Allocator = Other.Allocator;
253
254    Other.DiagStorage = nullptr;
255    return *this;
256  }
257
258  ~PartialDiagnostic() {
259    freeStorage();
260  }
261
262  void swap(PartialDiagnostic &PD) {
263    std::swap(DiagID, PD.DiagID);
264    std::swap(DiagStorage, PD.DiagStorage);
265    std::swap(Allocator, PD.Allocator);
266  }
267
268  unsigned getDiagID() const { return DiagID; }
269
270  void AddTaggedVal(intptr_t V, DiagnosticsEngine::ArgumentKind Kind) const {
271    if (!DiagStorage)
272      DiagStorage = getStorage();
273
274    assert(DiagStorage->NumDiagArgs < Storage::MaxArguments &&
275           "Too many arguments to diagnostic!");
276    DiagStorage->DiagArgumentsKind[DiagStorage->NumDiagArgs] = Kind;
277    DiagStorage->DiagArgumentsVal[DiagStorage->NumDiagArgs++] = V;
278  }
279
280  void AddString(StringRef V) const {
281    if (!DiagStorage)
282      DiagStorage = getStorage();
283
284    assert(DiagStorage->NumDiagArgs < Storage::MaxArguments &&
285           "Too many arguments to diagnostic!");
286    DiagStorage->DiagArgumentsKind[DiagStorage->NumDiagArgs]
287      = DiagnosticsEngine::ak_std_string;
288    DiagStorage->DiagArgumentsStr[DiagStorage->NumDiagArgs++] = V;
289  }
290
291  void Emit(const DiagnosticBuilder &DB) const {
292    if (!DiagStorage)
293      return;
294
295    // Add all arguments.
296    for (unsigned i = 0, e = DiagStorage->NumDiagArgs; i != e; ++i) {
297      if ((DiagnosticsEngine::ArgumentKind)DiagStorage->DiagArgumentsKind[i]
298            == DiagnosticsEngine::ak_std_string)
299        DB.AddString(DiagStorage->DiagArgumentsStr[i]);
300      else
301        DB.AddTaggedVal(DiagStorage->DiagArgumentsVal[i],
302            (DiagnosticsEngine::ArgumentKind)DiagStorage->DiagArgumentsKind[i]);
303    }
304
305    // Add all ranges.
306    for (const CharSourceRange &Range : DiagStorage->DiagRanges)
307      DB.AddSourceRange(Range);
308
309    // Add all fix-its.
310    for (const FixItHint &Fix : DiagStorage->FixItHints)
311      DB.AddFixItHint(Fix);
312  }
313
314  void EmitToString(DiagnosticsEngine &Diags,
315                    SmallVectorImpl<char> &Buf) const {
316    // FIXME: It should be possible to render a diagnostic to a string without
317    //        messing with the state of the diagnostics engine.
318    DiagnosticBuilder DB(Diags.Report(getDiagID()));
319    Emit(DB);
320    DB.FlushCounts();
321    Diagnostic(&Diags).FormatDiagnostic(Buf);
322    DB.Clear();
323    Diags.Clear();
324  }
325
326  /// Clear out this partial diagnostic, giving it a new diagnostic ID
327  /// and removing all of its arguments, ranges, and fix-it hints.
328  void Reset(unsigned DiagID = 0) {
329    this->DiagID = DiagID;
330    freeStorage();
331  }
332
333  bool hasStorage() const { return DiagStorage != nullptr; }
334
335  /// Retrieve the string argument at the given index.
336  StringRef getStringArg(unsigned I) {
337    assert(DiagStorage && "No diagnostic storage?");
338    assert(I < DiagStorage->NumDiagArgs && "Not enough diagnostic args");
339    assert(DiagStorage->DiagArgumentsKind[I]
340             == DiagnosticsEngine::ak_std_string && "Not a string arg");
341    return DiagStorage->DiagArgumentsStr[I];
342  }
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 IdentifierInfo *II) {
372    PD.AddTaggedVal(reinterpret_cast<intptr_t>(II),
373                    DiagnosticsEngine::ak_identifierinfo);
374    return PD;
375  }
376
377  // Adds a DeclContext to the diagnostic. The enable_if template magic is here
378  // so that we only match those arguments that are (statically) DeclContexts;
379  // other arguments that derive from DeclContext (e.g., RecordDecls) will not
380  // match.
381  template<typename T>
382  friend inline
383  typename std::enable_if<std::is_same<T, DeclContext>::value,
384                          const PartialDiagnostic &>::type
385  operator<<(const PartialDiagnostic &PD, T *DC) {
386    PD.AddTaggedVal(reinterpret_cast<intptr_t>(DC),
387                    DiagnosticsEngine::ak_declcontext);
388    return PD;
389  }
390
391  friend inline const PartialDiagnostic &operator<<(const PartialDiagnostic &PD,
392                                                    SourceRange R) {
393    PD.AddSourceRange(CharSourceRange::getTokenRange(R));
394    return PD;
395  }
396
397  friend inline const PartialDiagnostic &operator<<(const PartialDiagnostic &PD,
398                                                    const CharSourceRange &R) {
399    PD.AddSourceRange(R);
400    return PD;
401  }
402
403  friend const PartialDiagnostic &operator<<(const PartialDiagnostic &PD,
404                                             const FixItHint &Hint) {
405    PD.AddFixItHint(Hint);
406    return PD;
407  }
408};
409
410inline const DiagnosticBuilder &operator<<(const DiagnosticBuilder &DB,
411                                           const PartialDiagnostic &PD) {
412  PD.Emit(DB);
413  return DB;
414}
415
416/// A partial diagnostic along with the source location where this
417/// diagnostic occurs.
418using PartialDiagnosticAt = std::pair<SourceLocation, PartialDiagnostic>;
419
420} // namespace clang
421
422#endif // LLVM_CLANG_BASIC_PARTIALDIAGNOSTIC_H
423