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
23static llvm::raw_ostream &PrintSeverity(Stream &stream,
24 lldb::Severity severity) {
25 llvm::HighlightColor color;
26 llvm::StringRef text;
27 switch (severity) {
29 color = llvm::HighlightColor::Error;
30 text = "error: ";
31 break;
33 color = llvm::HighlightColor::Warning;
34 text = "warning: ";
35 break;
37 color = llvm::HighlightColor::Remark;
38 text = "note: ";
39 break;
40 }
41 return llvm::WithColor(stream.AsRawOstream(), color, llvm::ColorMode::Enable)
42 << text;
43}
44
46 std::optional<uint16_t> offset_in_command,
47 bool show_inline,
48 llvm::ArrayRef<DiagnosticDetail> details) {
49 if (details.empty())
50 return;
51
52 if (!offset_in_command) {
53 for (const DiagnosticDetail &detail : details) {
54 PrintSeverity(stream, detail.severity);
55 stream << detail.rendered << '\n';
56 }
57 return;
58 }
59
60 // Since there is no other way to find this out, use the color
61 // attribute as a proxy for whether the terminal supports Unicode
62 // characters. In the future it might make sense to move this into
63 // Host so it can be customized for a specific platform.
64 llvm::StringRef cursor, underline, vbar, joint, hbar, spacer;
65 if (stream.AsRawOstream().colors_enabled()) {
66 cursor = "˄";
67 underline = "˜";
68 vbar = "│";
69 joint = "╰";
70 hbar = "─";
71 spacer = " ";
72 } else {
73 cursor = "^";
74 underline = "~";
75 vbar = "|";
76 joint = "";
77 hbar = "";
78 spacer = "";
79 }
80
81 // Partition the diagnostics.
82 std::vector<DiagnosticDetail> remaining_details, other_details,
83 hidden_details;
84 for (const DiagnosticDetail &detail : details) {
85 if (!show_inline || !detail.source_location) {
86 other_details.push_back(detail);
87 continue;
88 }
89 if (detail.source_location->hidden) {
90 hidden_details.push_back(detail);
91 continue;
92 }
93 if (!detail.source_location->in_user_input) {
94 other_details.push_back(detail);
95 continue;
96 }
97
98 remaining_details.push_back(detail);
99 }
100
101 // Sort the diagnostics.
102 auto sort = [](std::vector<DiagnosticDetail> &ds) {
103 std::stable_sort(ds.begin(), ds.end(), [](auto &d1, auto &d2) {
104 auto l1 = d1.source_location.value_or(DiagnosticDetail::SourceLocation{});
105 auto l2 = d2.source_location.value_or(DiagnosticDetail::SourceLocation{});
106 return std::tie(l1.line, l1.column) < std::tie(l2.line, l2.column);
107 });
108 };
109 sort(remaining_details);
110 sort(other_details);
111 sort(hidden_details);
112
113 // Print a line with caret indicator(s) below the lldb prompt + command.
114 const size_t padding = *offset_in_command;
115 stream << std::string(padding, ' ');
116 {
117 size_t x_pos = 1;
118 for (const DiagnosticDetail &detail : remaining_details) {
119 auto &loc = *detail.source_location;
120
121 if (x_pos > loc.column)
122 continue;
123
124 stream << std::string(loc.column - x_pos, ' ') << cursor;
125 x_pos = loc.column + 1;
126 for (unsigned i = 0; i + 1 < loc.length; ++i) {
127 stream << underline;
128 x_pos += 1;
129 }
130 }
131 }
132 stream << '\n';
133
134 // Reverse the order within groups of diagnostics that are on the same column.
135 auto group = [](std::vector<DiagnosticDetail> &details) {
136 for (auto it = details.begin(), end = details.end(); it != end;) {
137 auto eq_end = std::find_if(it, end, [&](const DiagnosticDetail &d) {
138 return d.source_location->column != it->source_location->column;
139 });
140 std::reverse(it, eq_end);
141 it = eq_end;
142 }
143 };
144 group(remaining_details);
145
146 // Work through each detail in reverse order using the vector/stack.
147 bool did_print = false;
148 for (auto detail = remaining_details.rbegin();
149 detail != remaining_details.rend();
150 ++detail, remaining_details.pop_back()) {
151 // Get the information to print this detail and remove it from the stack.
152 // Print all the lines for all the other messages first.
153 stream << std::string(padding, ' ');
154 size_t x_pos = 1;
155 for (auto &remaining_detail :
156 llvm::ArrayRef(remaining_details).drop_back(1)) {
157 uint16_t column = remaining_detail.source_location->column;
158 // Is this a note with the same column as another diagnostic?
159 if (column == detail->source_location->column)
160 continue;
161
162 if (column >= x_pos) {
163 stream << std::string(column - x_pos, ' ') << vbar;
164 x_pos = column + 1;
165 }
166 }
167
168 uint16_t column = detail->source_location->column;
169 // Print the line connecting the ^ with the error message.
170 if (column >= x_pos)
171 stream << std::string(column - x_pos, ' ') << joint << hbar << spacer;
172
173 // Print a colorized string based on the message's severity type.
174 PrintSeverity(stream, detail->severity);
175
176 // Finally, print the message and start a new line.
177 stream << detail->message << '\n';
178 did_print = true;
179 }
180
181 // Print the non-located details.
182 for (const DiagnosticDetail &detail : other_details) {
183 PrintSeverity(stream, detail.severity);
184 stream << detail.rendered << '\n';
185 did_print = true;
186 }
187
188 // Print the hidden details as a last resort.
189 if (!did_print)
190 for (const DiagnosticDetail &detail : hidden_details) {
191 PrintSeverity(stream, detail.severity);
192 stream << detail.rendered << '\n';
193 }
194}
195
196} // 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
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)
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.