]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - contrib/llvm/tools/clang/include/clang/Basic/PartialDiagnostic.h
Merge clang 7.0.1 and several follow-up changes
[FreeBSD/FreeBSD.git] / contrib / llvm / tools / clang / include / clang / Basic / PartialDiagnostic.h
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
30 namespace clang {
31
32 class DeclContext;
33 class IdentifierInfo;
34
35 class PartialDiagnostic {
36 public:
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
118 private:
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
184 public:
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
411 inline 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.
419 using PartialDiagnosticAt = std::pair<SourceLocation, PartialDiagnostic>;
420
421 } // namespace clang
422
423 #endif // LLVM_CLANG_BASIC_PARTIALDIAGNOSTIC_H