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