clang API Documentation
00001 //===--- PartialDiagnostic.h - Diagnostic "closures" ------------*- C++ -*-===// 00002 // 00003 // The LLVM Compiler Infrastructure 00004 // 00005 // This file is distributed under the University of Illinois Open Source 00006 // License. See LICENSE.TXT for details. 00007 // 00008 //===----------------------------------------------------------------------===// 00009 // 00010 // This file implements a partial diagnostic that can be emitted anwyhere 00011 // in a DiagnosticBuilder stream. 00012 // 00013 //===----------------------------------------------------------------------===// 00014 00015 #ifndef LLVM_CLANG_PARTIALDIAGNOSTIC_H 00016 #define LLVM_CLANG_PARTIALDIAGNOSTIC_H 00017 00018 #include "clang/Basic/Diagnostic.h" 00019 #include "clang/Basic/SourceLocation.h" 00020 #include "llvm/ADT/STLExtras.h" 00021 #include "llvm/Support/DataTypes.h" 00022 #include <cassert> 00023 00024 namespace clang { 00025 00026 class PartialDiagnostic { 00027 public: 00028 enum { 00029 // The MaxArguments and MaxFixItHints member enum values from 00030 // DiagnosticsEngine are private but DiagnosticsEngine declares 00031 // PartialDiagnostic a friend. These enum values are redeclared 00032 // here so that the nested Storage class below can access them. 00033 MaxArguments = DiagnosticsEngine::MaxArguments 00034 }; 00035 00036 struct Storage { 00037 Storage() : NumDiagArgs(0), NumDiagRanges(0) { } 00038 00039 enum { 00040 /// MaxArguments - The maximum number of arguments we can hold. We 00041 /// currently only support up to 10 arguments (%0-%9). 00042 /// A single diagnostic with more than that almost certainly has to 00043 /// be simplified anyway. 00044 MaxArguments = PartialDiagnostic::MaxArguments 00045 }; 00046 00047 /// NumDiagArgs - This contains the number of entries in Arguments. 00048 unsigned char NumDiagArgs; 00049 00050 /// NumDiagRanges - This is the number of ranges in the DiagRanges array. 00051 unsigned char NumDiagRanges; 00052 00053 /// DiagArgumentsKind - This is an array of ArgumentKind::ArgumentKind enum 00054 /// values, with one for each argument. This specifies whether the argument 00055 /// is in DiagArgumentsStr or in DiagArguments. 00056 unsigned char DiagArgumentsKind[MaxArguments]; 00057 00058 /// DiagArgumentsVal - The values for the various substitution positions. 00059 /// This is used when the argument is not an std::string. The specific value 00060 /// is mangled into an intptr_t and the interpretation depends on exactly 00061 /// what sort of argument kind it is. 00062 intptr_t DiagArgumentsVal[MaxArguments]; 00063 00064 /// \brief The values for the various substitution positions that have 00065 /// string arguments. 00066 std::string DiagArgumentsStr[MaxArguments]; 00067 00068 /// DiagRanges - The list of ranges added to this diagnostic. It currently 00069 /// only support 10 ranges, could easily be extended if needed. 00070 CharSourceRange DiagRanges[10]; 00071 00072 /// FixItHints - If valid, provides a hint with some code 00073 /// to insert, remove, or modify at a particular position. 00074 SmallVector<FixItHint, 6> FixItHints; 00075 }; 00076 00077 /// \brief An allocator for Storage objects, which uses a small cache to 00078 /// objects, used to reduce malloc()/free() traffic for partial diagnostics. 00079 class StorageAllocator { 00080 static const unsigned NumCached = 16; 00081 Storage Cached[NumCached]; 00082 Storage *FreeList[NumCached]; 00083 unsigned NumFreeListEntries; 00084 00085 public: 00086 StorageAllocator(); 00087 ~StorageAllocator(); 00088 00089 /// \brief Allocate new storage. 00090 Storage *Allocate() { 00091 if (NumFreeListEntries == 0) 00092 return new Storage; 00093 00094 Storage *Result = FreeList[--NumFreeListEntries]; 00095 Result->NumDiagArgs = 0; 00096 Result->NumDiagRanges = 0; 00097 Result->FixItHints.clear(); 00098 return Result; 00099 } 00100 00101 /// \brief Free the given storage object. 00102 void Deallocate(Storage *S) { 00103 if (S >= Cached && S <= Cached + NumCached) { 00104 FreeList[NumFreeListEntries++] = S; 00105 return; 00106 } 00107 00108 delete S; 00109 } 00110 }; 00111 00112 private: 00113 // NOTE: Sema assumes that PartialDiagnostic is location-invariant 00114 // in the sense that its bits can be safely memcpy'ed and destructed 00115 // in the new location. 00116 00117 /// DiagID - The diagnostic ID. 00118 mutable unsigned DiagID; 00119 00120 /// DiagStorage - Storage for args and ranges. 00121 mutable Storage *DiagStorage; 00122 00123 /// \brief Allocator used to allocate storage for this diagnostic. 00124 StorageAllocator *Allocator; 00125 00126 /// \brief Retrieve storage for this particular diagnostic. 00127 Storage *getStorage() const { 00128 if (DiagStorage) 00129 return DiagStorage; 00130 00131 if (Allocator) 00132 DiagStorage = Allocator->Allocate(); 00133 else { 00134 assert(Allocator != reinterpret_cast<StorageAllocator *>(~uintptr_t(0))); 00135 DiagStorage = new Storage; 00136 } 00137 return DiagStorage; 00138 } 00139 00140 void freeStorage() { 00141 if (!DiagStorage) 00142 return; 00143 00144 // The hot path for PartialDiagnostic is when we just used it to wrap an ID 00145 // (typically so we have the flexibility of passing a more complex 00146 // diagnostic into the callee, but that does not commonly occur). 00147 // 00148 // Split this out into a slow function for silly compilers (*cough*) which 00149 // can't do decent partial inlining. 00150 freeStorageSlow(); 00151 } 00152 00153 void freeStorageSlow() { 00154 if (Allocator) 00155 Allocator->Deallocate(DiagStorage); 00156 else if (Allocator != reinterpret_cast<StorageAllocator *>(~uintptr_t(0))) 00157 delete DiagStorage; 00158 DiagStorage = 0; 00159 } 00160 00161 void AddSourceRange(const CharSourceRange &R) const { 00162 if (!DiagStorage) 00163 DiagStorage = getStorage(); 00164 00165 assert(DiagStorage->NumDiagRanges < 00166 llvm::array_lengthof(DiagStorage->DiagRanges) && 00167 "Too many arguments to diagnostic!"); 00168 DiagStorage->DiagRanges[DiagStorage->NumDiagRanges++] = R; 00169 } 00170 00171 void AddFixItHint(const FixItHint &Hint) const { 00172 if (Hint.isNull()) 00173 return; 00174 00175 if (!DiagStorage) 00176 DiagStorage = getStorage(); 00177 00178 DiagStorage->FixItHints.push_back(Hint); 00179 } 00180 00181 public: 00182 struct NullDiagnostic {}; 00183 /// \brief Create a null partial diagnostic, which cannot carry a payload, 00184 /// and only exists to be swapped with a real partial diagnostic. 00185 PartialDiagnostic(NullDiagnostic) 00186 : DiagID(0), DiagStorage(0), Allocator(0) { } 00187 00188 PartialDiagnostic(unsigned DiagID, StorageAllocator &Allocator) 00189 : DiagID(DiagID), DiagStorage(0), Allocator(&Allocator) { } 00190 00191 PartialDiagnostic(const PartialDiagnostic &Other) 00192 : DiagID(Other.DiagID), DiagStorage(0), Allocator(Other.Allocator) 00193 { 00194 if (Other.DiagStorage) { 00195 DiagStorage = getStorage(); 00196 *DiagStorage = *Other.DiagStorage; 00197 } 00198 } 00199 00200 PartialDiagnostic(const PartialDiagnostic &Other, Storage *DiagStorage) 00201 : DiagID(Other.DiagID), DiagStorage(DiagStorage), 00202 Allocator(reinterpret_cast<StorageAllocator *>(~uintptr_t(0))) 00203 { 00204 if (Other.DiagStorage) 00205 *this->DiagStorage = *Other.DiagStorage; 00206 } 00207 00208 PartialDiagnostic(const Diagnostic &Other, StorageAllocator &Allocator) 00209 : DiagID(Other.getID()), DiagStorage(0), Allocator(&Allocator) 00210 { 00211 // Copy arguments. 00212 for (unsigned I = 0, N = Other.getNumArgs(); I != N; ++I) { 00213 if (Other.getArgKind(I) == DiagnosticsEngine::ak_std_string) 00214 AddString(Other.getArgStdStr(I)); 00215 else 00216 AddTaggedVal(Other.getRawArg(I), Other.getArgKind(I)); 00217 } 00218 00219 // Copy source ranges. 00220 for (unsigned I = 0, N = Other.getNumRanges(); I != N; ++I) 00221 AddSourceRange(Other.getRange(I)); 00222 00223 // Copy fix-its. 00224 for (unsigned I = 0, N = Other.getNumFixItHints(); I != N; ++I) 00225 AddFixItHint(Other.getFixItHint(I)); 00226 } 00227 00228 PartialDiagnostic &operator=(const PartialDiagnostic &Other) { 00229 DiagID = Other.DiagID; 00230 if (Other.DiagStorage) { 00231 if (!DiagStorage) 00232 DiagStorage = getStorage(); 00233 00234 *DiagStorage = *Other.DiagStorage; 00235 } else { 00236 freeStorage(); 00237 } 00238 00239 return *this; 00240 } 00241 00242 ~PartialDiagnostic() { 00243 freeStorage(); 00244 } 00245 00246 void swap(PartialDiagnostic &PD) { 00247 std::swap(DiagID, PD.DiagID); 00248 std::swap(DiagStorage, PD.DiagStorage); 00249 std::swap(Allocator, PD.Allocator); 00250 } 00251 00252 unsigned getDiagID() const { return DiagID; } 00253 00254 void AddTaggedVal(intptr_t V, DiagnosticsEngine::ArgumentKind Kind) const { 00255 if (!DiagStorage) 00256 DiagStorage = getStorage(); 00257 00258 assert(DiagStorage->NumDiagArgs < Storage::MaxArguments && 00259 "Too many arguments to diagnostic!"); 00260 DiagStorage->DiagArgumentsKind[DiagStorage->NumDiagArgs] = Kind; 00261 DiagStorage->DiagArgumentsVal[DiagStorage->NumDiagArgs++] = V; 00262 } 00263 00264 void AddString(StringRef V) const { 00265 if (!DiagStorage) 00266 DiagStorage = getStorage(); 00267 00268 assert(DiagStorage->NumDiagArgs < Storage::MaxArguments && 00269 "Too many arguments to diagnostic!"); 00270 DiagStorage->DiagArgumentsKind[DiagStorage->NumDiagArgs] 00271 = DiagnosticsEngine::ak_std_string; 00272 DiagStorage->DiagArgumentsStr[DiagStorage->NumDiagArgs++] = V; 00273 } 00274 00275 void Emit(const DiagnosticBuilder &DB) const { 00276 if (!DiagStorage) 00277 return; 00278 00279 // Add all arguments. 00280 for (unsigned i = 0, e = DiagStorage->NumDiagArgs; i != e; ++i) { 00281 if ((DiagnosticsEngine::ArgumentKind)DiagStorage->DiagArgumentsKind[i] 00282 == DiagnosticsEngine::ak_std_string) 00283 DB.AddString(DiagStorage->DiagArgumentsStr[i]); 00284 else 00285 DB.AddTaggedVal(DiagStorage->DiagArgumentsVal[i], 00286 (DiagnosticsEngine::ArgumentKind)DiagStorage->DiagArgumentsKind[i]); 00287 } 00288 00289 // Add all ranges. 00290 for (unsigned i = 0, e = DiagStorage->NumDiagRanges; i != e; ++i) 00291 DB.AddSourceRange(DiagStorage->DiagRanges[i]); 00292 00293 // Add all fix-its. 00294 for (unsigned i = 0, e = DiagStorage->FixItHints.size(); i != e; ++i) 00295 DB.AddFixItHint(DiagStorage->FixItHints[i]); 00296 } 00297 00298 void EmitToString(DiagnosticsEngine &Diags, 00299 llvm::SmallVectorImpl<char> &Buf) const { 00300 // FIXME: It should be possible to render a diagnostic to a string without 00301 // messing with the state of the diagnostics engine. 00302 DiagnosticBuilder DB(Diags.Report(getDiagID())); 00303 Emit(DB); 00304 DB.FlushCounts(); 00305 Diagnostic(&Diags).FormatDiagnostic(Buf); 00306 DB.Clear(); 00307 Diags.Clear(); 00308 } 00309 00310 /// \brief Clear out this partial diagnostic, giving it a new diagnostic ID 00311 /// and removing all of its arguments, ranges, and fix-it hints. 00312 void Reset(unsigned DiagID = 0) { 00313 this->DiagID = DiagID; 00314 freeStorage(); 00315 } 00316 00317 bool hasStorage() const { return DiagStorage != 0; } 00318 00319 friend const PartialDiagnostic &operator<<(const PartialDiagnostic &PD, 00320 unsigned I) { 00321 PD.AddTaggedVal(I, DiagnosticsEngine::ak_uint); 00322 return PD; 00323 } 00324 00325 friend const PartialDiagnostic &operator<<(const PartialDiagnostic &PD, 00326 int I) { 00327 PD.AddTaggedVal(I, DiagnosticsEngine::ak_sint); 00328 return PD; 00329 } 00330 00331 friend inline const PartialDiagnostic &operator<<(const PartialDiagnostic &PD, 00332 const char *S) { 00333 PD.AddTaggedVal(reinterpret_cast<intptr_t>(S), 00334 DiagnosticsEngine::ak_c_string); 00335 return PD; 00336 } 00337 00338 friend inline const PartialDiagnostic &operator<<(const PartialDiagnostic &PD, 00339 StringRef S) { 00340 00341 PD.AddString(S); 00342 return PD; 00343 } 00344 00345 friend inline const PartialDiagnostic &operator<<(const PartialDiagnostic &PD, 00346 const SourceRange &R) { 00347 PD.AddSourceRange(CharSourceRange::getTokenRange(R)); 00348 return PD; 00349 } 00350 00351 friend inline const PartialDiagnostic &operator<<(const PartialDiagnostic &PD, 00352 const CharSourceRange &R) { 00353 PD.AddSourceRange(R); 00354 return PD; 00355 } 00356 00357 friend const PartialDiagnostic &operator<<(const PartialDiagnostic &PD, 00358 const FixItHint &Hint) { 00359 PD.AddFixItHint(Hint); 00360 return PD; 00361 } 00362 00363 }; 00364 00365 inline const DiagnosticBuilder &operator<<(const DiagnosticBuilder &DB, 00366 const PartialDiagnostic &PD) { 00367 PD.Emit(DB); 00368 return DB; 00369 } 00370 00371 /// \brief A partial diagnostic along with the source location where this 00372 /// diagnostic occurs. 00373 typedef std::pair<SourceLocation, PartialDiagnostic> PartialDiagnosticAt; 00374 00375 } // end namespace clang 00376 #endif