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