Joos1W Compiler Framework
All Classes Functions Typedefs Pages
Diagnostics.h
1 #pragma once
2 
3 #include <forward_list>
4 #include <iostream>
5 #include <ranges>
6 #include <string_view>
7 #include <variant>
8 
9 #include "diagnostics/Location.h"
10 
11 namespace diagnostics {
12 
13 class DiagnosticStorage;
14 class DiagnosticBuilder;
15 class DiagnosticEngine;
16 
17 /* ===--------------------------------------------------------------------=== */
18 // DiagnosticStorage
19 /* ===--------------------------------------------------------------------=== */
20 
22  friend class DiagnosticEngine;
23 
24 public:
25  DiagnosticStorage(SourceRange loc)
26  : argIndex{1}, arguments_{loc} {}
27 
28  void addArgument(std::string_view arg) {
29  assert(argIndex < MaxArguments && "too many arguments");
30  arguments_[argIndex++] = arg;
31  }
32 
33  void addArgument(uint64_t arg) {
34  assert(argIndex < MaxArguments && "too many arguments");
35  arguments_[argIndex++] = arg;
36  }
37 
38  void addRange(SourceRange range) {
39  assert(argIndex < MaxArguments && "too many ranges");
40  arguments_[argIndex++] = range;
41  }
42 
43  auto args() const { return std::views::counted(arguments_, argIndex); }
44 
45  std::ostream& emit(std::ostream& os) const {
46  for(auto& arg : arguments_) {
47  if(std::holds_alternative<std::string_view>(arg)) {
48  os << std::get<std::string_view>(arg);
49  } else if(std::holds_alternative<SourceRange>(arg)) {
50  os << "\n\tat:";
51  std::get<SourceRange>(arg).print(os);
52  } else {
53  os << std::get<uint64_t>(arg);
54  }
55  }
56  return os;
57  }
58 
59 private:
60  int argIndex = 0;
61  static constexpr int MaxArguments = 15;
62  std::variant<std::string_view, uint64_t, SourceRange> arguments_[MaxArguments];
63 };
64 
65 /* ===--------------------------------------------------------------------=== */
66 // DiagnosticBuilder
67 /* ===--------------------------------------------------------------------=== */
68 
70 public:
71  DiagnosticBuilder(DiagnosticStorage& loc) noexcept : storage{loc} {}
72  DiagnosticStorage& storage;
73 };
74 
75 /* ===--------------------------------------------------------------------=== */
76 // DiagnosticStream
77 /* ===--------------------------------------------------------------------=== */
78 
79 class DiagnosticStream : public std::ostream {
80 public:
81  explicit DiagnosticStream(std::ostream& stream) : stream_{stream} {}
82  std::ostream& get() { return buffer; }
83  ~DiagnosticStream() {
84  auto str = buffer.str();
85  stream_ << str;
86  // Prevent double newlines
87  if(!str.ends_with('\n')) stream_ << std::endl;
88  }
89 
90 private:
91  std::ostream& stream_;
92  std::ostringstream buffer;
93 };
94 
95 template <typename T>
96 DiagnosticStream& operator<<(DiagnosticStream& str, T&& value) {
97  str.get() << value;
98  return str;
99 }
100 
101 /* ===--------------------------------------------------------------------=== */
102 // DiagnosticEngine
103 /* ===--------------------------------------------------------------------=== */
104 
106 public:
107  explicit DiagnosticEngine(int verbose = 0) : verbose_{verbose} {}
108  DiagnosticBuilder ReportError(SourceRange loc) {
109  errors_.emplace_after(errors_.before_begin(), loc);
110  return DiagnosticBuilder{errors_.front()};
111  }
112  DiagnosticBuilder ReportWarning(SourceRange loc) {
113  warnings_.emplace_after(warnings_.before_begin(), loc);
114  return DiagnosticBuilder{warnings_.front()};
115  }
116  DiagnosticStream ReportDebug(int level = 1) {
117  assert(Verbose(level) &&
118  "Debug messages not available. Did you forget to check for Verbose?");
119  // FIXME(kevin): In the future, allow for custom streams
120  return DiagnosticStream{std::cerr};
121  }
122  void setVerbose(int verbose) { verbose_ = verbose; }
123  bool hasErrors() const { return !errors_.empty(); }
124  auto errors() const { return std::views::all(errors_); }
125  bool hasWarnings() const { return !warnings_.empty(); }
126  auto warnings() const { return std::views::all(warnings_); }
127 
128 public:
129  bool Verbose(int level = 1) const { return verbose_ >= level; }
130 
131 private:
132  int verbose_ = 0;
133  std::forward_list<DiagnosticStorage> errors_;
134  std::forward_list<DiagnosticStorage> warnings_;
135 };
136 
137 /* ===--------------------------------------------------------------------=== */
138 // Stream operators for DiagnosticBuilder
139 /* ===--------------------------------------------------------------------=== */
140 
141 static inline DiagnosticBuilder& operator<<(DiagnosticBuilder& builder,
142  std::string_view str) {
143  builder.storage.addArgument(str);
144  return builder;
145 }
146 
147 static inline DiagnosticBuilder& operator<<(DiagnosticBuilder& builder,
148  const char* str) {
149  builder.storage.addArgument(std::string_view{str});
150  return builder;
151 }
152 
153 static inline DiagnosticBuilder& operator<<(DiagnosticBuilder& builder,
154  SourceRange range) {
155  builder.storage.addRange(range);
156  return builder;
157 }
158 
159 template <typename T>
160 static inline DiagnosticBuilder& operator<<(DiagnosticBuilder&& builder, T value) {
161  return (DiagnosticBuilder&)builder << value;
162 }
163 
164 } // namespace diagnostics