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/Compiler.h"
23 #include "llvm/Support/DataTypes.h"
28 class PartialDiagnostic {
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
39 Storage() : NumDiagArgs(0), NumDiagRanges(0) { }
42 /// \brief The maximum number of arguments we can hold. We
43 /// currently only support up to 10 arguments (%0-%9).
45 /// A single diagnostic with more than that almost certainly has to
46 /// be simplified anyway.
47 MaxArguments = PartialDiagnostic::MaxArguments
50 /// \brief The number of entries in Arguments.
51 unsigned char NumDiagArgs;
53 /// \brief This is the number of ranges in the DiagRanges array.
54 unsigned char NumDiagRanges;
56 /// \brief Specifies for each argument whether it is in DiagArgumentsStr
57 /// or in DiagArguments.
58 unsigned char DiagArgumentsKind[MaxArguments];
60 /// \brief The values for the various substitution positions.
62 /// This is used when the argument is not an std::string. The specific value
63 /// is mangled into an intptr_t and the interpretation depends on exactly
64 /// what sort of argument kind it is.
65 intptr_t DiagArgumentsVal[MaxArguments];
67 /// \brief The values for the various substitution positions that have
69 std::string DiagArgumentsStr[MaxArguments];
71 /// \brief The list of ranges added to this diagnostic.
73 /// It currently only support 10 ranges, could easily be extended if needed.
74 CharSourceRange DiagRanges[10];
76 /// \brief If valid, provides a hint with some code to insert, remove, or
77 /// modify at a particular position.
78 SmallVector<FixItHint, 6> FixItHints;
81 /// \brief An allocator for Storage objects, which uses a small cache to
82 /// objects, used to reduce malloc()/free() traffic for partial diagnostics.
83 class StorageAllocator {
84 static const unsigned NumCached = 16;
85 Storage Cached[NumCached];
86 Storage *FreeList[NumCached];
87 unsigned NumFreeListEntries;
93 /// \brief Allocate new storage.
95 if (NumFreeListEntries == 0)
98 Storage *Result = FreeList[--NumFreeListEntries];
99 Result->NumDiagArgs = 0;
100 Result->NumDiagRanges = 0;
101 Result->FixItHints.clear();
105 /// \brief Free the given storage object.
106 void Deallocate(Storage *S) {
107 if (S >= Cached && S <= Cached + NumCached) {
108 FreeList[NumFreeListEntries++] = S;
117 // NOTE: Sema assumes that PartialDiagnostic is location-invariant
118 // in the sense that its bits can be safely memcpy'ed and destructed
119 // in the new location.
121 /// \brief The diagnostic ID.
122 mutable unsigned DiagID;
124 /// \brief Storage for args and ranges.
125 mutable Storage *DiagStorage;
127 /// \brief Allocator used to allocate storage for this diagnostic.
128 StorageAllocator *Allocator;
130 /// \brief Retrieve storage for this particular diagnostic.
131 Storage *getStorage() const {
136 DiagStorage = Allocator->Allocate();
138 assert(Allocator != reinterpret_cast<StorageAllocator *>(~uintptr_t(0)));
139 DiagStorage = new Storage;
148 // The hot path for PartialDiagnostic is when we just used it to wrap an ID
149 // (typically so we have the flexibility of passing a more complex
150 // diagnostic into the callee, but that does not commonly occur).
152 // Split this out into a slow function for silly compilers (*cough*) which
153 // can't do decent partial inlining.
157 void freeStorageSlow() {
159 Allocator->Deallocate(DiagStorage);
160 else if (Allocator != reinterpret_cast<StorageAllocator *>(~uintptr_t(0)))
165 void AddSourceRange(const CharSourceRange &R) const {
167 DiagStorage = getStorage();
169 assert(DiagStorage->NumDiagRanges <
170 llvm::array_lengthof(DiagStorage->DiagRanges) &&
171 "Too many arguments to diagnostic!");
172 DiagStorage->DiagRanges[DiagStorage->NumDiagRanges++] = R;
175 void AddFixItHint(const FixItHint &Hint) const {
180 DiagStorage = getStorage();
182 DiagStorage->FixItHints.push_back(Hint);
186 struct NullDiagnostic {};
187 /// \brief Create a null partial diagnostic, which cannot carry a payload,
188 /// and only exists to be swapped with a real partial diagnostic.
189 PartialDiagnostic(NullDiagnostic)
190 : DiagID(0), DiagStorage(0), Allocator(0) { }
192 PartialDiagnostic(unsigned DiagID, StorageAllocator &Allocator)
193 : DiagID(DiagID), DiagStorage(0), Allocator(&Allocator) { }
195 PartialDiagnostic(const PartialDiagnostic &Other)
196 : DiagID(Other.DiagID), DiagStorage(0), Allocator(Other.Allocator)
198 if (Other.DiagStorage) {
199 DiagStorage = getStorage();
200 *DiagStorage = *Other.DiagStorage;
204 #if LLVM_HAS_RVALUE_REFERENCES
205 PartialDiagnostic(PartialDiagnostic &&Other)
206 : DiagID(Other.DiagID), DiagStorage(Other.DiagStorage),
207 Allocator(Other.Allocator) {
208 Other.DiagStorage = 0;
212 PartialDiagnostic(const PartialDiagnostic &Other, Storage *DiagStorage)
213 : DiagID(Other.DiagID), DiagStorage(DiagStorage),
214 Allocator(reinterpret_cast<StorageAllocator *>(~uintptr_t(0)))
216 if (Other.DiagStorage)
217 *this->DiagStorage = *Other.DiagStorage;
220 PartialDiagnostic(const Diagnostic &Other, StorageAllocator &Allocator)
221 : DiagID(Other.getID()), DiagStorage(0), Allocator(&Allocator)
224 for (unsigned I = 0, N = Other.getNumArgs(); I != N; ++I) {
225 if (Other.getArgKind(I) == DiagnosticsEngine::ak_std_string)
226 AddString(Other.getArgStdStr(I));
228 AddTaggedVal(Other.getRawArg(I), Other.getArgKind(I));
231 // Copy source ranges.
232 for (unsigned I = 0, N = Other.getNumRanges(); I != N; ++I)
233 AddSourceRange(Other.getRange(I));
236 for (unsigned I = 0, N = Other.getNumFixItHints(); I != N; ++I)
237 AddFixItHint(Other.getFixItHint(I));
240 PartialDiagnostic &operator=(const PartialDiagnostic &Other) {
241 DiagID = Other.DiagID;
242 if (Other.DiagStorage) {
244 DiagStorage = getStorage();
246 *DiagStorage = *Other.DiagStorage;
254 #if LLVM_HAS_RVALUE_REFERENCES
255 PartialDiagnostic &operator=(PartialDiagnostic &&Other) {
258 DiagID = Other.DiagID;
259 DiagStorage = Other.DiagStorage;
260 Allocator = Other.Allocator;
262 Other.DiagStorage = 0;
267 ~PartialDiagnostic() {
271 void swap(PartialDiagnostic &PD) {
272 std::swap(DiagID, PD.DiagID);
273 std::swap(DiagStorage, PD.DiagStorage);
274 std::swap(Allocator, PD.Allocator);
277 unsigned getDiagID() const { return DiagID; }
279 void AddTaggedVal(intptr_t V, DiagnosticsEngine::ArgumentKind Kind) const {
281 DiagStorage = getStorage();
283 assert(DiagStorage->NumDiagArgs < Storage::MaxArguments &&
284 "Too many arguments to diagnostic!");
285 DiagStorage->DiagArgumentsKind[DiagStorage->NumDiagArgs] = Kind;
286 DiagStorage->DiagArgumentsVal[DiagStorage->NumDiagArgs++] = V;
289 void AddString(StringRef V) const {
291 DiagStorage = getStorage();
293 assert(DiagStorage->NumDiagArgs < Storage::MaxArguments &&
294 "Too many arguments to diagnostic!");
295 DiagStorage->DiagArgumentsKind[DiagStorage->NumDiagArgs]
296 = DiagnosticsEngine::ak_std_string;
297 DiagStorage->DiagArgumentsStr[DiagStorage->NumDiagArgs++] = V;
300 void Emit(const DiagnosticBuilder &DB) const {
304 // Add all arguments.
305 for (unsigned i = 0, e = DiagStorage->NumDiagArgs; i != e; ++i) {
306 if ((DiagnosticsEngine::ArgumentKind)DiagStorage->DiagArgumentsKind[i]
307 == DiagnosticsEngine::ak_std_string)
308 DB.AddString(DiagStorage->DiagArgumentsStr[i]);
310 DB.AddTaggedVal(DiagStorage->DiagArgumentsVal[i],
311 (DiagnosticsEngine::ArgumentKind)DiagStorage->DiagArgumentsKind[i]);
315 for (unsigned i = 0, e = DiagStorage->NumDiagRanges; i != e; ++i)
316 DB.AddSourceRange(DiagStorage->DiagRanges[i]);
319 for (unsigned i = 0, e = DiagStorage->FixItHints.size(); i != e; ++i)
320 DB.AddFixItHint(DiagStorage->FixItHints[i]);
323 void EmitToString(DiagnosticsEngine &Diags,
324 SmallVectorImpl<char> &Buf) const {
325 // FIXME: It should be possible to render a diagnostic to a string without
326 // messing with the state of the diagnostics engine.
327 DiagnosticBuilder DB(Diags.Report(getDiagID()));
330 Diagnostic(&Diags).FormatDiagnostic(Buf);
335 /// \brief Clear out this partial diagnostic, giving it a new diagnostic ID
336 /// and removing all of its arguments, ranges, and fix-it hints.
337 void Reset(unsigned DiagID = 0) {
338 this->DiagID = DiagID;
342 bool hasStorage() const { return DiagStorage != 0; }
344 friend const PartialDiagnostic &operator<<(const PartialDiagnostic &PD,
346 PD.AddTaggedVal(I, DiagnosticsEngine::ak_uint);
350 friend const PartialDiagnostic &operator<<(const PartialDiagnostic &PD,
352 PD.AddTaggedVal(I, DiagnosticsEngine::ak_sint);
356 friend inline const PartialDiagnostic &operator<<(const PartialDiagnostic &PD,
358 PD.AddTaggedVal(reinterpret_cast<intptr_t>(S),
359 DiagnosticsEngine::ak_c_string);
363 friend inline const PartialDiagnostic &operator<<(const PartialDiagnostic &PD,
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);
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
383 typename llvm::enable_if<llvm::is_same<T, DeclContext>,
384 const PartialDiagnostic &>::type
385 operator<<(const PartialDiagnostic &PD, T *DC) {
386 PD.AddTaggedVal(reinterpret_cast<intptr_t>(DC),
387 DiagnosticsEngine::ak_declcontext);
391 friend inline const PartialDiagnostic &operator<<(const PartialDiagnostic &PD,
392 const SourceRange &R) {
393 PD.AddSourceRange(CharSourceRange::getTokenRange(R));
397 friend inline const PartialDiagnostic &operator<<(const PartialDiagnostic &PD,
398 const CharSourceRange &R) {
399 PD.AddSourceRange(R);
403 friend const PartialDiagnostic &operator<<(const PartialDiagnostic &PD,
404 const FixItHint &Hint) {
405 PD.AddFixItHint(Hint);
411 inline const DiagnosticBuilder &operator<<(const DiagnosticBuilder &DB,
412 const PartialDiagnostic &PD) {
417 /// \brief A partial diagnostic along with the source location where this
418 /// diagnostic occurs.
419 typedef std::pair<SourceLocation, PartialDiagnostic> PartialDiagnosticAt;
421 } // end namespace clang