1 //===--- PartialDiagnostic.h - Diagnostic "closures" ------------*- C++ -*-===//
3 // The LLVM Compiler Infrastructure
5 // This file is distributed under the University of Illinois Open Source
6 // License. See LICENSE.TXT for details.
8 //===----------------------------------------------------------------------===//
11 /// \brief Implements a partial diagnostic that can be emitted anwyhere
12 /// in a DiagnosticBuilder stream.
14 //===----------------------------------------------------------------------===//
16 #ifndef LLVM_CLANG_PARTIALDIAGNOSTIC_H
17 #define LLVM_CLANG_PARTIALDIAGNOSTIC_H
19 #include "clang/Basic/Diagnostic.h"
20 #include "clang/Basic/SourceLocation.h"
21 #include "llvm/ADT/STLExtras.h"
22 #include "llvm/Support/DataTypes.h"
27 class PartialDiagnostic {
30 // The MaxArguments and MaxFixItHints member enum values from
31 // DiagnosticsEngine are private but DiagnosticsEngine declares
32 // PartialDiagnostic a friend. These enum values are redeclared
33 // here so that the nested Storage class below can access them.
34 MaxArguments = DiagnosticsEngine::MaxArguments
38 Storage() : NumDiagArgs(0), NumDiagRanges(0) { }
41 /// \brief The maximum number of arguments we can hold. We
42 /// currently only support up to 10 arguments (%0-%9).
44 /// A single diagnostic with more than that almost certainly has to
45 /// be simplified anyway.
46 MaxArguments = PartialDiagnostic::MaxArguments
49 /// \brief The number of entries in Arguments.
50 unsigned char NumDiagArgs;
52 /// \brief This is the number of ranges in the DiagRanges array.
53 unsigned char NumDiagRanges;
55 /// \brief Specifies for each argument whether it is in DiagArgumentsStr
56 /// or in DiagArguments.
57 unsigned char DiagArgumentsKind[MaxArguments];
59 /// \brief The values for the various substitution positions.
61 /// This is used when the argument is not an std::string. The specific value
62 /// is mangled into an intptr_t and the interpretation depends on exactly
63 /// what sort of argument kind it is.
64 intptr_t DiagArgumentsVal[MaxArguments];
66 /// \brief The values for the various substitution positions that have
68 std::string DiagArgumentsStr[MaxArguments];
70 /// \brief The list of ranges added to this diagnostic.
72 /// It currently only support 10 ranges, could easily be extended if needed.
73 CharSourceRange DiagRanges[10];
75 /// \brief If valid, provides a hint with some code to insert, remove, or
76 /// modify at a particular position.
77 SmallVector<FixItHint, 6> FixItHints;
80 /// \brief An allocator for Storage objects, which uses a small cache to
81 /// objects, used to reduce malloc()/free() traffic for partial diagnostics.
82 class StorageAllocator {
83 static const unsigned NumCached = 16;
84 Storage Cached[NumCached];
85 Storage *FreeList[NumCached];
86 unsigned NumFreeListEntries;
92 /// \brief Allocate new storage.
94 if (NumFreeListEntries == 0)
97 Storage *Result = FreeList[--NumFreeListEntries];
98 Result->NumDiagArgs = 0;
99 Result->NumDiagRanges = 0;
100 Result->FixItHints.clear();
104 /// \brief Free the given storage object.
105 void Deallocate(Storage *S) {
106 if (S >= Cached && S <= Cached + NumCached) {
107 FreeList[NumFreeListEntries++] = S;
116 // NOTE: Sema assumes that PartialDiagnostic is location-invariant
117 // in the sense that its bits can be safely memcpy'ed and destructed
118 // in the new location.
120 /// \brief The diagnostic ID.
121 mutable unsigned DiagID;
123 /// \brief Storage for args and ranges.
124 mutable Storage *DiagStorage;
126 /// \brief Allocator used to allocate storage for this diagnostic.
127 StorageAllocator *Allocator;
129 /// \brief Retrieve storage for this particular diagnostic.
130 Storage *getStorage() const {
135 DiagStorage = Allocator->Allocate();
137 assert(Allocator != reinterpret_cast<StorageAllocator *>(~uintptr_t(0)));
138 DiagStorage = new Storage;
147 // The hot path for PartialDiagnostic is when we just used it to wrap an ID
148 // (typically so we have the flexibility of passing a more complex
149 // diagnostic into the callee, but that does not commonly occur).
151 // Split this out into a slow function for silly compilers (*cough*) which
152 // can't do decent partial inlining.
156 void freeStorageSlow() {
158 Allocator->Deallocate(DiagStorage);
159 else if (Allocator != reinterpret_cast<StorageAllocator *>(~uintptr_t(0)))
164 void AddSourceRange(const CharSourceRange &R) const {
166 DiagStorage = getStorage();
168 assert(DiagStorage->NumDiagRanges <
169 llvm::array_lengthof(DiagStorage->DiagRanges) &&
170 "Too many arguments to diagnostic!");
171 DiagStorage->DiagRanges[DiagStorage->NumDiagRanges++] = R;
174 void AddFixItHint(const FixItHint &Hint) const {
179 DiagStorage = getStorage();
181 DiagStorage->FixItHints.push_back(Hint);
185 struct NullDiagnostic {};
186 /// \brief 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 : DiagID(0), DiagStorage(0), Allocator(0) { }
191 PartialDiagnostic(unsigned DiagID, StorageAllocator &Allocator)
192 : DiagID(DiagID), DiagStorage(0), Allocator(&Allocator) { }
194 PartialDiagnostic(const PartialDiagnostic &Other)
195 : DiagID(Other.DiagID), DiagStorage(0), Allocator(Other.Allocator)
197 if (Other.DiagStorage) {
198 DiagStorage = getStorage();
199 *DiagStorage = *Other.DiagStorage;
203 PartialDiagnostic(const PartialDiagnostic &Other, Storage *DiagStorage)
204 : DiagID(Other.DiagID), DiagStorage(DiagStorage),
205 Allocator(reinterpret_cast<StorageAllocator *>(~uintptr_t(0)))
207 if (Other.DiagStorage)
208 *this->DiagStorage = *Other.DiagStorage;
211 PartialDiagnostic(const Diagnostic &Other, StorageAllocator &Allocator)
212 : DiagID(Other.getID()), DiagStorage(0), Allocator(&Allocator)
215 for (unsigned I = 0, N = Other.getNumArgs(); I != N; ++I) {
216 if (Other.getArgKind(I) == DiagnosticsEngine::ak_std_string)
217 AddString(Other.getArgStdStr(I));
219 AddTaggedVal(Other.getRawArg(I), Other.getArgKind(I));
222 // Copy source ranges.
223 for (unsigned I = 0, N = Other.getNumRanges(); I != N; ++I)
224 AddSourceRange(Other.getRange(I));
227 for (unsigned I = 0, N = Other.getNumFixItHints(); I != N; ++I)
228 AddFixItHint(Other.getFixItHint(I));
231 PartialDiagnostic &operator=(const PartialDiagnostic &Other) {
232 DiagID = Other.DiagID;
233 if (Other.DiagStorage) {
235 DiagStorage = getStorage();
237 *DiagStorage = *Other.DiagStorage;
245 ~PartialDiagnostic() {
249 void swap(PartialDiagnostic &PD) {
250 std::swap(DiagID, PD.DiagID);
251 std::swap(DiagStorage, PD.DiagStorage);
252 std::swap(Allocator, PD.Allocator);
255 unsigned getDiagID() const { return DiagID; }
257 void AddTaggedVal(intptr_t V, DiagnosticsEngine::ArgumentKind Kind) const {
259 DiagStorage = getStorage();
261 assert(DiagStorage->NumDiagArgs < Storage::MaxArguments &&
262 "Too many arguments to diagnostic!");
263 DiagStorage->DiagArgumentsKind[DiagStorage->NumDiagArgs] = Kind;
264 DiagStorage->DiagArgumentsVal[DiagStorage->NumDiagArgs++] = V;
267 void AddString(StringRef V) const {
269 DiagStorage = getStorage();
271 assert(DiagStorage->NumDiagArgs < Storage::MaxArguments &&
272 "Too many arguments to diagnostic!");
273 DiagStorage->DiagArgumentsKind[DiagStorage->NumDiagArgs]
274 = DiagnosticsEngine::ak_std_string;
275 DiagStorage->DiagArgumentsStr[DiagStorage->NumDiagArgs++] = V;
278 void Emit(const DiagnosticBuilder &DB) const {
282 // Add all arguments.
283 for (unsigned i = 0, e = DiagStorage->NumDiagArgs; i != e; ++i) {
284 if ((DiagnosticsEngine::ArgumentKind)DiagStorage->DiagArgumentsKind[i]
285 == DiagnosticsEngine::ak_std_string)
286 DB.AddString(DiagStorage->DiagArgumentsStr[i]);
288 DB.AddTaggedVal(DiagStorage->DiagArgumentsVal[i],
289 (DiagnosticsEngine::ArgumentKind)DiagStorage->DiagArgumentsKind[i]);
293 for (unsigned i = 0, e = DiagStorage->NumDiagRanges; i != e; ++i)
294 DB.AddSourceRange(DiagStorage->DiagRanges[i]);
297 for (unsigned i = 0, e = DiagStorage->FixItHints.size(); i != e; ++i)
298 DB.AddFixItHint(DiagStorage->FixItHints[i]);
301 void EmitToString(DiagnosticsEngine &Diags,
302 llvm::SmallVectorImpl<char> &Buf) const {
303 // FIXME: It should be possible to render a diagnostic to a string without
304 // messing with the state of the diagnostics engine.
305 DiagnosticBuilder DB(Diags.Report(getDiagID()));
308 Diagnostic(&Diags).FormatDiagnostic(Buf);
313 /// \brief Clear out this partial diagnostic, giving it a new diagnostic ID
314 /// and removing all of its arguments, ranges, and fix-it hints.
315 void Reset(unsigned DiagID = 0) {
316 this->DiagID = DiagID;
320 bool hasStorage() const { return DiagStorage != 0; }
322 friend const PartialDiagnostic &operator<<(const PartialDiagnostic &PD,
324 PD.AddTaggedVal(I, DiagnosticsEngine::ak_uint);
328 friend const PartialDiagnostic &operator<<(const PartialDiagnostic &PD,
330 PD.AddTaggedVal(I, DiagnosticsEngine::ak_sint);
334 friend inline const PartialDiagnostic &operator<<(const PartialDiagnostic &PD,
336 PD.AddTaggedVal(reinterpret_cast<intptr_t>(S),
337 DiagnosticsEngine::ak_c_string);
341 friend inline const PartialDiagnostic &operator<<(const PartialDiagnostic &PD,
348 friend inline const PartialDiagnostic &operator<<(const PartialDiagnostic &PD,
349 const SourceRange &R) {
350 PD.AddSourceRange(CharSourceRange::getTokenRange(R));
354 friend inline const PartialDiagnostic &operator<<(const PartialDiagnostic &PD,
355 const CharSourceRange &R) {
356 PD.AddSourceRange(R);
360 friend const PartialDiagnostic &operator<<(const PartialDiagnostic &PD,
361 const FixItHint &Hint) {
362 PD.AddFixItHint(Hint);
368 inline const DiagnosticBuilder &operator<<(const DiagnosticBuilder &DB,
369 const PartialDiagnostic &PD) {
374 /// \brief A partial diagnostic along with the source location where this
375 /// diagnostic occurs.
376 typedef std::pair<SourceLocation, PartialDiagnostic> PartialDiagnosticAt;
378 } // end namespace clang