clang  8.0.0svn
PartialDiagnostic.h
Go to the documentation of this file.
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 /// 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/LLVM.h"
22 #include "llvm/ADT/SmallVector.h"
23 #include "llvm/ADT/StringRef.h"
24 #include <cassert>
25 #include <cstdint>
26 #include <string>
27 #include <type_traits>
28 #include <utility>
29 
30 namespace clang {
31 
32 class DeclContext;
33 class IdentifierInfo;
34 
36 public:
37  enum {
38  // The MaxArguments and MaxFixItHints member enum values from
39  // DiagnosticsEngine are private but DiagnosticsEngine declares
40  // PartialDiagnostic a friend. These enum values are redeclared
41  // here so that the nested Storage class below can access them.
42  MaxArguments = DiagnosticsEngine::MaxArguments
43  };
44 
45  struct Storage {
46  enum {
47  /// The maximum number of arguments we can hold. We
48  /// currently only support up to 10 arguments (%0-%9).
49  ///
50  /// A single diagnostic with more than that almost certainly has to
51  /// be simplified anyway.
53  };
54 
55  /// The number of entries in Arguments.
56  unsigned char NumDiagArgs = 0;
57 
58  /// Specifies for each argument whether it is in DiagArgumentsStr
59  /// or in DiagArguments.
61 
62  /// The values for the various substitution positions.
63  ///
64  /// This is used when the argument is not an std::string. The specific value
65  /// is mangled into an intptr_t and the interpretation depends on exactly
66  /// what sort of argument kind it is.
68 
69  /// The values for the various substitution positions that have
70  /// string arguments.
72 
73  /// The list of ranges added to this diagnostic.
75 
76  /// If valid, provides a hint with some code to insert, remove, or
77  /// modify at a particular position.
79 
80  Storage() = default;
81  };
82 
83  /// An allocator for Storage objects, which uses a small cache to
84  /// objects, used to reduce malloc()/free() traffic for partial diagnostics.
86  static const unsigned NumCached = 16;
87  Storage Cached[NumCached];
88  Storage *FreeList[NumCached];
89  unsigned NumFreeListEntries;
90 
91  public:
94 
95  /// Allocate new storage.
97  if (NumFreeListEntries == 0)
98  return new Storage;
99 
100  Storage *Result = FreeList[--NumFreeListEntries];
101  Result->NumDiagArgs = 0;
102  Result->DiagRanges.clear();
103  Result->FixItHints.clear();
104  return Result;
105  }
106 
107  /// Free the given storage object.
108  void Deallocate(Storage *S) {
109  if (S >= Cached && S <= Cached + NumCached) {
110  FreeList[NumFreeListEntries++] = S;
111  return;
112  }
113 
114  delete S;
115  }
116  };
117 
118 private:
119  // NOTE: Sema assumes that PartialDiagnostic is location-invariant
120  // in the sense that its bits can be safely memcpy'ed and destructed
121  // in the new location.
122 
123  /// The diagnostic ID.
124  mutable unsigned DiagID = 0;
125 
126  /// Storage for args and ranges.
127  mutable Storage *DiagStorage = nullptr;
128 
129  /// Allocator used to allocate storage for this diagnostic.
130  StorageAllocator *Allocator = nullptr;
131 
132  /// Retrieve storage for this particular diagnostic.
133  Storage *getStorage() const {
134  if (DiagStorage)
135  return DiagStorage;
136 
137  if (Allocator)
138  DiagStorage = Allocator->Allocate();
139  else {
140  assert(Allocator != reinterpret_cast<StorageAllocator *>(~uintptr_t(0)));
141  DiagStorage = new Storage;
142  }
143  return DiagStorage;
144  }
145 
146  void freeStorage() {
147  if (!DiagStorage)
148  return;
149 
150  // The hot path for PartialDiagnostic is when we just used it to wrap an ID
151  // (typically so we have the flexibility of passing a more complex
152  // diagnostic into the callee, but that does not commonly occur).
153  //
154  // Split this out into a slow function for silly compilers (*cough*) which
155  // can't do decent partial inlining.
156  freeStorageSlow();
157  }
158 
159  void freeStorageSlow() {
160  if (Allocator)
161  Allocator->Deallocate(DiagStorage);
162  else if (Allocator != reinterpret_cast<StorageAllocator *>(~uintptr_t(0)))
163  delete DiagStorage;
164  DiagStorage = nullptr;
165  }
166 
167  void AddSourceRange(const CharSourceRange &R) const {
168  if (!DiagStorage)
169  DiagStorage = getStorage();
170 
171  DiagStorage->DiagRanges.push_back(R);
172  }
173 
174  void AddFixItHint(const FixItHint &Hint) const {
175  if (Hint.isNull())
176  return;
177 
178  if (!DiagStorage)
179  DiagStorage = getStorage();
180 
181  DiagStorage->FixItHints.push_back(Hint);
182  }
183 
184 public:
185  struct NullDiagnostic {};
186 
187  /// Create a null partial diagnostic, which cannot carry a payload,
188  /// and only exists to be swapped with a real partial diagnostic.
190 
191  PartialDiagnostic(unsigned DiagID, StorageAllocator &Allocator)
192  : DiagID(DiagID), Allocator(&Allocator) {}
193 
195  : DiagID(Other.DiagID), Allocator(Other.Allocator) {
196  if (Other.DiagStorage) {
197  DiagStorage = getStorage();
198  *DiagStorage = *Other.DiagStorage;
199  }
200  }
201 
203  : DiagID(Other.DiagID), DiagStorage(Other.DiagStorage),
204  Allocator(Other.Allocator) {
205  Other.DiagStorage = nullptr;
206  }
207 
208  PartialDiagnostic(const PartialDiagnostic &Other, Storage *DiagStorage)
209  : DiagID(Other.DiagID), DiagStorage(DiagStorage),
210  Allocator(reinterpret_cast<StorageAllocator *>(~uintptr_t(0))) {
211  if (Other.DiagStorage)
212  *this->DiagStorage = *Other.DiagStorage;
213  }
214 
216  : DiagID(Other.getID()), Allocator(&Allocator) {
217  // Copy arguments.
218  for (unsigned I = 0, N = Other.getNumArgs(); I != N; ++I) {
220  AddString(Other.getArgStdStr(I));
221  else
222  AddTaggedVal(Other.getRawArg(I), Other.getArgKind(I));
223  }
224 
225  // Copy source ranges.
226  for (unsigned I = 0, N = Other.getNumRanges(); I != N; ++I)
227  AddSourceRange(Other.getRange(I));
228 
229  // Copy fix-its.
230  for (unsigned I = 0, N = Other.getNumFixItHints(); I != N; ++I)
231  AddFixItHint(Other.getFixItHint(I));
232  }
233 
235  DiagID = Other.DiagID;
236  if (Other.DiagStorage) {
237  if (!DiagStorage)
238  DiagStorage = getStorage();
239 
240  *DiagStorage = *Other.DiagStorage;
241  } else {
242  freeStorage();
243  }
244 
245  return *this;
246  }
247 
249  freeStorage();
250 
251  DiagID = Other.DiagID;
252  DiagStorage = Other.DiagStorage;
253  Allocator = Other.Allocator;
254 
255  Other.DiagStorage = nullptr;
256  return *this;
257  }
258 
260  freeStorage();
261  }
262 
264  std::swap(DiagID, PD.DiagID);
265  std::swap(DiagStorage, PD.DiagStorage);
266  std::swap(Allocator, PD.Allocator);
267  }
268 
269  unsigned getDiagID() const { return DiagID; }
270 
272  if (!DiagStorage)
273  DiagStorage = getStorage();
274 
275  assert(DiagStorage->NumDiagArgs < Storage::MaxArguments &&
276  "Too many arguments to diagnostic!");
277  DiagStorage->DiagArgumentsKind[DiagStorage->NumDiagArgs] = Kind;
278  DiagStorage->DiagArgumentsVal[DiagStorage->NumDiagArgs++] = V;
279  }
280 
281  void AddString(StringRef V) const {
282  if (!DiagStorage)
283  DiagStorage = getStorage();
284 
285  assert(DiagStorage->NumDiagArgs < Storage::MaxArguments &&
286  "Too many arguments to diagnostic!");
287  DiagStorage->DiagArgumentsKind[DiagStorage->NumDiagArgs]
289  DiagStorage->DiagArgumentsStr[DiagStorage->NumDiagArgs++] = V;
290  }
291 
292  void Emit(const DiagnosticBuilder &DB) const {
293  if (!DiagStorage)
294  return;
295 
296  // Add all arguments.
297  for (unsigned i = 0, e = DiagStorage->NumDiagArgs; i != e; ++i) {
300  DB.AddString(DiagStorage->DiagArgumentsStr[i]);
301  else
302  DB.AddTaggedVal(DiagStorage->DiagArgumentsVal[i],
304  }
305 
306  // Add all ranges.
307  for (const CharSourceRange &Range : DiagStorage->DiagRanges)
308  DB.AddSourceRange(Range);
309 
310  // Add all fix-its.
311  for (const FixItHint &Fix : DiagStorage->FixItHints)
312  DB.AddFixItHint(Fix);
313  }
314 
316  SmallVectorImpl<char> &Buf) const {
317  // FIXME: It should be possible to render a diagnostic to a string without
318  // messing with the state of the diagnostics engine.
319  DiagnosticBuilder DB(Diags.Report(getDiagID()));
320  Emit(DB);
321  DB.FlushCounts();
322  Diagnostic(&Diags).FormatDiagnostic(Buf);
323  DB.Clear();
324  Diags.Clear();
325  }
326 
327  /// Clear out this partial diagnostic, giving it a new diagnostic ID
328  /// and removing all of its arguments, ranges, and fix-it hints.
329  void Reset(unsigned DiagID = 0) {
330  this->DiagID = DiagID;
331  freeStorage();
332  }
333 
334  bool hasStorage() const { return DiagStorage != nullptr; }
335 
336  /// Retrieve the string argument at the given index.
337  StringRef getStringArg(unsigned I) {
338  assert(DiagStorage && "No diagnostic storage?");
339  assert(I < DiagStorage->NumDiagArgs && "Not enough diagnostic args");
340  assert(DiagStorage->DiagArgumentsKind[I]
341  == DiagnosticsEngine::ak_std_string && "Not a string arg");
342  return DiagStorage->DiagArgumentsStr[I];
343  }
344 
346  unsigned I) {
348  return PD;
349  }
350 
352  int I) {
354  return PD;
355  }
356 
357  friend inline const PartialDiagnostic &operator<<(const PartialDiagnostic &PD,
358  const char *S) {
359  PD.AddTaggedVal(reinterpret_cast<intptr_t>(S),
361  return PD;
362  }
363 
364  friend inline const PartialDiagnostic &operator<<(const PartialDiagnostic &PD,
365  StringRef S) {
366 
367  PD.AddString(S);
368  return PD;
369  }
370 
371  friend inline const PartialDiagnostic &operator<<(const PartialDiagnostic &PD,
372  const IdentifierInfo *II) {
373  PD.AddTaggedVal(reinterpret_cast<intptr_t>(II),
375  return PD;
376  }
377 
378  // Adds a DeclContext to the diagnostic. The enable_if template magic is here
379  // so that we only match those arguments that are (statically) DeclContexts;
380  // other arguments that derive from DeclContext (e.g., RecordDecls) will not
381  // match.
382  template<typename T>
383  friend inline
384  typename std::enable_if<std::is_same<T, DeclContext>::value,
385  const PartialDiagnostic &>::type
386  operator<<(const PartialDiagnostic &PD, T *DC) {
387  PD.AddTaggedVal(reinterpret_cast<intptr_t>(DC),
389  return PD;
390  }
391 
392  friend inline const PartialDiagnostic &operator<<(const PartialDiagnostic &PD,
393  SourceRange R) {
394  PD.AddSourceRange(CharSourceRange::getTokenRange(R));
395  return PD;
396  }
397 
398  friend inline const PartialDiagnostic &operator<<(const PartialDiagnostic &PD,
399  const CharSourceRange &R) {
400  PD.AddSourceRange(R);
401  return PD;
402  }
403 
405  const FixItHint &Hint) {
406  PD.AddFixItHint(Hint);
407  return PD;
408  }
409 };
410 
412  const PartialDiagnostic &PD) {
413  PD.Emit(DB);
414  return DB;
415 }
416 
417 /// A partial diagnostic along with the source location where this
418 /// diagnostic occurs.
419 using PartialDiagnosticAt = std::pair<SourceLocation, PartialDiagnostic>;
420 
421 } // namespace clang
422 
423 #endif // LLVM_CLANG_BASIC_PARTIALDIAGNOSTIC_H
void AddFixItHint(const FixItHint &Hint) const
Definition: Diagnostic.h:1168
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:1294
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:1338
const std::string & getArgStdStr(unsigned Idx) const
Return the provided argument string specified by Idx.
Definition: Diagnostic.h:1345
friend const PartialDiagnostic & operator<<(const PartialDiagnostic &PD, StringRef S)
void AddTaggedVal(intptr_t V, DiagnosticsEngine::ArgumentKind Kind) const
Definition: Diagnostic.h:1155
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:1411
bool isNull() const
Definition: Diagnostic.h:86
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:149
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:1385
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:75
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:1042
Represents a character-granular source range.
unsigned getNumRanges() const
Return the number of source ranges associated with this diagnostic.
Definition: Diagnostic.h:1392
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:880
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:82
Storage * Allocate()
Allocate new storage.
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:1163
Kind
const CharSourceRange & getRange(unsigned Idx) const
Definition: Diagnostic.h:1397
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:1147
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:1407
void Deallocate(Storage *S)
Free the given storage object.
PartialDiagnostic(unsigned DiagID, StorageAllocator &Allocator)
Defines the clang::SourceLocation class and associated facilities.
The maximum number of arguments we can hold.
unsigned getNumArgs() const
Definition: Diagnostic.h:1330
A little helper class (which is basically a smart pointer that forwards info from DiagnosticsEngine) ...
Definition: Diagnostic.h:1315
Annotates a diagnostic with some code that should be inserted, removed, or replaced to fix the proble...
Definition: Diagnostic.h:66
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:756
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...