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