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(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
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
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(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
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
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