Clang Project

clang_source_code/include/clang/Basic/PartialDiagnostic.h
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<CharSourceRange8DiagRanges;
74
75    /// If valid, provides a hint with some code to insert, remove, or
76    /// modify at a particular position.
77    SmallVector<FixItHint6>  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      (~uintptr_t(0))", "/home/seafit/code_projects/clang_source/clang/include/clang/Basic/PartialDiagnostic.h", 139, __PRETTY_FUNCTION__))" file_link="../../../../include/assert.h.html#88" macro="true">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 &Rconst {
167    if (!DiagStorage)
168      DiagStorage = getStorage();
169
170    DiagStorage->DiagRanges.push_back(R);
171  }
172
173  void AddFixItHint(const FixItHint &Hintconst {
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 DiagIDStorageAllocator &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 &OtherStorage *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 &OtherStorageAllocator &Allocator)
215      : DiagID(Other.getID()), Allocator(&Allocator) {
216    // Copy arguments.
217    for (unsigned I = 0N = 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 = 0N = Other.getNumRanges(); I != N; ++I)
226      AddSourceRange(Other.getRange(I));
227
228    // Copy fix-its.
229    for (unsigned I = 0N = 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(DiagIDPD.DiagID);
264    std::swap(DiagStoragePD.DiagStorage);
265    std::swap(AllocatorPD.Allocator);
266  }
267
268  unsigned getDiagID() const { return DiagID; }
269
270  void AddTaggedVal(intptr_t VDiagnosticsEngine::ArgumentKind Kindconst {
271    if (!DiagStorage)
272      DiagStorage = getStorage();
273
274     (0) . __assert_fail ("DiagStorage->NumDiagArgs < Storage..MaxArguments && \"Too many arguments to diagnostic!\"", "/home/seafit/code_projects/clang_source/clang/include/clang/Basic/PartialDiagnostic.h", 275, __PRETTY_FUNCTION__))" file_link="../../../../include/assert.h.html#88" macro="true">assert(DiagStorage->NumDiagArgs < Storage::MaxArguments &&
275 (0) . __assert_fail ("DiagStorage->NumDiagArgs < Storage..MaxArguments && \"Too many arguments to diagnostic!\"", "/home/seafit/code_projects/clang_source/clang/include/clang/Basic/PartialDiagnostic.h", 275, __PRETTY_FUNCTION__))" file_link="../../../../include/assert.h.html#88" macro="true">           "Too many arguments to diagnostic!");
276    DiagStorage->DiagArgumentsKind[DiagStorage->NumDiagArgs] = Kind;
277    DiagStorage->DiagArgumentsVal[DiagStorage->NumDiagArgs++] = V;
278  }
279
280  void AddString(StringRef Vconst {
281    if (!DiagStorage)
282      DiagStorage = getStorage();
283
284     (0) . __assert_fail ("DiagStorage->NumDiagArgs < Storage..MaxArguments && \"Too many arguments to diagnostic!\"", "/home/seafit/code_projects/clang_source/clang/include/clang/Basic/PartialDiagnostic.h", 285, __PRETTY_FUNCTION__))" file_link="../../../../include/assert.h.html#88" macro="true">assert(DiagStorage->NumDiagArgs < Storage::MaxArguments &&
285 (0) . __assert_fail ("DiagStorage->NumDiagArgs < Storage..MaxArguments && \"Too many arguments to diagnostic!\"", "/home/seafit/code_projects/clang_source/clang/include/clang/Basic/PartialDiagnostic.h", 285, __PRETTY_FUNCTION__))" file_link="../../../../include/assert.h.html#88" macro="true">           "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 &DBconst {
292    if (!DiagStorage)
293      return;
294
295    // Add all arguments.
296    for (unsigned i = 0e = DiagStorage->NumDiagArgsi != 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> &Bufconst {
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     (0) . __assert_fail ("DiagStorage && \"No diagnostic storage?\"", "/home/seafit/code_projects/clang_source/clang/include/clang/Basic/PartialDiagnostic.h", 337, __PRETTY_FUNCTION__))" file_link="../../../../include/assert.h.html#88" macro="true">assert(DiagStorage && "No diagnostic storage?");
338     (0) . __assert_fail ("I < DiagStorage->NumDiagArgs && \"Not enough diagnostic args\"", "/home/seafit/code_projects/clang_source/clang/include/clang/Basic/PartialDiagnostic.h", 338, __PRETTY_FUNCTION__))" file_link="../../../../include/assert.h.html#88" macro="true">assert(I < DiagStorage->NumDiagArgs && "Not enough diagnostic args");
339     (0) . __assert_fail ("DiagStorage->DiagArgumentsKind[I] == DiagnosticsEngine..ak_std_string && \"Not a string arg\"", "/home/seafit/code_projects/clang_source/clang/include/clang/Basic/PartialDiagnostic.h", 340, __PRETTY_FUNCTION__))" file_link="../../../../include/assert.h.html#88" macro="true">assert(DiagStorage->DiagArgumentsKind[I]
340 (0) . __assert_fail ("DiagStorage->DiagArgumentsKind[I] == DiagnosticsEngine..ak_std_string && \"Not a string arg\"", "/home/seafit/code_projects/clang_source/clang/include/clang/Basic/PartialDiagnostic.h", 340, __PRETTY_FUNCTION__))" file_link="../../../../include/assert.h.html#88" macro="true">             == 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(IDiagnosticsEngine::ak_uint);
347    return PD;
348  }
349
350  friend const PartialDiagnostic &operator<<(const PartialDiagnostic &PD,
351                                             int I) {
352    PD.AddTaggedVal(IDiagnosticsEngine::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<SourceLocationPartialDiagnostic>;
419
420// namespace clang
421
422#endif // LLVM_CLANG_BASIC_PARTIALDIAGNOSTIC_H
423
clang::PartialDiagnostic::Storage
clang::PartialDiagnostic::Storage::NumDiagArgs
clang::PartialDiagnostic::Storage::DiagArgumentsKind
clang::PartialDiagnostic::Storage::DiagArgumentsVal
clang::PartialDiagnostic::Storage::DiagArgumentsStr
clang::PartialDiagnostic::Storage::DiagRanges
clang::PartialDiagnostic::Storage::FixItHints
clang::PartialDiagnostic::StorageAllocator
clang::PartialDiagnostic::StorageAllocator::NumCached
clang::PartialDiagnostic::StorageAllocator::Cached
clang::PartialDiagnostic::StorageAllocator::FreeList
clang::PartialDiagnostic::StorageAllocator::NumFreeListEntries
clang::PartialDiagnostic::StorageAllocator::Allocate
clang::PartialDiagnostic::StorageAllocator::Deallocate
clang::PartialDiagnostic::DiagID
clang::PartialDiagnostic::DiagStorage
clang::PartialDiagnostic::Allocator
clang::PartialDiagnostic::getStorage
clang::PartialDiagnostic::freeStorage
clang::PartialDiagnostic::freeStorageSlow
clang::PartialDiagnostic::AddSourceRange
clang::PartialDiagnostic::AddFixItHint
clang::PartialDiagnostic::NullDiagnostic
clang::PartialDiagnostic::swap
clang::PartialDiagnostic::getDiagID
clang::PartialDiagnostic::AddTaggedVal
clang::PartialDiagnostic::AddString
clang::PartialDiagnostic::Emit
clang::PartialDiagnostic::EmitToString
clang::PartialDiagnostic::Reset
clang::PartialDiagnostic::hasStorage
clang::PartialDiagnostic::getStringArg