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