|
1 | 1 | #pragma once
|
2 | 2 |
|
3 |
| -#include <binlog/EventStream.hpp> |
4 |
| -#include <binlog/PrettyPrinter.hpp> |
| 3 | +#include <binlog/binlog.hpp> |
5 | 4 | #include <string>
|
6 | 5 | #include <vector>
|
7 | 6 | #include <unordered_map>
|
| 7 | +#include <optional> |
8 | 8 | #include <cassert>
|
9 | 9 | #include <fstream>
|
10 | 10 | #include <filesystem>
|
11 | 11 | #include <sstream>
|
12 | 12 | #include <mutex>
|
| 13 | +#include <fmt/format.h> |
| 14 | +#include <fmt/chrono.h> |
| 15 | +#include <nlohmann/json.hpp> |
13 | 16 |
|
14 | 17 | namespace codeql {
|
15 | 18 |
|
16 | 19 | extern const std::string_view programName;
|
17 | 20 |
|
| 21 | +struct SwiftDiagnosticsLocation { |
| 22 | + std::string_view file; |
| 23 | + unsigned startLine; |
| 24 | + unsigned startColumn; |
| 25 | + unsigned endLine; |
| 26 | + unsigned endColumn; |
| 27 | + |
| 28 | + nlohmann::json json() const; |
| 29 | + std::string str() const; |
| 30 | +}; |
| 31 | + |
18 | 32 | // Models a diagnostic source for Swift, holding static information that goes out into a diagnostic
|
19 | 33 | // These are internally stored into a map on id's. A specific error log can use binlog's category
|
20 | 34 | // as id, which will then be used to recover the diagnostic source while dumping.
|
21 |
| -struct SwiftDiagnosticsSource { |
| 35 | +struct SwiftDiagnostic { |
| 36 | + enum class Visibility : unsigned char { |
| 37 | + none = 0b000, |
| 38 | + statusPage = 0b001, |
| 39 | + cliSummaryTable = 0b010, |
| 40 | + telemetry = 0b100, |
| 41 | + all = 0b111, |
| 42 | + }; |
| 43 | + |
22 | 44 | std::string_view id;
|
23 | 45 | std::string_view name;
|
24 | 46 | static constexpr std::string_view extractorName = "swift";
|
25 | 47 | std::string_view action;
|
26 | 48 | // space separated if more than 1. Not a vector to allow constexpr
|
27 | 49 | // TODO(C++20) with vector going constexpr this can be turned to `std::vector<std::string_view>`
|
28 | 50 | std::string_view helpLinks;
|
29 |
| - |
30 | 51 | // for the moment, we only output errors, so no need to store the severity
|
31 | 52 |
|
32 |
| - // registers a diagnostics source for later retrieval with get, if not done yet |
33 |
| - template <const SwiftDiagnosticsSource* Source> |
34 |
| - static void ensureRegistered() { |
35 |
| - static std::once_flag once; |
36 |
| - std::call_once(once, registerImpl, Source); |
37 |
| - } |
| 53 | + Visibility visibility{Visibility::all}; |
38 | 54 |
|
39 |
| - // gets a previously inscribed SwiftDiagnosticsSource for the given id. Will abort if none exists |
40 |
| - static const SwiftDiagnosticsSource& get(const std::string& id) { return *map().at(id); } |
| 55 | + std::optional<SwiftDiagnosticsLocation> location{}; |
41 | 56 |
|
42 |
| - // emit a JSON diagnostics for this source with the given timestamp and message to out |
43 |
| - // A plaintextMessage is used that includes both the message and the action to take. Dots are |
44 |
| - // appended to both. The id is used to construct the source id in the form |
45 |
| - // `swift/<prog name>/<id with '-' replacing '_'>` |
46 |
| - void emit(std::ostream& out, std::string_view timestamp, std::string_view message) const; |
47 |
| - |
48 |
| - private: |
49 |
| - static void registerImpl(const SwiftDiagnosticsSource* source); |
| 57 | + constexpr SwiftDiagnostic(std::string_view id, |
| 58 | + std::string_view name, |
| 59 | + std::string_view action = "", |
| 60 | + std::string_view helpLinks = "", |
| 61 | + Visibility visibility = Visibility::all) |
| 62 | + : id{id}, name{name}, action{action}, helpLinks{helpLinks}, visibility{visibility} {} |
50 | 63 |
|
51 |
| - std::string sourceId() const; |
52 |
| - using Map = std::unordered_map<std::string, const SwiftDiagnosticsSource*>; |
| 64 | + constexpr SwiftDiagnostic(std::string_view id, std::string_view name, Visibility visibility) |
| 65 | + : SwiftDiagnostic(id, name, "", "", visibility) {} |
53 | 66 |
|
54 |
| - static Map& map() { |
55 |
| - static Map ret; |
| 67 | + // create a JSON diagnostics for this source with the given timestamp and message to out |
| 68 | + // A plaintextMessage is used that includes both the message and the action to take. Dots are |
| 69 | + // appended to both. The id is used to construct the source id in the form |
| 70 | + // `swift/<prog name>/<id>` |
| 71 | + nlohmann::json json(const std::chrono::system_clock::time_point& timestamp, |
| 72 | + std::string_view message) const; |
| 73 | + |
| 74 | + // returns <id> or <id>@<location> if a location is present |
| 75 | + std::string abbreviation() const; |
| 76 | + |
| 77 | + SwiftDiagnostic withLocation(std::string_view file, |
| 78 | + unsigned startLine = 0, |
| 79 | + unsigned startColumn = 0, |
| 80 | + unsigned endLine = 0, |
| 81 | + unsigned endColumn = 0) const { |
| 82 | + auto ret = *this; |
| 83 | + ret.location = SwiftDiagnosticsLocation{file, startLine, startColumn, endLine, endColumn}; |
56 | 84 | return ret;
|
57 | 85 | }
|
58 |
| -}; |
59 |
| - |
60 |
| -// An output modeling binlog's output stream concept that intercepts binlog entries and translates |
61 |
| -// them to appropriate diagnostics JSON entries |
62 |
| -class SwiftDiagnosticsDumper { |
63 |
| - public: |
64 |
| - // opens path for writing out JSON entries. Returns whether the operation was successful. |
65 |
| - bool open(const std::filesystem::path& path) { |
66 |
| - output.open(path); |
67 |
| - return output.good(); |
68 |
| - } |
69 |
| - |
70 |
| - void flush() { output.flush(); } |
71 |
| - |
72 |
| - // write out binlog entries as corresponding JSON diagnostics entries. Expects all entries to have |
73 |
| - // a category equal to an id of a previously created SwiftDiagnosticSource. |
74 |
| - void write(const char* buffer, std::size_t bufferSize); |
75 | 86 |
|
76 | 87 | private:
|
77 |
| - binlog::EventStream events; |
78 |
| - std::ofstream output; |
79 |
| - binlog::PrettyPrinter timestampedMessagePrinter{"%u %m", "%Y-%m-%dT%H:%M:%S.%NZ"}; |
| 88 | + bool has(Visibility v) const; |
80 | 89 | };
|
81 | 90 |
|
82 |
| -} // namespace codeql |
| 91 | +inline constexpr SwiftDiagnostic::Visibility operator|(SwiftDiagnostic::Visibility lhs, |
| 92 | + SwiftDiagnostic::Visibility rhs) { |
| 93 | + return static_cast<SwiftDiagnostic::Visibility>(static_cast<unsigned char>(lhs) | |
| 94 | + static_cast<unsigned char>(rhs)); |
| 95 | +} |
83 | 96 |
|
84 |
| -namespace codeql_diagnostics { |
85 |
| -constexpr codeql::SwiftDiagnosticsSource internal_error{ |
86 |
| - "internal_error", |
| 97 | +inline constexpr SwiftDiagnostic::Visibility operator&(SwiftDiagnostic::Visibility lhs, |
| 98 | + SwiftDiagnostic::Visibility rhs) { |
| 99 | + return static_cast<SwiftDiagnostic::Visibility>(static_cast<unsigned char>(lhs) & |
| 100 | + static_cast<unsigned char>(rhs)); |
| 101 | +} |
| 102 | + |
| 103 | +constexpr SwiftDiagnostic internalError{ |
| 104 | + "internal-error", |
87 | 105 | "Internal error",
|
88 |
| - "Contact us about this issue", |
| 106 | + SwiftDiagnostic::Visibility::telemetry, |
89 | 107 | };
|
90 |
| -} // namespace codeql_diagnostics |
| 108 | +} // namespace codeql |
0 commit comments