clang  9.0.0svn
PartialDiagnostic.h
Go to the documentation of this file.
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"
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 
29 namespace clang {
30 
31 class DeclContext;
32 class IdentifierInfo;
33 
35 public:
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.
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.
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.
67 
68  /// The values for the various substitution positions that have
69  /// string arguments.
71 
72  /// The list of ranges added to this diagnostic.
74 
75  /// If valid, provides a hint with some code to insert, remove, or
76  /// modify at a particular position.
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.
85  static const unsigned NumCached = 16;
86  Storage Cached[NumCached];
87  Storage *FreeList[NumCached];
88  unsigned NumFreeListEntries;
89 
90  public:
93 
94  /// Allocate new storage.
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 
117 private:
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 
183 public:
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.
189 
190  PartialDiagnostic(unsigned DiagID, StorageAllocator &Allocator)
191  : DiagID(DiagID), Allocator(&Allocator) {}
192 
194  : DiagID(Other.DiagID), Allocator(Other.Allocator) {
195  if (Other.DiagStorage) {
196  DiagStorage = getStorage();
197  *DiagStorage = *Other.DiagStorage;
198  }
199  }
200 
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 
215  : DiagID(Other.getID()), Allocator(&Allocator) {
216  // Copy arguments.
217  for (unsigned I = 0, N = Other.getNumArgs(); I != N; ++I) {
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 
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 
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 
259  freeStorage();
260  }
261 
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 
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]
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) {
299  DB.AddString(DiagStorage->DiagArgumentsStr[i]);
300  else
301  DB.AddTaggedVal(DiagStorage->DiagArgumentsVal[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 
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 
345  unsigned I) {
347  return PD;
348  }
349 
351  int I) {
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),
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),
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),
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 
404  const FixItHint &Hint) {
405  PD.AddFixItHint(Hint);
406  return PD;
407  }
408 };
409 
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.
418 using PartialDiagnosticAt = std::pair<SourceLocation, PartialDiagnostic>;
419 
420 } // namespace clang
421 
422 #endif // LLVM_CLANG_BASIC_PARTIALDIAGNOSTIC_H
void AddFixItHint(const FixItHint &Hint) const
Definition: Diagnostic.h:1170
PartialDiagnostic(const PartialDiagnostic &Other, Storage *DiagStorage)
PartialDiagnostic(const PartialDiagnostic &Other)
static CharSourceRange getTokenRange(SourceRange R)
void AddTaggedVal(intptr_t V, DiagnosticsEngine::ArgumentKind Kind) const
DiagnosticBuilder Report(SourceLocation Loc, unsigned DiagID)
Issue the message to the client.
Definition: Diagnostic.h:1296
PartialDiagnostic(const Diagnostic &Other, StorageAllocator &Allocator)
PartialDiagnostic(PartialDiagnostic &&Other)
DiagnosticsEngine::ArgumentKind getArgKind(unsigned Idx) const
Return the kind of the specified index.
Definition: Diagnostic.h:1340
const std::string & getArgStdStr(unsigned Idx) const
Return the provided argument string specified by Idx.
Definition: Diagnostic.h:1347
friend const PartialDiagnostic & operator<<(const PartialDiagnostic &PD, StringRef S)
void AddTaggedVal(intptr_t V, DiagnosticsEngine::ArgumentKind Kind) const
Definition: Diagnostic.h:1157
One of these records is kept for each identifier that is lexed.
friend const PartialDiagnostic & operator<<(const PartialDiagnostic &PD, const char *S)
unsigned char NumDiagArgs
The number of entries in Arguments.
void AddString(StringRef V) const
const FixItHint & getFixItHint(unsigned Idx) const
Definition: Diagnostic.h:1413
bool isNull() const
Definition: Diagnostic.h:85
void Reset(unsigned DiagID=0)
Clear out this partial diagnostic, giving it a new diagnostic ID and removing all of its arguments...
unsigned char DiagArgumentsKind[MaxArguments]
Specifies for each argument whether it is in DiagArgumentsStr or in DiagArguments.
Forward-declares and imports various common LLVM datatypes that clang wants to use unqualified...
Concrete class used by the front-end to report problems and issues.
Definition: Diagnostic.h:148
friend const PartialDiagnostic & operator<<(const PartialDiagnostic &PD, const IdentifierInfo *II)
intptr_t getRawArg(unsigned Idx) const
Return the specified non-string argument in an opaque form.
Definition: Diagnostic.h:1387
Defines the Diagnostic-related interfaces.
friend const PartialDiagnostic & operator<<(const PartialDiagnostic &PD, const FixItHint &Hint)
__INTPTR_TYPE__ intptr_t
A signed integer type with the property that any valid pointer to void can be converted to this type...
Definition: opencl-c.h:82
PartialDiagnostic & operator=(const PartialDiagnostic &Other)
friend const PartialDiagnostic & operator<<(const PartialDiagnostic &PD, const CharSourceRange &R)
A little helper class used to produce diagnostics.
Definition: Diagnostic.h:1044
Represents a character-granular source range.
unsigned getNumRanges() const
Return the number of source ranges associated with this diagnostic.
Definition: Diagnostic.h:1394
std::string DiagArgumentsStr[MaxArguments]
The values for the various substitution positions that have string arguments.
An allocator for Storage objects, which uses a small cache to objects, used to reduce malloc()/free()...
void Clear()
Clear out the current diagnostic.
Definition: Diagnostic.h:882
SmallVector< CharSourceRange, 8 > DiagRanges
The list of ranges added to this diagnostic.
void EmitToString(DiagnosticsEngine &Diags, SmallVectorImpl< char > &Buf) const
The result type of a method or function.
StringRef getStringArg(unsigned I)
Retrieve the string argument at the given index.
__UINTPTR_TYPE__ uintptr_t
An unsigned integer type with the property that any valid pointer to void can be converted to this ty...
Definition: opencl-c.h:89
Storage * Allocate()
Allocate new storage.
llvm::DOTGraphTraits< ExplodedGraph * > DefaultDOTGraphTraits const ExplodedNode const ExplodedNode *Out<< "\l\|";Out<< "StateID: ST"<< State-> getID()<< "
PartialDiagnostic & operator=(PartialDiagnostic &&Other)
void Emit(const DiagnosticBuilder &DB) const
PartialDiagnostic(NullDiagnostic)
Create a null partial diagnostic, which cannot carry a payload, and only exists to be swapped with a ...
void AddSourceRange(const CharSourceRange &R) const
Definition: Diagnostic.h:1165
Kind
const CharSourceRange & getRange(unsigned Idx) const
Definition: Diagnostic.h:1399
intptr_t DiagArgumentsVal[MaxArguments]
The values for the various substitution positions.
std::pair< SourceLocation, PartialDiagnostic > PartialDiagnosticAt
A partial diagnostic along with the source location where this diagnostic occurs. ...
void AddString(StringRef S) const
Definition: Diagnostic.h:1149
friend const PartialDiagnostic & operator<<(const PartialDiagnostic &PD, int I)
Dataflow Directional Tag Classes.
friend const PartialDiagnostic & operator<<(const PartialDiagnostic &PD, SourceRange R)
unsigned getNumFixItHints() const
Definition: Diagnostic.h:1409
void Deallocate(Storage *S)
Free the given storage object.
PartialDiagnostic(unsigned DiagID, StorageAllocator &Allocator)
Defines the clang::SourceLocation class and associated facilities.
unsigned getNumArgs() const
Definition: Diagnostic.h:1332
A little helper class (which is basically a smart pointer that forwards info from DiagnosticsEngine) ...
Definition: Diagnostic.h:1317
The maximum number of arguments we can hold.
Annotates a diagnostic with some code that should be inserted, removed, or replaced to fix the proble...
Definition: Diagnostic.h:65
friend const PartialDiagnostic & operator<<(const PartialDiagnostic &PD, unsigned I)
A trivial tuple used to represent a source range.
void FormatDiagnostic(SmallVectorImpl< char > &OutStr) const
Format this diagnostic into a string, substituting the formal arguments into the %0 slots...
Definition: Diagnostic.cpp:763
void swap(PartialDiagnostic &PD)
SmallVector< FixItHint, 6 > FixItHints
If valid, provides a hint with some code to insert, remove, or modify at a particular position...