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
29namespace clang {
30
31class PartialDiagnostic : public StreamingDiagnostic {
32private:
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;
39public:
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
197inline 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.
205using PartialDiagnosticAt = std::pair<SourceLocation, PartialDiagnostic>;
206
207} // namespace clang
208
209#endif // LLVM_CLANG_BASIC_PARTIALDIAGNOSTIC_H
210

source code of clang/include/clang/Basic/PartialDiagnostic.h