1 //===-- DiagnosticsRendering.cpp ------------------------------------------===//
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
7 //===----------------------------------------------------------------------===//
9 #include "lldb/Utility/DiagnosticsRendering.h"
12 using namespace lldb_private
;
15 namespace lldb_private
{
17 char DiagnosticError::ID
;
19 lldb::ErrorType
DiagnosticError::GetErrorType() const {
20 return lldb::eErrorTypeExpression
;
23 static llvm::raw_ostream
&PrintSeverity(Stream
&stream
,
24 lldb::Severity severity
) {
25 llvm::HighlightColor color
;
28 case lldb::eSeverityError
:
29 color
= llvm::HighlightColor::Error
;
32 case lldb::eSeverityWarning
:
33 color
= llvm::HighlightColor::Warning
;
36 case lldb::eSeverityInfo
:
37 color
= llvm::HighlightColor::Remark
;
41 return llvm::WithColor(stream
.AsRawOstream(), color
, llvm::ColorMode::Enable
)
45 void RenderDiagnosticDetails(Stream
&stream
,
46 std::optional
<uint16_t> offset_in_command
,
48 llvm::ArrayRef
<DiagnosticDetail
> details
) {
52 if (!offset_in_command
) {
53 for (const DiagnosticDetail
&detail
: details
) {
54 PrintSeverity(stream
, detail
.severity
);
55 stream
<< detail
.rendered
<< '\n';
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()) {
81 // Partition the diagnostics.
82 std::vector
<DiagnosticDetail
> remaining_details
, other_details
,
84 for (const DiagnosticDetail
&detail
: details
) {
85 if (!show_inline
|| !detail
.source_location
) {
86 other_details
.push_back(detail
);
89 if (detail
.source_location
->hidden
) {
90 hidden_details
.push_back(detail
);
93 if (!detail
.source_location
->in_user_input
) {
94 other_details
.push_back(detail
);
98 remaining_details
.push_back(detail
);
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
);
109 sort(remaining_details
);
111 sort(hidden_details
);
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
, ' ');
118 for (const DiagnosticDetail
&detail
: remaining_details
) {
119 auto &loc
= *detail
.source_location
;
121 if (x_pos
> loc
.column
)
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
) {
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
;
140 std::reverse(it
, eq_end
);
144 group(remaining_details
);
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
, ' ');
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
)
162 if (column
>= x_pos
) {
163 stream
<< std::string(column
- x_pos
, ' ') << vbar
;
168 uint16_t column
= detail
->source_location
->column
;
169 // Print the line connecting the ^ with the error message.
171 stream
<< std::string(column
- x_pos
, ' ') << joint
<< hbar
<< spacer
;
173 // Print a colorized string based on the message's severity type.
174 PrintSeverity(stream
, detail
->severity
);
176 // Finally, print the message and start a new line.
177 stream
<< detail
->message
<< '\n';
181 // Print the non-located details.
182 for (const DiagnosticDetail
&detail
: other_details
) {
183 PrintSeverity(stream
, detail
.severity
);
184 stream
<< detail
.rendered
<< '\n';
188 // Print the hidden details as a last resort.
190 for (const DiagnosticDetail
&detail
: hidden_details
) {
191 PrintSeverity(stream
, detail
.severity
);
192 stream
<< detail
.rendered
<< '\n';
196 } // namespace lldb_private