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