1203955Srdivacky//===--- PartialDiagnostic.h - Diagnostic "closures" ------------*- C++ -*-===// 2198092Srdivacky// 3198092Srdivacky// The LLVM Compiler Infrastructure 4198092Srdivacky// 5198092Srdivacky// This file is distributed under the University of Illinois Open Source 6198092Srdivacky// License. See LICENSE.TXT for details. 7198092Srdivacky// 8198092Srdivacky//===----------------------------------------------------------------------===// 9239462Sdim/// 10239462Sdim/// \file 11239462Sdim/// \brief Implements a partial diagnostic that can be emitted anwyhere 12239462Sdim/// in a DiagnosticBuilder stream. 13239462Sdim/// 14198092Srdivacky//===----------------------------------------------------------------------===// 15198092Srdivacky 16198092Srdivacky#ifndef LLVM_CLANG_PARTIALDIAGNOSTIC_H 17198092Srdivacky#define LLVM_CLANG_PARTIALDIAGNOSTIC_H 18198092Srdivacky 19198092Srdivacky#include "clang/Basic/Diagnostic.h" 20198092Srdivacky#include "clang/Basic/SourceLocation.h" 21198092Srdivacky#include "llvm/ADT/STLExtras.h" 22249423Sdim#include "llvm/Support/Compiler.h" 23218893Sdim#include "llvm/Support/DataTypes.h" 24206084Srdivacky#include <cassert> 25198092Srdivacky 26198092Srdivackynamespace clang { 27198092Srdivacky 28198092Srdivackyclass PartialDiagnostic { 29206084Srdivackypublic: 30234353Sdim enum { 31234353Sdim // The MaxArguments and MaxFixItHints member enum values from 32234353Sdim // DiagnosticsEngine are private but DiagnosticsEngine declares 33234353Sdim // PartialDiagnostic a friend. These enum values are redeclared 34234353Sdim // here so that the nested Storage class below can access them. 35234353Sdim MaxArguments = DiagnosticsEngine::MaxArguments 36234353Sdim }; 37234353Sdim 38198092Srdivacky struct Storage { 39234353Sdim Storage() : NumDiagArgs(0), NumDiagRanges(0) { } 40198092Srdivacky 41198092Srdivacky enum { 42239462Sdim /// \brief The maximum number of arguments we can hold. We 43198092Srdivacky /// currently only support up to 10 arguments (%0-%9). 44239462Sdim /// 45198092Srdivacky /// A single diagnostic with more than that almost certainly has to 46198092Srdivacky /// be simplified anyway. 47234353Sdim MaxArguments = PartialDiagnostic::MaxArguments 48198092Srdivacky }; 49234353Sdim 50239462Sdim /// \brief The number of entries in Arguments. 51198092Srdivacky unsigned char NumDiagArgs; 52234353Sdim 53239462Sdim /// \brief This is the number of ranges in the DiagRanges array. 54198092Srdivacky unsigned char NumDiagRanges; 55198092Srdivacky 56239462Sdim /// \brief Specifies for each argument whether it is in DiagArgumentsStr 57239462Sdim /// or in DiagArguments. 58198092Srdivacky unsigned char DiagArgumentsKind[MaxArguments]; 59234353Sdim 60239462Sdim /// \brief The values for the various substitution positions. 61239462Sdim /// 62234353Sdim /// This is used when the argument is not an std::string. The specific value 63221345Sdim /// is mangled into an intptr_t and the interpretation depends on exactly 64198092Srdivacky /// what sort of argument kind it is. 65200583Srdivacky intptr_t DiagArgumentsVal[MaxArguments]; 66234353Sdim 67218893Sdim /// \brief The values for the various substitution positions that have 68218893Sdim /// string arguments. 69218893Sdim std::string DiagArgumentsStr[MaxArguments]; 70234353Sdim 71239462Sdim /// \brief The list of ranges added to this diagnostic. 72239462Sdim /// 73239462Sdim /// It currently only support 10 ranges, could easily be extended if needed. 74210299Sed CharSourceRange DiagRanges[10]; 75234353Sdim 76239462Sdim /// \brief If valid, provides a hint with some code to insert, remove, or 77239462Sdim /// modify at a particular position. 78234353Sdim SmallVector<FixItHint, 6> FixItHints; 79198092Srdivacky }; 80198092Srdivacky 81234353Sdim /// \brief An allocator for Storage objects, which uses a small cache to 82206084Srdivacky /// objects, used to reduce malloc()/free() traffic for partial diagnostics. 83206084Srdivacky class StorageAllocator { 84219077Sdim static const unsigned NumCached = 16; 85206084Srdivacky Storage Cached[NumCached]; 86206084Srdivacky Storage *FreeList[NumCached]; 87206084Srdivacky unsigned NumFreeListEntries; 88234353Sdim 89206084Srdivacky public: 90206084Srdivacky StorageAllocator(); 91206084Srdivacky ~StorageAllocator(); 92234353Sdim 93206084Srdivacky /// \brief Allocate new storage. 94206084Srdivacky Storage *Allocate() { 95206084Srdivacky if (NumFreeListEntries == 0) 96206084Srdivacky return new Storage; 97234353Sdim 98206084Srdivacky Storage *Result = FreeList[--NumFreeListEntries]; 99206084Srdivacky Result->NumDiagArgs = 0; 100206084Srdivacky Result->NumDiagRanges = 0; 101234353Sdim Result->FixItHints.clear(); 102206084Srdivacky return Result; 103206084Srdivacky } 104234353Sdim 105206084Srdivacky /// \brief Free the given storage object. 106206084Srdivacky void Deallocate(Storage *S) { 107206084Srdivacky if (S >= Cached && S <= Cached + NumCached) { 108206084Srdivacky FreeList[NumFreeListEntries++] = S; 109206084Srdivacky return; 110206084Srdivacky } 111234353Sdim 112206084Srdivacky delete S; 113206084Srdivacky } 114206084Srdivacky }; 115234353Sdim 116206084Srdivackyprivate: 117205219Srdivacky // NOTE: Sema assumes that PartialDiagnostic is location-invariant 118205219Srdivacky // in the sense that its bits can be safely memcpy'ed and destructed 119205219Srdivacky // in the new location. 120205219Srdivacky 121239462Sdim /// \brief The diagnostic ID. 122198092Srdivacky mutable unsigned DiagID; 123234353Sdim 124239462Sdim /// \brief Storage for args and ranges. 125198092Srdivacky mutable Storage *DiagStorage; 126198092Srdivacky 127206084Srdivacky /// \brief Allocator used to allocate storage for this diagnostic. 128206084Srdivacky StorageAllocator *Allocator; 129234353Sdim 130206084Srdivacky /// \brief Retrieve storage for this particular diagnostic. 131206084Srdivacky Storage *getStorage() const { 132206084Srdivacky if (DiagStorage) 133206084Srdivacky return DiagStorage; 134234353Sdim 135206084Srdivacky if (Allocator) 136206084Srdivacky DiagStorage = Allocator->Allocate(); 137206084Srdivacky else { 138206084Srdivacky assert(Allocator != reinterpret_cast<StorageAllocator *>(~uintptr_t(0))); 139206084Srdivacky DiagStorage = new Storage; 140206084Srdivacky } 141206084Srdivacky return DiagStorage; 142206084Srdivacky } 143234353Sdim 144234353Sdim void freeStorage() { 145198092Srdivacky if (!DiagStorage) 146206084Srdivacky return; 147234353Sdim 148234353Sdim // The hot path for PartialDiagnostic is when we just used it to wrap an ID 149234353Sdim // (typically so we have the flexibility of passing a more complex 150234353Sdim // diagnostic into the callee, but that does not commonly occur). 151234353Sdim // 152234353Sdim // Split this out into a slow function for silly compilers (*cough*) which 153234353Sdim // can't do decent partial inlining. 154234353Sdim freeStorageSlow(); 155234353Sdim } 156234353Sdim 157234353Sdim void freeStorageSlow() { 158206084Srdivacky if (Allocator) 159206084Srdivacky Allocator->Deallocate(DiagStorage); 160206084Srdivacky else if (Allocator != reinterpret_cast<StorageAllocator *>(~uintptr_t(0))) 161206084Srdivacky delete DiagStorage; 162206084Srdivacky DiagStorage = 0; 163198092Srdivacky } 164234353Sdim 165210299Sed void AddSourceRange(const CharSourceRange &R) const { 166198092Srdivacky if (!DiagStorage) 167206084Srdivacky DiagStorage = getStorage(); 168198092Srdivacky 169234353Sdim assert(DiagStorage->NumDiagRanges < 170198092Srdivacky llvm::array_lengthof(DiagStorage->DiagRanges) && 171198092Srdivacky "Too many arguments to diagnostic!"); 172200583Srdivacky DiagStorage->DiagRanges[DiagStorage->NumDiagRanges++] = R; 173234353Sdim } 174198092Srdivacky 175206084Srdivacky void AddFixItHint(const FixItHint &Hint) const { 176202379Srdivacky if (Hint.isNull()) 177202379Srdivacky return; 178234353Sdim 179202379Srdivacky if (!DiagStorage) 180206084Srdivacky DiagStorage = getStorage(); 181202379Srdivacky 182234353Sdim DiagStorage->FixItHints.push_back(Hint); 183202379Srdivacky } 184234353Sdim 185198092Srdivackypublic: 186239462Sdim struct NullDiagnostic {}; 187239462Sdim /// \brief Create a null partial diagnostic, which cannot carry a payload, 188239462Sdim /// and only exists to be swapped with a real partial diagnostic. 189239462Sdim PartialDiagnostic(NullDiagnostic) 190239462Sdim : DiagID(0), DiagStorage(0), Allocator(0) { } 191239462Sdim 192206084Srdivacky PartialDiagnostic(unsigned DiagID, StorageAllocator &Allocator) 193206084Srdivacky : DiagID(DiagID), DiagStorage(0), Allocator(&Allocator) { } 194234353Sdim 195234353Sdim PartialDiagnostic(const PartialDiagnostic &Other) 196206084Srdivacky : DiagID(Other.DiagID), DiagStorage(0), Allocator(Other.Allocator) 197200583Srdivacky { 198206084Srdivacky if (Other.DiagStorage) { 199206084Srdivacky DiagStorage = getStorage(); 200206084Srdivacky *DiagStorage = *Other.DiagStorage; 201206084Srdivacky } 202206084Srdivacky } 203206084Srdivacky 204249423Sdim#if LLVM_HAS_RVALUE_REFERENCES 205249423Sdim PartialDiagnostic(PartialDiagnostic &&Other) 206249423Sdim : DiagID(Other.DiagID), DiagStorage(Other.DiagStorage), 207249423Sdim Allocator(Other.Allocator) { 208249423Sdim Other.DiagStorage = 0; 209249423Sdim } 210249423Sdim#endif 211249423Sdim 212234353Sdim PartialDiagnostic(const PartialDiagnostic &Other, Storage *DiagStorage) 213234353Sdim : DiagID(Other.DiagID), DiagStorage(DiagStorage), 214206084Srdivacky Allocator(reinterpret_cast<StorageAllocator *>(~uintptr_t(0))) 215206084Srdivacky { 216200583Srdivacky if (Other.DiagStorage) 217206084Srdivacky *this->DiagStorage = *Other.DiagStorage; 218198092Srdivacky } 219234353Sdim 220226633Sdim PartialDiagnostic(const Diagnostic &Other, StorageAllocator &Allocator) 221218893Sdim : DiagID(Other.getID()), DiagStorage(0), Allocator(&Allocator) 222218893Sdim { 223218893Sdim // Copy arguments. 224218893Sdim for (unsigned I = 0, N = Other.getNumArgs(); I != N; ++I) { 225226633Sdim if (Other.getArgKind(I) == DiagnosticsEngine::ak_std_string) 226218893Sdim AddString(Other.getArgStdStr(I)); 227218893Sdim else 228218893Sdim AddTaggedVal(Other.getRawArg(I), Other.getArgKind(I)); 229218893Sdim } 230234353Sdim 231218893Sdim // Copy source ranges. 232218893Sdim for (unsigned I = 0, N = Other.getNumRanges(); I != N; ++I) 233218893Sdim AddSourceRange(Other.getRange(I)); 234234353Sdim 235218893Sdim // Copy fix-its. 236218893Sdim for (unsigned I = 0, N = Other.getNumFixItHints(); I != N; ++I) 237218893Sdim AddFixItHint(Other.getFixItHint(I)); 238218893Sdim } 239234353Sdim 240200583Srdivacky PartialDiagnostic &operator=(const PartialDiagnostic &Other) { 241200583Srdivacky DiagID = Other.DiagID; 242200583Srdivacky if (Other.DiagStorage) { 243206084Srdivacky if (!DiagStorage) 244206084Srdivacky DiagStorage = getStorage(); 245234353Sdim 246206084Srdivacky *DiagStorage = *Other.DiagStorage; 247200583Srdivacky } else { 248206084Srdivacky freeStorage(); 249200583Srdivacky } 250200583Srdivacky 251200583Srdivacky return *this; 252200583Srdivacky } 253200583Srdivacky 254249423Sdim#if LLVM_HAS_RVALUE_REFERENCES 255249423Sdim PartialDiagnostic &operator=(PartialDiagnostic &&Other) { 256249423Sdim freeStorage(); 257249423Sdim 258249423Sdim DiagID = Other.DiagID; 259249423Sdim DiagStorage = Other.DiagStorage; 260249423Sdim Allocator = Other.Allocator; 261249423Sdim 262249423Sdim Other.DiagStorage = 0; 263249423Sdim return *this; 264249423Sdim } 265249423Sdim#endif 266249423Sdim 267198092Srdivacky ~PartialDiagnostic() { 268206084Srdivacky freeStorage(); 269198092Srdivacky } 270198092Srdivacky 271239462Sdim void swap(PartialDiagnostic &PD) { 272239462Sdim std::swap(DiagID, PD.DiagID); 273239462Sdim std::swap(DiagStorage, PD.DiagStorage); 274239462Sdim std::swap(Allocator, PD.Allocator); 275239462Sdim } 276239462Sdim 277198092Srdivacky unsigned getDiagID() const { return DiagID; } 278198092Srdivacky 279226633Sdim void AddTaggedVal(intptr_t V, DiagnosticsEngine::ArgumentKind Kind) const { 280206084Srdivacky if (!DiagStorage) 281206084Srdivacky DiagStorage = getStorage(); 282206084Srdivacky 283206084Srdivacky assert(DiagStorage->NumDiagArgs < Storage::MaxArguments && 284206084Srdivacky "Too many arguments to diagnostic!"); 285206084Srdivacky DiagStorage->DiagArgumentsKind[DiagStorage->NumDiagArgs] = Kind; 286206084Srdivacky DiagStorage->DiagArgumentsVal[DiagStorage->NumDiagArgs++] = V; 287206084Srdivacky } 288206084Srdivacky 289226633Sdim void AddString(StringRef V) const { 290218893Sdim if (!DiagStorage) 291218893Sdim DiagStorage = getStorage(); 292234353Sdim 293218893Sdim assert(DiagStorage->NumDiagArgs < Storage::MaxArguments && 294218893Sdim "Too many arguments to diagnostic!"); 295218893Sdim DiagStorage->DiagArgumentsKind[DiagStorage->NumDiagArgs] 296226633Sdim = DiagnosticsEngine::ak_std_string; 297218893Sdim DiagStorage->DiagArgumentsStr[DiagStorage->NumDiagArgs++] = V; 298218893Sdim } 299218893Sdim 300198092Srdivacky void Emit(const DiagnosticBuilder &DB) const { 301198092Srdivacky if (!DiagStorage) 302198092Srdivacky return; 303234353Sdim 304198092Srdivacky // Add all arguments. 305198092Srdivacky for (unsigned i = 0, e = DiagStorage->NumDiagArgs; i != e; ++i) { 306226633Sdim if ((DiagnosticsEngine::ArgumentKind)DiagStorage->DiagArgumentsKind[i] 307226633Sdim == DiagnosticsEngine::ak_std_string) 308218893Sdim DB.AddString(DiagStorage->DiagArgumentsStr[i]); 309218893Sdim else 310218893Sdim DB.AddTaggedVal(DiagStorage->DiagArgumentsVal[i], 311226633Sdim (DiagnosticsEngine::ArgumentKind)DiagStorage->DiagArgumentsKind[i]); 312198092Srdivacky } 313234353Sdim 314198092Srdivacky // Add all ranges. 315198092Srdivacky for (unsigned i = 0, e = DiagStorage->NumDiagRanges; i != e; ++i) 316200583Srdivacky DB.AddSourceRange(DiagStorage->DiagRanges[i]); 317234353Sdim 318218893Sdim // Add all fix-its. 319234353Sdim for (unsigned i = 0, e = DiagStorage->FixItHints.size(); i != e; ++i) 320206084Srdivacky DB.AddFixItHint(DiagStorage->FixItHints[i]); 321198092Srdivacky } 322234353Sdim 323239462Sdim void EmitToString(DiagnosticsEngine &Diags, 324249423Sdim SmallVectorImpl<char> &Buf) const { 325239462Sdim // FIXME: It should be possible to render a diagnostic to a string without 326239462Sdim // messing with the state of the diagnostics engine. 327239462Sdim DiagnosticBuilder DB(Diags.Report(getDiagID())); 328239462Sdim Emit(DB); 329239462Sdim DB.FlushCounts(); 330239462Sdim Diagnostic(&Diags).FormatDiagnostic(Buf); 331239462Sdim DB.Clear(); 332239462Sdim Diags.Clear(); 333239462Sdim } 334239462Sdim 335206084Srdivacky /// \brief Clear out this partial diagnostic, giving it a new diagnostic ID 336206084Srdivacky /// and removing all of its arguments, ranges, and fix-it hints. 337206084Srdivacky void Reset(unsigned DiagID = 0) { 338206084Srdivacky this->DiagID = DiagID; 339206084Srdivacky freeStorage(); 340198092Srdivacky } 341234353Sdim 342206084Srdivacky bool hasStorage() const { return DiagStorage != 0; } 343234353Sdim 344198092Srdivacky friend const PartialDiagnostic &operator<<(const PartialDiagnostic &PD, 345198092Srdivacky unsigned I) { 346226633Sdim PD.AddTaggedVal(I, DiagnosticsEngine::ak_uint); 347198092Srdivacky return PD; 348198092Srdivacky } 349198893Srdivacky 350198893Srdivacky friend const PartialDiagnostic &operator<<(const PartialDiagnostic &PD, 351198893Srdivacky int I) { 352226633Sdim PD.AddTaggedVal(I, DiagnosticsEngine::ak_sint); 353198893Srdivacky return PD; 354198893Srdivacky } 355198893Srdivacky 356198092Srdivacky friend inline const PartialDiagnostic &operator<<(const PartialDiagnostic &PD, 357198893Srdivacky const char *S) { 358226633Sdim PD.AddTaggedVal(reinterpret_cast<intptr_t>(S), 359226633Sdim DiagnosticsEngine::ak_c_string); 360198893Srdivacky return PD; 361198893Srdivacky } 362198893Srdivacky 363198893Srdivacky friend inline const PartialDiagnostic &operator<<(const PartialDiagnostic &PD, 364226633Sdim StringRef S) { 365234353Sdim 366218893Sdim PD.AddString(S); 367218893Sdim return PD; 368218893Sdim } 369234353Sdim 370218893Sdim friend inline const PartialDiagnostic &operator<<(const PartialDiagnostic &PD, 371198092Srdivacky const SourceRange &R) { 372210299Sed PD.AddSourceRange(CharSourceRange::getTokenRange(R)); 373210299Sed return PD; 374210299Sed } 375210299Sed 376210299Sed friend inline const PartialDiagnostic &operator<<(const PartialDiagnostic &PD, 377210299Sed const CharSourceRange &R) { 378198092Srdivacky PD.AddSourceRange(R); 379198092Srdivacky return PD; 380198092Srdivacky } 381234353Sdim 382198092Srdivacky friend const PartialDiagnostic &operator<<(const PartialDiagnostic &PD, 383206084Srdivacky const FixItHint &Hint) { 384206084Srdivacky PD.AddFixItHint(Hint); 385202379Srdivacky return PD; 386202379Srdivacky } 387234353Sdim 388198092Srdivacky}; 389198092Srdivacky 390198092Srdivackyinline const DiagnosticBuilder &operator<<(const DiagnosticBuilder &DB, 391198092Srdivacky const PartialDiagnostic &PD) { 392198092Srdivacky PD.Emit(DB); 393198092Srdivacky return DB; 394198092Srdivacky} 395234353Sdim 396218893Sdim/// \brief A partial diagnostic along with the source location where this 397218893Sdim/// diagnostic occurs. 398218893Sdimtypedef std::pair<SourceLocation, PartialDiagnostic> PartialDiagnosticAt; 399198092Srdivacky 400198092Srdivacky} // end namespace clang 401206084Srdivacky#endif 402