diagnostic_emitter.h 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295
  1. // Part of the Carbon Language project, under the Apache License v2.0 with LLVM
  2. // Exceptions. See /LICENSE for license information.
  3. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
  4. #ifndef CARBON_TOOLCHAIN_DIAGNOSTICS_DIAGNOSTIC_EMITTER_H_
  5. #define CARBON_TOOLCHAIN_DIAGNOSTICS_DIAGNOSTIC_EMITTER_H_
  6. #include <cstdint>
  7. #include <string>
  8. #include <type_traits>
  9. #include <utility>
  10. #include "common/check.h"
  11. #include "llvm/ADT/Any.h"
  12. #include "llvm/ADT/SmallVector.h"
  13. #include "llvm/Support/FormatVariadic.h"
  14. #include "toolchain/diagnostics/diagnostic.h"
  15. #include "toolchain/diagnostics/diagnostic_consumer.h"
  16. #include "toolchain/diagnostics/diagnostic_converter.h"
  17. #include "toolchain/diagnostics/diagnostic_kind.h"
  18. namespace Carbon {
  19. namespace Internal {
  20. // Disable type deduction based on `args`; the type of `diagnostic_base`
  21. // determines the diagnostic's parameter types.
  22. template <typename Arg>
  23. using NoTypeDeduction = std::type_identity_t<Arg>;
  24. } // namespace Internal
  25. template <typename LocT, typename AnnotateFn>
  26. class DiagnosticAnnotationScope;
  27. // Manages the creation of reports, the testing if diagnostics are enabled, and
  28. // the collection of reports.
  29. //
  30. // This class is parameterized by a location type, allowing different
  31. // diagnostic clients to provide location information in whatever form is most
  32. // convenient for them, such as a position within a buffer when lexing, a token
  33. // when parsing, or a parse tree node when type-checking, and to allow unit
  34. // tests to be decoupled from any concrete location representation.
  35. template <typename LocT>
  36. class DiagnosticEmitter {
  37. public:
  38. // A builder-pattern type to provide a fluent interface for constructing
  39. // a more complex diagnostic. See `DiagnosticEmitter::Build` for the
  40. // expected usage.
  41. // This is nodiscard to protect against accidentally building a diagnostic
  42. // without emitting it.
  43. class [[nodiscard]] DiagnosticBuilder {
  44. public:
  45. // DiagnosticBuilder is move-only and cannot be copied.
  46. DiagnosticBuilder(DiagnosticBuilder&&) noexcept = default;
  47. auto operator=(DiagnosticBuilder&&) noexcept
  48. -> DiagnosticBuilder& = default;
  49. // Adds a note diagnostic attached to the main diagnostic being built.
  50. // The API mirrors the main emission API: `DiagnosticEmitter::Emit`.
  51. // For the expected usage see the builder API: `DiagnosticEmitter::Build`.
  52. template <typename... Args>
  53. auto Note(LocT loc, const DiagnosticBase<Args...>& diagnostic_base,
  54. Internal::NoTypeDeduction<Args>... args) -> DiagnosticBuilder& {
  55. if (!emitter_) {
  56. return *this;
  57. }
  58. CARBON_CHECK(diagnostic_base.Level == DiagnosticLevel::Note ||
  59. diagnostic_base.Level == DiagnosticLevel::LocationInfo,
  60. "{0}", static_cast<int>(diagnostic_base.Level));
  61. AddMessage(loc, diagnostic_base, {emitter_->MakeAny<Args>(args)...});
  62. return *this;
  63. }
  64. // Emits the built diagnostic and its attached notes.
  65. // For the expected usage see the builder API: `DiagnosticEmitter::Build`.
  66. template <typename... Args>
  67. auto Emit() -> void {
  68. if (!emitter_) {
  69. return;
  70. }
  71. for (auto annotate_fn : llvm::reverse(emitter_->annotate_fns_)) {
  72. annotate_fn(*this);
  73. }
  74. emitter_->consumer_->HandleDiagnostic(std::move(diagnostic_));
  75. }
  76. // Returns true if this DiagnosticBuilder may emit a diagnostic. Can be used
  77. // to avoid excess work computing notes, etc, if no diagnostic is going to
  78. // be emitted anyway.
  79. explicit operator bool() { return emitter_; }
  80. private:
  81. friend class DiagnosticEmitter<LocT>;
  82. template <typename... Args>
  83. explicit DiagnosticBuilder(DiagnosticEmitter<LocT>* emitter, LocT loc,
  84. const DiagnosticBase<Args...>& diagnostic_base,
  85. llvm::SmallVector<llvm::Any> args)
  86. : emitter_(emitter), diagnostic_({.level = diagnostic_base.Level}) {
  87. AddMessage(loc, diagnostic_base, std::move(args));
  88. CARBON_CHECK(diagnostic_base.Level != DiagnosticLevel::Note);
  89. }
  90. // Create a null `DiagnosticBuilder` that will not emit anything. Notes will
  91. // be silently ignored.
  92. DiagnosticBuilder() : emitter_(nullptr) {}
  93. // Adds a message to the diagnostic, handling conversion of the location and
  94. // arguments.
  95. template <typename... Args>
  96. auto AddMessage(LocT loc, const DiagnosticBase<Args...>& diagnostic_base,
  97. llvm::SmallVector<llvm::Any> args) -> void {
  98. if (!emitter_) {
  99. return;
  100. }
  101. auto converted = emitter_->converter_->ConvertLoc(
  102. loc, [&](DiagnosticLoc context_loc,
  103. const DiagnosticBase<>& context_diagnostic_base) {
  104. AddMessageWithDiagnosticLoc(context_loc, context_diagnostic_base,
  105. {});
  106. });
  107. // Use the last byte offset from the first message.
  108. if (diagnostic_.messages.empty()) {
  109. diagnostic_.last_byte_offset = converted.last_byte_offset;
  110. }
  111. AddMessageWithDiagnosticLoc(converted.loc, diagnostic_base, args);
  112. }
  113. // Adds a message to the diagnostic, handling conversion of the arguments. A
  114. // DiagnosticLoc must be provided instead of a LocT in order to
  115. // avoid potential recursion.
  116. template <typename... Args>
  117. auto AddMessageWithDiagnosticLoc(
  118. DiagnosticLoc loc, const DiagnosticBase<Args...>& diagnostic_base,
  119. llvm::SmallVector<llvm::Any> args) -> void {
  120. if (!emitter_) {
  121. return;
  122. }
  123. diagnostic_.messages.emplace_back(DiagnosticMessage{
  124. .kind = diagnostic_base.Kind,
  125. .level = diagnostic_base.Level,
  126. .loc = loc,
  127. .format = diagnostic_base.Format,
  128. .format_args = std::move(args),
  129. .format_fn = [](const DiagnosticMessage& message) -> std::string {
  130. return FormatFn<Args...>(
  131. message, std::make_index_sequence<sizeof...(Args)>());
  132. }});
  133. }
  134. // Handles the cast of llvm::Any to Args types for formatv.
  135. // TODO: Custom formatting can be provided with an format_provider, but that
  136. // affects all formatv calls. Consider replacing formatv with a custom call
  137. // that allows diagnostic-specific formatting.
  138. template <typename... Args, size_t... N>
  139. static auto FormatFn(const DiagnosticMessage& message,
  140. std::index_sequence<N...> /*indices*/) -> std::string {
  141. static_assert(sizeof...(Args) == sizeof...(N), "Invalid template args");
  142. CARBON_CHECK(message.format_args.size() == sizeof...(Args),
  143. "Argument count mismatch on {0}: {1} != {2}", message.kind,
  144. message.format_args.size(), sizeof...(Args));
  145. return llvm::formatv(
  146. message.format.data(),
  147. llvm::any_cast<
  148. typename Internal::DiagnosticTypeForArg<Args>::StorageType>(
  149. message.format_args[N])...);
  150. }
  151. DiagnosticEmitter<LocT>* emitter_;
  152. Diagnostic diagnostic_;
  153. };
  154. // The `converter` and `consumer` are required to outlive the diagnostic
  155. // emitter.
  156. // TODO: Adjust construction to take stored arguments as pointers, and
  157. // consider taking `converter` by value (move/copy) instead of reference.
  158. explicit DiagnosticEmitter(DiagnosticConverter<LocT>& converter,
  159. DiagnosticConsumer& consumer)
  160. : converter_(&converter), consumer_(&consumer) {}
  161. ~DiagnosticEmitter() = default;
  162. // Emits an error.
  163. //
  164. // When passing arguments, they may be buffered. As a consequence, lifetimes
  165. // may outlive the `Emit` call.
  166. template <typename... Args>
  167. auto Emit(LocT loc, const DiagnosticBase<Args...>& diagnostic_base,
  168. Internal::NoTypeDeduction<Args>... args) -> void {
  169. DiagnosticBuilder(this, loc, diagnostic_base, {MakeAny<Args>(args)...})
  170. .Emit();
  171. }
  172. // A fluent interface for building a diagnostic and attaching notes for added
  173. // context or information. For example:
  174. //
  175. // emitter_.Build(loc1, MyDiagnostic)
  176. // .Note(loc2, MyDiagnosticNote)
  177. // .Emit();
  178. template <typename... Args>
  179. auto Build(LocT loc, const DiagnosticBase<Args...>& diagnostic_base,
  180. Internal::NoTypeDeduction<Args>... args) -> DiagnosticBuilder {
  181. return DiagnosticBuilder(this, loc, diagnostic_base,
  182. {MakeAny<Args>(args)...});
  183. }
  184. // Create a null `DiagnosticBuilder` that will not emit anything. Notes will
  185. // be silently ignored.
  186. auto BuildSuppressed() -> DiagnosticBuilder { return DiagnosticBuilder(); }
  187. private:
  188. // Converts an argument to llvm::Any for storage, handling input to storage
  189. // type conversion when needed.
  190. template <typename Arg>
  191. auto MakeAny(Arg arg) -> llvm::Any {
  192. llvm::Any converted = converter_->ConvertArg(arg);
  193. using Storage = Internal::DiagnosticTypeForArg<Arg>::StorageType;
  194. CARBON_CHECK(
  195. llvm::any_cast<Storage>(&converted),
  196. "Failed to convert argument of type {0} to its storage type {1}",
  197. typeid(Arg).name(), typeid(Storage).name());
  198. return converted;
  199. }
  200. template <typename OtherLocT, typename AnnotateFn>
  201. friend class DiagnosticAnnotationScope;
  202. DiagnosticConverter<LocT>* converter_;
  203. DiagnosticConsumer* consumer_;
  204. llvm::SmallVector<llvm::function_ref<auto(DiagnosticBuilder& builder)->void>>
  205. annotate_fns_;
  206. };
  207. // This relies on `void*` location handling on `DiagnosticEmitter`.
  208. //
  209. // TODO: Based on how this ends up used or if we get more distinct emitters, it
  210. // might be worth considering having diagnostics specify that they don't apply
  211. // to source-location carrying emitters. For example, this might look like a
  212. // `CARBON_NO_LOC_DIAGNOSTIC` macro, or some other factoring. But it might end
  213. // up being more noise than it is worth.
  214. class NoLocDiagnosticEmitter : public DiagnosticEmitter<void*> {
  215. public:
  216. // This constructor only applies to NoLocDiagnosticEmitter.
  217. explicit NoLocDiagnosticEmitter(DiagnosticConsumer* consumer)
  218. : DiagnosticEmitter(converter_, *consumer) {}
  219. // Emits an error. This specialization only applies to
  220. // `NoLocDiagnosticEmitter`.
  221. template <typename... Args>
  222. auto Emit(const DiagnosticBase<Args...>& diagnostic_base,
  223. Internal::NoTypeDeduction<Args>... args) -> void {
  224. DiagnosticEmitter::Emit(nullptr, diagnostic_base, args...);
  225. }
  226. private:
  227. struct Converter : DiagnosticConverter<void*> {
  228. auto ConvertLoc(void* /*loc*/, ContextFnT /*context_fn*/) const
  229. -> ConvertedDiagnosticLoc override {
  230. return {.loc = {.filename = ""}, .last_byte_offset = -1};
  231. }
  232. };
  233. Converter converter_;
  234. };
  235. // An RAII object that denotes a scope in which any diagnostic produced should
  236. // be annotated in some way.
  237. //
  238. // This object is given a function `annotate` that will be called with a
  239. // `DiagnosticBuilder& builder` for any diagnostic that is emitted through the
  240. // given emitter. That function can annotate the diagnostic by calling
  241. // `builder.Note` to add notes.
  242. template <typename LocT, typename AnnotateFn>
  243. class DiagnosticAnnotationScope {
  244. public:
  245. DiagnosticAnnotationScope(DiagnosticEmitter<LocT>* emitter,
  246. AnnotateFn annotate)
  247. : emitter_(emitter), annotate_(std::move(annotate)) {
  248. emitter_->annotate_fns_.push_back(annotate_);
  249. }
  250. ~DiagnosticAnnotationScope() { emitter_->annotate_fns_.pop_back(); }
  251. private:
  252. DiagnosticEmitter<LocT>* emitter_;
  253. // Make a copy of the annotation function to ensure that it lives long enough.
  254. AnnotateFn annotate_;
  255. };
  256. template <typename LocT, typename AnnotateFn>
  257. DiagnosticAnnotationScope(DiagnosticEmitter<LocT>* emitter, AnnotateFn annotate)
  258. -> DiagnosticAnnotationScope<LocT, AnnotateFn>;
  259. } // namespace Carbon
  260. #endif // CARBON_TOOLCHAIN_DIAGNOSTICS_DIAGNOSTIC_EMITTER_H_