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) { } 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. Allocate()89 Storage *Allocate() { 90 if (NumFreeListEntries == 0) 91 return new Storage; 92 93 Storage *Result = FreeList[--NumFreeListEntries]; 94 Result->NumDiagArgs = 0; 95 Result->DiagRanges.clear(); 96 Result->FixItHints.clear(); 97 return Result; 98 } 99 100 /// \brief Free the given storage object. Deallocate(Storage * S)101 void Deallocate(Storage *S) { 102 if (S >= Cached && S <= Cached + NumCached) { 103 FreeList[NumFreeListEntries++] = S; 104 return; 105 } 106 107 delete S; 108 } 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. getStorage()126 Storage *getStorage() const { 127 if (DiagStorage) 128 return DiagStorage; 129 130 if (Allocator) 131 DiagStorage = Allocator->Allocate(); 132 else { 133 assert(Allocator != reinterpret_cast<StorageAllocator *>(~uintptr_t(0))); 134 DiagStorage = new Storage; 135 } 136 return DiagStorage; 137 } 138 freeStorage()139 void freeStorage() { 140 if (!DiagStorage) 141 return; 142 143 // The hot path for PartialDiagnostic is when we just used it to wrap an ID 144 // (typically so we have the flexibility of passing a more complex 145 // diagnostic into the callee, but that does not commonly occur). 146 // 147 // Split this out into a slow function for silly compilers (*cough*) which 148 // can't do decent partial inlining. 149 freeStorageSlow(); 150 } 151 freeStorageSlow()152 void freeStorageSlow() { 153 if (Allocator) 154 Allocator->Deallocate(DiagStorage); 155 else if (Allocator != reinterpret_cast<StorageAllocator *>(~uintptr_t(0))) 156 delete DiagStorage; 157 DiagStorage = nullptr; 158 } 159 AddSourceRange(const CharSourceRange & R)160 void AddSourceRange(const CharSourceRange &R) const { 161 if (!DiagStorage) 162 DiagStorage = getStorage(); 163 164 DiagStorage->DiagRanges.push_back(R); 165 } 166 AddFixItHint(const FixItHint & Hint)167 void AddFixItHint(const FixItHint &Hint) const { 168 if (Hint.isNull()) 169 return; 170 171 if (!DiagStorage) 172 DiagStorage = getStorage(); 173 174 DiagStorage->FixItHints.push_back(Hint); 175 } 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. PartialDiagnostic(NullDiagnostic)181 PartialDiagnostic(NullDiagnostic) 182 : DiagID(0), DiagStorage(nullptr), Allocator(nullptr) { } 183 PartialDiagnostic(unsigned DiagID,StorageAllocator & Allocator)184 PartialDiagnostic(unsigned DiagID, StorageAllocator &Allocator) 185 : DiagID(DiagID), DiagStorage(nullptr), Allocator(&Allocator) { } 186 PartialDiagnostic(const PartialDiagnostic & Other)187 PartialDiagnostic(const PartialDiagnostic &Other) 188 : DiagID(Other.DiagID), DiagStorage(nullptr), Allocator(Other.Allocator) 189 { 190 if (Other.DiagStorage) { 191 DiagStorage = getStorage(); 192 *DiagStorage = *Other.DiagStorage; 193 } 194 } 195 PartialDiagnostic(PartialDiagnostic && Other)196 PartialDiagnostic(PartialDiagnostic &&Other) 197 : DiagID(Other.DiagID), DiagStorage(Other.DiagStorage), 198 Allocator(Other.Allocator) { 199 Other.DiagStorage = nullptr; 200 } 201 PartialDiagnostic(const PartialDiagnostic & Other,Storage * DiagStorage)202 PartialDiagnostic(const PartialDiagnostic &Other, Storage *DiagStorage) 203 : DiagID(Other.DiagID), DiagStorage(DiagStorage), 204 Allocator(reinterpret_cast<StorageAllocator *>(~uintptr_t(0))) 205 { 206 if (Other.DiagStorage) 207 *this->DiagStorage = *Other.DiagStorage; 208 } 209 PartialDiagnostic(const Diagnostic & Other,StorageAllocator & Allocator)210 PartialDiagnostic(const Diagnostic &Other, StorageAllocator &Allocator) 211 : DiagID(Other.getID()), DiagStorage(nullptr), Allocator(&Allocator) 212 { 213 // Copy arguments. 214 for (unsigned I = 0, N = Other.getNumArgs(); I != N; ++I) { 215 if (Other.getArgKind(I) == DiagnosticsEngine::ak_std_string) 216 AddString(Other.getArgStdStr(I)); 217 else 218 AddTaggedVal(Other.getRawArg(I), Other.getArgKind(I)); 219 } 220 221 // Copy source ranges. 222 for (unsigned I = 0, N = Other.getNumRanges(); I != N; ++I) 223 AddSourceRange(Other.getRange(I)); 224 225 // Copy fix-its. 226 for (unsigned I = 0, N = Other.getNumFixItHints(); I != N; ++I) 227 AddFixItHint(Other.getFixItHint(I)); 228 } 229 230 PartialDiagnostic &operator=(const PartialDiagnostic &Other) { 231 DiagID = Other.DiagID; 232 if (Other.DiagStorage) { 233 if (!DiagStorage) 234 DiagStorage = getStorage(); 235 236 *DiagStorage = *Other.DiagStorage; 237 } else { 238 freeStorage(); 239 } 240 241 return *this; 242 } 243 244 PartialDiagnostic &operator=(PartialDiagnostic &&Other) { 245 freeStorage(); 246 247 DiagID = Other.DiagID; 248 DiagStorage = Other.DiagStorage; 249 Allocator = Other.Allocator; 250 251 Other.DiagStorage = nullptr; 252 return *this; 253 } 254 ~PartialDiagnostic()255 ~PartialDiagnostic() { 256 freeStorage(); 257 } 258 swap(PartialDiagnostic & PD)259 void swap(PartialDiagnostic &PD) { 260 std::swap(DiagID, PD.DiagID); 261 std::swap(DiagStorage, PD.DiagStorage); 262 std::swap(Allocator, PD.Allocator); 263 } 264 getDiagID()265 unsigned getDiagID() const { return DiagID; } 266 AddTaggedVal(intptr_t V,DiagnosticsEngine::ArgumentKind Kind)267 void AddTaggedVal(intptr_t V, DiagnosticsEngine::ArgumentKind Kind) const { 268 if (!DiagStorage) 269 DiagStorage = getStorage(); 270 271 assert(DiagStorage->NumDiagArgs < Storage::MaxArguments && 272 "Too many arguments to diagnostic!"); 273 DiagStorage->DiagArgumentsKind[DiagStorage->NumDiagArgs] = Kind; 274 DiagStorage->DiagArgumentsVal[DiagStorage->NumDiagArgs++] = V; 275 } 276 AddString(StringRef V)277 void AddString(StringRef V) const { 278 if (!DiagStorage) 279 DiagStorage = getStorage(); 280 281 assert(DiagStorage->NumDiagArgs < Storage::MaxArguments && 282 "Too many arguments to diagnostic!"); 283 DiagStorage->DiagArgumentsKind[DiagStorage->NumDiagArgs] 284 = DiagnosticsEngine::ak_std_string; 285 DiagStorage->DiagArgumentsStr[DiagStorage->NumDiagArgs++] = V; 286 } 287 Emit(const DiagnosticBuilder & DB)288 void Emit(const DiagnosticBuilder &DB) const { 289 if (!DiagStorage) 290 return; 291 292 // Add all arguments. 293 for (unsigned i = 0, e = DiagStorage->NumDiagArgs; i != e; ++i) { 294 if ((DiagnosticsEngine::ArgumentKind)DiagStorage->DiagArgumentsKind[i] 295 == DiagnosticsEngine::ak_std_string) 296 DB.AddString(DiagStorage->DiagArgumentsStr[i]); 297 else 298 DB.AddTaggedVal(DiagStorage->DiagArgumentsVal[i], 299 (DiagnosticsEngine::ArgumentKind)DiagStorage->DiagArgumentsKind[i]); 300 } 301 302 // Add all ranges. 303 for (const CharSourceRange &Range : DiagStorage->DiagRanges) 304 DB.AddSourceRange(Range); 305 306 // Add all fix-its. 307 for (const FixItHint &Fix : DiagStorage->FixItHints) 308 DB.AddFixItHint(Fix); 309 } 310 EmitToString(DiagnosticsEngine & Diags,SmallVectorImpl<char> & Buf)311 void EmitToString(DiagnosticsEngine &Diags, 312 SmallVectorImpl<char> &Buf) const { 313 // FIXME: It should be possible to render a diagnostic to a string without 314 // messing with the state of the diagnostics engine. 315 DiagnosticBuilder DB(Diags.Report(getDiagID())); 316 Emit(DB); 317 DB.FlushCounts(); 318 Diagnostic(&Diags).FormatDiagnostic(Buf); 319 DB.Clear(); 320 Diags.Clear(); 321 } 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 void Reset(unsigned DiagID = 0) { 326 this->DiagID = DiagID; 327 freeStorage(); 328 } 329 hasStorage()330 bool hasStorage() const { return DiagStorage != nullptr; } 331 332 friend const PartialDiagnostic &operator<<(const PartialDiagnostic &PD, 333 unsigned I) { 334 PD.AddTaggedVal(I, DiagnosticsEngine::ak_uint); 335 return PD; 336 } 337 338 friend const PartialDiagnostic &operator<<(const PartialDiagnostic &PD, 339 int I) { 340 PD.AddTaggedVal(I, DiagnosticsEngine::ak_sint); 341 return PD; 342 } 343 344 friend inline const PartialDiagnostic &operator<<(const PartialDiagnostic &PD, 345 const char *S) { 346 PD.AddTaggedVal(reinterpret_cast<intptr_t>(S), 347 DiagnosticsEngine::ak_c_string); 348 return PD; 349 } 350 351 friend inline const PartialDiagnostic &operator<<(const PartialDiagnostic &PD, 352 StringRef S) { 353 354 PD.AddString(S); 355 return PD; 356 } 357 358 friend inline const PartialDiagnostic &operator<<(const PartialDiagnostic &PD, 359 const IdentifierInfo *II) { 360 PD.AddTaggedVal(reinterpret_cast<intptr_t>(II), 361 DiagnosticsEngine::ak_identifierinfo); 362 return PD; 363 } 364 365 // Adds a DeclContext to the diagnostic. The enable_if template magic is here 366 // so that we only match those arguments that are (statically) DeclContexts; 367 // other arguments that derive from DeclContext (e.g., RecordDecls) will not 368 // match. 369 template<typename T> 370 friend inline 371 typename std::enable_if<std::is_same<T, DeclContext>::value, 372 const PartialDiagnostic &>::type 373 operator<<(const PartialDiagnostic &PD, T *DC) { 374 PD.AddTaggedVal(reinterpret_cast<intptr_t>(DC), 375 DiagnosticsEngine::ak_declcontext); 376 return PD; 377 } 378 379 friend inline const PartialDiagnostic &operator<<(const PartialDiagnostic &PD, 380 const SourceRange &R) { 381 PD.AddSourceRange(CharSourceRange::getTokenRange(R)); 382 return PD; 383 } 384 385 friend inline const PartialDiagnostic &operator<<(const PartialDiagnostic &PD, 386 const CharSourceRange &R) { 387 PD.AddSourceRange(R); 388 return PD; 389 } 390 391 friend const PartialDiagnostic &operator<<(const PartialDiagnostic &PD, 392 const FixItHint &Hint) { 393 PD.AddFixItHint(Hint); 394 return PD; 395 } 396 397 }; 398 399 inline const DiagnosticBuilder &operator<<(const DiagnosticBuilder &DB, 400 const PartialDiagnostic &PD) { 401 PD.Emit(DB); 402 return DB; 403 } 404 405 /// \brief A partial diagnostic along with the source location where this 406 /// diagnostic occurs. 407 typedef std::pair<SourceLocation, PartialDiagnostic> PartialDiagnosticAt; 408 409 } // end namespace clang 410 #endif 411