/Users/buildslave/jenkins/sharedspace/clang-stage2-coverage-R@2/llvm/tools/clang/include/clang/Basic/PartialDiagnostic.h
Line | Count | Source (jump to first uncovered line) |
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 | | /// \brief 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/SourceLocation.h" |
21 | | #include "llvm/ADT/STLExtras.h" |
22 | | #include "llvm/Support/Compiler.h" |
23 | | #include "llvm/Support/DataTypes.h" |
24 | | #include <cassert> |
25 | | |
26 | | namespace clang { |
27 | | |
28 | | class PartialDiagnostic { |
29 | | public: |
30 | | enum { |
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 |
36 | | }; |
37 | | |
38 | | struct Storage { |
39 | 513k | Storage() : NumDiagArgs(0) { } |
40 | | |
41 | | enum { |
42 | | /// \brief The maximum number of arguments we can hold. We |
43 | | /// currently only support up to 10 arguments (%0-%9). |
44 | | /// |
45 | | /// A single diagnostic with more than that almost certainly has to |
46 | | /// be simplified anyway. |
47 | | MaxArguments = PartialDiagnostic::MaxArguments |
48 | | }; |
49 | | |
50 | | /// \brief The number of entries in Arguments. |
51 | | unsigned char NumDiagArgs; |
52 | | |
53 | | /// \brief Specifies for each argument whether it is in DiagArgumentsStr |
54 | | /// or in DiagArguments. |
55 | | unsigned char DiagArgumentsKind[MaxArguments]; |
56 | | |
57 | | /// \brief The values for the various substitution positions. |
58 | | /// |
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]; |
63 | | |
64 | | /// \brief The values for the various substitution positions that have |
65 | | /// string arguments. |
66 | | std::string DiagArgumentsStr[MaxArguments]; |
67 | | |
68 | | /// \brief The list of ranges added to this diagnostic. |
69 | | SmallVector<CharSourceRange, 8> DiagRanges; |
70 | | |
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; |
74 | | }; |
75 | | |
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; |
83 | | |
84 | | public: |
85 | | StorageAllocator(); |
86 | | ~StorageAllocator(); |
87 | | |
88 | | /// \brief Allocate new storage. |
89 | 632k | Storage *Allocate() { |
90 | 632k | if (NumFreeListEntries == 0) |
91 | 38.5k | return new Storage; |
92 | 632k | |
93 | 593k | Storage *Result = FreeList[--NumFreeListEntries]; |
94 | 593k | Result->NumDiagArgs = 0; |
95 | 593k | Result->DiagRanges.clear(); |
96 | 593k | Result->FixItHints.clear(); |
97 | 593k | return Result; |
98 | 632k | } |
99 | | |
100 | | /// \brief Free the given storage object. |
101 | 612k | void Deallocate(Storage *S) { |
102 | 612k | if (S >= Cached && 612k S <= Cached + NumCached601k ) { |
103 | 592k | FreeList[NumFreeListEntries++] = S; |
104 | 592k | return; |
105 | 592k | } |
106 | 612k | |
107 | 19.7k | delete S; |
108 | 19.7k | } |
109 | | }; |
110 | | |
111 | | private: |
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. |
115 | | |
116 | | /// \brief The diagnostic ID. |
117 | | mutable unsigned DiagID; |
118 | | |
119 | | /// \brief Storage for args and ranges. |
120 | | mutable Storage *DiagStorage; |
121 | | |
122 | | /// \brief Allocator used to allocate storage for this diagnostic. |
123 | | StorageAllocator *Allocator; |
124 | | |
125 | | /// \brief Retrieve storage for this particular diagnostic. |
126 | 632k | Storage *getStorage() const { |
127 | 632k | if (DiagStorage) |
128 | 0 | return DiagStorage; |
129 | 632k | |
130 | 632k | if (632k Allocator632k ) |
131 | 632k | DiagStorage = Allocator->Allocate(); |
132 | 0 | else { |
133 | 0 | assert(Allocator != reinterpret_cast<StorageAllocator *>(~uintptr_t(0))); |
134 | 0 | DiagStorage = new Storage; |
135 | 0 | } |
136 | 632k | return DiagStorage; |
137 | 632k | } |
138 | | |
139 | 3.07M | void freeStorage() { |
140 | 3.07M | if (!DiagStorage) |
141 | 2.46M | return; |
142 | 3.07M | |
143 | 3.07M | // The hot path for PartialDiagnostic is when we just used it to wrap an ID |
144 | 3.07M | // (typically so we have the flexibility of passing a more complex |
145 | 3.07M | // diagnostic into the callee, but that does not commonly occur). |
146 | 3.07M | // |
147 | 3.07M | // Split this out into a slow function for silly compilers (*cough*) which |
148 | 3.07M | // can't do decent partial inlining. |
149 | 612k | freeStorageSlow(); |
150 | 612k | } |
151 | | |
152 | 612k | void freeStorageSlow() { |
153 | 612k | if (Allocator) |
154 | 612k | Allocator->Deallocate(DiagStorage); |
155 | 0 | else if (0 Allocator != reinterpret_cast<StorageAllocator *>(~uintptr_t(0))0 ) |
156 | 0 | delete DiagStorage; |
157 | 612k | DiagStorage = nullptr; |
158 | 612k | } |
159 | | |
160 | 99.3k | void AddSourceRange(const CharSourceRange &R) const { |
161 | 99.3k | if (!DiagStorage) |
162 | 3.76k | DiagStorage = getStorage(); |
163 | 99.3k | |
164 | 99.3k | DiagStorage->DiagRanges.push_back(R); |
165 | 99.3k | } |
166 | | |
167 | 21.1k | void AddFixItHint(const FixItHint &Hint) const { |
168 | 21.1k | if (Hint.isNull()) |
169 | 20.9k | return; |
170 | 21.1k | |
171 | 210 | if (210 !DiagStorage210 ) |
172 | 63 | DiagStorage = getStorage(); |
173 | 21.1k | |
174 | 21.1k | DiagStorage->FixItHints.push_back(Hint); |
175 | 21.1k | } |
176 | | |
177 | | public: |
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 | 1.03k | : DiagID(0), DiagStorage(nullptr), Allocator(nullptr) { } |
183 | | |
184 | | PartialDiagnostic(unsigned DiagID, StorageAllocator &Allocator) |
185 | 1.50M | : DiagID(DiagID), DiagStorage(nullptr), Allocator(&Allocator) { } |
186 | | |
187 | | PartialDiagnostic(const PartialDiagnostic &Other) |
188 | | : DiagID(Other.DiagID), DiagStorage(nullptr), Allocator(Other.Allocator) |
189 | 728k | { |
190 | 728k | if (Other.DiagStorage728k ) { |
191 | 63.0k | DiagStorage = getStorage(); |
192 | 63.0k | *DiagStorage = *Other.DiagStorage; |
193 | 63.0k | } |
194 | 728k | } |
195 | | |
196 | | PartialDiagnostic(PartialDiagnostic &&Other) |
197 | | : DiagID(Other.DiagID), DiagStorage(Other.DiagStorage), |
198 | 476k | Allocator(Other.Allocator) { |
199 | 476k | Other.DiagStorage = nullptr; |
200 | 476k | } |
201 | | |
202 | | PartialDiagnostic(const PartialDiagnostic &Other, Storage *DiagStorage) |
203 | | : DiagID(Other.DiagID), DiagStorage(DiagStorage), |
204 | | Allocator(reinterpret_cast<StorageAllocator *>(~uintptr_t(0))) |
205 | 43 | { |
206 | 43 | if (Other.DiagStorage) |
207 | 2 | *this->DiagStorage = *Other.DiagStorage; |
208 | 43 | } |
209 | | |
210 | | PartialDiagnostic(const Diagnostic &Other, StorageAllocator &Allocator) |
211 | | : DiagID(Other.getID()), DiagStorage(nullptr), Allocator(&Allocator) |
212 | 3.01k | { |
213 | 3.01k | // Copy arguments. |
214 | 6.08k | for (unsigned I = 0, N = Other.getNumArgs(); I != N6.08k ; ++I3.07k ) { |
215 | 3.07k | if (Other.getArgKind(I) == DiagnosticsEngine::ak_std_string) |
216 | 196 | AddString(Other.getArgStdStr(I)); |
217 | 3.07k | else |
218 | 2.88k | AddTaggedVal(Other.getRawArg(I), Other.getArgKind(I)); |
219 | 3.07k | } |
220 | 3.01k | |
221 | 3.01k | // Copy source ranges. |
222 | 4.46k | for (unsigned I = 0, N = Other.getNumRanges(); I != N4.46k ; ++I1.45k ) |
223 | 1.45k | AddSourceRange(Other.getRange(I)); |
224 | 3.01k | |
225 | 3.01k | // Copy fix-its. |
226 | 3.06k | for (unsigned I = 0, N = Other.getNumFixItHints(); I != N3.06k ; ++I52 ) |
227 | 52 | AddFixItHint(Other.getFixItHint(I)); |
228 | 3.01k | } |
229 | | |
230 | 8.75k | PartialDiagnostic &operator=(const PartialDiagnostic &Other) { |
231 | 8.75k | DiagID = Other.DiagID; |
232 | 8.75k | if (Other.DiagStorage8.75k ) { |
233 | 5.33k | if (!DiagStorage) |
234 | 5.33k | DiagStorage = getStorage(); |
235 | 5.33k | |
236 | 5.33k | *DiagStorage = *Other.DiagStorage; |
237 | 8.75k | } else { |
238 | 3.42k | freeStorage(); |
239 | 3.42k | } |
240 | 8.75k | |
241 | 8.75k | return *this; |
242 | 8.75k | } |
243 | | |
244 | 5.41k | PartialDiagnostic &operator=(PartialDiagnostic &&Other) { |
245 | 5.41k | freeStorage(); |
246 | 5.41k | |
247 | 5.41k | DiagID = Other.DiagID; |
248 | 5.41k | DiagStorage = Other.DiagStorage; |
249 | 5.41k | Allocator = Other.Allocator; |
250 | 5.41k | |
251 | 5.41k | Other.DiagStorage = nullptr; |
252 | 5.41k | return *this; |
253 | 5.41k | } |
254 | | |
255 | 2.69M | ~PartialDiagnostic() { |
256 | 2.69M | freeStorage(); |
257 | 2.69M | } |
258 | | |
259 | 1.03k | void swap(PartialDiagnostic &PD) { |
260 | 1.03k | std::swap(DiagID, PD.DiagID); |
261 | 1.03k | std::swap(DiagStorage, PD.DiagStorage); |
262 | 1.03k | std::swap(Allocator, PD.Allocator); |
263 | 1.03k | } |
264 | | |
265 | 66.0k | unsigned getDiagID() const { return DiagID; } |
266 | | |
267 | 704k | void AddTaggedVal(intptr_t V, DiagnosticsEngine::ArgumentKind Kind) const { |
268 | 704k | if (!DiagStorage) |
269 | 537k | DiagStorage = getStorage(); |
270 | 704k | |
271 | 704k | assert(DiagStorage->NumDiagArgs < Storage::MaxArguments && |
272 | 704k | "Too many arguments to diagnostic!"); |
273 | 704k | DiagStorage->DiagArgumentsKind[DiagStorage->NumDiagArgs] = Kind; |
274 | 704k | DiagStorage->DiagArgumentsVal[DiagStorage->NumDiagArgs++] = V; |
275 | 704k | } |
276 | | |
277 | 51.8k | void AddString(StringRef V) const { |
278 | 51.8k | if (!DiagStorage) |
279 | 22.5k | DiagStorage = getStorage(); |
280 | 51.8k | |
281 | 51.8k | assert(DiagStorage->NumDiagArgs < Storage::MaxArguments && |
282 | 51.8k | "Too many arguments to diagnostic!"); |
283 | 51.8k | DiagStorage->DiagArgumentsKind[DiagStorage->NumDiagArgs] |
284 | 51.8k | = DiagnosticsEngine::ak_std_string; |
285 | 51.8k | DiagStorage->DiagArgumentsStr[DiagStorage->NumDiagArgs++] = V; |
286 | 51.8k | } |
287 | | |
288 | 60.0k | void Emit(const DiagnosticBuilder &DB) const { |
289 | 60.0k | if (!DiagStorage) |
290 | 3.32k | return; |
291 | 60.0k | |
292 | 60.0k | // Add all arguments. |
293 | 222k | for (unsigned i = 0, e = DiagStorage->NumDiagArgs; 56.7k i != e222k ; ++i165k ) { |
294 | 165k | if ((DiagnosticsEngine::ArgumentKind)DiagStorage->DiagArgumentsKind[i] |
295 | 165k | == DiagnosticsEngine::ak_std_string) |
296 | 14.8k | DB.AddString(DiagStorage->DiagArgumentsStr[i]); |
297 | 165k | else |
298 | 150k | DB.AddTaggedVal(DiagStorage->DiagArgumentsVal[i], |
299 | 150k | (DiagnosticsEngine::ArgumentKind)DiagStorage->DiagArgumentsKind[i]); |
300 | 165k | } |
301 | 56.7k | |
302 | 56.7k | // Add all ranges. |
303 | 56.7k | for (const CharSourceRange &Range : DiagStorage->DiagRanges) |
304 | 66.2k | DB.AddSourceRange(Range); |
305 | 56.7k | |
306 | 56.7k | // Add all fix-its. |
307 | 56.7k | for (const FixItHint &Fix : DiagStorage->FixItHints) |
308 | 177 | DB.AddFixItHint(Fix); |
309 | 60.0k | } |
310 | | |
311 | | void EmitToString(DiagnosticsEngine &Diags, |
312 | 167 | SmallVectorImpl<char> &Buf) const { |
313 | 167 | // FIXME: It should be possible to render a diagnostic to a string without |
314 | 167 | // messing with the state of the diagnostics engine. |
315 | 167 | DiagnosticBuilder DB(Diags.Report(getDiagID())); |
316 | 167 | Emit(DB); |
317 | 167 | DB.FlushCounts(); |
318 | 167 | Diagnostic(&Diags).FormatDiagnostic(Buf); |
319 | 167 | DB.Clear(); |
320 | 167 | Diags.Clear(); |
321 | 167 | } |
322 | | |
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 | 375k | void Reset(unsigned DiagID = 0) { |
326 | 375k | this->DiagID = DiagID; |
327 | 375k | freeStorage(); |
328 | 375k | } |
329 | | |
330 | 43 | bool hasStorage() const { return DiagStorage != nullptr; } |
331 | | |
332 | | /// Retrieve the string argument at the given index. |
333 | 16 | StringRef getStringArg(unsigned I) { |
334 | 16 | assert(DiagStorage && "No diagnostic storage?"); |
335 | 16 | assert(I < DiagStorage->NumDiagArgs && "Not enough diagnostic args"); |
336 | 16 | assert(DiagStorage->DiagArgumentsKind[I] |
337 | 16 | == DiagnosticsEngine::ak_std_string && "Not a string arg"); |
338 | 16 | return DiagStorage->DiagArgumentsStr[I]; |
339 | 16 | } |
340 | | |
341 | | friend const PartialDiagnostic &operator<<(const PartialDiagnostic &PD, |
342 | 42.4k | unsigned I) { |
343 | 42.4k | PD.AddTaggedVal(I, DiagnosticsEngine::ak_uint); |
344 | 42.4k | return PD; |
345 | 42.4k | } |
346 | | |
347 | | friend const PartialDiagnostic &operator<<(const PartialDiagnostic &PD, |
348 | 222k | int I) { |
349 | 222k | PD.AddTaggedVal(I, DiagnosticsEngine::ak_sint); |
350 | 222k | return PD; |
351 | 222k | } |
352 | | |
353 | | friend inline const PartialDiagnostic &operator<<(const PartialDiagnostic &PD, |
354 | 712 | const char *S) { |
355 | 712 | PD.AddTaggedVal(reinterpret_cast<intptr_t>(S), |
356 | 712 | DiagnosticsEngine::ak_c_string); |
357 | 712 | return PD; |
358 | 712 | } |
359 | | |
360 | | friend inline const PartialDiagnostic &operator<<(const PartialDiagnostic &PD, |
361 | 51.6k | StringRef S) { |
362 | 51.6k | |
363 | 51.6k | PD.AddString(S); |
364 | 51.6k | return PD; |
365 | 51.6k | } |
366 | | |
367 | | friend inline const PartialDiagnostic &operator<<(const PartialDiagnostic &PD, |
368 | 386 | const IdentifierInfo *II) { |
369 | 386 | PD.AddTaggedVal(reinterpret_cast<intptr_t>(II), |
370 | 386 | DiagnosticsEngine::ak_identifierinfo); |
371 | 386 | return PD; |
372 | 386 | } |
373 | | |
374 | | // Adds a DeclContext to the diagnostic. The enable_if template magic is here |
375 | | // so that we only match those arguments that are (statically) DeclContexts; |
376 | | // other arguments that derive from DeclContext (e.g., RecordDecls) will not |
377 | | // match. |
378 | | template<typename T> |
379 | | friend inline |
380 | | typename std::enable_if<std::is_same<T, DeclContext>::value, |
381 | | const PartialDiagnostic &>::type |
382 | 268 | operator<<(const PartialDiagnostic &PD, T *DC) { |
383 | 268 | PD.AddTaggedVal(reinterpret_cast<intptr_t>(DC), |
384 | 268 | DiagnosticsEngine::ak_declcontext); |
385 | 268 | return PD; |
386 | 268 | } |
387 | | |
388 | | friend inline const PartialDiagnostic &operator<<(const PartialDiagnostic &PD, |
389 | 97.5k | SourceRange R) { |
390 | 97.5k | PD.AddSourceRange(CharSourceRange::getTokenRange(R)); |
391 | 97.5k | return PD; |
392 | 97.5k | } |
393 | | |
394 | | friend inline const PartialDiagnostic &operator<<(const PartialDiagnostic &PD, |
395 | 355 | const CharSourceRange &R) { |
396 | 355 | PD.AddSourceRange(R); |
397 | 355 | return PD; |
398 | 355 | } |
399 | | |
400 | | friend const PartialDiagnostic &operator<<(const PartialDiagnostic &PD, |
401 | 21.1k | const FixItHint &Hint) { |
402 | 21.1k | PD.AddFixItHint(Hint); |
403 | 21.1k | return PD; |
404 | 21.1k | } |
405 | | |
406 | | }; |
407 | | |
408 | | inline const DiagnosticBuilder &operator<<(const DiagnosticBuilder &DB, |
409 | 0 | const PartialDiagnostic &PD) { |
410 | 0 | PD.Emit(DB); |
411 | 0 | return DB; |
412 | 0 | } |
413 | | |
414 | | /// \brief A partial diagnostic along with the source location where this |
415 | | /// diagnostic occurs. |
416 | | typedef std::pair<SourceLocation, PartialDiagnostic> PartialDiagnosticAt; |
417 | | |
418 | | } // end namespace clang |
419 | | #endif |