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 DeclContext;
32class IdentifierInfo;
33
34class PartialDiagnostic : public StreamingDiagnostic {
35private:
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;
42public:
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.
47 PartialDiagnostic(NullDiagnostic) {}
48
49 PartialDiagnostic(unsigned DiagID, DiagStorageAllocator &Allocator_)
50 : StreamingDiagnostic(Allocator_), DiagID(DiagID) {}
51
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
78 PartialDiagnostic(PartialDiagnostic &&Other) : DiagID(Other.DiagID) {
79 Allocator = Other.Allocator;
80 DiagStorage = Other.DiagStorage;
81 Other.DiagStorage = nullptr;
82 }
83
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
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
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
144 unsigned getDiagID() const { return DiagID; }
145 void setDiagID(unsigned ID) { DiagID = ID; }
146
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
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
188 bool hasStorage() const { return DiagStorage != nullptr; }
189
190 /// Retrieve the string argument at the given index.
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
200inline 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.
208using PartialDiagnosticAt = std::pair<SourceLocation, PartialDiagnostic>;
209
210} // namespace clang
211
212#endif // LLVM_CLANG_BASIC_PARTIALDIAGNOSTIC_H
213