clang  6.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 /// \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"
21 #include "llvm/ADT/STLExtras.h"
22 #include "llvm/Support/Compiler.h"
23 #include "llvm/Support/DataTypes.h"
24 #include <cassert>
25 
26 namespace clang {
27 
29 public:
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.
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.
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.
63 
64  /// \brief The values for the various substitution positions that have
65  /// string arguments.
67 
68  /// \brief The list of ranges added to this diagnostic.
70 
71  /// \brief If valid, provides a hint with some code to insert, remove, or
72  /// modify at a particular position.
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.
79  static const unsigned NumCached = 16;
80  Storage Cached[NumCached];
81  Storage *FreeList[NumCached];
82  unsigned NumFreeListEntries;
83 
84  public:
87 
88  /// \brief Allocate new storage.
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 
111 private:
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 
177 public:
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.
182  : DiagID(0), DiagStorage(nullptr), Allocator(nullptr) { }
183 
184  PartialDiagnostic(unsigned DiagID, StorageAllocator &Allocator)
185  : DiagID(DiagID), DiagStorage(nullptr), Allocator(&Allocator) { }
186 
188  : DiagID(Other.DiagID), DiagStorage(nullptr), Allocator(Other.Allocator)
189  {
190  if (Other.DiagStorage) {
191  DiagStorage = getStorage();
192  *DiagStorage = *Other.DiagStorage;
193  }
194  }
195 
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 
211  : DiagID(Other.getID()), DiagStorage(nullptr), Allocator(&Allocator)
212  {
213  // Copy arguments.
214  for (unsigned I = 0, N = Other.getNumArgs(); I != N; ++I) {
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 
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 
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 
256  freeStorage();
257  }
258 
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 
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]
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) {
296  DB.AddString(DiagStorage->DiagArgumentsStr[i]);
297  else
298  DB.AddTaggedVal(DiagStorage->DiagArgumentsVal[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 
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  /// Retrieve the string argument at the given index.
333  StringRef getStringArg(unsigned I) {
334  assert(DiagStorage && "No diagnostic storage?");
335  assert(I < DiagStorage->NumDiagArgs && "Not enough diagnostic args");
336  assert(DiagStorage->DiagArgumentsKind[I]
337  == DiagnosticsEngine::ak_std_string && "Not a string arg");
338  return DiagStorage->DiagArgumentsStr[I];
339  }
340 
342  unsigned I) {
344  return PD;
345  }
346 
348  int I) {
350  return PD;
351  }
352 
353  friend inline const PartialDiagnostic &operator<<(const PartialDiagnostic &PD,
354  const char *S) {
355  PD.AddTaggedVal(reinterpret_cast<intptr_t>(S),
357  return PD;
358  }
359 
360  friend inline const PartialDiagnostic &operator<<(const PartialDiagnostic &PD,
361  StringRef S) {
362 
363  PD.AddString(S);
364  return PD;
365  }
366 
367  friend inline const PartialDiagnostic &operator<<(const PartialDiagnostic &PD,
368  const IdentifierInfo *II) {
369  PD.AddTaggedVal(reinterpret_cast<intptr_t>(II),
371  return PD;
372  }
373 
374  // Adds a DeclContext to the diagnostic. The enable_if template magic is here
375  // so that we only match those arguments that are (statically) DeclContexts;
376  // other arguments that derive from DeclContext (e.g., RecordDecls) will not
377  // match.
378  template<typename T>
379  friend inline
380  typename std::enable_if<std::is_same<T, DeclContext>::value,
381  const PartialDiagnostic &>::type
382  operator<<(const PartialDiagnostic &PD, T *DC) {
383  PD.AddTaggedVal(reinterpret_cast<intptr_t>(DC),
385  return PD;
386  }
387 
388  friend inline const PartialDiagnostic &operator<<(const PartialDiagnostic &PD,
389  SourceRange R) {
390  PD.AddSourceRange(CharSourceRange::getTokenRange(R));
391  return PD;
392  }
393 
394  friend inline const PartialDiagnostic &operator<<(const PartialDiagnostic &PD,
395  const CharSourceRange &R) {
396  PD.AddSourceRange(R);
397  return PD;
398  }
399 
401  const FixItHint &Hint) {
402  PD.AddFixItHint(Hint);
403  return PD;
404  }
405 
406 };
407 
409  const PartialDiagnostic &PD) {
410  PD.Emit(DB);
411  return DB;
412 }
413 
414 /// \brief A partial diagnostic along with the source location where this
415 /// diagnostic occurs.
416 typedef std::pair<SourceLocation, PartialDiagnostic> PartialDiagnosticAt;
417 
418 } // end namespace clang
419 #endif
void AddFixItHint(const FixItHint &Hint) const
Definition: Diagnostic.h:1082
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:1207
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:1250
const std::string & getArgStdStr(unsigned Idx) const
Return the provided argument string specified by Idx.
Definition: Diagnostic.h:1257
friend const PartialDiagnostic & operator<<(const PartialDiagnostic &PD, StringRef S)
void AddTaggedVal(intptr_t V, DiagnosticsEngine::ArgumentKind Kind) const
Definition: Diagnostic.h:1069
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:1323
bool isNull() const
Definition: Diagnostic.h:84
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.
Concrete class used by the front-end to report problems and issues.
Definition: Diagnostic.h:147
The maximum number of arguments we can hold.
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:1297
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:955
Represents a character-granular source range.
const FunctionProtoType * T
unsigned getNumRanges() const
Return the number of source ranges associated with this diagnostic.
Definition: Diagnostic.h:1304
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:794
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:1077
Kind
const CharSourceRange & getRange(unsigned Idx) const
Definition: Diagnostic.h:1309
intptr_t DiagArgumentsVal[MaxArguments]
The values for the various substitution positions.
void AddString(StringRef S) const
Definition: Diagnostic.h:1061
std::pair< SourceLocation, PartialDiagnostic > PartialDiagnosticAt
A partial diagnostic along with the source location where this diagnostic occurs. ...
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:1319
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:1242
A little helper class (which is basically a smart pointer that forwards info from DiagnosticsEngine) ...
Definition: Diagnostic.h:1227
Annotates a diagnostic with some code that should be inserted, removed, or replaced to fix the proble...
Definition: Diagnostic.h:64
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:667
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...