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 /// Implements a partial diagnostic that can be emitted anwyhere
12 /// in a DiagnosticBuilder stream.
14 //===----------------------------------------------------------------------===//
16 #ifndef LLVM_CLANG_BASIC_PARTIALDIAGNOSTIC_H
17 #define LLVM_CLANG_BASIC_PARTIALDIAGNOSTIC_H
19 #include "clang/Basic/Diagnostic.h"
20 #include "clang/Basic/LLVM.h"
21 #include "clang/Basic/SourceLocation.h"
22 #include "llvm/ADT/SmallVector.h"
23 #include "llvm/ADT/StringRef.h"
27 #include <type_traits>
35 class PartialDiagnostic {
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
47 /// The maximum number of arguments we can hold. We
48 /// currently only support up to 10 arguments (%0-%9).
50 /// A single diagnostic with more than that almost certainly has to
51 /// be simplified anyway.
52 MaxArguments = PartialDiagnostic::MaxArguments
55 /// The number of entries in Arguments.
56 unsigned char NumDiagArgs = 0;
58 /// Specifies for each argument whether it is in DiagArgumentsStr
59 /// or in DiagArguments.
60 unsigned char DiagArgumentsKind[MaxArguments];
62 /// The values for the various substitution positions.
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.
67 intptr_t DiagArgumentsVal[MaxArguments];
69 /// The values for the various substitution positions that have
71 std::string DiagArgumentsStr[MaxArguments];
73 /// The list of ranges added to this diagnostic.
74 SmallVector<CharSourceRange, 8> DiagRanges;
76 /// If valid, provides a hint with some code to insert, remove, or
77 /// modify at a particular position.
78 SmallVector<FixItHint, 6> FixItHints;
83 /// An allocator for Storage objects, which uses a small cache to
84 /// objects, used to reduce malloc()/free() traffic for partial diagnostics.
85 class StorageAllocator {
86 static const unsigned NumCached = 16;
87 Storage Cached[NumCached];
88 Storage *FreeList[NumCached];
89 unsigned NumFreeListEntries;
95 /// Allocate new storage.
97 if (NumFreeListEntries == 0)
100 Storage *Result = FreeList[--NumFreeListEntries];
101 Result->NumDiagArgs = 0;
102 Result->DiagRanges.clear();
103 Result->FixItHints.clear();
107 /// Free the given storage object.
108 void Deallocate(Storage *S) {
109 if (S >= Cached && S <= Cached + NumCached) {
110 FreeList[NumFreeListEntries++] = S;
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.
123 /// The diagnostic ID.
124 mutable unsigned DiagID = 0;
126 /// Storage for args and ranges.
127 mutable Storage *DiagStorage = nullptr;
129 /// Allocator used to allocate storage for this diagnostic.
130 StorageAllocator *Allocator = nullptr;
132 /// Retrieve storage for this particular diagnostic.
133 Storage *getStorage() const {
138 DiagStorage = Allocator->Allocate();
140 assert(Allocator != reinterpret_cast<StorageAllocator *>(~uintptr_t(0)));
141 DiagStorage = new Storage;
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).
154 // Split this out into a slow function for silly compilers (*cough*) which
155 // can't do decent partial inlining.
159 void freeStorageSlow() {
161 Allocator->Deallocate(DiagStorage);
162 else if (Allocator != reinterpret_cast<StorageAllocator *>(~uintptr_t(0)))
164 DiagStorage = nullptr;
167 void AddSourceRange(const CharSourceRange &R) const {
169 DiagStorage = getStorage();
171 DiagStorage->DiagRanges.push_back(R);
174 void AddFixItHint(const FixItHint &Hint) const {
179 DiagStorage = getStorage();
181 DiagStorage->FixItHints.push_back(Hint);
185 struct NullDiagnostic {};
187 /// 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) {}
191 PartialDiagnostic(unsigned DiagID, StorageAllocator &Allocator)
192 : DiagID(DiagID), Allocator(&Allocator) {}
194 PartialDiagnostic(const PartialDiagnostic &Other)
195 : DiagID(Other.DiagID), Allocator(Other.Allocator) {
196 if (Other.DiagStorage) {
197 DiagStorage = getStorage();
198 *DiagStorage = *Other.DiagStorage;
202 PartialDiagnostic(PartialDiagnostic &&Other)
203 : DiagID(Other.DiagID), DiagStorage(Other.DiagStorage),
204 Allocator(Other.Allocator) {
205 Other.DiagStorage = nullptr;
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;
215 PartialDiagnostic(const Diagnostic &Other, StorageAllocator &Allocator)
216 : DiagID(Other.getID()), Allocator(&Allocator) {
218 for (unsigned I = 0, N = Other.getNumArgs(); I != N; ++I) {
219 if (Other.getArgKind(I) == DiagnosticsEngine::ak_std_string)
220 AddString(Other.getArgStdStr(I));
222 AddTaggedVal(Other.getRawArg(I), Other.getArgKind(I));
225 // Copy source ranges.
226 for (unsigned I = 0, N = Other.getNumRanges(); I != N; ++I)
227 AddSourceRange(Other.getRange(I));
230 for (unsigned I = 0, N = Other.getNumFixItHints(); I != N; ++I)
231 AddFixItHint(Other.getFixItHint(I));
234 PartialDiagnostic &operator=(const PartialDiagnostic &Other) {
235 DiagID = Other.DiagID;
236 if (Other.DiagStorage) {
238 DiagStorage = getStorage();
240 *DiagStorage = *Other.DiagStorage;
248 PartialDiagnostic &operator=(PartialDiagnostic &&Other) {
251 DiagID = Other.DiagID;
252 DiagStorage = Other.DiagStorage;
253 Allocator = Other.Allocator;
255 Other.DiagStorage = nullptr;
259 ~PartialDiagnostic() {
263 void swap(PartialDiagnostic &PD) {
264 std::swap(DiagID, PD.DiagID);
265 std::swap(DiagStorage, PD.DiagStorage);
266 std::swap(Allocator, PD.Allocator);
269 unsigned getDiagID() const { return DiagID; }
271 void AddTaggedVal(intptr_t V, DiagnosticsEngine::ArgumentKind Kind) const {
273 DiagStorage = getStorage();
275 assert(DiagStorage->NumDiagArgs < Storage::MaxArguments &&
276 "Too many arguments to diagnostic!");
277 DiagStorage->DiagArgumentsKind[DiagStorage->NumDiagArgs] = Kind;
278 DiagStorage->DiagArgumentsVal[DiagStorage->NumDiagArgs++] = V;
281 void AddString(StringRef V) const {
283 DiagStorage = getStorage();
285 assert(DiagStorage->NumDiagArgs < Storage::MaxArguments &&
286 "Too many arguments to diagnostic!");
287 DiagStorage->DiagArgumentsKind[DiagStorage->NumDiagArgs]
288 = DiagnosticsEngine::ak_std_string;
289 DiagStorage->DiagArgumentsStr[DiagStorage->NumDiagArgs++] = V;
292 void Emit(const DiagnosticBuilder &DB) const {
296 // Add all arguments.
297 for (unsigned i = 0, e = DiagStorage->NumDiagArgs; i != e; ++i) {
298 if ((DiagnosticsEngine::ArgumentKind)DiagStorage->DiagArgumentsKind[i]
299 == DiagnosticsEngine::ak_std_string)
300 DB.AddString(DiagStorage->DiagArgumentsStr[i]);
302 DB.AddTaggedVal(DiagStorage->DiagArgumentsVal[i],
303 (DiagnosticsEngine::ArgumentKind)DiagStorage->DiagArgumentsKind[i]);
307 for (const CharSourceRange &Range : DiagStorage->DiagRanges)
308 DB.AddSourceRange(Range);
311 for (const FixItHint &Fix : DiagStorage->FixItHints)
312 DB.AddFixItHint(Fix);
315 void EmitToString(DiagnosticsEngine &Diags,
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()));
322 Diagnostic(&Diags).FormatDiagnostic(Buf);
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;
334 bool hasStorage() const { return DiagStorage != nullptr; }
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];
345 friend const PartialDiagnostic &operator<<(const PartialDiagnostic &PD,
347 PD.AddTaggedVal(I, DiagnosticsEngine::ak_uint);
351 friend const PartialDiagnostic &operator<<(const PartialDiagnostic &PD,
353 PD.AddTaggedVal(I, DiagnosticsEngine::ak_sint);
357 friend inline const PartialDiagnostic &operator<<(const PartialDiagnostic &PD,
359 PD.AddTaggedVal(reinterpret_cast<intptr_t>(S),
360 DiagnosticsEngine::ak_c_string);
364 friend inline const PartialDiagnostic &operator<<(const PartialDiagnostic &PD,
371 friend inline const PartialDiagnostic &operator<<(const PartialDiagnostic &PD,
372 const IdentifierInfo *II) {
373 PD.AddTaggedVal(reinterpret_cast<intptr_t>(II),
374 DiagnosticsEngine::ak_identifierinfo);
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
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),
388 DiagnosticsEngine::ak_declcontext);
392 friend inline const PartialDiagnostic &operator<<(const PartialDiagnostic &PD,
394 PD.AddSourceRange(CharSourceRange::getTokenRange(R));
398 friend inline const PartialDiagnostic &operator<<(const PartialDiagnostic &PD,
399 const CharSourceRange &R) {
400 PD.AddSourceRange(R);
404 friend const PartialDiagnostic &operator<<(const PartialDiagnostic &PD,
405 const FixItHint &Hint) {
406 PD.AddFixItHint(Hint);
411 inline const DiagnosticBuilder &operator<<(const DiagnosticBuilder &DB,
412 const PartialDiagnostic &PD) {
417 /// A partial diagnostic along with the source location where this
418 /// diagnostic occurs.
419 using PartialDiagnosticAt = std::pair<SourceLocation, PartialDiagnostic>;
423 #endif // LLVM_CLANG_BASIC_PARTIALDIAGNOSTIC_H