mirror of
https://github.com/encounter/dawn-cmake.git
synced 2025-06-05 14:13:39 +00:00
Fuzzers like to generate silly long source, and formatting large spans of these can take considerable time. Only format the diagnostic if it is going to be displayed. Significantly speeds up some fuzzing tests, fixing some timeouts. Also add a minor optimization to the formatter repeat() implementation. Fixed: chromium:1230313 Change-Id: Ib1f6ac0b31010f86cb7f4e1432dc703ecbe52cb0 Reviewed-on: https://dawn-review.googlesource.com/c/tint/+/58841 Auto-Submit: Ben Clayton <bclayton@google.com> Commit-Queue: Ryan Harrison <rharrison@chromium.org> Kokoro: Kokoro <noreply+kokoro@google.com> Reviewed-by: Ryan Harrison <rharrison@chromium.org>
276 lines
7.8 KiB
C++
276 lines
7.8 KiB
C++
// Copyright 2020 The Tint Authors.
|
|
//
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
// you may not use this file except in compliance with the License.
|
|
// You may obtain a copy of the License at
|
|
//
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
// See the License for the specific language governing permissions and
|
|
// limitations under the License.
|
|
|
|
#include "src/diagnostic/formatter.h"
|
|
|
|
#include <algorithm>
|
|
#include <iterator>
|
|
#include <vector>
|
|
|
|
#include "src/diagnostic/diagnostic.h"
|
|
#include "src/diagnostic/printer.h"
|
|
|
|
namespace tint {
|
|
namespace diag {
|
|
namespace {
|
|
|
|
const char* to_str(Severity severity) {
|
|
switch (severity) {
|
|
case Severity::Note:
|
|
return "note";
|
|
case Severity::Warning:
|
|
return "warning";
|
|
case Severity::Error:
|
|
return "error";
|
|
case Severity::InternalCompilerError:
|
|
return "internal compiler error";
|
|
case Severity::Fatal:
|
|
return "fatal";
|
|
}
|
|
return "";
|
|
}
|
|
|
|
std::string to_str(const Source::Location& location) {
|
|
std::stringstream ss;
|
|
if (location.line > 0) {
|
|
ss << location.line;
|
|
if (location.column > 0) {
|
|
ss << ":" << location.column;
|
|
}
|
|
}
|
|
return ss.str();
|
|
}
|
|
|
|
} // namespace
|
|
|
|
/// State holds the internal formatter state for a format() call.
|
|
struct Formatter::State {
|
|
/// Constructs a State associated with the given printer.
|
|
/// @param p the printer to write formatted messages to.
|
|
explicit State(Printer* p) : printer(p) {}
|
|
~State() { flush(); }
|
|
|
|
/// set_style() sets the current style to new_style, flushing any pending
|
|
/// messages to the printer if the style changed.
|
|
/// @param new_style the new style to apply for future written messages.
|
|
void set_style(const diag::Style& new_style) {
|
|
if (style.color != new_style.color || style.bold != new_style.bold) {
|
|
flush();
|
|
style = new_style;
|
|
}
|
|
}
|
|
|
|
/// flush writes any pending messages to the printer, clearing the buffer.
|
|
void flush() {
|
|
auto str = stream.str();
|
|
if (str.length() > 0) {
|
|
printer->write(str, style);
|
|
std::stringstream reset;
|
|
stream.swap(reset);
|
|
}
|
|
}
|
|
|
|
/// operator<< queues msg to be written to the printer.
|
|
/// @param msg the value or string to write to the printer
|
|
/// @returns this State so that calls can be chained
|
|
template <typename T>
|
|
State& operator<<(const T& msg) {
|
|
stream << msg;
|
|
return *this;
|
|
}
|
|
|
|
/// newline queues a newline to be written to the printer.
|
|
void newline() { stream << std::endl; }
|
|
|
|
/// repeat queues the character c to be written to the printer n times.
|
|
/// @param c the character to print `n` times
|
|
/// @param n the number of times to print character `c`
|
|
void repeat(char c, size_t n) {
|
|
std::fill_n(std::ostream_iterator<char>(stream), n, c);
|
|
}
|
|
|
|
private:
|
|
Printer* printer;
|
|
diag::Style style;
|
|
std::stringstream stream;
|
|
};
|
|
|
|
Formatter::Formatter() {}
|
|
Formatter::Formatter(const Style& style) : style_(style) {}
|
|
|
|
void Formatter::format(const List& list, Printer* printer) const {
|
|
State state{printer};
|
|
|
|
bool please_report_bug = false;
|
|
bool first = true;
|
|
for (auto diag : list) {
|
|
state.set_style({});
|
|
if (!first) {
|
|
state.newline();
|
|
}
|
|
format(diag, state);
|
|
first = false;
|
|
|
|
if (static_cast<int>(diag.severity) > static_cast<int>(Severity::Error)) {
|
|
please_report_bug = true;
|
|
}
|
|
}
|
|
if (please_report_bug) {
|
|
state.set_style({Color::kRed, true});
|
|
state << R"(
|
|
********************************************************************
|
|
* The tint shader compiler has encountered an unexpected error. *
|
|
* *
|
|
* Please help us fix this issue by submitting a bug report at *
|
|
* crbug.com/tint with the source program that triggered the bug. *
|
|
********************************************************************
|
|
)";
|
|
}
|
|
|
|
if (style_.print_newline_at_end) {
|
|
state.newline();
|
|
}
|
|
}
|
|
|
|
void Formatter::format(const Diagnostic& diag, State& state) const {
|
|
auto const& src = diag.source;
|
|
auto const& rng = src.range;
|
|
bool has_code = diag.code != nullptr && diag.code[0] != '\0';
|
|
|
|
state.set_style({Color::kDefault, true});
|
|
|
|
struct TextAndColor {
|
|
std::string text;
|
|
Color color;
|
|
bool bold = false;
|
|
};
|
|
std::vector<TextAndColor> prefix;
|
|
prefix.reserve(6);
|
|
|
|
if (style_.print_file && !src.file_path.empty()) {
|
|
if (rng.begin.line > 0) {
|
|
prefix.emplace_back(TextAndColor{src.file_path + ":" + to_str(rng.begin),
|
|
Color::kDefault});
|
|
} else {
|
|
prefix.emplace_back(TextAndColor{src.file_path, Color::kDefault});
|
|
}
|
|
} else if (rng.begin.line > 0) {
|
|
prefix.emplace_back(TextAndColor{to_str(rng.begin), Color::kDefault});
|
|
}
|
|
|
|
Color severity_color = Color::kDefault;
|
|
switch (diag.severity) {
|
|
case Severity::Note:
|
|
break;
|
|
case Severity::Warning:
|
|
severity_color = Color::kYellow;
|
|
break;
|
|
case Severity::Error:
|
|
severity_color = Color::kRed;
|
|
break;
|
|
case Severity::Fatal:
|
|
case Severity::InternalCompilerError:
|
|
severity_color = Color::kMagenta;
|
|
break;
|
|
}
|
|
if (style_.print_severity) {
|
|
prefix.emplace_back(
|
|
TextAndColor{to_str(diag.severity), severity_color, true});
|
|
}
|
|
if (has_code) {
|
|
prefix.emplace_back(TextAndColor{diag.code, severity_color});
|
|
}
|
|
|
|
for (size_t i = 0; i < prefix.size(); i++) {
|
|
if (i > 0) {
|
|
state << " ";
|
|
}
|
|
state.set_style({prefix[i].color, prefix[i].bold});
|
|
state << prefix[i].text;
|
|
}
|
|
|
|
state.set_style({Color::kDefault, true});
|
|
if (!prefix.empty()) {
|
|
state << ": ";
|
|
}
|
|
state << diag.message;
|
|
|
|
if (style_.print_line && src.file_content != nullptr && rng.begin.line > 0) {
|
|
state.newline();
|
|
state.set_style({Color::kDefault, false});
|
|
|
|
for (size_t line_num = rng.begin.line;
|
|
(line_num <= rng.end.line) &&
|
|
(line_num <= src.file_content->lines.size());
|
|
line_num++) {
|
|
auto& line = src.file_content->lines[line_num - 1];
|
|
auto line_len = line.size();
|
|
|
|
for (auto c : line) {
|
|
if (c == '\t') {
|
|
state.repeat(' ', style_.tab_width);
|
|
} else {
|
|
state << c;
|
|
}
|
|
}
|
|
|
|
state.newline();
|
|
state.set_style({Color::kCyan, false});
|
|
|
|
// Count the number of glyphs in the line span.
|
|
// start and end use 1-based indexing .
|
|
auto num_glyphs = [&](size_t start, size_t end) {
|
|
size_t count = 0;
|
|
start = (start > 0) ? (start - 1) : 0;
|
|
end = (end > 0) ? (end - 1) : 0;
|
|
for (size_t i = start; (i < end) && (i < line_len); i++) {
|
|
count += (line[i] == '\t') ? style_.tab_width : 1;
|
|
}
|
|
return count;
|
|
};
|
|
|
|
if (line_num == rng.begin.line && line_num == rng.end.line) {
|
|
// Single line
|
|
state.repeat(' ', num_glyphs(1, rng.begin.column));
|
|
state.repeat('^', std::max<size_t>(
|
|
num_glyphs(rng.begin.column, rng.end.column), 1));
|
|
} else if (line_num == rng.begin.line) {
|
|
// Start of multi-line
|
|
state.repeat(' ', num_glyphs(1, rng.begin.column));
|
|
state.repeat('^', num_glyphs(rng.begin.column, line_len + 1));
|
|
} else if (line_num == rng.end.line) {
|
|
// End of multi-line
|
|
state.repeat('^', num_glyphs(1, rng.end.column));
|
|
} else {
|
|
// Middle of multi-line
|
|
state.repeat('^', num_glyphs(1, line_len + 1));
|
|
}
|
|
state.newline();
|
|
}
|
|
|
|
state.set_style({});
|
|
}
|
|
}
|
|
|
|
std::string Formatter::format(const List& list) const {
|
|
StringPrinter printer;
|
|
format(list, &printer);
|
|
return printer.str();
|
|
}
|
|
|
|
Formatter::~Formatter() = default;
|
|
|
|
} // namespace diag
|
|
} // namespace tint
|