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/// 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"
20#include "clang/Basic/LLVM.h"
21#include "clang/Basic/SourceLocation.h"
22#include "llvm/ADT/SmallVector.h"
23#include "llvm/ADT/StringRef.h"
24#include <cassert>
25#include <cstdint>
26#include <string>
27#include <type_traits>
28#include <utility>
29
30namespace clang {
31
32class DeclContext;
33class IdentifierInfo;
34
35class PartialDiagnostic {
36public:
37 enum {
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
43 };
44
45 struct Storage {
46 enum {
47 /// The maximum number of arguments we can hold. We
48 /// currently only support up to 10 arguments (%0-%9).
49 ///
50 /// A single diagnostic with more than that almost certainly has to
51 /// be simplified anyway.
52 MaxArguments = PartialDiagnostic::MaxArguments
53 };
54
55 /// The number of entries in Arguments.
56 unsigned char NumDiagArgs = 0;
57
58 /// Specifies for each argument whether it is in DiagArgumentsStr
59 /// or in DiagArguments.
60 unsigned char DiagArgumentsKind[MaxArguments];
61
62 /// The values for the various substitution positions.
63 ///
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];
68
69 /// The values for the various substitution positions that have
70 /// string arguments.
71 std::string DiagArgumentsStr[MaxArguments];
72
73 /// The list of ranges added to this diagnostic.
74 SmallVector<CharSourceRange, 8> DiagRanges;
75
76 /// If valid, provides a hint with some code to insert, remove, or
77 /// modify at a particular position.
78 SmallVector<FixItHint, 6> FixItHints;
79
80 Storage() = default;
81 };
82
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;
90
91 public:
92 StorageAllocator();
93 ~StorageAllocator();
94
95 /// Allocate new storage.
96 Storage *Allocate() {
97 if (NumFreeListEntries == 0)
98 return new Storage;
99
100 Storage *Result = FreeList[--NumFreeListEntries];
101 Result->NumDiagArgs = 0;
102 Result->DiagRanges.clear();
103 Result->FixItHints.clear();
104 return Result;
105 }
106
107 /// Free the given storage object.
108 void Deallocate(Storage *S) {
109 if (S >= Cached && S <= Cached + NumCached) {
110 FreeList[NumFreeListEntries++] = S;
111 return;
112 }
113
114 delete S;
115 }
116 };
117
118private:
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.
122
123 /// The diagnostic ID.
124 mutable unsigned DiagID = 0;
125
126 /// Storage for args and ranges.
127 mutable Storage *DiagStorage = nullptr;
128
129 /// Allocator used to allocate storage for this diagnostic.
130 StorageAllocator *Allocator = nullptr;
131
132 /// Retrieve storage for this particular diagnostic.
133 Storage *getStorage() const {
134 if (DiagStorage)
135 return DiagStorage;
136
137 if (Allocator)
138 DiagStorage = Allocator->Allocate();
139 else {
140 assert(Allocator != reinterpret_cast<StorageAllocator *>(~uintptr_t(0)));
141 DiagStorage = new Storage;
142 }
143 return DiagStorage;
144 }
145
146 void freeStorage() {
147 if (!DiagStorage)
148 return;
149
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).
153 //
154 // Split this out into a slow function for silly compilers (*cough*) which
155 // can't do decent partial inlining.
156 freeStorageSlow();
157 }
158
159 void freeStorageSlow() {
160 if (Allocator)
161 Allocator->Deallocate(DiagStorage);
162 else if (Allocator != reinterpret_cast<StorageAllocator *>(~uintptr_t(0)))
163 delete DiagStorage;
164 DiagStorage = nullptr;
165 }
166
167 void AddSourceRange(const CharSourceRange &R) const {
168 if (!DiagStorage)
169 DiagStorage = getStorage();
170
171 DiagStorage->DiagRanges.push_back(R);
172 }
173
174 void AddFixItHint(const FixItHint &Hint) const {
175 if (Hint.isNull())
176 return;
177
178 if (!DiagStorage)
179 DiagStorage = getStorage();
180
181 DiagStorage->FixItHints.push_back(Hint);
182 }
183
184public:
185 struct NullDiagnostic {};
186
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) {}
190
191 PartialDiagnostic(unsigned DiagID, StorageAllocator &Allocator)
192 : DiagID(DiagID), Allocator(&Allocator) {}
193
194 PartialDiagnostic(const PartialDiagnostic &Other)
195 : DiagID(Other.DiagID), Allocator(Other.Allocator) {
196 if (Other.DiagStorage) {
197 DiagStorage = getStorage();
198 *DiagStorage = *Other.DiagStorage;
199 }
200 }
201
202 PartialDiagnostic(PartialDiagnostic &&Other)
203 : DiagID(Other.DiagID), DiagStorage(Other.DiagStorage),
204 Allocator(Other.Allocator) {
205 Other.DiagStorage = nullptr;
206 }
207
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;
213 }
214
215 PartialDiagnostic(const Diagnostic &Other, StorageAllocator &Allocator)
216 : DiagID(Other.getID()), Allocator(&Allocator) {
217 // Copy arguments.
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));
221 else
222 AddTaggedVal(Other.getRawArg(I), Other.getArgKind(I));
223 }
224
225 // Copy source ranges.
226 for (unsigned I = 0, N = Other.getNumRanges(); I != N; ++I)
227 AddSourceRange(Other.getRange(I));
228
229 // Copy fix-its.
230 for (unsigned I = 0, N = Other.getNumFixItHints(); I != N; ++I)
231 AddFixItHint(Other.getFixItHint(I));
232 }
233
234 PartialDiagnostic &operator=(const PartialDiagnostic &Other) {
235 DiagID = Other.DiagID;
236 if (Other.DiagStorage) {
237 if (!DiagStorage)
238 DiagStorage = getStorage();
239
240 *DiagStorage = *Other.DiagStorage;
241 } else {
242 freeStorage();
243 }
244
245 return *this;
246 }
247
248 PartialDiagnostic &operator=(PartialDiagnostic &&Other) {
249 freeStorage();
250
251 DiagID = Other.DiagID;
252 DiagStorage = Other.DiagStorage;
253 Allocator = Other.Allocator;
254
255 Other.DiagStorage = nullptr;
256 return *this;
257 }
258
259 ~PartialDiagnostic() {
260 freeStorage();
261 }
262
263 void swap(PartialDiagnostic &PD) {
264 std::swap(DiagID, PD.DiagID);
265 std::swap(DiagStorage, PD.DiagStorage);
266 std::swap(Allocator, PD.Allocator);
267 }
268
269 unsigned getDiagID() const { return DiagID; }
270
271 void AddTaggedVal(intptr_t V, DiagnosticsEngine::ArgumentKind Kind) const {
272 if (!DiagStorage)
273 DiagStorage = getStorage();
274
275 assert(DiagStorage->NumDiagArgs < Storage::MaxArguments &&
276 "Too many arguments to diagnostic!");
277 DiagStorage->DiagArgumentsKind[DiagStorage->NumDiagArgs] = Kind;
278 DiagStorage->DiagArgumentsVal[DiagStorage->NumDiagArgs++] = V;
279 }
280
281 void AddString(StringRef V) const {
282 if (!DiagStorage)
283 DiagStorage = getStorage();
284
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;
290 }
291
292 void Emit(const DiagnosticBuilder &DB) const {
293 if (!DiagStorage)
294 return;
295
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]);
301 else
302 DB.AddTaggedVal(DiagStorage->DiagArgumentsVal[i],
303 (DiagnosticsEngine::ArgumentKind)DiagStorage->DiagArgumentsKind[i]);
304 }
305
306 // Add all ranges.
307 for (const CharSourceRange &Range : DiagStorage->DiagRanges)
308 DB.AddSourceRange(Range);
309
310 // Add all fix-its.
311 for (const FixItHint &Fix : DiagStorage->FixItHints)
312 DB.AddFixItHint(Fix);
313 }
314
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()));
320 Emit(DB);
321 DB.FlushCounts();
322 Diagnostic(&Diags).FormatDiagnostic(Buf);
323 DB.Clear();
324 Diags.Clear();
325 }
326
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;
331 freeStorage();
332 }
333
334 bool hasStorage() const { return DiagStorage != nullptr; }
335
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];
343 }
344
345 friend const PartialDiagnostic &operator<<(const PartialDiagnostic &PD,
346 unsigned I) {
347 PD.AddTaggedVal(I, DiagnosticsEngine::ak_uint);
348 return PD;
349 }
350
351 friend const PartialDiagnostic &operator<<(const PartialDiagnostic &PD,
352 int I) {
353 PD.AddTaggedVal(I, DiagnosticsEngine::ak_sint);
354 return PD;
355 }
356
357 friend inline const PartialDiagnostic &operator<<(const PartialDiagnostic &PD,
358 const char *S) {
359 PD.AddTaggedVal(reinterpret_cast<intptr_t>(S),
360 DiagnosticsEngine::ak_c_string);
361 return PD;
362 }
363
364 friend inline const PartialDiagnostic &operator<<(const PartialDiagnostic &PD,
365 StringRef S) {
366
367 PD.AddString(S);
368 return PD;
369 }
370
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);
375 return PD;
376 }
377
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
381 // match.
382 template<typename T>
383 friend inline
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);
389 return PD;
390 }
391
392 friend inline const PartialDiagnostic &operator<<(const PartialDiagnostic &PD,
393 SourceRange R) {
394 PD.AddSourceRange(CharSourceRange::getTokenRange(R));
395 return PD;
396 }
397
398 friend inline const PartialDiagnostic &operator<<(const PartialDiagnostic &PD,
399 const CharSourceRange &R) {
400 PD.AddSourceRange(R);
401 return PD;
402 }
403
404 friend const PartialDiagnostic &operator<<(const PartialDiagnostic &PD,
405 const FixItHint &Hint) {
406 PD.AddFixItHint(Hint);
407 return PD;
408 }
409};
410
411inline const DiagnosticBuilder &operator<<(const DiagnosticBuilder &DB,
412 const PartialDiagnostic &PD) {
413 PD.Emit(DB);
414 return DB;
415}
416
417/// A partial diagnostic along with the source location where this
418/// diagnostic occurs.
419using PartialDiagnosticAt = std::pair<SourceLocation, PartialDiagnostic>;
420
421} // namespace clang
422
423#endif // LLVM_CLANG_BASIC_PARTIALDIAGNOSTIC_H
424