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. |
44 | PartialDiagnostic(NullDiagnostic) {} |
45 | |
46 | PartialDiagnostic(unsigned DiagID, DiagStorageAllocator &Allocator_) |
47 | : StreamingDiagnostic(Allocator_), DiagID(DiagID) {} |
48 | |
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 | |
75 | PartialDiagnostic(PartialDiagnostic &&Other) : DiagID(Other.DiagID) { |
76 | Allocator = Other.Allocator; |
77 | DiagStorage = Other.DiagStorage; |
78 | Other.DiagStorage = nullptr; |
79 | } |
80 | |
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 | |
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(Idx: I) == DiagnosticsEngine::ak_std_string) |
96 | AddString(V: Other.getArgStdStr(Idx: I)); |
97 | else |
98 | AddTaggedVal(V: Other.getRawArg(Idx: I), Kind: Other.getArgKind(Idx: I)); |
99 | } |
100 | |
101 | // Copy source ranges. |
102 | for (unsigned I = 0, N = Other.getNumRanges(); I != N; ++I) |
103 | AddSourceRange(R: Other.getRange(Idx: I)); |
104 | |
105 | // Copy fix-its. |
106 | for (unsigned I = 0, N = Other.getNumFixItHints(); I != N; ++I) |
107 | AddFixItHint(Hint: Other.getFixItHint(Idx: 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 | |
135 | void swap(PartialDiagnostic &PD) { |
136 | std::swap(a&: DiagID, b&: PD.DiagID); |
137 | std::swap(a&: DiagStorage, b&: PD.DiagStorage); |
138 | std::swap(a&: Allocator, b&: PD.Allocator); |
139 | } |
140 | |
141 | unsigned getDiagID() const { return DiagID; } |
142 | void setDiagID(unsigned ID) { DiagID = ID; } |
143 | |
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(V: DiagStorage->DiagArgumentsStr[i]); |
153 | else |
154 | DB.AddTaggedVal(V: DiagStorage->DiagArgumentsVal[i], |
155 | Kind: (DiagnosticsEngine::ArgumentKind)DiagStorage->DiagArgumentsKind[i]); |
156 | } |
157 | |
158 | // Add all ranges. |
159 | for (const CharSourceRange &Range : DiagStorage->DiagRanges) |
160 | DB.AddSourceRange(R: Range); |
161 | |
162 | // Add all fix-its. |
163 | for (const FixItHint &Fix : DiagStorage->FixItHints) |
164 | DB.AddFixItHint(Hint: Fix); |
165 | } |
166 | |
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(DiagID: getDiagID())); |
172 | Emit(DB); |
173 | Diagnostic(&Diags).FormatDiagnostic(OutStr&: 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 | |
185 | bool hasStorage() const { return DiagStorage != nullptr; } |
186 | |
187 | /// Retrieve the string argument at the given index. |
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 | |