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) { }
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 Specifies for each argument whether it is in DiagArgumentsStr
54 /// or in DiagArguments.
55 unsigned char DiagArgumentsKind[MaxArguments];
57 /// \brief The values for the various substitution positions.
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.
62 intptr_t DiagArgumentsVal[MaxArguments];
64 /// \brief The values for the various substitution positions that have
66 std::string DiagArgumentsStr[MaxArguments];
68 /// \brief The list of ranges added to this diagnostic.
69 SmallVector<CharSourceRange, 8> DiagRanges;
71 /// \brief If valid, provides a hint with some code to insert, remove, or
72 /// modify at a particular position.
73 SmallVector<FixItHint, 6> FixItHints;
76 /// \brief An allocator for Storage objects, which uses a small cache to
77 /// objects, used to reduce malloc()/free() traffic for partial diagnostics.
78 class StorageAllocator {
79 static const unsigned NumCached = 16;
80 Storage Cached[NumCached];
81 Storage *FreeList[NumCached];
82 unsigned NumFreeListEntries;
88 /// \brief Allocate new storage.
90 if (NumFreeListEntries == 0)
93 Storage *Result = FreeList[--NumFreeListEntries];
94 Result->NumDiagArgs = 0;
95 Result->DiagRanges.clear();
96 Result->FixItHints.clear();
100 /// \brief Free the given storage object.
101 void Deallocate(Storage *S) {
102 if (S >= Cached && S <= Cached + NumCached) {
103 FreeList[NumFreeListEntries++] = S;
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.
116 /// \brief The diagnostic ID.
117 mutable unsigned DiagID;
119 /// \brief Storage for args and ranges.
120 mutable Storage *DiagStorage;
122 /// \brief Allocator used to allocate storage for this diagnostic.
123 StorageAllocator *Allocator;
125 /// \brief Retrieve storage for this particular diagnostic.
126 Storage *getStorage() const {
131 DiagStorage = Allocator->Allocate();
133 assert(Allocator != reinterpret_cast<StorageAllocator *>(~uintptr_t(0)));
134 DiagStorage = new Storage;
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).
147 // Split this out into a slow function for silly compilers (*cough*) which
148 // can't do decent partial inlining.
152 void freeStorageSlow() {
154 Allocator->Deallocate(DiagStorage);
155 else if (Allocator != reinterpret_cast<StorageAllocator *>(~uintptr_t(0)))
157 DiagStorage = nullptr;
160 void AddSourceRange(const CharSourceRange &R) const {
162 DiagStorage = getStorage();
164 DiagStorage->DiagRanges.push_back(R);
167 void AddFixItHint(const FixItHint &Hint) const {
172 DiagStorage = getStorage();
174 DiagStorage->FixItHints.push_back(Hint);
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.
181 PartialDiagnostic(NullDiagnostic)
182 : DiagID(0), DiagStorage(nullptr), Allocator(nullptr) { }
184 PartialDiagnostic(unsigned DiagID, StorageAllocator &Allocator)
185 : DiagID(DiagID), DiagStorage(nullptr), Allocator(&Allocator) { }
187 PartialDiagnostic(const PartialDiagnostic &Other)
188 : DiagID(Other.DiagID), DiagStorage(nullptr), Allocator(Other.Allocator)
190 if (Other.DiagStorage) {
191 DiagStorage = getStorage();
192 *DiagStorage = *Other.DiagStorage;
196 PartialDiagnostic(PartialDiagnostic &&Other)
197 : DiagID(Other.DiagID), DiagStorage(Other.DiagStorage),
198 Allocator(Other.Allocator) {
199 Other.DiagStorage = nullptr;
202 PartialDiagnostic(const PartialDiagnostic &Other, Storage *DiagStorage)
203 : DiagID(Other.DiagID), DiagStorage(DiagStorage),
204 Allocator(reinterpret_cast<StorageAllocator *>(~uintptr_t(0)))
206 if (Other.DiagStorage)
207 *this->DiagStorage = *Other.DiagStorage;
210 PartialDiagnostic(const Diagnostic &Other, StorageAllocator &Allocator)
211 : DiagID(Other.getID()), DiagStorage(nullptr), Allocator(&Allocator)
214 for (unsigned I = 0, N = Other.getNumArgs(); I != N; ++I) {
215 if (Other.getArgKind(I) == DiagnosticsEngine::ak_std_string)
216 AddString(Other.getArgStdStr(I));
218 AddTaggedVal(Other.getRawArg(I), Other.getArgKind(I));
221 // Copy source ranges.
222 for (unsigned I = 0, N = Other.getNumRanges(); I != N; ++I)
223 AddSourceRange(Other.getRange(I));
226 for (unsigned I = 0, N = Other.getNumFixItHints(); I != N; ++I)
227 AddFixItHint(Other.getFixItHint(I));
230 PartialDiagnostic &operator=(const PartialDiagnostic &Other) {
231 DiagID = Other.DiagID;
232 if (Other.DiagStorage) {
234 DiagStorage = getStorage();
236 *DiagStorage = *Other.DiagStorage;
244 PartialDiagnostic &operator=(PartialDiagnostic &&Other) {
247 DiagID = Other.DiagID;
248 DiagStorage = Other.DiagStorage;
249 Allocator = Other.Allocator;
251 Other.DiagStorage = nullptr;
255 ~PartialDiagnostic() {
259 void swap(PartialDiagnostic &PD) {
260 std::swap(DiagID, PD.DiagID);
261 std::swap(DiagStorage, PD.DiagStorage);
262 std::swap(Allocator, PD.Allocator);
265 unsigned getDiagID() const { return DiagID; }
267 void AddTaggedVal(intptr_t V, DiagnosticsEngine::ArgumentKind Kind) const {
269 DiagStorage = getStorage();
271 assert(DiagStorage->NumDiagArgs < Storage::MaxArguments &&
272 "Too many arguments to diagnostic!");
273 DiagStorage->DiagArgumentsKind[DiagStorage->NumDiagArgs] = Kind;
274 DiagStorage->DiagArgumentsVal[DiagStorage->NumDiagArgs++] = V;
277 void AddString(StringRef V) const {
279 DiagStorage = getStorage();
281 assert(DiagStorage->NumDiagArgs < Storage::MaxArguments &&
282 "Too many arguments to diagnostic!");
283 DiagStorage->DiagArgumentsKind[DiagStorage->NumDiagArgs]
284 = DiagnosticsEngine::ak_std_string;
285 DiagStorage->DiagArgumentsStr[DiagStorage->NumDiagArgs++] = V;
288 void Emit(const DiagnosticBuilder &DB) const {
292 // Add all arguments.
293 for (unsigned i = 0, e = DiagStorage->NumDiagArgs; i != e; ++i) {
294 if ((DiagnosticsEngine::ArgumentKind)DiagStorage->DiagArgumentsKind[i]
295 == DiagnosticsEngine::ak_std_string)
296 DB.AddString(DiagStorage->DiagArgumentsStr[i]);
298 DB.AddTaggedVal(DiagStorage->DiagArgumentsVal[i],
299 (DiagnosticsEngine::ArgumentKind)DiagStorage->DiagArgumentsKind[i]);
303 for (const CharSourceRange &Range : DiagStorage->DiagRanges)
304 DB.AddSourceRange(Range);
307 for (const FixItHint &Fix : DiagStorage->FixItHints)
308 DB.AddFixItHint(Fix);
311 void EmitToString(DiagnosticsEngine &Diags,
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()));
318 Diagnostic(&Diags).FormatDiagnostic(Buf);
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;
330 bool hasStorage() const { return DiagStorage != nullptr; }
332 friend const PartialDiagnostic &operator<<(const PartialDiagnostic &PD,
334 PD.AddTaggedVal(I, DiagnosticsEngine::ak_uint);
338 friend const PartialDiagnostic &operator<<(const PartialDiagnostic &PD,
340 PD.AddTaggedVal(I, DiagnosticsEngine::ak_sint);
344 friend inline const PartialDiagnostic &operator<<(const PartialDiagnostic &PD,
346 PD.AddTaggedVal(reinterpret_cast<intptr_t>(S),
347 DiagnosticsEngine::ak_c_string);
351 friend inline const PartialDiagnostic &operator<<(const PartialDiagnostic &PD,
358 friend inline const PartialDiagnostic &operator<<(const PartialDiagnostic &PD,
359 const IdentifierInfo *II) {
360 PD.AddTaggedVal(reinterpret_cast<intptr_t>(II),
361 DiagnosticsEngine::ak_identifierinfo);
365 // Adds a DeclContext to the diagnostic. The enable_if template magic is here
366 // so that we only match those arguments that are (statically) DeclContexts;
367 // other arguments that derive from DeclContext (e.g., RecordDecls) will not
371 typename std::enable_if<std::is_same<T, DeclContext>::value,
372 const PartialDiagnostic &>::type
373 operator<<(const PartialDiagnostic &PD, T *DC) {
374 PD.AddTaggedVal(reinterpret_cast<intptr_t>(DC),
375 DiagnosticsEngine::ak_declcontext);
379 friend inline const PartialDiagnostic &operator<<(const PartialDiagnostic &PD,
380 const SourceRange &R) {
381 PD.AddSourceRange(CharSourceRange::getTokenRange(R));
385 friend inline const PartialDiagnostic &operator<<(const PartialDiagnostic &PD,
386 const CharSourceRange &R) {
387 PD.AddSourceRange(R);
391 friend const PartialDiagnostic &operator<<(const PartialDiagnostic &PD,
392 const FixItHint &Hint) {
393 PD.AddFixItHint(Hint);
399 inline const DiagnosticBuilder &operator<<(const DiagnosticBuilder &DB,
400 const PartialDiagnostic &PD) {
405 /// \brief A partial diagnostic along with the source location where this
406 /// diagnostic occurs.
407 typedef std::pair<SourceLocation, PartialDiagnostic> PartialDiagnosticAt;
409 } // end namespace clang