-
Notifications
You must be signed in to change notification settings - Fork 644
Expand file tree
/
Copy pathdiagnostic.h
More file actions
429 lines (377 loc) · 12.3 KB
/
Copy pathdiagnostic.h
File metadata and controls
429 lines (377 loc) · 12.3 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#pragma once
#include <algorithm>
#include <array>
#include <functional>
#include <iosfwd>
#include <iostream>
#include <optional>
#include <string>
#include <tuple>
#include <utility>
#include <vector>
#include <fmt/format.h>
#include <fmt/ranges.h>
#include <thrift/compiler/detail/pluggable_functions.h>
#include <thrift/compiler/metrics/metrics.h>
#include <thrift/compiler/source_location.h>
namespace apache::thrift::compiler {
// A diagnostic level.
enum class diagnostic_level {
error,
warning,
info,
debug,
};
const char* level_to_string(diagnostic_level level);
class fixit {
public:
/**
* @param original - original text to be fixed
* @param replacement - replacement text to replace the original
* @param loc - location of the fixit in the source code, if left
* blank will be populated with the location of the diagnostic
*/
fixit(std::string original, std::string replacement, source_location loc = {})
: original_(std::move(original)),
replacement_(std::move(replacement)),
loc_(loc) {}
fixit(std::string original, std::string replacement, int line, int column)
: original_(std::move(original)),
replacement_(std::move(replacement)),
line_(line),
column_(column) {}
const std::string& original() const { return original_; }
const std::string& replacement() const { return replacement_; }
int lineno() const { return line_; }
int column() const { return column_; }
// Resolve the location of the fixit in the source code, or use the provided
// diagonstic location
void resolve_location(
source_manager& sm, source_location diagnostic_location);
private:
std::string original_;
std::string replacement_;
int line_ = 0;
int column_ = 0;
source_location loc_;
// Comparing two fixit hints, not including the source_location since that
// is an optional param that ultimately just gets resolved into line and
// column
friend bool operator==(const fixit& lhs, const fixit& rhs) {
return std::tie(lhs.original_, lhs.replacement_, lhs.line_, lhs.column_) ==
std::tie(rhs.original_, rhs.replacement_, rhs.line_, rhs.column_);
}
};
/**
* A diagnostic message.
*/
class diagnostic {
public:
/**
* Creates a diagnostic.
*
* @param level - diagnostic level
* @param message - detailed diagnostic message
* @param file - file path location of diagnostic
* @param line - line location of diagnostic in the file, if known
* @param name - name given to this diagnostic, if any
* @param fixit - an optional fix to apply to the source code
*/
diagnostic(
diagnostic_level level,
std::string message,
std::string file,
int line = 0,
std::string name = "",
std::optional<fixit> fixit_hint = {})
: level_(level),
message_(std::move(message)),
file_(std::move(file)),
line_(line),
name_(std::move(name)),
fixit_hint_(std::move(fixit_hint)) {}
diagnostic_level level() const { return level_; }
const std::string& message() const { return message_; }
const std::string& file() const { return file_; }
int lineno() const { return line_; }
const std::string& name() const { return name_; }
const std::optional<fixit>& fixit_hint() const { return fixit_hint_; }
std::string str() const;
void set_name(std::string&& name) { name_ = std::move(name); }
private:
diagnostic_level level_;
std::string message_;
std::string file_;
int line_;
std::string name_;
std::optional<fixit> fixit_hint_;
friend bool operator==(const diagnostic& lhs, const diagnostic& rhs) {
return std::tie(
lhs.level_,
lhs.line_,
lhs.message_,
lhs.file_,
lhs.name_,
lhs.fixit_hint_) ==
std::tie(
rhs.level_,
rhs.line_,
rhs.message_,
rhs.file_,
rhs.name_,
rhs.fixit_hint_);
}
friend bool operator!=(const diagnostic& lhs, const diagnostic& rhs) {
return !(lhs == rhs);
}
};
// A container of diagnostic results.
class diagnostic_results {
public:
explicit diagnostic_results(std::vector<diagnostic> initial_diagnostics);
diagnostic_results() = default;
const std::vector<diagnostic>& diagnostics() const& { return diagnostics_; }
std::vector<diagnostic>&& diagnostics() && { return std::move(diagnostics_); }
void add(diagnostic diag);
bool has_error() const { return count(diagnostic_level::error) != 0; }
std::size_t count(diagnostic_level level) const {
return counts_.at(static_cast<size_t>(level));
}
template <typename F>
void retain_if(F&& f) {
diagnostics_.erase(
std::remove_if(
diagnostics_.begin(),
diagnostics_.end(),
[&](const diagnostic& diag) {
if (!f(diag)) {
decrement(diag.level());
return true;
}
return false;
}),
diagnostics_.end());
}
private:
std::vector<diagnostic> diagnostics_;
std::array<int, static_cast<size_t>(diagnostic_level::debug) + 1> counts_{};
void increment(diagnostic_level level) {
++counts_.at(static_cast<size_t>(level));
}
void decrement(diagnostic_level level) {
--counts_.at(static_cast<size_t>(level));
}
};
struct diagnostic_params {
bool debug = false;
bool info = false;
int warn_level = 1;
bool log_metrics = true;
bool should_report(diagnostic_level level) const {
switch (level) {
case diagnostic_level::warning:
return warn_level > 0;
case diagnostic_level::debug:
return debug;
case diagnostic_level::info:
return info;
default:
return true;
}
}
// Params that only collect errors.
static diagnostic_params only_errors() { return {false, false, 0}; }
static diagnostic_params strict() { return {false, false, 2}; }
static diagnostic_params keep_all() { return {true, true, 2}; }
};
// A source location used in diagnostic reporting functions to support AST nodes
// without adding a dependency on AST.
struct diagnostic_location {
source_location loc;
/*implicit*/ diagnostic_location(source_location l) : loc(l) {}
template <typename T>
/*implicit*/ diagnostic_location(const T& locatable)
: loc(locatable.src_range().begin) {}
};
// A class used by the Thrift compiler to report diagnostics.
class diagnostics_engine {
public:
explicit diagnostics_engine(
source_manager& sm,
std::function<void(diagnostic)> report_cb,
diagnostic_params params = {})
: source_mgr_(&sm), report_cb_(std::move(report_cb)), params_(params) {}
explicit diagnostics_engine(
source_manager& sm,
diagnostic_results& results,
diagnostic_params params = {})
: diagnostics_engine(
sm,
[&results](diagnostic diag) { results.add(std::move(diag)); },
params) {}
static diagnostics_engine ignore_all(source_manager& sm) {
return diagnostics_engine(
sm, [](const diagnostic&) {}, diagnostic_params::only_errors());
}
diagnostic_params& params() { return params_; }
const diagnostic_params& params() const { return params_; }
source_manager& source_mgr() { return *source_mgr_; }
const source_manager& source_mgr() const { return *source_mgr_; }
bool has_errors() const { return has_errors_; }
void report(diagnostic diag) {
if (diag.level() == diagnostic_level::error) {
has_errors_ = true;
}
if (params_.should_report(diag.level())) {
report_cb_(std::move(diag));
}
}
template <typename... T>
void report(
diagnostic_location loc,
diagnostic_level level,
fmt::format_string<T...> msg,
T&&... args) {
do_report(
loc.loc,
{}, /* name */
{}, /* fixit_hint */
level,
fmt::format(msg, std::forward<T>(args)...));
}
template <typename... T>
void report(
diagnostic_location loc,
std::string name,
std::optional<fixit> fixit_hint,
diagnostic_level level,
fmt::format_string<T...> msg,
T&&... args) {
do_report(
loc.loc,
std::move(name),
std::move(fixit_hint),
level,
fmt::format(msg, std::forward<T>(args)...));
}
template <typename... T>
void report(
diagnostic_location loc,
std::string name,
diagnostic_level level,
fmt::format_string<T...> msg,
T&&... args) {
do_report(
loc.loc,
std::move(name),
{}, /* fixit_hint */
level,
fmt::format(msg, std::forward<T>(args)...));
}
template <typename... T>
void warning(
diagnostic_location loc, fmt::format_string<T...> msg, T&&... args) {
report(loc.loc, diagnostic_level::warning, msg, std::forward<T>(args)...);
}
template <typename... T>
void warning_legacy_strict(
diagnostic_location loc, fmt::format_string<T...> msg, T&&... args) {
if (params().warn_level >= 2) {
warning(loc.loc, msg, std::forward<T>(args)...);
}
}
template <typename... T>
void error(
diagnostic_location loc, fmt::format_string<T...> msg, T&&... args) {
report(loc.loc, diagnostic_level::error, msg, std::forward<T>(args)...);
}
// Reports an error and returns false, if the provided condition is false.
template <typename... T>
bool check(
bool condition,
diagnostic_location loc,
fmt::format_string<T...> msg,
T&&... args) {
if (!condition) {
error(loc, msg, std::forward<T>(args)...);
}
return condition;
}
detail::metrics& metrics() { return metrics_; }
private:
void do_report(
source_location loc,
std::string name,
std::optional<fixit> fixit_hint,
diagnostic_level level,
std::string msg);
source_manager* source_mgr_;
std::function<void(diagnostic)> report_cb_;
diagnostic_params params_;
bool has_errors_ = false;
detail::metrics metrics_;
};
// Makes a diagnostics engine that prints errors to stderr and ignores other
// kinds of diagnostics.
diagnostics_engine make_diagnostics_printer(source_manager& sm);
std::ostream& operator<<(std::ostream& out, const diagnostic&);
struct log_metrics_tag {
static void default_impl(
detail::metrics& metrics, const diagnostic_params& d_params) {
if (!d_params.should_report(diagnostic_level::debug)) {
return;
}
for (int metric = 0;
metric <= static_cast<int>(detail::metrics::StringValue::LAST);
++metric) {
auto stringValue = static_cast<detail::metrics::StringValue>(metric);
fmt::print(
stdout,
"{}={}\n",
detail::metrics::to_string(stringValue),
metrics.get(stringValue).get());
}
for (int metric = 0;
metric <= static_cast<int>(detail::metrics::IntValue::LAST);
++metric) {
auto intValue = static_cast<detail::metrics::IntValue>(metric);
fmt::print(
stdout,
"{}={}\n",
detail::metrics::to_string(intValue),
metrics.get(intValue).get());
}
for (int metric = 0;
metric <= static_cast<int>(detail::metrics::EventString::LAST);
++metric) {
auto event = static_cast<detail::metrics::EventString>(metric);
fmt::print(
stdout,
"{}={}\n",
detail::metrics::to_string(event),
fmt::join(metrics.get(event).get(), ","));
}
}
};
} // namespace apache::thrift::compiler
template <>
struct fmt::formatter<apache::thrift::compiler::diagnostic> {
constexpr auto parse(format_parse_context& ctx) { return ctx.begin(); }
format_context::iterator format(
const apache::thrift::compiler::diagnostic& d, format_context& ctx) const;
};