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 : public StreamingDiagnostic { 35 private: 36 // NOTE: Sema assumes that PartialDiagnostic is location-invariant 37 // in the sense that its bits can be safely memcpy'ed and destructed 38 // in the new location. 39 40 /// The diagnostic ID. 41 mutable unsigned DiagID = 0; 42 public: 43 struct NullDiagnostic {}; 44 45 /// Create a null partial diagnostic, which cannot carry a payload, 46 /// and only exists to be swapped with a real partial diagnostic. PartialDiagnostic(NullDiagnostic)47 PartialDiagnostic(NullDiagnostic) {} 48 PartialDiagnostic(unsigned DiagID,DiagStorageAllocator & Allocator_)49 PartialDiagnostic(unsigned DiagID, DiagStorageAllocator &Allocator_) 50 : StreamingDiagnostic(Allocator_), DiagID(DiagID) {} 51 PartialDiagnostic(const PartialDiagnostic & Other)52 PartialDiagnostic(const PartialDiagnostic &Other) 53 : StreamingDiagnostic(), DiagID(Other.DiagID) { 54 Allocator = Other.Allocator; 55 if (Other.DiagStorage) { 56 DiagStorage = getStorage(); 57 *DiagStorage = *Other.DiagStorage; 58 } 59 } 60 61 template <typename T> const PartialDiagnostic &operator<<(const T &V) const { 62 const StreamingDiagnostic &DB = *this; 63 DB << V; 64 return *this; 65 } 66 67 // It is necessary to limit this to rvalue reference to avoid calling this 68 // function with a bitfield lvalue argument since non-const reference to 69 // bitfield is not allowed. 70 template <typename T, typename = typename std::enable_if< 71 !std::is_lvalue_reference<T>::value>::type> 72 const PartialDiagnostic &operator<<(T &&V) const { 73 const StreamingDiagnostic &DB = *this; 74 DB << std::move(V); 75 return *this; 76 } 77 PartialDiagnostic(PartialDiagnostic && Other)78 PartialDiagnostic(PartialDiagnostic &&Other) : DiagID(Other.DiagID) { 79 Allocator = Other.Allocator; 80 DiagStorage = Other.DiagStorage; 81 Other.DiagStorage = nullptr; 82 } 83 PartialDiagnostic(const PartialDiagnostic & Other,DiagnosticStorage * DiagStorage_)84 PartialDiagnostic(const PartialDiagnostic &Other, 85 DiagnosticStorage *DiagStorage_) 86 : DiagID(Other.DiagID) { 87 Allocator = reinterpret_cast<DiagStorageAllocator *>(~uintptr_t(0)); 88 DiagStorage = DiagStorage_; 89 if (Other.DiagStorage) 90 *this->DiagStorage = *Other.DiagStorage; 91 } 92 PartialDiagnostic(const Diagnostic & Other,DiagStorageAllocator & Allocator_)93 PartialDiagnostic(const Diagnostic &Other, DiagStorageAllocator &Allocator_) 94 : DiagID(Other.getID()) { 95 Allocator = &Allocator_; 96 // Copy arguments. 97 for (unsigned I = 0, N = Other.getNumArgs(); I != N; ++I) { 98 if (Other.getArgKind(I) == DiagnosticsEngine::ak_std_string) 99 AddString(Other.getArgStdStr(I)); 100 else 101 AddTaggedVal(Other.getRawArg(I), Other.getArgKind(I)); 102 } 103 104 // Copy source ranges. 105 for (unsigned I = 0, N = Other.getNumRanges(); I != N; ++I) 106 AddSourceRange(Other.getRange(I)); 107 108 // Copy fix-its. 109 for (unsigned I = 0, N = Other.getNumFixItHints(); I != N; ++I) 110 AddFixItHint(Other.getFixItHint(I)); 111 } 112 113 PartialDiagnostic &operator=(const PartialDiagnostic &Other) { 114 DiagID = Other.DiagID; 115 if (Other.DiagStorage) { 116 if (!DiagStorage) 117 DiagStorage = getStorage(); 118 119 *DiagStorage = *Other.DiagStorage; 120 } else { 121 freeStorage(); 122 } 123 124 return *this; 125 } 126 127 PartialDiagnostic &operator=(PartialDiagnostic &&Other) { 128 freeStorage(); 129 130 DiagID = Other.DiagID; 131 DiagStorage = Other.DiagStorage; 132 Allocator = Other.Allocator; 133 134 Other.DiagStorage = nullptr; 135 return *this; 136 } 137 swap(PartialDiagnostic & PD)138 void swap(PartialDiagnostic &PD) { 139 std::swap(DiagID, PD.DiagID); 140 std::swap(DiagStorage, PD.DiagStorage); 141 std::swap(Allocator, PD.Allocator); 142 } 143 getDiagID()144 unsigned getDiagID() const { return DiagID; } setDiagID(unsigned ID)145 void setDiagID(unsigned ID) { DiagID = ID; } 146 Emit(const DiagnosticBuilder & DB)147 void Emit(const DiagnosticBuilder &DB) const { 148 if (!DiagStorage) 149 return; 150 151 // Add all arguments. 152 for (unsigned i = 0, e = DiagStorage->NumDiagArgs; i != e; ++i) { 153 if ((DiagnosticsEngine::ArgumentKind)DiagStorage->DiagArgumentsKind[i] 154 == DiagnosticsEngine::ak_std_string) 155 DB.AddString(DiagStorage->DiagArgumentsStr[i]); 156 else 157 DB.AddTaggedVal(DiagStorage->DiagArgumentsVal[i], 158 (DiagnosticsEngine::ArgumentKind)DiagStorage->DiagArgumentsKind[i]); 159 } 160 161 // Add all ranges. 162 for (const CharSourceRange &Range : DiagStorage->DiagRanges) 163 DB.AddSourceRange(Range); 164 165 // Add all fix-its. 166 for (const FixItHint &Fix : DiagStorage->FixItHints) 167 DB.AddFixItHint(Fix); 168 } 169 EmitToString(DiagnosticsEngine & Diags,SmallVectorImpl<char> & Buf)170 void EmitToString(DiagnosticsEngine &Diags, 171 SmallVectorImpl<char> &Buf) const { 172 // FIXME: It should be possible to render a diagnostic to a string without 173 // messing with the state of the diagnostics engine. 174 DiagnosticBuilder DB(Diags.Report(getDiagID())); 175 Emit(DB); 176 Diagnostic(&Diags).FormatDiagnostic(Buf); 177 DB.Clear(); 178 Diags.Clear(); 179 } 180 181 /// Clear out this partial diagnostic, giving it a new diagnostic ID 182 /// and removing all of its arguments, ranges, and fix-it hints. 183 void Reset(unsigned DiagID = 0) { 184 this->DiagID = DiagID; 185 freeStorage(); 186 } 187 hasStorage()188 bool hasStorage() const { return DiagStorage != nullptr; } 189 190 /// Retrieve the string argument at the given index. getStringArg(unsigned I)191 StringRef getStringArg(unsigned I) { 192 assert(DiagStorage && "No diagnostic storage?"); 193 assert(I < DiagStorage->NumDiagArgs && "Not enough diagnostic args"); 194 assert(DiagStorage->DiagArgumentsKind[I] 195 == DiagnosticsEngine::ak_std_string && "Not a string arg"); 196 return DiagStorage->DiagArgumentsStr[I]; 197 } 198 }; 199 200 inline const DiagnosticBuilder &operator<<(const DiagnosticBuilder &DB, 201 const PartialDiagnostic &PD) { 202 PD.Emit(DB); 203 return DB; 204 } 205 206 /// A partial diagnostic along with the source location where this 207 /// diagnostic occurs. 208 using PartialDiagnosticAt = std::pair<SourceLocation, PartialDiagnostic>; 209 210 } // namespace clang 211 212 #endif // LLVM_CLANG_BASIC_PARTIALDIAGNOSTIC_H 213