Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/contrib/llvm-project/lldb/source/Utility/DiagnosticsRendering.cpp
213766 views
1
//===-- DiagnosticsRendering.cpp ------------------------------------------===//
2
//
3
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4
// See https://llvm.org/LICENSE.txt for license information.
5
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6
//
7
//===----------------------------------------------------------------------===//
8
9
#include "lldb/Utility/DiagnosticsRendering.h"
10
#include <cstdint>
11
12
using namespace lldb_private;
13
using namespace lldb;
14
15
namespace lldb_private {
16
17
char DiagnosticError::ID;
18
19
lldb::ErrorType DiagnosticError::GetErrorType() const {
20
return lldb::eErrorTypeExpression;
21
}
22
23
StructuredData::ObjectSP Serialize(llvm::ArrayRef<DiagnosticDetail> details) {
24
auto make_array = []() { return std::make_unique<StructuredData::Array>(); };
25
auto make_dict = []() {
26
return std::make_unique<StructuredData::Dictionary>();
27
};
28
auto dict_up = make_dict();
29
dict_up->AddIntegerItem("version", 1u);
30
auto array_up = make_array();
31
for (const DiagnosticDetail &diag : details) {
32
auto detail_up = make_dict();
33
if (auto &sloc = diag.source_location) {
34
auto sloc_up = make_dict();
35
sloc_up->AddStringItem("file", sloc->file.GetPath());
36
sloc_up->AddIntegerItem("line", sloc->line);
37
sloc_up->AddIntegerItem("length", sloc->length);
38
sloc_up->AddBooleanItem("hidden", sloc->hidden);
39
sloc_up->AddBooleanItem("in_user_input", sloc->in_user_input);
40
detail_up->AddItem("source_location", std::move(sloc_up));
41
}
42
llvm::StringRef severity = "unknown";
43
switch (diag.severity) {
44
case lldb::eSeverityError:
45
severity = "error";
46
break;
47
case lldb::eSeverityWarning:
48
severity = "warning";
49
break;
50
case lldb::eSeverityInfo:
51
severity = "note";
52
break;
53
}
54
detail_up->AddStringItem("severity", severity);
55
detail_up->AddStringItem("message", diag.message);
56
detail_up->AddStringItem("rendered", diag.rendered);
57
array_up->AddItem(std::move(detail_up));
58
}
59
dict_up->AddItem("details", std::move(array_up));
60
return dict_up;
61
}
62
63
static llvm::raw_ostream &PrintSeverity(Stream &stream,
64
lldb::Severity severity) {
65
llvm::HighlightColor color;
66
llvm::StringRef text;
67
switch (severity) {
68
case lldb::eSeverityError:
69
color = llvm::HighlightColor::Error;
70
text = "error: ";
71
break;
72
case lldb::eSeverityWarning:
73
color = llvm::HighlightColor::Warning;
74
text = "warning: ";
75
break;
76
case lldb::eSeverityInfo:
77
color = llvm::HighlightColor::Remark;
78
text = "note: ";
79
break;
80
}
81
return llvm::WithColor(stream.AsRawOstream(), color, llvm::ColorMode::Enable)
82
<< text;
83
}
84
85
void RenderDiagnosticDetails(Stream &stream,
86
std::optional<uint16_t> offset_in_command,
87
bool show_inline,
88
llvm::ArrayRef<DiagnosticDetail> details) {
89
if (details.empty())
90
return;
91
92
if (!offset_in_command) {
93
for (const DiagnosticDetail &detail : details) {
94
PrintSeverity(stream, detail.severity);
95
stream << detail.rendered << '\n';
96
}
97
return;
98
}
99
100
// Since there is no other way to find this out, use the color
101
// attribute as a proxy for whether the terminal supports Unicode
102
// characters. In the future it might make sense to move this into
103
// Host so it can be customized for a specific platform.
104
llvm::StringRef cursor, underline, vbar, joint, hbar, spacer;
105
if (stream.AsRawOstream().colors_enabled()) {
106
cursor = "˄";
107
underline = "˜";
108
vbar = "│";
109
joint = "╰";
110
hbar = "─";
111
spacer = " ";
112
} else {
113
cursor = "^";
114
underline = "~";
115
vbar = "|";
116
joint = "";
117
hbar = "";
118
spacer = "";
119
}
120
121
// Partition the diagnostics.
122
std::vector<DiagnosticDetail> remaining_details, other_details,
123
hidden_details;
124
for (const DiagnosticDetail &detail : details) {
125
if (!show_inline || !detail.source_location) {
126
other_details.push_back(detail);
127
continue;
128
}
129
if (detail.source_location->hidden) {
130
hidden_details.push_back(detail);
131
continue;
132
}
133
if (!detail.source_location->in_user_input) {
134
other_details.push_back(detail);
135
continue;
136
}
137
138
remaining_details.push_back(detail);
139
}
140
141
// Sort the diagnostics.
142
auto sort = [](std::vector<DiagnosticDetail> &ds) {
143
llvm::stable_sort(ds, [](auto &d1, auto &d2) {
144
auto l1 = d1.source_location.value_or(DiagnosticDetail::SourceLocation{});
145
auto l2 = d2.source_location.value_or(DiagnosticDetail::SourceLocation{});
146
return std::tie(l1.line, l1.column) < std::tie(l2.line, l2.column);
147
});
148
};
149
sort(remaining_details);
150
sort(other_details);
151
sort(hidden_details);
152
153
// Print a line with caret indicator(s) below the lldb prompt + command.
154
const size_t padding = *offset_in_command;
155
stream << std::string(padding, ' ');
156
{
157
size_t x_pos = 1;
158
for (const DiagnosticDetail &detail : remaining_details) {
159
auto &loc = *detail.source_location;
160
161
if (x_pos > loc.column)
162
continue;
163
164
stream << std::string(loc.column - x_pos, ' ') << cursor;
165
x_pos = loc.column + 1;
166
for (unsigned i = 0; i + 1 < loc.length; ++i) {
167
stream << underline;
168
x_pos += 1;
169
}
170
}
171
}
172
stream << '\n';
173
174
// Reverse the order within groups of diagnostics that are on the same column.
175
auto group = [](std::vector<DiagnosticDetail> &details) {
176
for (auto it = details.begin(), end = details.end(); it != end;) {
177
auto eq_end = std::find_if(it, end, [&](const DiagnosticDetail &d) {
178
return d.source_location->column != it->source_location->column;
179
});
180
std::reverse(it, eq_end);
181
it = eq_end;
182
}
183
};
184
group(remaining_details);
185
186
// Work through each detail in reverse order using the vector/stack.
187
bool did_print = false;
188
for (; !remaining_details.empty(); remaining_details.pop_back()) {
189
const auto &detail = remaining_details.back();
190
// Get the information to print this detail and remove it from the stack.
191
// Print all the lines for all the other messages first.
192
stream << std::string(padding, ' ');
193
size_t x_pos = 1;
194
for (auto &remaining_detail :
195
llvm::ArrayRef(remaining_details).drop_back(1)) {
196
uint16_t column = remaining_detail.source_location->column;
197
// Is this a note with the same column as another diagnostic?
198
if (column == detail.source_location->column)
199
continue;
200
201
if (column >= x_pos) {
202
stream << std::string(column - x_pos, ' ') << vbar;
203
x_pos = column + 1;
204
}
205
}
206
207
uint16_t column = detail.source_location->column;
208
// Print the line connecting the ^ with the error message.
209
if (column >= x_pos)
210
stream << std::string(column - x_pos, ' ') << joint << hbar << spacer;
211
212
// Print a colorized string based on the message's severity type.
213
PrintSeverity(stream, detail.severity);
214
215
// Finally, print the message and start a new line.
216
stream << detail.message << '\n';
217
did_print = true;
218
}
219
220
// Print the non-located details.
221
for (const DiagnosticDetail &detail : other_details) {
222
PrintSeverity(stream, detail.severity);
223
stream << detail.rendered << '\n';
224
did_print = true;
225
}
226
227
// Print the hidden details as a last resort.
228
if (!did_print)
229
for (const DiagnosticDetail &detail : hidden_details) {
230
PrintSeverity(stream, detail.severity);
231
stream << detail.rendered << '\n';
232
}
233
}
234
235
} // namespace lldb_private
236
237