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 "lldb/Host/Terminal.h"
11
12#include <cstdint>
13
14using namespace lldb_private;
15using namespace lldb;
16
17namespace lldb_private {
18
20
24
25StructuredData::ObjectSP Serialize(llvm::ArrayRef<DiagnosticDetail> details) {
26 auto make_array = []() { return std::make_unique<StructuredData::Array>(); };
27 auto make_dict = []() {
28 return std::make_unique<StructuredData::Dictionary>();
29 };
30 auto dict_up = make_dict();
31 dict_up->AddIntegerItem("version", 1u);
32 auto array_up = make_array();
33 for (const DiagnosticDetail &diag : details) {
34 auto detail_up = make_dict();
35 if (auto &sloc = diag.source_location) {
36 auto sloc_up = make_dict();
37 sloc_up->AddStringItem("file", sloc->file.GetPath());
38 sloc_up->AddIntegerItem("line", sloc->line);
39 sloc_up->AddIntegerItem("length", sloc->length);
40 sloc_up->AddBooleanItem("hidden", sloc->hidden);
41 sloc_up->AddBooleanItem("in_user_input", sloc->in_user_input);
42 detail_up->AddItem("source_location", std::move(sloc_up));
43 }
44 llvm::StringRef severity = "unknown";
45 switch (diag.severity) {
47 severity = "error";
48 break;
50 severity = "warning";
51 break;
53 severity = "note";
54 break;
55 }
56 detail_up->AddStringItem("severity", severity);
57 detail_up->AddStringItem("message", diag.message);
58 detail_up->AddStringItem("rendered", diag.rendered);
59 array_up->AddItem(std::move(detail_up));
60 }
61 dict_up->AddItem("details", std::move(array_up));
62 return dict_up;
63}
64
65static llvm::raw_ostream &PrintSeverity(Stream &stream,
66 lldb::Severity severity) {
67 llvm::HighlightColor color;
68 llvm::StringRef text;
69 switch (severity) {
71 color = llvm::HighlightColor::Error;
72 text = "error: ";
73 break;
75 color = llvm::HighlightColor::Warning;
76 text = "warning: ";
77 break;
79 color = llvm::HighlightColor::Remark;
80 text = "note: ";
81 break;
82 }
83 return llvm::WithColor(stream.AsRawOstream(), color, llvm::ColorMode::Enable)
84 << text;
85}
86
88 std::optional<uint16_t> offset_in_command,
89 bool show_inline,
90 llvm::ArrayRef<DiagnosticDetail> details,
91 bool force_ascii) {
92 if (details.empty())
93 return;
94
95 if (!offset_in_command) {
96 for (const DiagnosticDetail &detail : details) {
97 PrintSeverity(stream, detail.severity);
98 stream << detail.rendered << '\n';
99 }
100 return;
101 }
102
103 llvm::StringRef cursor, underline, vbar, joint, hbar, spacer;
104 if (Terminal::SupportsUnicode() && !force_ascii) {
105 cursor = "˄";
106 underline = "˜";
107 vbar = "│";
108 joint = "╰";
109 hbar = "─";
110 spacer = " ";
111 } else {
112 cursor = "^";
113 underline = "~";
114 vbar = "|";
115 joint = "";
116 hbar = "";
117 spacer = "";
118 }
119
120 // Partition the diagnostics.
121 std::vector<DiagnosticDetail> remaining_details, other_details,
122 hidden_details;
123 for (const DiagnosticDetail &detail : details) {
124 if (!show_inline || !detail.source_location) {
125 other_details.push_back(detail);
126 continue;
127 }
128 if (detail.source_location->hidden) {
129 hidden_details.push_back(detail);
130 continue;
131 }
132 if (!detail.source_location->in_user_input) {
133 other_details.push_back(detail);
134 continue;
135 }
136
137 remaining_details.push_back(detail);
138 }
139
140 // Sort the diagnostics.
141 auto sort = [](std::vector<DiagnosticDetail> &ds) {
142 llvm::stable_sort(ds, [](auto &d1, auto &d2) {
143 auto l1 = d1.source_location.value_or(DiagnosticDetail::SourceLocation{});
144 auto l2 = d2.source_location.value_or(DiagnosticDetail::SourceLocation{});
145 return std::tie(l1.line, l1.column) < std::tie(l2.line, l2.column);
146 });
147 };
148 sort(remaining_details);
149 sort(other_details);
150 sort(hidden_details);
151
152 // Print a line with caret indicator(s) below the lldb prompt + command.
153 const size_t padding = *offset_in_command;
154 stream << std::string(padding, ' ');
155 {
156 size_t x_pos = 1;
157 for (const DiagnosticDetail &detail : remaining_details) {
158 auto &loc = *detail.source_location;
159
160 if (x_pos > loc.column)
161 continue;
162
163 stream << std::string(loc.column - x_pos, ' ') << cursor;
164 x_pos = loc.column + 1;
165 for (unsigned i = 0; i + 1 < loc.length; ++i) {
166 stream << underline;
167 x_pos += 1;
168 }
169 }
170 }
171 stream << '\n';
172
173 // Reverse the order within groups of diagnostics that are on the same column.
174 auto group = [](std::vector<DiagnosticDetail> &details) {
175 for (auto it = details.begin(), end = details.end(); it != end;) {
176 auto eq_end = std::find_if(it, end, [&](const DiagnosticDetail &d) {
177 return d.source_location->column != it->source_location->column;
178 });
179 std::reverse(it, eq_end);
180 it = eq_end;
181 }
182 };
183 group(remaining_details);
184
185 // Work through each detail in reverse order using the vector/stack.
186 bool did_print = false;
187 for (; !remaining_details.empty(); remaining_details.pop_back()) {
188 const auto &detail = remaining_details.back();
189 // Get the information to print this detail and remove it from the stack.
190 // Print all the lines for all the other messages first.
191 stream << std::string(padding, ' ');
192 size_t x_pos = 1;
193 for (auto &remaining_detail :
194 llvm::ArrayRef(remaining_details).drop_back(1)) {
195 uint16_t column = remaining_detail.source_location->column;
196 // Is this a note with the same column as another diagnostic?
197 if (column == detail.source_location->column)
198 continue;
199
200 if (column >= x_pos) {
201 stream << std::string(column - x_pos, ' ') << vbar;
202 x_pos = column + 1;
203 }
204 }
205
206 uint16_t column = detail.source_location->column;
207 // Print the line connecting the ^ with the error message.
208 if (column >= x_pos)
209 stream << std::string(column - x_pos, ' ') << joint << hbar << spacer;
210
211 // Print a colorized string based on the message's severity type.
212 PrintSeverity(stream, detail.severity);
213
214 // Finally, print the message and start a new line.
215 stream << detail.message << '\n';
216 did_print = true;
217 }
218
219 // Print the non-located details.
220 for (const DiagnosticDetail &detail : other_details) {
221 PrintSeverity(stream, detail.severity);
222 stream << detail.rendered << '\n';
223 did_print = true;
224 }
225
226 // Print the hidden details as a last resort.
227 if (!did_print)
228 for (const DiagnosticDetail &detail : hidden_details) {
229 PrintSeverity(stream, detail.severity);
230 stream << detail.rendered << '\n';
231 }
232}
233
234} // 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:406
std::shared_ptr< Object > ObjectSP
static bool SupportsUnicode()
Returns whether or not the current terminal supports Unicode rendering.
Definition Terminal.cpp:403
A class that represents a running process on the host machine.
static llvm::raw_ostream & PrintSeverity(Stream &stream, lldb::Severity severity)
void RenderDiagnosticDetails(Stream &stream, std::optional< uint16_t > offset_in_command, bool show_inline, llvm::ArrayRef< DiagnosticDetail > details, bool force_ascii=false)
Renders an array of DiagnosticDetail instances.
StructuredData::ObjectSP Serialize(llvm::ArrayRef< DiagnosticDetail > details)
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.