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_PARTIALDIAGNOSTIC_H 17 #define LLVM_CLANG_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 { StorageStorage39 Storage() : NumDiagArgs(0), NumDiagRanges(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 This is the number of ranges in the DiagRanges array. 54 unsigned char NumDiagRanges; 55 56 /// \brief Specifies for each argument whether it is in DiagArgumentsStr 57 /// or in DiagArguments. 58 unsigned char DiagArgumentsKind[MaxArguments]; 59 60 /// \brief The values for the various substitution positions. 61 /// 62 /// This is used when the argument is not an std::string. The specific value 63 /// is mangled into an intptr_t and the interpretation depends on exactly 64 /// what sort of argument kind it is. 65 intptr_t DiagArgumentsVal[MaxArguments]; 66 67 /// \brief The values for the various substitution positions that have 68 /// string arguments. 69 std::string DiagArgumentsStr[MaxArguments]; 70 71 /// \brief The list of ranges added to this diagnostic. 72 /// 73 /// It currently only support 10 ranges, could easily be extended if needed. 74 CharSourceRange DiagRanges[10]; 75 76 /// \brief 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 81 /// \brief An allocator for Storage objects, which uses a small cache to 82 /// objects, used to reduce malloc()/free() traffic for partial diagnostics. 83 class StorageAllocator { 84 static const unsigned NumCached = 16; 85 Storage Cached[NumCached]; 86 Storage *FreeList[NumCached]; 87 unsigned NumFreeListEntries; 88 89 public: 90 StorageAllocator(); 91 ~StorageAllocator(); 92 93 /// \brief Allocate new storage. Allocate()94 Storage *Allocate() { 95 if (NumFreeListEntries == 0) 96 return new Storage; 97 98 Storage *Result = FreeList[--NumFreeListEntries]; 99 Result->NumDiagArgs = 0; 100 Result->NumDiagRanges = 0; 101 Result->FixItHints.clear(); 102 return Result; 103 } 104 105 /// \brief Free the given storage object. Deallocate(Storage * S)106 void Deallocate(Storage *S) { 107 if (S >= Cached && S <= Cached + NumCached) { 108 FreeList[NumFreeListEntries++] = S; 109 return; 110 } 111 112 delete S; 113 } 114 }; 115 116 private: 117 // NOTE: Sema assumes that PartialDiagnostic is location-invariant 118 // in the sense that its bits can be safely memcpy'ed and destructed 119 // in the new location. 120 121 /// \brief The diagnostic ID. 122 mutable unsigned DiagID; 123 124 /// \brief Storage for args and ranges. 125 mutable Storage *DiagStorage; 126 127 /// \brief Allocator used to allocate storage for this diagnostic. 128 StorageAllocator *Allocator; 129 130 /// \brief Retrieve storage for this particular diagnostic. getStorage()131 Storage *getStorage() const { 132 if (DiagStorage) 133 return DiagStorage; 134 135 if (Allocator) 136 DiagStorage = Allocator->Allocate(); 137 else { 138 assert(Allocator != reinterpret_cast<StorageAllocator *>(~uintptr_t(0))); 139 DiagStorage = new Storage; 140 } 141 return DiagStorage; 142 } 143 freeStorage()144 void freeStorage() { 145 if (!DiagStorage) 146 return; 147 148 // The hot path for PartialDiagnostic is when we just used it to wrap an ID 149 // (typically so we have the flexibility of passing a more complex 150 // diagnostic into the callee, but that does not commonly occur). 151 // 152 // Split this out into a slow function for silly compilers (*cough*) which 153 // can't do decent partial inlining. 154 freeStorageSlow(); 155 } 156 freeStorageSlow()157 void freeStorageSlow() { 158 if (Allocator) 159 Allocator->Deallocate(DiagStorage); 160 else if (Allocator != reinterpret_cast<StorageAllocator *>(~uintptr_t(0))) 161 delete DiagStorage; 162 DiagStorage = 0; 163 } 164 AddSourceRange(const CharSourceRange & R)165 void AddSourceRange(const CharSourceRange &R) const { 166 if (!DiagStorage) 167 DiagStorage = getStorage(); 168 169 assert(DiagStorage->NumDiagRanges < 170 llvm::array_lengthof(DiagStorage->DiagRanges) && 171 "Too many arguments to diagnostic!"); 172 DiagStorage->DiagRanges[DiagStorage->NumDiagRanges++] = R; 173 } 174 AddFixItHint(const FixItHint & Hint)175 void AddFixItHint(const FixItHint &Hint) const { 176 if (Hint.isNull()) 177 return; 178 179 if (!DiagStorage) 180 DiagStorage = getStorage(); 181 182 DiagStorage->FixItHints.push_back(Hint); 183 } 184 185 public: 186 struct NullDiagnostic {}; 187 /// \brief Create a null partial diagnostic, which cannot carry a payload, 188 /// and only exists to be swapped with a real partial diagnostic. PartialDiagnostic(NullDiagnostic)189 PartialDiagnostic(NullDiagnostic) 190 : DiagID(0), DiagStorage(0), Allocator(0) { } 191 PartialDiagnostic(unsigned DiagID,StorageAllocator & Allocator)192 PartialDiagnostic(unsigned DiagID, StorageAllocator &Allocator) 193 : DiagID(DiagID), DiagStorage(0), Allocator(&Allocator) { } 194 PartialDiagnostic(const PartialDiagnostic & Other)195 PartialDiagnostic(const PartialDiagnostic &Other) 196 : DiagID(Other.DiagID), DiagStorage(0), Allocator(Other.Allocator) 197 { 198 if (Other.DiagStorage) { 199 DiagStorage = getStorage(); 200 *DiagStorage = *Other.DiagStorage; 201 } 202 } 203 204 #if LLVM_HAS_RVALUE_REFERENCES PartialDiagnostic(PartialDiagnostic && Other)205 PartialDiagnostic(PartialDiagnostic &&Other) 206 : DiagID(Other.DiagID), DiagStorage(Other.DiagStorage), 207 Allocator(Other.Allocator) { 208 Other.DiagStorage = 0; 209 } 210 #endif 211 PartialDiagnostic(const PartialDiagnostic & Other,Storage * DiagStorage)212 PartialDiagnostic(const PartialDiagnostic &Other, Storage *DiagStorage) 213 : DiagID(Other.DiagID), DiagStorage(DiagStorage), 214 Allocator(reinterpret_cast<StorageAllocator *>(~uintptr_t(0))) 215 { 216 if (Other.DiagStorage) 217 *this->DiagStorage = *Other.DiagStorage; 218 } 219 PartialDiagnostic(const Diagnostic & Other,StorageAllocator & Allocator)220 PartialDiagnostic(const Diagnostic &Other, StorageAllocator &Allocator) 221 : DiagID(Other.getID()), DiagStorage(0), Allocator(&Allocator) 222 { 223 // Copy arguments. 224 for (unsigned I = 0, N = Other.getNumArgs(); I != N; ++I) { 225 if (Other.getArgKind(I) == DiagnosticsEngine::ak_std_string) 226 AddString(Other.getArgStdStr(I)); 227 else 228 AddTaggedVal(Other.getRawArg(I), Other.getArgKind(I)); 229 } 230 231 // Copy source ranges. 232 for (unsigned I = 0, N = Other.getNumRanges(); I != N; ++I) 233 AddSourceRange(Other.getRange(I)); 234 235 // Copy fix-its. 236 for (unsigned I = 0, N = Other.getNumFixItHints(); I != N; ++I) 237 AddFixItHint(Other.getFixItHint(I)); 238 } 239 240 PartialDiagnostic &operator=(const PartialDiagnostic &Other) { 241 DiagID = Other.DiagID; 242 if (Other.DiagStorage) { 243 if (!DiagStorage) 244 DiagStorage = getStorage(); 245 246 *DiagStorage = *Other.DiagStorage; 247 } else { 248 freeStorage(); 249 } 250 251 return *this; 252 } 253 254 #if LLVM_HAS_RVALUE_REFERENCES 255 PartialDiagnostic &operator=(PartialDiagnostic &&Other) { 256 freeStorage(); 257 258 DiagID = Other.DiagID; 259 DiagStorage = Other.DiagStorage; 260 Allocator = Other.Allocator; 261 262 Other.DiagStorage = 0; 263 return *this; 264 } 265 #endif 266 ~PartialDiagnostic()267 ~PartialDiagnostic() { 268 freeStorage(); 269 } 270 swap(PartialDiagnostic & PD)271 void swap(PartialDiagnostic &PD) { 272 std::swap(DiagID, PD.DiagID); 273 std::swap(DiagStorage, PD.DiagStorage); 274 std::swap(Allocator, PD.Allocator); 275 } 276 getDiagID()277 unsigned getDiagID() const { return DiagID; } 278 AddTaggedVal(intptr_t V,DiagnosticsEngine::ArgumentKind Kind)279 void AddTaggedVal(intptr_t V, DiagnosticsEngine::ArgumentKind Kind) const { 280 if (!DiagStorage) 281 DiagStorage = getStorage(); 282 283 assert(DiagStorage->NumDiagArgs < Storage::MaxArguments && 284 "Too many arguments to diagnostic!"); 285 DiagStorage->DiagArgumentsKind[DiagStorage->NumDiagArgs] = Kind; 286 DiagStorage->DiagArgumentsVal[DiagStorage->NumDiagArgs++] = V; 287 } 288 AddString(StringRef V)289 void AddString(StringRef V) const { 290 if (!DiagStorage) 291 DiagStorage = getStorage(); 292 293 assert(DiagStorage->NumDiagArgs < Storage::MaxArguments && 294 "Too many arguments to diagnostic!"); 295 DiagStorage->DiagArgumentsKind[DiagStorage->NumDiagArgs] 296 = DiagnosticsEngine::ak_std_string; 297 DiagStorage->DiagArgumentsStr[DiagStorage->NumDiagArgs++] = V; 298 } 299 Emit(const DiagnosticBuilder & DB)300 void Emit(const DiagnosticBuilder &DB) const { 301 if (!DiagStorage) 302 return; 303 304 // Add all arguments. 305 for (unsigned i = 0, e = DiagStorage->NumDiagArgs; i != e; ++i) { 306 if ((DiagnosticsEngine::ArgumentKind)DiagStorage->DiagArgumentsKind[i] 307 == DiagnosticsEngine::ak_std_string) 308 DB.AddString(DiagStorage->DiagArgumentsStr[i]); 309 else 310 DB.AddTaggedVal(DiagStorage->DiagArgumentsVal[i], 311 (DiagnosticsEngine::ArgumentKind)DiagStorage->DiagArgumentsKind[i]); 312 } 313 314 // Add all ranges. 315 for (unsigned i = 0, e = DiagStorage->NumDiagRanges; i != e; ++i) 316 DB.AddSourceRange(DiagStorage->DiagRanges[i]); 317 318 // Add all fix-its. 319 for (unsigned i = 0, e = DiagStorage->FixItHints.size(); i != e; ++i) 320 DB.AddFixItHint(DiagStorage->FixItHints[i]); 321 } 322 EmitToString(DiagnosticsEngine & Diags,SmallVectorImpl<char> & Buf)323 void EmitToString(DiagnosticsEngine &Diags, 324 SmallVectorImpl<char> &Buf) const { 325 // FIXME: It should be possible to render a diagnostic to a string without 326 // messing with the state of the diagnostics engine. 327 DiagnosticBuilder DB(Diags.Report(getDiagID())); 328 Emit(DB); 329 DB.FlushCounts(); 330 Diagnostic(&Diags).FormatDiagnostic(Buf); 331 DB.Clear(); 332 Diags.Clear(); 333 } 334 335 /// \brief Clear out this partial diagnostic, giving it a new diagnostic ID 336 /// and removing all of its arguments, ranges, and fix-it hints. 337 void Reset(unsigned DiagID = 0) { 338 this->DiagID = DiagID; 339 freeStorage(); 340 } 341 hasStorage()342 bool hasStorage() const { return DiagStorage != 0; } 343 344 friend const PartialDiagnostic &operator<<(const PartialDiagnostic &PD, 345 unsigned I) { 346 PD.AddTaggedVal(I, DiagnosticsEngine::ak_uint); 347 return PD; 348 } 349 350 friend const PartialDiagnostic &operator<<(const PartialDiagnostic &PD, 351 int I) { 352 PD.AddTaggedVal(I, DiagnosticsEngine::ak_sint); 353 return PD; 354 } 355 356 friend inline const PartialDiagnostic &operator<<(const PartialDiagnostic &PD, 357 const char *S) { 358 PD.AddTaggedVal(reinterpret_cast<intptr_t>(S), 359 DiagnosticsEngine::ak_c_string); 360 return PD; 361 } 362 363 friend inline const PartialDiagnostic &operator<<(const PartialDiagnostic &PD, 364 StringRef S) { 365 366 PD.AddString(S); 367 return PD; 368 } 369 370 friend inline const PartialDiagnostic &operator<<(const PartialDiagnostic &PD, 371 const SourceRange &R) { 372 PD.AddSourceRange(CharSourceRange::getTokenRange(R)); 373 return PD; 374 } 375 376 friend inline const PartialDiagnostic &operator<<(const PartialDiagnostic &PD, 377 const CharSourceRange &R) { 378 PD.AddSourceRange(R); 379 return PD; 380 } 381 382 friend const PartialDiagnostic &operator<<(const PartialDiagnostic &PD, 383 const FixItHint &Hint) { 384 PD.AddFixItHint(Hint); 385 return PD; 386 } 387 388 }; 389 390 inline const DiagnosticBuilder &operator<<(const DiagnosticBuilder &DB, 391 const PartialDiagnostic &PD) { 392 PD.Emit(DB); 393 return DB; 394 } 395 396 /// \brief A partial diagnostic along with the source location where this 397 /// diagnostic occurs. 398 typedef std::pair<SourceLocation, PartialDiagnostic> PartialDiagnosticAt; 399 400 } // end namespace clang 401 #endif 402