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