PartialDiagnostic.h 6.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220
  1. #pragma once
  2. #ifdef __GNUC__
  3. #pragma GCC diagnostic push
  4. #pragma GCC diagnostic ignored "-Wunused-parameter"
  5. #endif
  6. //===- PartialDiagnostic.h - Diagnostic "closures" --------------*- C++ -*-===//
  7. //
  8. // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
  9. // See https://llvm.org/LICENSE.txt for license information.
  10. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
  11. //
  12. //===----------------------------------------------------------------------===//
  13. //
  14. /// \file
  15. /// Implements a partial diagnostic that can be emitted anwyhere
  16. /// in a DiagnosticBuilder stream.
  17. //
  18. //===----------------------------------------------------------------------===//
  19. #ifndef LLVM_CLANG_BASIC_PARTIALDIAGNOSTIC_H
  20. #define LLVM_CLANG_BASIC_PARTIALDIAGNOSTIC_H
  21. #include "clang/Basic/Diagnostic.h"
  22. #include "clang/Basic/LLVM.h"
  23. #include "clang/Basic/SourceLocation.h"
  24. #include "llvm/ADT/SmallVector.h"
  25. #include "llvm/ADT/StringRef.h"
  26. #include <cassert>
  27. #include <cstdint>
  28. #include <string>
  29. #include <type_traits>
  30. #include <utility>
  31. namespace clang {
  32. class PartialDiagnostic : public StreamingDiagnostic {
  33. private:
  34. // NOTE: Sema assumes that PartialDiagnostic is location-invariant
  35. // in the sense that its bits can be safely memcpy'ed and destructed
  36. // in the new location.
  37. /// The diagnostic ID.
  38. mutable unsigned DiagID = 0;
  39. public:
  40. struct NullDiagnostic {};
  41. /// Create a null partial diagnostic, which cannot carry a payload,
  42. /// and only exists to be swapped with a real partial diagnostic.
  43. PartialDiagnostic(NullDiagnostic) {}
  44. PartialDiagnostic(unsigned DiagID, DiagStorageAllocator &Allocator_)
  45. : StreamingDiagnostic(Allocator_), DiagID(DiagID) {}
  46. PartialDiagnostic(const PartialDiagnostic &Other)
  47. : StreamingDiagnostic(), DiagID(Other.DiagID) {
  48. Allocator = Other.Allocator;
  49. if (Other.DiagStorage) {
  50. DiagStorage = getStorage();
  51. *DiagStorage = *Other.DiagStorage;
  52. }
  53. }
  54. template <typename T> const PartialDiagnostic &operator<<(const T &V) const {
  55. const StreamingDiagnostic &DB = *this;
  56. DB << V;
  57. return *this;
  58. }
  59. // It is necessary to limit this to rvalue reference to avoid calling this
  60. // function with a bitfield lvalue argument since non-const reference to
  61. // bitfield is not allowed.
  62. template <typename T, typename = typename std::enable_if<
  63. !std::is_lvalue_reference<T>::value>::type>
  64. const PartialDiagnostic &operator<<(T &&V) const {
  65. const StreamingDiagnostic &DB = *this;
  66. DB << std::move(V);
  67. return *this;
  68. }
  69. PartialDiagnostic(PartialDiagnostic &&Other) : DiagID(Other.DiagID) {
  70. Allocator = Other.Allocator;
  71. DiagStorage = Other.DiagStorage;
  72. Other.DiagStorage = nullptr;
  73. }
  74. PartialDiagnostic(const PartialDiagnostic &Other,
  75. DiagnosticStorage *DiagStorage_)
  76. : DiagID(Other.DiagID) {
  77. Allocator = reinterpret_cast<DiagStorageAllocator *>(~uintptr_t(0));
  78. DiagStorage = DiagStorage_;
  79. if (Other.DiagStorage)
  80. *this->DiagStorage = *Other.DiagStorage;
  81. }
  82. PartialDiagnostic(const Diagnostic &Other, DiagStorageAllocator &Allocator_)
  83. : DiagID(Other.getID()) {
  84. Allocator = &Allocator_;
  85. // Copy arguments.
  86. for (unsigned I = 0, N = Other.getNumArgs(); I != N; ++I) {
  87. if (Other.getArgKind(I) == DiagnosticsEngine::ak_std_string)
  88. AddString(Other.getArgStdStr(I));
  89. else
  90. AddTaggedVal(Other.getRawArg(I), Other.getArgKind(I));
  91. }
  92. // Copy source ranges.
  93. for (unsigned I = 0, N = Other.getNumRanges(); I != N; ++I)
  94. AddSourceRange(Other.getRange(I));
  95. // Copy fix-its.
  96. for (unsigned I = 0, N = Other.getNumFixItHints(); I != N; ++I)
  97. AddFixItHint(Other.getFixItHint(I));
  98. }
  99. PartialDiagnostic &operator=(const PartialDiagnostic &Other) {
  100. DiagID = Other.DiagID;
  101. if (Other.DiagStorage) {
  102. if (!DiagStorage)
  103. DiagStorage = getStorage();
  104. *DiagStorage = *Other.DiagStorage;
  105. } else {
  106. freeStorage();
  107. }
  108. return *this;
  109. }
  110. PartialDiagnostic &operator=(PartialDiagnostic &&Other) {
  111. freeStorage();
  112. DiagID = Other.DiagID;
  113. DiagStorage = Other.DiagStorage;
  114. Allocator = Other.Allocator;
  115. Other.DiagStorage = nullptr;
  116. return *this;
  117. }
  118. void swap(PartialDiagnostic &PD) {
  119. std::swap(DiagID, PD.DiagID);
  120. std::swap(DiagStorage, PD.DiagStorage);
  121. std::swap(Allocator, PD.Allocator);
  122. }
  123. unsigned getDiagID() const { return DiagID; }
  124. void setDiagID(unsigned ID) { DiagID = ID; }
  125. void Emit(const DiagnosticBuilder &DB) const {
  126. if (!DiagStorage)
  127. return;
  128. // Add all arguments.
  129. for (unsigned i = 0, e = DiagStorage->NumDiagArgs; i != e; ++i) {
  130. if ((DiagnosticsEngine::ArgumentKind)DiagStorage->DiagArgumentsKind[i]
  131. == DiagnosticsEngine::ak_std_string)
  132. DB.AddString(DiagStorage->DiagArgumentsStr[i]);
  133. else
  134. DB.AddTaggedVal(DiagStorage->DiagArgumentsVal[i],
  135. (DiagnosticsEngine::ArgumentKind)DiagStorage->DiagArgumentsKind[i]);
  136. }
  137. // Add all ranges.
  138. for (const CharSourceRange &Range : DiagStorage->DiagRanges)
  139. DB.AddSourceRange(Range);
  140. // Add all fix-its.
  141. for (const FixItHint &Fix : DiagStorage->FixItHints)
  142. DB.AddFixItHint(Fix);
  143. }
  144. void EmitToString(DiagnosticsEngine &Diags,
  145. SmallVectorImpl<char> &Buf) const {
  146. // FIXME: It should be possible to render a diagnostic to a string without
  147. // messing with the state of the diagnostics engine.
  148. DiagnosticBuilder DB(Diags.Report(getDiagID()));
  149. Emit(DB);
  150. Diagnostic(&Diags).FormatDiagnostic(Buf);
  151. DB.Clear();
  152. Diags.Clear();
  153. }
  154. /// Clear out this partial diagnostic, giving it a new diagnostic ID
  155. /// and removing all of its arguments, ranges, and fix-it hints.
  156. void Reset(unsigned DiagID = 0) {
  157. this->DiagID = DiagID;
  158. freeStorage();
  159. }
  160. bool hasStorage() const { return DiagStorage != nullptr; }
  161. /// Retrieve the string argument at the given index.
  162. StringRef getStringArg(unsigned I) {
  163. assert(DiagStorage && "No diagnostic storage?");
  164. assert(I < DiagStorage->NumDiagArgs && "Not enough diagnostic args");
  165. assert(DiagStorage->DiagArgumentsKind[I]
  166. == DiagnosticsEngine::ak_std_string && "Not a string arg");
  167. return DiagStorage->DiagArgumentsStr[I];
  168. }
  169. };
  170. inline const DiagnosticBuilder &operator<<(const DiagnosticBuilder &DB,
  171. const PartialDiagnostic &PD) {
  172. PD.Emit(DB);
  173. return DB;
  174. }
  175. /// A partial diagnostic along with the source location where this
  176. /// diagnostic occurs.
  177. using PartialDiagnosticAt = std::pair<SourceLocation, PartialDiagnostic>;
  178. } // namespace clang
  179. #endif // LLVM_CLANG_BASIC_PARTIALDIAGNOSTIC_H
  180. #ifdef __GNUC__
  181. #pragma GCC diagnostic pop
  182. #endif