1 //===- PartialDiagnostic.h - Diagnostic "closures" --------------*- C++ -*-===// 2 // 3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4 // See https://llvm.org/LICENSE.txt for license information. 5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6 // 7 //===----------------------------------------------------------------------===// 8 // 9 /// \file 10 /// Implements a partial diagnostic that can be emitted anwyhere 11 /// in a DiagnosticBuilder stream. 12 // 13 //===----------------------------------------------------------------------===// 14 15 #ifndef LLVM_CLANG_BASIC_PARTIALDIAGNOSTIC_H 16 #define LLVM_CLANG_BASIC_PARTIALDIAGNOSTIC_H 17 18 #include "clang/Basic/Diagnostic.h" 19 #include "clang/Basic/LLVM.h" 20 #include "clang/Basic/SourceLocation.h" 21 #include "llvm/ADT/SmallVector.h" 22 #include "llvm/ADT/StringRef.h" 23 #include <cassert> 24 #include <cstdint> 25 #include <string> 26 #include <type_traits> 27 #include <utility> 28 29 namespace clang { 30 31 class DeclContext; 32 class IdentifierInfo; 33 34 class PartialDiagnostic { 35 public: 36 enum { 37 // The MaxArguments and MaxFixItHints member enum values from 38 // DiagnosticsEngine are private but DiagnosticsEngine declares 39 // PartialDiagnostic a friend. These enum values are redeclared 40 // here so that the nested Storage class below can access them. 41 MaxArguments = DiagnosticsEngine::MaxArguments 42 }; 43 44 struct Storage { 45 enum { 46 /// The maximum number of arguments we can hold. We 47 /// currently only support up to 10 arguments (%0-%9). 48 /// 49 /// A single diagnostic with more than that almost certainly has to 50 /// be simplified anyway. 51 MaxArguments = PartialDiagnostic::MaxArguments 52 }; 53 54 /// The number of entries in Arguments. 55 unsigned char NumDiagArgs = 0; 56 57 /// Specifies for each argument whether it is in DiagArgumentsStr 58 /// or in DiagArguments. 59 unsigned char DiagArgumentsKind[MaxArguments]; 60 61 /// The values for the various substitution positions. 62 /// 63 /// This is used when the argument is not an std::string. The specific value 64 /// is mangled into an intptr_t and the interpretation depends on exactly 65 /// what sort of argument kind it is. 66 intptr_t DiagArgumentsVal[MaxArguments]; 67 68 /// The values for the various substitution positions that have 69 /// string arguments. 70 std::string DiagArgumentsStr[MaxArguments]; 71 72 /// The list of ranges added to this diagnostic. 73 SmallVector<CharSourceRange, 8> DiagRanges; 74 75 /// 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 Storage() = default; 80 }; 81 82 /// An allocator for Storage objects, which uses a small cache to 83 /// objects, used to reduce malloc()/free() traffic for partial diagnostics. 84 class StorageAllocator { 85 static const unsigned NumCached = 16; 86 Storage Cached[NumCached]; 87 Storage *FreeList[NumCached]; 88 unsigned NumFreeListEntries; 89 90 public: 91 StorageAllocator(); 92 ~StorageAllocator(); 93 94 /// Allocate new storage. 95 Storage *Allocate() { 96 if (NumFreeListEntries == 0) 97 return new Storage; 98 99 Storage *Result = FreeList[--NumFreeListEntries]; 100 Result->NumDiagArgs = 0; 101 Result->DiagRanges.clear(); 102 Result->FixItHints.clear(); 103 return Result; 104 } 105 106 /// Free the given storage object. 107 void Deallocate(Storage *S) { 108 if (S >= Cached && S <= Cached + NumCached) { 109 FreeList[NumFreeListEntries++] = S; 110 return; 111 } 112 113 delete S; 114 } 115 }; 116 117 private: 118 // NOTE: Sema assumes that PartialDiagnostic is location-invariant 119 // in the sense that its bits can be safely memcpy'ed and destructed 120 // in the new location. 121 122 /// The diagnostic ID. 123 mutable unsigned DiagID = 0; 124 125 /// Storage for args and ranges. 126 mutable Storage *DiagStorage = nullptr; 127 128 /// Allocator used to allocate storage for this diagnostic. 129 StorageAllocator *Allocator = nullptr; 130 131 /// Retrieve storage for this particular diagnostic. 132 Storage *getStorage() const { 133 if (DiagStorage) 134 return DiagStorage; 135 136 if (Allocator) 137 DiagStorage = Allocator->Allocate(); 138 else { 139 assert(Allocator != reinterpret_cast<StorageAllocator *>(~uintptr_t(0))); 140 DiagStorage = new Storage; 141 } 142 return DiagStorage; 143 } 144 145 void freeStorage() { 146 if (!DiagStorage) 147 return; 148 149 // The hot path for PartialDiagnostic is when we just used it to wrap an ID 150 // (typically so we have the flexibility of passing a more complex 151 // diagnostic into the callee, but that does not commonly occur). 152 // 153 // Split this out into a slow function for silly compilers (*cough*) which 154 // can't do decent partial inlining. 155 freeStorageSlow(); 156 } 157 158 void freeStorageSlow() { 159 if (Allocator) 160 Allocator->Deallocate(DiagStorage); 161 else if (Allocator != reinterpret_cast<StorageAllocator *>(~uintptr_t(0))) 162 delete DiagStorage; 163 DiagStorage = nullptr; 164 } 165 166 void AddSourceRange(const CharSourceRange &R) const { 167 if (!DiagStorage) 168 DiagStorage = getStorage(); 169 170 DiagStorage->DiagRanges.push_back(R); 171 } 172 173 void AddFixItHint(const FixItHint &Hint) const { 174 if (Hint.isNull()) 175 return; 176 177 if (!DiagStorage) 178 DiagStorage = getStorage(); 179 180 DiagStorage->FixItHints.push_back(Hint); 181 } 182 183 public: 184 struct NullDiagnostic {}; 185 186 /// 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 190 PartialDiagnostic(unsigned DiagID, StorageAllocator &Allocator) 191 : DiagID(DiagID), Allocator(&Allocator) {} 192 193 PartialDiagnostic(const PartialDiagnostic &Other) 194 : DiagID(Other.DiagID), Allocator(Other.Allocator) { 195 if (Other.DiagStorage) { 196 DiagStorage = getStorage(); 197 *DiagStorage = *Other.DiagStorage; 198 } 199 } 200 201 PartialDiagnostic(PartialDiagnostic &&Other) 202 : DiagID(Other.DiagID), DiagStorage(Other.DiagStorage), 203 Allocator(Other.Allocator) { 204 Other.DiagStorage = nullptr; 205 } 206 207 PartialDiagnostic(const PartialDiagnostic &Other, Storage *DiagStorage) 208 : DiagID(Other.DiagID), DiagStorage(DiagStorage), 209 Allocator(reinterpret_cast<StorageAllocator *>(~uintptr_t(0))) { 210 if (Other.DiagStorage) 211 *this->DiagStorage = *Other.DiagStorage; 212 } 213 214 PartialDiagnostic(const Diagnostic &Other, StorageAllocator &Allocator) 215 : DiagID(Other.getID()), Allocator(&Allocator) { 216 // Copy arguments. 217 for (unsigned I = 0, N = Other.getNumArgs(); I != N; ++I) { 218 if (Other.getArgKind(I) == DiagnosticsEngine::ak_std_string) 219 AddString(Other.getArgStdStr(I)); 220 else 221 AddTaggedVal(Other.getRawArg(I), Other.getArgKind(I)); 222 } 223 224 // Copy source ranges. 225 for (unsigned I = 0, N = Other.getNumRanges(); I != N; ++I) 226 AddSourceRange(Other.getRange(I)); 227 228 // Copy fix-its. 229 for (unsigned I = 0, N = Other.getNumFixItHints(); I != N; ++I) 230 AddFixItHint(Other.getFixItHint(I)); 231 } 232 233 PartialDiagnostic &operator=(const PartialDiagnostic &Other) { 234 DiagID = Other.DiagID; 235 if (Other.DiagStorage) { 236 if (!DiagStorage) 237 DiagStorage = getStorage(); 238 239 *DiagStorage = *Other.DiagStorage; 240 } else { 241 freeStorage(); 242 } 243 244 return *this; 245 } 246 247 PartialDiagnostic &operator=(PartialDiagnostic &&Other) { 248 freeStorage(); 249 250 DiagID = Other.DiagID; 251 DiagStorage = Other.DiagStorage; 252 Allocator = Other.Allocator; 253 254 Other.DiagStorage = nullptr; 255 return *this; 256 } 257 258 ~PartialDiagnostic() { 259 freeStorage(); 260 } 261 262 void swap(PartialDiagnostic &PD) { 263 std::swap(DiagID, PD.DiagID); 264 std::swap(DiagStorage, PD.DiagStorage); 265 std::swap(Allocator, PD.Allocator); 266 } 267 268 unsigned getDiagID() const { return DiagID; } 269 270 void AddTaggedVal(intptr_t V, DiagnosticsEngine::ArgumentKind Kind) const { 271 if (!DiagStorage) 272 DiagStorage = getStorage(); 273 274 assert(DiagStorage->NumDiagArgs < Storage::MaxArguments && 275 "Too many arguments to diagnostic!"); 276 DiagStorage->DiagArgumentsKind[DiagStorage->NumDiagArgs] = Kind; 277 DiagStorage->DiagArgumentsVal[DiagStorage->NumDiagArgs++] = V; 278 } 279 280 void AddString(StringRef V) const { 281 if (!DiagStorage) 282 DiagStorage = getStorage(); 283 284 assert(DiagStorage->NumDiagArgs < Storage::MaxArguments && 285 "Too many arguments to diagnostic!"); 286 DiagStorage->DiagArgumentsKind[DiagStorage->NumDiagArgs] 287 = DiagnosticsEngine::ak_std_string; 288 DiagStorage->DiagArgumentsStr[DiagStorage->NumDiagArgs++] = V; 289 } 290 291 void Emit(const DiagnosticBuilder &DB) const { 292 if (!DiagStorage) 293 return; 294 295 // Add all arguments. 296 for (unsigned i = 0, e = DiagStorage->NumDiagArgs; i != e; ++i) { 297 if ((DiagnosticsEngine::ArgumentKind)DiagStorage->DiagArgumentsKind[i] 298 == DiagnosticsEngine::ak_std_string) 299 DB.AddString(DiagStorage->DiagArgumentsStr[i]); 300 else 301 DB.AddTaggedVal(DiagStorage->DiagArgumentsVal[i], 302 (DiagnosticsEngine::ArgumentKind)DiagStorage->DiagArgumentsKind[i]); 303 } 304 305 // Add all ranges. 306 for (const CharSourceRange &Range : DiagStorage->DiagRanges) 307 DB.AddSourceRange(Range); 308 309 // Add all fix-its. 310 for (const FixItHint &Fix : DiagStorage->FixItHints) 311 DB.AddFixItHint(Fix); 312 } 313 314 void EmitToString(DiagnosticsEngine &Diags, 315 SmallVectorImpl<char> &Buf) const { 316 // FIXME: It should be possible to render a diagnostic to a string without 317 // messing with the state of the diagnostics engine. 318 DiagnosticBuilder DB(Diags.Report(getDiagID())); 319 Emit(DB); 320 DB.FlushCounts(); 321 Diagnostic(&Diags).FormatDiagnostic(Buf); 322 DB.Clear(); 323 Diags.Clear(); 324 } 325 326 /// Clear out this partial diagnostic, giving it a new diagnostic ID 327 /// and removing all of its arguments, ranges, and fix-it hints. 328 void Reset(unsigned DiagID = 0) { 329 this->DiagID = DiagID; 330 freeStorage(); 331 } 332 333 bool hasStorage() const { return DiagStorage != nullptr; } 334 335 /// Retrieve the string argument at the given index. 336 StringRef getStringArg(unsigned I) { 337 assert(DiagStorage && "No diagnostic storage?"); 338 assert(I < DiagStorage->NumDiagArgs && "Not enough diagnostic args"); 339 assert(DiagStorage->DiagArgumentsKind[I] 340 == DiagnosticsEngine::ak_std_string && "Not a string arg"); 341 return DiagStorage->DiagArgumentsStr[I]; 342 } 343 344 friend const PartialDiagnostic &operator<<(const PartialDiagnostic &PD, 345 unsigned I) { 346 PD.AddTaggedVal(I, DiagnosticsEngine::ak_uint); 347 return PD; 348 } 349 350 friend const PartialDiagnostic &operator<<(const PartialDiagnostic &PD, 351 int I) { 352 PD.AddTaggedVal(I, DiagnosticsEngine::ak_sint); 353 return PD; 354 } 355 356 friend inline const PartialDiagnostic &operator<<(const PartialDiagnostic &PD, 357 const char *S) { 358 PD.AddTaggedVal(reinterpret_cast<intptr_t>(S), 359 DiagnosticsEngine::ak_c_string); 360 return PD; 361 } 362 363 friend inline const PartialDiagnostic &operator<<(const PartialDiagnostic &PD, 364 StringRef S) { 365 366 PD.AddString(S); 367 return PD; 368 } 369 370 friend inline const PartialDiagnostic &operator<<(const PartialDiagnostic &PD, 371 const IdentifierInfo *II) { 372 PD.AddTaggedVal(reinterpret_cast<intptr_t>(II), 373 DiagnosticsEngine::ak_identifierinfo); 374 return PD; 375 } 376 377 // Adds a DeclContext to the diagnostic. The enable_if template magic is here 378 // so that we only match those arguments that are (statically) DeclContexts; 379 // other arguments that derive from DeclContext (e.g., RecordDecls) will not 380 // match. 381 template<typename T> 382 friend inline 383 typename std::enable_if<std::is_same<T, DeclContext>::value, 384 const PartialDiagnostic &>::type 385 operator<<(const PartialDiagnostic &PD, T *DC) { 386 PD.AddTaggedVal(reinterpret_cast<intptr_t>(DC), 387 DiagnosticsEngine::ak_declcontext); 388 return PD; 389 } 390 391 friend inline const PartialDiagnostic &operator<<(const PartialDiagnostic &PD, 392 SourceRange R) { 393 PD.AddSourceRange(CharSourceRange::getTokenRange(R)); 394 return PD; 395 } 396 397 friend inline const PartialDiagnostic &operator<<(const PartialDiagnostic &PD, 398 const CharSourceRange &R) { 399 PD.AddSourceRange(R); 400 return PD; 401 } 402 403 friend const PartialDiagnostic &operator<<(const PartialDiagnostic &PD, 404 const FixItHint &Hint) { 405 PD.AddFixItHint(Hint); 406 return PD; 407 } 408 }; 409 410 inline const DiagnosticBuilder &operator<<(const DiagnosticBuilder &DB, 411 const PartialDiagnostic &PD) { 412 PD.Emit(DB); 413 return DB; 414 } 415 416 /// A partial diagnostic along with the source location where this 417 /// diagnostic occurs. 418 using PartialDiagnosticAt = std::pair<SourceLocation, PartialDiagnostic>; 419 420 } // namespace clang 421 422 #endif // LLVM_CLANG_BASIC_PARTIALDIAGNOSTIC_H 423