clang API Documentation
00001 //===--- PartialDiagnostic.h - Diagnostic "closures" ------------*- C++ -*-===// 00002 // 00003 // The LLVM Compiler Infrastructure 00004 // 00005 // This file is distributed under the University of Illinois Open Source 00006 // License. See LICENSE.TXT for details. 00007 // 00008 //===----------------------------------------------------------------------===// 00009 /// 00010 /// \file 00011 /// \brief Implements a partial diagnostic that can be emitted anwyhere 00012 /// in a DiagnosticBuilder stream. 00013 /// 00014 //===----------------------------------------------------------------------===// 00015 00016 #ifndef LLVM_CLANG_BASIC_PARTIALDIAGNOSTIC_H 00017 #define LLVM_CLANG_BASIC_PARTIALDIAGNOSTIC_H 00018 00019 #include "clang/Basic/Diagnostic.h" 00020 #include "clang/Basic/SourceLocation.h" 00021 #include "llvm/ADT/STLExtras.h" 00022 #include "llvm/Support/Compiler.h" 00023 #include "llvm/Support/DataTypes.h" 00024 #include <cassert> 00025 00026 namespace clang { 00027 00028 class PartialDiagnostic { 00029 public: 00030 enum { 00031 // The MaxArguments and MaxFixItHints member enum values from 00032 // DiagnosticsEngine are private but DiagnosticsEngine declares 00033 // PartialDiagnostic a friend. These enum values are redeclared 00034 // here so that the nested Storage class below can access them. 00035 MaxArguments = DiagnosticsEngine::MaxArguments 00036 }; 00037 00038 struct Storage { 00039 Storage() : NumDiagArgs(0) { } 00040 00041 enum { 00042 /// \brief The maximum number of arguments we can hold. We 00043 /// currently only support up to 10 arguments (%0-%9). 00044 /// 00045 /// A single diagnostic with more than that almost certainly has to 00046 /// be simplified anyway. 00047 MaxArguments = PartialDiagnostic::MaxArguments 00048 }; 00049 00050 /// \brief The number of entries in Arguments. 00051 unsigned char NumDiagArgs; 00052 00053 /// \brief Specifies for each argument whether it is in DiagArgumentsStr 00054 /// or in DiagArguments. 00055 unsigned char DiagArgumentsKind[MaxArguments]; 00056 00057 /// \brief The values for the various substitution positions. 00058 /// 00059 /// This is used when the argument is not an std::string. The specific value 00060 /// is mangled into an intptr_t and the interpretation depends on exactly 00061 /// what sort of argument kind it is. 00062 intptr_t DiagArgumentsVal[MaxArguments]; 00063 00064 /// \brief The values for the various substitution positions that have 00065 /// string arguments. 00066 std::string DiagArgumentsStr[MaxArguments]; 00067 00068 /// \brief The list of ranges added to this diagnostic. 00069 SmallVector<CharSourceRange, 8> DiagRanges; 00070 00071 /// \brief If valid, provides a hint with some code to insert, remove, or 00072 /// modify at a particular position. 00073 SmallVector<FixItHint, 6> FixItHints; 00074 }; 00075 00076 /// \brief An allocator for Storage objects, which uses a small cache to 00077 /// objects, used to reduce malloc()/free() traffic for partial diagnostics. 00078 class StorageAllocator { 00079 static const unsigned NumCached = 16; 00080 Storage Cached[NumCached]; 00081 Storage *FreeList[NumCached]; 00082 unsigned NumFreeListEntries; 00083 00084 public: 00085 StorageAllocator(); 00086 ~StorageAllocator(); 00087 00088 /// \brief Allocate new storage. 00089 Storage *Allocate() { 00090 if (NumFreeListEntries == 0) 00091 return new Storage; 00092 00093 Storage *Result = FreeList[--NumFreeListEntries]; 00094 Result->NumDiagArgs = 0; 00095 Result->DiagRanges.clear(); 00096 Result->FixItHints.clear(); 00097 return Result; 00098 } 00099 00100 /// \brief Free the given storage object. 00101 void Deallocate(Storage *S) { 00102 if (S >= Cached && S <= Cached + NumCached) { 00103 FreeList[NumFreeListEntries++] = S; 00104 return; 00105 } 00106 00107 delete S; 00108 } 00109 }; 00110 00111 private: 00112 // NOTE: Sema assumes that PartialDiagnostic is location-invariant 00113 // in the sense that its bits can be safely memcpy'ed and destructed 00114 // in the new location. 00115 00116 /// \brief The diagnostic ID. 00117 mutable unsigned DiagID; 00118 00119 /// \brief Storage for args and ranges. 00120 mutable Storage *DiagStorage; 00121 00122 /// \brief Allocator used to allocate storage for this diagnostic. 00123 StorageAllocator *Allocator; 00124 00125 /// \brief Retrieve storage for this particular diagnostic. 00126 Storage *getStorage() const { 00127 if (DiagStorage) 00128 return DiagStorage; 00129 00130 if (Allocator) 00131 DiagStorage = Allocator->Allocate(); 00132 else { 00133 assert(Allocator != reinterpret_cast<StorageAllocator *>(~uintptr_t(0))); 00134 DiagStorage = new Storage; 00135 } 00136 return DiagStorage; 00137 } 00138 00139 void freeStorage() { 00140 if (!DiagStorage) 00141 return; 00142 00143 // The hot path for PartialDiagnostic is when we just used it to wrap an ID 00144 // (typically so we have the flexibility of passing a more complex 00145 // diagnostic into the callee, but that does not commonly occur). 00146 // 00147 // Split this out into a slow function for silly compilers (*cough*) which 00148 // can't do decent partial inlining. 00149 freeStorageSlow(); 00150 } 00151 00152 void freeStorageSlow() { 00153 if (Allocator) 00154 Allocator->Deallocate(DiagStorage); 00155 else if (Allocator != reinterpret_cast<StorageAllocator *>(~uintptr_t(0))) 00156 delete DiagStorage; 00157 DiagStorage = nullptr; 00158 } 00159 00160 void AddSourceRange(const CharSourceRange &R) const { 00161 if (!DiagStorage) 00162 DiagStorage = getStorage(); 00163 00164 DiagStorage->DiagRanges.push_back(R); 00165 } 00166 00167 void AddFixItHint(const FixItHint &Hint) const { 00168 if (Hint.isNull()) 00169 return; 00170 00171 if (!DiagStorage) 00172 DiagStorage = getStorage(); 00173 00174 DiagStorage->FixItHints.push_back(Hint); 00175 } 00176 00177 public: 00178 struct NullDiagnostic {}; 00179 /// \brief Create a null partial diagnostic, which cannot carry a payload, 00180 /// and only exists to be swapped with a real partial diagnostic. 00181 PartialDiagnostic(NullDiagnostic) 00182 : DiagID(0), DiagStorage(nullptr), Allocator(nullptr) { } 00183 00184 PartialDiagnostic(unsigned DiagID, StorageAllocator &Allocator) 00185 : DiagID(DiagID), DiagStorage(nullptr), Allocator(&Allocator) { } 00186 00187 PartialDiagnostic(const PartialDiagnostic &Other) 00188 : DiagID(Other.DiagID), DiagStorage(nullptr), Allocator(Other.Allocator) 00189 { 00190 if (Other.DiagStorage) { 00191 DiagStorage = getStorage(); 00192 *DiagStorage = *Other.DiagStorage; 00193 } 00194 } 00195 00196 PartialDiagnostic(PartialDiagnostic &&Other) 00197 : DiagID(Other.DiagID), DiagStorage(Other.DiagStorage), 00198 Allocator(Other.Allocator) { 00199 Other.DiagStorage = nullptr; 00200 } 00201 00202 PartialDiagnostic(const PartialDiagnostic &Other, Storage *DiagStorage) 00203 : DiagID(Other.DiagID), DiagStorage(DiagStorage), 00204 Allocator(reinterpret_cast<StorageAllocator *>(~uintptr_t(0))) 00205 { 00206 if (Other.DiagStorage) 00207 *this->DiagStorage = *Other.DiagStorage; 00208 } 00209 00210 PartialDiagnostic(const Diagnostic &Other, StorageAllocator &Allocator) 00211 : DiagID(Other.getID()), DiagStorage(nullptr), Allocator(&Allocator) 00212 { 00213 // Copy arguments. 00214 for (unsigned I = 0, N = Other.getNumArgs(); I != N; ++I) { 00215 if (Other.getArgKind(I) == DiagnosticsEngine::ak_std_string) 00216 AddString(Other.getArgStdStr(I)); 00217 else 00218 AddTaggedVal(Other.getRawArg(I), Other.getArgKind(I)); 00219 } 00220 00221 // Copy source ranges. 00222 for (unsigned I = 0, N = Other.getNumRanges(); I != N; ++I) 00223 AddSourceRange(Other.getRange(I)); 00224 00225 // Copy fix-its. 00226 for (unsigned I = 0, N = Other.getNumFixItHints(); I != N; ++I) 00227 AddFixItHint(Other.getFixItHint(I)); 00228 } 00229 00230 PartialDiagnostic &operator=(const PartialDiagnostic &Other) { 00231 DiagID = Other.DiagID; 00232 if (Other.DiagStorage) { 00233 if (!DiagStorage) 00234 DiagStorage = getStorage(); 00235 00236 *DiagStorage = *Other.DiagStorage; 00237 } else { 00238 freeStorage(); 00239 } 00240 00241 return *this; 00242 } 00243 00244 PartialDiagnostic &operator=(PartialDiagnostic &&Other) { 00245 freeStorage(); 00246 00247 DiagID = Other.DiagID; 00248 DiagStorage = Other.DiagStorage; 00249 Allocator = Other.Allocator; 00250 00251 Other.DiagStorage = nullptr; 00252 return *this; 00253 } 00254 00255 ~PartialDiagnostic() { 00256 freeStorage(); 00257 } 00258 00259 void swap(PartialDiagnostic &PD) { 00260 std::swap(DiagID, PD.DiagID); 00261 std::swap(DiagStorage, PD.DiagStorage); 00262 std::swap(Allocator, PD.Allocator); 00263 } 00264 00265 unsigned getDiagID() const { return DiagID; } 00266 00267 void AddTaggedVal(intptr_t V, DiagnosticsEngine::ArgumentKind Kind) const { 00268 if (!DiagStorage) 00269 DiagStorage = getStorage(); 00270 00271 assert(DiagStorage->NumDiagArgs < Storage::MaxArguments && 00272 "Too many arguments to diagnostic!"); 00273 DiagStorage->DiagArgumentsKind[DiagStorage->NumDiagArgs] = Kind; 00274 DiagStorage->DiagArgumentsVal[DiagStorage->NumDiagArgs++] = V; 00275 } 00276 00277 void AddString(StringRef V) const { 00278 if (!DiagStorage) 00279 DiagStorage = getStorage(); 00280 00281 assert(DiagStorage->NumDiagArgs < Storage::MaxArguments && 00282 "Too many arguments to diagnostic!"); 00283 DiagStorage->DiagArgumentsKind[DiagStorage->NumDiagArgs] 00284 = DiagnosticsEngine::ak_std_string; 00285 DiagStorage->DiagArgumentsStr[DiagStorage->NumDiagArgs++] = V; 00286 } 00287 00288 void Emit(const DiagnosticBuilder &DB) const { 00289 if (!DiagStorage) 00290 return; 00291 00292 // Add all arguments. 00293 for (unsigned i = 0, e = DiagStorage->NumDiagArgs; i != e; ++i) { 00294 if ((DiagnosticsEngine::ArgumentKind)DiagStorage->DiagArgumentsKind[i] 00295 == DiagnosticsEngine::ak_std_string) 00296 DB.AddString(DiagStorage->DiagArgumentsStr[i]); 00297 else 00298 DB.AddTaggedVal(DiagStorage->DiagArgumentsVal[i], 00299 (DiagnosticsEngine::ArgumentKind)DiagStorage->DiagArgumentsKind[i]); 00300 } 00301 00302 // Add all ranges. 00303 for (const CharSourceRange &Range : DiagStorage->DiagRanges) 00304 DB.AddSourceRange(Range); 00305 00306 // Add all fix-its. 00307 for (const FixItHint &Fix : DiagStorage->FixItHints) 00308 DB.AddFixItHint(Fix); 00309 } 00310 00311 void EmitToString(DiagnosticsEngine &Diags, 00312 SmallVectorImpl<char> &Buf) const { 00313 // FIXME: It should be possible to render a diagnostic to a string without 00314 // messing with the state of the diagnostics engine. 00315 DiagnosticBuilder DB(Diags.Report(getDiagID())); 00316 Emit(DB); 00317 DB.FlushCounts(); 00318 Diagnostic(&Diags).FormatDiagnostic(Buf); 00319 DB.Clear(); 00320 Diags.Clear(); 00321 } 00322 00323 /// \brief Clear out this partial diagnostic, giving it a new diagnostic ID 00324 /// and removing all of its arguments, ranges, and fix-it hints. 00325 void Reset(unsigned DiagID = 0) { 00326 this->DiagID = DiagID; 00327 freeStorage(); 00328 } 00329 00330 bool hasStorage() const { return DiagStorage != nullptr; } 00331 00332 friend const PartialDiagnostic &operator<<(const PartialDiagnostic &PD, 00333 unsigned I) { 00334 PD.AddTaggedVal(I, DiagnosticsEngine::ak_uint); 00335 return PD; 00336 } 00337 00338 friend const PartialDiagnostic &operator<<(const PartialDiagnostic &PD, 00339 int I) { 00340 PD.AddTaggedVal(I, DiagnosticsEngine::ak_sint); 00341 return PD; 00342 } 00343 00344 friend inline const PartialDiagnostic &operator<<(const PartialDiagnostic &PD, 00345 const char *S) { 00346 PD.AddTaggedVal(reinterpret_cast<intptr_t>(S), 00347 DiagnosticsEngine::ak_c_string); 00348 return PD; 00349 } 00350 00351 friend inline const PartialDiagnostic &operator<<(const PartialDiagnostic &PD, 00352 StringRef S) { 00353 00354 PD.AddString(S); 00355 return PD; 00356 } 00357 00358 friend inline const PartialDiagnostic &operator<<(const PartialDiagnostic &PD, 00359 const IdentifierInfo *II) { 00360 PD.AddTaggedVal(reinterpret_cast<intptr_t>(II), 00361 DiagnosticsEngine::ak_identifierinfo); 00362 return PD; 00363 } 00364 00365 // Adds a DeclContext to the diagnostic. The enable_if template magic is here 00366 // so that we only match those arguments that are (statically) DeclContexts; 00367 // other arguments that derive from DeclContext (e.g., RecordDecls) will not 00368 // match. 00369 template<typename T> 00370 friend inline 00371 typename std::enable_if<std::is_same<T, DeclContext>::value, 00372 const PartialDiagnostic &>::type 00373 operator<<(const PartialDiagnostic &PD, T *DC) { 00374 PD.AddTaggedVal(reinterpret_cast<intptr_t>(DC), 00375 DiagnosticsEngine::ak_declcontext); 00376 return PD; 00377 } 00378 00379 friend inline const PartialDiagnostic &operator<<(const PartialDiagnostic &PD, 00380 const SourceRange &R) { 00381 PD.AddSourceRange(CharSourceRange::getTokenRange(R)); 00382 return PD; 00383 } 00384 00385 friend inline const PartialDiagnostic &operator<<(const PartialDiagnostic &PD, 00386 const CharSourceRange &R) { 00387 PD.AddSourceRange(R); 00388 return PD; 00389 } 00390 00391 friend const PartialDiagnostic &operator<<(const PartialDiagnostic &PD, 00392 const FixItHint &Hint) { 00393 PD.AddFixItHint(Hint); 00394 return PD; 00395 } 00396 00397 }; 00398 00399 inline const DiagnosticBuilder &operator<<(const DiagnosticBuilder &DB, 00400 const PartialDiagnostic &PD) { 00401 PD.Emit(DB); 00402 return DB; 00403 } 00404 00405 /// \brief A partial diagnostic along with the source location where this 00406 /// diagnostic occurs. 00407 typedef std::pair<SourceLocation, PartialDiagnostic> PartialDiagnosticAt; 00408 00409 } // end namespace clang 00410 #endif