LLDB mainline
DiagnosticsRendering.cpp
Go to the documentation of this file.
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
10#include <cstdint>
11
12using namespace lldb_private;
13using namespace lldb;
14
15namespace lldb_private {
16
18
21}
22
23StructuredData::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) {
45 severity = "error";
46 break;
48 severity = "warning";
49 break;
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
63static llvm::raw_ostream &PrintSeverity(Stream &stream,
64 lldb::Severity severity) {
65 llvm::HighlightColor color;
66 llvm::StringRef text;
67 switch (severity) {
69 color = llvm::HighlightColor::Error;
70 text = "error: ";
71 break;
73 color = llvm::HighlightColor::Warning;
74 text = "warning: ";
75 break;
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
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 std::stable_sort(ds.begin(), ds.end(), [](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 (auto detail = remaining_details.rbegin();
189 detail != remaining_details.rend();
190 ++detail, remaining_details.pop_back()) {
191 // Get the information to print this detail and remove it from the stack.
192 // Print all the lines for all the other messages first.
193 stream << std::string(padding, ' ');
194 size_t x_pos = 1;
195 for (auto &remaining_detail :
196 llvm::ArrayRef(remaining_details).drop_back(1)) {
197 uint16_t column = remaining_detail.source_location->column;
198 // Is this a note with the same column as another diagnostic?
199 if (column == detail->source_location->column)
200 continue;
201
202 if (column >= x_pos) {
203 stream << std::string(column - x_pos, ' ') << vbar;
204 x_pos = column + 1;
205 }
206 }
207
208 uint16_t column = detail->source_location->column;
209 // Print the line connecting the ^ with the error message.
210 if (column >= x_pos)
211 stream << std::string(column - x_pos, ' ') << joint << hbar << spacer;
212
213 // Print a colorized string based on the message's severity type.
214 PrintSeverity(stream, detail->severity);
215
216 // Finally, print the message and start a new line.
217 stream << detail->message << '\n';
218 did_print = true;
219 }
220
221 // Print the non-located details.
222 for (const DiagnosticDetail &detail : other_details) {
223 PrintSeverity(stream, detail.severity);
224 stream << detail.rendered << '\n';
225 did_print = true;
226 }
227
228 // Print the hidden details as a last resort.
229 if (!did_print)
230 for (const DiagnosticDetail &detail : hidden_details) {
231 PrintSeverity(stream, detail.severity);
232 stream << detail.rendered << '\n';
233 }
234}
235
236} // namespace lldb_private
lldb::ErrorType GetErrorType() const override
A stream class that can stream formatted output to a file.
Definition: Stream.h:28
llvm::raw_ostream & AsRawOstream()
Returns a raw_ostream that forwards the data to this Stream object.
Definition: Stream.h:401
std::shared_ptr< Object > ObjectSP
A class that represents a running process on the host machine.
static llvm::raw_ostream & PrintSeverity(Stream &stream, lldb::Severity severity)
StructuredData::ObjectSP Serialize(llvm::ArrayRef< DiagnosticDetail > details)
void RenderDiagnosticDetails(Stream &stream, std::optional< uint16_t > offset_in_command, bool show_inline, llvm::ArrayRef< DiagnosticDetail > details)
Definition: SBAddress.h:15
Severity
Used for expressing severity in logs and diagnostics.
@ eErrorTypeExpression
These are from the ExpressionResults enum.
A source location consisting of a file name and position.
A compiler-independent representation of an lldb_private::Diagnostic.
std::optional< SourceLocation > source_location
Contains this diagnostic's source location, if applicable.