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